Seam与JBossWS整合,有助于标准的JEE Web Services完全利用Seam上下文框架的优势,包括支持对话的Web Services。 本章概述帮助Web Services在Sema环境内部运行所需要的步骤。
为了允许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>
那么对话在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 元素。
让我们浏览一个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 注解中的 name 和 serviceName 属性是可选的。
根据规范要求,要暴露作为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组件,来参与长运行对话的。