1 Reply Latest reply on Jul 7, 2009 10:55 PM by asookazian

    Propogating Conversations with JAX-WS Client

    mattdavies

      I spent the better part of a day beating my head into the desk as I could not get the my code to propogate a conversation.  I followed the seambay example and set up my web services exactly like the examples. I published my seam app to a JBoss server, and I used wsconsume to generate the stubs and service calls.


      I could call the methods, but I could not force seam to start the conversation.  I could log via soap, and the system would remember my Identity, but the conversation-scoped bean didn't remember me.


      The answer lies with the jax-ws client that I was using.  I found that I not only needed to ensure the session was active, but I also needed to put the conversationId provided by the first invocation of my service into the soap headers.


      So here is the client code in all its glory





      Client Code:





      import javax.xml.namespace.QName;
      import javax.xml.ws.BindingProvider;
      import javax.xml.ws.Service;
      
      import org.domain.soapstuff.TestService;
      import org.domain.soapstuff.TestService_Service;
      
      import com.sun.xml.ws.api.message.Header;
      import com.sun.xml.ws.api.message.HeaderList;
      import com.sun.xml.ws.api.message.Headers;
      import com.sun.xml.ws.developer.JAXWSProperties;
      import com.sun.xml.ws.developer.WSBindingProvider;
      
      
      
      
      
      
      public class Tester {
      
           public void testsoap () {
                // TODO Auto-generated method stub
      
                System.out.println("Starting testsoap");
                
                try {
                     long count = 1000;
                     
                     //Generate the services
                     TestService_Service ceservice = new TestService_Service();
                     TestService cservice = ceservice.getPort(TestService.class);
                     
                     // Set to keep the session active
                     ((BindingProvider)cservice).getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
                     
                     /*
                      * Login
                      * Then get and set the conversationId from the responseContext
                      */
                     System.out.println("logging in: " + cservice.login("matt","matt"));
                     setConversationId( getConversationId(cservice), cservice);
                     
                     /*
                      * Simple loop to ensure that my test method maintains and propogates the conversation
                      */
                     long start = System.currentTimeMillis();
                     for (long i = 0; i < count; i++ ) cservice.testMethod();
                     long end = System.currentTimeMillis();
                     
                     System.out.println("Elapsed: " + (end-start) + " " + ((double)(end-start) / (double) count) + " " + ((double)1000/( (double)(end-start) / (double) count))  + " / sec" );
                  
      
                     /*
                      * Now test logging out.
                      * Logout invalidates the Identity, ends the conversation, and also destroys the session for good measure
                      */
                     cservice.logout();
                     
                     /*
                      * Do it all again to ensure it works good.
                      */
                     System.out.println("logging in: " + cservice.login("matt","matt"));
                     setConversationId( getConversationId(cservice), cservice);
                     for (long i = 0; i < 2; i++ ) cservice.testMethod();
                     cservice.logout();
                     
                     System.out.println("Done");
                     
                     
                } catch (Exception e) {
                     e.printStackTrace();
                }
                
                System.out.println("Stopping testsoap");
           }
           
           public String getConversationId(TestService service) {
                String conversationId = "";
                
                /*
                 * Get the conversationId from the responseContext
                 * 
                 */
                try {
                     HeaderList hl = (HeaderList)((BindingProvider)service).getResponseContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
                     
                     for (Header h : hl) {
                          if (h.getLocalPart().equals("conversationId")) conversationId = h.getStringContent();
                     }
                } catch (Exception e) {
                     e.printStackTrace();
                }
                
                return conversationId;
           }
           
           public void setConversationId(String conversationId, TestService service) {
                try {
                     /*
                      * This is the magic
                      * this sets the soap header with the conversation id
                      */
                     WSBindingProvider bp = (WSBindingProvider) service;
                     bp.setOutboundHeaders(
                                 Headers.create(new QName("http://www.jboss.org/seam/webservice", "conversationId", "seam"),conversationId)
                     );
                } catch (Exception e) {
                     e.printStackTrace();
                }
           }
      
      }




      And the Web Service Code:




      @Stateless
      @Name("testBean")
      @WebService(name = "TestService", serviceName = "TestService")
      @WebContext(contextRoot="t/TestService")
      
      
      public class TestBeanImpl implements TestBean  {
           @Logger Log log;
           
           
           @In(create = true, value="entityManager")
           private EntityManager em;
           
           
           
           @Resource
           WebServiceContext wsCtxt;
           
           
           
           private String getRemoteIP () {
                MessageContext msgCtxt = wsCtxt.getMessageContext();
               HttpServletRequest req = (HttpServletRequest)msgCtxt.get(MessageContext.SERVLET_REQUEST);
               String clientIP = req.getRemoteAddr();
               
               return clientIP;
           }
           
           private String getSessionID () {
                MessageContext msgCtxt = wsCtxt.getMessageContext();
               HttpServletRequest req = (HttpServletRequest)msgCtxt.get(MessageContext.SERVLET_REQUEST);
               String clientIP = req.getSession().getId();
               return clientIP;
           }
           @WebMethod
              public boolean login(String username, String password)
              {
      
                Identity.instance().getCredentials().setUsername(username);
                 Identity.instance().getCredentials().setPassword(password);
                 Identity.instance().login();
                 log.info("Logging in " + username + " " + password);
                 getAction().start();
                 return Identity.instance().isLoggedIn();
               }
            @WebMethod
              public boolean logout()
              {
                 getAction().endConversation();
                 Identity.instance().logout();
                 MessageContext msgCtxt = wsCtxt.getMessageContext();
                    HttpServletRequest req = (HttpServletRequest)msgCtxt.get(MessageContext.SERVLET_REQUEST);
                    req.getSession().invalidate();
                 return !Identity.instance().isLoggedIn();
              }
      
           
           @WebMethod
          public void getSessionId() {
                
                log.info( getSessionID() );
                
      
           }
           
           @WebMethod @Restrict("#{identity.loggedIn}")
           public int testMethod() {
                TestBeanAction action = (TestBeanAction)Component.getInstance( "testBeanAction",true );
                int count = action.hello();
                action.start();
                return count;
           }
           
           
            private TestBeanAction getAction()
              {
                 return (TestBeanAction) Component.getInstance( "testBeanAction",true );
              }   
         
      
          
           
           
      }
      




      Hope this helps someone out there.

        • 1. Re: Propogating Conversations with JAX-WS Client
          asookazian

          You should be able to always replace this:


          @In(create = true, value="entityManager")
          private EntityManager em;



          with this:


          @In private EntityManager em;



          and this in components.xml:


          <persistence:managed-persistence-context name="entityManager"
                                               auto-create="true"
                                persistence-unit-jndi-name="java:/fooEntityManagerFactory"/> 



          Do you have more than one SMPC/PU?