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