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 }