2009-08-19 1 views
4

Я поддерживаю апплет, который помогает пользователям загружать фотографии на наш сервис. Файл апплет jar имеет несколько .properties файлов:Как предотвратить запросы на сервер при загрузке свойств, которые уже находятся в файле jar-файла апплета?

>> 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 включен в .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, пожалуйста, дайте мне знать.

+0

Какая у вас минимальная версия Java? – McDowell

+0

1.4 и выше - это то, что нам нужно поддерживать. Ваш ответ зависит от этой информации? В любом случае, ответьте. Если ваше решение недействительно для более старых версий, все равно будет полезно, если только добавить в список причин, почему мы должны прекратить поддержку 1.4 :) –

ответ

5

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

Поскольку набор языков es и ca и CustomManager поддерживает только языки (не кодовые коды или варианты), только следующие файлы могут быть загружены:

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

Как изощренными вы хотите получить с поддержкой Locale и ClassLoader, и где вы хотите, чтобы управлять списком языков, до вас.

Предостережение: я не думаю Я нарушил любые ограничения безопасности с реализацией, но я только протестировал код в приложении для настольных компьютеров.

+0

@McDowell: Спасибо, что нашли время ответить. Я попробую этот подход. –

+0

Спасибо за это решение, небольшое улучшение было бы: \t \t if (languages.contains (locale.getLanguage())) name = name + "_" + locale.getLanguage(); else if (languages.contains (locale.toString())) \t name = name + "_" + locale.toString(); –

+0

Обратите внимание, что с этим решением возникает проблема, если вы ожидаете, что ваши апплеты будут работать с плагином IcedTea-Web, это не будет работать для Java-кода, инициированного с помощью вызова JavaScript (LiveConnect), как «Thread.currentThread(). GetContextClassLoader () 'не вернет ваш загрузчик класса апплета, но один загрузчик классов не сможет загрузить ваши ресурсы. Мой совет, придерживайтесь стандартного 'java.util.ResourceBundle # getBundle' и просто отключите' codebase_lookup' для предотвращения вызовов на сервер. –

0

Просто установите атрибут falsecodebase_lookup в конфигурации <applet> тегов и ваш апплет не будет делать никаких дополнительных вызовов на сервер для ресурсов (например, пучки i18n ресурсов).

<applet codebase_lookup="false" ... 

Подробнее о http://docs.oracle.com/javase/6/docs/technotes/guides/plugin/developer_guide/special_attributes.html#codebase.

0

Решение Jaime Hablutzel прекрасно работает. Жаль, что у меня до сих пор нет достаточной репутации, чтобы проголосовать за нее.

Установка кодаbase_lookup на false при запуске апплета сообщает загрузчику классов именно то, что апплет развернут со всеми необходимыми классами и ресурсами в файлах jar и во избежание ненужного поиска в кодовой базе.

Смежные вопросы