本文是
Apache Axis
的用户指南的第一部分。
笔者在学习的过程中,作为记录,简单的翻译了一下。笔者已经翻译了
Apache Axis
的安装及应用文档,也谈不上翻译,只是笔者在学习的过程中,将笔者认为重要的部分作为记录,并加以笔者实践的结果。建议在阅读本文之前先阅读
Apache Axis
安装指南。
0.
介绍
SOAP
:
SOAP
是基于
XML
的通信协议和编码格式,它用于交互应用程序通信。现在的版本是
SOAP 1.2
,但是
SOAP 1.1
版本使用的更加广泛。
SOAP
由
W3C
的
XML
协议工作组管理。
SOAP
被广泛的认为是跨平台、跨语言的计算机应用和
Web Service
的核心。
AXIS
:
Axis
是
SOAP Engine----
用来构造
SOAP
处理器,例如客户端,服务器端,网管等等。
Axis
是使用
Java
实现的,但是
Axis
提供一个
C++
的客户端实现。
Axis
不仅仅是一个
SOAP Engine
,它还包括以下内容:
·
独立的服务器
·
可以
plug in
到
servlet engine
中的服务器
·
支持
WSDL
·
根据
WSDL
生成
Java
类
·
样例程序
·
SOAPMonitor
Axis 1.4
的特性如下:
·
兼容
SOAP 1.1/1.2
两个版本
·
灵活的配置和发布
·
支持
JWS
·
支持所有的基本类型,以及类型映射系统
·
自动的
Java Beans
序列化
/
反序列化,包含自定义的
Java
属性
-->XML
元素
/
属性的映射
·
自动的
Java
集合与
SOAP
数组的双向转换。
·
提供
RPC
和基于消息的
SOAP
服务
·
由发布的服务自动生成
WSDL
·
WSDL2Java
工具
·
Java2WSDL
工具
·
基本的安全扩展,可以集成到
Servlet 2.2
的安全
/
角色中
·
支持面向绘画的服务,通过使用
HTTP Cookie
或者独立于传输的
SOAP Headers
·
对
SOAP with Attachments
规范的初步支持
·
将
EJB
作为
Web Service
来访问
·
基
HTTP Servlet
的传输
·
基于
JMS
的支持
·
独立的服务器版本
·
大量的例子
根据
Apache Axis
安装指南,安装
Apache Axis
到
Apache Tocmat
服务器。运行本文所适用的例子之前,确保
CLASSPATH
包含以下类库:
·
axis-1_2/lib/axis.jar
·
axis-1_2/lib/jaxrpc.jar
·
axis-1_2/lib/saaj.jar
·
axis-1_2/lib/commons-logging.jar
·
axis-1_2/lib/commons-discovery.jar
·
axis-1_2/lib/wsdl4j.jar
·
axis-1_2/
(for the sample code)
·
遵守
JAXP-1.1
的
XML
解析器,推荐使用
Xerces
,因为在
Apache Axis
测试的时候使用的就是这个解析器
1.
使用
Axis
开发
Web Service
Getting Started
首先来看一个例子
Web Service
客户端程序,它调用
Apache
提供的
Axis
服务器的
echoString
方法:
package samples.userguide.example1;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;
public class TestClient {
public static void main(String[] args) {
try {
String endpoint =
"http://nagoya.apache.org:5049/axis/services/echo";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress(new java.net.URL(endpoint));
call.setOperationName(new QName("http://soapinterop.org/",
"echoString"));
String ret = (String) call.invoke(new Object[] { "Hello!" });
System.out.println("Sent 'Hello!', got '" + ret + "'");
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
程序首先创建了
Service
和
Call
对象,他们是标准的
JAX-RPC
对象,用来存储关于调用服务的元数据,然后设置了
SOAP
消息目的地的终端节点的
URL
,接着定义了
Web Service
的操作名,最后激活服务,传递一个数组参数
(
一个字符串
)
。
可以查看
SOAP
请求消息的结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoString xmlns:ns1="http://soapinterop.org/">
<arg0 xsi:type="xsd:string">Hello!</arg0>
</ns1:echoString>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
字符串参数自动的序列化成
XML
,服务器响应就是一个
String
,我们将它反序列化后并打印出来。
可以使用
tcpmon
或者
SOAP monitor
来查看请求和响应信息。
(
笔者并没有网络连接,不能连接到
Apache
网站,所以这个例子没有运行成功。
)
命名参数
在上面的例子中,可以看到
Axis
自动的将
XML
编码的参数命名为
arg0,arg1
,放在
SOAP
消息中。如果想改变这种命名,很简单,在调用
invoke()
方法之前,使用
addParameter
方法对每个参数命名,同时使用
setReturnType
对返回的参数命名,如下所示:
call.addParameter(“testParam”,
org.apache.axis.Constants.XSD_STRING,
javax.xml.rpc.ParameterMode.IN);
call.setReturnType(org.apache.axis.Constants.XSD_STRING);
进行上述设置后,会将
testParam
这个参数名赋给并且只赋给第一个调用方法的第一个参数。同时还定义了参数的类型
(org.apache.axis.Constants.XSD_STRING)
和这个参数是输入还是输出或者输入输出参数。在本例中是一个输入参数。现在再运行这个程序时,消息如下:
<testParam xsi:type="xsd:string">Hello!</testParam>
与”untyped”服务器互操作
在上面的例子中,强制设置了
invoke()
方法的返回值类型,本来是一个
Object
类型,但是转换为适当的实际类型
---
例如,我们已经知道
echoString
方法返回一个字符串,那么我们希望调用
client.invoke()
返回一个字符串。我们先来看一下它是如何工作的:
下面是
echoString
的一个响应:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
<result xsi:type="xsd:string">Hello!</result>
</ns1:echoStringResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
其中红色的部分包含了一个类型声明,
Axis
使用它来决定将元素的内容反序列化成什么类型的
Java
对象,在本例中将其反序列化成一个
Java String
对象。很多工具将这种
XML
中的显示类型信息叫做自描述。另外,一些工具返回如下的响应:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns1:echoStringResponse xmlns:ns1="http://soapinterop.org/">
<result>Hello, I'm a string!</result>
</ns1:echoStringResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
它不包含类型的生命,那么
Java
如何将
<result>
中的内容进行反序列化呢?反序列化成什么类型的对象呢?答案就是
metadata
,关于数据的数据。在本例中,我们需要一个对服务的描述来告诉我们期望的返回类型。下面是在
Axis
客户端需要做的:
call.setReturnType( org.apache.axis.Constants.XSD_STRING );
这个方法会告诉
Axis
客户端,如果返回值类型没有被声明,那么就将返回值的
xsi:type
属性定义成预定义的
SOAP String
类型。
另外还可以通过下面的方法设置返回类型的
Java
类:
call.setReturnClass(String.class);
现在,使用
Axis
作为客户端访问
SOAP
服务就介绍完了。总结一下,利用
Axis
作为客户端访问
SOAP
服务的步骤如下:
创建服务终端节点
初始化
Service
,初始化方法:
Service()
Service(EngineConfiguration config)
Service(EngineConfiguration engineConfiguration, AxisClient axisClient)
Service(java.io.InputStream wsdlInputStream, QName serviceName)
Service(Parser parser, QName serviceName)
Service(QName serviceName)
Service(java.lang.String wsdlLocation, QName serviceName)
Service(java.net.URL wsdlDoc, QName serviceName)
初始化
Call
,初始化方法调用
Service
的如下方法:
Call createCall()
Creates a new Call object with no prefilled data.
Call createCall(QName portName)
Creates a new Call object - will prefill as much info from the WSDL as it can.
Call createCall(QName portName, QName operationName)
Creates a new Call object - will prefill as much info from the WSDL as it can.
Call createCall(QName portName, java.lang.String operationName)
Creates a new Call object - will prefill as much info from the WSDL as it can.
设置调用的一些参数:
void addParameter(java.lang.String paramName, QName xmlType, java.lang.Class javaType, ParameterMode parameterMode)
void addParameter(java.lang.String paramName, QName xmlType, ParameterMode parameterMode)
void setOperationName(QName operationName)
void setPortTypeName(QName portType)
void setProperty(java.lang.String name, java.lang.Object value)
void setReturnType(QName xmlType)
void setReturnType(QName xmlType, java.lang.Class javaType)
void setTargetEndpointAddress(java.lang.String address)
.......(org.apache.axis.client.Call
、
org.apache.axis.Call
、
javax.xml.rpc.Call
的方法
)
调用
invoke
方法,调用
SOAP
服务
2.
使用
Axis
发布
Web Service
首先看一下下面这个非常简单的类:
public class Calculator {
public int add(int i1, int i2) {
return i1 + i2;
}
public int subtract(int i1, int i2) {
return i1 – i2;
}
}
如何将这个类可以通过
SOAP
访问呢?方法有很多种,我们通过
Axis
提供的最简单办法来完成:
JWS(Java Web Service)文件---瞬时部署
将
Calculator.java
文件拷贝到
<your-webapp-root>/axis/
目录下,并修改扩展名为
jws
,即拷贝后的文件为
Calculator.jws
,例如笔者拷贝的目录及文件名为
TOMCAT_HOME\webapps\axis\Calulator.jws
接下来就可以通过下面的
URL
访问
Web Service
了
Axis
将自动的查找文件,编译
java
类,转换成
SOAP
调用。使用下面的客户端程序:
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package samples.userguide.example2;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.utils.Options;
import javax.xml.rpc.ParameterMode;
public class CalcClient
{
public static void main(String [] args) throws Exception {
Options options = new Options(args);
String endpoint = "http://localhost:" + options.getPort() +
"/axis/Calculator.jws";
args = options.getRemainingArgs();
if (args == null || args.length != 3) {
System.err.println("Usage: CalcClient <add|subtract> arg1 arg2");
return;
}
String method = args[0];
if (!(method.equals("add") || method.equals("subtract"))) {
System.err.println("Usage: CalcClient <add|subtract> arg1 arg2");
return;
}
Integer i1 = new Integer(args[1]);
Integer i2 = new Integer(args[2]);
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName( method );
call.addParameter( "op1", XMLType.XSD_INT, ParameterMode.IN );
call.addParameter( "op2", XMLType.XSD_INT, ParameterMode.IN );
call.setReturnType( XMLType.XSD_INT );
Integer ret = (Integer) call.invoke( new Object [] { i1, i2 });
System.out.println("Got result : " + ret);
}
}
使用下面的调用来调用
Calculator.jws
:
G:\jee\eclipse\workspace\AxisUserGuide>java samples.userguide.example2.CalcClien
t -p8080 add 2 5
Got result : 7
G:\jee\eclipse\workspace\AxisUserGuide>java samples.userguide.example2.CalcClien
t -p8080 subtract 10 9
Got result : 1
G:\jee\eclipse\workspace\AxisUserGuide>java samples.userguide.example2.CalcClien
t -p8080 unknown 1 2
Usage: CalcClient <add|subtract> arg1 arg2
当端口不是
8080
的时候,需要改成自定义的端口。
由于
JWS web
服务是目的在于实现简单的
web
服务。不能使用
package
。当使用
package
的时候,将出现如下的错误:
G:\jee\eclipse\workspace\AxisUserGuide>java samples.userguide.example2.CalcClien
t -p8080 subtract 10 9
Exception in thread "main" AxisFault
faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
faultSubcode:
faultString: java.io.FileNotFoundException: G:\eclipse\workspace\apache-tomcat-
5.5.25\webapps\axis\WEB-INF\jwsClasses\samples\userguide\example2\Calculator.cla
ss (
指定されたファイルが見つかりません。
)
faultActor:
faultNode:
faultDetail:
{http://xml.apache.org/axis/}hostname:neusoft-dc6b5f1
3.
自定义部署
—
介绍
WSDD
使用
JWS
的唯一有点就是简单,但是灵活性很差,不可以进行配置,不能指定自定义类型,不能使用
Handlers
,所以很少使用。
使用部署描述符
为了灵活的使用
Axis
,主要使用其配置的功能,首先应该属性
Axis Web Service Deployment Descriptor(WSDD)
格式。部署描述符包含了需要部署到
Axis
中的内容,也就是说,
Axis Engine
需要使用的内容。通常部署一个
Web Service
,那么我们就从基本的服务开始开始了解
wsdd
。首先看下面这个
wsdd
文件:
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="MyService" provider="java:RPC">
<parameter name="className" value="samples.userguide.example3.MyService"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
比较简单,最外层的元素告诉
engine
这是一个
WSDD
部署,同时定义了
java
这个名称空间。接着
service
元素进行实际的
Web Service
的定义。
service
是一个目标链,也就是说它可能包含以下内容中的部分或者全部:请求流、核心处理器
(
对于服务来说叫做
provider)
、响应流。在本例中,
provider
是
java:RPC
,建立在
Axis
中,提示了这是个
Java RPC
服务。实际上处理这个的类是
org.apache.axis.providers.java.RPCProvider
。一些其他的
providers
包括如下:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/sunjavaduke/e0fa2869be884d6f887f0935625ecadb.png)
通过使用一系列的参数来告诉
RPCProvider
它需要实例化的类以及调用的类
(
例如
samples.userguide.example3.MyService)
。上面例子中的参数分别为指定服务的类、允许通过
SOAP
访问的方法,
”*”
表示所有的方法都可以访问。也可以通过空白或者都好分割的方面名来限制
SOAP
可以访问的方法列表。
4.
高级
WSDD—
指定更多的选项
WSDD
描述符可以包含关于服务的更多的信息,以及其他的部分,例如
Axis
中的
Handlers
,我们将在后面进行介绍。
服务范围
Axis
支持三种范围的服务对象,
”Request”
、
”Session”
、
”Application”
,其中
Request Scope
是默认的,为每个
SOAP
请求创建一个新的对象,
Session Scope
针对每个允许使用
session
的客户端创建一个新的对象,
Application scope
为所有的请求创建一个单例的共享对象。可以通过
<parameter>
指定,例如:
<service name=”MyService” ...>
<parameter name=”scope” value=”value”>
...
</service>
使用AdminClient
使用
AdminClient
,或者说
org.apache.axis.client.AdminClient
类来将
wsdd
文件发送到
Axis
服务器,以便真正的部署服务。如果将
Axis
部署到不是
tomcat
的服务器上的话,需要使用
-p<port>
参数,默认的是
8080
,一个典型的调用
AdminClient
的方式是:
java org.apache.axis.client.AdminClient deploy.wsdd
<Admin>Done processing</Admin>
这个命令使服务可以通过
SOAP
进行访问,通过运行
Client
类来检查一下:
java samples.userguide.example3.Client –lhttp://localhost:8080/axis/services/MyService “test me!”
You typed : test me!
整个运行过程如下:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/sunjavaduke/0f250304f8604c019d6a6a732e982cde.png)
也可以使用
AdminClient
来列出所有发布到服务器上的组件:
D:\axis-1_4>java org.apache.axis.client.AdminClient list
<ns1:deployment
xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.
apache.org/axis/wsdd/providers/java" xmlns:ns1="http://xml.apache.org/axis/wsdd/
">
<ns1:globalConfiguration>
<ns1:parameter name="sendMultiRefs" value="true"/>
......
<ns1:parameter name="attachments.implementation" value="org.apache.axis.attach
ments.AttachmentsImpl"/>
<ns1:requestFlow>
<ns1:handler type="java:org.apache.axis.handlers.JWSHandler">
<ns1:parameter name="scope" value="session"/>
</ns1:handler>
<ns1:handler type="java:org.apache.axis.handlers.JWSHandler">
<ns1:parameter name="scope" value="request"/>
<ns1:parameter name="extension" value=".jwr"/>
</ns1:handler>
</ns1:requestFlow>
</ns1:globalConfiguration>
<ns1:handler name="soapmonitor" type="java:org.apache.axis.handlers.SOAPMonitor
Handler">
<ns1:parameter name="wsdlURL" value="/axis/SOAPMonitorService-impl.wsdl"/>
<ns1:parameter name="namespace" value="http://tempuri.org/wsdl/2001/12/SOAPMon
itorService-impl.wsdl"/>
<ns1:parameter name="serviceName" value="SOAPMonitorService"/>
<ns1:parameter name="portName" value="Demo"/>
</ns1:handler>
.......other ns1:handler
<ns1:service name="AdminService" provider="java:MSG">
<ns1:parameter name="allowedMethods" value="AdminService"/>
<ns1:parameter name="enableRemoteAdmin" value="false"/>
<ns1:parameter name="className" value="org.apache.axis.utils.Admin"/>
<ns1:namespace>http://xml.apache.org/axis/wsdd/</ns1:namespace>
</ns1:service>
.......other ns1:service
<ns1:transport name="http">
<ns1:requestFlow>
<ns1:handler type="URLMapper"/>
<ns1:handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
</ns1:requestFlow>
<ns1:parameter name="qs:list" value="org.apache.axis.transport.http.QSListHand
ler"/>
.......other parameter
</ns1:transport>
......other transport
</ns1:deployment>
在上面的打印的信息中,包含了
services
、
handlers
、
transports
和其它的内容。实际上它就是
server-config.wsdd
文件的内容。我们在稍后会更详细的介绍它。
更多的部署相关—Handlers和Chains 处理器和链
现在介绍一些
Axis
更强大的特性。比方说,想统计一下服务被调用了多少次,那么就是用一个
handler
来完成。在
Axis
包含这样的例子。当需要使用一个如上的
handler
类的时候,首先需要部署
Handler
本身
(
也就是说在
wsdd
文件中需要对其进行声明
)
,然后在
service
中使用
handler
的名字,这个名字由你决定。下面是一个
handler
的
wsdd
文件:
<deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<!-- define the logging handler configuration -->
<handler name="track" type="java:samples.userguide.example4.LogHandler">
<parameter name="filename" value="d:/MyService.log"/>
</handler>
<!-- define the service, using the log handler we just defined -->
<service name="LogTestService" provider="java:RPC">
<requestFlow>
<handler type="track"/>
</requestFlow>
<parameter name="className" value="samples.userguide.example4.Service"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
第一部分定义了一个
Handler
叫做
”track”
,实现的类为
samples.userguide.example4.LogHandler
。这个类接受一个参数
filename
,用于将日志信息记录到这个文件中。
然后再
wsdd
重定义了
LogTestService
服务,和第一个例子相似,也是
RPC
服务。不同之处在于
service
元素包含了一个
<requestFlow>
元素,这表示当服务被调用的时候,一些
Handlers
会被提前调用。通过插入一个
track
引用,这样就可以确保在调用服务的时候,每个消息都会被记录。
根据前面的例子,发布服务并调用:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/sunjavaduke/62195827fb21469d9a3f4e79fa9a303f.png)
执行多次调用后,查看
d:/MyService.log
文件的内容,如下所示:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/sunjavaduke/4f0b1d92b26d4c81986dee06375d4fcf.png)
远程管理
默认情况下,
Axis
服务器只允许在本机上访问管理请求,如果希望启用远程管理,那么需要设置
AdminService
的
enableRemoteAdmin
属性为
true
。查看
TOMCAT_HOME\webapps\axis\WEB-INF
目录下的
server-config.wsdd
文件,可以看到
AdminService
的部署描述,添加如下的属性:
<service name="AdminService" provider="java:MSG">
<parameter name="allowedMethods" value="AdminService"/>
<parameter name="enableRemoteAdmin" value="true"/>
<parameter name="className" value="org.apache.axis.utils.Admin"/>
<namespace>http://xml.apache.org/axis/wsdd/</namespace>
</service>
启用远程管理的话将允许未被授权的访问,如果启用的话,需要确保在配置中添加了安全限制。