How to reload resource bundle in web application?
Asked Answered
M

3

11

I have resource bundle as Java class that read values from database. When i update db i need to reload bundle, but i don't know how. Anybody helps ?

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

In view i use bundle as below:

<f:loadBundle basename="model.helpers.Messages" var="m" />
Marcille answered 1/12, 2010 at 14:8 Comment(0)
N
5
ResourceBundle.clearCache();     

OR

Messages_en .clearCache();

Calling this method will reload the resources, it will refresh the bundle

Neper answered 1/12, 2010 at 14:10 Comment(3)
I notice, that when i have message bundle defined in faces-config.xml instead of using f:loadBundle - reloading doesn't work. Anybody know why ?Marcille
this may of worked once upon a time, but it doesn't on my wildfly 8->10 installations, even passing class loaders etc, it's cached and gets reloaded with the original data from that ache.Satisfied
Works for me with Tomcat to reload properties files in an exploded (aka. unzipped) webapp. Thanks!Lobectomy
S
12

This is not exactly trivial.

For one just clearing the ResourceBundle via clearCache() doesn't always yield the desired results. Often you need at least also try to clear using the context class loader:

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

This however will still not reload the resource bundle defined in a faces-config.xml file. At least the Mojarra JSF 1.2 implementation privately caches the resource bundle internally. This happens in:

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

It's possible to clear this cache via reflection (at the end of the day, it's just an entry in a Map), or you might wanna replace the Application. Both are not things you normally do lightheartedly.

Purely for development you could use JRebel, which probably already has knowledge of Mojarra and most likely does the reflection trick mentioned above.

After some experimenting, I came to the following code which does the trick on JBoss AS 5/JSF 1.2. It does tie your code to Mojarra (imports sun packages) and can break with any upgrade because of reflective tricks being used. But anyway, this is the code:

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

(replace the getFieldValue helper method with your own favorite reflective util if necessary and sprinkle with exception and null handlers where appropriate)

Stovall answered 22/1, 2011 at 22:19 Comment(3)
hi arjan, glad to know about this teak, I played around, didn't work for me for GlassFish only,I tried using both view level resource bundle and application level , both failed using this tweak in GF envNeper
What version of Glassfish was that? If it's V3 then I guess it might indeed not work since the version of Mojarra is totally different. I happened to be working on a JBoss AS 5 project so I only tested it there, but it might also not work on JBoss AS 6.Stovall
its V3, any idea how to make work around in V3, and system will notify if you start your comment with @usernameNeper
N
5
ResourceBundle.clearCache();     

OR

Messages_en .clearCache();

Calling this method will reload the resources, it will refresh the bundle

Neper answered 1/12, 2010 at 14:10 Comment(3)
I notice, that when i have message bundle defined in faces-config.xml instead of using f:loadBundle - reloading doesn't work. Anybody know why ?Marcille
this may of worked once upon a time, but it doesn't on my wildfly 8->10 installations, even passing class loaders etc, it's cached and gets reloaded with the original data from that ache.Satisfied
Works for me with Tomcat to reload properties files in an exploded (aka. unzipped) webapp. Thanks!Lobectomy
H
1

You can even avoid to have to import weld and jsf-impl classes in your module with some more lines of reflection:

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

(works well with Wildfly 10)

Hindermost answered 16/11, 2016 at 16:14 Comment(1)
This worked well for me in Wildfly 8, only thing that cleared that stupid cache...Satisfied

© 2022 - 2024 — McMap. All rights reserved.