JBoss.org Community Documentation

Chapter 20. Web Services

20.1. Configuration and Packaging
20.2. Conversational Web Services
20.2.1. A Recommended Strategy
20.3. An example web service

Seam integrates with JBossWS to allow standard JEE web services to take full advantage of Seam's contextual framework, including support for conversational web services. This chapter walks through the steps required to allow web services to run within a Seam environment.

To allow Seam to intercept web service requests so that the necessary Seam contexts can be created for the request, a special SOAP handler must be configured; org.jboss.seam.webservice.SOAPRequestHandler is a SOAPHandler implementation that does the work of managing Seam's lifecycle during the scope of a web service request.

A special configuration file, standard-jaxws-endpoint-config.xml should be placed into the META-INF directory of the jar file that contains the web service classes. This file contains the following SOAP handler configuration:


<jaxws-config xmlns="urn:jboss:jaxws-config:2.0" 
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              xmlns:javaee="http://java.sun.com/xml/ns/javaee"
              xsi:schemaLocation="urn:jboss:jaxws-config:2.0 jaxws-config_2_0.xsd">
   <endpoint-config>
      <config-name>Seam WebService Endpoint</config-name>
      <pre-handler-chains>
         <javaee:handler-chain>
            <javaee:protocol-bindings>##SOAP11_HTTP</javaee:protocol-bindings>
            <javaee:handler>
               <javaee:handler-name>SOAP Request Handler</javaee:handler-name>
               <javaee:handler-class>org.jboss.seam.webservice.SOAPRequestHandler</javaee:handler-class>
            </javaee:handler>
         </javaee:handler-chain>
      </pre-handler-chains>
   </endpoint-config>
</jaxws-config>

So how are conversations propagated between web service requests? Seam uses a SOAP header element present in both the SOAP request and response messages to carry the conversation ID from the consumer to the service, and back again. Here's an example of a web service request that contains a conversation ID:


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:seam="http://seambay.example.seam.jboss.org/">
  <soapenv:Header>
    <seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'>2</seam:conversationId>
  </soapenv:Header>
  <soapenv:Body>
    <seam:confirmAuction/>
  </soapenv:Body>
</soapenv:Envelope>    
    

As you can see in the above SOAP message, there is a conversationId element within the SOAP header that contains the conversation ID for the request, in this case 2. Unfortunately, because web services may be consumed by a variety of web service clients written in a variety of languages, it is up to the developer to implement conversation ID propagation between individual web services that are intended to be used within the scope of a single conversation.

An important thing to note is that the conversationId header element must be qualified with a namespace of http://www.jboss.org/seam/webservice, otherwise Seam will not be able to read the conversation ID from the request. Here's an example of a response to the above request message:


<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
  <env:Header>
    <seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'>2</seam:conversationId>
  </env:Header>
  <env:Body>
    <confirmAuctionResponse xmlns="http://seambay.example.seam.jboss.org/"/>
  </env:Body>
</env:Envelope>    
    

As you can see, the response message contains the same conversationId element as the request.

Let's walk through an example web service. The code in this section all comes from the seamBay example application in Seam's /examples directory, and follows the recommended strategy as described in the previous section. Let's first take a look at the web service class and one of its web service methods:


@Stateless
@WebService(name = "AuctionService", serviceName = "AuctionService")
public class AuctionService implements AuctionServiceRemote
{           
   @WebMethod
   public boolean login(String username, String password)
   {
      Identity.instance().setUsername(username);
      Identity.instance().setPassword(password);
      Identity.instance().login();
      return Identity.instance().isLoggedIn();
   }
   
   // snip
}

As you can see, our web service is a stateless session bean, and is annotated using the JWS annotations from the javax.jws package, as defined by JSR-181. The @WebService annotation tells the container that this class implements a web service, and the @WebMethod annotation on the login() method identifies the method as a web service method. The name and serviceName attributes in the @WebService annotation are optional.

As is required by the specification, each method that is to be exposed as a web service method must also be declared in the remote interface of the web service class (when the web service is a stateless session bean). In the above example, the AuctionServiceRemote interface must declare the login() method as it is annotated as a @WebMethod.

As you can see in the above code, the web service implements a login() method that delegates to Seam's built-in Identity component. In keeping with our recommended strategy, the web service is written as a simple facade, passing off the real work to a Seam component. This allows for the greatest reuse of business logic between web services and other clients.

Let's look at another example. This web service method begins a new conversation by delegating to the AuctionAction.createAuction() method:


   @WebMethod
   public void createAuction(String title, String description, int categoryId)
   {
      AuctionAction action = (AuctionAction) Component.getInstance(AuctionAction.class, true);
      action.createAuction();
      action.setDetails(title, description, categoryId);
   }

And here's the code from AuctionAction:


   @Begin
   public void createAuction()
   {
      auction = new Auction();
      auction.setAccount(authenticatedAccount);
      auction.setStatus(Auction.STATUS_UNLISTED);        
      durationDays = DEFAULT_AUCTION_DURATION;
   }

From this we can see how web services can participate in long running conversations, by acting as a facade and delegating the real work to a conversational Seam component.