首页 > 代码库 > cxf 消息寻址

cxf 消息寻址

一、消息寻址

  WS-Addressing是将消息路由数据包含在SOAP头中的一种标准方法。利用WS-Addressing的消息可以在标准化的SOAP头中包含自己的包含发送元数据,而不是依赖于网络层传输来传送路由信息。通过在标准的SOAP头中(wsa:ReplyTo)指定应答消息应该发送到哪里的端点引用,WS-Addressing可以支持异步交互方式。 服务提供者使用另一个连接,将应答消息发送给wsa:ReplyTo所指定的端点。这就将SOAP请求/应答消息的交互与HTTP请求/应答协议分离,这样,跨越任意时间的长时间运行的交互成为可能。

   通过这种方式,当服务器端需要较长时间来处理业务的时候,不需要一直保持与客户端的连接,当处理完毕之后,服务器将结果返回给指定的客户端。这类似于异步的交互。 

二、基于cxf的实现

  编写wsdl文件,存放于工程的src目录下

<?xml version="1.0" encoding="UTF-8"?><wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://apache.org/hello_world_soap_http"    xmlns:x1="http://apache.org/hello_world_soap_http/types" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"    xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="HelloWorld"    targetNamespace="http://apache.org/hello_world_soap_http">    <wsdl:types>        <schema xmlns="http://www.w3.org/2001/XMLSchema"            targetNamespace="http://apache.org/hello_world_soap_http/types"            elementFormDefault="qualified">            <element name="sayHi">                <complexType />            </element>            <element name="sayHiResponse">                <complexType>                    <sequence>                        <element name="responseType" type="xsd:string" />                    </sequence>                </complexType>            </element>            <element name="greetMe">                <complexType>                    <sequence>                        <element name="requestType" type="xsd:string" />                    </sequence>                </complexType>            </element>            <element name="greetMeResponse">                <complexType>                    <sequence>                        <element name="responseType" type="xsd:string" />                    </sequence>                </complexType>            </element>            <element name="greetMeOneWay">                <complexType>                    <sequence>                        <element name="requestType" type="xsd:string" />                    </sequence>                </complexType>            </element>            <element name="pingMe">                <complexType />            </element>            <element name="pingMeResponse">                <complexType />            </element>            <element name="faultDetail">                <complexType>                    <sequence>                        <element name="minor" type="xsd:short" />                        <element name="major" type="xsd:short" />                    </sequence>                </complexType>            </element>        </schema>    </wsdl:types>    <wsdl:message name="sayHiRequest">        <wsdl:part element="x1:sayHi" name="in" />    </wsdl:message>    <wsdl:message name="sayHiResponse">        <wsdl:part element="x1:sayHiResponse" name="out" />    </wsdl:message>    <wsdl:message name="greetMeRequest">        <wsdl:part element="x1:greetMe" name="in" />    </wsdl:message>    <wsdl:message name="greetMeResponse">        <wsdl:part element="x1:greetMeResponse" name="out" />    </wsdl:message>    <wsdl:message name="greetMeOneWayRequest">        <wsdl:part element="x1:greetMeOneWay" name="in" />    </wsdl:message>    <wsdl:message name="pingMeRequest">        <wsdl:part name="in" element="x1:pingMe" />    </wsdl:message>    <wsdl:message name="pingMeResponse">        <wsdl:part name="out" element="x1:pingMeResponse" />    </wsdl:message>    <wsdl:message name="pingMeFault">        <wsdl:part name="faultDetail" element="x1:faultDetail" />    </wsdl:message>    <wsdl:portType name="Greeter">        <wsdl:operation name="sayHi">            <wsdl:input message="tns:sayHiRequest" name="sayHiRequest" />            <wsdl:output message="tns:sayHiResponse" name="sayHiResponse" />        </wsdl:operation>        <wsdl:operation name="greetMe">            <wsdl:input message="tns:greetMeRequest" name="greetMeRequest" />            <wsdl:output message="tns:greetMeResponse" name="greetMeResponse" />        </wsdl:operation>        <wsdl:operation name="greetMeOneWay">            <wsdl:input message="tns:greetMeOneWayRequest" name="greetMeOneWayRequest" />        </wsdl:operation>        <wsdl:operation name="pingMe">            <wsdl:input name="pingMeRequest" message="tns:pingMeRequest" />            <wsdl:output name="pingMeResponse" message="tns:pingMeResponse" />            <wsdl:fault name="pingMeFault" message="tns:pingMeFault" />        </wsdl:operation>    </wsdl:portType>    <wsdl:binding name="Greeter_SOAPBinding" type="tns:Greeter">        <soap:binding style="document"            transport="http://schemas.xmlsoap.org/soap/http" />        <wsdl:operation name="sayHi">            <soap:operation soapAction="" style="document" />            <wsdl:input name="sayHiRequest">                <soap:body use="literal" />            </wsdl:input>            <wsdl:output name="sayHiResponse">                <soap:body use="literal" />            </wsdl:output>        </wsdl:operation>        <wsdl:operation name="greetMe">            <soap:operation soapAction="" style="document" />            <wsdl:input name="greetMeRequest">                <soap:body use="literal" />            </wsdl:input>            <wsdl:output name="greetMeResponse">                <soap:body use="literal" />            </wsdl:output>        </wsdl:operation>        <wsdl:operation name="greetMeOneWay">            <soap:operation soapAction="" style="document" />            <wsdl:input name="greetMeOneWayRequest">                <soap:body use="literal" />            </wsdl:input>        </wsdl:operation>        <wsdl:operation name="pingMe">            <soap:operation style="document" />            <wsdl:input>                <soap:body use="literal" />            </wsdl:input>            <wsdl:output>                <soap:body use="literal" />            </wsdl:output>            <wsdl:fault name="pingMeFault">                <soap:fault name="pingMeFault" use="literal" />            </wsdl:fault>        </wsdl:operation>    </wsdl:binding>    <!-- SOAPService 的访问地址, 与server中的发布地址一致 -->    <wsdl:service name="SOAPService">        <wsdl:port binding="tns:Greeter_SOAPBinding" name="SoapPort">            <soap:address location="http://localhost:9000/SoapContext/SoapPort" />            <wswa:UsingAddressing xmlns:wswa="http://www.w3.org/2005/02/addressing/wsdl" />        </wsdl:port>    </wsdl:service></wsdl:definitions>

  服务实现类GreeterImpl:

package demo.ws_addressing.server;import java.util.logging.Logger;import javax.jws.WebService;import org.apache.hello_world_soap_http.Greeter;import org.apache.hello_world_soap_http.PingMeFault;import org.apache.hello_world_soap_http.types.FaultDetail;@WebService(name = "SoapPort", portName = "SoapPort", serviceName = "SOAPService", targetNamespace = "http://apache.org/hello_world_soap_http", wsdlLocation = "file:./src/hello_world_addr.wsdl")// wsdlLocation 指定wsdl文件的地址public class GreeterImpl implements Greeter {    private static final Logger LOG = Logger.getLogger(GreeterImpl.class            .getPackage().getName());    public String greetMe(String me) {        LOG.info("Executing operation greetMe");        System.out.println("Executing operation greetMe");        System.out.println("Message received: " + me + "\n");        return "Hello " + me;    }    public void greetMeOneWay(String me) {        LOG.info("Executing operation greetMeOneWay");        System.out.println("Executing operation greetMeOneWay\n");        System.out.println("Hello there " + me);    }    public String sayHi() {        LOG.info("Executing operation sayHi");        System.out.println("Executing operation sayHi\n");        return "Bonjour";    }    public void pingMe() throws PingMeFault {        FaultDetail faultDetail = new FaultDetail();        faultDetail.setMajor((short) 2);        faultDetail.setMinor((short) 1);        LOG.info("Executing operation pingMe, throwing PingMeFault exception");        System.out                .println("Executing operation pingMe, throwing PingMeFault exception\n");        throw new PingMeFault("PingMeFault raised by server", faultDetail);    }}

  服务器端配置文件server.xml,存放在demo.ws_addressing.server包下

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:cxf="http://cxf.apache.org/core" xmlns:wsa="http://cxf.apache.org/ws/addressing"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation=" http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <cxf:bus>        <cxf:features>            <wsa:addressing />        </cxf:features>    </cxf:bus></beans>

  服务端启动类Server:

package demo.ws_addressing.server;import java.net.URL;import javax.xml.ws.Endpoint;import org.apache.cxf.Bus;import org.apache.cxf.BusFactory;import org.apache.cxf.bus.spring.SpringBusFactory;public class Server {    protected Server() throws Exception {        System.out.println("Starting Server");        SpringBusFactory bf = new SpringBusFactory();        URL busFile = Server.class.getResource("server.xml");        Bus bus = bf.createBus(busFile.toString());        BusFactory.setDefaultBus(bus);        Object implementor = new GreeterImpl();        String address = "http://localhost:9000/SoapContext/SoapPort"; // 发布服务地址,与wsdl中的访问地址一致        Endpoint.publish(address, implementor);    }    public static void main(String args[]) throws Exception {        new Server();        System.out.println("Server ready...");        Thread.sleep(5 * 60 * 1000);        System.out.println("Server exiting");        System.exit(0);    }}

  客户端配置文件client.xml存放在demo.ws_addressing.client包下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"    xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:http="http://cxf.apache.org/transports/http/configuration"    xsi:schemaLocation="http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd         http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <!--     表示服务器端处理完成后,将结果返回给"http://localhost:9990/decoupled_endpoint"。 这个地址。如果直接返回给自己,则可将下面注释 -->    <http:conduit        name="{http://apache.org/hello_world_soap_http}SoapPort.http-conduit">        <http:client DecoupledEndpoint="http://localhost:9990/decoupled_endpoint" />    </http:conduit>        <cxf:bus>        <cxf:features>            <wsa:addressing />        </cxf:features>    </cxf:bus></beans>

  客户端访问类Client:

package demo.ws_addressing.client;import static org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES;import java.lang.reflect.UndeclaredThrowableException;import java.net.URL;import java.util.Map;import javax.xml.namespace.QName;import javax.xml.ws.BindingProvider;import org.apache.cxf.Bus;import org.apache.cxf.BusFactory;import org.apache.cxf.bus.spring.SpringBusFactory;import org.apache.cxf.ws.addressing.AddressingProperties;import org.apache.cxf.ws.addressing.AttributedURIType;import org.apache.cxf.ws.addressing.ObjectFactory;import org.apache.hello_world_soap_http.Greeter;import org.apache.hello_world_soap_http.PingMeFault;import org.apache.hello_world_soap_http.SOAPService;public final class Client {    private static final QName SERVICE_NAME = new QName(            "http://apache.org/hello_world_soap_http", "SOAPService");    private static final ObjectFactory WSA_OBJECT_FACTORY = new ObjectFactory();    private static final String USER_NAME = System.getProperty("user.name");    private Client() {    }    public static void main(String args[]) throws Exception {         try {            URL wsdlURL = Client.class.getClassLoader().getResource(                    "hello_world_addr.wsdl");//加载wsdl文件            SpringBusFactory bf = new SpringBusFactory();            URL busFile = Client.class.getResource("client.xml");            Bus bus = bf.createBus(busFile.toString());            BusFactory.setDefaultBus(bus);            SOAPService service = new SOAPService(wsdlURL, SERVICE_NAME);            Greeter port = service.getSoapPort();            implicitPropagation(port);            // explicitPropagation(port);            // implicitPropagation(port);        } catch (UndeclaredThrowableException ex) {            ex.getUndeclaredThrowable().printStackTrace();        } catch (Throwable ex) {            ex.printStackTrace();        } finally {            System.exit(0);        }    }    /**     * A series of invocations with implicitly propogated Message Addressing     * Properties.     */    private static void implicitPropagation(Greeter port) {        System.out.println();        System.out.println("Implicit MessageAddressingProperties propagation");        System.out.println("------------------------------------------------");        System.out.println("Invoking sayHi...");        String resp = port.sayHi();        System.out.println("Server responded with: " + resp + "\n");        System.out.println("Invoking greetMe...");        resp = port.greetMe(USER_NAME);        System.out.println("Server responded with: " + resp + "\n");        System.out.println("Invoking greetMeOneWay...");        port.greetMeOneWay(USER_NAME);        System.out.println("No response from server as method is OneWay\n");        try {            System.out.println("Invoking pingMe, expecting exception...");            port.pingMe();        } catch (PingMeFault ex) {            System.out.println("Expected exception occurred: " + ex);        }    }    private static AddressingProperties createMaps() {        // get Message Addressing Properties instance        AddressingProperties maps = new AddressingProperties();        // set MessageID property        AttributedURIType messageID = WSA_OBJECT_FACTORY                .createAttributedURIType();        messageID.setValue("urn:uuid:" + System.currentTimeMillis());        maps.setMessageID(messageID);        return maps;    }    /**     * A series of invocations with explicitly propogated Message Addressing     * Properties.     */    private static void explicitPropagation(Greeter port) {        System.out.println();        System.out.println("Explicit MessageAddressingProperties propagation");        System.out.println("------------------------------------------------");        // associate MAPs with request context        Map<String, Object> requestContext = ((BindingProvider) port)                .getRequestContext();        requestContext.put(CLIENT_ADDRESSING_PROPERTIES, createMaps());        System.out.println("Invoking sayHi...");        String resp = port.sayHi();        System.out.println("Server responded with: " + resp + "\n");        // set the RelatesTo property to the initial message ID, so that        // the series of invocations are explicitly related        // RelatesToType relatesTo = WSA_OBJECT_FACTORY.createRelatesToType();        // relatesTo.setValue(messageID.getValue());        // maps.setRelatesTo(relatesTo);        System.out.println("Invoking greetMe...");        requestContext.put(CLIENT_ADDRESSING_PROPERTIES, createMaps());        resp = port.greetMe(USER_NAME);        System.out.println("Server responded with: " + resp + "\n");        System.out.println("Invoking greetMeOneWay...");        requestContext.put(CLIENT_ADDRESSING_PROPERTIES, createMaps());        port.greetMeOneWay(USER_NAME);        System.out.println("No response from server as method is OneWay\n");        // disassociate MAPs from request context        requestContext.remove(CLIENT_ADDRESSING_PROPERTIES);    }}

  分别运行Server类和Client类即可正常访问。通过tcpMon监听结果如下:

  1、当client中没有指定<http:conduit>元素时:

    请求:

技术分享

    响应

技术分享

    请求被处理后,结果返回给了原来的地址。

  2、当client中指定了<http:conduit>元素时:    

    请求

技术分享

    该soap请求<soap:Header>中指定了处理完成后,将结果返回给特定的地址。

    响应

技术分享

    响应为202,没有响应体。服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。服务器接收到请求后,将处理后的结果转发给"http://localhost:9990/decoupled_endpoint"  这个地址。

cxf 消息寻址