UML软件工程组织

新一代Web Service 实现包 -- AXIS2 学习笔记 (二)

客户端的调用

Web services提供的服务多种多样,有的可以马上获得结果,有的要消耗很长的时间。所以,如果我们需要多种调用方式来对付不同的情况。 大多数的Web services都提供阻塞(Blocking)和非阻塞(Non-Blocking)两种APIs.  这两个概念以前应该学过,简单说一下。 Blocking API - 调用端要等被调用的函数运行完毕才继续往下走。

Non-Bloking API - 调用端运行完调用函数以后就直接往下走了,调用端和被调用端是异步执行的。返回值是用回调函数来实现的。 这种异步叫做API层异步API Level Asynchrony)。他们只用到一个连接来发送和接收消息,而且,如果是那种需要运行很长时间的函数,还会碰到Time Out 错误,如果用两个连接分别处理发送和接收消息,调用的时间就可以缩短,也可以解决Time Out 问题。用两个连接来分别处理发送和接收消息,叫做传输层异步Transport Level Asynchrony)。

 API 传输 描述
 阻塞  1连接   简单的传统用法
 非阻塞  1连接   使用回调或者轮询 
 阻塞  2连接  Service是有返回值的,但是它的传输是单路。(比如 SMTP)
 非阻塞  2连接  最大程度的异步执行

理论真无聊,还是来看实例吧。

打开 Eclipse, 创建一个新Project, 新建一个叫userguide.clients的包, 把"samples\userguide\src\userguide\clients" 下面的文件都copy到那个包下面, 把AXIS2的lib下面的jar都加到ilbrary里面去(应该不用全加,懒一点就全加了吧.) 发现了关于echo的调用的方式, 居然有五个:
EchoBlockingClient
EchoBlockingDualClient
EchoBlockingWsaBasedClient
EchoNonBlockingClient
EchoNonBlockingDualClient

一个一个看吧.
EchoBlockingClient.java
public class EchoBlockingClient {
    private static EndpointReference targetEPR = new EndpointReference("http://localhost:8080/axis2/services/MyService");

    public static void main(String[] args) {
        try {
            OMElement payload = ClientUtil.getEchoOMElement();
            Call call = new Call();
            call.setTo(targetEPR);
            call.setTransportInfo(Constants.TRANSPORT_HTTP,
                    Constants.TRANSPORT_HTTP,
                    false);

            //Blocking invocation
            OMElement result = call.invokeBlocking("echo",
                    payload);

            StringWriter writer = new StringWriter();
            result.serializeWithCache(XMLOutputFactory.newInstance()
                    .createXMLStreamWriter(writer));
            writer.flush();

            System.out.println(writer.toString());

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
    }
}

和一代几乎一样, 弄一个EndpointReference, 再弄一个call, 其他不一样,但是也很简单, 弄一个OMElement作为参数, 返回也是一个OMElement. 可惜运行居然有错.

再来看双通道的版本
EchoBlockingDualClient.java
public class EchoBlockingDualClient {
    private static EndpointReference targetEPR = new EndpointReference("http://127.0.0.1:8080/axis2/services/MyService");

    public static void main(String[] args) {
        try {
            OMElement payload = ClientUtil.getEchoOMElement();

            Call call = new Call();
            call.setTo(targetEPR);

            call.engageModule(new QName(Constants.MODULE_ADDRESSING));
            call.setTransportInfo(Constants.TRANSPORT_HTTP,
                    Constants.TRANSPORT_HTTP,
                    true);

            //Blocking Invocation
            OMElement result = call.invokeBlocking("echo",
                    payload);

            StringWriter writer = new StringWriter();
            result.serializeWithCache(XMLOutputFactory.newInstance()
                    .createXMLStreamWriter(writer));
            writer.flush();
            System.out.println(writer.toString());


            //Need to close the Client Side Listener.
            call.close();

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

加了一句engageModule, 这句话好像没什么用,我删掉这句话也能运行的, 然后setTransportInfo最后一个参数改成了true. 关于setTransportInfo的三个参数, 第一个是发送的Transport, 第二个是接收的Transport, 第三个是"是否双通道", 支持的搭配形式如下:
http, http, true
http, http, false
http,smtp,true
smtp,http,true
smtp,smtp,true

看下一个吧,EchoNonBlockingClient,这个是单通道的非阻塞模式:
public class EchoNonBlockingClient {
    private static EndpointReference targetEPR = new EndpointReference("http://127.0.0.1:8080/axis2/services/MyService");

    public static void main(String[] args) {
        try {
            OMElement payload = ClientUtil.getEchoOMElement();

            Call call = new Call();
            call.setTo(targetEPR);
            call.setTransportInfo(Constants.TRANSPORT_HTTP,
                    Constants.TRANSPORT_HTTP,
                    false);

            //Callback to handle the response
            Callback callback = new Callback() {
                public void onComplete(AsyncResult result) {
                    try {
                        StringWriter writer = new StringWriter();
                        result.getResponseEnvelope().serializeWithCache(XMLOutputFactory.newInstance()
                                .createXMLStreamWriter(writer));
                        writer.flush();
                        System.out.println(writer.toString());


                    } catch (XMLStreamException e) {
                        reportError(e);
                    }
                }

                public void reportError(Exception e) {
                    e.printStackTrace();
                }
            };

            //Non-Blocking Invocation
            call.invokeNonBlocking("echo", payload, callback);

            //Wait till the callback receives the response.
            while (!callback.isComplete()) {
                Thread.sleep(1000);
            }

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

不同的地方,只是调用的方法从invokeBlocking变成了invokeNonBlocking,然后写了一个简单的匿名Callback类作为回调函数。关于这个Callback类,它是一个抽象类,其中有两个方法:onComplete和reportError,都是client端必须实现的,他还有一个Field,就是complete,可以用来设置和查询调用是否完成。可惜也不能运行,和上面的错误一样,是在createSOAPMessage的时候报null错误。

看下一个EchoNonBlockingDualClient,非阻塞的双通道:
public class EchoNonBlockingDualClient {
    private static EndpointReference targetEPR = new EndpointReference("http://127.0.0.1:8080/axis2/services/MyService");

    public static void main(String[] args) {
        try {
            OMElement payload = ClientUtil.getEchoOMElement();

            Call call = new Call();
            call.setTo(targetEPR);

            //The boolean flag informs the axis2 engine to use two separate transport connection
            //to retrieve the response.
            call.engageModule(new QName(Constants.MODULE_ADDRESSING));
            call.setTransportInfo(Constants.TRANSPORT_HTTP,
                    Constants.TRANSPORT_HTTP,
                    true);

            //Callback to handle the response
            Callback callback = new Callback() {
                public void onComplete(AsyncResult result) {
                    try {
                        StringWriter writer = new StringWriter();
                        result.getResponseEnvelope().serializeWithCache(XMLOutputFactory.newInstance()
                                .createXMLStreamWriter(writer));
                        writer.flush();
                        System.out.println(writer.toString());


                    } catch (XMLStreamException e) {
                        reportError(e);
                    }
                }

                public void reportError(Exception e) {
                    e.printStackTrace();
                }
            };

            //Non-Blocking Invocation
            call.invokeNonBlocking("echo", payload, callback);

            //Wait till the callback receives the response.
            while (!callback.isComplete()) {
                Thread.sleep(1000);
            }
            //Need to close the Client Side Listener.
            call.close();

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}
双通道和单通道基本没什么不同,只是双通道的时候,它总是要设定engageModule,然后多了一个call.close();,自己关闭监听器(close the Client Side Listener)。

以上这些都是需要返回值的调用,如果不需要返回值呢,看看PingClient
public class PingClient {
    private static EndpointReference targetEPR = new EndpointReference("http://localhost:8080/axis2/services/MyService");

    public static void main(String[] args) {
        try {
            OMElement payload = ClientUtil.getPingOMElement();

            MessageSender msgSender = new MessageSender();
            msgSender.setTo(targetEPR);
            msgSender.setSenderTransport(Constants.TRANSPORT_HTTP);

            msgSender.send("ping", payload);

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        }
    }

}
呵呵,这也忒简单了一点,就一个msgSender.send就搞定了。

Client端的调用组合基本看完了,但是还有一个EchoBlockingWsaBasedClient,这是什么东啊? 还有那个engageModule是用来做啥的?先不管它们吧。

看看那个我认为最有用的工具:WSDL2Java,在bin目录下面有WSDL2Java.bat和WSDL2Java.sh,这个工具是用来干啥的呢,和一代一样,是用来生成stub的,就是说别人发布了Web Service以后,就会有一个wsdl文件,这个工具可以根据wsdl生成几个class,把底层的调用都wrap起来了,然后你用的时候就像普通函数调用一样。
演练一下吧,从命令行走到samples\wsdl目录下面,看到Axis2SampleDocLit.wsdl,执行..\..\bin\WSDL2Java.bat -uri Axis2SampleDocLit.wsdl,目录下面一下子多了两个目录,不管schemaorg_apache_xmlbeans,把codegen目录copy到Eclipse的一个Project里面去,哇,好多class啊,不看别的,就看Axis2SampleDocLitPortTypeStub,里面有三个函数是Web Service提供的:echoStringArray,echoStruct和echoString,哈哈,什么Call类了,MessageContext了,都在里面了,使用起来嘛,就像这样:
try {
     Axis2SampleDocLitPortTypeStub stub= new Axis2SampleDocLitPortTypeStub(null,                                "http://localhost:8080/axis2/services/Axis2SampleDocLitPortType");
     //Create the request document to be sent.
     EchoStringParamDocument  reqDoc= EchoStringParamDocument.Factory.newInstance();
     reqDoc.setEchoStringParam("Axis2 Echo");
     //invokes the web service.
     EchoStringReturnDocument resDoc=stub.echoString(reqDoc);
     System.out.println(resDoc.getEchoStringReturn());

    } catch (Exception e) {
        e.printStackTrace();
    }

就是创一个stub,再创一个参数类(EchoStringParamDocument),然后调用函数传参数,和普通的函数调用没有区别。如果你在命令行输入WSDL2Java.bat,会看到它的帮助提示如下:
Usage WSDL2Code -uri <Location of WSDL> :WSDL file location
-o <output Location> : output file location
-a : Generate async style code only. Default if off
-s : Generate sync style code only. Default if off. takes precedence over -a
-p <package name> : set custom package name
-l <language> : valid languages are java and csharp. Default is java
-t : Generate TestCase to test the generated code
-ss : Generate server side code (i.e. skeletons).Default is off
-sd : Generate service descriptor (i.e. axis2.xml).Default is off.Valid with -ss

 

版权所有:UML软件工程组织