三.平台跨的不容易
本来这部分内容应该作为很后面的内容,但是由于工作已经作了,也总结了,那么就先写下来贴一下,也算是个分享吧,这部分内容在网上找了很久都没有,所以也算是不错的一个实践。
ISV有几家接了上来,有用PHP的,有.net的,这时候ASF框架的WebService继功能测试,性能测试,安全性测试进入了一个新的测试阶段,兼容性测试。由于ISV的技术力量参差不齐,所以我们需要包办实现所有语言的客户端调用Demo的工作,因此对我这个做ASF的人来说,又要懂得各个语言的客户端调用以及配置,幸好还有一个ISV Support部门也做一些这样的工作,但是由于都是新手,也没有太多的指望。
WebService之所以能够被认为是SOA最行之有效的技术手段,主要还是因为其通过wsdl规范以xml作为数据和操作请求描述的载体,基于SOAP协议在http或者smtp上传输,实现业务逻辑交互与实现语言及平台的无关性,达到跨平台交互的效果。然而作为协议,往往来说是制定了规范性的框架,但是框架内的细节实现,不同的厂商,平台,开发语言,开源框架都会有不同的实现方式,因此也造成了WebService客户端解析Soap数据包兼容性的问题。这个问题在普通的接口中不容易出现,只是在调用接口返回数据类型为对象数组的时候出现。
首先出现在Java平台的两个比较通用的开源WebService框架上:axis2,xfire。(cxf暂时还没有去做测试)。现象:axis2和xfire的两种客户端都无法正常解析ASF返回的数组对象。例如返回的是Account对象,Account有id,name,value三个属性。模拟返回2个Account对象,结果axis2客户端获得一个数组,内部有一个Account对象,不过三个属性都是没有被初始化。xfire客户端获得一个数组,内部有两个Account对象,同样属性都没有被初始化。跟踪两个客户端源码并结合返回的Soap消息分析,得到了问题的原因。
SOAP返回的包体如下:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<_ns_:getUserAccountArr2Response xmlns:_ns_="http://webservice.asf.xplatform.alisoft.com">
<return xmlns="http://webservice.asf.xplatform.alisoft.com">
<Account xmlns=””>
<accountId>11</accountId>
<isDeleted>false</isDeleted>
<accountBalance>100.23</accountBalance>
</Account>
<Account xmlns=””>
<accountId>111</accountId>
<isDeleted>false</isDeleted>
<accountBalance>111.23</accountBalance>
</Account>
</return>
</_ns_:getUserAccountArr2Response>
</soapenv:Body>
</soapenv:Envelope>
先来解释Axis2的问题,Axis2客户端在解析此包体的时候,首先检查return标签,然后根据wsdl中的描述确认内部数组对象类型为Account,然后循环获取结果集构造对象,但是按照axis2的内部逻辑处理正常的情况,应该没有Account这层标签,直接是多个结构体组装而成,由于多了Account这层外围标签,导致解析第一个对象就出现问题,因此,就出现了上面描述的结果。此时有些怀疑是否是ASF框架在返回SOAP的时候没有遵循WSDL的规范,但是没有检验过xfire也不能确定是否是没有符合规范而造成的。
在来解释一下XFire客户端调用问题的原因。同样跟踪了XFire的客户端代码,发现问题主要是出在最后给对象获取属性值的操作上。首先XFire客户端启动时会根据本地的接口包或者对象包路径来反转成为namingspace然后和属性名称一起生成QName缓存在本地,作为属性对象。然后当获得了返回SOAP消息包体的时候,根据这些QName去获取属性的内容,但是可以从上面描述的SOAP返回的内容来看,Account的namingspace丢失了,导致后面各个属性的namingspace也都丢失了。看了一下ASF在返回SOAP的代码,的却在构造SOAP返回包的时候无法获得对象的namingspace,只有它的上级return类型有namingspace,那么如何解决呢,转念一想,其实这也是一种规范,wsdl的生成工具大部分都遵循这种包反转作为namingspace的策略,因此在构造返回包体的时候采取了这个策略来填充SOAP包,XFire客户端正常。(后话,万一遇到一些和我一样自己喜欢修改wsdl的人,那么xfire就未必能够正常解析这类服务了)。从这儿也验证了ASF对于WSDL的消息包返回规范是正确的,也就也证明了axis2客户端的一个缺陷,因此在java平台暂时不建议客户使用axis2,同时axis2的客户端友好度远远低于xfire,不过axis2的优势在于配置灵活以及可插入性(这也是ASF为什么集成axis2作为默认的webservice发布框架的原因,后续blog会回顾其他几个测试的历程)
这还是开始,由于都是开源框架,所以调试和检测相对来说还比较方便。接着测试部就提出在用.net客户端调用返回对象数组出现问题,问题和XFire最早一样。当时我就很肯定地就是应该问题出在解析那些属性上。说实话,第一次接触.net,什么都不会,装了个vs 2005就开始捣鼓,不过.net真是傻瓜工具,调用webservice相当简单,就只需要建立一个web reference,其中web reference就指向一个wsdl地址,那么.net就自动替你动态生成好client了,然后就像普通的对象调用一样,直接可以操作此服务(不过ASF的webservice的发布和引用也已经做的这么傻瓜了^_^)。简单是把双刃剑,容易上手,但是容易养成不求甚解的习惯,工作到现在,要不是开发框架,我根本不会去管wsdl中哪个元素是什么用处,工具生成好了,用就罢了,只要不出错。懒倒还是一方面,最痛苦的莫过于没有办法看到源码,只能黑盒测试以及猜测,这时候我觉得java真是好。还问了一个以前的高手朋友,他做了6,7年的java然后转到.net上,我说怎么跟踪.net的源码,他和我说:“据说.net快要开放源码了”。#_#|| 我回了一句:“我基本上等不到那天了。”言归正传,下面是如何分析.net问题的报告。
Java&.Net WebService兼容问题
Java发布的webservice 在.net客户端调用的时,数组对象类型返回兼容问题。
问题描述:
Java发布的WebService在Java客户端调用下都是正常的,但是在.net的客户端调用下,如果返回的类型是数组对象类型,那么就会发现得到了数组,并且数组内部对象生成,但是对象内部的属性值无法获得。
问题分析:
在wsdl中定义数组对象类型返回有两种方式:
1. <xs:complexType name="Account">
<xs:sequence>
<xs:element minOccurs="0" name="accountBalance" type="xs:double"/>
<xs:element minOccurs="0" name="accountId" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="isDeleted" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="getUserAccountArrResponse">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="return" nillable="true" type="xsd:Account"/>
</xs:sequence>
</xs:complexType>
</xs:element>
2. <xs:element name="getUserAccountArr2Response">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="return" nillable="true" type="xsd:ArrayOfAccount"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Account">
<xs:sequence>
<xs:element minOccurs="0" name="accountBalance" type="xs:double"/>
<xs:element minOccurs="0" name="accountId" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="isDeleted" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ArrayOfAccount">
<xs:complexContent>
<xs:restriction base="soapenc:Array">
<xs:attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:Account[]"></xs:attribute>
</xs:restriction>
</xs:complexContent>
</xs:complexType>
配置一的情况:
有两种场景出现:
场景一:
public interface IAccountService2
{
public Account checkUserAccount(String accountId);
public Account[] getUserAccountList(String accountIdBeg,String accountIdEnd);
public Account[] getUserAccountArr(String accountIdBeg);
public Account[] getUserAccountArr2(String accountIdBeg);
public double payForAppOrder(Account account,double fee);
public void delAccount(Account account,String name);
public int checkUser(String accountId,String accountId1);
}
其中接口中所有的返回或者参数对象都和接口定义在同一个包体内,这样生成wsdl的时候xsd的schema就只有一份,那么.net的客户端数组对象返回问题不存在。
场景二:
public interface IAccountService
{
public AccountBean checkUserAccount(String accountId) throws InvocationTargetException;
public AccountBean[] getUserAccountList(String accountIdBeg,String accountIdEnd);
public AccountBean[] getUserAccountArr(String accountIdBeg);
public Account[] getUserAccountArr2(String accountIdBeg);
public double payForAppOrder(AccountBean account,double fee);
public void delAccount(AccountBean account,String name);
public int checkUser(String accountId,String accountId1);
}
接口中的返回对象和接口不在一个包内,那么生成的xsd的schema就有多个,那么.net的客户端调用java发布的webservice就存在前面描述的问题。
因此用同样的wsdl分别用.net和java发布,通过.net客户端去调用,前者不存在问题,后者有问题,截获soap相应报文如下:
java 返回的soap包:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<_ns_:getUserAccountArr2Response xmlns:_ns_="http://webservice.asf.xplatform.alisoft.com">
<return xmlns="http://webservice.asf.xplatform.alisoft.com">
<Account>
<accountId>11</accountId>
<isDeleted>false</isDeleted>
<accountBalance>100.23</accountBalance>
</Account>
<Account>
<accountId>111</accountId>
<isDeleted>false</isDeleted>
<accountBalance>111.23</accountBalance>
</Account>
</return>
</_ns_:getUserAccountArr2Response>
</soapenv:Body>
</soapenv:Envelope>
.net返回的soap包:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<getUserAccountArr2Response xmlns="http://webservice.asf.xplatform.alisoft.com">
<return>
<accountBalance>12.12</accountBalance>
<accountId>11</accountId>
<isDeleted xsi:nil="true"/>
</return>
<return>
<accountBalance>12.12</accountBalance>
<accountId>11</accountId>
<isDeleted xsi:nil="true"/>
</return>
</getUserAccountArr2Response>
</soap:Body>
</soap:Envelope>
但就作为wsdl中定义的话,return只有一个内容就是Account数组,java的定义应该比较符合定义内容。
部分结论:
也就是说在第一种配置情况下,wsdl中包含一个xsd的schema,.net客户端不存在任何问题。wsdl中存在多个schema的情况下,数组对象无法构造成功,但是对于单个对象返回可以正常解析。
解决方案:
1.修改服务框架服务端代码适应.net客户端(不可行,会导致java的出现问题)
2.将这种特殊接口的schema中定义的类都放在一个包里(觉得不是很合适)
3.把对象都序列化然后作为结果返回,个人感觉性能比较低,不过可以真的减小跨平台的问题。
配置二的情况:
不存在客户端调用的构造问题,不过需要改造客户端代码(其实就是获得了xml的数据片断,自己去解析xml的数据来构造客户端对象)。此类方法在网上也很通用,可以参看
www.salesforce.com提供给第三方的API接口介绍,就是类似的。
C# Example
private void querySample()
{
QueryResult qr = null;
binding.QueryOptionsValue = new sforce.QueryOptions();
binding.QueryOptionsValue.batchSize = 250;
binding.QueryOptionsValue.batchSizeSpecified = true;
qr = binding.query("select FirstName, LastName from Contact");
bool bContinue = true;
int loopCounter = 0;
while (bContinue)
{
Console.WriteLine("\nResults Set " + Convert.ToString(loopCounter++) + " - ");
//process the query results
for (int i=0;i<qr.records.Length;i++)
{
sforce.sObject con = qr.records[i];
string fName = con.Any[0].InnerText;
string lName = con.Any[1].InnerText;
if (fName == null)
Console.WriteLine("Contact " + (i + 1) + ": " + lName);
else
Console.WriteLine("Contact " + (i + 1) + ": " + fName + " " + lName);
}
//handle the loop + 1 problem by checking to see if the most recent queryResult
if (qr.done)
bContinue = false;
else
qr = binding.queryMore(qr.queryLocator);
}
Console.WriteLine("\nQuery succesfully executed.");
Console.Write("\nHit return to continue...");
Console.ReadLine();
}
}
此时,我们的客户端代码修改成为:
原来的代码:
jdk2Service.AccountService service5 = new jdk2Service.AccountService();
jdk2Service.Account[] re = service5.getUserAccountArr("demo");
jdk2Service.Account re2 = service5.checkUserAccount("test");
现在的代码:
jdkService.AccountService service3 = new jdkService.AccountService();
jdkService.ArrayOfAccountBean res = service3.getUserAccountArr("tea");
string name = res.Any[0].FirstChild.InnerText;//获取了第一个返回对象的第一个属性值。
这种模式比较通用在现在的跨平台的客户端调用webservice。
因此考虑AEP接口改造成为这种方式,同时可以给客户封装类似的构造函数库提供给客户使用。
结束语:
这个报告发给了我们的架构师们以及相关人员,晚上下班到家,收到了老大的邮件,让我们总架构师向微软提出这个问题,看是否真的是这样的情况,能否有好的方法解决。这让我想起了前一阵子谁说的一句话:“有多少人打过微软的客户服务电话反映过情况”。赫赫,我们这就算是反映了,效果么……,觉得求人不如求己,开源好啊^_^
分享到:
相关推荐
SDAP-Asca的操作平台为全分布式架构,包括多个核心服务,如SDAP-WEB-front(纯静态操作界面)、SDAP-WEB(统一登录及权限服务)、SDAP-CM(分布式检测对象接受服务)、SDAP-Schedule(职能调度器)等,确保了高效的...
标题中的“全部的SCA&SDO中文规范”指的是Service Component Architecture (SCA) 和 Service Data Objects (SDO) 的中文版本规范集合。这些技术是IBM提出的用于构建面向服务架构(SOA)应用的关键组件。 1. **...
总结而言,文件中提到的分布式SCA应用的设计与实现,是基于Tuscany SCA框架对SCA规范的具体应用和创新。该分布式模型展现了分布式系统在云端部署、易于实现、逻辑整合和实际应用中的特点和优势。通过这些详细的理论...
在"apache-tuscany-sca-1.6.zip"这个压缩包中,包含的是Apache Tuscany SCA 1.6版本的相关文件。这个版本可能包括了以下关键组件和资源: 1. **SCA模型**:SCA的核心是它的模型,它定义了服务、组件、接口、绑定和...
Fortify-SCA是惠普公司开发的一款先进的静态源代码分析工具,广泛应用于企业级的应用程序源代码安全漏洞检测。本指南旨在为用户提供Fortify-SCA的使用方法和命令解析,帮助用户理解和掌握如何使用Fortify进行高效的...
**服务组件架构(Service Component Architecture,简称SCA)** 是一种SOA的具体实现规范,由OASIS组织提出,旨在简化服务的创建、部署和管理。SCA提供了一种模型,用于定义服务、服务接口、服务绑定和服务组合,...
总之,SOA-SCA-Assemblymodel涉及的是在SOA架构下,如何使用SCA规范来构建和组装服务组件,以实现灵活、可扩展和可重用的应用程序。这一模型不仅简化了服务组件的开发和集成过程,还促进了不同平台和语言之间的互...
- **SCA(Service Component Architecture)**:服务组件架构是SOA的具体实现方式之一,它为开发基于SOA的应用程序提供了一套完整的规范。SCA不仅简化了开发过程,还将底层技术细节与业务逻辑分离,使开发者能更专注...
在学习和应用SCA规范时,应重点理解每个概念的含义及其相互关系,以及如何在实际项目中实现组件化和服务化的架构设计。 此外,开发者还需要关注SCA的实现框架,比如Apache Tuscany,这是一个开源的SCA实现,提供了...
在"SCA核心框架CF"中,我们主要关注的是SCA2.2.2版本的实现,这是一个基于C++的实现,旨在为开发者提供一种高效、可靠的平台来构建SOA(Service-Oriented Architecture,面向服务的架构)应用。 在"framework-core-...
6. **OSGi**:OSGi是一种Java模块化系统,它为Java应用程序提供了动态的模块化框架,Tuscany SCA的实现基于OSGi规范,这使得它能够灵活地管理和部署服务。 7. **分布式系统**:SCA常用于构建分布式系统,其中的服务...
**SCA(Service Component Architecture)**,即服务构件架构,是一种用于构建基于服务的应用程序和服务的技术规范。SCA旨在提供一个统一的方法来组合服务,使开发人员能够更加关注业务逻辑而非底层技术细节。这种...
- **Interface**:指定了服务接口的规范,可能基于Java、WSDL或其他格式。 #### 结论 SCA装配规范是构建SOA应用的关键,它通过SCDL文档提供了组件的定义、实现和组装的指导原则。Tuscany作为SCA的JAVA实现,不仅...
在“apache-tuscany-sca-1.6.2”这个压缩包中,我们能找到Apache Tuscany的特定版本——1.6.2。这个版本包含了项目的所有源代码,这对于开发者来说是极其宝贵的资源,因为源码可以提供深入理解其内部工作原理的机会...