2 Replies Latest reply on May 16, 2011 8:26 AM by Espen Myklevoll

    Phase Listener and EntityManager

    Bernard Chan Newbie

      I 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.