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,如需转载请自行联系原作者
目录
相关文章
|
13天前
|
Java Apache C++
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
Thrift 是一个轻量级、跨语言的远程服务调用框架,由 Facebook 开发并贡献给 Apache。它通过 IDL 生成多种语言的 RPC 服务端和客户端代码,支持 C++、Java、Python 等。Thrift 的主要特点包括开发速度快、接口维护简单、学习成本低和多语言支持。广泛应用于 Cassandra、Hadoop 等开源项目及 Facebook、百度等公司。
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
|
6月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
98 0
|
6月前
|
Java API Apache
ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
【4月更文挑战第11天】ZooKeeper【基础 03】Java 客户端 Apache Curator 基础 API 使用举例(含源代码)
74 11
|
6月前
|
存储 NoSQL 数据处理
Apache Paimon流式湖仓学习交流群成立
Apache Paimon流式湖仓学习交流群成立
490 59
|
6月前
|
Java API Apache
Apache CXF生成WebService的客户端
Apache CXF生成WebService的客户端
226 0
|
6月前
|
Apache
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
126 2
|
6月前
|
消息中间件 Java Kafka
Apache Kafka-初体验Kafka(04)-Java客户端操作Kafka
Apache Kafka-初体验Kafka(04)-Java客户端操作Kafka
58 0
|
28天前
|
SQL Java API
Apache Flink 2.0-preview released
Apache Flink 社区正积极筹备 Flink 2.0 的发布,这是自 Flink 1.0 发布以来的首个重大更新。Flink 2.0 将引入多项激动人心的功能和改进,包括存算分离状态管理、物化表、批作业自适应执行等,同时也包含了一些不兼容的变更。目前提供的预览版旨在让用户提前尝试新功能并收集反馈,但不建议在生产环境中使用。
576 13
Apache Flink 2.0-preview released
|
1月前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
63 3
|
2月前
|
SQL 消息中间件 关系型数据库
Apache Doris Flink Connector 24.0.0 版本正式发布
该版本新增了对 Flink 1.20 的支持,并支持通过 Arrow Flight SQL 高速读取 Doris 中数据。

推荐镜像

更多