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    }