Chapter 20. Web Services

Seam与JBossWS整合,有助于标准的JEE Web Services完全利用Seam上下文框架的优势,包括支持对话的Web Services。 本章概述帮助Web Services在Sema环境内部运行所需要的步骤。

20.1. 配置和打包

为了允许Seam拦截Web Service请求,以便能够为该请求创建必要的Seam上下文,必须配置一种特殊的SOAP处理器; org.jboss.seam.webservice.SOAPRequesHandler 是一种 SOAPHandler 实现,它完成在一个Web Service请求范围期间管理Seam生命周期的工作。 将一个项目配置为使用这个处理器的最容易方法是,将一个称作 standard-jaxws-endpoint-config.xml 的文件放到包含Web Service类的 jar 文件的 META-INF 路径下。 这个文件包含下列SOAP处理器配置:

<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>

20.2. 对话的Web Services

那么对话在Web Service请求之间如何传播呢? Seam使用一个在SOAP请求和响应消息中都有的SOAP标头元素,将对话ID从消费者传输到服务,并再传回来。 下面是一个包含对话ID的Web Service请求的例子:

<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>    
    

就如你在上面的SOAP消息中所见,在包含请求的对话ID(在这个例子中是2)的SOAP头内部,有一个 conversationId 元素,遗憾的是,由于Web Services可能被以不同语言编写的不同Web Service客户端消费,因此在准备用于单个对话范围之内的单独Web Services之间实现对话ID传播,由开发人员决定。

要注意的一件重要事情是,conversationId 头元素必须满足 http://www.jboss.org/seam/webservice 的命名空间条件,否则Seam将无法从请求中读取对话ID。以下是对上述请求消息响应的一个例子:

<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>    
    

如你所见,响应消息包含与请求相同的 conversationId 元素。

20.2.1. 建议策略

因为Web Services必须被实现为一个无状态的会话Bean或者POJO,对于对话的Web Services,建议用Web Service充当一个对话Seam组件的外观(facade)。

如果Web Service是作为无状态会话Bean编写的,那它也可能通过提供一个 @Name,把它变成一个Seam组件。 这么做允许Seam的双向注入(和其它)特性在Web Service类自身中使用。

20.3. Web Servic范例

让我们浏览一个Web Service的例子。 本节中的代码全部来自Seam的 /examples 目录下的seamBay范例应用程序,并遵循前一节中所述的建议策略。 让我们先来看看Web Service类和它的其中一个Web Service方法:

@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
}

如你所见,我们的Web Service是一个无状态的会话Bean,并如JSR-181所规定的,利用 javax.jws 包中的JWS注解进行注解。 @WebService 注解告诉容器,这个类实现一个Web Service,且 login() 方法中的 @WebMethod 注解将该方法当成是一个Web Service方法。 @WebService 注解中的 nameserviceName 属性是可选的。

根据规范要求,要暴露作为Web Service方法的每个方法也都必须在Web Service类的远程接口进行声明(当Web Service是一个无状态会话Bean的时候)。 在上述例子中,AuctionServiceRemote 接口必须声明 login() 方法,实际上就是注解 @WebMethod

就如你可以在上述代码中所见,Web Service实现一个委托给Seam内建的 Identity 组件的 login() 方法。 为了与我们的建议策略一致,Web Service是作为一个简单的外观编写的,将实际的工作委托给Seam组件。 这样有助于最好地在Web Services和其他客户端之间重用业务逻辑。

让我们看另一个例子。这个Web Service方法通过委托给 AuctionAction.createAuction() 方法,开始了一个新对话。

   @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);
   }

下面是来自 AuctionAction 的代码:

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

从中我们可以看到,Web Services是如何通过充当外观,并将实际的工作委托给一个对话的Seam组件,来参与长运行对话的。