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.commons.*; 018import org.apache.tapestry5.commons.internal.util.LockSupport; 019import org.apache.tapestry5.commons.services.InvalidationListener; 020import org.apache.tapestry5.commons.services.PlasticProxyFactory; 021import org.apache.tapestry5.commons.util.CollectionFactory; 022import org.apache.tapestry5.ioc.services.ThreadLocale; 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}