Search code examples
jsfserializationjsf-2.2resourcebundletransient

Accessing ResourceBundle as ManagedProperty Serialization Issue


first of all, sorry for my bad english!

in the following managed Bean (ApplicationScoped), i access a ResourceBundle(.properties) as a @ManagedProperty. a ResourceBundle Object is not serializable, so i get in the Eclipse/Tomcat Console an Error saying that this object cannot be serialized/de-serialized.. etc.

Exception loading sessions from persistent storage java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: java.util.PropertyResourceBundle

i have 2 Questions to this issue:

  • i think, JSF handles pre-defined(in faces-config.xml) ResourceBundles as ApplicationScoped beans. this means(if i understanding this correctly), this Object/Bean (ResourceBundle) is been stored somewhere somehow in a file (persistent storage). Now and since ResourceBundle is not serializable, then in which format is it been stored? and how JSF serves such "Beans"? serialized Objects are stored in files as Bytes, so how not serializable Objects are stored?
  • in the following example, i would declare my @ManagedProperty ResourceBundle as transient (due to serialization problem), but transient objects won't be stored in persistent storage (stateless), does this mean that with every call of the method getConfigurationAttribute(where i use this resourceBundle) will recreate/reload the ManagedPropery ResourceBundle since it is marked as transient?

Your help is greatly appreciated.

@ManagedBean(name="facesResource",eager=true)
@ApplicationScoped
public class FacesResource implements Serializable{
    private static final long serialVersionUID = 2454454363100273885L;
    @ManagedProperty("#{FACES_CONFIG}")
    private ResourceBundle facesConfig;
    //private transient ResourceBundle facesConfig;

    ....
    private Map<String,Language> languagesMap;  
    private Map<String,Theme> themesMap;
    ....

    public FacesResource(){

    }
    @PostConstruct
    public void init(){
        System.out.println("*** FacesResource init ....");
        try{
            ....
            this.initLanguages();
            this.initThemes();
            ....
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }       

    public String getConfigurationAttribute(String attributeKey){
        return this.facesConfig.getString(attributeKey);
    }
    // ... other methods & getter/setter ...etc

}

UPDATE:

  • the ResourceBundle in the FacesResource Bean is independent of the Request Locale, so its not a problem to load it in an ApplicationScoped Bean, BUT

  • since i access/inject(as @ManagedProperty) this ApplicationScoped Bean in other SessionScoped Beans, which should be serialized, which means, that all attributes (resourceBundle included) should be serialized too, and here i got the Problem with Serialization/Deserializazion

  • @BalusC: if i do like you suggest in your answer: ResourceBundle.getBundle("com.example.text"), i have to provide the baseName of the Bundle. BUT this is exactly what i want to avoid. i don't want to hardcode static Paths in java Source Codes), so that when the path changes(most unlikely, but for the Case), i don't like to change paths in Java Source Codes but only in faces-config.xml.

  • and i cannot use FacesContext.getCurrentInstance().getApplication().getResourceBundle(facesContext, "bundleVarName"); because my Bean is marked with eager=true, which means, the facesContext is NULL at this moment!


Solution

  • i think, JSF handles pre-defined(in faces-config.xml) ResourceBundles as ApplicationScoped beans.

    Nope. They are managed by ResourceBundle API itself. JSF just resolves them on a per-request basis based on the requested locale (otherwise it would affect the language of any user visiting the web application!). So, they are essentially request scoped. But this all has further nothing to do with serialization. The ResourceBundle class is simply never intented to be serializable. It just lazily loads the bundles in Java's memory.

    You'd best just do the same. Lazy loading it if it becomes null after deserialization. You only shouldn't evaluate #{FACES_CONFIG}, because it would be dependent on request locale. Provided that you can only use JSF <resource-bundle><var>, then you'd best load them via Application#getResourceBundle(). Provided a resource bundle var name of FACES_CONFIG, here's an example:

    private transient ResourceBundle facesConfig;
    
    public ResourceBundle getFacesConfig() {
        if (facesConfig == null) {
            FacesContext context = FacesContext.getCurrentInstance();
            facesConfig = context.getApplication().getResourceBundle(context, "FACES_CONFIG");
        }
    
        return facesConfig; 
    }
    

    By the way, the variable name facesConfig is very confusing. It namely suggests that it represents the contents of faces-config.xml.

    See also: