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.