简体   繁体   English

在OSGi包中加载ResourceBundle

[英]Loading a ResourceBundle within an OSGi bundle

I have an application that uses java.util.ResourceBundle to handle display strings. 我有一个使用java.util.ResourceBundle来处理显示字符串的应用程序。 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). 我使用ListResourceBundle作为基类型,因为我希望能够返回一些属性键的字符串数组(大多数是标准名称/值配对)。

When I'm trying to load the ResourceBundle in the OSGi environment I get a MissingResourceException. 当我尝试在OSGi环境中加载ResourceBundle时,我得到一个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. 我正在使用区域设置(en_AU)但没有为该区域设置提供特定的类,回退规则应该选择我的默认实现。 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. 这使用自定义服务接口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. 我进入DefaultDisplayNameProvider,所以我知道正在找到资源。 I know the class is in the bundle (it is stored in the internal bundle classes but is visible to the local class loader). 我知道类在bundle中(它存储在内部bundle类中,但对本地类加载器是可见的)。

My question is what do I need to do load my ResourceBundle's up within OSGi? 我的问题是我需要在OSGi中加载我的ResourceBundle? I'd prefer to continue with this approach rather than start using Resource Fragments or hard-coding string names. 我宁愿继续使用这种方法,而不是开始使用资源片段或硬编码字符串名称。

Solution

Both of the advice below to help with the ClassLoader was useful but it turns out my problem was much simpler. 以下两个有关ClassLoader帮助的建议都很有用,但事实证明我的问题要简单得多。 Don't use toString() to get the class name (which I should've known but must have been lazy that day). 不要使用toString()来获取类名(我应该知道但当天一定是懒惰的)。 The correct method to use is Class.getName(). 要使用的正确方法是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. 我会坚持使用String版本的资源名称。 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). 这意味着JVM不必在实际请求之前加载类(并不是说此时要做很多事情)。

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. 在我的脑海中:你可能想尝试ResourceBundle.getBundle(name,locale,classloader)方法,因为ResourceBundle不知道要搜索哪个类加载器。 You can get the classloader of the DefaultDisplayNameProvider with getClass().getClassLoader() . 您可以使用getClass().getClassLoader()获取DefaultDisplayNameProvider的类加载器。

regards, Frank 问候,弗兰克

You should always use the getBundle methods that take a ClassLoader parameter. 您应该始终使用带有ClassLoader参数的getBundle方法。 Calling the other versions of this method are pretty much as bad as calling the single-argument Class.forName ... ie, you're forcing the Java runtime to guess which classloader contains the resources. 调用此方法的其他版本与调用单参数Class.forName非常相似...即,您迫使Java运行时猜测哪个类加载器包含资源。

I really don't understand why ResourceBundle forces us to pass a class name rather than a Class object . 我真的不明白为什么ResourceBundle强制我们传递类而不是Class 对象 It's just another example of poor design in the Java standard libraries, I'm afraid. 这只是Java标准库中设计糟糕的另一个例子,我担心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM