Apache CXF 學習-使用Dispatch/Provider來直接處理SOAP消息

引入:java

前面的例子中咱們都是採用了SEI/SIB的方式來發送接收消息,其實咱們客戶端的代碼是直接採用的傳統的API調用,好比:web

service.calcSum(a, b)

而後,在CXF框架中,它會把這些API調用的方式經過JAXB轉爲SOAP格式的消息,而後返回SOAP格式的消息也經過JAXB轉回真正的返回值。因此這裏的弊端是,雖然真正在網絡上傳輸的是SOAP消息,可是咱們卻依然用傳統的調用方式操做,顯的畫蛇添足。若是一個對象很大,那麼將其經過JAXB轉爲SOAP消息則會花費必定的時間。那麼有沒有辦法可讓咱們客戶端和服務器端都直接對SOAP消息操做呢?這就須要咱們這裏討論的Dispatch/Provider技術。spring


實踐:apache

Dispatch/Provider老是成對用的,客戶端通常會構造一個SOAP消息,而後把它Dispatch到服務器的Endpoint之上,這就是Dispatch.而服務器端會給出如何對約定的SOAP消息格式進行處理而且構造返回消息的代碼,這就叫Provider。 從對於消息的處理方式上看, 有直接處理整個消息的,對應就是Service.Mode.MESSAGE,也有隻處理消息Payload的,對應就是Service.Mode.PAYLOAD,咱們這裏只演示Service.Mode.MESSAGE,另一個和這個用法相似。服務器


服務器端代碼:網絡

仍是從服務器端開始,首先咱們定義一個消息處理類CalcPlusServiceProvider,它能夠處理整個SOAP請求消息而且構造返回SOAP消息,咱們讓其邏輯爲只對請求的SOAP消息中的2個參數作加法運算,而後運算結果封裝在返回SOAP消息中,而且代碼中會分別把請求消息和響應消息打印到服務器的控制檯上。代碼以下:框架

package com.charles.cxfstudy.provider;
import java.io.IOException;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;
import org.w3c.dom.Node;
/**
 * 這個加法運算類用於演示基於Message模式的Provider,它會把消息做爲總體來處理
 * @author Administrator
 *
 */
@WebServiceProvider()
@ServiceMode(value=Service.Mode.MESSAGE)
public class CalcPlusServiceProvider implements Provider<DOMSource> {
    /**
     * 這個方法用於定義如何處理DOMSource的XML消息的邏輯,而且構造響應消息
     */
    public DOMSource invoke(DOMSource request) {
        try{
        //先構造 一個SOAPMessage,用於放入請求的SOAP消息
        MessageFactory factory = MessageFactory.newInstance();
        SOAPMessage soapRequestMsg = factory.createMessage();
        //注意,由於咱們的代碼是吧消息做爲總體處理,因此放入的是soapPart,而不是soapBody
        soapRequestMsg.getSOAPPart().setContent(request);
                                                                                                                                                                                                                                                                                                                                                                                    
        //打印到客戶端請求來的消息到控制檯
        System.out.println("從客戶端請求來的消息爲:");
        soapRequestMsg.writeTo(System.out);
        System.out.println();
                                                                                                                                                                                                                                                                                                                                                                                    
        //如今咱們從請求消息中分離出咱們所要的信息
        SOAPBody soapBody = soapRequestMsg.getSOAPBody();
                                                                                                                                                                                                                                                                                                                                                                                    
        Node calcSumNode = soapBody.getFirstChild();
                                                                                                                                                                                                                                                                                                                                                                                    
        //得到要作加法運算的數
        Node aNode = calcSumNode.getChildNodes().item(0);
        int a = Integer.parseInt(aNode.getTextContent());
        Node bNode = calcSumNode.getChildNodes().item(1);
        int b = Integer.parseInt(bNode.getTextContent());
                                                                                                                                                                                                                                                                                                                                                                                        
        //計算加法
        String sum = String.valueOf(a + b);
                                                                                                                                                                                                                                                                                                                                                                                    
        //封裝結果到響應對象中
        SOAPMessage soapResponseMsg = factory.createMessage();
                                                                                                                                                                                                                                                                                                                                                                                    
        //構造<calcSumResponse>元素,它的namespace爲"http://services.server.cxfstudy.charles.com",注意這個元素在SOAPMessage的<soap:Body>部分
        QName calcSumResponseQName = new QName("http://services.server.cxfstudy.charles.com","calcSumResponse");
        SOAPElement calcSumResponseEle = soapResponseMsg.getSOAPBody().addChildElement(calcSumResponseQName);
        calcSumResponseEle.addChildElement("sum").addTextNode(sum);
                                                                                                                                                                                                                                                                                                                                                                                    
        //打印即將返回到客戶端的響應消息到控制檯
        System.out.println("要發送到客戶端的消息爲:");
        soapResponseMsg.writeTo(System.out);
        System.out.println();
                                                                                                                                                                                                                                                                                                                                                                                    
        //把SOAPMessage轉爲DOMSource類型
        DOMSource response = new DOMSource(soapResponseMsg.getSOAPPart());
        return response;
                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                    
        }catch(SOAPException ex){
            ex.printStackTrace();
            return null;
        }catch(IOException ex){
            ex.printStackTrace();
            return null;
        }
                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                    
    }
}


爲了讓這個服務類生效,咱們配置到beans.xml中(參見http://supercharles888.blog.51cto.com/609344/1361334)dom

<?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:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<!-- 導入cxf中的spring的一些配置文件,他們都在cxf-<version>.jar文件中 -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint
id="calcPlusService"
implementor="com.charles.cxfstudy.provider.CalcPlusServiceProvider"
address="/calcPlus" />
</beans>


打包並部署應用到服務器上,就可使用了,最終的wsdl文件以下:ide

<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://provider.cxfstudy.charles.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalcPlusServiceProviderService" targetNamespace="http://provider.cxfstudy.charles.com/">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://provider.cxfstudy.charles.com/" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://provider.cxfstudy.charles.com/">
<xsd:element name="invoke" nillable="true" type="xsd:anyType"/>
<xsd:element name="invokeResponse" nillable="true" type="xsd:anyType"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="invokeResponse">
<wsdl:part element="tns:invokeResponse" name="invokeResponse"></wsdl:part>
</wsdl:message>
<wsdl:message name="invoke">
<wsdl:part element="tns:invoke" name="invoke"></wsdl:part>
</wsdl:message>
<wsdl:portType name="CalcPlusServiceProvider">
<wsdl:operation name="invoke">
<wsdl:input message="tns:invoke" name="invoke"></wsdl:input>
<wsdl:output message="tns:invokeResponse" name="invokeResponse"></wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalcPlusServiceProviderServiceSoapBinding" type="tns:CalcPlusServiceProvider">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="invoke">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="invoke">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="invokeResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalcPlusServiceProviderService">
<wsdl:port binding="tns:CalcPlusServiceProviderServiceSoapBinding" name="CalcPlusServiceProviderPort">
<soap:address location="http://localhost:8080/cxf_jaxws_provider/services/calcPlus"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>



客戶端代碼:工具

如今咱們來構造客戶端,由於咱們的目的是使用直接構造而且發送SOAP消息的方式而不是相似SEI調用的方式來發送消息,因此咱們先定義工具類,內含一個工具方法能夠發送SOAP消息而且得到從服務器端的返回消息:

package com.charles.cxfstudy.dispatcher;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.Service.Mode;
/**
 * 工具類用於發送和接收消息
 * @author Administrator
 *
 */
public class DispatcherUtil {
                                                                                                                                                                                                                                                              
    /**
     * 把指定的SOAP消息發送到指定的endpoint上,而且給出返回的SOAP消息
     * @param wsdlURLString
     * @param serviceQName
     * @param serviceProviderServiceName
     * @param serviceProviderPortName
     * @param soapRequest
     * @param factory
     * @return
     * @throws MalformedURLException
     * @throws SOAPException
     */
    public static SOAPMessage sendMessage (
            String wsdlURLString, String serviceQName,String serviceProviderServiceName,String serviceProviderPortName,SOAPMessage soapRequest,MessageFactory factory) throws MalformedURLException,SOAPException{
        //把SOAPMessage轉爲Source類型
        DOMSource requestMsg =  new DOMSource(soapRequest.getSOAPPart());
                                                                                                                                                                                                                                                                  
        URL wsdlURL = new URL(wsdlURLString);
                                                                                                                                                                                                                                                                  
        //構造一個Service對象
        QName serviceProvider = new QName(serviceQName,serviceProviderServiceName);
        QName portName        = new QName(serviceQName,serviceProviderPortName);
        Service service  = Service.create(wsdlURL, serviceProvider);
                                                                                                                                                                                                                                                                  
        //利用Service對象來發送(Dispatch) Source類型的SOAPMessage到指定的Port上
        Dispatch<DOMSource> domMsg  = service.createDispatch(portName, DOMSource.class, Mode.MESSAGE);
        //得到響應消息
        DOMSource respMsg = domMsg.invoke(requestMsg);
        SOAPMessage soapResponse = factory.createMessage();
        soapResponse.getSOAPPart().setContent(respMsg);
                                                                                                                                                                                                                                                                  
        return soapResponse;
                                                                                                                                                                                                                                                                                  
    }
}


注意:我很是喜歡這種方式,由於它最直接了,發送什麼消息就構造什麼消息,而後直接調用API,而無需用wsimport工具去操做WSDL文件去生成N多樁文件了


而後咱們的測試類的方法就是構造SOAP消息,而後調用工具方法來發送SOAP消息而且獲取返回消息,而且分別打印到客戶端的控制檯上:

/**
 * 這裏用於演示如何用Dispatch來發送一個SOAP消息到指定的Provider
 * @author Administrator
 *
 */
public class MainTest {
                                                                                                                                                                     
    public static SOAPMessage buildMessageForAdd(MessageFactory factory) throws SOAPException{
                                                                                                                                                                         
        SOAPMessage soapRequest = factory.createMessage();
                                                                                                                                                                         
        //構造<calcSum>元素,它的namespace爲"http://services.server.cxfstudy.charles.com",注意這個元素在SOAPMessage的<soap:Body>部分
        QName calcSumQName = new QName("http://services.server.cxfstudy.charles.com","calcSum");
        SOAPElement calcSumEle = soapRequest.getSOAPBody().addChildElement(calcSumQName);
        //在<calcSum>元素中添加2個子元素,一個爲<a>3</a>,一個爲<b>5</b>
        calcSumEle.addChildElement("a").addTextNode("3");
        calcSumEle.addChildElement("b").addTextNode("5");
                                                                                                                                                                         
        return soapRequest;
                                                                                                                                                                         
                                                                                                                                                                         
    }
                                                                                                                                                                     
                                                                                                                                                                     
                                                                                                                                                                     
    public static void main(String [] args) throws Exception {
                                                                                                                                                                         
        String wsdlURLStringForCalcPlus = "http://localhost:8080/cxf_jaxws_provider/services/calcPlus?wsdl";
        String serviceQName            =  "http://provider.cxfstudy.charles.com/";
        String serviceProviderStringForCalcPlus = "CalcPlusServiceProviderService";
        String servicePortStringForCalcPlus = "CalcPlusServiceProviderPort";
                                                                                                                                                                         
        //構造要發送的Soap消息內容而且轉爲Source類型
        //從MessageFactory 構造一個要發送的Soap消息
        MessageFactory factory = MessageFactory.newInstance();
                                                                                                                                                                         
        SOAPMessage soapRequest= buildMessageForAdd(factory);
        System.out.println("發送的消息爲:");
        soapRequest.writeTo(System.out);
        System.out.println();
                                                                                                                                                                         
                                                                                                                                                                         
                                                                                                                                                                         
        SOAPMessage soapResponse =DispatcherUtil.sendMessage(
                wsdlURLStringForCalcPlus,serviceQName,serviceProviderStringForCalcPlus,servicePortStringForCalcPlus,soapRequest, factory);
        System.out.println("響應的消息爲:");
        soapResponse.writeTo(System.out);
                                                                                                                                                                         
                                                                                                                                                                         
                                                                                                                                                                         
        }
}


從上看出,咱們構造了一個消息,其包含2個數,一個是3,一個是5,咱們指望經過web service計算加法後返回8。


看客戶端控制檯:

wKioL1MIRYfBSWbAAAMEJv3S7UA774.jpg


看服務器端的控制檯:

wKioL1MIRZaiB109AAGcxW2ncwQ991.jpg


顯然,和咱們設想的同樣,全部如今的處理都是和最終消息打交道,而且web服務也正確的作了加法運算,因此咱們代碼是徹底正確的。


附加說明:

本例演示瞭如何用Dispatch/Provider發送和處理Service模式是MESSAGE的消息,若是要處理Service模式是PAYLOAD的消息,則應該以下:

@WebServiceProvider()
@ServiceMode(value=Service.Mode.PAYLOAD)
public class CalcMinusServiceProvider implements Provider<DOMSource> {
相關文章
相關標籤/搜索