这些天稍微玩了一下

Axis ,以前做 WebServices 都使用 JBuilder ,感觉做 WebServices 如此简单,现在自己手动做,原来也是如此简单。高兴之余写一个简单的初学手册,就算是学习成果吧。当然对 Axis 理解的还不很深,所以错误之处望指点。

Axis 是一个实现 WebService Framework Apache Web Services Project http://ws.apache.org )的一个之项目,现在这个项目有很多之项目 Axis(http://ws.apache.org/axis/ ) 是其中一员,还有 XML-RPC (这个也是我比较喜欢的东东 J )。

现在 Axis 主要由两个版本一个是 Axis 一个是 Axis2 。两个好象有比较多的不同,我这里说的是 Axis ,过几天演技一下 Axis2 ,然后再写一篇吧。

好了现在开始做个 WebService 吧:

第一步当然是先去 Axis 主页下载一个来啦。下 Release 就行,最新的是 1.2.1 source 好象没有打包的只有 CVS 的。下来以后解压缩,主要有以下文件夹

Docs 顾名思义,这里放的是文档,其实 Axis 的文档作的很好,我就是按照它的 User Guide 一步步做下来的。

Lib 运行 Axis 时要用到的 jar 包,要完全正常运行还缺两个 mail.jar activation.jar 这两个是 javaMail 包,到处都能弄到。

Samples Axis 自带的例子包括很多种应用

Webapps  Axis 是发布到 Servlet Container 中的,要把 Axis 集成到你的项目中,就把这个文件夹里的内容合并到你的项目中就行了。

还有一个 xmls 文件夹,放得是一些可能用到的 xml 例子。

 

第二步,建一个项目, Web 项目,用 Eclipse 或者 Idea 都可以啊。如果你非要用记事本类的东西,我也不拦着你。

Axis 中的 Webapps\axis 文件夹下的东西统统 Copy 到你的 Web 文件夹下。其实有些东西是没用的,比如 classes 文件夹里的东西都可以去掉了,还有那几个 jws 文件也没有用。虽然 axis 最方便的发布 WebServices 的方法是把你的 .java 改成 .jws 的放到 Web 发布文件夹下的根目录下,但是这种方法没有什么适用价值。然后运行以下 Tomcat (或者其他的 Application Server )。然后浏览一下你的刚刚发布的这个项目,如果正常的话就可以看到 Axis 的默认画面,
axis1.jpg
这个页面不是必须的,在真正项目开发中可以把它去掉或换个名字。点击
List 连接进入已经发布的 WebServices 列表。
axis2.jpg
开始时应该只有
AdminService Version 。后面两个就是我们在下面要做的 WebServices

 

第三步,如果上面的一切正常,就可以正式开始做 WebServices 了。首先做一个 Services 实现类。 Calc.java 有两个方法 plus subtract 。这个 Service 所用到的数据类型都是基本类型。

public   class  Calc {
    
public   int  plus( int  a, int  b){
        
return  a + b;
    }

    
public   int  substract( int  a, int  b){
        
return  a - b;
    }
}

然后在 WEB-INF 目录下加入一个 server-config.wsdd 。这是 WebServices 的发布描述文件,作用类似于 web.xml 。它有自己的格式,但是具体的标记是什么样子的,在 Axis 的文档中没有详细的一一列出,只是提到了常用的一些。在 axis 的源码中有一些 wsdd XSD 文件,如果你用的是 IDEA 可以把这些 XSD 映射到 uri ,这样编辑器就有提示了。

下面这我们本文中的 server-config.wsdd 的样子:

<? xml version="1.0" encoding="UTF-8" ?>
< deployment  name ="defaultClientConfig"
            xmlns:java
="http://xml.apache.org/axis/wsdd/providers/java"
            xmlns:handler
="http://xml.apache.org/axis/wsdd/providers/handler"  xmlns ="http://xml.apache.org/axis/wsdd/" >
    
< globalConfiguration  name ="defaultClientConfig" >
        
< requestFlow  name ="RequestFlow1" >
            
< handler  name ="Handler1"  type ="java:org.apache.axis.handlers.JWSHandler" >
                
< parameter  name ="scope"  value ="session" />
            
</ handler >
            
< handler  name ="Handler2"  type ="java:org.apache.axis.handlers.JWSHandler" >
                
< parameter  name ="scope"  value ="request" />
                
< parameter  name ="extension"  value =".jwr" />
            
</ handler >
        
</ requestFlow >
    
</ globalConfiguration >
    
< handler  name ="URLMapper"  type ="java:org.apache.axis.handlers.http.URLMapper" />
    
< handler  name ="LocalResponder"  type ="java:org.apache.axis.transport.local.LocalResponder" />
    
< handler  name ="Authenticate"  type ="java:org.apache.axis.handlers.SimpleAuthenticationHandler" />
    
< transport  name ="http" >
        
< requestFlow  name ="RequestFlow1" >
            
< handler  name ="Handler1"  type ="URLMapper" />
            
< handler  name ="Handler2"  type ="java:org.apache.axis.handlers.http.HTTPAuthHandler" />
        
</ requestFlow >
    
</ transport >
    
< transport  name ="local" >
        
< responseFlow  name ="ResponseFlow1" >
            
< handler  name ="Handler1"  type ="LocalResponder" />
        
</ responseFlow >
    
</ transport >
    
< service  name ="AdminService"  provider ="java:MSG" >
        
< parameter  name ="allowedMethods"  value ="AdminService" />
        
< parameter  name ="enableRemoteAdmin"  value ="false" />
        
< parameter  name ="className"  value ="org.apache.axis.utils.Admin" />
        
< namespace > http://xml.apache.org/axis/wsdd/ </ namespace >
    
</ service >
    
< service  name ="Version"  provider ="java:RPC" >
        
< parameter  name ="allowedMethods"  value ="getVersion" />
        
< parameter  name ="className"  value ="org.apache.axis.Version" />
    
</ service >
    
< service  name ="CalcService"  provider ="java:RPC" >
        
< parameter  name ="allowedMethods"  value ="*" />
        
< parameter  name ="className"  value ="org.mstar.ws.Calc" />
        
< parameter  name ="scope"  value ="request" />
    
</ service >
    
< service  name ="FooService"  provider ="java:RPC" >
        
< parameter  name ="allowedMethods"  value ="*" />
        
< parameter  name ="className"  value ="org.mstar.ws.FooService" />
        
< parameter  name ="scope"  value ="session" />
        
< typeMapping  encodingStyle ="http://schemas.xmlsoap.org/soap/encoding/"
                     xmlns:ns1
="http://ws.mstar.org"
                     qname
="ns1:FooBean"
                     languageSpecificType
="java:org.mstar.ws.FooBean"
                     serializer
="org.apache.axis.encoding.ser.BeanSerializerFactory"
                     deserializer
="org.apache.axis.encoding.ser.BeanDeserializerFactory"
                     name
="FooBean" />
        
< requestFlow  name ="requestFlow1" >
            
< handler  name ="Handler1"  type ="java:org.mstar.ws.FooHandler" />
        
</ requestFlow >
        
< responseFlow >
            
< handler  name ="Handler1"  type ="java:org.mstar.ws.FooHandler" />
        
</ responseFlow >
    
</ service >
</ deployment >

这个文件比 Axis 自带的那些 deploy.wsdd 要多很多东西,在 Axis 的文档中它使用命令来把对 deploy.wsdd 进行发布的。在我的例子中是直接把 server-config.wsdd 写好放到 WEB-INF 下。所以要把 Service 上面那些东西加上,否则系统不能正常运行。

     < service  name ="CalcService"  provider ="java:RPC" >
        
< parameter  name ="allowedMethods"  value ="*" />
        
< parameter  name ="className"  value ="org.mstar.ws.Calc" />
        
< parameter  name ="scope"  value ="request" />
    
</ service >

Calc 的发布描述。其中 scope 属性默认是 request 所以不写也可以。其他 parameter 看名字就知道干什么的了。这样你在 List 页面中就可以查看 CalcService WSDL 了。

第四步就是写客户端程序了。

WSClient.java

try {
            String endpoint 
= "http://localhost:8080/ws/services/CalcService";

            Service service 
= new Service();
            Call call 
= service.createCall();

            call.setTargetEndpointAddress(endpoint);
            call.setOperationName(
new QName("http://ws.mstar.org""plus"));
            Object[] params 
= new Object[2];
            params[
0= 10;
            params[
1= 20;
            Integer result 
= (Integer) call.invoke(params);
            System.out.println(
"Result:  " + result);
        } 
catch (Exception e) {
            e.printStackTrace();
        }

这是动态的调用 WebService 的方法,并不需要根据 WSDL 生成客户端存根。但是对于 Service 中有复合类型的时候就不可以了。下一个例子讲的就是如何做客户端存根。如果这个例子能够正常运行就可以了。

第五步做一个稍微复杂一点的例子,对于 WebServices 不能仅仅对简单类型的数据进行操作,也应该能对复杂类型进行操作。下面的例子就是:

先要一个复杂类型的类

public   class  FooBean {
    
private  String foo1;
    
private  Integer foo2;
    
private  Boolean foo3;

    
public  FooBean(String foo1, Integer foo2, Boolean foo3) {
        
this .foo1  =  foo1;
        
this .foo2  =  foo2;
        
this .foo3  =  foo3;
}
…. Setter and Getter
}

然后是一个有 FooBean Service

public   class  FooService {
    
public  FooBean getFooBean(String foo1,Integer foo2,Boolean foo3){
        
return   new  FooBean(foo1 + " (Remote) " ,foo2 + 10 , ! foo3);
    }
}

很简单,就是返回一个 FooBean ,在参数上做了一些手脚 J

然后在 server-config.wsdd 中加入描述

< service  name ="FooService"  provider ="java:RPC" >
        
< parameter  name ="allowedMethods"  value ="*" />
        
< parameter  name ="className"  value ="org.mstar.ws.FooService" />
        
< parameter  name ="scope"  value ="session" />
        
< typeMapping  encodingStyle ="http://schemas.xmlsoap.org/soap/encoding/"
                     xmlns:ns1
="http://ws.mstar.org"
                     qname
="ns1:FooBean"
                     languageSpecificType
="java:org.mstar.ws.FooBean"
                     serializer
="org.apache.axis.encoding.ser.BeanSerializerFactory"
                     deserializer
="org.apache.axis.encoding.ser.BeanDeserializerFactory"
                     name
="FooBean" />
</ service >

这里多了 typeMapping 标记用来描述复杂数据类型 FooBean 。对入复杂类型的序列化可以是自定义的,在 serializer deserializer 属性中改。那个 xmlns ,要留意一下,因为在客户端生成存根时的 AntTask 中要写一些 Mapping ,来确定那些 xmlns 映射到哪些 package 。当然这些也可以在 WSDL 中找到。而 WSDL 中的 xmlns 也是在这里定义的。

下面也各客户端。由于这次的 Service 中有复杂类型所以要根据 WSDL 生成这些复杂类型和 Service 的存根。取得 WSDL 的方法可以使用 java2WSDL 来做,但那样太麻烦,其实在 List 页面中用右键 - 另存为就可以了。然后写一个 ant 文件

<? xml version="1.0" encoding="UTF-8" ?>
< project  default ="GenJavaSub"  basedir ="." >
    
< taskdef  name ="wsdl2java"  classname ="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask" />
    
< target  name ="GenJavaSub" >
        
< property  name ="output.dir"  value ="src" />
        
< property  name ="generated.dir"  value ="src/org/mstar/wsclient/generated" />
        
< property  name ="wsdl.url"  value ="wsdl/FooService.wsdl" />
        
< delete  dir ="${generated.dir}" />
        
< mkdir   dir ="${generated.dir}" />
        
< wsdl2java  all ="true"  debug ="false"  helperGen ="true"
            noimports
="false"
            output
="${output.dir}"
            serverside
="false"
            skeletonDeploy
="false"
            typeMappingVersion
="1.1"
            url
="${wsdl.url}"
            verbose
="false"
            noWrapped
="false" >
            
< mapping  namespace ="http://ws.mstar.org"  package ="org.mstar.wsclient.generated" />
            
< mapping  namespace ="http://localhost:8080/ws/services/FooService"  package ="org.mstar.wsclient.generated" />
        
</ wsdl2java >
    
</ target >
</ project >

这里上面的东西比较好理解,在下面的 mapping 中一定要注意,因为每个 Service ComplexType 都有自己的 xmlns ,这里就是把 xmlns 映射到指定的 package 比如 http://ws.mstar.org 映射到 org.mstar.wsclient.generated 包。这些 namespace 可以在 wsdl 文件中找到。运行 ant 就会看见在 src 中的 org.mstar.wsclient.generated 中多了几个 java 文件。接下在我们就可以用这几个类了。还有一点要注意 wsdl2java output 属性要指向 src ,而不是 generated 文件夹。

客户端调用就相对容易多了

 

FooServiceService serviceLocator  =   new  FooServiceServiceLocator();
try  {
FooService service 
=  serviceLocator.getFooService();
    FooBean fooBean 
=  service.getFooBean( " fooBean " 10 true );
System.out.println(fooBean);
catch  (ServiceException e) {
    e.printStackTrace();
catch  (RemoteException e) {
    e.printStackTrace();
}

运行客户端看看!

 

做简单的 WebService 就基本上这样了。但这样的 Webservice 还远远不能用户真正的项目中,还有很多问题没有解决,比如安全问题,有状态会话问题等等。这些问题很多可以通过 Hanlder 来解决,他有点类似于 FilterServlet 。具体的东西我还有没有研究, axis 在这方面的资料就很少了。研究明白以后再写一篇吧。