如何使用JAX-WS绑定收发带附件的SOAP消息?

日期: 2010-12-08 作者:王博 来源:TechTarget中国 英文

  WPS和WID V7.0通过JAX-WS绑定对SOAP附件有了比较全面的支持,包括:无引用附件(Unreferenced Attachments,SOAP消息包封中没有对附件的引用);符合W3C规范的带引用附件(Referenced Attachments,SOAP消息包封中含有对附件的引用,参考W3C规范);WS-I AP 1.0规范定义的 wsi:swaRef 类型的附件(参考WS-I规范4.4节)。本文将结合实例详细介绍JAX-WS绑定对这三种附件的支持以及各自的使用场景。首先从无引用附件开始介绍。

  无引用附件(Unreferenced Attachments)

  无引用,是指在接口消息中没有附件相关定义;也就是说,用户可以定义任意接口,添加任意与接口定义无关的附件。当然,用户需要自定义规则来接收相关附件。反映在SOAP消息上,就是包封部分与附件部分找不到任何关联。下面通过实例,展示如何添加、获取这种附件,并说明JAX-WS绑定在其中的作用。

  创建JAX-WS绑定

  打开WID业务集成视图,新建名为AttSharedLib的库模块,用来存放本文所有实例的数据类型和接口定义。为本例定义接口UnRefIntf,并添加请求-响应方法sayHello, 该方法具有String类型的输入参数input1和返回参数output1,绑定类型采用默认的document literal wrapped。创建发送端SCA模块UnRefSWAModule,为其添加库AttSharedLib。在模块中创建导出组件UnRefExport,为其添加接口UnRefIntf。右键点击UnRefExport,选择生成默认Web Service绑定(SOAP1.1/HTTP)。这与新建普通的JAX-WS绑定是一样的,原因是无引用附件本身就与Web Service定义无关,JAX-WS绑定只负责对这样的附件透明传输,不会对附件做任何特殊处理。要处理接收到的附件或需要发送附件,需要使用中介流组件或WID的UTE工具。下面分别给出介绍。

  利用中介流模块获取和发送无引用附件

  在模块中添加中介流组件UnRefMFC,并将它与UnRefExport连在一起。为UnRefMFC的sayHello方法创建一个空白中介流,在流中依次添加Detach和Attach两个客户中介,如图 1。

 图 1.UnRefMFC中介流

图 1.UnRefMFC中介流

  在Detach中添加获取无引用附件的代码,参见清单 1。

  清单 1. 获取无引用附件代码
     
 

以下是引用片段:
java.util.List atts = (java.util.List)smo.getAttachments(); 
 commonj.sdo.DataObject att = (commonj.sdo.DataObject)atts.get(0); 
 String cId = att.getString(“contentID”); 
 String cType = att.getString(“contentType”); 
 byte[] cData = att.getBytes(“data”); 
 java.io.File f = new java.io.File(“C:\Input.” + cType.split(“/”)[1]); 
 try{ 
  java.io.FileOutputStream fos = new java.io.FileOutputStream(f); 
    fos.write(cData); 
    fos.close(); 
 }catch(Exception e){ 
  e.printStackTrace(); 
 } 
 out.fire(smo); 
     
  JAX-WS导出绑定将SOAP消息中的附件放入SCA消息头(后被中介流转化成SMO消息头)中,通过代码

以下是引用片段:
java.util.List atts = (java.util.List)smo.getAttachments(); 
     
  来获取全部传入附件。单个附件具有:属性 contentID,用来唯一标识一个附件,推荐用户用以区分无引用附件;属性contentType,用来判断附件的类型;属性data对应附件的 二进制数据。应用可以利用这些信息对附件进行相应的处理。比如本例依据附件的类型将其存入文件系统。

  Attach中所添加发送无引用附件的代码,参见清单 2。

  清单 2. 发送无引用附件代码
     

以下是引用片段:
//Clear the original attachments 
java.util.List atts = (java.util.List)smo.getAttachments(); 
atts.clear(); 
try{ 
 com.ibm.websphere.sca.ServiceManager sm = new com.ibm.websphere.sca.ServiceManager();
 com.ibm.websphere.bo.BOFactory bof = (com.ibm.websphere.bo.BOFactory) sm 
 .locateService(“com/ibm/websphere/bo/BOFactory”); 
  commonj.sdo.DataObject att = bof 
 .create(“http://www.ibm.com/websphere/sibx/smo/v6.0.1″,”AttachmentType”); 
  //Load binary data to send 
  java.io.File file = new java.io.File(“C:\Input.jpeg”); 
  java.io.FileInputStream stream = new java.io.FileInputStream(file); 
  java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();  
  byte[] buffer = new byte[1024]; 
  int n; 
  while((n=stream.read(buffer))!=-1){ 
    out.write(buffer, 0, n); 
  } 
  stream.close(); 
  byte[] bytes = out.toByteArray(); 
  out.close(); 
   
  //attach the attachment 
  att.setString(“contentID”, “unref-attachment-1”); 
  att.setString(“contentType”, “image/jpeg”); 
  att.setBytes(“data”, bytes); 
  atts.add(att);  
 } 
 catch(Exception ex){ 
  ex.printStackTrace(); 
 } 
 out.fire(smo); 

     
  客户程序可以把smo当作附件存放的中介,因为JAX-WS导入绑定会把其中的所有 附件组织成SOAP消息的MIME部分进行发送。值得注意的是,在添加发送附件前,用户需要先清空已有附件(比如从导出端传入的附件),除非用户希望把已有附件一并发送。这样,无引用附件的应用程序开发完毕,下一节将对其进行测试。

  利用WID UTE进行测试

  我们利用WID UTE环境对本应用进行测试。安装WPS V7,并与WID关联,右键单击UnRefExport,在弹出菜单中选择测试组件,以打开集成测试客户端。在页面右下方的面板进行初始化请求参数设置。选择消息标签页,为input1赋予任意String类型的值;添加无引用附件。右键单击页面最下方的attachments,选择 添加元素,在弹出对话框中填写需要发送附件的个数,本例中填写 1。完成后,attachments多了一个子元素attachments[0],为其选择类型image/jpeg,在值字段为其选择 要发送的附件reqPhoto.jpeg。完成后如图 2。

图 2. 用UTE发送无引用附件

图 2. 用UTE发送无引用附件

  选择传输标签页,将URL的端口改为8888,供TCP/IP Monitor抓包使用, 配好并启动TCP/IP Monitor。点击页面左上角的“继续”按钮,启动测试。运行结束后,注意查看C盘下产生的文件Input.jpeg,这便是接收到的附件;如图3选择运行事件,

图 3. 无引用运行结果

图 3. 无引用运行结果

  在右下方的返回SOAP消息面板上注意图4所示的返回无引用附件。

图 4. 返回的无引用附件

图 4. 返回的无引用附件

  在TCP/Monitor中,可以看到图5所示的发送和接受SOAP消息。这样,无引用附件的介绍就完成了,下面介绍带引用的附件。

图 5. 捕获的消息

图 5. 捕获的消息

  符合W3C规范带的带引用附件(Referenced Attachments)

  带引用,是指在接口中包含与附件相关的定义;也就是WSDL消息定义中至少一个部分的类型是二进制(例如base64Binary),或指向一个二进制类型的元素。反映在SOAP消息上,就是包封部分具有对附件部分的引用,通过这个引用可以在MIME部分找到相应的附件。而W3C关于引用的定义是通过href指向附件的contentID完成的。下面展开对实例的介绍。

  为带引用附件创建JAX-WS绑定

  在AttSharedLib库模块中为本例定义一个名为W3CRefIntf的接口,并添加一个请求-响应方法sayHelloAgain,除了为该方法添加String类型的输入参数input1和返回参数output1外,还要添加一个类型是base64Binary的输入参数inputAttachment,这就是附件在接口中的定义;如果希望返回SOAP消息也带附件,就需要在返回消息中定义base64Binary类型的参数outputAttachment。 方法定义完成后,注意到接口配置表中绑定类型一行,我们需要点击右侧的改变绑定类型到document literal non-wrapped。这样,就得到了带引用附件的接口,如图 6。

图 6. 带引用附件接口
 
图 6. 带引用附件接口

  创建发送端SCA模块W3CRefModule,需添加库AttSharedLib。在模块中创建名为W3CRefExport的导出组件,添加接口W3CRefIntf。右键点击 W3CRefExport,选择生成默认的Web Service绑定(SOAP1.1/HTTP。这时,察看AttSharedLib的目录Web Service Ports,发现新生成了文件 W3CRefExport_W3CRefInftHttpPort,通过WSDL编辑器打开,所见既是WSDL接口MIME绑定描述文件。现截取sayHelloAgain方法的绑定描述如图 7。

图 7. 带引用绑定描述文件

图 7. 带引用绑定描述文件

  值得关注的是红框部分,请与接口定义部分相比对。JAX-WS绑定会把接收到的带引用附件按照绑定描述文件的定义组织到业务数据中,并在SCA或SMO消息头中生成关于这部分二进制业务数据的属性信息。要通过JAX-WS绑定发送带引用附件, 则需要通过中介流模块在SMO附件消息头中定义符合绑定描述文件的属性信息,这一点很重要,不然二进制数据无法以附件方式发送。具体使用细节在下节中讨论。

  获取和发送符合W3C规范的带引用附件

  在模块中添加中介流组件W3CRefMFC,并将其与W3CRefExport连在一起。为W3CRefMFC的sayHelloAgain方法创建一个空白中介流,在流中依次添加Detach和Attach两个客户中介,并在它们之间添加一个XSLT转换模块,如图8。

图 8.W3CRefMFC中介流

图 8.W3CRefMFC中介流

  为Detach的输出添加消息类型为sayHelloAgainRequestMsg,为Attach的输入添加消息类型为sayHelloAgainResponseMsg。您会注意到,在 XSLT 转换模块中有一个错误,提示映射方式为空,我们需要新建映射文件。有一点值得注意,在定义消息类型时,需要为消息根选择”/”,如图 9 所示。这样,所有的附件元素才可以参加映射。

图 9. 新建XML映射

图 9. 新建XML映射

  在Detach中添加获取引用附件的代码,参见 清单 3。

  清单 3. 获取引用附件代码
     
 

以下是引用片段:
byte[] cData = smo.getBytes(“/body/sayHelloAgainParameters1”); 
 java.util.List atts = (java.util.List)smo.getAttachments(); 
 commonj.sdo.DataObject att = (commonj.sdo.DataObject)atts.get(0); 
 String cType = att.getString(“contentType”); 
 String cID = att.getString(“contentID”); 
 System.out.println(“>> contentID is:” + cID); 
 String bodyPath = att.getString(“bodyPath”); 
 System.out.println(“>> bodyPath is:” + bodyPath); 
 java.io.File f = new java.io.File(“C:\Input.” + cType.split(“/”)[1]); 
 try{ 
  java.io.FileOutputStream fos = new java.io.FileOutputStream(f); 
    fos.write(cData); 
    fos.close(); 
 }catch(Exception e){ 
  e.printStackTrace(); 
 } 
 out.fire(smo); 
     
  值得注意的是,与无引用附件数据的获取方式不同,引用附件的数据是从业务数据中得到的,用户需要显示指定附件在消息定义中的XPath,如本例

以下是引用片段:
byte[] cData = smo.getBytes(“/body/sayHelloAgainParameters1”); 
     
  而通过smo.getAttachments()得到的,只是除二进制数据外的附件属性:contentID、contentType以及bodyPath。用户需要通过 bodyPath 进行数据和属性的匹配而不是contentID。

  如果需要发送带引用附件,首先需要在业务数据中包含二进制数据,然后就是在SMO消息头中包含附件属性的定义。在本例中,将输入附件做为返回附件发回客户端。这个操作是通过XSLT来完成的,如图10。

图 10.XSLT映射

图 10.XSLT映射

  强调一点,bodyPath要改为与返回消息相一致(sayHelloAgainResult1)。当然,这部分工作也可在Attach模块中通过用户代码实现,与无引用附件的添加一样需注意,在放入新附件前,用户需要先清空已有附件(比如从导出端传入的附件);还有就是附件的数据要通过 XPath显示设置到SMO中去。您可以根据兴趣自己在Attach中添加代码进行尝试。完成以后进行下面的测试。

  测试

  测试方法与无引用附件类似,只是添加附件的位置是在Body里面,为sayHelloAgainParameters1的值字段选择要发送的附件reqPhoto.jpeg。接下来做好其他相关配置并启动测试运行。运行结束后,注意查看C盘下产生的文件Input.jpeg,这便是接收到的附件;并注意查看返回的带引用附件。在TCP/Monitor中,可以看到发送和接受的SOAP消息。在发送的SOAP消息体 第一级子元素中,会发现

以下是引用片段:
<ns2:sayHelloAgainParameters1  
href=”cid:261275677705.1277098665390.IBM.WEBSERVICES@Bob”>
 </ns2:sayHelloAgainParameters1> 
     
  这就是对附件的引用元素。同时察看SystemOut.log中打出的以下两行,并与引用元素比对以体会W3C所定义的带引用附件的工作原理。最后,进行wsi:swaRef部分的介绍。

 

以下是引用片段:
contentID is:261275677705.1277098665390.IBM.WEBSERVICES@Bob 
 bodyPath is:sayHelloAgainParameters1 
     
  wsi:swaRef 类型的附件

  W3C定义的带引用附件具有以下几个缺点:首先,由于附件是在WSDL绑定描述文件中定义的,所以从消息定义本身无法区分其是否包含附件,这带来了从WSDL接口到Java接口映射时的不确定性(一个二进制定义到底映射为附件类型(javax.activation.DataHandler)还是byte[]);其次,只有消息的第一级子元素可以被定义为附件,一个复杂类型的某个深层次子元素无法定义为附件,这大大限制了附件的应用范围;最后,对于业界广泛应用的document/literal wrapped类型不能很好的支持。为解决这些问题,WS-I 为附件创建了一个新的XML类型:wsi:swaRef,参考wsi:swaRef schema。在WSDL接口定义中,对应 wsi:swaRef 类型的元素是一个对附件的引用,其值是附件的”cid:”+附件 contentID。

  创建wsi:swaRef类型JAX-WS绑定

  在AttSharedLib库模块的预定义资源中选择swaRef schema file,如图 11。

图 11. 添加swaRef schema

图 11. 添加swaRef schema

  在库模块的数据类型中新建UserData,并为其添加两个域,string类型的name和swaRef类型的photo,显然,photo域对应一个附件。为本例定义一个名为SwaRefIntf的接口,并添加一个请求响应方法echoPhoto,为方法定义同为UserData类型的输入、输出参数input1和output1。方法定义完成后,请注意接口配置表中的绑定类型一行,它所显示的是默认值document literal wrapped,不做改动。

  创建SCA模块SwaRefModule,并添加库AttSharedLib。在模块中创建名为SwaRefExport的导出组件,为其添加接口SwaRefIntf。右键点击SwaRefExport,选择生成默认的Web Service绑定(SOAP1.1/HTTP)。这时,察看AttSharedLib的目录Web Service Ports,发现新生成了文件SwaRefExport_SwaRefInftHttpPort,通过WSDL编辑器打开,所见既是WSDL接口绑定描述文件,它与普通WSDL绑定并无不同。这是因为,swaRef也是通过XML定义的,JAX-WS绑定对它的处理与其他XML类型一致。JAX-WS绑定并没有做针对wsi:swaRef元素的特殊处理,在使用上,完全需要用户严格按照规范来操作;在业务数据中出现的每个wsi:swaRef类型的元素,都需要在SMO消息头中添加一个附件(添加方法同无引用附件)。 这样,在SOAP消息的包封中出现的每一个wsi:swaRef元素都具有相对应的附件,而它们是通过contentID关联的。

  接受和发送wsi:swaRef类型的附件

  在模块中添加名为SwaRefMFC的中介流组件,并将它和SwaRefExport组件连在一起。为SwaRefMFC的echoPhoto方法创建一个空白中介流,在流中依次添加Detach和Attach 两个客户中介,并在两个客户中介中添加一个XSLT转换模块,具体步骤与W3C引用附件相应部分类似,不再重复。接下来,在Detach中添加获取无引用附件的代码,参见 清单 4。

  清单 4. 获取wsi:swaRef类型附件的代码
     
 

以下是引用片段:
String refId = smo.getString(“/body/echoPhoto/input1/photo”); 
 System.out.println(“>> The value for swaRef element is: ” + refId); 
 java.util.List atts = (java.util.List)smo.getAttachments(); 
 commonj.sdo.DataObject att = (commonj.sdo.DataObject)atts.get(0); 
 String cID = att.getString(“contentID”); 
 System.out.println(“>> contentID is:” + cID); 
 if (refId.split(“cid:”)[1].equals(cID)){ 
  String cType = att.getString(“contentType”); 
  byte[] cData = att.getBytes(“data”); 
  java.io.File f = new java.io.File(“C:\Input.” + cType.split(“/”)[1]); 
  try{ 
   java.io.FileOutputStream fos = new java.io.FileOutputStream(f); 
      fos.write(cData); 
      fos.close(); 
  }catch(Exception e){ 
   e.printStackTrace(); 
  } 
 } 
 out.fire(smo); 
     
  用户需要显示指定wsi:swaRef类型的元素在消息定义中的XPath,来获取对附件的引用ID, 该ID的组成方式是(”cid:” + contentID);然后通过该ID从通过smo.getAttachments()得到的所有附件中以contentID进行匹配来寻找其所对应附件。如果需要发送 wsi:swaRef 类型的附件,需要给相应的wsi:swaRef元素赋值”cid:”+contentID,同时在SMO消息头中添加contentID相一致的附件。 在本例中,将输入附件做为返回附件发回客户端。这个操作是通过XSLT完成的。这部分工作也可在Attach模块中通过用户代码实现,和无引用附件的添加一样要注意,在放入新附件前,用户需要先清空已有附件(比如从导出端传入的附件)。您可以根据兴趣自己在Attach中添加代码进行尝试。下面进行测试。

  测试

  找到Body里的wsi:swaRef类型的元素,在相应的值字段选择要发送的附件reqPhoto.jpeg,如图 12。

图 12. 添加wsi:swaRef类型附件

图 12. 添加wsi:swaRef类型附件

  接下来做好其他配置并启动测试。运行结束后,注意查看C盘下产生的文件Input.jpeg,这便是接收到的附件;并注意查看返回的附件。在 TCP/Monitor中 ,可以看到发送和接受的SOAP消息。截取发送消息如图 13 所示,请注意红框部分wsi:swaRef元素值与附件contentID的关系。至此,JAX-WS绑定所支持的全部三种附件类型已经介绍完毕,在文章的最后部分,将向您展示如何在不同的场景下选择恰当的附件类型。

图 13. 添加wsi:swaRef类型附件

图 13. 添加wsi:swaRef类型附件

  如何选择附件类型

  以上详细介绍了JAX-WS绑定在WPS V7.0中所支持的三种附件类型极其使用方法。那么在设计一个带二进制数据的服务接口时,如何选择附件使用类型呢?通过图14的问题组可以很方便的找到答案。

图 14. 附件类型选择

图 14. 附件类型选择

  总结

  本文结合实例,向您详细介绍了JAX-WS绑定在WPS V7.0中所支持的三种附件类型,它们的使用方法,以级JAX-WS绑定在其中起到的作用。在文章的最后,向您展示了如何在不同的场景下选择恰当的附件类型。

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

作者

王博
王博

相关推荐