这些天稍微玩了一下
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
在这方面的资料就很少了。研究明白以后再写一篇吧。