[UIMA-5961] resolve resource-bundles for default locales for internationalized exceptions at the time of the creation of the exception.

git-svn-id: https://svn.apache.org/repos/asf/uima/uimaj/trunk@1862398 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/uimaj-core/src/main/java/org/apache/uima/InternationalizedException.java b/uimaj-core/src/main/java/org/apache/uima/InternationalizedException.java
index ac32aed..b8e5711 100644
--- a/uimaj-core/src/main/java/org/apache/uima/InternationalizedException.java
+++ b/uimaj-core/src/main/java/org/apache/uima/InternationalizedException.java
@@ -20,6 +20,7 @@
 package org.apache.uima;
 
 import java.util.Locale;
+import java.util.ResourceBundle;
 
 import org.apache.uima.internal.util.I18nUtil;
 
@@ -75,6 +76,14 @@
     * Deserialized versions have null as their value, which is handled by the users
     */
    final transient private ClassLoader originalContextClassLoader;
+   
+   // see https://issues.apache.org/jira/browse/UIMA-5961
+   // the resourceBundle associated with the default locale, at the time of creation of this instance
+   final transient private ResourceBundle default_localized_resourceBundle;
+   // the default locale, at the time of creation of this instance
+   final transient private Locale default_locale;
+   // a user specified resource bundle, used when the default_locale is not appropriate
+   transient private ResourceBundle user_specified_resourceBundle = null;
 
    /**
     * Creates a new <code>InternationalizedException</code> with a null
@@ -135,6 +144,15 @@
            Object[] aArguments, Throwable aCause) {
       super();
       originalContextClassLoader = Thread.currentThread().getContextClassLoader();
+      try {
+        I18nUtil.setTccl(originalContextClassLoader); 
+        default_locale = Locale.getDefault();
+        default_localized_resourceBundle = (aMessageKey == null) 
+            ? null 
+            : I18nUtil.resolveResourceBundle(aResourceBundleName, default_locale, null);      
+      } finally {
+        I18nUtil.removeTccl();        
+      }
       mCause = aCause;
       mResourceBundleName = aResourceBundleName;
       mMessageKey = aMessageKey;
@@ -221,8 +239,18 @@
     */
    public String getLocalizedMessage(Locale aLocale) {
       // check for null message
-      if (getMessageKey() == null)
+      if (getMessageKey() == null) {
          return null;
+      }
+      
+      if (default_localized_resourceBundle != null && aLocale == default_locale) {
+        return I18nUtil.localizeMessage(default_localized_resourceBundle, aLocale, getMessageKey(), getArguments());
+      }
+      
+      if (user_specified_resourceBundle != null) {
+        return I18nUtil.localizeMessage(user_specified_resourceBundle, aLocale, getMessageKey(), getArguments());
+      }
+      
       try {
         I18nUtil.setTccl(originalContextClassLoader);       
         return I18nUtil.localizeMessage(getResourceBundleName(), aLocale, getMessageKey(), getArguments());
@@ -249,7 +277,7 @@
 //         return "EXCEPTION MESSAGE LOCALIZATION FAILED: " + e.toString();
 //      }
    }
-
+   
    /**
     * Gets the cause of this Exception.
     * 
@@ -284,4 +312,23 @@
       return this;
    }
 
+   /**
+    * For the case where the default locale is not being used for getting messages,
+    * and the lookup path in the classpath for the resource bundle needs to be set 
+    * at a specific point, call this method to set the resource bundle at that point in the call stack.
+    * 
+    * Example: If in a Pear, and you are throwing an exception, which is defined in a bundle
+    * in the Pear context, but the catcher of the throw is up the stack above where the pear context
+    * exists (and therefore, is no longer present at "catch" time), and
+    * you don't want to use the default-locale for getting the message out of the message bundle,
+    * 
+    * then do something like this
+    *   Exception e = new AnalysisEngineProcessException(MESSAGE_BUNDLE, "TEST_KEY", objects);
+    *   e.setResourceBundle(my_locale);  // call this method, pass in the needed locale object
+    *   throw e;  // or whatever should be done with it
+    * @param aLocale the locale to use when getting the message from the message bundle at a later time
+    */
+   public void setResourceBundle(Locale aLocale) {
+     user_specified_resourceBundle = I18nUtil.resolveResourceBundle(mResourceBundleName, aLocale, null);
+   }
 }
diff --git a/uimaj-core/src/main/java/org/apache/uima/internal/util/I18nUtil.java b/uimaj-core/src/main/java/org/apache/uima/internal/util/I18nUtil.java
index 8b5cc23..fbb4d5a 100644
--- a/uimaj-core/src/main/java/org/apache/uima/internal/util/I18nUtil.java
+++ b/uimaj-core/src/main/java/org/apache/uima/internal/util/I18nUtil.java
@@ -26,6 +26,8 @@
 /**
  * Internationaliation utilities.
  * 
+ * Static methods only
+ * 
  */
 public class I18nUtil {
   /**
@@ -109,11 +111,38 @@
   public static String localizeMessage(String aResourceBundleName, Locale aLocale,
           String aMessageKey, Object[] aArguments, ClassLoader aLoader) {
     try {
-      if (aLoader == null) {
-        aLoader = MsgLocalizationClassLoader.getMsgLocalizationClassLoader();        
-      }
-      // locate the resource bundle for this exception's messages
-      ResourceBundle bundle =  ResourceBundle.getBundle(aResourceBundleName, aLocale, aLoader);
+      ResourceBundle bundle =  resolveResourceBundle(aResourceBundleName, aLocale, aLoader);
+      return localizeMessage(bundle, aLocale, aMessageKey, aArguments);
+    } catch (Exception e) {
+      return "MESSAGE LOCALIZATION FAILED: " + e.getMessage();
+    }
+  }
+  
+  public static ResourceBundle resolveResourceBundle(String aResourceBundleName, Locale aLocale, ClassLoader aLoader) {
+    if (aLoader == null) {
+      aLoader = MsgLocalizationClassLoader.getMsgLocalizationClassLoader();        
+    }
+    // locate the resource bundle for this exception's messages
+    return ResourceBundle.getBundle(aResourceBundleName, aLocale, aLoader);
+  }
+  
+  /**
+   * Localize a message to a specified Locale.
+   * 
+   * @param aResourceBundle
+   *          the resource bundle to use to resolve message keys
+   * @param aLocale
+   *          locale to which to localize
+   * @param aMessageKey
+   *          key of message to localize
+   * @param aArguments
+   *          arguments to message (may be null if none)
+   * 
+   * @return localized message. If an exception occurs, returns "MESSAGE LOCALIZATION FAILED:"
+   *         followed by the exception message.
+   */
+  public static String localizeMessage(ResourceBundle bundle, Locale aLocale, String aMessageKey, Object[] aArguments) {
+    try {
       String message = bundle.getString(aMessageKey);
       // if arguments exist, use MessageFormat to include them
       if (aArguments != null && aArguments.length > 0) {
@@ -124,7 +153,7 @@
         return message;
     } catch (Exception e) {
       return "MESSAGE LOCALIZATION FAILED: " + e.getMessage();
-    }
+    }    
   }
 
   public static void setTccl(ClassLoader tccl) {