第24章. Web服务

第24章. Web服务


Seam 集成了JBossWS,允许标准JEE web服务充分利用Seam的上下文框架的优势, 包括支持对话web服务。本章通过必要的步骤允许web服务运行在一个Seam 环境。

 

24.1. 配置和打包

为了允许Seam拦截web服务请求,以便于可以为请求创建必要Seam上下文,一个特殊的SOAP(简单对象存取协议)处理器必须被配置; org.jboss.seam.webservice.SOAPRequestHandler是一个 SOAPHandler 实现,管理在一个web服务请求作用域期间的Seam的生命周期。


一个特殊的配置文件, standard-jaxws-endpoint-config.xml应该被放在包含web服务类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>

 

24.2. 对话的Web服务


那么对话是如何在web服务请求之间传播呢? Seam 使用一个SOAP头元素表示SOAP请求和响应消息,携带着对话ID在消费者与服务器之间传递。这里是一个包含对话ID的web服务的例子:

 

<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消息中看见的,在SOAP头内有一个conversationId 元素,包含了一个请求的对话ID,在这里是2。不幸的是因为web服务由各种语言编写的各种web服务客户端消费,这由开发者实现对话ID在单一对话作用域内使用的单独web服务之间的传播。


注意,一个重要的事情是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元素。

 

24.2.1. 推荐策略


因为web服务必须由无状态会话bean或POJO实现,所以,对于对话web 服务,推荐web服务作为对话Seam组件的一个正面(facade)。

jboss seam reference 2.1(第24章) - *工* - 要有光,于是就有了光

如果web服务作为无状态会话被编写,那么也可以通过@Name注释使其成为Seam组件。这样做便于Seam双向注入 (或其他)功能用于web服务类自身。


24.3. web服务例子


让我们浏览一个web服务例子。这节的所有代码来自Seam的 /examples 目录下的seamBay例子应用程序, 并遵循前节所述的推荐策略。 首先让我们看一看web服务类和它的一个web服务方法:


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

 

   }

 

……

 

}

正如你看见的,我们的web服务是一个无状态会话bean,并用javax.jws包的JWS注释注释了它,如JSR-181定义一样。 @WebService注释告诉容器该类实现了web服务,在login()方法上的@WebMethod注释指明该方法是一个web服务方法。@WebService 注释的Name和 serviceName属性是可选的。


如规范要求的一样,被暴露来作为web服务方法的每个方法必须在web服务类的远程接口中也被声明(当web服务是一个无状态会话bean时)。在上面的例子中, AuctionServiceRemote接口声明了与用@WebMethod注释一样的login()方法。


如你在上面代码中所见的, web服务实现了一个代理Seam内建的Identity组件的login()方法。按照我们推荐的策略,web服务作为一个简单的正面(facade)被编写,假冒Seam组件的真正工作。这样可以最大程度的重用web服务和客户端之间的业务逻辑。


让我看另外一个例子。这个web服务方法通过委托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服务如何通过作为一个正面(facade)并代理对话Seam组件的真正工作参与到长期运行对话。


24.4. 使用RESTEasy的RESTful HTTP web服务


Seam集成了实现了JAX-RS规范(JSR 311)的RESTEasy。 你可以决定要多么“深”地集成到你的Seam应用程序:

  • 无缝集成RESTEasy引导程序和配置,自动侦测资源和供应商。
  • SeamResourceServlet服务HTTP/REST请求,在web.xml中不需要外部的servlet和配置。
  • 编写资源作为Seam组件, 使用完全的Seam生命周期管理和拦截(双向注入)。

24.4.1. RESTEasy配置和请求服务


首先,得到RESTEasy库和jaxrs-api.jar包,与你的应用程序带的其他库一起部署。 还要部署集成的库jboss-seam-resteasy.jar 。


在启动时,所有注释了@javax.ws.rs.Path的类会被自动发现并注册成HTTP资源。 Seam用它内建的SeamResourceServlet自动访问并服务于HTTP请求。资源的URI(统一资源标识符)象下面这样被构建:

  • URI从SeamResourceServlet在web.xml中映射的模式(pattern开始,例如 /seam/resource,如果你按照常规例子。你可以改变这个设置,在不同的基本路径下暴露你RESTful资源。注意,这是一个全局改变,并且其他的Seam资源(例如 s:graphicImage)也是在这个基本路径下提供服务。
  • 然而,Seam集成的RESTEasy为这个基本路径增加了一个可配置字符串,默认是/rest。因此,举例来说,你的资源的完整路径会是/seam/resource/rest。 我们推荐你在你的应用程序中改变这个字符串,你可以添加一个版本号为准备将来REST API升级服务 (老的客户端会保持老的URI基本路径。) ,例如: /seam/resource/restv1。
  • 最后,实际的资源在定义的@Path下是可用的,如,一个用@Path("/customer")映射的资源在/seam/resource/rest/customer下是可用的。  


举一个例子,下面的资源定义会对使用URI http://your.hostname/seam/resource/rest/customer/123的任何GET请求返回一个纯文本表示:


@Path("/customer")

 

public class MyCustomerResource {

 

 

    @GET

 

    @Path("/{customerId}")

 

    @ProduceMime("text/plain")

 

    public String getCustomer(@PathParam("customerId") int id) {

 

         return ...;

 

    }

 

 }

 

如果这些默认是可以接受的,你不需要另外的配置,不必编辑web.xml 或任何其他设置。 然而,你可以在你的Seam 应用程序中配置RESTEasy 。 首先导入resteasy命名空间到你的XML配置文件头:

 

<components

 

   xmlns="http://jboss.com/products/seam/components"

 

   xmlns:resteasy="http://jboss.com/products/seam/resteasy"

 

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

 

   xsi:schemaLocation=

 

     http://jboss.com/products/seam/resteasy

 

         http://jboss.com/products/seam/resteasy-2.1.xsd

 

     http://jboss.com/products/seam/components

 

         http://jboss.com/products/seam/components-2.1.xsd">

 

然后,你可以象前面提及的改变/rest前缀:

 

<resteasy:application-config resource-path-prefix="/restv1"/>

 

现在你的资源的完整路径是/seam/resource/restv1/{resource} ——注意 ,你的@Path定义和映射并没有改变。这是一种应用广泛的通用开关,用于HTTP API版本。


如果你喜欢在你的资源内映射完整路径,你可以禁用断裂的基本路径:

 

<resteasy:application-config strip-seam-resource-path="false"/>

 

例如,现在资源的路径利用@Path("/seam/resource/rest/customer")来映射。然而,我们反对禁用这个功能,因为你的资源类映射是捆绑到一个特殊的部署情节。


对任何部署的@javax.ws.rs.Path 资源和任何@javax.ws.rs.ext.Provider类,Seam会扫描你的类路径。你可以禁用扫描,并手动配置这些类:

 

<resteasy:application-config

 

     scan-providers="false"

 

     scan-resources="false"

 

     use-builtin-providers="true">

 

 

 

     <resteasy:resource-class-names>

 

         <value>org.foo.MyCustomerResource</value>

 

         <value>org.foo.MyOrderResource</value>

 

     </resteasy:resource-class-names>

 

 

     <resteasy:provider-class-names>

 

         <value>org.foo.MyFancyProvider</value>

 

     </resteasy:provider-class-names>

 

 

 </resteasy:application-config>

 

use-built-in-providers切换启用(默认)或禁用RESTEasy内建供应商。 我们建设你启用它们,因为它们提供纯文本、JSON和JAXB编组的开箱即用功能。

 

最后,你可以配置媒体类型和语言的URI扩展名:

 

<resteasy:application-config>

 

 

    <resteasy:media-type-mappings>

 

       <key>txt</key><value>text/plain</value>

 

    </resteasy:media-type-mappings>

 

 

    <resteasy:language-mappings>

 

       <key>deutsch</key><value>de-DE</value>

 

    </resteasy:language-mappings>

 

 

</resteasy:application-config>

 

这个定义将对附加的访问和访问语言头的值 text/plain和de-DE映射URI后缀为.txt和.deutsc。


24.4.2. 资源和供应商作为Seam组件


默认时RESTEasy管理任何资源和供应商实例。那意味着RESTEasy会实例化一个资源类并且服务于一个请求,然后摧毁它。这是默认的JAX-RS生命周期。供应商对整个应用程序实例化一次,实际上是单独的并且被假定是无状态的。


你可以编写资源和供应商作为Seam组件,得益于丰富的Seam管理的生命周期、双向注入拦截和安全等等。简单地让你的资源类成为一个Seam组件:

@Name("customerResource")

 

@Path("/customer")

 

public class MyCustomerResource {

 

 

    @In

 

    CustomerDAO customerDAO;

 

 

    @GET

 

    @Path("/{customerId}")

 

    @ProduceMime("text/plain")

 

    public String getCustomer(@PathParam("customerId") int id) {

 

         return customerDAO.find(id).getName();

 

    }

 

}

现在,当一个请求访问服务器时,Seam处理customerResource实例。这是一个事件作用域的Seam JavaBean组件,因此不同于默认的JAX-RS生命周期。然而,你完全得到了Seam注入支持和所有其它Seam组件以及你想用的上下文。目前也支持SESSION、 APPLICATION和STATELESS资源组件。请记住,为正确地处理服务边会话上下文,所有的HTTP 请求必须转换成一个有效会话标识符 (cookie, URI路径参数)。


对话作用域(Conversation-scoped资源组件和对话映射目前还不支持,但不久会实现的。


供应商类也是Seam组件, 它们必须是应用作用域(APPLICATION-scoped)或无状态的。

资源和供应商可以是EJBs或JavaBeans, 象所有其它Seam组件一样。

你可能感兴趣的:(Web,应用服务器,jboss,webservice,seam)