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 }