引入: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消息中,而後傳遞到客戶端,客戶端對其解析還原。
從服務器控制檯,咱們也看到了LogHandler記錄下的真實的從客戶端發送以及從服務端返回的SOAP消息:
最後,咱們看下實際截圖:
顯然原始檔案庫在D:/tmp/resumeRep 目錄,咱們傳遞了Charles做爲面試者姓名,服務器端從檔案庫中獲取了charles_cv.doc,放到SOAP消息的附件中,而後客戶端從附件拿到了文檔,而且重命名而後保存到D:/tmp/Charles.doc中,一切順利。