001    // Copyright 2010, 2011 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 java.util.Locale;
018    import java.util.Map;
019    
020    import org.apache.tapestry5.ioc.AnnotationProvider;
021    import org.apache.tapestry5.ioc.Messages;
022    import org.apache.tapestry5.ioc.ObjectCreator;
023    import org.apache.tapestry5.ioc.ObjectLocator;
024    import org.apache.tapestry5.ioc.ObjectProvider;
025    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
026    import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
027    import org.apache.tapestry5.ioc.services.ThreadLocale;
028    import org.apache.tapestry5.services.InvalidationListener;
029    import org.apache.tapestry5.services.messages.ComponentMessagesSource;
030    
031    /**
032     * Allows for injection of the global application message catalog into services. The injected value
033     * is, in fact, a proxy. Each method access of the proxy will determine the current thread's locale, and delegate
034     * to the actual global message catalog for that particular locale. There's caching to keep it reasonably
035     * efficient.
036     * 
037     * @since 5.2.0
038     * @see ComponentMessagesSource#getApplicationCatalog(Locale)
039     */
040    public class ApplicationMessageCatalogObjectProvider implements ObjectProvider, InvalidationListener
041    {
042        private final ObjectLocator objectLocator;
043    
044        private ComponentMessagesSource messagesSource;
045    
046        private ThreadLocale threadLocale;
047    
048        private final Map<Locale, Messages> localeToMessages = CollectionFactory.newMap();
049    
050        private Messages proxy;
051    
052        private class ApplicationMessagesObjectCreator implements ObjectCreator<Messages>
053        {
054            public Messages createObject()
055            {
056                Locale locale = threadLocale.getLocale();
057    
058                Messages messages = localeToMessages.get(locale);
059    
060                if (messages == null)
061                {
062                    messages = messagesSource.getApplicationCatalog(locale);
063                    localeToMessages.put(locale, messages);
064                }
065    
066                return messages;
067            }
068        };
069    
070        public ApplicationMessageCatalogObjectProvider(ObjectLocator locator)
071        {
072            this.objectLocator = locator;
073        }
074    
075        /**
076         * Because this is an ObjectProvider and is part of the MasterObjectProvider pipeline, it has to
077         * be careful to not require further dependencies at construction time. This means we have to "drop out"
078         * of normal IoC dependency injection and adopt a lookup strategy based on the ObjectLocator. Further,
079         * we have to be careful about multi-threading issues.
080         */
081        private synchronized Messages getProxy()
082        {
083            if (proxy == null)
084            {
085                this.messagesSource = objectLocator.getService(ComponentMessagesSource.class);
086                this.threadLocale = objectLocator.getService(ThreadLocale.class);
087    
088                PlasticProxyFactory proxyFactory = objectLocator.getService("PlasticProxyFactory",
089                        PlasticProxyFactory.class);
090    
091                proxy = proxyFactory.createProxy(Messages.class, new ApplicationMessagesObjectCreator(),
092                        "<ApplicationMessagesProxy>");
093    
094                // Listen for invalidations; clear our cache of localized Messages bundles when
095                // and invalidation occurs.
096    
097                messagesSource.getInvalidationEventHub().addInvalidationListener(this);
098            }
099    
100            return proxy;
101        }
102    
103        public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
104        {
105            if (objectType.equals(Messages.class))
106                return objectType.cast(getProxy());
107    
108            return null;
109        }
110    
111        public void objectWasInvalidated()
112        {
113            localeToMessages.clear();
114        }
115    
116    }