这些天稍微玩了一下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的默认画面,

这个页面不是必须的,在真正项目开发中可以把它去掉或换个名字。点击List连接进入已经发布的WebServices列表。

开始时应该只有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在这方面的资料就很少了。研究明白以后再写一篇吧。
本文源码:http://www.blogjava.net/Files/mstar/WsLearn.rar