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