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 }