org.betaconceptframework.astroboa.portal.managedbean.RepositoryResourceBundle.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.portal.managedbean.RepositoryResourceBundle.java

Source

/*
 * Copyright (C) 2005-2012 BetaCONCEPT Limited
 *
 * This file is part of Astroboa.
 *
 * Astroboa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Astroboa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.betaconceptframework.astroboa.portal.managedbean;

import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.query.CacheRegion;
import org.betaconceptframework.astroboa.portal.schedule.RepositoryResourceBundleMessagesScheduler;
import org.betaconceptframework.astroboa.portal.utility.PortalStringConstants;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.international.LocaleSelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Astroboa Resource Bundle.
 * 
 * <p>
 *   Astroboa Resource bundle is responsible to deliver locale-specific
 *   resources (mainly strings) stored in a taxonomy. 
 *   Each topic of this taxonomy represents a locale-specific resource 
 *   and topic's localized labels represent the values of this resource 
 *   for different locales. 
 * </p>
 * 
 * <p>
 *  Astroboa resource bundle expects to find a taxonomy whose name is comprised of the value of the property 
 *  {@link PortalStringConstants#PORTAL_SYSTEM_NAME} (located in portal.properties),
 *  suffixed by "-"+{@link PortalStringConstants#REPOSITORY_RESOURCE_BUNDLE_TAXONOMY_NAME_SUFFIX}.
 * </p>
 * 
 * <p>
 *    All taxonomy's topic names and their localized labels are pre-loaded in order to 
 *  deliver the appropriate resources more efficiently. A scheduler is initialized as well, in 
 *  order to refresh the resources (topics and their labels). The period at which resources are refreshed
 *  is determined by the value of the property {@link PortalStringConstants#REPOSITORY_RESOURCE_BUNDLE_CACHE_REGION}
 *  (located in portal.properties file) and by default is {@link CacheRegion#ONE_DAY}. 
 * </p>
 * 
 * <p>
 *    Note, that value for cache is 1 day ({@link CacheRegion#ONE_DAY} ), scheduler is scheduled to run at 3:00 a.m. every day
 * and if value for cache is no cache ({@link CacheRegion#NONE} ), scheduler is scheduled to run every minute.
 * </p>
 * 
 * <p>
 *    Please not that all topics are loaded, regardless of whether they are only containers or not.
 * </p>
 * 
 * <p>
 *    This approach offers a number of advantages in respect to the use of messages.properties files 
 * 
 *   <ul>
 *     <li>Portal's localized labels are stored in the repository where all other portal related content (templates, layouts, css, etc)
 *     is stored. This means that practically all portal related content can be managed through Astroboa</li>
 *     <li>In traditional approach, if you needed to change the contents of a messages.properties file you need to restart the
 *     application which is not the case with this setup. Just perform the change in the taxonomy and it will be available
 *     on the next scheduler execution</li>
 *     <li>You can organize resources in a tree like hierarchy in contrary to the flat hierarchy offered by the messages.properties</li>
 *   </ul>
 * </p>
 * 
 * 
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
@Scope(ScopeType.APPLICATION)
@Name("repositoryResourceBundle")
public class RepositoryResourceBundle extends ResourceBundle {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @In(create = true)
    private RepositoryResourceBundleMessagesScheduler repositoryResourceBundleMessagesScheduler;

    private Map<String, Map<String, String>> resourcesPerKey = new HashMap<String, Map<String, String>>();

    public static RepositoryResourceBundle instance() {
        RepositoryResourceBundle repositoryResourceBundle = null;

        if (!Contexts.isApplicationContextActive()) {
            repositoryResourceBundle = new RepositoryResourceBundle();
        } else {
            repositoryResourceBundle = (RepositoryResourceBundle) Component
                    .getInstance(RepositoryResourceBundle.class, ScopeType.APPLICATION);
        }

        if (repositoryResourceBundle != null) {
            repositoryResourceBundle.initialize();
        }

        return repositoryResourceBundle;
    }

    private void initialize() {

        CacheRegion cacheRegion = CacheRegion.ONE_DAY;

        PropertiesConfiguration portalConfiguration = null;

        try {
            portalConfiguration = new PropertiesConfiguration("portal.properties");

            String cacheRegionValueInConfiguration = portalConfiguration
                    .getString(PortalStringConstants.REPOSITORY_RESOURCE_BUNDLE_CACHE_REGION);

            try {
                cacheRegion = CacheRegion.valueOf(cacheRegionValueInConfiguration);
            } catch (Exception e) {
                logger.warn("Invalid value of " + PortalStringConstants.REPOSITORY_RESOURCE_BUNDLE_CACHE_REGION
                        + " : " + cacheRegionValueInConfiguration + " Cache Region will be set to "
                        + CacheRegion.ONE_DAY);
                cacheRegion = CacheRegion.ONE_DAY;
            }
        } catch (Exception e) {
            logger.error("", e);
            portalConfiguration = null;
        }

        //Activate scheduler to load resource bundles
        switch (cacheRegion) {
        case ONE_DAY:
            // Run in 3 in the morning every day
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(), "0 0 3 * * ?");
            break;
        case NONE:
        case ONE_MINUTE:
            // Run every minute
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(1 * 60 * 1000));
            break;
        case FIVE_MINUTES:
            // Run every 5 minute
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(5 * 60 * 1000));
            break;
        case TEN_MINUTES:
            // Run every 10 minute
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(10 * 60 * 1000));
            break;
        case THIRTY_MINUTES:
            // Run every 30 minute
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(30 * 60 * 1000));
            break;
        case ONE_HOUR:
            // Run every hour
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(1 * 60 * 60 * 1000));
            break;
        case SIX_HOURS:
            // Run every 6 hours
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(6 * 60 * 60 * 1000));
            break;
        case TWELVE_HOURS:
            // Run every 12 hours
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(),
                    new Long(12 * 60 * 60 * 1000));
            break;
        default:
            // Run in 3 in the morning every day
            repositoryResourceBundleMessagesScheduler.loadRepositoryResourceBundle(new Date(), "0 0 3 * * ?");
            break;
        }

    }

    @Override
    public Enumeration<String> getKeys() {
        return Collections.enumeration(resourcesPerKey.keySet());
    }

    @Override
    protected Object handleGetObject(String key) {

        try {

            if (resourcesPerKey.isEmpty() || StringUtils.isBlank(key) || !resourcesPerKey.containsKey(key)) {
                return key;
            }

            //Retrieve active locale
            LocaleSelector localeSelector = LocaleSelector.instance();

            if (localeSelector == null || !resourcesPerKey.get(key).containsKey(localeSelector.getLocaleString())) {
                return key;
            }

            return resourcesPerKey.get(key).get(localeSelector.getLocaleString());

        } catch (Exception e) {
            logger.error("", e);
            return key;
        }
    }

    /**
     * @param resourcesPerKey
     */
    public void setResources(Map<String, Map<String, String>> resourcesPerKey) {
        if (resourcesPerKey == null) {
            clearResources();
        } else {
            this.resourcesPerKey = resourcesPerKey;
        }

    }

    /**
     * 
     */
    public void clearResources() {
        if (this.resourcesPerKey != null) {
            this.resourcesPerKey.clear();
        }

    }
}