    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
    015package org.apache.tapestry5.internal.services;
    016
    017import org.apache.tapestry5.ioc.*;
    018import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    019import org.apache.tapestry5.ioc.internal.util.LockSupport;
    020import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
    021import org.apache.tapestry5.ioc.services.ThreadLocale;
    022import org.apache.tapestry5.services.InvalidationListener;
    023import org.apache.tapestry5.services.messages.ComponentMessagesSource;
    024
    025import java.util.Locale;
    026import 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 */
    037public class ApplicationMessageCatalogObjectProvider extends LockSupport implements ObjectProvider
    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.newConcurrentMap();
    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            // an invalidation occurs.
    115
    116            messagesSource.getInvalidationEventHub().clearOnInvalidation(localeToMessages);
    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}