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

发布于 2024-08-02 12:18:59 字数 1714 浏览 3 评论 0原文

我维护一个小程序,帮助用户将照片上传到我们的服务。小程序 jar 文件有一些 .properties 文件:

>> jar -tf applet.jar | grep prop
res/messages.properties
res/messages_ca.properties
res/messages_es.properties
...

这些文件在小程序初始化期间加载。

messages = ResourceBundle.getBundle("res.messages");

然而,此调用会向服务器生成 4 到 5 个请求,查找 jar 文件中没有的文件,然后再返回到 .properties 文件中包含的 .properties 文件。代码>.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

ResourceManager.getBundle 解释说这是完成的方式:

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

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

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

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

有什么方法可以教小程序仅在 .jar 文件中查找这些文件吗?

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

I maintain an applet that helps users to upload photos to our service. The applet jar file has a few .properties files:

>> jar -tf applet.jar | grep prop
res/messages.properties
res/messages_ca.properties
res/messages_es.properties
...

These are loaded during applet initialization.

messages = ResourceBundle.getBundle("res.messages");

This call however generates 4 to 5 requests to the server looking for files that aren't in the jar file, before falling back on to the .properties file included in the .jar.

From the server error log:

[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

The documentation for ResourceManager.getBundle explains that this is the way it is done:

getBundle then iterates over the candidate bundle names to find the first one for which it can instantiate an actual resource bundle. For each candidate bundle name, it attempts to create a resource bundle:

  • First, it attempts to load a class using the candidate bundle name. If such a class can be found and loaded using the specified class loader, is assignment compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated, getBundle creates a new instance of this class and uses it as the result resource bundle.

  • Otherwise, getBundle attempts to locate a property resource file. It generates a path name from the candidate bundle name by replacing all "." characters with "/" and appending the string ".properties". It attempts to find a "resource" with this name using ClassLoader.getResource.

I am sure there are good reasons for doing it this way, but in my case it seems wasteful to me that there should be five failed requests for files which are known to be non-existent on the server.

Is there any way to teach the applet to look for these files in the .jar file only?

Note: I am not a Java programmer, so if there is a better way to load properties than ResourceManager.getBundle, please let me know.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浪荡不羁 2024-08-09 12:18:59

Java 1.6 引入了 ResourceBundle.Control类,如果您不支持 Java 1.4,它可能会提供一些帮助。就您而言,编写自己的包管理器并不是什么复杂的事情。

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

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

由于语言集是 escaCustomManager 仅支持语言(不支持国家/地区代码或变体),只能加载以下文件:

l10n/res/foo.properties
l10n/res/foo_es.properties
l10n/res/foo_ca.properties

您希望使用 语言环境ClassLoader 支持,以及您想要在哪里管理语言列表,都取决于您。

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

Java 1.6 introduced the ResourceBundle.Control class, which might have offered some help if you weren't supporting Java 1.4. As you are, it isn't rocket science to write your own bundle manager.

This demo code restricts bundle loading to properties files in a given set of languages:

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

This code simulates a call to retrieve strings for the Spanish/Spain locale:

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

Since the set of languages is es and ca and CustomManager supports only languages (not country codes or variants) only the following files could be loaded:

l10n/res/foo.properties
l10n/res/foo_es.properties
l10n/res/foo_ca.properties

How sophisticated you want to get with Locale and ClassLoader support, and where you want to manage your language list, is up to you.

Caveat: I don't think I violated any security restrictions with the implementation, but I only tested the code in a desktop app.

寄风 2024-08-09 12:18:59

只需在 标记配置中将 codebase_lookup 属性设置为 false,您的小程序就不会再对服务器进行任何其他调用资源(例如 i18n 资源包)。

<applet codebase_lookup="false" ...

有关更多详细信息,请参阅 http://docs。 oracle.com/javase/6/docs/technotes/guides/plugin/developer_guide/special_attributes.html#codebase

Just set the codebase_lookup attribute to false in your <applet> tag configuration and your applet won't do any additional calls to the server for resources (e.g. i18n resource bundles).

<applet codebase_lookup="false" ...

More details on http://docs.oracle.com/javase/6/docs/technotes/guides/plugin/developer_guide/special_attributes.html#codebase.

依 靠 2024-08-09 12:18:59

Jaime Hablutzel 解决方案效果很好。
遗憾的是,我还没有足够的声誉来投票。

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

Jaime Hablutzel solution works fine.
It's a pity, that I still don't have enough reputation to vote it up.

Setting codebase_lookup to false when you instanciate the applet tells the classloader precisely that the applet is deployed with all the needed classes and resources within the jar files and to avoid the unnecessary codebase lookup.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文