Phase Listener and EntityManager
cbkihong.cbkihong.hotmail.com Sep 22, 2008 5:45 AMI am faced with a necessity to install a custom phase listener for custom Ajax functionality in a Seam Web application (Seam 2.0.3 I believe). However, I was stumbled trying to struggle with a Seam-injected EntityManager in the phase listener.
This is part of the phase listener:
public class AjaxPhaseListener implements PhaseListener { @Override public void afterPhase(PhaseEvent arg0) { } @Override public void beforePhase(PhaseEvent arg0) { FacesContext context = arg0.getFacesContext(); if (!context.getExternalContext().getRequestServletPath().startsWith("/ajax")) { // None of our business return; } ELContext elc = FacesContext.getCurrentInstance().getELContext(); HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse(); try { if (context.getExternalContext().getRequestPathInfo() != null && context.getExternalContext().getRequestPathInfo().endsWith("testphaselistener.ajax")) { // This works .... if (PhaseId.RESTORE_VIEW.equals(arg0.getPhaseId())) { TourOperator company = (TourOperator)context.getApplication().getExpressionFactory().createValueExpression(elc, "#{touristsim.tourop_main.companyProfile}", TourOperator.class).getValue(elc); try { response.setContentType("text/plain; charset=UTF-8"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write("1,2,3\n"); response.getWriter().write(company.getName() + "\n"); } catch (IOException e) {} context.responseComplete(); } } else if (context.getExternalContext().getRequestPathInfo() != null && context.getExternalContext().getRequestPathInfo().endsWith("simcardlist.ajax")) { // ... but this doesn't !! TourOperatorMain service = (TourOperatorMain)context.getApplication().getExpressionFactory().createValueExpression(elc, "#{touristsim.tourop_main}", TourOperatorMain.class).getValue(elc); List<SimCardAssignment> cardlist = service.getUnassignedSimCards(); try { response.setContentType("text/plain; charset=UTF-8"); response.setHeader("Cache-Control", "no-cache"); for (SimCardAssignment c : cardlist) { response.getWriter().write(c.getSerialNumber() + "\n"); } } catch (IOException e) {} context.responseComplete(); } else { // anything else under /ajax, the view ID must be invalid throw new RuntimeException("Invalid ajax action"); } } catch (Exception e) { try { logger.log(Level.WARNING, "Ajax phase handler exception", e); response.sendError(404); } catch (Exception e2) { // There is nothing else we can do for now } context.responseComplete(); } } @Override public PhaseId getPhaseId() { return PhaseId.RESTORE_VIEW; } }
The EntityManager is defined this way in components.xml:
<persistence:managed-persistence-context name="entityManager" auto-create="true" entity-manager-factory="#{touristsimEntityManagerFactory}"/> <persistence:entity-manager-factory name="touristsimEntityManagerFactory" persistence-unit-name="touristsim"/>
This is the session bean itself:
@Name("touristsim.tourop_main") @Scope(SESSION) public class TourOperatorMain { @In(create=true) private EntityManager entityManager; @In(value="#{identity.compositeLogin.companyId}") private String companyID; private TourOperator tourOperator; @Create public void initTourOperatorProfile() { tourOperator = (TourOperator) entityManager.createNativeQuery("SELECT * FROM accounts_operators WHERE company_id = :tourop_code", TourOperator.class) .setParameter("tourop_code", companyID) .getSingleResult(); } // Need @BypassInterceptors to work in phase listener @BypassInterceptors public TourOperator getCompanyProfile() { return tourOperator; } // This does not work in phase listener @SuppressWarnings("unchecked") public List<SimCardAssignment> getUnassignedSimCards() { return (List<SimCardAssignment>) entityManager.createNativeQuery("SELECT * FROM sim_card_allocations WHERE company_id = :tourop_code AND status = 'N'", SimCardAssignment.class) .setParameter("tourop_code", companyID) .getResultList(); } // ... }
For one of the Ajax requests which access a method on the bean, the request works only if @BypassInterceptors is applied to the method.
When trying to access another Ajax request, which requires access to EntityManager, it seems the EntityManager is null or cannot be injected:
2008-09-22 10:03:23,376 ERROR [STDERR] 2008/9/22 AM 10:03:23 hk.com.xxx.touristsim.AjaxPhaseListener beforePhase WARNING: Ajax phase handler exception org.jboss.seam.RequiredException: @In attribute requires non-null value: touristsim.tourop_main.entityManager at org.jboss.seam.Component.getValueToInject(Component.java:2178) at org.jboss.seam.Component.injectAttributes(Component.java:1601) at org.jboss.seam.Component.inject(Component.java:1419) at org.jboss.seam.core.BijectionInterceptor.aroundInvoke(BijectionInterceptor.java:45) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68) at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:42) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68) at org.jboss.seam.core.SynchronizationInterceptor.aroundInvoke(SynchronizationInterceptor.java:32) at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68) at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107) at org.jboss.seam.intercept.JavaBeanInterceptor.interceptInvocation(JavaBeanInterceptor.java:166) at org.jboss.seam.intercept.JavaBeanInterceptor.invoke(JavaBeanInterceptor.java:102) at hk.com.xxx.touristsim.TourOperatorMain_$$_javassist_7.getUnassignedSimCards(TourOperatorMain_$$_javassist_7.java) at hk.com.xxx.touristsim.AjaxPhaseListener.beforePhase(AjaxPhaseListener.java:116) at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:214) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:96) at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:104) at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265) ......
I understand that JSF imposes no ordering control to phase listeners, but I do not even know if this explains the issue. Please kindly advise if there is any way out. Thank you in advance.