部署服务
要部署服务,键入如下命令:
ant deploy
对于随后的部署,按“迭代开发”部分所描述的来运行ant redeploy命令。
验证部署
要验证服务是否已经成功部署,打开一个浏览器窗口,然后验证服务端点的URL:
http://localhost:8080/hello-jaxrpc/hello
浏览器应该显示标题为Web Service的页面,它列出了带有状态ACTIVE的端口名MyHello。该页面还有一个URL指向服务的WSDL文件。
URL的hello-jaxrpc部分是实现了HelloWorld服务的servlet的上下文路径。这部分与hello-jaxrpc.war文件的前缀相对应。URL的/hello字符串与jaxrpc-ri.xml文件的urlPattern属性的值相匹配。注意/hello前的斜杠是urlPattern值所必需的。关于完整的jaxrpc-ri.xml文件清单,请参见“打包WAR文件”。
撤销部署服务
指南中到此为止没有撤销部署服务。完成这个例子后,可以通过键入如下命令来撤销部署服务:
ant undeploy
构建运行客户端
按照这些步骤来开发一个JAX-RPC客户端:
1. 生成占位程序
2. 编写客户端代码
3. 编译客户端代码
4. 将客户端的类打包成一个JAR文件
5. 运行客户端
接下来分别描述每个步骤。
生成占位程序
生成占位程序前,先确定按照“部署服务”中的指令安装了Hello.wsdl文件。要生成占位程序,进入<JWSD_HOME>/docs/tutorial/examples/jaxrpc/hello目录,然后输入下面命令:
ant generate-stubs
该命令以如下方式运行wscompile工具:
wscompile –gen:client –d build/client
-classpath build/shared config.xml
-gen:client选项指示wscompile生成客户端类,例如占位程序。-d选项指定生成文件的目标目录。更多信息请参见“wscompile工具”部分。
wscompile工具根据从Hello.wsdl和config.xml文件中读出的信息生成文件。当服务被部署时,Hello.wsdl文件被安装在Tomcat上。Hello.wsdl的位置由config.xml文件的<wsdl>元素指定,如下所示:
<?xml version="1.0"
encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location=
"http://localhost:8080/hello-jaxrpc/hello?WSDL"
packageName="hello"/>
</configuration>
wscompile工具执行的任务依赖于config.xml文件的内容。关于config.xml文件的更多信息请参见“配置文件”部分。高级用户可能希望检验XML模式文件:<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/common/jax-rpc-ri-config.xsd。
编写客户端代码
HelloClient是一个独立的程序,它调用HelloWorld服务的sayHello方法。它进行这一调用是借助于占位程序,用作远程服务的代理的本地对象。由于占位程序是在运行前生成的(由wscompile),因此它通常被称为静态占位程序。
HelloClient调用名为createProxy的私有方法来生成占位程序。注意该方法中的代码是实现特有的,可能不可移植,因为它依赖于MyHello_Impl对象。(MyHello_Impl类在前面部分由wscompile生成。)生成占位程序后,客户端程序将占位程序转换成HelloIF,服务定义接口类型。
HelloClient的源代码如下所示:
package hello;
import javax.xml.rpc.Stub;
public class HelloClient {
public static void main(String[]
args) {
try {
Stub stub = createProxy();
HelloIF hello = (HelloIF)stub;
System.out.println(hello.sayHello("Duke!"));
} catch
(Exception ex) {
ex.printStackTrace();
}
}
private
static Stub createProxy() {
// 注意:MyHello_Impl是实现特有的。
return
(Stub)(new MyHello_Impl().getHelloIFPort());
}
}
编译客户端代码
由于客户端代码引用了占位程序类,在编译客户端前确定执行了“生成占位程序”中的指令。要编译客户端,进入<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/hello目录,然后输入如下命令:
ant compile-client
打包客户端
要将客户端打包成一个JAR文件,输入下列命令: ant jar-client 该命令生成dist/hello-client.jar文件。
运行客户端
要运行HelloClient程序,输入: ant run 程序应该显示如下信息: Hello Duke!
命令ant run的目标是执行命令: java –classpath <cpath>
hello.HelloClient classpath包含前面部分生成的hello-client.jar文件,以及属于Java
WSDP的一些JAR文件。为了能够远程运行客户端,所有这些JAR文件必须存储在远程的客户机上。
迭代开发
为了演示开发的每个步骤,前面部分要求输入一些ant命令。但是,在迭代开发的过程中输入所有这些命令是不合适的。为了节约时间,在第一次部署完服务后,可以通过这些步骤来迭代:
1. 测试应用。 2. 编辑源文件。
3. 执行ant build来生成可部署的WAR文件。 4.
执行ant redeploy来撤销部署,再部署服务。 5. 执行ant
build-static,为具有静态占位程序的客户端生成JAR文件。 6.
执行ant run。
实现特有的特性
为了实现JAX-RPC规范,Java WSDP需要一些规范中没有说明的特性。这些特性是Java WSDP所特有的,可能与其它供应商提供的实现不兼容。对于JAX-RPC,Java
WSDP的实现特有的特性有:
- config.xml-参见例子中“生成占位程序”。
- jaxrpc-ri.xml-参见例子中“打包WAR文件”。
- 约束-前面的例子中,约束在hello-jaxrpc.war文件中,它是针对特定实现的。(但是,hello-portable.war文件不是针对特定实现的。)
- 占位程序-占位程序在hello-client.jar文件中。注意,HelloClient程序用具体例子说明MyHelloImpl,一个静态占位程序类,它是针对特定实现的。由于动态客户不包含静态占位程序,因此它们没有这一限制。关于更多的动态客户的信息,请参见“一个动态代理客户实例”和“动态调用接口(DII)客户实例”。
- 工具-wsdeploy和wscompile。
- 支持集合-参见表11-1。
JAX-RPC支持的类型
幕后,JAX-RPC将Java编程语言中的类型映射成XML/WSDL定义的类型。例如,JAX-RPC将java.lang.String类映射成xsd:string
XML数据类型。应用程序开发者不必知道这些映射的详细信息,但要知道并不是所有的Java 2平台的类都能进行映射,标准版(J2SETM平台)可以被用作JAX-RPC中的某个方法的参数或返回类型。
J2SE SDK类
JAX-RPC支持下面的J2S2 SDK类:
java.lang.Boolean
java.lang.Byte
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.String
java.math.BigDecimal
java.math.BigInteger
java.util.Calender
java.util.Date
JAX-RPC的发布版还支持java.util.Collection接口的一些实现类,参见表11-2。
表11-2 支持的Java Collection框架类
| java.util.Collection子接口 |
实现类 |
| List |
ArrayList LinkedList Stack
Vector |
| Map |
HashMap Hashtable Properties
TreeMap |
| Set |
HashSet TreeSet |
原始数据类型
JAX-RPC支持下列Java编程语言中的原始数据类型: boolean byte double float
int long short
数组
JAX-RPC还支持由JAX-RPC支持的数据类型构成的数组。例如,支持的数组可以是int[]和String[]。多维数组,例如BigDecimal[][],也是支持的。
应用程序类
JAX-RPC还支持应用程序中编写的类。例如,在一个订购处理应用程序中,可以提供名为Order、LineItem、Product的类。JAX-RPC规范以值类型的方式访问这些类,因为它们的值(或状态)可以用作方法的参数或返回值在客户和远程服务间传递。
要被JAX-RPC支持,一个应用程序类必须满足下列规则: l
必须要有一个公共的默认的构造器 l 必须没有实现(直接或间接地)java.rmi.Remote接口
l 它的字段必须是JAX-RPC所支持的类型
类可以包含公有的、私有的或受保护的字段。因为在远程调用过程中它的值将被传递(或返回),因此字段必须满足这些需求:
l 公有字段不能是final或临时变量 l
非公有字段必须有相应的getter和setter方法
JavaBeans组件
JAX-RPC还支持JavaBeans组件,但组件必须满足与应用程序类相同的规则。另外,一个JavaBean组件必须为它的每个属性提供getter和setter方法。Bean属性的类型必须是JAX-RPC所支持的类型。关于JavaBeans组件的例子请参见“JAX-RPC分布式服务”部分。
一个动态代理客户端实例
在“简单实例:HelloWorld”部分,客户端使用一个静态的占位程序作为代理。相对照而言,这部分的客户端实例通过动态代理,运行时生成的类来调用远程过程。在代理类生成前,客户端通过查找WSDL文档来获得服务的信息。
动态代理HelloClient清单
这儿是<JWSD_HOME>/docs/tutorial/examples/jaxrpc/proxy目录中HelloClient.java文件的完整清单。
package proxy;
import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
public class HelloClient {
public static void main(String[] args) {
try {
String UrlString =
"http://localhost:8080/ProxyHelloWorld.wsdl";
String nameSpaceUri = "http://proxy.org/wsdl";
String serviceName = "HelloWorld";
String portName = "HelloIFPort";
URL helloWsdlUrl = new URL(UrlString);
ServiceFactory
serviceFactory =
ServiceFactory.newInstance();
Service
helloService =
serviceFactory.createService(helloWsdlUrl,
new
QName(nameSpaceUri, serviceName));
HelloIF
myProxy = (HelloIF) helloService.getPort(
new
QName(nameSpaceUri, portName),
proxy.HelloIF.class);
System.out.println(myProxy.sayHello("Buzz"));
} catch
(Exception ex) {
ex.printStackTrace();
}
}
}
编译运行动态代理实例
执行如下步骤:
1、 如果没有准备好,按照“安装”部分的指令进行。
2、 进入<JWSD_HOME>/docs/tutorial/examples/jaxrpc/proxy目录中。
3、 输入如下命令:
ant build
ant deploy
ant build-dynamic
ant run
客户端应该显示如下信息:
A dynamic proxy hello to Buzz!
一个动态调用接口(DII)客户实例
使用动态调用接口(DII),即使直到调用时还不知道远程过程的签名或服务的名字,客户仍然可以调用远程过程。
由于DII客户的灵活性,它可以被用在服务代理程序中,用来动态找到服务,配置远程调用,执行调用。例如,一个在线服饰店的应用程序可以访问某个服务代理程序来专门负责运送。该代理程序将使用XML注册(JAXR)用的Java
API来查找满足某些标准的,例如低价格或者快速运送时间,运送公司的服务。运行时,代理程序使用DII来调用运送公司的Web服务上的远程过程。作为服饰店和运送公司间的一个中介,代理程序为所有各方都提供了好处。对于服饰店,它简化了运送处理,而对于运送公司,它为其找到了客户。
DII HelloClient清单
这儿是<JWSD_HOME>/docs/tutorial/examples/jaxrpc/dynamic目录中的HelloClient.java文件的完整清单。
package dynamic;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;
public class HelloClient {
private static String endpoint
=
"http://localhost:8080/dynamic-jaxrpc/dynamic";
private static String qnameService
= "Hello";
private static String qnamePort
= "HelloIF";
private static String BODY_NAMESPACE_VALUE
=
"http://dynamic.org/wsdl";
private static String ENCODING_STYLE_PROPERTY
=
"javax.xml.rpc.encodingstyle.namespace.uri";
private static String NS_XSD
=
"http://www.w3.org/2001/XMLSchema";
private static String URI_ENCODING
=
"http://schemas.xmlsoap.org/soap/encoding/";
public static void main(String[]
args) {
try {
ServiceFactory factory =
ServiceFactory.newInstance();
Service service =
factory.createService(new
QName(qnameService));
QName
port = new QName(qnamePort);
Call
call = service.createCall(port);
call.setTargetEndpointAddress(endpoint);
call.setProperty(Call.SOAPACTION_USE_PROPERTY,
new
Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
call.setProperty(ENCODING_STYLE_PROPERTY,
URI_ENCODING);
QName QNAME_TYPE_STRING =
new
QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);
call.setOperationName(
new QName(BODY_NAMESPACE_VALUE "sayHello"));
call.addParameter("String_1", QNAME_TYPE_STRING,
ParameterMode.IN);
String[] params = { "Duke!" };
String result = (String)call.invoke(params);
System.out.println(result);
} catch
(Exception ex) {
ex.printStackTrace();
}
}
}
编译运行DII实例
执行下列步骤:
1. 如果没有准备好,按照“安装”部分的指令进行。
2. 进入<JWSD_HOME>/docs/tutorial/examples/jaxrpc/dynamic目录。
3. 键入下列命令:
ant build
ant deploy
ant build-dynamic
ant run
客户端应该显示如下信息:
A dynamic hello to Duke!
wscompile工具
在JAX-RPC客户和服务中,wscompile工具用于生成占位程序、约束、序列化以及WSDL文件。工具读取用作输入的配置文件,以及某个WSDL文件或定义了服务的RMI接口。
语法
wscompile [options]
<configuration-file>
按照惯例配置文件名为config.xml,但并不要求必须是这个文件。
下表列出了wscompile的选项。注意必须完整的指定-import、-define、-gen选项中的一项。
表11-3 wscompile选项
| 选项 |
说明 |
| -classpath <path> |
指定查找输入类文件的路径;在Windows平台,路径应该包含在引号中,例如:-classpath
"\test;\foo;\acct" |
| -cp <path> |
与 -classpath <path> 选项相同 |
| -d <directory> |
指定存放生成的输出文件的路径 |
| -define |
读取服务的RMI接口,定义一个服务 |
| -f:<features> |
使given属性可用(参见下表的属性清单。当指定多个属性时,以逗号间隔。) |
| -features:<features> |
与-f:<features> 相同 |
| -g |
生成调试信息 |
| -gen |
与-gen:client 相同 |
| -gen:client |
生成客户事务(占位程序,等。) |
| -gen:server |
生成服务器事务(约束,等。)以及WSDL文件(如果正在使用wsdeploy,不必指定该选项。) |
| -gen:both |
生成客户和服务器事务 |
| -httpproxy:<host>:<port> |
指定一个HTTP代理服务器(端口默认为8080) |
| -import |
读取一个WSDL文件,生成服务的RMI接口和一个实现该接口的类的模板 |
| -keep |
保持生成的文件 |
| -model <file> |
将内部模型写入到给定文件 |
| -nd <directory> |
指定存放非类生成的文件的路径 |
| -O |
优化生成的代码 |
| -s <directory> |
指定存放生成的源文件的路径 |
| -verbose |
输出编译器正在完成的工作的消息 |
| -version |
打印版本信息 |
下表列除了-f 选项的属性清单(由逗号间隔)。
表11-4 wscompile的-f属性
| 属性 |
说明 |
| datahandleronly |
总是将附件映射成DataHandler类型 |
| explicitcontext |
打开显式服务上下文映射 |
| infix=<name> |
为生成的序列化指定使用的中缀 |
| nodatabinding |
为文字编码关闭数据库绑定 |
| noencodedtypes |
关闭编码类型信息 |
| nomultirefs |
关闭对多引用的支持 |
| novalidation |
关闭导入文档的完全合法性验证 |
| searchschema |
主动的查找子类型的模式 |
| serializeinterfaces |
打开直接的接口类型的序列化 |
配置文件
wscompile读取包含描述web服务信息的配置文件(config.xml)。config.xml文件的基本结构如下:
<?xml versiton=”1.0”
encoding=”UTF-8”?>
<configuration
xmlns=http://java.sun.com/xml/ns/jax-rpc/ri/config>
<service> or <wsdl> or <modelfile>
</configuration>
<configuration>元素可以完全地包含一个<service>、<wsdl>或者<modelifle>元素。
<service>元素
如果具体地指定了该元素,wscompile读取描述了该服务的RMI接口,生成一个WSDL文件。在<Interface>子元素中,name属性指定服务的RMI接口,而servantName属性指定了实现这一接口的类。例如:
<service name="CollectionIF_Service"
targetNamespace="http://echoservice.org/wsdl"
typeNamespace="http://echoservice.org/types"
packageName="stub_tie_generator_test">
<interface
name="stub_tie_generator_test.CollectionIF"
servantName="stub_tie_generator_test.CollectionImpl"/>
</service>
<wsdl>元素
如果指定了该元素,wscompile读取服务的WSDL文件,然后生成服务的RMI接口。Location属性指定WSDL文件的URL,而packageName属性指定由wscompile生成的类的包。例如:
<wsdl
location=http://tempuri.org/sample.wsdl
packageName=”org.tempuri.sample”/>
<modelfile>元素
该元素是针对高级用户的。 如果config.xml文件包含<service>或<wsdl>元素,wscompile生成包含描述服务的内部数据结构的模型文件。如果已经以这种方式成生了模型文件,下一次运行wscompile时可以再次使用该模型文件。例如:
<modelfile location=”mymodel.xml.gz”/>
wsdeploy工具
wsdeploy工具读取一个WAR文件和jaxrpc-ri.xml文件,然后生成另一个可部署的WAR文件。背后,wsdeploy运行了带有-gen:server选项的wscompile。该wscompile命令生成包含在由wsdeploy生成的WAR文件中的类和WSDL文件。
语法 wsdeploy的语法如下: wsdeploy <options> <input-war-file>
下表列出了工具的选项。注意-o选项是必需的。
表11-5 wsdeploy选项
| 选项 |
说明 |
| -classpath <path> |
指定可选的classpath |
| -keep |
保存临时文件 |
| -o <output-war-file> |
指定生成的war文件存放的路径 |
| -tmpdir <directory> |
指定使用的临时目录 |
| -verbose |
输出编译器正完成的工作的消息 |
| -version |
打印版本信息 |
输入WAR文件
通常使用GUI开发工具或ant war任务生成输入的WAR文件。这儿是一个简单输入WAR文件的内容:
META-INF/MANIFEST.MF WEB-INF/classes/hello/HelloIF.class
WEB-INF/classes/hello/HelloImpl.class WEB-INF/jaxrpc-ri.xml
WEB-INF/web.xml 在这个例子中,HelloIF是服务的RMI接口,而HelloImpl是实现了这个接口的类。web.xml文件是web组件的部署描述。Jaxrpc-ri.xml文件将在下面部分进行说明。
jaxrpc-ri.xml文件
下面的清单显示了一个简单的HelloWold服务的jaxrpc-ri.xml文件。
<webService>元素必须包含一个或多个<endpoint>元素。在该例中,注意<endpoint>的interface和implementation属性指定了服务的接口和实现类。<endpointMapping>元素把服务端口和接在urlPatternBase后面的endpoint
URL路径部分联系起来。
<?xml version="1.0" encoding="UTF-8"?>
<webServices
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://com.test/wsdl"
typeNamespaceBase="http://com.test/types"
urlPatternBase="/ws"> <endpoint
name="MyHello"
displayName="HelloWorld
Service"
description="A
simple web service"
interface="hello.HelloIF"
implementation="hello.HelloImpl"/>
<endpointMapping
endpointName="MyHello"
urlPattern="/hello"/>
</webServices>
如果服务具有多个endpoint,应该为每个endpoint指定端口和WSDL。下面的jaxrpc-ri.xml片断具有多个endpoint:
<endpoint
name="vendor"
displayName=")"
description="Vendor example
endpoint"
interface="com.buzzmurph.vendor.VendorPortType"
implementation="com.buzzmurph.act.vendor.VendorPortTypeImpl"
port="http://buzzmurph.com/preferred/Vendor.wsdl}VendorPort"
model="/WEB-INF/vendor.xml.gz"
wsdl="/WEB-INF/VendorImpl.wsdl"/>
<endpoint
name="supplier"
displayName=")"
description="Supplier example
endpoint"
interface="com.buzzmurph.supplier.SupplierPortType"
implementation="com.buzzmurph.supplier.SupplierPortTypeImpl"
port="http://buzzmurph.com/ord/Supplier.wsdl}SupplierPort"
model="/WEB-INF/supplier.xml.gz"
wsdl="/WEB-INF/SupplierImpl.wsdl"/>
<endpointMapping
endpointName="vendor"
urlPattern="/act/vendor"/>
<endpointMapping
endpointName="supplier"
urlPattern="/ord/supplier"/>
wscompile和wsdeploy的高级主题
这部分是针对那些熟悉WSDL、SOAP以及JAX-RPC规范的开发者的。
名字空间映射
这是一个模式类型名字实例。
SchemaType=”nsl:SampleType”
xmlns:nsl=http://echoservice.org/types
当从一个模式类型生成一个Java类型时,wscompile从模式类型名的本地部分获得类名。要指定生成的Java类所属的包名,需要指定一个模式类型名字空间和包名间的映射。可以在config.xml文件中增加一个<namespaceMappingRegistry>元素来定义这个映射。例如:
<service>
...
<namespaceMappingRegistry>
<namespaceMapping
namespace="http://echoservice.org/types"
packageName="echoservice.org.types"/>
</namespaceMappingRegistry>
...
</service>
处理程序
处理程序访问表示某个RPC请求或响应的SOAP消息。处理程序类必须实现了javax.xml.rpc.handler接口。因为处理程序可以访问SOAP消息,它可以使用javax.xml.soap包中的API来操纵消息。
- 处理程序任务的例子
- 加密和解密
- 登录和认证
- 缓冲
- 处理应用程序指定的SOAP头
处理程序链是一个处理程序序列。可以为客户指定一个处理程序链,为服务器指定一个处理程序链。在客户端,可以通过在jaxrpc-ri.xml文件中包含<handlerChains>元素实现。在服务器端,可以通过在config.xml文件中包含相同元素实现。这是一个config.xml文件中的<handlerChains>元素的例子。
<handlerChains>
<chain runAt="server"
roles=
"http://acme.org/auditing
http://acme.org/morphing"
xmlns:ns1="http://foo/foo-1">
<handler className="acme.MyHandler"
headers ="ns1:foo
ns1:bar"/>
<property
name="property"
value="xyz"/>
</handler>
</chain>
</handlerChains>
关于处理程序的更多信息,参见JAX-RPC规范中SOAP消息处理程序章节。
更多信息
关于JAX-RPC的更多信息以及相关技术,参考如下资料:
·
基于XML RPC1.0规范的Java API
http://java.sun.com/xml/downloads/jaxrpc.html
·
JAX-RPC主页
http://java.sun.com/xml/jaxrpc/index.html
·
简单对象访问协议(SOAP)1.1 W3C备忘录
http://www.w3.org/TR/SOAP/
·
Web服务描述语言(WSDL)1.1 W3C备忘录
http://www.w3.org/TR/wsdl
|