org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource.java

Source

/*
 * 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.codehaus.groovy.grails.context.support;

import grails.util.BuildSettings;
import grails.util.BuildSettingsHolder;
import grails.util.Environment;
import grails.util.Metadata;
import grails.util.PluginBuildSettings;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.plugins.BinaryGrailsPlugin;
import org.codehaus.groovy.grails.plugins.GrailsPlugin;
import org.codehaus.groovy.grails.plugins.GrailsPluginInfo;
import org.codehaus.groovy.grails.plugins.GrailsPluginManager;
import org.codehaus.groovy.grails.plugins.GrailsPluginUtils;
import org.codehaus.groovy.grails.plugins.PluginManagerAware;
import org.codehaus.groovy.grails.plugins.support.aware.GrailsApplicationAware;
import org.codehaus.groovy.grails.support.DevelopmentResourceLoader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

/**
 * 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/";
    protected GrailsApplication application;
    protected GrailsPluginManager pluginManager;
    protected List<String> pluginBaseNames = new ArrayList<String>();
    private ResourceLoader localResourceLoader;
    private PathMatchingResourcePatternResolver resourceResolver;
    private Map<Locale, PropertiesHolder> cachedMergedPluginProperties = new ConcurrentHashMap<Locale, PropertiesHolder>();
    private int pluginCacheMillis = -1;
    private final PluginBuildSettings pluginBuildSettings = GrailsPluginUtils.getPluginBuildSettings();

    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 (pluginManager == null || localResourceLoader == null) {
            return;
        }

        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;
                if (isInlinePlugin(plugin)) {
                    basePath = getInlinePluginPath(plugin);
                } else {
                    basePath = WEB_INF_PLUGINS_PATH.substring(1) + plugin.getFileSystemName();
                }

                final String baseName = StringUtils
                        .substringBefore(FilenameUtils.getBaseName(pluginBundle.getFilename()), "_");
                pluginBaseNames.add(basePath + "/grails-app/i18n/" + baseName);
            }
        }
    }

    /**
     * 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;

            // If the plugin is inline, use the absolute path to the internationalization files
            // in order to convert to resources.  Otherwise, use the relative WEB-INF path.
            String inlinePath = getInlinePluginPath(grailsPlugin);
            if (inlinePath == null) {
                basePath = WEB_INF_PLUGINS_PATH + grailsPlugin.getFileSystemName();
            } else {
                basePath = inlinePath;
            }
            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;
        }
    }

    /**
     * Tests whether or not the Grails plugin is currently being run "inline".
     * @param grailsPlugin The Grails plugin to test.
     * @return {@code true} if the plugin is being used "inline" or {@code false} if the
     *   plugin is not being used "inline".
     */
    protected boolean isInlinePlugin(GrailsPlugin grailsPlugin) {
        return getInlinePluginPath(grailsPlugin) != null;
    }

    /**
     * Returns the absolute path to the provided Grails plugin if it is being used "inline" or {@code null}
     * if the plugin is <b>not</b> being used "inline".
     * @param grailsPlugin The Grails plugin.
     * @return The absolute path to the "inline" plugin or {@code null} if the plugin is not being used "inline".
     */
    protected String getInlinePluginPath(GrailsPlugin grailsPlugin) {
        try {
            final GrailsPluginInfo pluginInfo = pluginBuildSettings
                    .getPluginInfoForName(grailsPlugin.getFileSystemShortName());
            if (pluginInfo != null) {
                return new File(pluginBuildSettings.getBuildSettings().getResourcesDir(),
                        "plugins/" + pluginInfo.getFullName()).getCanonicalPath();
            }
        } catch (final IOException e) {
            LOG.debug("Unable to retrieve plugin directory for plugin " + grailsPlugin.getFileSystemShortName(), e);
        }
        return null;
    }

    @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(Locale locale) {
        PropertiesHolder mergedHolder = cachedMergedPluginProperties.get(locale);
        if (mergedHolder != null) {
            return mergedHolder;
        }

        Properties mergedProps = new Properties();
        mergedHolder = new PropertiesHolder(mergedProps, -1);
        for (String basename : pluginBaseNames) {
            List<String> filenames = calculateAllFilenames(basename, locale);
            for (int j = filenames.size() - 1; j >= 0; j--) {
                String filename = filenames.get(j);
                PropertiesHolder propHolder = getProperties(filename);
                if (propHolder.getProperties() != null) {
                    mergedProps.putAll(propHolder.getProperties());
                }
            }
        }
        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);
                }
            }
        }
        cachedMergedPluginProperties.put(locale, mergedHolder);
        return mergedHolder;
    }

    @Override
    public void setCacheSeconds(int cacheSeconds) {
        pluginCacheMillis = (cacheSeconds * 1000);
        super.setCacheSeconds(cacheSeconds);
    }

    /**
     * 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;
    }

    private String findCodeInBinaryPlugins(String code, Locale locale) {
        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) {
                    String result = binaryPluginProperties.getProperty(code);
                    if (result != null)
                        return result;
                }
            }
        }
        return null;
    }

    private String findMessageInSourcePlugins(String code, Locale locale) {
        for (String pluginBaseName : pluginBaseNames) {
            List<String> filenames = calculateAllFilenames(pluginBaseName, locale);
            for (String filename : filenames) {
                PropertiesHolder holder = getProperties(filename);
                String result = holder.getProperty(code);
                if (result != null)
                    return result;
            }
        }
        return null;
    }

    private MessageFormat findMessageFormatInBinaryPlugins(String code, Locale locale) {
        final GrailsPlugin[] allPlugins = pluginManager.getAllPlugins();
        for (GrailsPlugin plugin : allPlugins) {
            if (!(plugin instanceof BinaryGrailsPlugin)) {
                continue;
            }

            BinaryGrailsPlugin binaryPlugin = (BinaryGrailsPlugin) plugin;
            final Properties binaryPluginProperties = binaryPlugin.getProperties(locale);
            if (binaryPluginProperties != null) {
                String foundCode = binaryPluginProperties.getProperty(code);
                if (foundCode != null) {
                    MessageFormat result = new MessageFormat(foundCode, locale);
                    if (result != null)
                        return result;
                }
            }
        }
        return null;
    }

    private MessageFormat findMessageFormatInSourcePlugins(String code, Locale locale) {
        for (String pluginBaseName : pluginBaseNames) {
            List<String> filenames = calculateAllFilenames(pluginBaseName, locale);
            for (String filename : filenames) {
                PropertiesHolder holder = getProperties(filename);
                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) {
        if (Metadata.getCurrent().isWarDeployed()) {
            localResourceLoader = resourceLoader;
        } else {
            // The "settings" may be null in some of the Grails unit tests.
            BuildSettings settings = BuildSettingsHolder.getSettings();

            String location = null;
            if (settings != null) {
                location = settings.getResourcesDir().getPath();
            } else if (Environment.getCurrent().isReloadEnabled()) {
                location = Environment.getCurrent().getReloadLocation();
            }

            if (location != null) {
                localResourceLoader = new DevelopmentResourceLoader(application, location);
            } else {
                localResourceLoader = resourceLoader;
            }
        }
        super.setResourceLoader(localResourceLoader);

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