    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}