贝高林的Blog

关注VoIP,关注通信领域的发展

贝高林ID:lin_bei
80898次访问,排名1164好友9人,关注者12
VoIP,TOM_SkYPE,WEB SERVICE
lin_bei的文章
原创 44 篇
翻译 9 篇
转载 16 篇
评论 74 篇
golin的公告

首页 点击这里给我发消息

博鸽-最好用的好友博客阅读器
最近评论
nightmair:你好,不知道你有没有检验这个cdr mysql 配置呢?我做过相同的动作,但是就是没有能够把cdr记录到数据库中,如果你已经解决了,请你帮助我一下好吗?
ZhMilo:你的程序并不能处理所有的wsdl啊?有什么改进么?
probezy:您好,我已经调通了,握手后,用一个Context就可以
probezy:您好,我是Asterisk新手,有些问题想请教:
1、我看到上面有两个拨号方案,它们是同时触发的吗?2、如果有两个Context,如何桥接成一路通话的?
3、您的callback.php向AMI发送的内容可以看一下吗?

谢谢,我写了一个SIP Proxy系统,目前想与Asterisk对接,但不知道如何配,请您多指教。
QQ:14795388……
roben_ivy:我一直在寻找有关WSDL文件解析的相关资料,真有幸在这里找到了。请问博主有没有有关UDDI方面的资料?
文章分类
收藏
相册
IT专家
Bob大叔
Eric Newcomer
Martin Fowler's Bliki
夏昕的专栏
孟岩
林信良的专栏
良葛格学习笔记
Open-Source SOA Resource
bloger's SOA resource
open-source ESB petals
open-source esb ServiceMix
Service Oriented Business Integration
开源社区与论坛
Csdn社区
IBM Java开发者社区
ITPUB论坛
JavaEye
JavaResearchOrg
Java开源大全(全)
martinfowler大师的主页
Matrix与Java共舞
中国Eclipse社区
最重要的J2EE门户
面向Java/J2EE的软件站点
同事blog链接
TOM-Skype掌门人(RSS)
同学blog链接
xiaofeng同学
微笑的百合
文生
团队blog
bettyna
Cactus团队blog
李彦超
赵宁
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 Web服务搜索与执行引擎(八)——WSDL解析精髓收藏

新一篇: Web服务搜索与执行引擎(九)——初看客户端如何调用Web服务 | 旧一篇: Web服务搜索与执行引擎(七)——重温WSDL与SOAP

       接上一篇文章的最后一段:“一般情况下,我们使用SOAP作为实现协议,那么客户端在分析了WSDL文件以后,将会把用户的输入转换成我们已经看到过的SOAP请求,之后的过程就与之前的完全一样。”基于这样的需求,接下应该做的第一件事情就是:分析处理WSDL文件。

上一篇blog里提到过,WSDL规范其实就好比是我们国家的法侓,它规定了公民(好比是SOAP消息)应该要怎么行使个人权力以及履行个人义务等,即具有指导性的意义。用比较规范的语言来说就是WSDL是用来描述一个异构组件如何被程远程未知平台调用的,即描述了SOAP消息的格式,在WEB服务中起到了服务端和服务客户端之间的契约作用。

在系列的第一篇文章里已经提到,系统的目标是:Web 服务客户机在事先不了解一个 Web 服务的组成的情况下可以动态地发现和调用该Web 服务。

那么,我们需要做什么才能动态地发现和调用这个服务?

首先,需要发现来自系统的服务资源库里关于此服务的信息,这个就是我在系列()Web服务搜索与执行引擎()--基于LuceneWeb服务检索 中写到的关于Web服务搜索模块如何从库里提取服务信息,一个最重要的信息是服务所对应的WSDL 文档的URL

其次,需要阅读来自服务提供者的 WSDL文档并进行解析,以获取各种信息。这一步就是本篇文章所要关注的。

最后,我将拥有足够的信息来使用SAAJ发送和接收对 Web 服务实现的 SOAP 请求。这一步就是在后续文章里将要写到的。

那么我们就来看看如何阅读来自服务提供者的 WSDL文档并进行解析?

WSDL的常用处理方法:

  • 基于 DOM 的方法:上一篇blog也说到了, WSDL 文件从本质上来讲是一个 XML 文件,现有的 DOM API(例如 Xerces)能够用来进行解析或者构建 WSDL 文件。这种方法是最通用的,但同时也是处理 XML 文件最费力的方法。尽管从技术上来讲是可行的,基于 DOM API 的实现对于代码敏感且容易出错。同时,这一解决方法迫使您不得不处理两个完全不同的模型:DOM WSDL 模型。
  • 基于特定 API 的方法: 利用 IBM WSDL4J 来实现 WSDL 操作。这种方法倾向于 WSDL 模型,它允许您直接操作 WSDL 。这种方法的不足在于您不仅要处理 WSDL 本身,还要处理 WS-AddressingWS-Policy 和扩展脚本。它同时还使用那些尚未成为标准的事物,这就意味着现有的一些 API 将会改变。这就意味着这种基于特定 API 的方法不得不掺杂一定数量的 DOM 处理。 但是这些不足跟前面提到的“倾向于 WSDL 模型,它允许您直接操作 WSDL”的好处比起来,我们还是觉得使用它是当前的最好选择。
  • 基于 Java 生成的方法:因为描述我们实现的所有脚本都是标准的 XML 脚本,因此可以生成对应于这些脚本的 Java 类(支持 XML 编组和分组)。在这种情况下, WSDL 文件直接转化为 Java 类,然后作为 Java 对象来管理,使用这种方式一个缺陷就是使客户程序代码庞大,臃肿,不好管理,增加了系统的复杂性。

   WSDL4J简介——解析和收集来自 WSDL 的信息

    WSDL4J是用来解析WSDL文件的技术,很多WEB服务的底层实现都用了该技术。

但在传统的WEB服务实现方案中,该技术作为底层实现被屏蔽。而我们的项目为了能够动态的调用异构平台的未知服务,必须使用该技术。

所以说WSDL4J 包将被用于以编程方式表示 WSDL 文件的元素,所以我可以浏览和收集来自该文件的各种信息。

  在服务资源库中查找 Web 服务

为了动态地调用 Web 服务,必须知道一些基本信息,如此Web服务的WSDL文档的URLWeb服务的名称。当服务消费者在同一种服务中,即搜索结果中选择一个后,可以根据服务ID,以及Vector totalservices获取一个Web服务对象,这个对象包含了有关远程Web服务的足够信息,注意是足够信息,当然有最起码的WSDL 文档的 URL。既然我知道了WSDL 实现的 URL,那我就可以继续用 WSDL4J 直接请求来自服务提供者的文档并对其进行解析,从而获得了下一步调用服务所需要的一个完全的Web服务信息,如操作的调用参数信息等等

解析 WSDL

既然经过上面步骤我们拥有了用于 Web 服务的 WSDL文档的 URL,那么我们现在就可以用到WSDL4J来解析它了。为了使用 SAAJ构建SOAP消息调用该服务,我们将需要从 WSDL 收集下列最基本的信息:

目标名称空间

服务名称

端口名称

操作名称

操作输入参数

 

 这时候如果大家对上面这些概念不太熟悉或者忘记了WSDL的详细细节,请翻看我上一篇blog:

 

Web服务搜索与执行引擎()——重温WSDLSOAP,另外对上一篇介绍WSDLblog做一个补充,因为解析WSDL文档时必须处理的一个问题就是处理definitions标签

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://weather.cactus.org" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://weather.cactus.org">

每个WSDL的根元素都是<definitions>,一般都在这里定义文档中的各种名称空间。对于上面的WSDL,定义了不少名称空间,现在来说说它们的作用。

我们都知道,WSDL应该是格式正确的XML文档。进一步,还应该把它看作一个Schema,因此,<definitions>元素中可以定义targetNamespace属性,表示在这个元素下的所有元素都属于这个目标名称空间。xmlns表示缺省的名称空间,请注意在上面的文档中,这个缺省名称空间的值和xmlns:wsdl的值是相同的(都是http://schemas.xmlsoap.org/wsdl/)。因此,在这个WSDL中的很多<wsdl:XXX>元素,例如<wsdl:types><wsdl:portType>等等,实际上省略掉前面的“wsdl:”效果也是一样的。

名称空间xmlns:tns tnsThis NameSpace的缩写,用来对当前WSDL进行引用。由于一个WSDL映射一个包(package),所以我们在Java平台下基于XFire开发的一个天气Web服务为我们生成的WSDL里,tns的值(http://weather.cactus.org)包含java包(org.cactus.weather)的信息就是顺理成章的了。请注意,tns1的值和<wsdl:types>里的<schema>元素的targetNamespace值是相同的。

名称空间xmlns:wsdlsoap是在与soap绑定时使用的,例如<wsdlsoap:binding><wsdlsoap:operation>等元素会用到。

名称空间xmlns:xsd是对XML Schema中各种数据类型的引用,例如stringboolean等等。想知道XML Schema中一共都定义了哪些数据类型,只要查看该名称空间的值(http://www.w3.org/2000/10/XMLSchema)即可。

在看具体的解析之前先看看WSDL4J的基本用法:

   首先通过我们系统获取企业所发布服务的WSDL文档.一旦获取了服务的WSDL文档,就可以采用WSDL4J对其进行解析.为了使用Web服务调用框架进行服务的调用,需要从WSDL文档中进行以下信息的收集:目标名称空间;服务名称;端口名称;操作名称;输入输出参数.

     WSDL文档的分类

      WSDL文档主要分为4种样式:文档/文字、文档/编码、RPC/文字、RPC/编码。基于文档和RPC样式的WSDL文档在数据类型定义方面主要存在如下区别:

基于文档样式的WSDL文档的每个Message所包含的Part部分指向一个Schema元素声明;

基于RPC样式的WSDL文档的每个Message所包含的Part部分指向了Schema类型的定义.

不同文档样式的数据类型定义区别如下所示:

<s:element name="getQuote">

<s:complexType>

<s:sequence>

<s:element minOccurs="0"maxOccurs="1"name="symbol"type="s:string"/>

</s:sequence>

</s:complexType>

</s:element>

基于文档的表示:

<message name="getQuoteSoapIn">

<part name="parameters" element="s0:getQuote"/>

</message>

基于RPC的表示:

<message name="getQuoteSoapIn">

<part name="symbol"type="s:string"/>

</message>

   用WSDL4J进行服务描述解析的基本流程

由于两种样式的WSDL文档在进行数据类型定义时不尽相同,因此采用WSDL4J在进行服务参数解析时也不相同,其中基于RPC样式的服务参数能够很好地被解析,而基于文档样式的服务参数需要采用其他如解析XML文档的方法去执行.文中应用采用基于RPC样式的服务描述文档,其中采用WSDL4J进行服务描述解析的代码框架见程序清单2:

1)获取服务的definitions根节点

WSDLFactory factory= WSDLFactory.newInstance();

WSDLReader reader=factory.newWSDLReader();

Def=reader.readWSDL(WsdlURL);

2)获取目标名称空间

targetNamespace=Def.getTargetNamespace();

3)获取服务端口

Vector allPorts=newVector();

Mapports= Def.getPortTypes();

PortType port= (PortType)allPorts.elementAt(0);

4)选择服务名称和端口名称

//首先选择和当前端口类型相同的绑定QName

QName bindingQName=null;

Mapbindings= Def.getBindings();

Binding binding= (Binding)bindings.get(it.next());

if(binding.getPortType()==port)

{bindingQName=binding.getQName();}

//然后通过端口绑定名称获取端口名称和服务名称

MapimplServices=implDef.getServices();

Serviceserv= (Service)implServices.get(it.next());

Mapm =serv.getPorts();

Portp= (Port)m.get(iter.next());

if(p.getBinding().getQName().toString().equals(bindingQName.toString())){

portName=serv.getQName().toString();

serviceName=p.getName();}

5)选择操作名称

Operationop= (Operation)operations.get(0);

operationName=op.getName();

6)保存输入参数名称和类型(输出参数类似)

Listparts=op.getInput().getMessage().getOrderedParts(null);

intcount=parts.size();

StringinNames=newString[count];

ClassinTypes=newClass[count];

for(inti=0;I<inNames.length;i++){

inNames[i]= (Part)parts.get(i).getName();

t = (Part)parts.get(i).getTypeName.

getLocalPart();

if("string".equals(t)

{inTypes[i]=String.class;}

}

对于我们项目具体的解析过程

下面用几个代码片段来解释整个过程:

清单1  Transformation.java

public com.swc.se.domain.Service buildserviceinformation(com.swc.se.domain.Service serviceinfo) throws Exception{

           WSDLReader reader 
= wsdlFactory.newWSDLReader();

        Definition def 
= reader.readWSDL(null, serviceinfo.getWsdllocation());

        wsdlTypes
=createSchemaFromTypes(def);

        Map services 
= def.getServices();

        
if(services != null){

               Iterator svcIter 
= services.values().iterator();

            populateComponent(serviceinfo, (Service)svcIter.next());

         }


        
return serviceinfo;

   }


 

清单1 主要的任务就是根据WSDL文档URL地址(serviceinfo参数只含有有限的信息,如IDWSDL文档的URL),构建一个com.swc.se.domain.Service类,用来描述WSDL文件中的Service元素,即用来装载从Service元素和其子元素中解析出来的数据。同时,com.swc.se.domain.Service类也对应一个远程服务。

第一步:读取WSDL文档的根构建一个