ApplicationScoped Bean with postconstruct method - jsf

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.

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

CDI extension, altering processed type

Using Weld 1.1.13.Final in test with Arquillian....
Let's say I inject into a field something volatile. Something like a property subject to change that I want the bean owning the injection point to receive change events. Thought about creating a CDI extension.
Caught ProcessAnnotatedType event and looking for all fields that have an custom annotation on field injection points:
<T> void pat(#Observes ProcessAnnotatedType<T> event, BeanManager bm) {
final AnnotatedType<T> target = event.getAnnotatedType();
for (AnnotatedField<? super T> field : target.getFields())
if (field.isAnnotationPresent(Value.class)) { // ignore that I don't check #Inject here for the moment
CtClass wrapper = pool.get(target.getJavaClass().getName());
ConstPool cp = wrapper.getClassFile().getConstPool();
CtMethod m = CtNewMethod.make(....)
....
wrapper.addMethod(m);
event.setAnnotatedType(bm.createAnnotatedType(wrapper.toClass()));
}
}
Had even grabbed thereafter all the injection points for fields and replaced the underlying WeldField with a new Field corresponding the "wrapper" type. Otherwise bean validation fails.
But this only works for stuff setup during startup not when for example Arquillian uses the Bean Manager to initialize a class that injects one of my "wraps". Things fail since the Bean Resolver uses the Type as a hash key to find beans.
Basically I don't think I can "mask" a class that is annotated (made into a bean) by the CDI with an extra method to receive custom events. Would have been cool but a Type is a Type (i.e. no idea how to proxy or fake the equals/hashCode).
Got it. Turns out the compute value function (google extension) inside the TypeSafeBeanResolver resolver (at least the CDI Weld implementation) is smart. If I just extend the class:
CtClass wrapper = pool.makeClass(target.getJavaClass().getName()+"Proxy");
wrapper.setSuperclass(pool.get(target.getJavaClass().getName()));
.....
final AnnotatedType<T> other = bm.createAnnotatedType(wrapper
.toClass());
then everything works fine. Tested capturing an event in a bean. Will post the code on a Gist with a comment.

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

Execute a bean's method before it may be used in the JSF page

A request scoped bean collects data, from all many other request beans & business logic. This bean is used through the EL expressions in the page but before this request scoped bean may be used in the page, it needs to build a directory using the collected data (This is done after all collection is over but before the bean properties may be used in page).
How can I execute the building of the directory in this bean after all collection but before it is used through the EL expressions in the page without using <f:event>? I need to build it only once.
#ManagedBean(name="namesDirectory")
#RequestScoped
public class NamesDirectory {
public void addForPersonNameRetrieval(Integer id) { // this is used to collect the data in bean
peopleNamesMap.put(id,null);
.......
}
public void buildDirectory(){ // used, when all collection is over, to build the diirectory
.......
}
public String getPersonName(Integer id) { // used in the JSF page through EL expressions
name = peopleNamesMap.get(id);
}
}
Here buildDirectory() needs to be executed at the end of all collection but before using getPersonName() in the JSF page
You have got several options. You could rebuild the directory after every insert or before every retrieval, however this may cause unnecessary rebuilds. You could rebuild the directory only when needed and called:
Add a flag requiresRebuild and default it to true.
Set it to true in addForPersonaNameRetrieval.
Set it to false in buildDirectory.
Call buildDirectory in getPersonName if a rebuild is necessary.

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