I have an application that uses java.util.ResourceBundle to handle display strings. I use the ListResourceBundle as a base type because I want to be able to return an array of strings for some property keys (most of them are the standard name/value pairing).
When I'm trying to load the ResourceBundle in the OSGi environment I get a MissingResourceException. I can't figure out why this would occur. I can resolve the class name and make a new instance of the class in the two statements before I try to fetch the bundle, so I know the class is in the bundle. I'm using a locale (en_AU) but not providing a specific class for that locale, the fallback rules should select my default implementation. Even when I added a class for the locale it didn't resolve.
The process I go through to fetch data is:
The service
public interface NameManager {
public String getCategoryName(String identifier, Locale locale);
}
The implementation of this
public class OsgiNameManager implements NameManager {
public String getCategoryName(@Nonnull Identifier category, Locale locale)
{
try {
Collection<ServiceReference> references = (Collection)osgiContext.getServiceReferences(
DisplayNameProvider.class, "(namespace=" + category + ")");
Iterator<ServiceReference> it = references.iterator();
while ( it.hasNext() ) {
ServiceReference reference = (ServiceReference)it.next();
DisplayNameProvider namer = (DisplayNameProvider)osgiContext.getService(reference);
String name = namer.getCategoryName(category, locale);
osgiContext.ungetService(reference);
if (name != null)
return name;
}
}
catch (InvalidSyntaxException badFormat) {
LOG.warn("No registered provider for category [" + category + "]" , badFormat);
}
return null;
}
}
This uses a custom service interface, DisplayNameProvider. Modules that wish to register names add this as a service in their activator. The default implementation takes a class for the fully qualified resource bundle name.
public class DefaultDisplayNameProvider implements DisplayNameProvider {
private Class<?> categoryResolver;
public String getCategoryName(@Nonnull String category, Locale locale)
{
ResourceBundle res = ResourceBundle.getBundle(categoryResolver.toString(), locale);
try {
return res.getString(CATEGORY_NAME_KEY);
}
catch (MissingResourceException noCols) {
return null;
}
}
}
(Note, I've left out some fields and static references, but this is the gist of what is done).
I'm getting inside of the DefaultDisplayNameProvider so I know the resource is being found. I know the class is in the bundle (it is stored in the internal bundle classes but is visible to the local class loader).
My question is what do I need to do load my ResourceBundle's up within OSGi? I'd prefer to continue with this approach rather than start using Resource Fragments or hard-coding string names.
Both of the advice below to help with the ClassLoader was useful but it turns out my problem was much simpler. Don't use toString() to get the class name (which I should've known but must have been lazy that day). The correct method to use is Class.getName().
This was found by noticing that if I used a properties file it loaded correctly. Then if I typed the class name in also worked. It was only when I was trying to reduce text entry error by using the existing class reference to get the correct name.
I'll stick with the String versions of the resource names. This means the JVM doesn't have to load the class till actually requested (not that there is a lot for it to do at this time).
Off the top of my head: You might want to try the ResourceBundle.getBundle(name,locale,classloader)
method, as ResourceBundle
wouldn't know which classloader to search. You can get the classloader of the DefaultDisplayNameProvider
with getClass().getClassLoader()
.
regards, Frank
You should always use the getBundle
methods that take a ClassLoader
parameter. Calling the other versions of this method are pretty much as bad as calling the single-argument Class.forName
... i.e., you're forcing the Java runtime to guess which classloader contains the resources.
I really don't understand why ResourceBundle forces us to pass a class name rather than a Class
object. It's just another example of poor design in the Java standard libraries, I'm afraid.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With