6 Replies Latest reply on Jan 23, 2008 8:17 PM by andrey.chorniy

    Servlets in long running conversations

    burtoncarl

      I'm trying to access objects from a long running conversation from withing a servlet. I haven't had much sucess though. So I've created a very simple servlet to simply access a conversation. Throughout my app seam adds "cid" for the conversation id in the parameters list. although for my testing I have been adding both cid and conversationId to the request.

      I've tried adding

      <web:context-filter url-pattern="/servlet/*" />


      to components.xml and/or

       <filter>
       <filter-name>Seam Servlet Filter</filter-name>
       <filter-class>org.jboss.seam.servlet.SeamServletFilter</filter-class>
       </filter>
       <filter-mapping>
       <filter-name>Seam Servlet Filter</filter-name>
       <url-pattern>/servlet/*</url-pattern>
       </filter-mapping>
      


      to the web.xml, both met with failure. I am able to access objects with Component.getInstance("someName"), but not within my conversation.

      Here are the bits of code I am able to share.

      web.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       version="2.5">
       <!-- Ajax4jsf setup -->
      
       <filter>
       <display-name>RichFaces Filter</display-name>
       <filter-name>richfaces</filter-name>
       <filter-class>org.ajax4jsf.Filter</filter-class>
       </filter>
      
      
       <filter-mapping>
       <filter-name>richfaces</filter-name>
       <servlet-name>Faces Servlet</servlet-name>
       <dispatcher>REQUEST</dispatcher>
       <dispatcher>FORWARD</dispatcher>
       <dispatcher>INCLUDE</dispatcher>
       </filter-mapping>
      
       <context-param>
       <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
       <param-value>org.jboss.seam.ui.facelet.SeamFaceletViewHandler</param-value>
       </context-param>
      
       <context-param>
       <param-name>javax.faces.CONFIG_FILES</param-name>
       <param-value>/WEB-INF/navigation.xml</param-value>
       </context-param>
      
       <listener>
       <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
       </listener>
      
       <context-param>
       <param-name>org.richfaces.SKIN</param-name>
       <param-value>csc2</param-value>
       </context-param>
      
       <context-param>
       <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
       <param-value>client</param-value>
       </context-param>
      
       <context-param>
       <param-name>facelets.DEVELOPMENT</param-name>
       <param-value>true</param-value>
       </context-param>
      
       <context-param>
       <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
       <param-value>.xhtml</param-value>
       </context-param>
      
       <context-param>
       <param-name>facelets.SKIP_COMMENTS</param-name>
       <param-value>true</param-value>
       </context-param>
      
       <filter>
       <filter-name>Seam Filter</filter-name>
       <filter-class>org.jboss.seam.web.SeamFilter</filter-class>
       </filter>
      
       <filter-mapping>
       <filter-name>Seam Filter</filter-name>
       <url-pattern>/*</url-pattern>
       </filter-mapping>
      
       <!--
       <filter>
       <filter-name>Seam Context Filter</filter-name>
       <filter-class>org.jboss.seam.web.ContextFilter</filter-class>
       </filter>
       <filter-mapping>
       <filter-name>Seam Context Filter</filter-name>
       <url-pattern>/servlet/*</url-pattern>
       </filter-mapping>
      
       <filter>
       <filter-name>Seam Multipart Filter</filter-name>
       <filter-class>org.jboss.seam.web.MultipartFilter</filter-class>
       </filter>
      
       <filter-mapping>
       <filter-name>Seam Multipart Filter</filter-name>
       <url-pattern>*.xhtml</url-pattern>
       </filter-mapping>
       -->
      
       <servlet>
       <servlet-name>Faces Servlet</servlet-name>
       <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
       <load-on-startup>1</load-on-startup>
       </servlet>
       <servlet-mapping>
       <servlet-name>Faces Servlet</servlet-name>
       <url-pattern>*.htm</url-pattern>
       </servlet-mapping>
      
      <!--
       <filter>
       <filter-name>Seam Servlet Filter</filter-name>
       <filter-class>org.jboss.seam.servlet.SeamServletFilter</filter-class>
       </filter>
       <filter-mapping>
       <filter-name>Seam Servlet Filter</filter-name>
       <url-pattern>/servlet/*</url-pattern>
       </filter-mapping>
       -->
      
      
       <servlet>
       <description>For testing conversations within servlet</description>
       <display-name>ConversationTestServlet</display-name>
       <servlet-name>ConversationTestServlet</servlet-name>
       <servlet-class>com.gdi.csc.servlet.ConversationTestServlet</servlet-class>
       <load-on-startup>3</load-on-startup>
       </servlet>
       <servlet-mapping>
       <servlet-name>ConversationTestServlet</servlet-name>
       <url-pattern>/servlet/conversation</url-pattern>
       </servlet-mapping>
      
       <security-constraint> <!-- This stops the user from replacing the .htm extensino with .xhtml to read the source files... -->
       <display-name>Restrict XHTML Documents</display-name>
       <web-resource-collection>
       <web-resource-name>XHTML</web-resource-name>
       <url-pattern>*.xhtml</url-pattern>
       </web-resource-collection>
       <auth-constraint>
       <role-name>NONE</role-name>
       </auth-constraint>
       </security-constraint>
      
       <session-config>
       <session-timeout>30</session-timeout>
       </session-config>
      
       <!--
       <error-page>
       <error-code>404</error-code>
       <location>/home.htm</location>
       </error-page>
       -->
      </web-app>
      


      components.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <components xmlns="http://jboss.com/products/seam/components"
       xmlns:core="http://jboss.com/products/seam/core"
       xmlns:security="http://jboss.com/products/seam/security"
       xmlns:framework="http://jboss.com/products/seam/framework"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:web="http://jboss.com/products/seam/web"
       xmlns:mail="http://jboss.com/products/seam/mail"
       xsi:schemaLocation=
       "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-1.2.xsd
       http://jboss.com/products/seam/security http://jboss.com/products/seam/security-1.2.xsd
       http://jboss.com/products/seam/framework http://jboss.com/products/seam/framework-1.2.xsd
       http://jboss.com/products/seam/components http://jboss.com/products/seam/components-1.2.xsd
       http://jboss.com/products/seam/web http://jboss.com/products/seam/web-1.2.xsd"
       >
      
       <core:init debug="true" jndi-pattern="#{ejbName}/local"/>
      
       <!-- 120 second conversation timeout -->
       <core:manager conversation-timeout="120000"
       concurrent-request-timeout="1000"
       conversation-id-parameter="cid"
       conversation-is-long-running-parameter="clr"/>
      
      <!--
       <core:jbpm>
       <core:process-definitions>
       <value>ordermanagement1.jpdl.xml</value>
       </core:process-definitions>
       <core:pageflow-definitions>
       <value>checkout.jpdl.xml</value>
       <value>newuser.jpdl.xml</value>
       </core:pageflow-definitions>
       </core:jbpm>
      -->
       <core:ejb installed="true"/>
      
      <!-- <core:microcontainer installed="true"/> -->
      
       <security:identity authenticate-method="#{csc2authenticator.authenticate}"/>
       <event type="org.jboss.seam.notLoggedIn">
       <action expression="#{redirect.captureCurrentView}"/>
       </event>
      
       <event type="org.jboss.seam.postAuthenticate">
       <action expression="#{redirect.returnToCapturedView}"/>
       </event>
      
      
       <core:managed-persistence-context name="entityManager"
       auto-create="true"
       persistence-unit-jndi-name="java:/myEntityFactory" />
      
       <!--factory for "session" property used to have an ability
       to inject Hibernate session object directly in the
       @In Session session;-->
       <factory name="session" auto-create="true" scope="STATELESS"
       value="#{entityManager.delegate}"/>
      
       <!-- Conversation propagation with redirects -->
       <web:redirect-filter url-pattern="*.htm"/>
      
       <!-- Context management for custom servlets -->
       <web:context-filter url-pattern="/servlet/*" />
      
      
       <!--To enable file upload capability
       max-request-size - maximum file size in bytes
       -->
       <web:multipart-filter auto-create="true"
       class="org.jboss.seam.web.MultipartFilter" max-request-size="4000000"
       url-pattern="*.htm"/>
      
       <!-- Mail session component-->
       <mail:mail-session scope="application"
       host="#{applicationProperties.smtpHost}"
       port="#{applicationProperties.smtpPort}"
       username="#{applicationProperties.smtpUserName}"
       password="#{applicationProperties.smtpPassword}"/>
       <!--
       <component class="org.jboss.seam.web.MultipartFilter">
       <property name="maxRequestSize">4000000</property>
       </component>
       -->
      </components>
      


      ConversationTestServlet.java
      /**
       * Copyright 2008 CoActive Marketing Group
       */
      package com.gdi.csc.servlet;
      
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.Enumeration;
      
      import javax.faces.event.PhaseId;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      
      import org.apache.log4j.Logger;
      import org.jboss.seam.Component;
      import org.jboss.seam.contexts.ContextAdaptor;
      import org.jboss.seam.contexts.Lifecycle;
      import org.jboss.seam.core.Conversation;
      import org.jboss.seam.core.Manager;
      
      /**
       * @author Burton Hodges
       *
       */
      @SuppressWarnings("serial")
      public class ConversationTestServlet extends HttpServlet {
       private static Logger logger = Logger.getLogger(ThumbServlet.class);
      
       @Override
       protected void doGet(HttpServletRequest request, HttpServletResponse resp)
       throws ServletException, IOException {
       debugRequestParameters(request);
       debugConversation(request);
       }
      
       private void debugConversation(HttpServletRequest request){
       try{
      // HttpSession session = request .getSession(true);
      // Lifecycle.setPhaseId(PhaseId.INVOKE_APPLICATION);
      // Lifecycle.setServletRequest(request);
      // Lifecycle.beginRequest(getServletContext(), session, request);
      // Manager.instance().restoreConversation(request.getParameterMap());
      // Lifecycle.resumeConversation(session);
      // Manager.instance().handleConversationPropagation(request.getParameterMap());
      
       {
       logger.info("debugConversation: trying Component.getInstance(\"conversation\")");
       Object o = Component.getInstance("conversation");
       if(o==null){
       logger.info("debugConversation: Component.getInstance(\"conversation\") returned null");
       }else{
       logger.info("debugConversation: object is instance of "+o.getClass().getName());
       debugConversation(Conversation.class.cast(o));
       }
       }
      
       logger.info("debugConversation: trying Conversation.instance()");
       debugConversation(Conversation.instance());
      
      // Manager.instance().endRequest(ContextAdaptor.getSession(session));
      // Lifecycle.endRequest(session);
      
       }catch (Exception e){
       logger.info("debugConversation: Error occured debuging seam conversation", e);
       }
       }
      
       private void debugConversation(Conversation conversation){
       if(conversation==null){
       logger.info("debugConversation: conversation is null");
       return;
       }
       logger.info("debugConversation: "+conversation);
       logger.info("debugConversation: "+conversation.getId());
       logger.info("debugConversation: "+conversation.getParentId());
       logger.info("debugConversation: "+conversation.getRootId());
       logger.info("debugConversation: "+conversation.getViewId());
       }
      
       @SuppressWarnings("unchecked")
       private void debugRequestParameters(HttpServletRequest request){
       logger.info("debugRequestParameters: Debugging request parameters");
      
       ArrayList<String> keys = new ArrayList<String>();
       Enumeration e = request.getParameterNames();
       while(e.hasMoreElements())
       keys.add(e.nextElement().toString());
       for(String key: keys)
       logger.info("debugRequestParameters: "+key+"="+request.getParameter(key));
       }
      
      }
      


      and an excerpt from my log file:
      2008-01-13 21:18:33,828 [http-8080-Processor25] INFO - debugRequestParameters: Debugging request parameters
      2008-01-13 21:18:33,828 [http-8080-Processor25] INFO - debugRequestParameters: conversationId=1
      2008-01-13 21:18:33,828 [http-8080-Processor25] INFO - debugRequestParameters: cid=1
      2008-01-13 21:18:33,828 [http-8080-Processor25] INFO - debugConversation: trying Component.getInstance("conversation")
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: object is instance of org.jboss.seam.core.Conversation
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: org.jboss.seam.core.Conversation@2eb6e9
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: 2
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: null
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: 2
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: null
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: trying Conversation.instance()
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: org.jboss.seam.core.Conversation@2eb6e9
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: 2
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: null
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: 2
      2008-01-13 21:18:33,843 [http-8080-Processor25] INFO - debugConversation: null
      


      Any help would be appreciated!
      Thanks in advance.

        • 1. Re: Servlets in long running conversations
          nickarls

          Just browsing through the API, but a long shot: can you access the conversation entries list (org.jboss.seam.core.ConversationEntries) which is supposed to

          "Manage a map of conversation id to ConversationEntry in the session context"

          • 2. Re: Servlets in long running conversations
            nickarls

            Or the same "conversationList" that can be used from the UI for selecting conversations. Or do I really have to read through all that code you just posted to see if I'm answering the wrong question? ;-)

            • 3. Re: Servlets in long running conversations
              burtoncarl

              Well the real issue isn't getting the Conversation object, it's getting my thread to join the conversation so that when I use Component.getInstance(name) I am able to get the instance of my object from my current long running conversation.

              • 4. Re: Servlets in long running conversations
                andrey.chorniy

                Confirmed - it doesn't work.
                In the stacktrace for the servlet I see the ContextFilter, but it is failed to get anything related to the "Conversation-Context" via the
                Component.getInstance()

                One more addition - I use the Tomcat 5.5 + Seam 1.2.1GA + <core:ejb installed="true"/>

                Can anybody confirm that ContextFilter works OK for the JBoss based deployment ? I mean that conversation-context is avaiable in the servlet

                • 5. Re: Servlets in long running conversations
                  pmuir

                  If you can post a runnable example with reproduction steps using Seam 2 to JIRA then we can take a look. Thanks :)

                  • 6. Re: Servlets in long running conversations
                    andrey.chorniy

                    I have some news. it is start working for me, but to achieve that I have to add cookie parameter JSESSIONID to identify the session in which that conversation was originally created.

                    ContextFilter and the underlying code uses the HTTP-Session to restore Conversation-Entries.

                    If I make request with plain http-get request like:
                    http://server-host/context/servlet/some-servlet?cid=2
                    from another browser - it doesn't work.

                    If I use two tabs of FireFox - in the first tab I open the conversation, in the second I make a servlet-request - IT WORKS.

                    So, it start working after I've added JSESSIONID cookie to my call servlet-request.
                    I make a programmatic call (not a browser reauest), so I have control over cookies.
                    But it seems that it is impossible to do it if you don't know the JSESSIONID value or have no controll over cookies. To do it via plain URL approach you have to turn-off cookies in the browser to add the jsessionid parameter to the get-request.
                    From one side - it add some additional security, from the other side - you not always can use it, since you are not always can establish the HTTPSession for that


                    Here is the code which is used to load current Conversation context.
                    It is stored in the sessionContext, which is based on the HTTPSession object

                    org.jboss.seam.contexts.Lifecycle
                     public static void beginRequest(ServletContext servletContext, HttpSession session, ServletRequest request)
                     {
                     log.debug( ">>> Begin web request" );
                     Contexts.eventContext.set( new WebRequestContext( ContextAdaptor.getRequest(request) ) );
                     Contexts.sessionContext.set( new WebSessionContext( ContextAdaptor.getSession(session) ) );
                     Contexts.applicationContext.set( new WebApplicationContext(servletContext) );
                     Contexts.conversationContext.set(null); //in case endRequest() was never called
                     }
                    
                    org.jboss.seam.core.ConversationEntries
                     public static ConversationEntries instance()
                     {
                     if ( !Contexts.isSessionContextActive() )
                     {
                     throw new IllegalStateException("No session context active");
                     }
                     return (ConversationEntries) Component.getInstance(ConversationEntries.class, ScopeType.SESSION);
                     }