Hi all,

Summary:

org.apache.uima.InternationalizedException's getLocalizedMessage
doesn't account for the use of an "extension" class path when loading
message resources. For applications that use extension class paths, the
resource is therefore not found.

Details:

I'm getting an exception when trying to create an
AnalysisEngineProcessException:

org.apache.uima.analysis_engine.AnalysisEngineProcessException:
    EXCEPTION MESSAGE LOCALIZATION FAILED: 
    java.util.MissingResourceException: Can't find bundle for base
    name TestAnnotatorRB, locale en_US

The code that generates this message is in InternationalizedException's
getLocalizedMethod:

    public String getLocalizedMessage(Locale aLocale) {
        // check for null message
        if (getMessageKey() == null)
          return null;

        try {
          // locate the resource bundle for this exception's messages
          ResourceBundle bundle = 
              ResourceBundle.getBundle(getResourceBundleName(),
                                       aLocale);
          // retrieve the message from the resource bundle
          String message = bundle.getString(getMessageKey());
          // if arguments exist, use MessageFormat to include them
          if (getArguments().length > 0) {
            MessageFormat fmt = new MessageFormat(message);
            fmt.setLocale(aLocale);
            return fmt.format(getArguments());
          } else
            return message;
        } catch (Exception e) {
          return "EXCEPTION MESSAGE LOCALIZATION FAILED: " + 
                 e.toString();
        }
      }

The error results from ResourceBundle.getBundle's inability to find the
above named resource.

I've double- and tripled-checked and the above resource is present in
my PEAR file under the "resources" sub-directory. However, there is a
wrinkle: my application dynamically creates the class path used by the
annotators. This class path is then specified to ResourceManager to
make it's so-called "extension" class path. So I load all the
annotators from the "extension" ClassLoader rather than the "system"
ClassLoader. I have verified that I can successfully load the resource
directly from that extension ClassLoader. The problem seems to lie in
how the JDK's ResourceBundle finds a given resource when no ClassLoader
is explicitly specified in which to find the resource. When no
ClassLoader is provided to ResourceBundle.getBundle, the ClassLoader is
determined thusly:

    /*
     * Automatic determination of the ClassLoader to be used to load
     * resources on behalf of the client.  N.B. The client is 
     * getLoader's caller's caller.
     */
    private static ClassLoader getLoader() {
        Class[] stack = getClassContext();
        /* Magic number 2 identifies our caller's caller */
        Class c = stack[2];
        ClassLoader cl = (c == null) ? null : c.getClassLoader();
        if (cl == null) {
            cl = ClassLoader.getSystemClassLoader();
        }
        return cl;
    }

So it assumes that the resource can be found using the ClassLoader that
loaded the calling class' - org.apache.uima.InternationalizedException.
In this case, the ClassLoader that loaded InternationalizedException is
the "system" ClassLoader, not the "extension" ClassLoader, thus the
resource is not found and the error message shown above is displayed.

ResourceBundle's getBundle method takes an optional ClassLoader. It
seems like changing or augmenting InternationalizedException's
getLocalizedMethod method to check also using the ResourceManager's
ClassLoader would then find the resource.

Thoughts?

Thanks,
Kirk

Reply via email to