Java 在加载小程序jar文件中已有的属性时,如何防止对服务器的请求?

Java 在加载小程序jar文件中已有的属性时,如何防止对服务器的请求?,java,properties,jar,applet,Java,Properties,Jar,Applet,我维护了一个小程序,帮助用户将照片上传到我们的服务中。小程序jar文件有几个属性文件: >> jar -tf applet.jar | grep prop res/messages.properties res/messages_ca.properties res/messages_es.properties ... 这些将在小程序初始化期间加载 messages = ResourceBundle.getBundle("res.messages"); 但是,在

我维护了一个小程序,帮助用户将照片上传到我们的服务中。小程序
jar
文件有几个
属性文件:

>> jar -tf applet.jar | grep prop
res/messages.properties
res/messages_ca.properties
res/messages_es.properties
...
这些将在小程序初始化期间加载

messages = ResourceBundle.getBundle("res.messages");
但是,在返回到
.jar
中包含的
.properties
文件之前,此调用会向服务器生成4到5个请求,以查找不在
jar
文件中的文件

从服务器错误日志:

[error] File does not exist: /photos/res/messages.class
[error] File does not exist: /photos/res/messages_en.class
[error] File does not exist: /photos/res/messages_en.properties
[error] File does not exist: /photos/res/messages_en_US.class
[error] File does not exist: /photos/res/messages_en_US.properties
的文档说明了这是执行此操作的方式:

然后,getBundle迭代候选bundle名称,以找到它可以实例化实际资源bundle的第一个名称。对于每个候选捆绑包名称,它将尝试创建一个资源捆绑包:

  • 首先,它尝试使用候选包名称加载类。如果可以使用指定的类加载器找到并加载这样的类,分配与ResourceBundle兼容,可以从ResourceBundle访问,并且可以实例化,则getBundle将创建该类的新实例并将其用作结果资源束

  • 否则,getBundle将尝试定位属性资源文件。它通过将所有“.”字符替换为“/”并附加字符串“.properties”,从候选包名称生成路径名。它尝试使用ClassLoader.getResource查找具有此名称的“资源”

我相信这样做是有充分理由的,但在我的情况下,对于服务器上已知不存在的文件,应该有五个失败的请求,这对我来说似乎是浪费

有没有办法教小程序只在
.jar
文件中查找这些文件

注意:我不是Java程序员,因此如果有比
ResourceManager.getBundle
更好的加载属性的方法,请告诉我。

Java 1.6介绍了该类,如果您不支持Java 1.4,它可能会提供一些帮助。就您而言,编写自己的bundle管理器并不是什么了不起的事

此演示代码将捆绑包加载限制为使用给定语言集的属性文件:

public class CustomManager {
  private static final Map BUNDLES = new HashMap();

  public static ResourceBundle getBundle(String name, Set languages) {
    Locale locale = Locale.getDefault();
    if (languages.contains(locale.getLanguage())) {
      name = name + "_" + locale.getLanguage();
    }
    synchronized (BUNDLES) {
      ResourceBundle bundle = (ResourceBundle) BUNDLES.get(name);
      if (bundle == null) {
        ClassLoader loader = getContextClassLoader();
        bundle = loadBundle(loader, name.replace('.', '/') + ".properties");
        BUNDLES.put(name, BUNDLES);
      }
      return bundle;
    }
  }

  private static ClassLoader getContextClassLoader() {
    return Thread.currentThread().getContextClassLoader() != null ? Thread
        .currentThread().getContextClassLoader() : CustomManager.class
        .getClassLoader() != null ? CustomManager.class.getClassLoader()
        : ClassLoader.getSystemClassLoader();
  }

  private static ResourceBundle loadBundle(ClassLoader loader, String res) {
    try { InputStream in = loader.getResourceAsStream(res);
      try { return new PropertyResourceBundle(in);
      } finally { in.close(); }
    } catch (IOException e) { throw new IllegalStateException(e.toString()); }
  }
}
此代码模拟检索西班牙语/西班牙地区字符串的调用:

Set languages = new HashSet(Arrays.asList(new String[] { "es", "ca" }));
Locale.setDefault(new Locale("es", "ES"));
ResourceBundle bundle = CustomManager.getBundle("l10n.res.foo", languages);
System.out.println(bundle.getString("bar"));
由于语言集为
es
ca
CustomManager
仅支持语言(不支持国家代码或变体),因此只能加载以下文件:

l10n/res/foo.properties
l10n/res/foo_es.properties
l10n/res/foo_ca.properties
您希望获得的技术和支持有多成熟,以及您希望在哪里管理您的语言列表,这取决于您自己


警告:我认为我的实现没有违反任何安全限制,但我只是在桌面应用程序中测试了代码。

只需在
Jaime Hablutzel解决方案中将
codebase\u lookup
属性设置为
false
。 遗憾的是,我还没有足够的声誉去投票支持它


当实例化小程序时,将codebase_lookup设置为false会准确地告诉类加载器小程序已在jar文件中部署了所有需要的类和资源,以避免不必要的代码库查找。

您的最低Java版本是什么?1.4及以上版本是我们需要支持的。你的答案取决于这些信息吗?请回复。如果您的解决方案不适用于旧版本,那么它仍然会很有用,如果只是将我们停止支持1.4的原因添加到列表中:)@McDowell:感谢您花时间回复。我将尝试这种方法。感谢这个解决方案,有一点改进是:if(languages.contains(locale.getLanguage()))name=name+“”+locale.getLanguage();如果(languages.contains(locale.toString()))name=name+“”+locale.toString();请注意,此解决方案有一个问题,如果您希望您的小程序与IcedTea Web插件一起工作,则这对从JavaScript调用(LiveConnect)启动的Java代码不起作用,因为
Thread.currentThread().getContextClassLoader()
不会返回小程序类加载器,但一个类加载器无法加载您的资源。我的建议是,坚持使用标准的
java.util.ResourceBundle#getBundle
,只需禁用
codebase\u查找
,即可防止对服务器的调用。
<applet codebase_lookup="false" ...