Apache CXF 学习-使用MTOM来让客户端接收从服务端发过来的带附件的SOAP消息

简介:

引入:

上文中我们很详实的演示了如何通过MTOM,让客户端构造一个带附件的SOAP消息,然后服务器端解析处理SOAP消息,并且通过LogHandler来观察请求和返回的SOAP消息的内容。这里我们演示相反过程,就是如何从服务器端下载一个带附件的SOAP消息,然后客户端对这个SOAP消息进行处理,尤其是对附件的处理。


实践:

因为大体上都和上文类似,所以这里我就不做太多的讲解了。

我们设计某个需求,假设公司要面试,大家都把简历投到公司某个简历仓库中,所以我们需要通过web service,根据面试人的名字,从简历仓库获取其面试的职位和他的简历(是一个Microsoft Word文档)


服务端:

首先还是定义VO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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,和上文一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
  * 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中:

1
2
3
4
5
6
7
8
9
10
<? 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,它定义了下载面试人信息的业务方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
  * 这是一个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,并且提供业务方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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的支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<? 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。


客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
  * 客户端测试代码
  */
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中,一切顺利。





本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1362359,如需转载请自行联系原作者
目录
相关文章
|
1月前
|
Java Apache C++
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
Thrift 是一个轻量级、跨语言的远程服务调用框架,由 Facebook 开发并贡献给 Apache。它通过 IDL 生成多种语言的 RPC 服务端和客户端代码,支持 C++、Java、Python 等。Thrift 的主要特点包括开发速度快、接口维护简单、学习成本低和多语言支持。广泛应用于 Cassandra、Hadoop 等开源项目及 Facebook、百度等公司。
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
|
7月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
103 0
|
7月前
|
Java API Apache
ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
【4月更文挑战第11天】ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
78 11
|
7月前
|
存储 NoSQL 数据处理
Apache Paimon流式湖仓学习交流群成立
Apache Paimon流式湖仓学习交流群成立
516 59
|
7月前
|
Java API Apache
Apache CXF生成WebService的客户端
Apache CXF生成WebService的客户端
256 0
|
7月前
|
Apache
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
139 2
|
7月前
|
消息中间件 Java Kafka
Apache Kafka-初体验Kafka(04)-Java客户端操作Kafka
Apache Kafka-初体验Kafka(04)-Java客户端操作Kafka
62 0
|
9天前
|
存储 人工智能 大数据
The Past, Present and Future of Apache Flink
本文整理自阿里云开源大数据负责人王峰(莫问)在 Flink Forward Asia 2024 上海站主论坛开场的分享,今年正值 Flink 开源项目诞生的第 10 周年,借此时机,王峰回顾了 Flink 在过去 10 年的发展历程以及 Flink社区当前最新的技术成果,最后展望下一个十年 Flink 路向何方。
277 33
The Past, Present and Future of Apache Flink
|
2月前
|
SQL Java API
Apache Flink 2.0-preview released
Apache Flink 社区正积极筹备 Flink 2.0 的发布,这是自 Flink 1.0 发布以来的首个重大更新。Flink 2.0 将引入多项激动人心的功能和改进,包括存算分离状态管理、物化表、批作业自适应执行等,同时也包含了一些不兼容的变更。目前提供的预览版旨在让用户提前尝试新功能并收集反馈,但不建议在生产环境中使用。
826 13
Apache Flink 2.0-preview released
|
2月前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
89 3

推荐镜像

更多
下一篇
DataWorks