Sunday, January 12, 2014

Redirection inside @PostConstruct vs preRenderView event

I tried to implement the logic which is needed to redirect when page loading. I have used EJB (3.0) and JSF (2.1)  technologies here.
Initially I used @PostConstruct method with @ViewScoped in ManagedBean.


import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; @ManagedBean(name = "redirectTest") @ViewScoped public class RedirectTest implements Serializable { @PostConstruct public void init() { if (isRedirect) { ExternalContext context = FacesContext.getCurrentInstance() .getExternalContext(); try { context.redirect(context.getRequestContextPath() FacesContext.getCurrentInstance().responseComplete(); } catch (IOException e) { e.printStackTrace(); } } }
It throws following exception

com.sun.faces.mgbean.ManagedBeanCreationException: An error occurred performing resource injection on managed bean redirectTest at com.sun.faces.mgbean.BeanBuilder.invokePostConstruct(BeanBuilder.java:229) .... Caused By: com.sun.faces.spi.InjectionProviderException: com.oracle.pitchfork.interfaces.LifecycleCallbackException: Failure to invoke public void redirectTest.init() on bean class class redirectTest with args: null at com.bea.faces.WeblogicInjectionProvider.invokePostConstruct(WeblogicInjectionProvider.java:40) at com.sun.faces.mgbean.BeanBuilder.invokePostConstruct(BeanBuilder.java:223) ... Caused By: com.oracle.pitchfork.interfaces.LifecycleCallbackException: Failure to invoke public void redirectTest.init() on bean class class redirectTest with args: null at com.oracle.pitchfork.inject.Jsr250Metadata.invokeLifecycleMethod(Jsr250Metadata.java:395) ... Caused By: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) .. Caused By: java.lang.IllegalStateException: Response already committed at weblogic.servlet.internal.ServletResponseImpl.objectIfCommitted(ServletResponseImpl.java:1651) ...
 
Above exception has given message as “Response already committed”.  Response is already committed means ,it has been sent first few bytes as response from server to client. This is a point of no return. This happen when the backing bean is referenced (and thus constructed) for the first time relatively late in the view, maybe about halfway or in the end. For example


 (1) Bytes already sent to response 
 (2) Point when managedbean created ( if @ViewScoped )

It can not be solved even close the response manually using

FacesContext.getCurrentInstance().responseComplete();

Use the preRenderView method execute during the beginning of the render response phase, right before the HTML is been rendered. The @PostConstruct is intented to perform actions directly after bean's construction and the setting of all injected dependencies and managed properties such as @EJB, @Inject, @ManagedProperty, etc. The injected dependencies are namely not available inside the bean's constructor. This will thus run only once per view, session or application when the bean is view, session or application scoped. The preRenderView event is invoked on every HTTP request (yes, this also includes ajax requests!).

Solution : preRenderView
By using preRenderView will give the solution for this scenario.

Refer this post about best place for the f:metadata element : post

There is the issue , preRenderView might calling more than one time when using with . This can be avoid using
public void init(){ if (!FacesContext.getCurrentInstance().isPostback(){ //put initView codes here }
JSF 2.2 onward "f:viewAction" tag, which is supposed to replace this "f:event type="preRenderView", it will however be possible with the onPostback attribute:

"f:viewAction action="#{defaultNewQuestionHandler.init}" onPostback="false" "