Apache CXF 學習-使用MTOM來讓客戶端接收從服務端發過來的帶附件的SOAP消息

引入:java

上文中咱們很詳實的演示瞭如何經過MTOM,讓客戶端構造一個帶附件的SOAP消息,而後服務器端解析處理SOAP消息,而且經過LogHandler來觀察請求和返回的SOAP消息的內容。這裏咱們演示相反過程,就是如何從服務器端下載一個帶附件的SOAP消息,而後客戶端對這個SOAP消息進行處理,尤爲是對附件的處理。web


實踐:面試

由於大致上都和上文相似,因此這裏我就不作太多的講解了。spring

咱們設計某個需求,假設公司要面試,你們都把簡歷投到公司某個簡歷倉庫中,因此咱們須要經過web service,根據面試人的名字,從簡歷倉庫獲取其面試的職位和他的簡歷(是一個Microsoft Word文檔)apache


服務端:服務器

首先仍是定義VO:app

package com.charles.cxfstudy.server.vo;
import javax.activation.DataHandler;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.bind.annotation.XmlType;
/**
 * 咱們這裏定義一個VO,關於面試人的信息,好比姓名,職位,簡歷(是一個word文檔)
 * @author Administrator
 *
 */
@XmlType(name="candidateInfo")
@XmlAccessorType(XmlAccessType.FIELD)
public class CandidateInfo {
                                                                                                                                                                                                                                                                                                                              
    private String name; //面試人姓名
    private String job;  //面試人要應聘的職位
                                                                                                                                                                                                                                                                                                                              
    @XmlMimeType("application/octet-stream")
    private DataHandler resume; //面試人的簡歷,這是一個word文檔
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getJob() {
        return job;
    }
    public void setJob(String job) {
        this.job = job;
    }
    public DataHandler getResume() {
        return resume;
    }
    public void setResume(DataHandler resume) {
        this.resume = resume;
    }
}


而後咱們定義一個能夠監控請求/相應消息的LogHandler,和上文同樣:ide

/**
 * SOAP Handler能夠用來對SOAP消息進行訪問。
 * 這裏演示的是第一種,它必須實現SOAPHandler<SOAPMessageContext>接口
 */
package com.charles.cxfstudy.server.handlers;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
/**
 * 記錄進/出Endpoint的消息到控制檯的Handler
 *
 * @author charles.wang
 *
 */
public class LogHandler implements SOAPHandler<SOAPMessageContext> {
    /**
     * 如何去處理SOAP消息的邏輯。 這裏會先判斷消息的類型是入站仍是出站消息,而後把消息寫到標準輸出流
     */
    public boolean handleMessage(SOAPMessageContext context) {
        // 先判斷消息來源是入站仍是出站的
        // (入站表示是發送到web service站點的消息,出站表示是從web service站點返回的消息)
        boolean outbound = (Boolean) context
                .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        // 若是是出站消息
        if (outbound) {
            System.out.println("這是從Endpoint返回到客戶端的SOAP消息");
        } else {
            System.out.println("這是客戶端的請求SOAP消息");
        }
        SOAPMessage message = context.getMessage();
        try {
            message.writeTo(System.out);
            System.out.println('\n');
        } catch (Exception ex) {
            System.err.print("Exception occured when handling message");
        }
        return true;
    }
    /**
     * 如何去處理錯誤的SOAP消息 這裏會先打印當前調用的方法,而後從消息上下文中取出消息,而後寫到標準輸出流
     */
    public boolean handleFault(SOAPMessageContext context) {
        SOAPMessage message = context.getMessage();
        try {
            message.writeTo(System.out);
            System.out.println();
        } catch (Exception ex) {
            System.err.print("Exception occured when handling fault message");
        }
        return true;
    }
    /**
     * 這裏沒有資源清理的需求,因此咱們只打印動做到控制檯
     */
    public void close(MessageContext context) {
        System.out.println("LogHandler->close(context) method invoked");
    }
    public Set<QName> getHeaders() {
        return null;
    }
}


下面對LogHandler配置,添加其到HandlerChain中:測試

<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
   <handler-chain>
   <!-- 配置能夠記錄出/入Endpoint消息內容到控制檯的Handler -->
    <handler>
        <handler-name>LogHandler</handler-name>
        <handler-class>com.charles.cxfstudy.server.handlers.LogHandler</handler-class>
    </handler>
   </handler-chain>
</handler-chains>


而後咱們開發SEI,它定義了下載面試人信息的業務方法:this

/**
 * 這是一個web服務接口定義,定義瞭如何對於去人才庫下載指定面試人的信息的處理 (內含做爲附件的簡歷)
 */
package com.charles.cxfstudy.server.services;
import javax.jws.WebParam;
import javax.jws.WebService;
import com.charles.cxfstudy.server.vo.CandidateInfo;
import com.charles.cxfstudy.server.vo.Profile;
/**
 * @author Administrator
 *
 */
@WebService
public interface IDownloadCandidateInfoService {
                                                                                                                                                                                                                                            
    /**
     * 下載面試人的信息
     */
    CandidateInfo downloadCandidateInfo(@WebParam(name="name") String name);
}


而後開發SIB,它實現了SEI,而且提供業務方法的實現:

package com.charles.cxfstudy.server.services;
import java.io.File;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.jws.HandlerChain;
import javax.jws.WebService;
import com.charles.cxfstudy.server.vo.CandidateInfo;
/**
 * 服務實現類,提供下載面試人信息的類
 * @author Administrator
 *
 */
@WebService(endpointInterface = "com.charles.cxfstudy.server.services.IDownloadCandidateInfoService")
@HandlerChain(file="/handler_chains.xml")
public class DownloadCandidateInfoServiceImpl implements
        IDownloadCandidateInfoService {
                                                                                                                                                                                                                              
    private String resumeRepositoryPath="D:/tmp/resumeRep/";
    /**
     * 去人才庫去下載指定的面試人信息,而後返回給客戶端
     */
    public CandidateInfo downloadCandidateInfo(String name) {
                                                                                                                                                                                                                                  
        CandidateInfo ci = new CandidateInfo();
        if(name.trim().equals("Charles")){
            ci.setName("Charles");
            ci.setJob("System Architect");
            ci.setResume(new DataHandler(new FileDataSource(new File(resumeRepositoryPath+"charles_cv.doc"))));
        }
        else{
            ci.setName("Kevin");
            ci.setJob("Senior Developer");
            ci.setResume(new DataHandler(new FileDataSource(new File(resumeRepositoryPath+"kevin_cv.doc"))));
        }
                                                                                                                                                                                                                                  
        return ci;    
    }
}


而後在bean配置文件中聲明咱們的SIB,而且在服務器端啓用對MTOM的支持:

<?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-servlet.xml" />
...
<!-- 這裏是第二個web service,它提供下載應聘人信息的功能(主要演示從服務器返回帶附件的SOAP消息 -->
<jaxws:endpoint
id="downloadCandidateInfoService"
implementor="com.charles.cxfstudy.server.services.DownloadCandidateInfoServiceImpl"
address="/downloadCandidateInfo" >
  <!-- 下面這段註釋在服務器端開啓了MTOM,因此它能夠正確的處理來自客戶端的帶附件的SOAP消息 -->
   <jaxws:properties>
       <entry key="mtom-enabled" value="true"/>
   </jaxws:properties>
</jaxws:endpoint>
</beans>



打包部署到容器,直到經過頁面能夠訪問對應的wsdl。


客戶端:

/**
 * 客戶端測試代碼
 */
package com.charles.mtom.receiveattachedsoap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
/**
 * @author charles.wang
 *
 */
public class MainTest {
    public static void main(String [] args) throws Exception{
                                                                                                                                                                             
    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
    factory.setServiceClass(IDownloadCandidateInfoService.class);
                                                                                                                                                                             
    //下面三行代碼:激活客戶端能處理帶附件的SOAP消息的功能:
    Map<String,Object> props = new HashMap<String,Object>();
    props.put("mtom-enabled", Boolean.TRUE);
    factory.setProperties(props);
                                                                                                                                                                                 
    factory.setAddress("http://localhost:8080/cxf_mtom_service/services/downloadCandidateInfo");
                                                                                                                                                                             
    //調用業務方法
    IDownloadCandidateInfoService service = (IDownloadCandidateInfoService) factory.create();
                                                                                                                                                                             
    //調用業務方法,發送soap請求,獲取帶附件的soap消息
    CandidateInfo candidateInfo = service.downloadCandidateInfo("Charles");
    String candidateName = candidateInfo.getName();
    String candidateJob  = candidateInfo.getJob();
    InputStream is =candidateInfo.getResume().getInputStream();
    File candidateResume = new File("D:/tmp/download"+candidateName+".doc");
    OutputStream os = new FileOutputStream(candidateResume);
                                                                                                                                                                             
    //把返回的附件複製到咱們指定的文件中
    byte[] b = new byte[100000];
    int bytesRead = 0;
    while ((bytesRead = is.read(b)) != -1) {
        os.write(b, 0, bytesRead);
    }
    os.close();
    is.close();
                                                                                                                                                                             
    System.out.println("面試候選人名字爲:"+candidateName);
    System.out.println("面試候選人申請職位爲:"+candidateJob);
    System.out.println("面試候選人簡歷在:"+candidateResume.getAbsolutePath()+","+"文件大小爲:"+candidateResume.length()+"字節");
                                                                                                                                                                             
    }
}



咱們運行這個例子,果真,咱們傳遞的面試者的名字對應的附件被服務器塞到SOAP消息中,而後傳遞到客戶端,客戶端對其解析還原。

wKioL1MJscSy9baoAAIO_YCN3sw505.jpg


從服務器控制檯,咱們也看到了LogHandler記錄下的真實的從客戶端發送以及從服務端返回的SOAP消息:

wKiom1MJsVSi8ZJdAALAqLVoF2M285.jpg

最後,咱們看下實際截圖:

wKiom1MJsiziCtcXAANiuoFtiDQ596.jpg

顯然原始檔案庫在D:/tmp/resumeRep 目錄,咱們傳遞了Charles做爲面試者姓名,服務器端從檔案庫中獲取了charles_cv.doc,放到SOAP消息的附件中,而後客戶端從附件拿到了文檔,而且重命名而後保存到D:/tmp/Charles.doc中,一切順利。

相關文章
相關標籤/搜索