Axis2是一套崭新的WebService引擎,该版本是对Axis1.x重新设计的产物。Axis2不仅支持SOAP1.1SOAP1.2,还集成了非常流行的REST WebService,同时还支持SpringJSON等技术。这些都将在后面的系列教程中讲解。在本文中主要介绍了如何使用Axis2开发一个不需要任何配置文件的WebService,并在客户端使用JavaC#调用这个WebService

一、Axis2的下载和安装

    读者可以从如下的网址下载Axis2的最新版本:

    http://ws.apache.org/axis2/

    在本文使用了目前Axis2的最新版本1.4.1。读者可以下载如下两个zip包:

    axis2-1.4.1-bin.zip

    axis2-1.4.1-war.zip

    其中axis2-1.4.1-bin.zip文件中包含了Axis2中所有的jar文件, axis2-1.4.1-war.zip文件用于将WebService发布到Web容器中。

    axis2-1.4.1-war.zip文件解压到相应的目录,将目录中的axis2.war文件放到<Tomcat安装目录>\webapps目录中(本文使用的Tomcat的版本是6.x),并启动Tomcat

    在浏览器地址栏中输入如下的URL

    http://localhost:8080/axis2/

    如果在浏览器中显示出如图1所示的页面,则表示Axis2安装成功。



图1

二、编写和发布WebService

  对于用Java实现的服务程序给人的印象就是需要进行大量的配置,不过这一点在Axis2中将被终结。在Axis2中不需要进行任何的配置,就可以直接将一个简单的POJO发布成WebService。其中POJO中所有的public方法将被发布成WebService方法。

    下面我们来实现一个简单的POJO,代码如下:

public class SimpleService
{
    
public String getGreeting(String name)
    {
        
return "你好 " + name;
    }    
    
public int getPrice()
    {
        
return new java.util.Random().nextInt(1000);
    }    
}

    SimpleService类中有两个方法,由于这两个方法都是public方法,因此,它们都将作为WebService方法被发布。

    编译SimpleService类后,将SimpleService.class文件放到<Tomcat安装目录>\webapps\axis2\WEB-INF\pojo目录中(如果没有pojo目录,则建立该目录)。现在我们已经成功将SimpleService类发布成了WebService。在浏览器地址栏中输入如下的URL

http://localhost:8080/axis2/services/listServices

    这时当前页面将显示所有在Axis2中发布的WebService,如图2所示。



图2

    在浏览器地址栏中输入如下的两个URL来分别测试getGreetinggetPrice方法:

http://localhost:8080/axis2/services/SimpleService/getGreeting?name=bill

http://localhost:8080/axis2/services/SimpleService/getPrice

    3和图4分别显示了getGreetinggetPrice方法的测试结果。

图3  getGreeting方法的测试结果

图4  getPrice方法的测试结果

    在编写、发布和测试0配置的WebService时应注意如下几点:

    1. POJO类不能使用package关键字声明包。

    2. Axis2在默认情况下可以热发布WebService,也就是说,将WebService.class文件复制到pojo目录中时,Tomcat不需要重新启动就可以自动发布WebService。如果想取消Axis2的热发布功能,可以打开<Tomcat安装目录>\webapps\axis2\WEB-INF\conf\axis2.xml,找到如下的配置代码:

<parameter name="hotdeployment">true</parameter>

    true改为false即可。要注意的是,Axis2在默认情况下虽然是热发布,但并不是热更新,也就是说,一旦成功发布了WebService,再想更新该WebService,就必须重启Tomcat。这对于开发人员调试WebService非常不方便,因此,在开发WebService时,可以将Axis2设为热更新。在axis2.xml文件中找到<parameter name="hotupdate">false</parameter>,将false改为true即可。

    3. 在浏览器中测试WebService时,如果WebService方法有参数,需要使用URL的请求参数来指定该WebService方法参数的值,请求参数名与方法参数名要一致,例如,要测试getGreeting方法,请求参数名应为name,如上面的URL所示。

    4. 发布WebServicepojo目录只是默认的,如果读者想在其他的目录发布WebService,可以打开axis2.xml文件,并在<axisconfig>元素中添加如下的子元素:

    <deployer extension=".class" directory="my" class="org.apache.axis2.deployment.POJODeployer"/>

    上面的配置允许在<Tomcat安装目录>"webapps"axis2"WEB-INF"my目录中发布WebService。例如,将本例中的SimpleService.class复制到my目录中也可以成功发布(但要删除pojo目录中的SimpleService.class,否则WebService会重名)。

三、Java实现调用WebService的客户端程序

    WebService是为程序服务的,只在浏览器中访问WebService是没有意义的。因此,在本节使用Java实现了一个控制台程序来调用上一节发布的WebService。调用WebService的客户端代码如下:

package client;

import javax.xml.namespace.QName;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;

public class RPCClient
{
    
public static void main(String[] args) throws Exception  
    {
        
//  使用RPC方式调用WebService        
        RPCServiceClient serviceClient = new RPCServiceClient();
        Options options 
= serviceClient.getOptions();
        
//  指定调用WebService的URL
        EndpointReference targetEPR = new EndpointReference(
                
"http://localhost:8080/axis2/services/SimpleService");
        options.setTo(targetEPR);
        
//  指定getGreeting方法的参数值
        Object[] opAddEntryArgs = new Object[] {"超人"};
        
//  指定getGreeting方法返回值的数据类型的Class对象
        Class[] classes = new Class[] {String.class};
        
//  指定要调用的getGreeting方法及WSDL文件的命名空间
        QName opAddEntry = new QName("http://ws.apache.org/axis2""getGreeting");
        
//  调用getGreeting方法并输出该方法的返回值
        System.out.println(serviceClient.invokeBlocking(opAddEntry, opAddEntryArgs, classes)[0]);
        
//  下面是调用getPrice方法的代码,这些代码与调用getGreeting方法的代码类似
        classes = new Class[] {int.class};
        opAddEntry 
= new QName("http://ws.apache.org/axis2""getPrice");
        System.out.println(serviceClient.invokeBlocking(opAddEntry, 
new Object[]{}, classes)[0]);
    } 
}

运行上面的程序后,将在控制台输出如下的信息:
你好 超人
443

    在编写客户端代码时应注意如下几点:

    1. 客户端代码需要引用很多Axis2jar包,如果读者不太清楚要引用哪个jar包,可以在Eclipse的工程中引用Axis2发行包的lib目录中的所有jar包。

    2. 在本例中使用了RPCServiceClient类的invokeBlocking方法调用了WebService中的方法。invokeBlocking方法有三个参数,其中第一个参数的类型是QName对象,表示要调用的方法名;第二个参数表示要调用的WebService方法的参数值,参数类型为Object[];第三个参数表示WebService方法的返回值类型的Class对象,参数类型为Class[]。当方法没有参数时,invokeBlocking方法的第二个参数值不能是null,而要使用new Object[]{}

    3. 如果被调用的WebService方法没有返回值,应使用RPCServiceClient类的invokeRobust方法,该方法只有两个参数,它们的含义与invokeBlocking方法的前两个参数的含义相同。

    4. 在创建QName对象时,QName类的构造方法的第一个参数表示WSDL文件的命名空间名,也就是<wsdl:definitions>元素的targetNamespace属性值,下面是SimpleService类生成的WSDL文件的代码片段:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ns1="http://org.apache.axis2/xsd"
xmlns:ns
="http://ws.apache.org/axis2" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:http
="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mime
="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12
="http://schemas.xmlsoap.org/wsdl/soap12/"
targetNamespace
="http://ws.apache.org/axis2"
>
    
<wsdl:types>
         
    
</wsdl:types>
     
</wsdl:definitions>

四、用wsdl2java简化客户端的编写

    也许有很多读者会说“有没有搞错啊,只调用两个WebService方法用要写这么多代码,太麻烦了”。

    不过幸好Axis2提供了一个wsdl2java.bat命令可以根据WSDL文件自动产生调用WebService的代码。wsdl2java.bat命令可以在<Axis2安装目录>"bin目录中找到。在使用wsdl2java.bat命令之前需要设置AXIS2_HOME环境变量,该变量值是<Axis2安装目录>

    Windows控制台输出如下的命令行来生成调用WebService的代码:

%AXIS2_HOME%\bin\wsdl2java -uri http://localhost:8080/axis2/services/SimpleService?wsdl -p client -s -o stub

    其中-url参数指定了wsdl文件的路径,可以是本地路径,也可以是网络路径。-p参数指定了生成的Java类的包名,-o参数指定了生成的一系列文件保存的根目录。在执行完上面的命令后,读者就会发现在当前目录下多了个stub目录,在."stub"src"client目录可以找到一个SimpleServiceStub.java文件,该文件复杂调用WebService,读者可以在程序中直接使用这个类,代码如下:

package client;

import javax.xml.namespace.QName;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;

public class StubClient
{
    
public static void main(String[] args) throws Exception  
    {
        SimpleServiceStub stub 
= new SimpleServiceStub();
        SimpleServiceStub.GetGreeting gg 
= new SimpleServiceStub.GetGreeting();
        gg.setName(
"比尔");
        System.out.println( stub.getGreeting(gg).get_return());
        System.out.println(stub.getPrice().get_return());
    } 
}

    上面的代码大大简化了调用WebService的步骤,并使代码更加简洁。但要注意的是,wsdl2java.bat命令生成的Stub类将WebService方法的参数都封装在了相应的类中,类名为方法名,例如,getGreeting方法的参数都封装在了GetGreeting类中,要想调用getGreeting方法,必须先创建GetGreeting类的对象实例。

五、使用C#调用WebService

    从理论上说,WebService可以被任何支持SOAP协议的语言调用。在Visual Studio中使用C#调用WebService是在所有语言中最容易实现的(VB.net的调用方法类似,也同样很简单)。

    新建一个Visual Studio工程,并在引用Web服务的对话框中输入如下的URL,并输入Web引用名为“WebService”:

    http://localhost:8080/axis2/services/SimpleService?wsdl

    然后引用Web服务的对话框就会显示该WebService中的所有的方法,如图5所示。



图5

    在完成上面的工作后,只需要如下三行C#代码就可以调用getGreetinggetPrice方法,并显示这两个方法的返回值:

WebService.SimpleService simpleService = new WSC.WebService.SimpleService();
MessageBox.Show( simpleService.getGreeting(
"比尔"));
MessageBox.Show(simpleService.getPrice().@return.ToString());

    .net解析WSDL文件时直接将getGreeting方法的参数映射为String类型,因此,可以直接进行传值。
    从上面的调用过程可以看出,添加Web引用的过程就相当于在Java中调用wsdl2java.bat自动生成stub类的过程。只是在调用stub类时与C#有一定的区别,但从总体上来说,都大大简化了调用WebService的过程。

posted @ 2009-02-23 17:37 zhuyongjp 阅读(912) | 评论 (0)编辑 收藏
 

接口 interface

import  J2EE技术的爬虫.util.Collection;
import  J2EE技术的爬虫.util.List;

public   interface  IMathService  {
 
/**
  * 加
  * 
@param  a
  * 
@param  b
  * 
@return
  
*/

 
public   int  add( int  a, int  b);
 
/**
  * 减
  * 
@param  a
  * 
@param  b
  * 
@return
  
*/

 
public   int  sub( int  a, int  b);
/**
上传二进制文件
*/

  
public  String sendFile(String fileName, byte [] file );
 
}



实现 implements

import  J2EE技术的爬虫.io.File;
import  J2EE技术的爬虫.io.FileOutputStream;
import  J2EE技术的爬虫.sql.Connection;
import  J2EE技术的爬虫.sql.ResultSet;
import  J2EE技术的爬虫.sql.Statement;
import  J2EE技术的爬虫.util.ArrayList;
import  J2EE技术的爬虫.util.Collection;
import  J2EE技术的爬虫.util.List;

import  com.newsoft.oa.bean.User;
import  com.newsoft.oa.uitl.Connector;
import  com.thoughtworks.xstream.XStream;

public   class  MathServiceImpl  implements  IMathService {
  
public   int  add( int  a, int  b) {
   
return  a + b;
  }

  
  
public   int  sub( int  a, int  b) {
   
return  a - b;
  }
 
  
public  String getWelComeStr(String name) {
   
return   " hi  " + name + " ! 欢迎你 " ;
  }

  
public  List getUsers() {
   List l
= new  ArrayList();
   l.add(
" name " );
   l.add(
" password " );
   l.add(
" sex " );
   
return  l;
  }


public  String sendFile(String fileName,  byte [] filebytes)  {
 
try {
  String path
= "" ;
   
if (filebytes != null && filebytes.length > 0 ) {
    File file
= new  File( " / " + fileName);
    
    file.createNewFile();
    FileOutputStream fos
= new  FileOutputStream(file);
    fos.write(filebytes);
    fos.close();
    path
= file.getAbsolutePath();
    System.out.println(path);
    
    file
= null ;
    
   }

   
   
return  path;
 }
catch (Exception ex) {
  
return   " false " ;
 }

}



}



 


配置文件

放在 Classes/META-INF/xfire/service.xml;里面

<? xml version="1.0" encoding="UTF-8" ?>
< beans  xmlns ="http://xfire.codehaus.org/config/1.0" >
 
< service >
  
< name > MathService </ name >
  
< namespace > newsoft/oa/MathService </ namespace >
  
< serviceClass >
   com.newsoft.oa.services.IMathService
  
</ serviceClass >
  
< implementationClass >
   com.newsoft.oa.services.MathServiceImpl
  
</ implementationClass >

 
</ service >
</ beans >


其实是借鉴了Spring的写法,用过Spring不会对着陌生,(Application-context.xml)

WEB-XML加上

< servlet >
  
< servlet-name > XFireServlet </ servlet-name >
  
< servlet-class >
   org.codehaus.xfire.transport.http.XFireConfigurableServlet
  
</ servlet-class >
 
</ servlet >

 
< servlet-mapping >
  
< servlet-name > XFireServlet </ servlet-name >
  
< url-pattern > /servlet/XFireServlet/* </ url-pattern >
 
</ servlet-mapping >

 
< servlet-mapping >
  
< servlet-name > XFireServlet </ servlet-name >
  
< url-pattern > /services/* </ url-pattern >
 
</ servlet-mapping >


最后就是客户端了

/ /Create a metadata of the service              
 Service serviceModel 
= new ObjectServiceFactory().create(IMathService.class); 
// Create a proxy for the deployed service      
  
 XFireProxyFactory factory 
= new XFireProxyFactory(XFireFactory.newInstance().getXFire());   
 String serviceUrl 
= "http://localhost:8080/ws/services/MathService";
  client 
= null;       
 
try {          
   client 
= (IMathService) factory.create(serviceModel, serviceUrl);  
   File file
=new File("c:\\SUPERMAP 白皮书.pdf");
   FileInputStream fis
=new FileInputStream(file);
   
byte[] b=new byte[fis.available()];
   fis.read(b);
  System.out.println(client.sendFile(file.getName(), b));
 }
 catch (Exception ex) {         
   ex.printStackTrace();
 }
                           //Invoke the service    
 int serviceResponse = 0;
 
int a=10,b=20;

就是

Service serviceModel = new ObjectServiceFactory().create(IMathService.class); 
 XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire());  
 String serviceUrl = http://localhost:8080/ws/services/MathService;

三行字建立连接请求,

太轻松了

传输文件速度也可以,二进制,2M多的文件,也能轻松传递

用.net和delphi平台测试,兼容性没问题(按道理 soap,也不应该有问题)

这是为客户搭建的在 档案系统和OA审批间作文件归档的尝试项目

哈,完整的项目代码,就不方便讲了。
posted @ 2009-02-20 15:07 zhuyongjp 阅读(709) | 评论 (0)编辑 收藏
 
我们项目的整个架构使用的比较流行的WSH MVC组合,即webwork2 + Spring + Hibernate;
1.首先集成Apacha CXF WebService 到 Spring 框架中;
   apache cxf 下载地址:http://people.apache.org/dist/incubator/cxf/2.0.4-incubator/apache-cxf-2.0.4-incubator.zip
  在spring context配置文件中引入以下cxf配置
   Xml代码
<import resource="classpath*:META-INF/cxf/cxf.xml" />  
<import resource="classpath*:META-INF/cxf/cxf-extension-soap.xml" />  
<import resource="classpath*:META-INF/cxf/cxf-servlet.xml" />  

在web.xml中添加过滤器:
   Xml代码

2.开发服务端WebService接口:
    Java代码
  1. /**  
  2.  * WebService接口定义类.  
  3.  *   
  4.  * 使用@WebService将接口中的所有方法输出为Web Service.  
  5.  * 可用annotation对设置方法、参数和返回值在WSDL中的定义.  
  6.  */  
  7. @WebService  
  8. public interface WebServiceSample {   
  9.   
  10.   
  11.     /**  
  12.      * 一个简单的方法,返回一个字符串  
  13.      * @param hello  
  14.      * @return  
  15.      */  
  16.     String say(String hello);   
  17.        
  18.     /**  
  19.      * 稍微复杂一些的方法,传递一个对象给服务端处理  
  20.      * @param user  
  21.      * @return  
  22.      */  
  23.     String sayUserName(   
  24.             @WebParam(name = "user")    
  25.             UserDTO user);   
  26.        
  27.     /**  
  28.      * 最复杂的方法,返回一个List封装的对象集合  
  29.      * @return  
  30.      */  
  31.     public    
  32.     @WebResult(partName="o")   
  33.     ListObject findUsers();   
  34.   
  35. }  

   由简单到复杂定义了三个接口,模拟业务需求;

3.实现接口

   Java代码
  1. /**  
  2.  * WebService实现类.  
  3.  *   
  4.  * 使用@WebService指向Interface定义类即可.  
  5.  */  
  6. @WebService(endpointInterface = "cn.org.coral.biz.examples.webservice.WebServiceSample")   
  7. public class WebServiceSampleImpl implements WebServiceSample {   
  8.   
  9.     public String sayUserName(UserDTO user) {   
  10.         return "hello "+user.getName();   
  11.     }   
  12.   
  13.     public String say(String hello) {   
  14.         return "hello "+hello;   
  15.     }   
  16.   
  17.     public ListObject findUsers() {   
  18.         ArrayList<Object> list = new ArrayList<Object>();   
  19.            
  20.         list.add(instancUser(1,"lib"));   
  21.         list.add(instancUser(2,"mld"));   
  22.         list.add(instancUser(3,"lq"));   
  23.         list.add(instancUser(4,"gj"));   
  24.         ListObject o = new ListObject();   
  25.         o.setList(list);   
  26.         return o;   
  27.     }   
  28.        
  29.     private UserDTO instancUser(Integer id,String name){   
  30.         UserDTO user = new UserDTO();   
  31.         user.setId(id);   
  32.         user.setName(name);   
  33.         return user;   
  34.     }   
  35. }  

 4.依赖的两个类:用户对象与List对象
     Java代码

  1. /**  
  2.  * Web Service传输User信息的DTO.  
  3.  *   
  4.  * 分离entity类与web service接口间的耦合,隔绝entity类的修改对接口的影响.  
  5.  * 使用JAXB 2.0的annotation标注JAVA-XML映射,尽量使用默认约定.  
  6.  *   
  7.  */  
  8. @XmlAccessorType(XmlAccessType.FIELD)   
  9. @XmlType(name = "User")   
  10. public class UserDTO {   
  11.   
  12.     protected Integer id;   
  13.   
  14.     protected String name;   
  15.   
  16.     public Integer getId() {   
  17.         return id;   
  18.     }   
  19.   
  20.     public void setId(Integer value) {   
  21.         id = value;   
  22.     }   
  23.   
  24.     public String getName() {   
  25.         return name;   
  26.     }   
  27.   
  28.     public void setName(String value) {   
  29.         name = value;   
  30.     }   
  31. }  

   关于List对象,参照了有关JWS的一个问题中的描述:DK6.0 自带的WebService中 WebMethod的参数好像不能是ArrayList 或者其他List
传递List需要将List 包装在其他对象内部才行 (个人理解 如有不对请指出) ,我在实践中也遇到了此类问题.通过以下封装的对象即可以传递List对象.

Java代码
  1. /**  
  2.  * <p>Java class for listObject complex type.  
  3.  *   
  4.  * <p>The following schema fragment specifies the expected content contained within this class.  
  5.  *   
  6.  * <pre>  
  7.  * <complexType name="listObject">  
  8.  *   <complexContent>  
  9.  *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">  
  10.  *       <sequence>  
  11.  *         <element name="list" type="{http://www.w3.org/2001/XMLSchema}anyType" maxOccurs="unbounded" minOccurs="0"/>  
  12.  *       </sequence>  
  13.  *     </restriction>  
  14.  *   </complexContent>  
  15.  * </complexType>  
  16.  * </pre>  
  17.  *   
  18.  *   
  19.  */  
  20. @XmlAccessorType(XmlAccessType.FIELD)   
  21. @XmlType(name = "listObject", propOrder = { "list" })   
  22. public class ListObject {   
  23.   
  24.     @XmlElement(nillable = true)   
  25.     protected List<Object> list;   
  26.   
  27.     /**  
  28.      * Gets the value of the list property.  
  29.      *   
  30.      * <p>  
  31.      * This accessor method returns a reference to the live list,  
  32.      * not a snapshot. Therefore any modification you make to the  
  33.      * returned list will be present inside the JAXB object.  
  34.      * This is why there is not a <CODE>set</CODE> method for the list property.  
  35.      *   
  36.      * <p>  
  37.      * For example, to add a new item, do as follows:  
  38.      * <pre>  
  39.      *    getList().add(newItem);  
  40.      * </pre>  
  41.      *   
  42.      *   
  43.      * <p>  
  44.      * Objects of the following type(s) are allowed in the list  
  45.      * {@link Object }  
  46.      *   
  47.      *   
  48.      */  
  49.     public List<Object> getList() {   
  50.         if (list == null) {   
  51.             list = new ArrayList<Object>();   
  52.         }   
  53.         return this.list;   
  54.     }   
  55.   
  56.     public void setList(ArrayList<Object> list) {   
  57.         this.list = list;   
  58.     }   
  59.   
  60. }  

5.WebService 服务端 spring 配置文件 ws-context.xml
Xml代码
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns:jaxws="http://cxf.apache.org/jaxws"  
  4.     xsi:schemaLocation="http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd"  
  5.     default-autowire="byName" default-lazy-init="true">  
  6.        
  7.     <jaxws:endpoint id="webServiceSample"  
  8.         address="/WebServiceSample" implementor="cn.org.coral.biz.examples.webservice.WebServiceSampleImpl"/>  
  9.   
  10. </beans>  

WebService 客户端 spring 配置文件 wsclient-context.xml

Xml代码
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns:jaxws="http://cxf.apache.org/jaxws"  
  4.     xsi:schemaLocation="http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd"  
  5.     default-autowire="byName" default-lazy-init="true">  
  6.   
  7.     <!-- ws client -->  
  8.     <bean id="identityValidateServiceClient" class="cn.org.coral.admin.service.IdentityValidateService"  
  9.         factory-bean="identityValidateServiceClientFactory" factory-method="create" />  
  10.   
  11.     <bean id="identityValidateServiceClientFactory"  
  12.         class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">  
  13.         <property name="serviceClass"  
  14.             value="cn.org.coral.admin.service.IdentityValidateService" />  
  15.         <property name="address"  
  16.             value="http://88.148.29.54:8080/coral/services/IdentityValidateService"/>  
  17.     </bean>  
  18.        
  19. </beans>  
6.发布到tomcat服务器以后通过以下地址即可查看自定义的webservice接口生成的wsdl:
http://88.148.29.54:8080/aio/services/WebServiceSample?wsdl

7.调用WebService接口的Junit单元测试程序
Java代码
  1. package test.coral.sample;   
  2.   
  3. import org.springframework.test.AbstractDependencyInjectionSpringContextTests;   
  4.   
  5. import cn.org.coral.biz.examples.webservice.WebServiceSample;   
  6. import cn.org.coral.biz.examples.webservice.dto.UserDTO;   
  7.   
  8. public class TestWebServiceSample extends  
  9.         AbstractDependencyInjectionSpringContextTests {   
  10.     WebServiceSample webServiceSampleClient;   
  11.   
  12.     public void setWebServiceSampleClient(WebServiceSample webServiceSampleClient) {   
  13.         this.webServiceSampleClient = webServiceSampleClient;   
  14.     }   
  15.        
  16.     @Override  
  17.     protected String[] getConfigLocations() {   
  18.         setAutowireMode(AUTOWIRE_BY_NAME);   
  19.                   //spring 客户端配置文件保存位置   
  20.         return new String[] { "classpath:/cn/org/coral/biz/examples/webservice/wsclient-context.xml" };   
  21.     }   
  22.        
  23.     public void testWSClinet(){   
  24.         Assert.hasText(webServiceSampleClient.say(" world"));   
  25.     }   
  26. }  
posted @ 2009-02-20 13:41 zhuyongjp 阅读(1429) | 评论 (0)编辑 收藏
 
     摘要: 本文是一个由四篇文章组成的系列的第三篇(请参阅 参考资料),本系列文章旨在介绍创建、描述和发布 Web 服务的过程。在第一部分中,我借助 WSDL 编制示例讲解了如何描述一个 Web 服务。在第二部分中,我讨论了 SOAP 的体系结构及其语义。在本文中,我将看一看与 SOAP 相关的互操作性问题。 Web 服务模型将整个 B2B 领域分成三个步骤或三个域:描述一个服务、将该服务与具体的实现绑定,然...  阅读全文
posted @ 2009-02-19 10:44 zhuyongjp 阅读(406) | 评论 (0)编辑 收藏
 
     摘要: SOAP 和 WSDL 我在本系列文章的 第 1 部分介绍了 WSDL。WSDL 描述了 Web 服务的接口。Web 服务所有者将用 SOAP 来实现他们的接口。因此, WSDL 服务实际上作为 SOAP 服务一样存在。一旦 Web 服务用户拥有 WSDL 文件,他或者她就知晓接口的细节。他或者她就会用 SOAP 来与 Web 服务通信。 可以把 Web 服务考虑为对象,可以通过 WSDL 接口公...  阅读全文
posted @ 2009-02-19 10:24 zhuyongjp 阅读(1436) | 评论 (0)编辑 收藏
 
可互操作的基于 Web 分布式应用程序的思想并非新近出现。仅举一例,电子数据交换(EDI)市场需求早在 B2B 在线电子商务获得任何有意义的实现之前就存在了 ― 并且随着 B2B 电子市场的普及,互操作性已经成为最迫切的 EDI 需求。

以任何在线电子市场为例。存在着许多企业,各自提供特有的“服务( services )”(让我们称之为“Web 服务( Web services )”)。在当今的电子商务中,尚不存在一种机制,使一个业务能自动发现其预期伙伴提供的服务。所谓的 下一代 .com还是提供这种自动的发现机制。

什么是 WSDL?

这种新的 .com 需要一种解决方案来描述它所提供的服务(Web 服务)。具体而言,这意味着您需要一种格式或某种类型的语法,使您可以通过使用它们来描述下列问题的答案:

您的在线业务提供什么服务?
您如何调用业务服务?
当用户调用您的业务服务时,该业务服务需要他/她提供什么信息?
用户将如何提供这些必需信息?
服务将以什么格式发送返回给用户的信息?
很幸运,WSDL 提供了完成所有这些作业的机制。



WSDL 和 SOAP

为更好理解 WSDL 是如何工作的,我将首先描述 SOAP 和 HTTP 是如何使用 WSDL 工作的。WSDL 的用途是“描述”您的 Web 服务。业务之间将通过交换 WSDL 文件来理解对方的服务。一旦知道您伙伴的服务并希望调用它们,SOAP 就派上用场了。可以将服务看作是通过 SOAP 访问的对象。

最有可能的情况是,您将通过因特网或电子邮件与潜在伙伴通信。当然,因特网使用 HTTP 而电子邮件以 SMTP 方式工作,这使得 HTTP 和 SMTP 成为作为 SOAP 的“传输服务提供者”的有利候选人。



WSDL 编写

现在,我将讲述为 Web 服务编写 WSDL 的过程。目的是公开现有的 Web 服务。您所处的情况也许就是下列情况之一:

您有一个现存的服务(例如,一个网站),并希望表示它的功能性。
您有一个 WSDL,并且希望依照已经决定表示的功能性来实现 Web服务器端的逻辑。(有些人也许会认为这是一个不可能的方案,但是 UDDI 的指纹概念使它变得极为可能;我将在本系列的第四部分讨论 UDDI)。
您正在从零开始,并且既无网站又无 WSDL 界面。
本文中所涵盖的信息适用于这些可能性中的任意一种或全部。

WSDL 编写的四个步骤

我将把 WSDL 编写分成四个简单步骤。遵循每个步骤,您的 Web 服务将准备就绪用于部署。

步骤 1:服务接口

您将构建一个移动电话销售公司的服务接口作为样本项目(我将这个服务称为 MobilePhoneService )。该公司销售不同型号的移动电话,所以公司 Web 服务的后端数据存储库中将包含一个具有两列( model number 和 price )的表格。(为了将焦点保持在 WSDL 本身,我保持该表格的简单性)。有两个关于要使用 WSDL 表示的服务的方法:

getListOfModels ()
getPrice (modelNumber)
GetListOfModels 方法提供了一个字符串数组,其中每个字符串表示一种移动电话的型号。 GetPrice 获得型号,然后返回它的价格。WSDL 将这些方法作为操作调用。现在将开始构建“WSDL 接口文件( WSDL interface file )”。

每个 WSDL 文件的根元素都是 <definitions> ,必须在其中提供服务的完整描述。首先,必须在 <definitions> 元素中提供各种名称空间的声明。三个必须做的外部名称空间声明是 WSDL、SOAP 和 XSD(XML 模式定义)。还有一个名称空间 ― TNS,它指您的 MobilePhoneService(这表示 TNS(targetNamespace 的缩写)包含专为 MobilePhoneService 定义的所有元素和属性的名称)。但是 WSDL 是您将在 WSDL 编写中使用得最多的主要名称空间。在本系列文章中使用到其它名称空间时,我将提到它们的效用。

关于名称空间只要注意一点:WSDL 广泛地使用名称空间这一概念。我鼓励您到 W3C 的官方网站去学习关于名称空间的更多知识(请参阅 参考资料)。WSDL 是这种思想的一种实现,因为名称空间提供了无限的灵活性,而这恰恰是用于电子数据交换的可移植格式所需要的。

<definitions> 元素包含一个或多个 <portType> 元素,实际上,每个元素都是您希望表示的一系列 operation 。或者,您也可以将单个 portType 元素看作是将各种方法组成类的一个逻辑分组。例如,如果您的供应链管理解决方案需要在客户和供应商之间进行交互,您最可能做的是分别定义与他们交互的功能性;也就是说,您将为用户和供应商各定义一个 portType。应该将每个 portType 称为 服务,因此整个 WSDL 文件将成为一个服务集合。

必须为每个服务提供一个名称。在本例中,仅有一个服务(因此只有一个 <portType> )。 需要使用该 portType 元素的 name 属性为移动电话销售服务指定名称。

在每个服务内可以有几个方法、或者 operation ,WSDL 通过 <operation> 元素来引用它们。样本应用程序有两个要表示的方法: getListOfModels 和 getPrice 。因此,您需要提供两个 <operation> 元素,每个元素有一个 name 。 我已经使用 <operation> 元素的 name 属性命名了每个操作。

此时,WSDL 文件看上去象 清单 1。


清单 1:定义操作

 1<?xml version="1.0" encoding="UTF-8" ?> 
 2<definitions  name="MobilePhoneService" 
 3     targetNamespace="www.mobilephoneservice.com/MobilePhoneService-interface" 
 4     xmlns="http://schemas.xmlsoap.org/wsdl/" 
 5     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
 6     xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" 
 7     xmlns:xsd="http://www.w3.org/1999/XMLSchema"> 
 8     <portType name="MobilePhoneService_port"> 
 9             <operation name="getListOfModels "> 
10                     
11                     
12             </operation> 
13             <operation name="getPrice"> 
14                     
15                         
16             </operation> 
17     </portType> 
18</definitions> 
19


步骤 2:指定参数
Listing 2: Defining parameters 
        
       
 1<?xml version="1.0" encoding="UTF-8" ?> 
 2<definitions  name="MobilePhoneService" 
 3targetNamespace="http://www.mobilephoneservice.com/MobilePhoneService-interface" 
 4xmlns="http://schemas.xmlsoap.org/wsdl/" 
 5xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
 6xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" 
 7xmlns:xsd="http://www.w3.org/1999/XMLSchema"> 
 8<types> 
 9<xsd:schema targetNamespace="http://www.mobilephoneservice.com/MobilePhoneService" 
10xmlns="http://www.w3.org/1999/XMLSchema/"> 
11<xsd:complexType name="Vector"> 
12<xsd:element name="elementData" type="xsd:String" /> 
13<xsd:element name="elementCount" type="xsd:int" /> 
14</xsd:complexType> 
15</xsd:schema> 
16</types> 
17<message name="ListOfPhoneModels"> 
18<part name="models" type="tns:Vector"> 
19</message> 
20<message name="PhoneModel"> 
21<part name="model" type="xsd:String"> 
22</message> 
23<message name="PhoneModelPrice"> 
24<part name="price" type="xsd:String"> 
25</message> 
26<portType name="MobilePhoneService_port"> 
27<operation name="getListOfModels "> 
28<output message="ListOfPhoneModels"/> 
29</operation> 
30<operation name="getPrice"> 
31<Input message="PhoneModel"/> 
32<output message="PhoneModelPrice"/> 
33</operation> 
34</portType> 
35</definitions> 
36      


定义好操作(或方法)以后,现在需要指定将向它们发送和从它们返回的参数。在 WSDL 术语中,所有参数称为“消息”。认为您是在递送消息而结果得到返回的消息是有用的。方法调用是这样一种操作:它准备返回“消息”来响应进入的消息。

请回忆,在第一步骤中有两个操作要表示。第一个操作 getListOfModels 不必获得任何参数并且返回一个字符串数组,其中每个字符串表示移动电话的型号。因此,必须定义一个包含字符串数组的 <message> 元素。

看看 清单 2 中的各种 <message> 元素。其中的第一个元素有一个等于 ListOfPhoneModels 的名称属性(该消息的逻辑名称),以及名称为 models 的单个 <part> 元素,这意味着该 ListOfPhoneModels 是一个“只含有一个 part 的”消息,其中仅有的这个 part 的名称是“models”。消息可以有任意多个 part ― 只要为它们起不同的名称,以唯一标识。

我已包括了 <part> 元素的另一个属性,它就是 type 。将这个“type”属性当作 C++ 或 Java 中的数据类型。我已经将 models 的数据类型指定为 tns:Vector。(请回忆,我在 <definitions> 根元素中指定了一些名称空间,其中之一是 tns 。)这个类型即指 MobilePhoneService 名称空间。这意味着当编写 WSDL 时,您可以创建自己的名称空间。现在您也许会问两个逻辑问题:为什么?和怎么做?

要回答 为什么,让我们以 getListOfModels 操作返回的字符串数组为例。WSDL 使用 XML 模式定义(XSD)定义的一些原始数据类型(诸如 int、float、long、short、byte、string、Boolean 等等),并允许您直接使用它们,或者以这些原始数据类型构建复杂数据类型后,在消息中使用它们。这就是为什么当引用复杂数据类型时,您需要定义自己的名称空间。在本例中,需要为 array of strings 构建一个复杂数据类型。

现在来看 怎么做问题,您将使用 XSD 创建自己的名称空间。为实现这个目的,我在 <types> 元素中使用了 xsd:complexType 元素用来定义称为 Vector 的数据类型。 Vector 使用两个原始数据类型:string(元素数据)和 Integer(元素计数)。因此, Vector 成为名称空间的一部分并可以通过别名 tns 来引用。

在 清单 2 中,我以类似的方式定义了另外两个消息 PhoneModel 和 PhoneModelPrice 。这两个消息只使用了 xsd 名称空间中的原始数据类型 string,因此您不必为使用它们而定义任何更复杂的数据类型。

您也许已经注意到当创建 <message> 元素时,没有指定这些消息是进入参数还是返回值。这是一个您将在 <portType> 元素内的 <operation> 元素中完成的工作。因此,正如您在 清单 2 中所看到的,我已经将 <input> 和 <output> 元素都添加到这两个操作中。每个 input 元素通过消息名来引用它并将它当作用户调用该操作时要提供的参数。类似地,每个 <output> 元素引用一个消息;它将该消息当作操作调用的返回值。

至今, 清单 2准确地限定了目前的讨论的范围。

步骤 3:消息传递和传输

我以一种抽象方式定义了操作和消息,而不考虑实现的细节。实际上,WSDL 的任务是定义或描述 Web 服务,然后提供一个对外部框架的引用来定义 WSDL 用户将如何实现这些服务。可以将这个框架当作 WSDL 抽象定义和它们的实现之间的“绑定( binding )”。

当前,最流行的绑定( binding )技术是使用简单对象访问协议(SOAP)。WSDL 将指定能够访问 Web 服务实际实现的 SOAP 服务器,并且从那时起 SOAP 的整个任务就是将用户从 WSDL 文件带到它的实现。SOAP 是本系列文章中下一部分的主题,所以我将暂时避免讨论 SOAP 细节而继续集中讲述 WSDL 编写。

WSDL 编写的第三个步骤是描述将 SOAP 与 WSDL 文件绑定到一起的过程。您将把 <binding> 元素包括到 <definitions> 元素内。这个 binding 元素应该有 name 和 type 属性。 name 将标识这个绑定而 type 将标识您希望与这个绑定相关联的 portType(一组操作)。在 清单 3 中,您会发现 <portType> 元素的 name 与 <binding> 元素的 type 属性值相匹配。

WSDL binding 元素包含您将用于绑定用途的外部技术的声明。因为正在使用 SOAP,所以这里将使用 SOAP 的名称空间。WSDL 术语中,对外部名称空间的使用称为 extensibility 元素。

在 清单 3 中,您将看见一个空的 <soap:binding/> 元素。该元素的用途是声明将把 SOAP 作为绑定和传输服务使用。

<soap:binding> 元素有两个属性:style 和 transport。style 是一个可选属性,它描述该绑定内操作的性质。transport 属性指定 HTTP 作为该绑定将使用的级别较低的传输服务。

SOAP 客户机将从 WSDL 文件中读取 SOAP 结构并与另一端的 SOAP 服务器协调,所以必须特别关注 interoperability 。我打算在本系列文章的第三部分详细讲述该问题。

在空的 <soap:binding/> 元素后面,有两个 WSDL <operation> 元素,分别表示步骤 1 的操作。每个 <operation> 元素提供各自操作的绑定细节。因此,我提供了另一个 extensibility 元素,即 <soap:operation/> (仍然是一个空元素,与它发生的那个操作相关)。该 <soap:operation/> 元素有一个 soapAction 属性,SOAP 客户机将使用该属性创建 SOAP 请求。

请回忆步骤 2 中, getListOfModels 操作只有输出而无任何输入。因此,必须为该操作提供一个 <output> 元素。该输出包含 <soap:body/> 元素(仍然是一个空元素,与它发生的那个操作相关)。SOAP 客户机需要该信息来创建 SOAP 请求。 <soap:body/> 的名称空间属性值应该与您将部署到 SOAP 服务器上的 service 的名称相对应,SOAP 服务器将在在本系列文章的下一部分中讲述。

您已几乎要完成步骤 3 了。只要将下一个操作复制到这个操作的后面,您将完成 清单 3。

步骤 4:概括

您已经生成了一个完整描述服务 interface 的 WSDL 文件。现在,WSDL 需要一个附加步骤来创建该 WSDL 文件的概要。WSDL 将该文件称为 implementation 文件,在本系列文章的第四部分中,当您在 UDDI 注册中心发布 Web 服务时,会使用它。请看 清单 4― 这个 WSDL 实现文件。它的主要特性如下:

除了
清单 4(实现文件)引用不同的 targetNamespace 去引用实现文件以外, <definitions> 根元素和 清单 3(WSDL 接口文件)中的完全相同。
有一个
<import> 元素,该元素引用 清单 3的接口文件(文件名 MobilePhoneService-interface.wsdl)和它的名称空间。
有一个
<service> 标记,其中有一个表示该服务的逻辑名 name 。在 service 元素内有一个引用在 清单 3中创建的 SOAP 绑定的 port 元素。

将 IBM 的 Web Services ToolKit(WSTK)用于 WSDL 编写

现在,Web 服务已经完全就绪用于部署。我已经展示了如何手工创建这些文件(使用象 emacs 这样的简单文本编辑器)。可以使用诸如 IBM 的 WSTK(请参阅 参考资料以获得该工具箱以及本文提到的其它参考资料的链接)之类的 Web 服务编写工具来生成相同的这些文件。

WSTK 可以使用向导帮助过程来生成这些文件。用户可以生成与我在以上教程中演示的同样两种方法的 WSDL 文件,并将 WSTK 文件和 清单 3和 4中的 WSDL 文件作比较。

您将注意到下列差异:

WSTK
依照逻辑规则创建了所有名称属性;在本示例中,我使用了自己视为方便的名称。
WSTK 为每个操作至少生成一个 input
标记,即使该操作不必获得任何输入。 listAllPhoneModels 操作没有任何 input 元素,但是如果使用 WSTK 生成相同文件,它将因为包含这个方法的一个空 input 元素。
WSTK 产生了除已生成的两个文件以外的第三个文件。这第三个文件是
SOAP 引擎用于服务部署的 SOAP 部署描述符。我将在本系列文章中讨论服务部署。
在这部分中,我演示了手工进行 WSDL 编写以创建接口和实现文件,并与 IBM 的 Web Services ToolKit 生成的文件作了比较。在本系列的下一部分中,我将讨论在 SOAP 服务器上部署这个 WSDL 服务。
posted @ 2009-02-19 09:50 zhuyongjp 阅读(418) | 评论 (0)编辑 收藏
仅列出标题
共3页: 上一页 1 2 3