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