4 Replies Latest reply on Jul 21, 2006 1:11 AM by justinwalsh

    Stateless search to stateful conversation

      Hi,

      I have a seam component "FindDocumentAction" (a stateless EJB) which is responsible for the search functionality of the application. The user is able to search for documents by entering a reference number.

      The outcome of a search can be one of two things:
      1) When multiple documents are returned, the search results page is displayed, with a link to display each document (viewdocument page)
      2) When a single document is returned from the search (singleton search), we send the user straight on to the viewdocument page for the document.

      The viewing of the document is the start of a conversation, handled by a stateful seam component (stateful session bean - viewDocumentAction).

      In the first case (multiple search results returned), I generate a link in the results page as follows, which passes in a request parameter containing the document identifier (documentId):

      <s:link action="#{viewDocumentAction.selectDocument}">
       <f:param name="documentId" value="#{doc.id}"/>
       <h:outputText value="#{doc.reference}"/>
      </s:link>
      


      This link invokes the selectDocument() method of the viewDocumentAction component (the conversational scoped stateful session bean) - which is annotated with the @Begin tag - and a new conversation is begun:

      @Stateful
      @Name("viewDocumentAction")
      @Scope(ScopeType.CONVERSATION)
      public class ViewDocumentAction implements ViewDocument {
      
       @PersistenceContext(type=PersistenceContextType.EXTENDED)
       private EntityManager em;
      
       @Out
       private Document document;
      
       @RequestParameter("documentId")
       private Integer documentId;
      
       @Begin
       public String selectDocument() {
      
       document = (Document)em.createQuery(
       "from Document d where d.id = :documentId")
       .setParameter("documentId", documentId)
       .getSingleResult();
      
       return "viewDocument";
       }
      }
      



      The second case is what is troubling me. I've tried the following approaches with mixed results:

      1) Forward straight to the viewDocument page, and create an entry in the pages.xml file:
      <page view-id="/secure/viewDocument.xhtml" action="#{viewDocumentAction.selectDocument}" />
      

      ... which invokes the selectDocument() method. This also necessitates a change from using the s:link tag mentioned above to using a simple link to the page - as we don't want to invoke the action selectDocument() twice. The request parameter does not seem to be present when the event is generated.

      2) In the case of the singleton search result, lookup the stateful bean from the stateless session beans find method:
      
       if (documentSearchResults.size() == 1) {
       // lookup viewDocumentAction
       ViewDocument viewDocumentAction = ....
      
       Document singleDocument = documentSearchResults.get(0);
       return viewDocumentAction.selectDocument(singleDocument);
       }
      
      


      thus invoking a new method on the stateful bean which is also used to start the conversation:

       @Begin
       public String selectDocument(Document doc) {
      
       document = em.merge(doc);
       return "viewDocument";
       }
      

      Since I am in a stateless component, I don't want to inject a stateful bean using the @EJB tag, as the stateless bean could potentially be shared by multiple clients. The lookup is thus scoped to the method, manual and therefore ugly (jndi name may vary depending on type of deployment etc).
      (BTW Should I be asking seam to resolve the named stateful component or should I just do a ejb context lookup and rely on seam to intercept?)


      What I'm effectively trying to achieve is to start a conversation (by invoking a components @Begin method) with a request parameter present but this is proving tricky to to programatically. Perhaps I should redirect the client with the request parameter (as per the RESTful example) in the case of a singleton search - but how do I ensure that the conversation is begun when I hit the viewDocument page?

      Any pointers would be appreciated.

      Thanks

        • 1. Re: Stateless search to stateful conversation
          gavin.king

          You should use a redirect combined with a page action. This is the usual pattern for all these kinds of things. You _need_ the redirect, because you want to allow bookmarking of the item detail page.

          • 2. Re: Stateless search to stateful conversation

            Hi,

            I've tried this. When the page action is executed, the request parameter is not present yet. A simple example:

            ViewDocument:

            @Local
            public interface ViewDocument {
             void selectDocument();
             void destroy();
            }
            


            ViewDocumentAction:
            @Stateful
            @Name("viewDocumentAction")
            @Scope(ScopeType.CONVERSATION)
            public class ViewDocumentAction implements ViewDocument {
            
             @RequestParameter
             private Integer documentId;
            
             @Begin
             public void selectDocument() {
             System.out.println("documentId [" + documentId + "]");
             if (documentId == null) {
             throw new IllegalStateException("DocumentId is null");
             }
             }
            
             @Remove @Destroy @End
             public void destroy() {
            
             }
            
            }
            


            Invoked from:
             <h:outputLink value="viewDocument.seam" id="viewDocument">
             <h:outputText value="[View Document]"/>
             <f:param name="documentId" value="1" />
             </h:outputLink>
            


            pages.xml
            <pages>
             <page view-id="/viewDocument.xhtml" action="#{viewDocumentAction.selectDocument}"/>
            </pages>
            


            06:26:49,735 INFO [STDOUT] documentId [null]
            06:26:49,735 ERROR [[Faces Servlet]] Servlet.service() for servlet Faces Servlet
            threw exception
            javax.faces.el.EvaluationException: Exception while invoking expression #{viewDocumentAction.selectDocument}
            ....
            Caused by: javax.ejb.EJBException: java.lang.IllegalStateException: DocumentId is null
            at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.ja
            va:69)

            • 3. Re: Stateless search to stateful conversation
              gavin.king

              Recheck your code. This should work fine.

              • 4. Re: Stateless search to stateful conversation

                Hi,

                Started from scratch again and working fine now. Thanks for your time. Was starting to get disillusioned - but back on the path to insight.