Package org.grails.spring.context.support

Source Code of org.grails.spring.context.support.PluginAwareResourceBundleMessageSource

/*
* Copyright 2004-2005 Graeme Rocher
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.spring.context.support;

import grails.core.GrailsApplication;
import grails.core.support.GrailsApplicationAware;
import grails.plugins.GrailsPlugin;
import grails.plugins.GrailsPluginManager;
import grails.plugins.PluginManagerAware;
import grails.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.grails.plugins.BinaryGrailsPlugin;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* A ReloadableResourceBundleMessageSource that is capable of loading message sources from plugins.
*
* @author Graeme Rocher
* @since 1.1
*/
public class PluginAwareResourceBundleMessageSource extends ReloadableResourceBundleMessageSource implements GrailsApplicationAware, PluginManagerAware, InitializingBean {
    private static final Log LOG = LogFactory.getLog(PluginAwareResourceBundleMessageSource.class);

    private static final Resource[] NO_RESOURCES = {};

    private static final String WEB_INF_PLUGINS_PATH = "/WEB-INF/plugins/";
    private static final String GRAILS_APP_I18N_PATH_COMPONENT = "/grails-app/i18n/";
    protected GrailsApplication application;
    protected GrailsPluginManager pluginManager;
    protected List<String> pluginBaseNames = new ArrayList<String>();
    private ResourceLoader localResourceLoader;
    private PathMatchingResourcePatternResolver resourceResolver;
    private ConcurrentMap<Locale, CacheEntry<PropertiesHolder>> cachedMergedPluginProperties = new ConcurrentHashMap<Locale, CacheEntry<PropertiesHolder>>();
    private ConcurrentMap<Locale, CacheEntry<PropertiesHolder>> cachedMergedBinaryPluginProperties = new ConcurrentHashMap<Locale, CacheEntry<PropertiesHolder>>();
    private long pluginCacheMillis = Long.MIN_VALUE;

    public List<String> getPluginBaseNames() {
        return pluginBaseNames;
    }

    public void setPluginBaseNames(List<String> pluginBaseNames) {
        this.pluginBaseNames = pluginBaseNames;
    }

    public void setGrailsApplication(GrailsApplication grailsApplication) {
        application = grailsApplication;
    }

    public void setPluginManager(GrailsPluginManager pluginManager) {
        this.pluginManager = pluginManager;
    }

    public void setResourceResolver(PathMatchingResourcePatternResolver resourceResolver) {
        this.resourceResolver = resourceResolver;
    }

    public void afterPropertiesSet() throws Exception {
        if (pluginCacheMillis == Long.MIN_VALUE) {
            pluginCacheMillis = cacheMillis;
        }
       
        if (pluginManager == null || localResourceLoader == null) {
            return;
        }

        Resource[] resources = resourceResolver.getResources("classpath*:**/*.properties");

        List<String> basenames = new ArrayList<String>();
        for (Resource resource : resources) {
            String filename = resource.getFilename();
            String baseName = GrailsStringUtils.getFileBasename(filename);
            int i = baseName.indexOf('_');
            if(i > -1) {
                baseName = baseName.substring(0, i);
            }
            if(!basenames.contains(baseName))
                basenames.add(baseName);
        }

        setBasenames(basenames.toArray( new String[basenames.size()]));



        for (GrailsPlugin plugin : pluginManager.getAllPlugins()) {
            for (Resource pluginBundle : getPluginBundles(plugin)) {
                // If the plugin is an inline plugin, use the abosolute path to the plugin's i18n files.
                // Otherwise, use the relative path to the plugin from the application's perspective.
                String basePath = WEB_INF_PLUGINS_PATH.substring(1) + plugin.getFileSystemName();

                final String baseName = GrailsStringUtils.substringBefore(GrailsStringUtils.getFileBasename(pluginBundle.getFilename()), "_");
                String pathToAdd = basePath + GRAILS_APP_I18N_PATH_COMPONENT + baseName;
                if(!pluginBaseNames.contains(pathToAdd)) {
                    pluginBaseNames.add(pathToAdd);
                }
            }
        }
    }

    /**
     * Returns the i18n message bundles for the provided plugin or an empty
     * array if the plugin does not contain any .properties files in its
     * grails-app/i18n folder.
     * @param grailsPlugin The grails plugin that may or may not contain i18n internationalization files.
     * @return An array of {@code Resource} objects representing the internationalization files or
     *    an empty array if no files are found.
     */
    protected Resource[] getPluginBundles(GrailsPlugin grailsPlugin) {
        if (grailsPlugin instanceof BinaryGrailsPlugin) {
            return NO_RESOURCES;
        }

        try {
            String basePath = WEB_INF_PLUGINS_PATH + grailsPlugin.getFileSystemName();
            return resourceResolver.getResources(basePath + "/grails-app/i18n/*.properties");
        }
        catch (IOException e) {
            LOG.debug("Could not resolve any resources for plugin " + grailsPlugin.getFileSystemName(), e);
            return NO_RESOURCES;
        }
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        String msg = super.resolveCodeWithoutArguments(code, locale);
        return msg == null ? resolveCodeWithoutArgumentsFromPlugins(code, locale) : msg;
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        MessageFormat mf = super.resolveCode(code, locale);
        return mf == null ? resolveCodeFromPlugins(code, locale) : mf;
    }

    /**
     * Get a PropertiesHolder that contains the actually visible properties
     * for a Locale, after merging all specified resource bundles.
     * Either fetches the holder from the cache or freshly loads it.
     * <p>Only used when caching resource bundle contents forever, i.e.
     * with cacheSeconds < 0. Therefore, merged properties are always
     * cached forever.
     */
    protected PropertiesHolder getMergedPluginProperties(final Locale locale) {
        return CacheEntry.getValue(cachedMergedPluginProperties, locale, cacheMillis, new Callable<PropertiesHolder>() {
            @Override
            public PropertiesHolder call() throws Exception {
                Properties mergedProps = new Properties();
                PropertiesHolder mergedHolder = new PropertiesHolder(mergedProps);
                mergeBinaryPluginProperties(locale, mergedProps);
                for (String basename : pluginBaseNames) {
                    List<Pair<String, Resource>> filenamesAndResources = calculateAllFilenames(basename, locale);
                    for (int j = filenamesAndResources.size() - 1; j >= 0; j--) {
                        Pair<String, Resource> filenameAndResource = filenamesAndResources.get(j);
                        if(filenameAndResource.getbValue() != null) {
                            PropertiesHolder propHolder = getProperties(filenameAndResource.getaValue(), filenameAndResource.getbValue());
                            mergedProps.putAll(propHolder.getProperties());
                        }
                    }
                }
                return mergedHolder;
            }
        });
    }

    /**
     * Attempts to resolve a String for the code from the list of plugin base names
     *
     * @param code The code
     * @param locale The locale
     * @return a MessageFormat
     */
    protected String resolveCodeWithoutArgumentsFromPlugins(String code, Locale locale) {
        if (pluginCacheMillis < 0) {
            PropertiesHolder propHolder = getMergedPluginProperties(locale);
            String result = propHolder.getProperty(code);
            if (result != null) {
                return result;
            }
        }
        else {
            String result = findMessageInSourcePlugins(code, locale);
            if (result != null) return result;

            result = findCodeInBinaryPlugins(code, locale);
            if (result != null) return result;

        }
        return null;
    }
   
    protected PropertiesHolder getMergedBinaryPluginProperties(final Locale locale) {
        return CacheEntry.getValue(cachedMergedBinaryPluginProperties, locale, cacheMillis, new Callable<PropertiesHolder>() {
            @Override
            public PropertiesHolder call() throws Exception {
                Properties mergedProps = new Properties();
                PropertiesHolder mergedHolder = new PropertiesHolder(mergedProps);
                mergeBinaryPluginProperties(locale, mergedProps);
                return mergedHolder;
            }

        });
    }

    protected void mergeBinaryPluginProperties(final Locale locale, Properties mergedProps) {
        final GrailsPlugin[] allPlugins = pluginManager.getAllPlugins();
        for (GrailsPlugin plugin : allPlugins) {
            if (plugin instanceof BinaryGrailsPlugin) {
                BinaryGrailsPlugin binaryPlugin = (BinaryGrailsPlugin) plugin;
                final Properties binaryPluginProperties = binaryPlugin.getProperties(locale);
                if (binaryPluginProperties != null) {
                    mergedProps.putAll(binaryPluginProperties);
                }
            }
        }
    }

    private String findCodeInBinaryPlugins(String code, Locale locale) {
        return getMergedBinaryPluginProperties(locale).getProperty(code);
    }

    private String findMessageInSourcePlugins(String code, Locale locale) {
        for (String pluginBaseName : pluginBaseNames) {
            List<Pair<String, Resource>> filenamesAndResources = calculateAllFilenames(pluginBaseName, locale);
            for (Pair<String, Resource> filenameAndResource : filenamesAndResources) {
                PropertiesHolder holder = getProperties(filenameAndResource.getaValue(), filenameAndResource.getbValue());
                String result = holder.getProperty(code);
                if (result != null) return result;
            }
        }
        return null;
    }

    private MessageFormat findMessageFormatInBinaryPlugins(String code, Locale locale) {
        return getMergedBinaryPluginProperties(locale).getMessageFormat(code, locale);
    }

    private MessageFormat findMessageFormatInSourcePlugins(String code, Locale locale) {
        for (String pluginBaseName : pluginBaseNames) {
            List<Pair<String, Resource>> filenamesAndResources = calculateAllFilenames(pluginBaseName, locale);
            for (Pair<String, Resource> filenameAndResource : filenamesAndResources) {
                PropertiesHolder holder = getProperties(filenameAndResource.getaValue(), filenameAndResource.getbValue());
                MessageFormat result = holder.getMessageFormat(code, locale);
                if (result != null) return result;
            }
        }
        return null;
    }

    /**
     * Attempts to resolve a MessageFormat for the code from the list of plugin base names
     *
     * @param code The code
     * @param locale The locale
     * @return a MessageFormat
     */
    protected MessageFormat resolveCodeFromPlugins(String code, Locale locale) {
        if (pluginCacheMillis < 0) {
            PropertiesHolder propHolder = getMergedPluginProperties(locale);
            MessageFormat result = propHolder.getMessageFormat(code, locale);
            if (result != null) {
                return result;
            }
        }
        else {
            MessageFormat result = findMessageFormatInSourcePlugins(code, locale);
            if (result != null) return result;

            result = findMessageFormatInBinaryPlugins(code, locale);
            if (result != null) return result;
        }
        return null;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        super.setResourceLoader(resourceLoader);

        this.localResourceLoader = resourceLoader;
        if (resourceResolver == null) {
            resourceResolver = new PathMatchingResourcePatternResolver(localResourceLoader);
        }
    }

   
    /**
     * Set the number of seconds to cache the list of matching properties files loaded from plugin.
     * <ul>
     * <li>Default value is the same value as cacheSeconds
     * </ul>
     */
    public void setPluginCacheSeconds(int pluginCacheSeconds) {
        this.pluginCacheMillis = (pluginCacheSeconds * 1000);
    }   
}
TOP

Related Classes of org.grails.spring.context.support.PluginAwareResourceBundleMessageSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.