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));