CSDN博客

img starchu1981

Web Service学习笔记之----JAX-RPC

发表于2003/10/2 1:17:00  5002人阅读

分类: WebService

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-1www.theserverside.com
2. JWSDP-1.2 Tutorial  java.sun.com

声明:如需转帖,请标明出处

0 0

相关博文

我的热门文章

img
取 消
img