引入:
上文中我们很详实的演示了如何通过MTOM,让客户端构造一个带附件的SOAP消息,然后服务器端解析处理SOAP消息,并且通过LogHandler来观察请求和返回的SOAP消息的内容。这里我们演示相反过程,就是如何从服务器端下载一个带附件的SOAP消息,然后客户端对这个SOAP消息进行处理,尤其是对附件的处理。
实践:
因为大体上都和上文类似,所以这里我就不做太多的讲解了。
我们设计某个需求,假设公司要面试,大家都把简历投到公司某个简历仓库中,所以我们需要通过web service,根据面试人的名字,从简历仓库获取其面试的职位和他的简历(是一个Microsoft Word文档)
服务端:
首先还是定义VO:
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,和上文一样:
/**
* 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,它定义了下载面试人信息的业务方法:
/**
* 这是一个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中,一切顺利。
转载于:https://blog.51cto.com/supercharles888/1362359