Accessing ResourceBundle as ManagedProperty Serialization Issue - jsf

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!

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:
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException

You can't use #ManagedProperty on a not Serializable Type.
Is it a resource bundle for localized Strings?
Read this: http://www.mkyong.com/jsf2/jsf-2-0-and-resource-bundles-example/
FacesContext facesContext = FacesContext.getCurrentInstance();
ResourceBundle resourceBundle = facesContext.getApplication()
.getResourceBundle(facesContext, "bundleName");

Related

Non-lazy instantiation of CDI SessionScoped beans

CDI newbie question. Simple test scenario: JSF + CDI SessionScoped beans.
I need an elegant way to instantiate a known set of session scoped CDI beans without mentioning them on a JSF page or calling their methods from other beans. As a test case - a simple logging bean, which simply logs start and end time of an http session.
Sure, I could create an empty JSF component, place it inside of a site-wide template and make it trigger dummy methods of the required session beans, but it's kinda ugly from my pov.
Another option I see, is to choose a single session bean (which gets initialized 100% either by EL in JSF or by references from other beans), and use its #PostConstruct method to trigger other session beans - the solution a little less uglier than the previous one.
Looks like I'm missing something here, I'd appreciate any other ideas.
While accepting the Karl's answer and being thankful to Luiggi for his hint, I also post my solution which is based on HttpSessionListener but does not require messing with BeanProvider or BeanManager whatsoever.
#WebListener
public class SessionListener implements HttpSessionListener {
#Inject
Event<SessionStartEvent> startEvent;
#Inject
Event<SessionEndEvent> endEvent;
#Override
public void sessionCreated(HttpSessionEvent se) {
SessionStartEvent e = new SessionStartEvent();
startEvent.fire(e);
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
SessionEndEvent e = new SessionEndEvent();
endEvent.fire(e);
}
}
To my surprise, the code above instantiates all the beans which methods are observing these events:
#Named
#SessionScoped
public class SessionLogger implements Serializable {
#PostConstruct
public void init() {
// is called first
}
public void start(#Observes SessionStartEvent event) {
// is called second
}
}
Yes, HttpSessionListener would do it. Simply inject the beans and invoke them.
If you container does not support injection in a HttpSessionListener you could have a look at deltaspike core and BeanProvider
http://deltaspike.apache.org/core.html

Timing out from a bean

I want my session to timeout after a given interval of time. In web.xml I've been using code like:
<session-config>
<session-timeout>20</session-timeout>
</session-config>
where 20 is the timeout period in minutes, which works correctly.
What I would like to do is to do it programatically using code like this inside one of my beans as follow:
#ManagedBean(name="login")
#SessionScoped
public class MyLoginBean implements HttpSessionListener, Serializable {
// private variables etc.
HttpServletRequest request;
HttpSession session = request.getSession();
// Constructor
public MyLoginBean() {
session.setMaxInactiveInterval(1200);
}
// The rest of the code
}
where the timeout here is 1200 seconds, i.e. 20 minutes. Unfortunately, on opening up a browser to look at the application, it fails with the message:
com.sun.faces.mgbean.ManagedBeanCreationException: Cant instantiate class: com.csharp.MyLoginBean.
Followed by:
java.lang.NullPointerException
What am I doing wrong here? I know that setMaxInactiveInterval() refers to the particular session, which in this case is the login bean, rather than everything, which is what the code in web.xml file specifies. I have several beans, but timing out the login bean is the only one that matters.
I'm using JSF 2.0 with Glassfish 3.1.1 and Eclipse Indigo, so some advice would be very much appreciated.
The NullPointerException has an extremely simple cause. It's one of the most simplest exceptions. To learn about the cause of an arbitrary exception, just look in its javadoc. All Java exceptions have their causes explained in the javadoc. Here's an extract of the javadoc of NullPointerException:
Thrown when an application attempts to use null in a case where an object is required. These include:
Calling the instance method of a null object.
Accessing or modifying the field of a null object.
Taking the length of null as if it were an array.
Accessing or modifying the slots of null as if it were an array.
Throwing null as if it were a Throwable value.
Applications should throw instances of this class to indicate other illegal uses of the null object.
Your problem is caused by point 1. Here,
HttpServletRequest request;
HttpSession session = request.getSession();
you're trying to invoke getSession() method on null instead of a concrete HttpServletRequest instance. In fact, you should have obtained the HttpServletRequest via ExternalContext#getRequest() and assigned it to request.
However, you've bigger problems: you should absolutely not get hold of the current servlet request as a property of a session scoped bean (which lives longer than the HTTP request!). You should get it inside the thread local scope (i.e. wholly inside the constructor or the method block). You should also not let your JSF managed bean implement the HttpSessionListener. This makes no utter sense. You'd end up with 2 instances, one created as listener by the container and another one created as managed bean by JSF.
Just thus should do:
#ManagedBean(name="login")
#SessionScoped
public class MyLoginBean implements Serializable {
public MyLoginBean() {
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession();
session.setMaxInactiveInterval(1200);
}
// ...
}
Or, if you're using JSF 2.1, use the one provided by ExternalContext:
FacesContext.getCurrentInstance().getExternalContext().setSessionMaxInactiveInterval(1200);

ApplicationScoped Bean with postconstruct method

I have an application scoped bean to hold the information in my database. After its instantiation it should fetch the data, so I annotated the method with #PostConstruct. As soon as I request the jsf page where this bean is referenced the server log explodes! I think it somehow recurses and the only stacktrace I get is that a System Exception occurred during the repBean.acceptVisitor(Visitor); method. The server log then gets several GB big and I have to manually delete it in order to have free disk space. If I delete the #PostConstruct annotation there are no exceptions. After calling the update() method from another bean the repositoryContent variable is updated properly and contains the information. The only problem then is that my jsf page doesn't display the content for some strange reason.
#ManagedBean(eager=true)
#ApplicationScoped
public class IndexBean implements Serializable {
private ArrayList<ViewFolder> repositoryContent;
#EJB
RepositoryService repBean;
#PostConstruct
public void update() {
RepositoryVisitor Visitor = new RepositoryVisitor();
repBean.acceptVisitor(Visitor);
repositoryContent = Visitor.getList();
}
}
This is not normal behaviour.
One of the following lines
RepositoryVisitor Visitor = new RepositoryVisitor();
repBean.acceptVisitor(Visitor);
repositoryContent = Visitor.getList();
is indirectly evaluating the EL expression #{indexBean} which in turn causes the bean being constructed once again, because it is not been put in service yet. It would only be put in service (and thus available as a concrete #{indexBean}) when the #PostConstruct finishes. This all causes an infinite loop.
You might need to do some refactoring, or to pass the application scoped bean instance itself to the method call so that it can be used directly instead of being referenced by an EL expression.

CDI: How to put an existing object into application scope (without writing CDI extension)?

I have an object which I initialized manually (legacy code).
I'd like to put this object to the application context to make it available through CDI.
How do I do that without writing a CDI extension?
Using a producer. Make a bean that #Produces instances of your class.
Do you need something like that?
#ApplicationScoped
public class ApplicationScopedClass {
#Produces
public static ApplicationScopedClass makeAnInstance() {
return new ApplicationScopedClass();
}
}

Prevent component tree serialization for certain parts of application

Is it possible to explicitly deny JSF from serializing some component trees? At the moment I am passing a non-serializable object to a <h:inputText>:
<h:inputText value="#{nonSerializableBean.nonSerializableClassInstance}" />
What happens after a few clicks is that I get (during view restoration):
javax.faces.FacesException: Unexpected error restoring state for component
with id configurationForm:j_idt292:j_idt302:field. Cause:
java.lang.IllegalStateException: java.lang.InstantiationException:
my.namespace.NonSerializableClass
I think this occurs because JSF cannot restore the nonSerializableClassInstance:
Caused by: java.lang.IllegalStateException: java.lang.InstantiationException: com.foobar.utils.text.Period
at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:110)
at javax.faces.component.ComponentStateHelper.restoreState(ComponentStateHelper.java:292)
at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1444)
at javax.faces.component.UIOutput.restoreState(UIOutput.java:255)
at javax.faces.component.UIInput.restoreState(UIInput.java:1359)
A bonus question: Is it ok not to make backing beans Serializable? Should this then prevent serialization/deserialization of these?
Some background:
We have a bunch of 3rd party classes that we need to provide forms for in JSF. The problem is that we cannot directly use these classes on JSF pages, because they do not implement Serializable interface, and thus will/should fail if JSF runtime decides to serialize/deserialize the page and the component-tree. The classes are "closed" and we are not allowed to modify them.
Running Mojarra 2.0.2.
Javabeans are by spec supposed to implement Serializable. JSF just follows/adheres this spec.
The classes are "closed" and we are not allowed to modify them.
Your best bet is to wrap it as a transient property of a class which implements Serializable and implement the writeObject() and readObject() accordingly.
public class SerializableClass implements Serializable {
private transient NonSerializableClass nonSerializableClass;
public SerializableClass(NonSerializableClass nonSerializableClass) {
this.nonSerializableClass = nonSerializableClass;
}
public NonSerializableClass getNonSerializableClass() {
return nonSerializableClass;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(nonSerializableClass.getProperty1());
oos.writeObject(nonSerializableClass.getProperty2());
// ...
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
nonSerializableClass = new NonSerializableClass();
nonSerializableClass.setProperty1((String) ois.readObject());
nonSerializableClass.setProperty2((String) ois.readObject());
// ...
}
}
Finally use that class instead. You could eventually let it extends NonSerializableClass and then autogenerate delegate methods by a bit decent IDE.
Either way, it's only going to be a lot of opaque and boilerplate code, but since you're not allowed to modify those classes... (I would personally just push that 3rd party stuff to have them their so-called Javabeans to implement Serializable since it are them who's breaking the standards/specs).
I don't know what you expect if the class members (e.g. nonSerializableClassInstance) are not getting serialized.
Of course, you can mark them as transient.
The aim of a managed bean is to hold the application state - you will lose the state if some members are not getting serialized (if the server has the need of doing this).

Resources