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}