Как перезагрузить пакет ресурсов в веб-приложении?

У меня есть пакет ресурсов как класс Java, который считывает значения из базы данных. Когда я обновляю db, мне нужно перезагрузить пакет, но я не знаю, как это сделать. Кто-нибудь помогает?

package model.helpers;
public class Messages_en extends ListResourceBundle {
      protected Object[][] getContents() {
            // from DB
            // ...
      }
}

В поле зрения я использую пакет, как показано ниже:

<f:loadBundle basename="model.helpers.Messages" var="m" />

Ответ 1

ResourceBundle.clearCache();     

ИЛИ

Messages_en .clearCache();

Вызов этого метода перезагрузит ресурсы, он обновит пакет

Ответ 2

Это не совсем тривиально.

Для простое удаление ResourceBundle через clearCache() не всегда дает желаемые результаты. Часто вам нужно, по крайней мере, также попытаться очистить загрузчик контекстного класса:

ResourceBundle.clearCache(Thread.currentThread().getContextClassLoader());

Однако это все равно не перезагрузит пакет ресурсов, определенный в файле faces-config.xml. По крайней мере, реализация Mojarra JSF 1.2 конфиденциально кэширует пакет ресурсов внутри компании. Это происходит в:

FacesContext -> Application -> associate (ApplicationAssociate) -> resourceBundles (Map<String, ApplicationResourceBundle>()) -> resources (Map<Locale, ResourceBundle>) 

Можно очистить этот кеш через отражение (в конце дня это всего лишь запись на карте), или вы можете заменить приложение. Оба - это не то, что вы обычно делаете беззаботно.

Чисто для развития вы могли бы использовать JRebel, который, вероятно, уже знает Mojarra и, скорее всего, делает трюк с отражением, упомянутый выше.

После некоторых экспериментов я пришел к следующему коду, который делает трюк на JBoss AS 5/JSF 1.2. Он привязывает ваш код к Mojarra (импортирует пакеты солнца) и может сломаться при любом обновлении из-за использования рефлексивных трюков. Но в любом случае это код:

public static void reloadBundle() {

    ResourceBundle.clearCache(Thread.currentThread().getContextClassLoader());

    ApplicationResourceBundle appBundle = ApplicationAssociate.getCurrentInstance().getResourceBundles().get("your_bundle_name");               
    Map<Locale, ResourceBundle> resources = getFieldValue(appBundle, "resources");          
    resources.clear();
}

@SuppressWarnings("unchecked")
private static <T> T getFieldValue(Object object, String fieldName) {
    try {
        Field field = object.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return (T) field.get(object);
    } catch (Exception e) {
        return null;
    }       
}

(замените вспомогательный метод getFieldValue с помощью собственного любимого рефлексивного использования, если это необходимо, и по возможности посыпать исключениями и нулевыми обработчиками)

Ответ 3

Вы даже можете избежать импорта классов сварки и jsf-impl в свой модуль с еще несколькими линиями отражения:

Class<?> applicationAssociateClass = Class.forName("com.sun.faces.application.ApplicationAssociate");
Method getCurrentInstance = applicationAssociateClass.getMethod("getCurrentInstance");
Object applicationAssociate = getCurrentInstance.invoke(null);
Method getResourceBundles = applicationAssociate.getClass().getMethod("getResourceBundles");
Map<String, ?> resourceBundles = (Map<String, ?>)getResourceBundles.invoke(applicationAssociate);
Object appBundle = resourceBundles.get(name);
Map<Locale, ResourceBundle> resources = getFieldValue(appBundle, "resources");
resources.clear();

(хорошо работает с Wildfly 10)