Getting objects from conversation in a custom PhaseListener
jakec Jul 5, 2007 7:24 PMI need to make a URL that can download a binary file based on information stored in a long running conversation. I can get it to work fine once from a page, but the second time, it fails with:
ERROR [PhaseListenerManager] Exception in PhaseListener RESTORE_VIEW(1) afterPhase java.lang.IllegalStateException: No active event context at org.jboss.seam.core.Manager.instance(Manager.java:267)
I'm pretty sure something I'm doing is ending the long running conversation, because if I just download a document once, then try to continue with my workflow by going to the next page, or even just refresh the current page, I get a similar error:
org.jboss.seam.NoConversationException: no long-running conversation for @Conversational bean
I'm pretty new at Seam, so if I'm making a colossal blunder, please be kind... :-) I've searched all over trying to figure out what is going on, but I just can't seem to find anything relevant.
Here is my PhaseListener:
package com.myco.myproj; import java.util.Set; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.servlet.http.HttpServletResponse; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.security.Restrict; import org.jboss.seam.contexts.EntityBean; import org.jboss.seam.core.Manager; import org.jboss.seam.jsf.TransactionalSeamPhaseListener; import org.jboss.seam.log.Log; @Name("docPhaseListener") @Restrict("#{identity.loggedIn}") @SuppressWarnings("serial") public class DocPhaseListener extends TransactionalSeamPhaseListener { public final static int BUF_SIZE = 16384; public final static String DOC_VIEW_ID = "/document/"; private static Log log = (Log) org.jboss.seam.log.Logging.getLog(DocPhaseListener.class); private MyObject getMyObject(FacesContext context) { MyObject myObject = null; log.info("Manager.instance().getCurrentConversationId()="+Manager.instance().getCurrentConversationId()); ValueBinding vb = context.getApplication().createValueBinding("#{myObject}"); if(vb == null) { log.info("vb is null!"); } else { Object o = null; log.info("vb type="+vb.getType(context)+", vb value="+(o=vb.getValue(context))); if(o instanceof MyObject) myObject = (MyObject) o; else log.info("Not MyObject!"); } return myObject; } public void afterPhase(PhaseEvent event) { super.afterPhase(event); FacesContext context = event.getFacesContext(); String viewId = context.getViewRoot().getViewId(); int index = viewId.indexOf(DOC_VIEW_ID); if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId()) && index != -1 && viewId.endsWith(".xhtml")) { MyObject myObject = getMyObject(context); if(myObject != null) Util.handleDocumentRequest(context, myObject); else { // TODO: Handle bad requests better ((HttpServletResponse)context.getExternalContext().getResponse()).setContentType("application/octet-stream"); context.responseComplete(); } } } }
Since I'm extending TransactionalSeamPhaseListener (it seems necessary to get into the conversation the first time), and you can't have more than one SeamPhaseListener, I had to replace the existing one with mine, so my faces-config.xml looks like this:
<lifecycle> <!--phase-listener>org.jboss.seam.jsf.TransactionalSeamPhaseListener</phase-listener--> <phase-listener>com.medorder.mazama.DocumentPhaseListener</phase-listener> </lifecycle>
My UI is part of a rich:dataTable, and the column looks like this:
<h:column> <f:facet name="header">Download</f:facet> <h:outputLink value="document/download.seam?cid=#{conversation.id}&clr=true"> <f:verbatim>Download</f:verbatim> </h:outputLink> </h:column>
Eventually, this URL will be called from an embedded Flash object, so I really need to use a PhaseListener. Can anyone tell me what I'm doing wrong to end the conversation?
I have also tried just implementing PhaseListener, and pulling the object from the SessionMap directly using the key "org.jboss.seam.CONVERSATION#xx$myObject", but I get the same kind of problem. The second time I access the EntityBean that is returned, the instance it points to is null.
Also, can someone confirm my suspicion that @Name and @Restrict are pointless to use on a PhaseListener? I don't think any injection has worked on it, and I'm pretty sure that it is not possible to use those on something that pretty much by definition lives outside the life-cycle.