views:

269

answers:

3

Given a ResourceBundle object, is there a way to get a URL or other indicator as to where it was found when it was looked up?

The ResourceBundle is being created with:

ResourceBundle rb = ResourceBundle.getBundle("filename");

And it is being loaded from a file subdir/filename.properties, in most cases. I'd like to be able to programmatically refer to this location, rather than hardcoding it.

+1  A: 

Judging from the documentation, you can't, because it's a class that can be subclassed in arbitrary ways (not necessarily associated with a URL or classname) and there's no linkage back to the creator.

Also note the constructor for PropertyResourceBundle: it takes an InputStream which can be obtained anywhere: you can provide your own InputStream by subclassing it, to provide arbitrary data that is generated out of thin air.

Jason S
+1  A: 

Try this:

URL location = getClass().getClassLoader().getResource("filename.properties");

That's the mechanism used by ResourceBundle to find the resource. Check out the [Javadoc for ResourceBundle][1] to see the specifics of how it maps a bundle name to an underlying resource. You may need to be a bit more clever if you have locales and things involved.

[1]: http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle%28java.lang.String, java.util.Locale, java.lang.ClassLoader, java.util.ResourceBundle.Control)

monorailkitty
that's how you get a ResourceBundle given a Class. OP is asking how to obtain a file/class/URL given a ResourceBundle, which is swimming upstream rather than downstream.
Jason S
Thanks! That's pretty much what I did. Then, because I had control of how I was getting the ResourceBundle in the first place, I went back and pitched it and just loaded a Properties object from that URL, reusing the URL later when I needed to get to the file: new File(location.toURI())
skiphoppy
+1  A: 

If you are able to modify all invokations of ResourceBundle.getResource, you can pass your own implementation of ResourceBundle.Control to cache the URL, from which the resolved bundle is actually loaded

public class ResourceBundleSourceControl extends ResourceBundle.Control {

private static final Map<ResourceBundle, URL> sources = 
 new HashMap<ResourceBundle, URL>();  

private String bundleName;

@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format,
  ClassLoader loader, boolean reload) throws IllegalAccessException,
  InstantiationException, IOException {
 ResourceBundle rb =  super.newBundle(baseName, locale, format, loader, reload);
 URL sourceURL = loader.getResource(toResourceName(bundleName, "properties"));
 if(rb != null && sourceURL != null) {
  synchronized(sources) {
   sources.put(rb, sourceURL);
  }
 }
 return rb;
}

@Override
public String toBundleName(String arg0, Locale arg1) {
 bundleName = super.toBundleName(arg0, arg1);
 return bundleName;
}

public static URL getSourceURL(ResourceBundle b) {
 synchronized (sources) {
  return sources.get(b);    
 }
}

The drawback is, that you have to pass an instance of this class to each invocation of ResourceBundle.getBundle. Since the resource bundles are lazily cached, you can not be sure that each retrieval of the bundle will actually load it, which is necessary for the Control implementation to catch it.

After loading the bundle, you can use the static getSourceURL method, to find the URL from which it was actually loaded:

ResourceBundle rb = ResourceBundle.getBundle(
    "props.Default", new ResourceBundleSourceControl()); 
System.out.println(
    "URL: " + ResourceBundleSourceControl.getSourceURL(rb));
jarnbjo