    001    // Copyright 2010, 2011, 2012 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.services;
    016
    017    import org.apache.tapestry5.ioc.*;
    018    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    019    import org.apache.tapestry5.ioc.internal.util.LockSupport;
    020    import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
    021    import org.apache.tapestry5.ioc.services.ThreadLocale;
    022    import org.apache.tapestry5.services.InvalidationListener;
    023    import org.apache.tapestry5.services.messages.ComponentMessagesSource;
    024
    025    import java.util.Locale;
    026    import java.util.Map;
    027
    028    /**
    029     * Allows for injection of the global application message catalog into services. The injected value
    030     * is, in fact, a proxy. Each method access of the proxy will determine the current thread's locale, and delegate
    031     * to the actual global message catalog for that particular locale. There's caching to keep it reasonably
    032     * efficient.
    033     *
    034     * @see ComponentMessagesSource#getApplicationCatalog(Locale)
    035     * @since 5.2.0
    036     */
    037    public class ApplicationMessageCatalogObjectProvider extends LockSupport implements ObjectProvider, InvalidationListener
    038    {
    039        private final ObjectLocator objectLocator;
    040
    041        private ComponentMessagesSource messagesSource;
    042
    043        private ThreadLocale threadLocale;
    044
    045        private final Map<Locale, Messages> localeToMessages = CollectionFactory.newMap();
    046
    047        private Messages proxy;
    048
    049        private class ApplicationMessagesObjectCreator implements ObjectCreator<Messages>
    050        {
    051            public Messages createObject()
    052            {
    053                Locale locale = threadLocale.getLocale();
    054
    055                Messages messages = localeToMessages.get(locale);
    056
    057                if (messages == null)
    058                {
    059                    messages = messagesSource.getApplicationCatalog(locale);
    060                    localeToMessages.put(locale, messages);
    061                }
    062
    063                return messages;
    064            }
    065        }
    066
    067        ;
    068
    069        public ApplicationMessageCatalogObjectProvider(ObjectLocator locator)
    070        {
    071            this.objectLocator = locator;
    072        }
    073
    074        /**
    075         * Because this is an ObjectProvider and is part of the MasterObjectProvider pipeline, it has to
    076         * be careful to not require further dependencies at construction time. This means we have to "drop out"
    077         * of normal IoC dependency injection and adopt a lookup strategy based on the ObjectLocator. Further,
    078         * we have to be careful about multi-threading issues.
    079         */
    080        private Messages getProxy()
    081        {
    082            try
    083            {
    084                acquireReadLock();
    085
    086                if (proxy == null)
    087                {
    088                    createProxy();
    089                }
    090
    091                return proxy;
    092            } finally
    093            {
    094                releaseReadLock();
    095            }
    096        }
    097
    098        private void createProxy()
    099        {
    100            try
    101            {
    102                upgradeReadLockToWriteLock();
    103
    104                this.messagesSource = objectLocator.getService(ComponentMessagesSource.class);
    105                this.threadLocale = objectLocator.getService(ThreadLocale.class);
    106
    107                PlasticProxyFactory proxyFactory = objectLocator.getService("PlasticProxyFactory",
    108                        PlasticProxyFactory.class);
    109
    110                proxy = proxyFactory.createProxy(Messages.class, new ApplicationMessagesObjectCreator(),
    111                        "<ApplicationMessagesProxy>");
    112
    113                // Listen for invalidations; clear our cache of localized Messages bundles when
    114                // and invalidation occurs.
    115
    116                messagesSource.getInvalidationEventHub().addInvalidationListener(this);
    117            } finally
    118            {
    119                downgradeWriteLockToReadLock();
    120            }
    121        }
    122
    123        public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
    124        {
    125            if (objectType.equals(Messages.class))
    126                return objectType.cast(getProxy());
    127
    128            return null;
    129        }
    130
    131        public void objectWasInvalidated()
    132        {
    133            localeToMessages.clear();
    134        }
    135
    136    }