001// Copyright 2006-2013 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 java.util.Arrays; 018import java.util.List; 019import java.util.Locale; 020import java.util.concurrent.Callable; 021 022import org.apache.tapestry5.SymbolConstants; 023import org.apache.tapestry5.commons.Messages; 024import org.apache.tapestry5.commons.Resource; 025import org.apache.tapestry5.commons.internal.util.TapestryException; 026import org.apache.tapestry5.commons.services.InvalidationEventHub; 027import org.apache.tapestry5.http.TapestryHttpSymbolConstants; 028import org.apache.tapestry5.ioc.annotations.PostInjection; 029import org.apache.tapestry5.ioc.annotations.Symbol; 030import org.apache.tapestry5.ioc.internal.util.URLChangeTracker; 031import org.apache.tapestry5.ioc.services.ClasspathURLConverter; 032import org.apache.tapestry5.ioc.services.ThreadLocale; 033import org.apache.tapestry5.ioc.services.UpdateListener; 034import org.apache.tapestry5.model.ComponentModel; 035import org.apache.tapestry5.services.ComponentClassResolver; 036import org.apache.tapestry5.services.messages.ComponentMessagesSource; 037import org.apache.tapestry5.services.messages.PropertiesFileParser; 038import org.apache.tapestry5.services.pageload.ComponentRequestSelectorAnalyzer; 039import org.apache.tapestry5.services.pageload.ComponentResourceLocator; 040import org.apache.tapestry5.services.pageload.ComponentResourceSelector; 041import org.slf4j.Logger; 042 043public class ComponentMessagesSourceImpl implements ComponentMessagesSource, UpdateListener 044{ 045 private final MessagesSourceImpl messagesSource; 046 047 private final MessagesBundle appCatalogBundle; 048 049 private final ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer; 050 051 private final ThreadLocale threadLocale; 052 053 private class ComponentModelBundle implements MessagesBundle 054 { 055 private final ComponentModel model; 056 057 public ComponentModelBundle(ComponentModel model) 058 { 059 this.model = model; 060 } 061 062 public Resource getBaseResource() 063 { 064 return model.getBaseResource(); 065 } 066 067 public Object getId() 068 { 069 return model.getComponentClassName(); 070 } 071 072 public MessagesBundle getParent() 073 { 074 ComponentModel parentModel = model.getParentModel(); 075 076 if (parentModel == null) 077 return appCatalogBundle; 078 079 return new ComponentModelBundle(parentModel); 080 } 081 } 082 083 public ComponentMessagesSourceImpl(@Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE) 084 boolean productionMode, 085 @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) 086 boolean multipleClassLoaders, 087 List<Resource> appCatalogResources, PropertiesFileParser parser, 088 ComponentResourceLocator resourceLocator, ClasspathURLConverter classpathURLConverter, 089 ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, 090 ThreadLocale threadLocale, ComponentClassResolver componentClassResolver, 091 Logger logger) 092 { 093 this(productionMode, multipleClassLoaders, appCatalogResources, resourceLocator, parser, new URLChangeTracker(classpathURLConverter), 094 componentRequestSelectorAnalyzer, threadLocale, componentClassResolver, logger); 095 } 096 097 ComponentMessagesSourceImpl(boolean productionMode, boolean multipleClassLoaders, Resource appCatalogResource, 098 ComponentResourceLocator resourceLocator, PropertiesFileParser parser, 099 URLChangeTracker tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, 100 ThreadLocale threadLocale, ComponentClassResolver componentClassResolver, 101 Logger logger) 102 { 103 this(productionMode, multipleClassLoaders, Arrays.asList(appCatalogResource), resourceLocator, parser, tracker, componentRequestSelectorAnalyzer, threadLocale, componentClassResolver, logger); 104 } 105 106 ComponentMessagesSourceImpl(boolean productionMode, boolean multipleClassLoaders, List<Resource> appCatalogResources, 107 ComponentResourceLocator resourceLocator, PropertiesFileParser parser, 108 URLChangeTracker tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, 109 ThreadLocale threadLocale, ComponentClassResolver componentClassResolver, 110 Logger logger) 111 { 112 messagesSource = new MessagesSourceImpl(productionMode, multipleClassLoaders, productionMode ? null : tracker, resourceLocator, 113 parser, componentClassResolver, logger); 114 115 appCatalogBundle = createAppCatalogBundle(appCatalogResources); 116 this.componentRequestSelectorAnalyzer = componentRequestSelectorAnalyzer; 117 this.threadLocale = threadLocale; 118 } 119 120 @PostInjection 121 public void setupReload(ReloadHelper reloadHelper) 122 { 123 reloadHelper.addReloadCallback(new Runnable() 124 { 125 public void run() 126 { 127 messagesSource.invalidate(); 128 } 129 }); 130 } 131 132 public void checkForUpdates() 133 { 134 messagesSource.checkForUpdates(); 135 } 136 137 public Messages getMessages(ComponentModel componentModel, Locale locale) 138 { 139 return getMessagesWithForcedLocale(() -> getMessages(componentModel, componentRequestSelectorAnalyzer.buildSelectorForRequest()), locale); 140 } 141 142 private Messages getMessagesWithForcedLocale(Callable<Messages> callable, Locale locale) 143 { 144 final Locale original = threadLocale.getLocale(); 145 try 146 { 147 threadLocale.setLocale(locale); 148 return callable.call(); 149 } catch (Exception e) { 150 throw new TapestryException(e.getMessage(), e); 151 } 152 finally { 153 threadLocale.setLocale(original); 154 } 155 } 156 157 public Messages getMessages(ComponentModel componentModel, ComponentResourceSelector selector) 158 { 159 final MessagesBundle bundle = new ComponentModelBundle(componentModel); 160 return messagesSource.getMessages(bundle, selector); 161 } 162 163 public Messages getApplicationCatalog(Locale locale) 164 { 165 return getMessagesWithForcedLocale(() -> messagesSource.getMessages(appCatalogBundle, componentRequestSelectorAnalyzer.buildSelectorForRequest()), locale); 166 } 167 168 private MessagesBundle createAppCatalogBundle(List<Resource> resources) 169 { 170 MessagesBundle current = null; 171 172 for (Resource r : resources) 173 { 174 current = createMessagesBundle(r, current); 175 } 176 177 return current; 178 } 179 180 private MessagesBundle createMessagesBundle(final Resource resource, final MessagesBundle parent) 181 { 182 return new MessagesBundle() 183 { 184 public Resource getBaseResource() 185 { 186 return resource; 187 } 188 189 public Object getId() 190 { 191 return resource.getPath(); 192 } 193 194 public MessagesBundle getParent() 195 { 196 return parent; 197 } 198 }; 199 } 200 201 public InvalidationEventHub getInvalidationEventHub() 202 { 203 return messagesSource; 204 } 205}