本部分是
apache axis
用户指南的第三部分。在
Axis
中使用
WSDL
文件。
10.
使用
WSDL
Web Service Description Language
是由
IBM
和
Microsoft
完成的规范,并且被很多其他的组织所支持。
WSDK
用于结构化的描述
Web Services
。
Web
服务的
WSDL
是由程序来使用的,它包含了服务的接口,服务使用的数据类型以及服务的位置。更多的内容,可以参考
W3C
的
WSDL
规范。
Axis
通过以下三宗方式来支持
WSDL
¨
在
Axis
中发布一个服务后,用户可以通过标准的
web
浏览器来访问服务的
URL
,在后面添加一个
?WSDL
,这样就可以获得自动生成的服务的
WSDL
文件。
¨
提供了一个
WSDL2Java
工具,用于根据
WSDL
文件生成
Java
代理类和框架类。
¨
提供了一个
Java2WSDL
工具,用于根据
Java
类构建
WSDL
文件。
?WSDL
:获取服务的
WSDL
文件
当通过
Axis
成功发布一个服务后,这个服务和唯一的一个
URL
相关联。对于
JWS
文件来说,
URL
就是
JWS
文件本身的路径;对于非
JWS
服务来说,一般情况下
URL
的格式如下:
http://<host>[:port]/axis/services/<service-name>
如果可以通过浏览器访问服务的
URL
,那么就会看到一个提示消息,提示终端节点是一个
Axis
服务,那么你应该通过
SOAP
来访问。提示信息如下:
<service-name>
Hi there, this is an AXIS service!
Perhaps there will be a form for invoking the service here...
然而,如果在
URL
后面添加一个
”?wsdl”
,那么
Axis
会自动生成一个服务的
WSDL
文件,并在浏览器中以
XML
格式显示,如下图所示:
WSDL2Java:
根据
WSDL
文件创建
Stubs
、
Skeletons
和数据类型
客户端绑定
Axis
的
WSDL-to-Java
工具的类为
org.apache.axis.wsdl.WSDL2Java
,基本调用方式如下:
java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL)
这会为客户端生成必要的绑定,这个过程中
Axis
遵守
JAX-RPC
规范。假设执行以下命令:
cm %AXIS_HOME%/samples/addr
java org.apache.axis.wsdl.WSDL2Java AddressBook.wsdl
这会在
%AXIS_HOME%/samples/addr/AddressFetcher2
目录生成很多类。他们之所以在这里生成,是由于这是
WSDL
中声明的
targetNamespace
,并且是映射到
Java
的包名。名称空间会在下面进行说明:
类型
根据
WSDL
类型生成的
Java
类根据
WSDL
的类型进行命名。一般来说
(
不是绝对的
),
这个
java
类会是一个
bean
。例如下面的
WSDL
类型:
<xsd:complexType name="phone">
<xsd:all>
<xsd:element name="areaCode" type="xsd:int"/>
<xsd:element name="exchange" type="xsd:string"/>
<xsd:element name="number" type="xsd:string"/>
</xsd:all>
</xsd:complexType>
WSDL2Java
会生成如下的
Java
类:
public class Phone implements java.io.Serializable {
public Phone() {...}
public int getAreaCode() {...}
public void setAreaCode(int areaCode) {...}
public java.lang.String getExchange() {...}
public void setExchange(java.lang.String exchange) {...}
public java.lang.String getNumber() {...}
public void setNumber(java.lang.String number) {...}
public boolean equals(Object obj) {...}
public int hashCode() {...}
}
XML-->Java
的映射:元数据
上面例子中的
XML
类型名为
phone
,而生成的
Java
类名为
Phone
,第一个字母为大写,以适应
Java
编码规范。这种映射经常发生,因为对于
XML
的名称或者标志符的限制要比
Java
少得多。例如,如果
phone
的子元素的名字为
new
,那么由于
new
是
Java
中的保留字,所以不能生成这样的成员属性。为了支持这种类型的映射,同时支持
XML
属性的序列化与反序列化,通过类型元数据系统来关联
Java
数据类和描述符。
当
WSDL2Java
工具生成了一个数据
bean
,例如上面的
Phone
类,它会观察是否
schema
中包含一些没有直接映射到
Java
的成员变量的属性或者名字。如果有的话,就会生成一个静态的代码片断并为这个类提供一个类型描述符。类型描述符是一个变量描述符的集合,每一个变量描述符将
Java
的变量映射到
XML
元素或者属性。
// Type metadata
private static TypeDesc typeDesc;
static {
typeDesc = new TypeDesc(AttributeBean.class);
FieldDesc field;
// An attribute with a specified QName
field = new AttributeDesc();
field.setFieldName("name");
field.setXmlName(new QName("foo", "nameAttr"));
typeDesc.addFieldDesc(field);
// An attribute with a default QName
field = new AttributeDesc();
field.setFieldName("male");
typeDesc.addFieldDesc(field);
// An element with a specified QName
field = new ElementDesc();
field.setFieldName("age");
field.setXmlName(new QName("foo", "ageElement"));
typeDesc.addFieldDesc(field);
}
Holders
这种类型作为输入输出或者输出参数使用。
Java
没有输入输出
/
输出参数的概念。为了实现这种行为,
JAX-RPC
指定使用
holder
类。一个
Holder
类似一个简单的
java
类,包含了它的一个类型。例如上面例子中的
Phone
类的
holder
类如下:
package samples.addr.holders;
public final class PhoneHolder implements javax.xml.rpc.holders.Holder {
public samples.addr.Phone value;
public PhoneHolder()
{
}
public PhoneHolder(samples.addr.Phone value) {
this.value = value;
}
}
holder
类只有在类型被作为输入输出或者输出参数的时候才被生成。
Holder
类实在类名后面加上后缀
Holder
来命名,并且生成到包名为
holders
下面。
PortTypes
服务定义接口
(Service Definition Interface,SDI)
是继承了
WSDL
的
portType
的接口,可以通过这个接口访问服务操作。例如下面的
WSDL
:
<message name="empty">
<message name="AddEntryRequest">
<part name="name" type="xsd:string"/>
<part name="address" type="typens:address"/>
</message>
<portType name="AddressBook">
<operation name="addEntry">
<input message="tns:AddEntryRequest"/>
<output message="tns:empty"/>
</operation>
</portType>
WSDL2Java
会生成如下的接口:
public interface AddressBook extends java.rmi.Remote {
public void addEntry(String name, Address address) throws java.rmi.RemoteException;
}
SDI
接口的名字就是
portType
得名字。为了构造
SDI
,
WSDL2Java
需要从
portType
何
binding
种同时获取信息。
JAX-RPC
规范对这部分的说明是:
"The name of the Java interface is mapped from the name attribute of the wsdl:portType element. ... If the mapping to a service definition interface uses elements of the wsdl:binding ..., then the name of the service definition interface is mapped from the name of the wsdl:binding element."
这个规范是
JAX-RPC
,这意味着只适用于
portType
是一个
RPC
接口。如果绑定的信息不是
RPC
,那么将使用绑定的名称。
可以有一个
portType—pt
和两个绑定
bRPC
和
bDoc
,这样的话就不能为两个绑定使用一个接口,此时需要使用两个接口,一个叫
pt
,另一个叫
bDoc
,同时还要生成两个
stubs
,一个叫
bRPCStub(
实现了
pt)
,另一个叫
bDocStub(
实现了
bDoc)
。
document/literal
改变了接口的形式。
绑定
Stub
类实现了
SDI
,
Stub
的名字是绑定的名字
+Stub
。它包含将方法调用转换成
SOAP
调用的代码,通过使用
Axis
的
Service
和
Call
对象。它作为远程服务的代理,使调用时就像调用本地对象一样。也就是说,不需要处理
endpoing
的
URL
,名称空间或者参数数组,这些需要通过
Service
和
Call
对象动态调用。
Stub
隐藏了所有这些的具体实现。
根据下面的
WSDL
片段:
<binding name="AddressBookSOAPBinding" type="tns:AddressBook">
...
</binding>
WSDL2Java
会生成如下的
Stub
:
public class AddressBookSOAPBindingStub extends org.apache.axis.client.Stub
implements AddressBook {
public AddressBookSOAPBindingStub() throws org.apache.axis.AxisFault
{...}
public AddressBookSOAPBindingStub(URL endpointURL,
javax.xml.rpc.Service service)
throws org.apache.axis.AxisFault
{...}
public AddressBookSOAPBindingStub(javax.xml.rpc.Service service)
throws org.apache.axis.AxisFault
{...}
public void addEntry(String name, Address address) throws RemoteException
{...}
}
Services
服务
正常来讲,客户端应用程序不会直接实例化一个
stub
,而是实例化一个
service locator
,然后调用一个方法来返回
stub
。这个
locator
是根据
WSDL
中的服务元素来指定的。
WSDL2Java
根据
service
元素生成两个独享,例如:
<service name="AddressBookService">
<port name="AddressBook" binding="tns:AddressBookSOAPBinding">
<soap:address location="http://localhost:8080/axis/services/AddressBook"/>
</port>
</service>
WSDL2Java
会生成如下接口和类:
public interface AddressBookService extends javax.xml.rpc.Service {
public String getAddressBookAddress();
public AddressBook getAddressBook() throws javax.xml.rpc.ServiceException;
public AddressBook getAddressBook(URL portAddress) throws javax.xml.rpc.ServiceException;
}
实现了
AddressBookServie
的类:
public class AddressBookServiceLocator extends org.apache.axis.client.Service
implements AddressBookService {
...
}
Service
接口定义了
WSDL
中定义的每个接口的
get
方法。
locator
实现了
service
接口,也就是说它实现了
get
方法。它用来获取
Stub
实例。
Service
类会默认的创建一个指向
endpoint URL
的
Stub
,但是当请求
PortType
的时候,可能需要指定一个不同的
URL
。
一个典型的
stub
类的应用如下:
public class Tester
{
public static void main(String [] args) throws Exception {
// Make a service
AddressBookService service = new AddressBookServiceLocator();
// Now use the service to get a stub which implements the SDI.
AddressBook port = service.getAddressBook();
// Make the actual call
Address address = new Address(...);
port.addEntry("Russell Butek", address);
}
}
服务器端的绑定
就像在客户端有一个
Web Service
的
Java stub
一样,一个
Java
框架的
skeleton
在服务器端使用。为了生成
Skeleton
类,需要使用
WSDL2Java
的
—server-side –skeletonDeploy true
选项。例如,仍旧使用
AddressBook.wsdl
文件:
java org.apache.axis.wsdl.WSDL2Java --server-side --skeletonDeploy true AddressBook.wsdl
可以看到
WSDL2Java
生成了所有在之前生成的
client
端的类,但是又声称了一些新的文件:
如果不指定
—skeletonDeploy true
选项,那么不会生成
skeleton
,而是生成的
deploy.wsdd
文件指示实现类已经直接部署了。在这种情况下,
deploy.wsdd
文件包含了额外的元数据来描述实现类的操作和参数。通过下面的方法可以直接部署服务:
java org.apache.axis.wsdl.WSDL2Java --server-side AddressBook.wsdl
下面是在服务器端生成的文件:
绑定
Skeleton
描述
Skeleton
类是介于
Axis engine
和实际的服务实现之间的类。它的名字就是绑定的名字
+Skeleton
。例如对于
AddressBook
绑定,
WSDL2Java
会生成如下的
Skeleton
:
public class AddressBookSOAPBindingSkeleton implements AddressBook,
org.apache.axis.wsdl.Skeleton {
private AddressBook impl;
public AddressBookSOAPBindingSkeleton() {
this.impl = new AddressBookSOAPBindingImpl();
}
public AddressBookSOAPBindingSkeleton(AddressBook impl) {
this.impl = impl;
}
public void addEntry(java.lang.String name, Address address)
throws java.rmi.RemoteException
{
impl.addEntry(name, address);
}
}
实际的
Skeleton
类可能内容还有很多,这里只拷贝了基本的框架。
skeleton
类包含一个
AddressBook
服务的实现。这个实现要么通过构造器传递给
skeleton
,要么是一个生成的实现的实例。当
Axis engine
调用
skeleton
的
addEntry
的方法时,它只需要简单的将调用分配给实际的实现的
addEntry
方法。
实现模板描述
WSDL
还根据绑定生成了一个实现模板:
public class AddressBookSOAPBindingImpl implements AddressBook {
public void addEntry(String name, Address address)
throws java.rmi.RemoteException {
}
}
这个类实际上只是一个实现的测试,并没有做任何事,它预想服务的编程人员来根据这个模板完成具体的实现。
服务
这个工具同时生成了
deploy.wsdd
和
undeploy.wsdd
文件,可以供
AdminClient
食用。这些文件只有当填充了实现类的具体方法后,编译类文件,然后将类文件放到
Axis engine
可以访问的位置后,才可以调用
AdminClient
方法来发布服务。
11.Java2WSDL
这个工具笔者就不介绍了
笔者认为使用
WSDL2Java
工具生成的代码中冗余的代码太多,并且编码规范有一些
ApacheAxis-Specific
,所以觉得还是自己手写代码比较好一些。
一旦写完了
Java
代码,可以通过
Web
浏览器访问,获取
WSDL
文件,没有必要使用
Java2WSDL
了。
12.
公开的
Axis
接口
Axis
公开的接口相对稳定,可以使用,即使
Axis
重构的话,这部分也应该不会修改。或者做一些兼容性的修改。
可以实现的一些
Apache Axis
的接口如下:
JAX-RPC
的一些接口,这些接口是针对
JAX-RPC
规范
1.0
的,会根据新的规范进行修改。
Axis
接口:这些相对不稳定。
12.
重要的类
org.apache.axis.MessageContext
Axis
所知道的关于请求
/
响应的所有的信息都是通过
MessageContext
来获取的。
Axis
将下面的内容存储在
MessageContext
中:
AxisEngine
的引用
请求和响应的消息
(org.apache.axis.Message
对象可以通过
Getter
和
Setter
方法存取
)
无状态以及服务范围的信息
(
服务是否维持
session
信息
)
当前处理状态
认证信息
(
用户名和密码,可以由
servlet
服务器提供或者其他方式
)
丰富的属性。几乎所有关于属性的信息都可以通过
MessageContext.getProperty()
方法获取。只需要知道属性的名称,通常是一个常量,定义在例如
org.apache.axis.transport.http.HTTPConstants
这样的类中。例如,可以获取
Axis Servlet
的
ServletContext
,通过
(HttpServlet)msgC.getProperty(HTTPConstants.MC_HTTP_SERVLET).getServletContext()
。
在服务中,当前的
MessageContext
总是可以通过静态的方法
MessageContext.getCurrentContext()
获取。
org.apache.axis.Message
org.apache.axis.Message
对象是
Axis
对
SOAP
消息的一种表示。请求和响应消息可以从
MessageContext
中获取
(
如上所述
)
。
Message
包括:
MIME
头
(
如果
message
本身包含
MIME
信息
)
附件
(
如果
message
本身包含附件
)
SOAPPart(
快速获取
SOAPPart
的
SOAPEnvelope)
,可以通过
SOAPPart
访问
SOAP
的任意信息
(<soap:Envelope>
标签内的任意信息
)
org.apache.axis.SOAPEnvelope
一个
MessageContext
有两个
Message
,每个都包含一个
SOAPPart
,
SOAPPart
包含
SOAPEnvelope
。
SOAPEnvelope
包含
SOAP
信封的所有内容。可以从
SOAPEnvelope
中获取
SOAP Header
和
SAOP Body
。
更多内容,参考具体的
API
。