Apache Axis 是提交给 W3C 的一种 SOAP(Simple Object Access Protocol) 实现, Axis 在实现 Java Web 服务方面稳定可靠。许多公司在它们的产品中使用了 Axis 来支持 Web 服务,而且 Axis 还有一个非常活跃的用户区。 Axis 有两种版本,即 Axis1.x 和 Axis2 。 Axis2 最近刚刚推出,与其前身相比, Axis2 对 Axis1.x 进行重新设计,并支持 SOAP1.2 及 REST 等交换协议。在 1.x 版本下, Axis 发布过好几个生产版本。在本节中,我们将使用 Axis1.3 ,您可以从 http://ws.apache.org/axis/java/releases.html 处下载。
契约最先式方法和契约最后式方法的比较
在实现 Web 服务时,我们通常有两种方法,即契约最先式和契约最后式方法。在契约最先式方法中,我们先开始编写 Web 服务的契约,即先开始设计 WSDL 文件;然后,我们使用一些工具根据 WSDL 文件生成 Java 代码,生成的 Java 代码包括 Java 接口及其实现类,另外,还包括一些与 Web 服务管道相关的代码。而在契约最后式的方法中,您先开始编写 Java 代码,然后,从这些代码中生成 WSDL 文件。
在定义和实现 Web 服务时,虽然我们需要考虑多种因素,如您要定义的 Web 服务上下文等,但是,在通常情况下,人们更偏爱使用契约最先式方法。但契约最先式方法的一个实际困难是,创建 WSDL 的过程并不是非常简单。因此,在没有特别工具支持的情况下,我们也许并不能轻易创建一个 WSDL 文件。为了解决这个难题,我们可以采用一种混合的方法,其具体步骤如下:
1) 先创建 Web 服务接口 ( 即 Java 接口 )
2) 根据该接口生成 WSDL
3) 然后,您就可以按照契约最先式方法的正常步骤,继续我们的开发过程
在我们本节的例子中,我们将采用上面这种混合方法,因此,您只需要对本章中例子中的编译脚本稍加修改,您就可以将本例改为契约最先式或契约最后式方法实现的 Web 服务。
使用Apache Axis来实现Web服务
我们将使用 Axis1.3 来实现 Web 服务,您可以在 http://ws.apache.org/axis/java/releases.html 处免费下载,下载后,请将安装包解压到本地硬盘的合适目录,然后修改源代码 examples.PROPERTIES 文件中的 axis.home 属性,让它指向 Axis 的安装目录。
与本章前面的两个例子不同的是,我们将编译并对 Web 服务文件进行打包,让它成为一个标准的 Web 文档文件 (.war 文件 ) 。然后,我们需要 Web 服务器来部署该 war 文件,这里我们将使用 Apache Tomcat6.x ,您可以从 http://tomcat.apache.org/ 处下载 Tomcat6.x 。
服务器端和客户端端代码
我们将使用契约最先式方法实现服务器端,但由于我们不想手动编写 WSDL ,所以咱们就先来写一个 Java 接口。本例中所有的文件都位于 ch03/02_Axis/src 目录下 ( 译者注:本书代码请从 http://www.packtpub.com/files/code/3216_Code.zip 处下载 ) 。我们现在来逐个看看这些文件。
IHelloWeb.java
IHelloWeb是一个简单的Java接口,它只定义了一个业务方法:
- public interface IHelloWeb{
- public String hello(String param);
- }
在契约最先式的实现方法中,我们先从WSDL开始,因为WSDL是一种平台中立的语言,这可以保证,使用契约最先式方法实现的客户端和服务器端可以相互交互。但本例中,我们先从Java接口开始,因此,为了保证生成的WSDL也满足可互操作性的标准。您需要注意,在您产生WSDL前,Java接口方法声明中包含的参数及返回类型,要能够和标准的可移植的WSDL相兼容。
HelloWebService.java
接下来我们需要实现Web服务,HelloWebService类的源码非常简单:
- public class HelloWebService implements IHelloWeb{
- private static int times;
- public HelloWebService(){
- System.out.println("Inside HelloWebService.HelloWebService...");
- }
- public String hello(String param){
- System.out.println("Inside HelloWebService.hello... - " +
- (++times));
- return "Return From Server";
- }
- }
其实,我们可以生成一个Web服务实现类的模板,而不用从头开始实现它。在实现类模板中,您可以手动添加您的业务逻辑。您可以使用这种方法在真实的生产环境中部署Web服务,但在本例中,我们不用手动添加业务逻辑,而是只使用一个小巧的ant脚本自动实现所有步骤。
build.xml
build.xml文件非常重要,它将引导您一步一步地实现web服务,从Java接口开始,实现业务逻辑,然后打包为一个标准war文件。因此,我们将整个的编译脚本拷贝如下:
下面,我们来逐步理解一下上面这个Web服务的实现过程。按照上面脚本的顺序,我们执行以下ant任务。
clean任务:这个ant任务将删除所有临时目录及上次编译生成的所有文件;
init任务:这个ant任务将创建几个新的目录;
precompile任务:这个步骤将编译Java接口类;
java2wsdl任务:java2wsdl将从预先编译过的Java接口中生成WSDL,您可以浏览Apache的http://ws.apache.org/axis/java/reference.html页面,了解上面脚本中java2wsdl任务中的一些选项的意义;
wsdl2java任务:我们现在开始实施契约最先式方法。一旦有了WSDL,我们就可以通过wsdl2java这个工具生成Web服务代码,包括Web服务的实现模板类,这些生成的文件都位于同一个目录中(如本例中的gensrc目录)。同时,本任务执行后也将产生一个用于部署Web服务的deploy.wsdd文件,和一个用于取消部署的undeploy.wsdd文件。在后面我们将通过这两个wsdd文件生成服务器端的配置;
implement任务:前面已经讲过,为了避免向生成的Web服务实现模板中手动添加代码,我们可以利用前面已经写好的一个与实现模板类同名的Java文件,这个文件中包含的类名与模板类也相同(即HelloWebService),并且它实现了业务逻辑。因此,我们可以将前面准备好的实现类替换生成的模板类,这样做的效果就相当于向生成的模板类中添加了业务代码;
compile任务:我们将通过该任务编译所有生成的类,其中包含业务逻辑的Web服务实现类;
copy任务:该任务将所有需要的Axis库文件拷贝到一个临时目录(如本例中的lib)中,以便于打包只用;
deploy任务:在本任务中,我们使用org.apache.axis.utils.Admin类产生部署描述文件server.config.wsdd,这个类的输入为前面wsdl2java任务生成的deploy.wsdd。然后,我们接下来创建一个标准的Web归档文件(.war),这样,我们可以非常容易地将它部署到我们钟爱的Web服务器中;
complieclient:这是最后一个步骤,编译客户端代码。
RpcClient.java
RpcClient利用自动生成的客户端存根文件,按RPC风格调用远程Web服务。
- public class RpcClient {
- private static String wsdlUrl = "http://localhost:8080/AxisEndToEnd/services/HelloWebService?WSDL";
- private static String namespaceURI = "http://AxisEndToEnd.axis.apache.binildas.com";
- private static String localPart = "IHelloWebService";
- protected void executeClient(String[] args) throws Exception {
- IHelloWebService iHelloWebService = null;
- IHelloWeb iHelloWeb = null;
- if (args.length == 3) {
- iHelloWebService = new IHelloWebServiceLocator(args[0], new QName(
- args[1], args[2]));
- } else {
- iHelloWebService = new IHelloWebServiceLocator(wsdlUrl, new QName(
- namespaceURI, localPart));
- }
- iHelloWeb = iHelloWebService.getHelloWebService();
- log("Response From Server : " + iHelloWeb.hello("Binil"));
- }
- public static void main(String[] args) throws Exception {
- RpcClient client = new RpcClient();
- client.executeClient(args);
- }
- }
CallClient.java
这里,我们还提供了一段叫做CallClient的Web服务客户端代码,其中使用了Axis和SOAP API,以面向文档的方式调用该Web服务。
- public class CallClient {
- public static String wsURL = "http://localhost:8080/AxisEndToEnd/services/HelloWebService?WSDL";
- public static String action = "HelloWebService";
-
- public static String msg = "<?xml version=/"1.0/" encoding=/"UTF-8/"?><soapenv:Envelope ...>";
- public static void test() throws Exception {
- InputStream input = new ByteArrayInputStream(msg.getBytes());
- Service service = new Service();
- Call call = (Call) service.createCall();
- SOAPEnvelope soapEnvelope = new SOAPEnvelope(input);
- call.setTargetEndpointAddress(new URL(wsURL));
- if (action != null) {
- call.setUseSOAPAction(true);
- call.setSOAPActionURI(action);
- }
- soapEnvelope = call.invoke(soapEnvelope);
- System.out.println("Response:/n" + soapEnvelope.toString());
- }
- public static void main(String args[]) throws Exception {
- CallClient callClient = new CallClient();
- if (args.length > 0) {
- wsURL = args[0];
- }
- if (args.length > 1) {
- action = args[1];
- }
- callClient.test();
- }
- }
上面的Web服务请求并没有显示完全,其完全版本请参考本章源代码。
运行服务器和客户端
前面的build.xml文件有点长,这对于新手而言,执行前面的10个步骤来实现Web服务,并不是一件简单的事。但是,我们可以使用一个简单的Ant命令来完成上面所有步骤,因此,您可以把上面的build.xml文件保存下来,以备以后在您的项目中重用它们。要编译服务器端代码,请执行下列命令:
cd ch03/02_Axis
ant
下图显示了每一步编译执行的过程:

编译完成后,我们会在下面的文件夹中找到可部署的Web归档文件(AxisEndToEnd.war):
Ch03/02_Axis/dist
现在,您就可以把这个war文件拷贝到您的Web服务器的Webapps目录,然后,重启服务器。如果部署没有问题,您就可以访问http://localhost:8080/AxisEndToEnd/services/HelloWebService?WSDL获取该Web服务的WSDL。
接下来您就可以执行客户端代码测试您的Web服务,因为我们提供了两个版本的客户端代码,因此,您测试Web服务时会有两个选择。要执行RpcClient,请使用下列命令:
cd ch03/02_Axis
ant runrpc
下图显示了RpcClient的执行过程,您会看到控制台上会打印出从服务器端接收到的应答。

要执行CallClient,请使用下列命令:
cd ch03/02_Axis
ant runcall
下图显示了CallClient的执行过程,您会看到控制台上会打印出从服务器端接收到的应答。
