WEB服务主要是关于对消息的处理,消息处理程序扮演了对消息预处理和后处理的角色。这些一系列的处理消息的程序可以按照一定的顺序来处理,这种按照一定的顺序来进行处理的方式叫做处理链。当然这些处理程序调用的次序由部署的设置和它们在服务器端还是在客户端运行来决定的。
版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接 作者:xzzhouhu(作者的Blog:http://blog.matrix.org.cn/page/xzzhouhu) 原文:http://www.developer.com/services/article.php/3503766 译文:http://www.matrix.org.cn/resource/article/44/44145_Handler+Chain.html 关键字:Handler Chain
为什么使用处理链? 一个处理程序可以在客户端或服务器端用来创建一个SOAP的请求消息和响应消息。例如一个使用消息处理程序的简单例子,在SOAP消息中的实体部分对数据进行加密和解密,客户端在他向WEB服务发送SOAP请求消息前必须使用消息处理程序对消息进行加密,WEB服务收到请求后,在它将数据发送到后台实现WEB服务之前,必须使用消息处理程序对从客户端发送过来的消息进行解密。与请求消息的传输方向相反,SOAP响应消息也要执行相同的步骤。
另外一个例子,在SOAP消息的头部分访问信息进行处理。SOAP头经常用来存储WEB服务详细说明信息并且使用消息处理程序来操作它。例如将用户名和密码可以放在SOAP消息的头部分中来发送到验证处理程序。 SOAP消息处理可以用来提高WEB服务的使用效率。这种方法的实现可以通过使用队列缓冲SOAP响应的次序。处理程序可以使SOAP的请求信息生效并且将消息交互纪录下来。
处理链: 处理链就是将每一个消息处理装配成一个链表形式进行处理。在WEB服务中实现可能需要调用多个处理类,用来提供处理请求/响应前面或后面的服务。JAX-RPC运行环境中可以在链表中调用多个处理类。这种处理消息的概念称为处理链。
处理链模型: 一个消息处理类功能主要在客户服务器端通信中,尽可能的为终端服务和可以设置用来截取SOAP消息并且在以下几种情况下对消息执行 各种操作: 1)在客户端,在SOAP请求被建立后并且在发送到WEB服务之前 2)在WEB服务端,在SOAP消息请求被WEB服务程序处理之前 3)在WEB服务端,在WEB服务发送SOAP消息响应之前 4)在客户端,在JAX-RPC运行时处理从WEB服务中收到的SOAP响应之前 消息处理链可以使用后台组件也可以不使用,下面主要针对各种处理唇?芯咛宕?怼?
(图1) 图一描述了使用后台组件进行处理链操作。当客户端调用WEB服务时,SOAP请求到达处理链中的处理请求部分,一旦处理通过,处理程序将消息传送到WEB服务中并且执行。处理响应程序被WEB服务调用并且将SOAP响应发送到客户端。
图2使用后台组件的处理链模型
举例: 这个例子我们采用的服务为WEBLOGIC8.X,这里我们通过后台组件提供查询服务的验证处理程序来详细说明。这里面的验证内容主要是帐号和个人识别码,它们被放在SOAP请求消息的头部分中,消息处理程序将重新获得帐号和个人识别码并验证,如果验证通过,一旦通过验证,后台组件将会调用余额查询来获得此帐号的余额。 以下就是写一个处理程序的步骤: 1)处理程序必须实现javax.xml.rpc.handler.Handler 接口或继承Weblogic中的GenericHandler 类。 2)javax.xml.rpc.handler.Handler 接口包含了以下这些方法用来实现
init () destroy() getHeaders() handleRequest() handleResponse() handleFault()
如果使用weblogic.webservice.GenericHandler 类,只需要重写必须的方法,在这个例子中验证处理程序重写了handleRequest() 和handleResponse() 方法。如果handleRequest()方法返回true,将会调用处理链中的下一个处理程序,如果这个处理程序是处理链中最后一个元素,将会调用后台的WEB服务程序。如果 handleRequest()方法返回false,对应的handleResponse()方法将会被调用。验证处理程序中handleRequest() 将从SOAP的头部分中重新获得帐号和个人识别码,并且验证后要返回true 或false结果。
AuthenticationHandler.java
import java.util.Map; import java.util.Iterator;
import javax.xml.rpc.JAXRPCException; import javax.xml.rpc.handler.soap.SOAPMessageContext; import javax.xml.rpc.handler.HandlerInfo; import javax.xml.rpc.handler.MessageContext; import javax.xml.namespace.QName; import javax.xml.soap.*;
import weblogic.webservice.GenericHandler;
public class AuthenticationHandler extends GenericHandler {
private int me = System.identityHashCode(this);
private HandlerInfo handlerInfo = null; String accountNo = null; String pin = null;
public void init(HandlerInfo handlerInfo) { this.handlerInfo = handlerInfo; } public boolean handleRequest(MessageContext messageContext) { System.err.println("** handleRequest called in: "+me); try { SOAPMessageContext sctx = (SOAPMessageContext)messageContext; SOAPMessage message = sctx.getMessage(); SOAPPart sp = message.getSOAPPart(); SOAPEnvelope senv = sp.getEnvelope(); SOAPHeader sh = senv.getHeader();
Iterator iter = sh.getChildElements(); if(iter.hasNext()) { iter.next(); //skip text Object obj = iter.next(); //account details SOAPElement elt = (SOAPElement)obj; iter = elt.getChildElements(); iter.next(); //skip text node obj = iter.next(); elt = (SOAPElement)obj; accountNo = elt.getValue(); iter.next(); //skip text node obj = iter.next(); elt = (SOAPElement)obj; pin = elt.getValue(); } } catch (Exception e) { e.printStackTrace(); throw new JAXRPCException(e); } if(accountNo.equals("12345") && pin.equals("6789")) return true; else return false; } public boolean handleResponse(MessageContext messageContext) { System.out.println("Inside HandleResponse"); return true;
} public QName[] getHeaders() { return handlerInfo.getHeaders(); } }
以下就是余额查询的代码,是一个非常简单的服务返回给出已知帐号的余额。 BalanceEnquiryService.java
public class BalanceEnquiryService { public float getBalance(String accountNo) { if(accountNo.equals("12345")) return 5000f; else return 100f; } }
WEB服务中的web-service.xml 必须要修改为包含处理链的描述,下面几个部分需要在web-service.xml 中修改的: 1)在<web-services> 的根元素中创建一个<handler-chains> 子元素,在这个子元素中包含了WEB服务中一系列处理链程序的定义。 2)创建<handler-chains>的子元素<handler-chain> ,在这个子元素中,显示了所有处理链中的处理程序。对于每一个处理程序,使用类名必须指定 java类的全限定名以便实现处理程序。使用<init-params>元素来指定处理程序的初始化参数。
<handler-chains> <handler-chain name="myChain"> <handler class-name="myHandlers.handlerOne" /> <handler class-name="myHandlers.handlerTwo" /> <handler class-name="myHandlers.handlerThree" /> </handler-chain> </handler-chains>
3.使用<operations>(它本身是<web-service> 的子元素)的子元素<operation>元素,来详细说明处理链是WEB服务中的一个操作。 注意:处理链只是自执行的,没有后台组件参与执行,在这中情况下,只要使用处理链属性中的<operation>元素并且并不需要指定组件或方法的属性,就像下面所选其中的一部分:
<web-service> <operations> <operation name="chainService" handler-chain="myChain" /> </operations> </web-service>
这里有一个完整的WEB.xml的例子: web-service.xml
<web-services xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <handler-chains> <handler-chain name="AuthenticationHandlerChain"> <handler class-name="AuthenticationHandler" /> </handler-chain> </handler-chains> <web-service name="BalanceEnquiryService" uri="/BalanceEnquiryService" targetNamespace="http://www.bea.com"> <components> <java-class name="jccomp0" class-name="BalanceEnquiryService"> </java-class> </components> <operations> <operation method="getBalance" handler-chain="AuthenticationHandlerChain" component="jccomp0"> <params> <param name="param0" class-name="java.lang.String" style="in" type="xsd:string" /> <return-param name="result" class-name="java.lang.Float" type="xsd:float" /> </params> </operation> </operations> </web-service> </web-services>
这里面处理链的名字叫做AuthenticationHandlerChain,这里面包含了一个AuthenticationHandler处理程序,注意,在操作中包含了操作属性handler-chain="AuthenticationHandlerChain". 这里给出了ant脚本build.xml用来打包EAR,注意在打包之前必须有Web.xml 和 application.xml build.xml
<project name="webservices-handler" default="all" basedir="."> <!-- set global properties for this build --> <property environment="env" /> <property name="compiler" value="modern" /> <property name="debug" value="yes" /> <property name="deprecation" value="yes" /> <property name="build.compiler" value="${compiler}" /> <property name="source" value="." /> <property name="build" value="${source}/build" /> <property name="war_file" value="BalanceEnquiry.war" /> <property name="ear_file" value="BalanceEnquiry.ear" /> <property name="namespace" value="http://www.bea.com" /> <property name="client.classes.dir" value="{source}/clientclasses" /> <target name="all" depends="clean, war, ear" /> <target name="clean"> <delete dir="${build}" /> <delete file="${source}/${ear_file}" /> <delete file="${war_file}" /> </target> <target name="war"> <delete dir="${build}" /> <mkdir dir="${build}" /> <mkdir dir="${build}/WEB-INF" /> <mkdir dir="${build}/WEB-INF/classes" /> <javac srcdir="${source}" includes="AuthenticationHandler.java, BalanceEnquiryService.java" destdir="${build}/WEB-INF/classes" /> <copy todir="${build}/WEB-INF"> <fileset dir="${source}"> <include name="web-services.xml" /> <include name="web.xml" /> </fileset> </copy> <jar jarfile="${war_file}" basedir="${build}" /> </target> <target name="ear" depends="war"> <delete dir="${build}" /> <mkdir dir="${build}" /> <mkdir dir="${build}/META-INF" /> <copy todir="${build}"> <fileset dir="${source}"> <include name="${war_file}" /> </fileset> </copy> <copy todir="${build}/META-INF"> <fileset dir="${source}"> <include name="application.xml" /> </fileset> </copy> <jar jarfile="${source}/${ear_file}" basedir="${build}" /> </target> </project>
ant脚本在创建WAR文件的同时也进行EAR打包,在WEBLOGIC服务器上部署打包好的EAR文件,下面是客户端调用余额查询的程序: Client.java
import org.apache.axis.client.Call; import org.apache.axis.client.ServiceFactory; import org.apache.axis.client.Service; import org.apache.axis.MessageContext; import org.apache.axis.attachments.Attachments; import org.apache.axis.message.SOAPEnvelope;
import javax.xml.namespace.QName; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPBodyElement; import javax.xml.soap.SOAPConnection; import javax.xml.soap.SOAPConnectionFactory; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import javax.xml.soap.AttachmentPart; import javax.xml.soap.MessageFactory; import javax.xml.soap.Name; import javax.xml.rpc.ParameterMode;
import java.net.URL; import java.util.Iterator;
public class Client { private static String TARGET_NAMESPACE = "http://www.bea.com"; private static QName xsdFloat = new QName("http://www.w3.org/2001/XMLSchema", "float"); public static org.apache.axis.message.SOAPEnvelope env = null; public static SOAPMessage message = null; public static void main(String[] argv) throws Exception { Client client = new Client(); env = client.constructSOAPEnvelope(); client.constructHeader(env); client.constructBody(env); System.setProperty( "javax.xml.rpc.ServiceFactory", "org.apache.axis.client.ServiceFactory" );
String url = "http://localhost:7001/BalanceEnquiry/BalanceEnquiryService"; ServiceFactory factory = (org.apache.axis.client.ServiceFactory)ServiceFactory. newInstance(); QName serviceName = new QName(TARGET_NAMESPACE, "BalanceEnquiryService");
Service service = (org.apache.axis.client.Service)factory. createService(serviceName); Call call = (org.apache.axis.client.Call)service.createCall(); call.setTargetEndpointAddress(url); SOAPEnvelope result = call.invoke(env); System.out.println(result); } public SOAPEnvelope constructSOAPEnvelope() throws Exception { org.apache.axis.message.SOAPEnvelope env = new org.apache.axis.message.SOAPEnvelope(); return env; } public void constructHeader(SOAPEnvelope envelope) throws Exception { SOAPHeader header = envelope.getHeader(); Name headerElementName = envelope.createName("AccountDetails","", "http://schemas.xmlsoap.org/soap/ envelope/"); SOAPHeaderElement headerElement = header.addHeaderElement(headerElementName); headerElement.setMustUnderstand(false); headerElement.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope/"); SOAPElement accNo = headerElement.addChildElement("accountNo"); accNo.addTextNode("12345"); SOAPElement pinNo = headerElement.addChildElement("pin"); pinNo.addTextNode("6789"); }
public void constructBody(SOAPEnvelope envelope) throws Exception { SOAPBody body = envelope.getBody(); Name bodyRootElementName = envelope.createName("getBalance","", "http://schemas.xmlsoap.org/soap/ encoding/"); SOAPBodyElement bodyRootElement = body.addBodyElement(bodyRootElementName); SOAPElement bodyElement = bodyRootElement.addChildElement("param0"); bodyElement.addAttribute(envelope.createName("xsi:type"), "xsd:string"); bodyElement.addTextNode("12"); } }
如果在客户端也有一个处理程序,以下部分要发生变化: (1)导入import javax.xml.rpc.handler.HandlerInfo; import javax.xml.rpc.handler.HandlerRegistry;两个类。
(2)在call().invoke前面加:
QName portName = new QName( "http://bea.com/", "HelloWorldServicePort"); HandlerRegistry registry = service.getHandlerRegistry(); List handlerList = new ArrayList(); handlerList.add( new HandlerInfo( ClientHandler.class, null,null ) ); registry.setHandlerChain( portName, handlerList );
结论: 我们希望能让大家更多的了解关于处理链机制并且应用到WEB服务中。
|