Pregunta

I have to rely on a third party library that expects certain things to be in the classpath. It seems to use

ResourceBundle.getBundle("cmbcmenv");

to get a property file. The location of this file is specified by an environment variable set when installing the connector (it creates a variable "IBMCMROOT" which points to the directory containing the file). I can't use relative or absolute paths in my classpath, since the installation directory of the connector may vary.

I've tried adding a new ClassLoader manually like this:

ClassLoader defaultLoader = Thread.currentThread().getContextClassLoader();     
        String config = System.getenv().get("IBMCMROOT")+"/cmgmt";      
        URLClassLoader loader = URLClassLoader.newInstance(new URL[]{new File(config).toURI().toURL()},defaultLoader);
        Thread.currentThread().setContextClassLoader(loader);           

Which also fails on getBundle. Since it's a third party library, I can't use

ResourceBundle.getBundle("cmbcmenv",Locale.getDefault(),loader);

Wouldn't have this problem otherwise..

How can I force ResourceBundle to use a specific ClassLoader? It seems that getBundle uses either CallerClassloader or SystemClassloader but there's not way to influence that, right?

¿Fue útil?

Solución

You can trick the ResourceBundle class to cache a bundle instance using a different ClassLoader than used for the actual lookup in the key.

ResourceBundle rb=ResourceBundle.getBundle("Resources", new ResourceBundle.Control() {
  @Override
  public long getTimeToLive(String baseName, Locale locale) {
    return Long.MAX_VALUE;
  }
  @Override
  public ResourceBundle newBundle(String baseName, Locale locale,
      String format, ClassLoader loader, boolean reload)
      throws IllegalAccessException, InstantiationException, IOException {
    return super.newBundle(baseName, locale, format, defaultLoader, reload);
  }
});

Note that the getBundle invocation is using the default SystemClassLoader (or caller ClassLoader) as no class loader is specified. This is the ClassLoader used in the key for caching the result. But the custom Control implementation replaces the ClassLoader with your defaultLoader instance when looking up the real bundle.

Note that you should perform this action right before the lookup action of the 3rd party library. This control says that the bundle should never expire but it might still get freed when the JVM is low on memory.

Note that ResourceBundle also caches failures so this trick must be applied before someone else tries to lookup that bundle.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top