Web Service学习笔记之----JAX-RPC 
导读 本文是我对学习jwsdp-1.2时所做笔记的整理,其中主要是一些指导性的内容,并没有多少概念以及原理的介绍,读者可能觉得略显简单,如果想要学习基本概念可以参考网上有关Web Service的资料。本文例子所使用的开发环境是WindowXP+JWSDP-1.2。 
一.Web Service简介 1.定义  由两部分组成  ·SOAP--Web Service之间的基本通信协议。  ·WSDL--Web Service描述语言,它定义了Web Service做什么,怎么做和查询的信息。 2.简单的Web Service实现  包含四个基本步骤  ·创建Web Service的商业逻辑(通常是一些Java类)  ·将这些Java类部署到一个SOAP服务器上  ·生成客户访问代码  ·部署客户应用  注意:WSDL等文件的生成通常是利用厂商提供的工具来完成 3.WSDL解析 WSDL描述语言一般包含三部分  ·What部分--包括了type、message和portType元素    Type:定义了Web Service使用的数据结构(使用XML Schema定义)    Message:一个Message是SOAP的基本通信元素。每个Message可以有一个或多个Part,每个Part代表一个参数。    PortType:消息汇总为不同的操作并归入到一个被称为portType的实体中。一个portType代表一个接口(Web Service支              持的操作集合),每个Web Service可以有多个接口,它们都使用portType表示。每个操作又包含了input和        output部分。  ·How部分--包含binding元素    binding元素将portType绑定到特定的通信协议上(如HTTP上的SOAP协议)  ·Where部分--由service元素组成    它将portType,binding以及Web Service实际的位置(URI)放在一起描述 4.客户端   通常Web Service可以有三种类型的客户   ·商业伙伴(Business Partner)--包括分发商,零售商以及大型消费者)     此类客户通过SOAP、WSDL、ebXML、UDDI等XML技术与Web Service连接   ·瘦客户--包括Web浏览器、PDA以及无线设备     该类客户通常经由轻量协议(如HTTP)与Web Service连接   ·肥客户--包括Applet、各类应用以及现存系统     通常使用重量级协议(如IIOP)连接Web Service 
二.使用JAX-RPC开发Web Service 1.JAX-RPC支持的数据类型 JAX-RPC除了支持Java的基本数据类型外还支持一些自定义对象,但这些对象有一些条件限制 ·有缺省构造函数的对象 ·没有实现java.rmi.Remote接口 ·字段必须是JAX-RPC支持的类型 ·公有字段不能声明为final或transient ·非公有字段必须有对应的setter和getter方法 2.使用JAX-RPC创建Web Service ·基本步骤 A. 编写服务端接口并实现 一个服务的end-point有一些规定:必须实现java.rmi.Remot接口而且每个方法需要抛出RemoteException异常。 B. 编译、生成并且将所有服务需要的类和文件打包成WAR文件 C. 部署包含服务的WAR文件 ·如何创建服务 A. 编译服务所需的类文件 B. 生成服务所需文件 可以使用wscompile工具生成model.gz文件,它包含了描述服务的内部数据结构命令如下 wscompile -define -d build -nd build -classpath build config.xml  -model build/model.gz define标志告诉工具读取服务的 endpoint接口并且创建WSDL文件。-d和-nd标志告诉工具将输出文件写入指定的目录build。工具需要读以下的config.xml文件 <?xml version=”1.0” encoding=”UTF-8”?> <configuration xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/config”> <service name=”HelloService” targetNamespace=”urn:Star” typeNamespace=”urn:Star” packageName=”helloservice”> <interface name=”helloservice.HelloIF”/> </service> </configuration> 该文件告诉wscompile创建model文件所需的信息  ·服务名称:MyHelloService  ·WSDL名字空间:urn:Star  ·HelloService的所有类在包helloservice中  ·服务的端点(endpoint)接口:helloservice.HelloIF C. 将服务打包为WAR文件 WEB-INF/classes/hello/HelloIF.class WEB-INF/classes/hello/HelloImpl.class WEB-INF/jaxrpc-ri.xml WEB-INF/model.gz WEB-INF/web.xml jaxrpc-ri.xml文件如下所述 <?xml version=”1.0” encoding=”UTF-8”?> <webServices xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/dd” version=”1.0” targetNamespaceBase=”urn:Star” typeNamespaceBase=”urn:Star” urlPatternBase=”webservice”> <endpoint name=”Hello” displayName=”HelloWorld Service” description=”A simple web service” interface=”helloservice.HelloIF” model=”/WEB-INF/model.gz” implementation=”helloservice.HelloImpl”/> <endpointMapping endpointName=”Hello” urlPattern=”/hello”/> </webServices> D. 处理WAR文件 使用命令行  wsdeploy -o hello-jaxrpc.war hello-jaxrpc-original.war wsdeploy工具完成以下几个任务  ·读 hello-jaxrpc-original.war作为输入  ·从jaxrpc-ri.xml文件中获得信息  ·为服务生成tie classes  ·生成名为HelloService.wsdl的WSDL文件  ·将tie classes和HelloService.wsdl文件打包到新的war文件中 E. 在服务器上部署服务 如果你使用的是TOMCAT,你可以将WAR文件拷贝到webapps目录下,然后可以在 <http://localhost:8080/[context]/[servicename>上看是否配置成功 ·如何使用JAX-RPC创建Web Service客户端  通常有三种类型的客户:Static Stub、Dynamic Proxy和Dynamic Invocation Interface(DII) Static Stub客户 ·生成Stub 通过使用config-wsdl.xml和wscompile工具,可以生成stub wscompile -gen:client -d build -classpath build config-wsdl.xml  config-wsdl.xml文件如下    <?xml version="1.0" encoding="UTF-8"?> <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config"> <wsdl location="http://localhost:8080/helloWS/hello?WSDL" packageName="staticstub"/> </configuration>    wscompile工具读取服务器上的WSDL文件并生成stub ·编写静态客户代码  Stub stub=(Stub)(new HelloService_Impl().getHelloIFPort());  HelloIF hello=(HelloIF)stub;  Hello.sayHello(“starchu”); 注意:HelloService_Impl类由wscompile生成   ·编译代码 ·运行客户端(需要包含saaj API和JAX-RPC API运行) Dynamic Proxy客户 ·生成接口 通过使用config-wsdl.xml文件和wscompile工具,可以生成客户所需的接口 wscompile -import -d build -classpath build config-wsdl.xml config-wsdl.xml和前面列出的文件内容相同。 ·编写动态客户代码  ServiceFactory factory=ServiceFactory.newInstance();  URL wsdlUrl=new URL(“<your web service wsdl url>”);  Service service=factory.createService(wsdlUrl,   new QName(“urn:Star”,”HelloService”));  HelloIF hello=(HelloIF)service.getPort(   new QName(“urn:Star”,”HelloIFPort”),HelloIF.class);  Hello.sayHello(“starchu”);  注意:这里不再需要静态客户代码的HelloService_Impl类   ·编译代码 ·运行客户端(需要包含saaj API和JAX-RPC API运行) Dynamic Invocation Interface客户 这个方法为我们提供了更有弹性的客户调用方式,客户代码不在需要由wscompile工具生成的运行时类,当然这种代码更加复杂。具体步骤如下: ·创建ServiceFactory实例  ServiceFactory factory=ServiceFactory.newInstance(); 
·创建Service(利用服务名的Qname)  Service service=factory.createService(new QName(“HelloService”)); 
·创建Call对象(使用端点接口的Qname)  Call call=service.createCall(new QName(“HelloIF”)); 
·设置端点的地址和一些Call对象属性  call.setTargetEndpointAddress(args[0]);  call.setProperty(Call.SOAPACTION_USE_PROPERTY,new Boolean(true));  call.setProperty(Call.SOAPACTION_URI_PROPERTY,””);  call.setProperty(“javax.xml.rpc.encodingstyle.namespace.uri”,     “http://schemas.xmlsoap.org/soap/encoding/”); 
·设置远程调用的返回类型、操作名和参数  QName stringType=new Qname(“http://www.w3.org/2001/XMLSchema”,”string”)  call.setReturnType(stringType);  call.setOperationName(new Qname(“urn:Star”,”sayHello”));  call.addParameter(“String_1”,stringType,ParameterMode.IN); 
·调用call的invoke方法  String [] param={ “ starchu “ };  String retValue=call.invoke(param); 
·编译代码并对Main方法设置<http://localhost:8080/helloWS/hello参数(服务器需有效>) 
3.SOAP Message Handler的例子 通常使用JAX-RPC建立的Web Service并不需要开发人员自己处理SOAP消息,但是JAX-RPC提供了一种机制可以使程序员获得这种处理能力,这就是所谓的消息处理器。总的来说,像日志和加解密功能可以通过SOAP消息处理器实现,除此之外,你根本不需要处理SOAP消息。 ·基本Handler处理过程  SOAP请求  ·客户端处理器在请求消息发送到服务器前被调用  ·服务端处理器在请求消息分发到端点前被调用  SOAP应答  ·服务端处理器在应答消息发送回客户前被调用  ·客户端处理器在应答消息转换成Java方法返回前被调用  SOAP错误  处理过程与SOAP应答的方式一样 注意:处理器可以在任意端组成处理器链 A.Handler基本编程模型  服务端  ·编写服务端点接口代码、实现服务并且实现服务端处理器类  ·创建jaxrpc-ri.xml文件,以便wscompile使用,其中包含了Handler的信息  ·创建web.xml文件  ·编译所有代码  ·将文件打包为WAR文件  ·用wsdeploy工具将原始war文件替换为完整可部署的war文件  ·在服务器上部署war文件  客户端  ·编写客户程序以及客户端处理器代码  ·创建config.xml文件以便wscompile使用,它包含了客户端处理器的信息  ·编译代码  ·运行wscompile生成服务端点接口和客户类  ·编译所有代码,并运行客户应用 
B.建立客户端处理器 处理器必须扩展javax.xml.rpc.handler.GenericHandler类并且提供至少两个方法的实现init和getHandlers。此外,你可以利用handleXXX方法处理请求、应答和错误SOAP消息。基本步骤如下  ·编写客户端处理器代码  Public class ClientHandler extends GenericHandler{   Public void init(HandlerInfo info){    This.info=info;   }   public QName[] getHeaders(){    return info.getHeaders();   }   public boolean handleRequest(MessageContext context){    SOAPMessageContext smc=(SOAPMessageContext)context;    SOAPMessage message=smc.getMessage();    file://You can use SOAP API to implement your own logic    file://such as logging and encrypt    ……    file://Set a logger element in the SOAPHeader    SOAPHeaderElement loggerElement= header.addHeaderElement(envelope.createName(“loginfo”,  “ns1”,”urn:Star:headprops”));      loggerElement.setMustUnderstand(true);      loggerElement.setValue(“10”);      file://Set a name element in the SOAP Header    SOAPHeaderElement nameElement= Header.addHeaderElement(envelope.createName(“client”,      “ns1”,”urn:Star:headprops”));      nameElement.addTextNode(“Star chu”);   }  }  ·编辑config.xml文件   <?xml version=”1.0” encoding=”UTF-8”?>   <configuration xmlns=”http://java.sun.com/xml/ns/jax-rpc/ri/config”?>    <wsdl location=”http://localhost:8080/handlerWS/handler?WSDL    packageName=”client”>    <handlerChains>      <chain runAt=”client”>     <handler className=”client.ClientHandler”>       <property name=”name” value=”client handler”/>     </handler>      </chain>    </handlerChains></wsdl></configuration>  ·编写静态客户 C.建立服务端处理器  ·编写服务端处理器(与客户端结构类似)   Public boolean handleRequest(MessageContext context){    SOAPMessageContext smc=(SOAPMessageContext)context;    ……    Iterator it=header.examineAllHeaderElements();    While(it.hasNext()){     SOAPElement element=(SOAPElement)it.next();     If(element name is loginfo and must understand it){      element.getValue();      element.detach();      file://Invoke only when the setMustUnderstand(true)     }    }   }  detach方法用来移除元素,这个需求仅当一个元素设置了mustUnderstand属性在必要。  ·编辑jaxrpc-ri.xml文件   <?xml version=”1.0” encoding=”UTF-8”?>   <webServices xmlns=”http://java.sun.com/jax-rpc/config/ri/dd”    version=”1.0” targetNamespaceBase=”urn:Star:wsdl”    typeNamespaceBase=”urn:Star:types”    urlPatternBase=”/handler”>    <endpoint name=”HandlerTest”     displayName=”Handler Test”     description=” … …”     interface=”service.HandlerTest”     model=”/WEB-INF/model.gz”     implementation=”service.HandlerTestImpl”>      <handlerChains>       <chain runAt=”server”>     <handler className=”service.LoggerHandler”       headers=”ns1:loginfo” xmlns:ns1=”urn:Star:headerprops”> <property name=”name” value=”Logger”/>     </handler>     <handler className=”service.NameHandler”>       <propery name=”name” value=”Name”/>     </handler>       </chain>      </handlerChains>    </endpoint>    <endpointMapping endpointName=”HandlerTest”  urlPattern=”/handler”/>   </webServices> 在第一个处理器中,XML使用了属性 headers描述头信息。这是因为客户代码告诉服务端,logger头必须被理解,否则客户将收到SOAP错误消息 ·生成WAR文件并部署到服务器上 4.源代码 ·HelloIF.java(endpoint接口) package helloservice; 
import java.rmi.RemoteException; import java.rmi.Remote; 
public interface HelloIF extends Remote{  public String sayHello(String target) throws RemoteException; } ·HelloImpl.java package helloservice; 
public class HelloImpl implements HelloIF{  private String message="Hello";  public String sayHello(String target){   return message+target;  } } ·StaticClient.java package staticstub; import javax.xml.rpc.Stub; public class StaticClient{  private static String endpointAddress;  public static void main(String [] args){   if(args.length!=1){    System.err.println("Usage : java HelloClient [endpoint address]");    System.exit(-1);   }     endpointAddress=args[0];   System.out.println("Connect to :"+endpointAddress);   try{    Stub stub=createStub();    stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,      endpointAddress);    HelloIF hello=(HelloIF)stub;    System.out.println(hello.sayHello(" Starchu!"));     }catch(Exception e){System.err.println(e.toString());}  }  private static Stub createStub(){   return (Stub)(new HelloService_Impl().getHelloIFPort());  } }  ·DynamicClient.java package dynamicproxy; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.rpc.Service; import javax.xml.rpc.ServiceFactory; import javax.xml.rpc.JAXRPCException; import staticstub.HelloIF; 
public class DynamicClient{  private static String wsdl;   private static String namespaceUri="urn:Star:wsdl";     private static String serviceName="HandlerService";    private static String portName="HandlerTestPort";    
 public static void main(String [] args){   if(args.length!=1){    System.err.println("Usage : java DynamicClient [server Url]");    System.exit(-1);   }   System.out.println("Connect to :"+args[0]);   helloWsdl=args[0]+"?WSDL";   try{    URL wsdlUrl=new URL(wsdl);    ServiceFactory serviceFactory=ServiceFactory.newInstance();    Service service=    serviceFactory.createService(wsdlUrl,     new QName(namespaceUri,serviceName));    HandlerTest proxy=(HandlerTest)service.getPort(    new QName(namespaceUri,portName),HandlerTest.class);    proxy.test();   }catch(Exception e){    System.err.println(e.toString());   }  } }  ·DIIClient.java package dii; import javax.xml.rpc.*; import javax.xml.namespace.*; 
public class DIIClient{  private static String qnameService = "HelloService";  private static String qnamePort = "HelloIF";  private static String BODY_NAMESPACE_VALUE ="urn:Star"; private static String ENCODING_STYLE_PROPERTY ="javax.xml.rpc.encodingstyle.namespace.uri";  private static String NS_XSD ="http://www.w3.org/2001/XMLSchema"; private static String URI_ENCODING ="http://schemas.xmlsoap.org/soap/encoding/"; 
 public static void main(String [] args){   try{      ServiceFactory factory=ServiceFactory.newInstance();   Service service=factory.createService(new QName(qnameService));   QName port=new QName(qnamePort);   Call call=service.createCall(port);   call.setTargetEndpointAddress(args[0]);   call.setProperty(Call.SOAPACTION_USE_PROPERTY,new Boolean(true));   call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");   call.setProperty(ENCODING_STYLE_PROPERTY,URI_ENCODING);   QName qnameTypeString=new QName(NS_XSD,"string");   call.setReturnType(qnameTypeString); call.setOperationName(new QName(BODY_NAMESPACE_VALUE,"sayHello"));   call.addParameter("String_1",qnameTypeString,ParameterMode.IN);   String [] params = { "Starchu" };   System.out.println((String)call.invoke(params));     }catch(Exception e){    System.err.println(e.toString());   }  } }  ·Ant文件build.xml <project name="helloWS" basedir="." default="deploy">  <property file="build.properties"/>  <property name="build" value="build"/>  <property name="dist" value="${build}\classes"/>  <property name="lib" value="${build}\lib"/>  <property name="src" value="src"/>  <property name="etc" value="${src}\etc"/> 
 <target name="clean">  <delete dir="${build}"/>  </target> 
 <target name="init">  <mkdir dir="${build}"/>  <mkdir dir="${dist}"/>  <mkdir dir="${lib}"/>  </target> 
 <path id="classpath">  <fileset dir="${tomcat.home}">    <include name="jaxrpc/**/*.jar"/>    <include name="jaxb/**/*.jar"/>          <include name="jaxp/**/*.jar"/>    <include name="saaj/**/*.jar"/>          <include name="jwsdp-shared/lib/**/*.jar"/>  </fileset>  <pathelement path="${dist}"/>  <pathelement location="${lib}"/>  </path> 
 <target name="compile-service" depends="init"> <javac srcdir="${src}" destdir="${dist}" includes="HelloService/**/*.java"/>  </target> 
 <target name="generate-sei-service" depends="compile-service">  <exec executable="wscompile.bat">        <arg line="-define -d ${build} -nd ${build} -classpath ${dist} ${etc}\config-interface.xml -model ${build}\model.gz"/>  </exec>  <copy todir="${build}">   <fileset dir="${etc}" includes="*.xml"/>  </copy>  </target> 
 <target name="package-service" depends="generate-sei-service">       <war warfile="${build}\${ant.project.name}-portable.war"          webxml="${build}\web.xml">           <webinf dir="${build}" includes="*.xml,*.gz,*.wsdl" excludes="web.xml"/> <classes dir="${dist}" includes="**/*.class" defaultexcludes="no"/>       </war>  </target> 
 <target name="process-war" depends="package-service">   <exec executable="wsdeploy.bat"> <arg line="-o ${ant.project.name}.war ${build}\${ant.project.name}-portable.war"/>  </exec>  </target> 
 <target name="deploy">  <copy file="${ant.project.name}.war" todir="${server}"/>  </target>    <target name="undeploy">  <delete file="${server}\${ant.project.name}.war"/>  </target> 
<!--      Generating Static Client    -->  <target name="generate-stubs" depends="init">  <exec executable="wscompile.bat"> <arg line="-gen:client -d ${dist} -classpath ${dist} ${etc}\config-wsdl.xml"/>  </exec>  </target> 
 <target name="compile-client" depends="generate-stubs">  <javac srcdir="${src}" destdir="${dist}" includes="StaticStub/**/*.java">   <classpath refid="classpath"/>  </javac>  </target>    <target name="package-client" depends="compile-client"> <jar destfile="${lib}\client.jar" basedir="${dist}" excludes="HelloService/**/*.class"/>   </target> 
 <target name="run-client" depends="package-client">       <java classname="staticstub.HelloClient"           classpathref="classpath"           fork="true">        <sysproperty key="endpoint" value="${endpoint}"/>         <arg value="${server.port.url}"/>     </java>  </target> 
 <!--     Generating Dynamic Client   -->  <target name="generate-interface" depends="init">  <exec executable="wscompile.bat">    <arg line="-import -d ${dist} -classpath ${dist} ${etc}\config-wsdl.xml"/>  </exec>  </target> 
 <target name="compile-dynamic-client" depends="generate-interface"> <javac srcdir="${src}" destdir="${dist}" includes="DynamicProxy/**/*.java">  <classpath refid="classpath"/> </javac>  </target>    <target name="package-dynamic-client" depends="compile-dynamic-client"> <jar destfile="${lib}\client.jar" basedir="${dist}" includes="**/HelloIF.class,**/DynamicClient.class"/>   </target> 
 <target name="run-dynamic-client" depends="package-dynamic-client">     <java classname="dynamicproxy.DynamicClient"           classpathref="classpath"           fork="true">        <sysproperty key="endpoint" value="${endpoint}"/>         <arg value="${server.port.url}"/>     </java>  </target> 
 <!--     Generating Dynamic Invocation Interface   --> 
 <target name="compile-dii">  <javac srcdir="${src}" destdir="${dist}" includes="DII/**/*.java">   <classpath refid="classpath"/>  </javac>  </target> 
 <target name="run-dii" depends="compile-dii">     <java classname="dii.DIIClient"           classpathref="classpath"           fork="true">        <sysproperty key="endpoint" value="${endpoint}"/>         <arg value="${server.port.url}"/>     </java>  </target> 
</project> ·属性文件(build.xml文件使用) server=C:/Java/jwsdp-1.2/webapps tomcat.home=C:/Java/jwsdp-1.2 endpoint=http://localhost:8080/helloWS/hello server.port.url=http://localhost:8080/helloWS/hello 
参考资料 1. Developing Web Service Series <http://www.theserverside.com/resources/article.jsp?l=Systinet-web-services-part-1> www.theserverside.com 2. JWSDP-1.2 Tutorial  java.sun.com 
声明:如需转帖,请标明出处
   
 
  |