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"  这个地址。

posted on 2015-01-18 19:09  烟火_  阅读(1214)  评论(0编辑  收藏  举报

导航