WebService入门

来源:互联网 发布:科比13数据 编辑:程序博客网 时间:2024/04/30 12:35

Web Service在java领域,AXIS因其容易使用,很多项目都使用了它,对于Sun提供的Web Service技术,则不多使用,现更新有关版本,重新使用了一下Sun提供的JWSDP,并写了这篇入门.供需要者参考.

一 Sun公司的Web Service
       Web服务是基于web的使用开放的基于xml标准的企业应用,是和调用客户端交换数据的传输协议,Java技术和WEB服务被组织成这些亚类:

  Java Web Services Developer Pack (Java WSDP) 
  Java API for XML-Based RPC (JAX-RPC) 
  Java API for XML Registries (JAXR) 
  Java API for XML Processing (JAXP) 
  Java Architecture for XML Binding (JAXB) 
  SOAP with Attachments API for Java (SAAJ) 
  XML and Web Services Security 

JAX-RPC代表基于xml远程过程调用的java API,是一项为构建使用RPC和XML的Web服务和客户端的技术,通常用于分布式的CS模式中,它是一种RPC机制能使客户端执行其他系统上的过程.
在JAX-RPC中,一个RPC由基于XML协议如SOAP表示,这个SOAP规范定义了一个信封结构,编码规则以及为代表远程调用和响应的协定.这些调用和响应作为SOAP消息在HTTP上传输.

尽管SOAP消息复杂,但JAX-RPC API向应用开发者隐藏了这些复杂性.在服务器端,开发者指定那个调用过程是通过用java语言写的接口定义的方法, 开发者还要编码一个或多个实现这些方法的类.客户端程序也容易编码:一个客户端创建一个代理(一个代表服务的本地对象),然后简单的在那个代理上调用那个方法.使用JAX-RPC,开发者不用生成或分析SOAP消息,这是JAX-RPC运行时系统做的事情:它在来往的API调用和响应到SOAP消息之间进行转换.

使用JAX-RPC,客户端和web services有一个巨大的好处:java编程语言的平台独立性.此外,JAX-RPC没有限制: 一个JAX-RPC客户端能访问一个并不在java平台上运行的web服务,反之亦然.这个灵活性是可能的以为JAX-RPC使用由W3C定义的技术:HTTP, SOAP,WSDL. WSDL为把一个服务在消息上描述为一套端点操作指定了一个XML格式.

二 SUN提供的WEB服务构建工具

J2EE提供了APIs和创建,发布可互操作WS和客户端的工具,其中JAX-RPC(SI)标准实现提供了wscompile 和 wsdeploy 工具,使你开发,发布,调用Web Service:

  • wscompile可接受两种输入:WSDL和Service Endpoint Interface,输出为可移植制品和model文件.
  • wsdeploy只有一种输入:包括wscompile输入和输出再加上一个部署描述符文件,输出是可发布的特定平台上的web service.

这些工具同Java WSDP一起打包可供使用,java web服务开发包是一个免费的集成工具箱,用它构建,测试,发布XML应用,web服务以及带有最新web服务技术和标准实现的web应用.

(一)  wscompile工具

        1 工具介绍

        它生成由JAX-RPC运行时需要的各种客户端和服务端制品以供开发,部署,调用一个Web服务. 这个工具有两种使用形式:批处理和Ant构建脚本.

批的例子:
  wscompile -gen -classpath lib/foo.jar;lib/bar.jar -d generated config.xml
  wscompile -gen  -f:infix:Name -d generated config.xml
  wscompile -define -f:nodatabinding -f:novalidation config.xml
  wscompile -import -f:explicitcontext config.xml
Ant脚本,在Ant中使用这个任务前要先定义:

<taskdef name="wscompile" classname="com.sun.xml.rpc.tools.ant.Wscompile">
    <classpath path="${classpath}"/>
</taskdef>
其中的类路径要指向以下档案库:


jaxrpc-api.jar
jaxrpc-impl.jar
jaxrpc-spi.jar
saaj-api.jar
saaj-impl.jar
jaxp-api.jar
dom.jar
sax.jar
xalan.jar
xercesImpl.jar
activation.jar
mail.jar
jax-qname.jar
namespace.jar
xsdlib.jar
relaxngDatatype.jar


例子:
<wscompile gen="true" base="${build}" features="explicitcontext"  classpath="xyz.jar" debug="true" config="config.xml">
<wscompile import="true" keep="true" sourceBase="${source.dir}" base="${build}" model="model.xml.gz" PrintStackTrace="true"  config="config.xml">
    <classpath refid="compile.classpath"/>
</wscompile>
<wscompile  fork="true"  define="true"   nonClassDir="${wsdl.dir}"   features="documentliteral, useonewayoperations" config="config.xml"/>

        2  wscompile的配置文件(config.xml)
        这个配置文件是wscompile工具的主要信息源,这些信息同工具调用期间指定的一些开关协同工作来生成需要的制品.
有四种不同的方式向工具提供信息,每种提供信息的方式需要不同格式的配置文件,每个格式允许工具的多种模式(-import, -define, and -gen).四种方式为:

  • service,一套基于服务端点接口(SEI)的服务描述.
  • wsdl,一个要导入和处理的WSDL文档.当一个的描述被一个已经存在的WSDL文档提供或是客户端制品需要根据WSDL生成时使用这个格式. 例如:
    <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
        <wsdl location="./StockQuoteService.wsdl"
            pacakgeName="com.example"/>
    </configuration>
  • modelfile,一个前面保存的model文件(在wscompile通过-model选项生成).
    这个文件是JAX-RPC标准实现对一个WS端点或客户端表示,由wscompile工具生成,通常用作一个.xml.gz文件.它存储了所有内部数据结构的关键信息,以容许容易的生成实现特定的制品. 因为wsdeploy只支持有限的一套选项,很多选项都指定在wscompile阶段期间,它们保存成model的一部分.当定义一个服务时或是客户端制品需要从已存在的model文件生成时使用.例如:
    <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
        <modelfile location="./model.xml.gz"/>
    </configuration>
  • j2eeMappingFile,由JSR109定义的J2EE影射信息.
    这个文件包括在java接口和WSDL定义之间的影射的对应信息.J2EE部署工具使用这个信息和WSDL文件一起为发布的服务和服务引用生成stubs和ties.

配置文件还有三个配置项:

  • 添加类型影射,可定义自己的串行化器和反串行化器,提供对JAX-RPC运行时不支持的数据类型的支持.
  • 添加处理器
  • 添加namespace-to-package影射.这个功能使你指定XML命名空间到java包的影射,也可以指定java包到XML命名空间的影射.

  (二) wsdeploy工具
           1 工具介绍

           这个工具和wscompile协同工作来生成一个能在任何servlet容器中发布的war文件,它授受一个生WAR文件,它包括一个SEI,一个SEI实现,一些值类型,一些服务特定的异常,一个model文件,一个部署描述符(jaxrpc-ri.xml), 和一些可选的其它实现特定的制品.工具处理生的war文件并生成一个实现特定的熟的war文件.这个过程包括生成串行化器,连接(TIEs),运行时描述符以及其它为了成功的发布WS需要的制品. 这个工具也有两种使用形式:批处理和Ant构建脚本.
批的例子:

wsdeploy -o target.war myapp.war
Ant脚本,在Ant中使用这个任务前要先定义:

<taskdef name="wsdeploy" classname="com.sun.xml.rpc.tools.ant.Wsdeploy">
    <classpath path="${classpath}"/>
</taskdef>

其中的类路径指向同wscompile相同:

<wsdeploy  classpath="xyz.jar"  tmpdir="${tmp}"  outWarFile="stock.war"  inWarFile="stock-raw.war"/>
<wsdeploy  keep="true" outWarFile="stock.war" inWarFile="stock-raw.war"><classpath refid="compile.classpath"/></wsdeploy>
<wsdeploy fork="true" source="1.0.3" outWarFile="stock.war"  inWarFile="stock-raw.war"></wsdeploy>

    2  wscompile的配置文件(部署描述符 jaxrpc-ri.xml)

它被打包在生的WAR文件中,并由wsdeploy工具使用来生成熟的WAR文件.这个描述符提供了wsdeploy工具向web容器中发布WS的信息.
该文件主要是使用endpoint和endpointMapping两个元素来定义端点和服务影射,把SEI及其实现聚集在一起,连同model文件提供给发布工具.

三 编写一个Hello例子

IDE环境:Eclipse,插件sysdeo,发布环境:Tomcat559.

使用类库:Sun公司的参考实现.

本例要使用的材料:

  • HelloIF.java,一个SEI接口,它扩展了Remote接口.
  • HelloImpl.java,实现了SEI接口.
  • web.xml     ,这个文件可以是你的任何Web应用的部署描述符文件,这个例子可以发布到你的现有Web应用,只需要把那个应用的部署描述符文件放在开发环境的WEB-INF文件夹下.
  • config-interface.xml,描述SEI接口.
  • config-wsdl.xml,使用WSDL描述服务接口,本例不介绍这个文件的使用方法,通常这个使用场景多是为访问服务生成客户端stub文件.另一个场景就是加入自定义的java类型到SOAP类型的影射等等很多细微的控制,或者是完全替代SEI接口.

以上文件的位置,可参看下图:

1 编写SEI,HelloIF:

package helloservice;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloIF extends Remote {
    public String sayHello(String s) throws RemoteException;
}

2  实现SEI:

package helloservice;

public class HelloImpl implements HelloIF {

    public String message ="Hello";

    public String sayHello(String s) {
        return message + s;
    }
}

3  配置文件 config-interface.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
 <service name="MyHelloService" targetNamespace="urn:Foo"
  typeNamespace="urn:Foo" packageName="helloservice">
  <interface name="helloservice.HelloIF"
   servantName="helloservice.HelloImpl" />
 </service>
</configuration>

4  构建服务,下面是一个Ant构建脚本.

<?xml version="1.0" encoding="GBK"?>
<project name="webservice" default="build" basedir=".">
 <property name="jaxrpc.lib.dir" value="D:/Sun/AppServer/lib">
 </property>
 <property name="classes.dir" value="./build/classes">
 </property>
 <property name="src.dir" value="./build/src">
 </property>
 <property name="war.file" value="hello_raw.war">
 </property>

 <property name="nonclass.dir" value="./build/nonclass">
 </property>
 <property name="build" value="${nonclass.dir}">
 </property>
 <property name="assemble" value="./assemble">
 </property>
 <property name="assemble.war" value="./assemble/war">
 </property>
 <property name="assemble.ear" value="./assemble/ear">
 </property>
 <path id="jaxrpc-classpath">
  <fileset dir="${jaxrpc.lib.dir}">
   <include name="**/*.jar" />
  </fileset>
  <fileset dir="D:/jdbc/postgresql">
   <include name="*.jar" />
  </fileset>
 </path>
 <path id="compile.classpath">
  <fileset dir="${jaxrpc.lib.dir}">
   <include name="**/*.jar" />
  </fileset>
  <fileset dir="D:/jwsdp-1.5/jaxrpc/lib">
   <include name="**/*.jar" />
  </fileset>
 </path>
 <taskdef name="wscompile" classpathref="jaxrpc-classpath" classname="com.sun.xml.rpc.tools.ant.Wscompile">
 </taskdef>
 <taskdef name="wsdeploy" classpathref="jaxrpc-classpath" classname="com.sun.xml.rpc.tools.ant.Wsdeploy">
 </taskdef>
 <target name="prepare">
  <mkdir dir="${src.dir}" />
  <mkdir dir="${nonclass.dir}" />
  <mkdir dir="${classes.dir}" />
  <mkdir dir="${assemble}" />
  <mkdir dir="${assemble.war}" />
  <mkdir dir="${assemble.ear}" />
 </target>
 <target name="build" depends="prepare" description="生成java制品,如生成可供wsdeploy使用的web档案,称为生war,就象作菜一样,先顺菜,以及model,wsdl文件,这里生成的WSDL文件,wsdeploy并不使用">
  <echo message="build the WAR...." />
  <wscompile import="false" define="true" gen="false" keep="true" base="${classes.dir}" sourceBase="${src.dir}" classpath="./classes" nonClassDir="${nonclass.dir}" model="model.xml.gz" xPrintStackTrace="true" config="config-interface.xml" verbose="true">
   <classpath refid="compile.classpath" />
  </wscompile>
 </target>
 <target name="deploy" description="生成可供发布的web档案,称为煮熟的war,这个阶段也生成了WSDL文件并被直接打包了">
  <echo message="deploy the WAR...." />
  <wsdeploy keep="false" verbose="true" tmpDir="./tmp" outWarFile="skysoft.war" inWarFile="hello_raw.war">
   <classpath refid="compile.classpath" />
  </wsdeploy>
 </target>
 <target name="create-war" description="打包由wscompile生成的制品,以及所有发布所用的材料">
  <echo message="Creating the WAR...." />
  <delete file="${assemble.war}/${war.file}" />
  <delete dir="${assemble.war}/WEB-INF" />
  <copy todir="${assemble.war}/WEB-INF/classes/">
   <!--fileset dir="${build}/" includes="**/*.class" excludes="**/*Client.class, **/*.wsdl, **/*mapping.xml" /-->
   <fileset dir="./classes" includes="**/*.class" excludes="**/*Client.class, **/*.wsdl, **/*mapping.xml" />
  </copy>
  <copy todir="${assemble.war}/WEB-INF/lib/">
   <fileset dir="./lib" includes="**/*.jar" excludes="**/*Client.class, **/*.wsdl, **/*mapping.xml" />
  </copy>
  <copy file="jaxrpc-ri.xml" todir="${assemble.war}/WEB-INF" />
  <copy file="model.xml.gz" todir="${assemble.war}/WEB-INF" />
  <war destfile="${assemble.war}/${war.file}" webxml="./web.xml" filesonly="true">
   <fileset dir="${assemble.war}" includes="WEB-INF/**, build/**" />
  </war>
  <copy file="${assemble.war}/${war.file}" todir="." />
 </target>
 <target name="genstaticstub" description="生成静态桩,供静态的调用服务">
  <echo message="gen statics tub" />
  <wscompile client="true" keep="true" base="." sourceBase="." xPrintStackTrace="true" config="config-wsdl.xml" verbose="true">
   <classpath refid="compile.classpath" />
  </wscompile>
 </target>
 <target name="generate-dynamic-interface" description="根据WSDL文件生成SEI及其它材料,供动态调用">
  <echo message="generate dynamic interface" />
  <wscompile import="true" keep="true" features="norpcstructures" base="./dynmicstub" sourceBase="./dynmicstub" xPrintStackTrace="true" config="config-wsdl.xml" verbose="true">
   <classpath refid="compile.classpath" />
  </wscompile>
 </target>
</project>

其中的各个任务都有说明.

注意,如果你使用JAX-RPC1.1.2的话,wsdeploy生成web.xml文件有些小错误,需要手工更改,这个问题在1.1.3中已经修正.

好了,在Eclipse中运行各个任务,把最后得到的skysoft.war,作为一个新的应用发布,也可发布到你现有的应用,需要解压这个文件,然后拷贝需要的文件以及库文件到你的应用中即可.

(四) 调用服务


调用服务的方式有三种:静态调用,动态调用,动态调用接口.

1 静态调用,使用了wscompile生成的静态桩文件.

package hello;

import javax.xml.rpc.Stub;
import staticstub.*;

public class HelloClient {
 private String endpointAddress;

 public static void main(String[] args) {
  args=new String[]{"http://localhost:8080/skysoft/hello?WSDL"};
  System.out.println("Endpoint address = " + args[0]);
  try {
   Stub stub = createProxy();
   stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
     args[0]);
   HelloIF hello = (HelloIF) stub;
   System.out.println(hello.sayHello("Duke!"));
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }

 private static Stub createProxy() {
  // Note: MyHelloService_Impl is implementation-specific.
  return (Stub) (new MyHelloService_Impl().getHelloIFPort());
 }

}

2 动态调用,主要是使用ServiceFactory来执行调用.

package hello;

import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
//import dynamicproxy.HelloIF;
import staticstub.HelloIF;

public class DynClient {
 public static void main(String[] args) {
  try {
   args = new String[] { "http://localhost:8080/skysoft/hello" };
   String UrlString = args[0] + "?WSDL";
   String nameSpaceUri = "urn:Foo";
   String serviceName = "MyHelloService";
   String portName = "HelloIFPort";

   System.out.println("UrlString = " + UrlString);

   URL helloWsdlUrl = new URL(UrlString);

   ServiceFactory serviceFactory = ServiceFactory.newInstance();

   Service helloService = serviceFactory.createService(helloWsdlUrl,
     new QName(nameSpaceUri, serviceName));

   HelloIF myProxy = (HelloIF) helloService.getPort(new QName(
     nameSpaceUri, portName), HelloIF.class);

   System.out.println(myProxy.sayHello("Buzz"));
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

3 动态调用接口(DII),这种方式不需要任何附加代码,根据WSDL提供的调用方法的元描述,动态确定方法及其参数返回类型,有些类似于CORBA的接口池提供的服务.

package hello;

import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;

public class DiiClient {

 private static String qnameService = "MyHelloService";

 private static String qnamePort = "HelloIF";

 private static String BODY_NAMESPACE_VALUE = "urn:Foo";

 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) {
  args=new String[]{"http://localhost:8080/skysoft/hello?WSDL"};
  System.out.println("Endpoint address dii = " + args[0]);

  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 QNAME_TYPE_STRING = new QName(NS_XSD, "string");

   call.setReturnType(QNAME_TYPE_STRING);

   call.setOperationName(new QName(BODY_NAMESPACE_VALUE, "sayHello"));
   call.addParameter("String_1", QNAME_TYPE_STRING, ParameterMode.IN);

   String[] params = { "Murph!" };

   String result = (String) call.invoke(params);

   System.out.println(result);
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
}

到此,这个Web Service入门描述就结束了,它提供了WS的Sun描述,以及在tomcat上发布服务的过程和一个构建脚本,借助jsp插件,使用Eclipse完成了一个简单的Web服务开发,发布,并用三种方式进行了测试.

原创粉丝点击