From 12656d59b86ff92daea754dfbbaa6d03eff19476 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Tue, 19 Feb 2008 12:52:48 +0000 Subject: [PATCH] Merged V2.2 to HEAD 8290: Merged V2.1-A to V2.2 8229: AWC-1149: Resource bundles in AMP files git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8323 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org/alfresco/web/app/Application.java | 4 +- .../web/app/ResourceBundleBootstrap.java | 28 ++ .../web/app/ResourceBundleWrapper.java | 295 +++++++++--------- .../web/app/ResourceBundleWrapperTest.java | 101 ++++++ .../app/resourceBundleWrapperTest.properties | 3 + 5 files changed, 286 insertions(+), 145 deletions(-) create mode 100644 source/java/org/alfresco/web/app/ResourceBundleBootstrap.java create mode 100644 source/java/org/alfresco/web/app/ResourceBundleWrapperTest.java create mode 100644 source/java/org/alfresco/web/app/resourceBundleWrapperTest.properties diff --git a/source/java/org/alfresco/web/app/Application.java b/source/java/org/alfresco/web/app/Application.java index c4ac2b0c94..2b8b6a26a8 100644 --- a/source/java/org/alfresco/web/app/Application.java +++ b/source/java/org/alfresco/web/app/Application.java @@ -688,7 +688,7 @@ public class Application { locale = Locale.getDefault(); } - bundle = ResourceBundleWrapper.getResourceBundleWrapper(session.getServletContext()).getResourceBundle(MESSAGE_BUNDLE, locale); + bundle = ResourceBundleWrapper.getResourceBundle(session.getServletContext(), MESSAGE_BUNDLE, locale); session.setAttribute(MESSAGE_BUNDLE, bundle); } @@ -719,7 +719,7 @@ public class Application { locale = Locale.getDefault(); } - bundle = ResourceBundleWrapper.getResourceBundleWrapper(FacesContextUtils.getRequiredWebApplicationContext(context).getServletContext()).getResourceBundle(MESSAGE_BUNDLE, locale); + bundle = ResourceBundleWrapper.getResourceBundle(FacesContextUtils.getRequiredWebApplicationContext(context).getServletContext(), MESSAGE_BUNDLE, locale); session.put(MESSAGE_BUNDLE, bundle); } diff --git a/source/java/org/alfresco/web/app/ResourceBundleBootstrap.java b/source/java/org/alfresco/web/app/ResourceBundleBootstrap.java new file mode 100644 index 0000000000..f1a975f47d --- /dev/null +++ b/source/java/org/alfresco/web/app/ResourceBundleBootstrap.java @@ -0,0 +1,28 @@ +/** + * + */ +package org.alfresco.web.app; + +import java.util.List; + +/** + * Resource bundle bootstrap bean + * + * @author Roy Wetherall + */ +public class ResourceBundleBootstrap +{ + /** + * Set the resource bundles to be registered. This should be a list of resource + * bundle base names whose content will be made available to the web client. + * + * @param resourceBundles the resource bundles + */ + public void setResourceBundles(List resourceBundles) + { + for (String resourceBundle : resourceBundles) + { + ResourceBundleWrapper.addResourceBundle(resourceBundle); + } + } +} diff --git a/source/java/org/alfresco/web/app/ResourceBundleWrapper.java b/source/java/org/alfresco/web/app/ResourceBundleWrapper.java index 03cdb7ebb9..37b9bf5a16 100644 --- a/source/java/org/alfresco/web/app/ResourceBundleWrapper.java +++ b/source/java/org/alfresco/web/app/ResourceBundleWrapper.java @@ -25,7 +25,9 @@ package org.alfresco.web.app; import java.io.Serializable; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -52,107 +54,25 @@ import org.springframework.web.context.support.WebApplicationContextUtils; public final class ResourceBundleWrapper extends ResourceBundle implements Serializable { private static final long serialVersionUID = -3230653664902689948L; + private static Log logger = LogFactory.getLog(ResourceBundleWrapper.class); + + /** List of custom bundle names */ + private static List addedBundleNames = new ArrayList(10); - private static Log logger = LogFactory.getLog(ResourceBundleWrapper.class); + /** List of delegate resource bundles */ + transient private List delegates; - private String name; - private Locale locale; - - transient private ResourceBundle delegate; - transient private ResourceBundle delegateCustom; - - private MessageService messageService; - - public static final String BEAN_RESOURCE_BUNDLE_WRAPPER = "resourceBundleWrapper"; - + public static final String BEAN_RESOURCE_MESSAGE_SERVICE = "messageService"; public static final String PATH = "app:company_home/app:dictionary/app:webclient_extension"; - - public ResourceBundleWrapper(MessageService messageService) - { - this.messageService = messageService; - } - - // Helper to get the ResourceBundleWrapper instance with access to the repository - public static ResourceBundleWrapper getResourceBundleWrapper(ServletContext context) - { - return (ResourceBundleWrapper)WebApplicationContextUtils.getRequiredWebApplicationContext(context).getBean( - BEAN_RESOURCE_BUNDLE_WRAPPER); - } /** * Constructor - * - * @param bundle The ResourceBundle to route calls too - * @param customBundle A custom version of bundle to look in if the string is - * not found in bundle + * + * @param bundles the resource bundles including the default, custom and any added */ - private ResourceBundleWrapper(String name, Locale locale) + private ResourceBundleWrapper(List bundles) { - this.name = name; - this.locale = locale; - retreiveBundles(); - } - - private void retreiveBundles() - { - ResourceBundle bundle = ResourceBundle.getBundle(name, locale); - if (bundle == null) - { - throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + name); - } - - // also look up the custom version of the bundle in the extension package - ResourceBundle customBundle = null; - - // first try in the repo otherwise try the classpath - StoreRef storeRef = null; - String path = null; - - try - { - String customName = null; - int idx = name.lastIndexOf("."); - if (idx != -1) - { - customName = name.substring(idx+1, name.length()); - } - else - { - customName = name; - } - - storeRef = Repository.getStoreRef(); - - // TODO - make path configurable in one place ... - // Note: path here is XPath for selectNodes query - path = PATH + "/cm:" + customName; - customBundle = messageService.getRepoResourceBundle(Repository.getStoreRef(), path, locale); - } - catch (Throwable t) - { - // for now ... ignore the error, cannot be found or read from repo - logger.debug("Custom Web Client properties not found: " + storeRef + path); - } - - if (customBundle == null) - { - // classpath - String customName = determineCustomBundleName(name); - try - { - customBundle = ResourceBundle.getBundle(customName, locale); - - if (logger.isDebugEnabled()) - logger.debug("Located and loaded custom bundle: " + customName); - } - catch (MissingResourceException mre) - { - // ignore the error, just leave custom bundle as null - } - } - - this.delegate = bundle; - this.delegateCustom = customBundle; + this.delegates = bundles; } /** @@ -160,37 +80,26 @@ public final class ResourceBundleWrapper extends ResourceBundle implements Seria */ public Enumeration getKeys() { - if (this.delegate == null) + if (this.delegates.size() == 1) { - // restore if the session was [de]serialised - as bundles themselves are not serialised - retreiveBundles(); - } - - if (this.delegateCustom == null) - { - return this.delegate.getKeys(); + return this.delegates.get(0).getKeys(); } else { - // get existing keys - Enumeration keys = this.delegate.getKeys(); - Enumeration customKeys = this.delegateCustom.getKeys(); - - // combine keys into one list - Vector allKeys = new Vector(100, 2); - while (keys.hasMoreElements()) + Vector allKeys = new Vector(100, 2); + for (ResourceBundle delegate : this.delegates) { - allKeys.add(keys.nextElement()); - } - while (customKeys.hasMoreElements()) - { - allKeys.add(customKeys.nextElement()); - } + Enumeration keys = delegate.getKeys(); + while(keys.hasMoreElements() == true) + { + allKeys.add(keys.nextElement()); + } + } return allKeys.elements(); } } - + /** * @see java.util.ResourceBundle#handleGetObject(java.lang.String) */ @@ -198,40 +107,32 @@ public final class ResourceBundleWrapper extends ResourceBundle implements Seria { Object result = null; - if (this.delegate == null) + for (ResourceBundle delegate : this.delegates) { - // restore if the session was [de]serialised - as bundles themselves are not serialised - retreiveBundles(); - } - - try - { - result = this.delegate.getObject(key); - } - catch (MissingResourceException err) - { - // if the string wasn't found in the normal bundle - // try the custom bundle if there is one try { - if (this.delegateCustom != null) + // Try and lookup the key from the resource bundle + result = delegate.getObject(key); + if (result != null) { - result = this.delegateCustom.getObject(key); + break; } } catch (MissingResourceException mre) { - // don't do anything here, dealt with below + // ignore as this means the key was not present } - - // if the key was not found return a default string - if (result == null) + } + + // if the key was not found return a default string + if (result == null) + { + if (logger.isWarnEnabled() == true) { - if (logger.isWarnEnabled()) - logger.warn("Failed to find I18N message string key: " + key); - - result = "$$" + key + "$$"; + logger.warn("Failed to find I18N message string key: " + key); } + + result = "$$" + key + "$$"; } return result; @@ -240,15 +141,123 @@ public final class ResourceBundleWrapper extends ResourceBundle implements Seria /** * Factory method to get a named wrapped resource bundle for a particular locale. * - * @param name Bundle name - * @param locale Locale to retrieve bundle for + * @param servletContext ServletContext + * @param name Bundle name + * @param locale Locale to retrieve bundle for * * @return Wrapped ResourceBundle instance for specified locale */ - public static ResourceBundle getResourceBundle(String name, Locale locale) + public static ResourceBundle getResourceBundle(ServletContext servletContext, String name, Locale locale) { - // apply our wrapper to catch MissingResourceException - return new ResourceBundleWrapper(name, locale); + List bundles = new ArrayList(ResourceBundleWrapper.addedBundleNames.size() + 2); + + // Load the default bundle + ResourceBundle bundle = ResourceBundle.getBundle(name, locale); + if (bundle == null) + { + throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + name); + } + bundles.add(bundle); + + // also look up the custom version of the bundle in the extension package + ResourceBundle customBundle = null; + + if (servletContext != null) + { + MessageService messageService = (MessageService)WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean(BEAN_RESOURCE_MESSAGE_SERVICE); + + // first try in the repo otherwise try the classpath + StoreRef storeRef = null; + String path = null; + + try + { + String customName = null; + int idx = name.lastIndexOf("."); + if (idx != -1) + { + customName = name.substring(idx+1, name.length()); + } + else + { + customName = name; + } + + storeRef = Repository.getStoreRef(); + + // TODO - make path configurable in one place ... + // Note: path here is XPath for selectNodes query + path = PATH + "/cm:" + customName; + customBundle = messageService.getRepoResourceBundle(Repository.getStoreRef(), path, locale); + } + catch (Throwable t) + { + // for now ... ignore the error, cannot be found or read from repo + logger.debug("Custom Web Client properties not found: " + storeRef + path); + } + } + + if (customBundle == null) + { + // also look up the custom version of the bundle in the extension package + String customName = determineCustomBundleName(name); + try + { + customBundle = ResourceBundle.getBundle(customName, locale); + + if (logger.isDebugEnabled()== true) + { + logger.debug("Located and loaded custom bundle: " + customName); + } + } + catch (MissingResourceException mre) + { + // ignore the error, just leave custom bundle as null + } + } + + // Add the custom bundle to the list + if (customBundle != null) + { + bundles.add(customBundle); + } + + // Add any additional bundles + for (String bundleName : ResourceBundleWrapper.addedBundleNames) + { + try + { + // Load the added bundle + ResourceBundle addedBundle = ResourceBundle.getBundle(bundleName, locale); + bundles.add(addedBundle); + + if (logger.isDebugEnabled()) + { + logger.debug("Located and loaded added bundle: " + bundleName); + } + } + catch (MissingResourceException mre) + { + // ignore the error, just log some debug info + if (logger.isDebugEnabled()) + { + logger.debug("Unable to load added bundle: " + bundleName); + } + } + } + + // apply our wrapper to catch MissingResourceException + return new ResourceBundleWrapper(bundles); + } + + /** + * Adds a resource bundle to the collection of custom bundles available + * + * @param name the name of the resource bundle + */ + public static void addResourceBundle(String name) + { + ResourceBundleWrapper.addedBundleNames.add(name); } /** diff --git a/source/java/org/alfresco/web/app/ResourceBundleWrapperTest.java b/source/java/org/alfresco/web/app/ResourceBundleWrapperTest.java new file mode 100644 index 0000000000..acc5532218 --- /dev/null +++ b/source/java/org/alfresco/web/app/ResourceBundleWrapperTest.java @@ -0,0 +1,101 @@ +/** + * + */ +package org.alfresco.web.app; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +import junit.framework.TestCase; + +/** + * Unit test for resource bundle wrapper + * + * @author Roy Wetherall + */ +public class ResourceBundleWrapperTest extends TestCase +{ + private static final String BUNDLE_NAME = "org.alfresco.web.app.resourceBundleWrapperTest"; + private static final String KEY_1 = "test_key_one"; + private static final String KEY_2 = "test_key_two"; + private static final String MSG_1 = "Test Key One"; + private static final String MSG_2 = "Test Key Two"; + + /** + * Test adding the bundles + */ + public void testAddingBundles() + { + // Check that the string's are not added to the bundle + ResourceBundle before = ResourceBundleWrapper.getResourceBundle(null, "alfresco.messages.webclient", Locale.US); + Enumeration keys = before.getKeys(); + assertFalse(containsValue(keys, KEY_1)); + assertFalse(containsValue(keys, KEY_2)); + try + { + before.getString(KEY_1); + fail("Not expecting the key to be there"); + } + catch (Throwable exception){}; + try + { + before.getString(KEY_2); + fail("Not expecting the key to be there"); + } + catch (Throwable exception){}; + + // Add an additional resource bundle + ResourceBundleWrapper.addResourceBundle(BUNDLE_NAME); + + // Check that the string's are now added to the bundle + ResourceBundle after = ResourceBundleWrapper.getResourceBundle(null, "alfresco.messages.webclient", Locale.US); + Enumeration keys2 = after.getKeys(); + assertTrue(containsValue(keys2, KEY_1)); + assertEquals(after.getString(KEY_1), MSG_1); + assertEquals(after.getString(KEY_2), MSG_2); + } + + /** + * Test the bootstrap bean + */ + public void testBootstrap() + { + // Use the bootstrap bean to add the bundles + List bundles = new ArrayList(1); + bundles.add(BUNDLE_NAME); + ResourceBundleBootstrap bootstrap = new ResourceBundleBootstrap(); + bootstrap.setResourceBundles(bundles); + + // Check that the string's are now added to the bundle + ResourceBundle after = ResourceBundleWrapper.getResourceBundle(null, "alfresco.messages.webclient", Locale.US); + Enumeration keys2 = after.getKeys(); + assertTrue(containsValue(keys2, KEY_1)); + assertTrue(containsValue(keys2, KEY_2)); + assertEquals(after.getString(KEY_1), MSG_1); + assertEquals(after.getString(KEY_2), MSG_2); + } + + /** + * Check whether the list contains the values + * + * @param values list of values to check + * @param value value to look for + * @return boolean true if value contained, false otherwise + */ + private boolean containsValue(Enumeration values, String value) + { + boolean result = false; + while (values.hasMoreElements() == true) + { + if (values.nextElement().equals(value) == true) + { + result = true; + break; + } + } + return result; + } +} diff --git a/source/java/org/alfresco/web/app/resourceBundleWrapperTest.properties b/source/java/org/alfresco/web/app/resourceBundleWrapperTest.properties new file mode 100644 index 0000000000..a3b9aacfba --- /dev/null +++ b/source/java/org/alfresco/web/app/resourceBundleWrapperTest.properties @@ -0,0 +1,3 @@ +test_key_one=Test Key One +test_key_two=Test Key Two +test_key_three=Test Key Three \ No newline at end of file