放翁(文初)的一畝三分地

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            210 隨筆 :: 1 文章 :: 320 評(píng)論 :: 0 Trackbacks
           

          三.平臺(tái)跨的不容易

                 本來(lái)這部分內(nèi)容應(yīng)該作為很后面的內(nèi)容,但是由于工作已經(jīng)作了,也總結(jié)了,那么就先寫下來(lái)貼一下,也算是個(gè)分享吧,這部分內(nèi)容在網(wǎng)上找了很久都沒有,所以也算是不錯(cuò)的一個(gè)實(shí)踐。

                 ISV有幾家接了上來(lái),有用PHP的,有.net的,這時(shí)候ASF框架的WebService繼功能測(cè)試,性能測(cè)試,安全性測(cè)試進(jìn)入了一個(gè)新的測(cè)試階段,兼容性測(cè)試。由于ISV的技術(shù)力量參差不齊,所以我們需要包辦實(shí)現(xiàn)所有語(yǔ)言的客戶端調(diào)用Demo的工作,因此對(duì)我這個(gè)做ASF的人來(lái)說(shuō),又要懂得各個(gè)語(yǔ)言的客戶端調(diào)用以及配置,幸好還有一個(gè)ISV Support部門也做一些這樣的工作,但是由于都是新手,也沒有太多的指望。

                 WebService之所以能夠被認(rèn)為是SOA最行之有效的技術(shù)手段,主要還是因?yàn)槠渫ㄟ^(guò)wsdl規(guī)范以xml作為數(shù)據(jù)和操作請(qǐng)求描述的載體,基于SOAP協(xié)議在http或者smtp上傳輸,實(shí)現(xiàn)業(yè)務(wù)邏輯交互與實(shí)現(xiàn)語(yǔ)言及平臺(tái)的無(wú)關(guān)性,達(dá)到跨平臺(tái)交互的效果。然而作為協(xié)議,往往來(lái)說(shuō)是制定了規(guī)范性的框架,但是框架內(nèi)的細(xì)節(jié)實(shí)現(xiàn),不同的廠商,平臺(tái),開發(fā)語(yǔ)言,開源框架都會(huì)有不同的實(shí)現(xiàn)方式,因此也造成了WebService客戶端解析Soap數(shù)據(jù)包兼容性的問(wèn)題。這個(gè)問(wèn)題在普通的接口中不容易出現(xiàn),只是在調(diào)用接口返回?cái)?shù)據(jù)類型為對(duì)象數(shù)組的時(shí)候出現(xiàn)。

          首先出現(xiàn)在Java平臺(tái)的兩個(gè)比較通用的開源WebService框架上:axis2,xfire。(cxf暫時(shí)還沒有去做測(cè)試)。現(xiàn)象:axis2xfire的兩種客戶端都無(wú)法正常解析ASF返回的數(shù)組對(duì)象。例如返回的是Account對(duì)象,Accountidnamevalue三個(gè)屬性。模擬返回2個(gè)Account對(duì)象,結(jié)果axis2客戶端獲得一個(gè)數(shù)組,內(nèi)部有一個(gè)Account對(duì)象,不過(guò)三個(gè)屬性都是沒有被初始化。xfire客戶端獲得一個(gè)數(shù)組,內(nèi)部有兩個(gè)Account對(duì)象,同樣屬性都沒有被初始化。跟蹤兩個(gè)客戶端源碼并結(jié)合返回的Soap消息分析,得到了問(wèn)題的原因。

                 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>

          先來(lái)解釋Axis2的問(wèn)題,Axis2客戶端在解析此包體的時(shí)候,首先檢查return標(biāo)簽,然后根據(jù)wsdl中的描述確認(rèn)內(nèi)部數(shù)組對(duì)象類型為Account,然后循環(huán)獲取結(jié)果集構(gòu)造對(duì)象,但是按照axis2的內(nèi)部邏輯處理正常的情況,應(yīng)該沒有Account這層標(biāo)簽,直接是多個(gè)結(jié)構(gòu)體組裝而成,由于多了Account這層外圍標(biāo)簽,導(dǎo)致解析第一個(gè)對(duì)象就出現(xiàn)問(wèn)題,因此,就出現(xiàn)了上面描述的結(jié)果。此時(shí)有些懷疑是否是ASF框架在返回SOAP的時(shí)候沒有遵循WSDL的規(guī)范,但是沒有檢驗(yàn)過(guò)xfire也不能確定是否是沒有符合規(guī)范而造成的。

          在來(lái)解釋一下XFire客戶端調(diào)用問(wèn)題的原因。同樣跟蹤了XFire的客戶端代碼,發(fā)現(xiàn)問(wèn)題主要是出在最后給對(duì)象獲取屬性值的操作上。首先XFire客戶端啟動(dòng)時(shí)會(huì)根據(jù)本地的接口包或者對(duì)象包路徑來(lái)反轉(zhuǎn)成為namingspace然后和屬性名稱一起生成QName緩存在本地,作為屬性對(duì)象。然后當(dāng)獲得了返回SOAP消息包體的時(shí)候,根據(jù)這些QName去獲取屬性的內(nèi)容,但是可以從上面描述的SOAP返回的內(nèi)容來(lái)看,Accountnamingspace丟失了,導(dǎo)致后面各個(gè)屬性的namingspace也都丟失了。看了一下ASF在返回SOAP的代碼,的卻在構(gòu)造SOAP返回包的時(shí)候無(wú)法獲得對(duì)象的namingspace,只有它的上級(jí)return類型有namingspace,那么如何解決呢,轉(zhuǎn)念一想,其實(shí)這也是一種規(guī)范,wsdl的生成工具大部分都遵循這種包反轉(zhuǎn)作為namingspace的策略,因此在構(gòu)造返回包體的時(shí)候采取了這個(gè)策略來(lái)填充SOAP包,XFire客戶端正常。(后話,萬(wàn)一遇到一些和我一樣自己喜歡修改wsdl的人,那么xfire就未必能夠正常解析這類服務(wù)了)。從這兒也驗(yàn)證了ASF對(duì)于WSDL的消息包返回規(guī)范是正確的,也就也證明了axis2客戶端的一個(gè)缺陷,因此在java平臺(tái)暫時(shí)不建議客戶使用axis2,同時(shí)axis2的客戶端友好度遠(yuǎn)遠(yuǎn)低于xfire,不過(guò)axis2的優(yōu)勢(shì)在于配置靈活以及可插入性(這也是ASF為什么集成axis2作為默認(rèn)的webservice發(fā)布框架的原因,后續(xù)blog會(huì)回顧其他幾個(gè)測(cè)試的歷程)

          這還是開始,由于都是開源框架,所以調(diào)試和檢測(cè)相對(duì)來(lái)說(shuō)還比較方便。接著測(cè)試部就提出在用.net客戶端調(diào)用返回對(duì)象數(shù)組出現(xiàn)問(wèn)題,問(wèn)題和XFire最早一樣。當(dāng)時(shí)我就很肯定地就是應(yīng)該問(wèn)題出在解析那些屬性上。說(shuō)實(shí)話,第一次接觸.net,什么都不會(huì),裝了個(gè)vs 2005就開始搗鼓,不過(guò).net真是傻瓜工具,調(diào)用webservice相當(dāng)簡(jiǎn)單,就只需要建立一個(gè)web reference,其中web reference就指向一個(gè)wsdl地址,那么.net就自動(dòng)替你動(dòng)態(tài)生成好client了,然后就像普通的對(duì)象調(diào)用一樣,直接可以操作此服務(wù)(不過(guò)ASFwebservice的發(fā)布和引用也已經(jīng)做的這么傻瓜了^_^)。簡(jiǎn)單是把雙刃劍,容易上手,但是容易養(yǎng)成不求甚解的習(xí)慣,工作到現(xiàn)在,要不是開發(fā)框架,我根本不會(huì)去管wsdl中哪個(gè)元素是什么用處,工具生成好了,用就罷了,只要不出錯(cuò)。懶倒還是一方面,最痛苦的莫過(guò)于沒有辦法看到源碼,只能黑盒測(cè)試以及猜測(cè),這時(shí)候我覺得java真是好。還問(wèn)了一個(gè)以前的高手朋友,他做了67年的java然后轉(zhuǎn)到.net上,我說(shuō)怎么跟蹤.net的源碼,他和我說(shuō):“據(jù)說(shuō).net快要開放源碼了”。#_#|| 我回了一句:“我基本上等不到那天了。”言歸正傳,下面是如何分析.net問(wèn)題的報(bào)告。

          Java&.Net WebService兼容問(wèn)題

          Java發(fā)布的webservice .net客戶端調(diào)用的時(shí),數(shù)組對(duì)象類型返回兼容問(wèn)題。

          問(wèn)題描述:

          Java發(fā)布的WebServiceJava客戶端調(diào)用下都是正常的,但是在.net的客戶端調(diào)用下,如果返回的類型是數(shù)組對(duì)象類型,那么就會(huì)發(fā)現(xiàn)得到了數(shù)組,并且數(shù)組內(nèi)部對(duì)象生成,但是對(duì)象內(nèi)部的屬性值無(wú)法獲得。

          問(wèn)題分析:

          wsdl中定義數(shù)組對(duì)象類型返回有兩種方式:

          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>   

          配置一的情況:

          有兩種場(chǎng)景出現(xiàn):

          場(chǎng)景一:

          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);

          }

          其中接口中所有的返回或者參數(shù)對(duì)象都和接口定義在同一個(gè)包體內(nèi),這樣生成wsdl的時(shí)候xsdschema就只有一份,那么.net的客戶端數(shù)組對(duì)象返回問(wèn)題不存在。

          場(chǎng)景二:

          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);

          }

          接口中的返回對(duì)象和接口不在一個(gè)包內(nèi),那么生成的xsdschema就有多個(gè),那么.net的客戶端調(diào)用java發(fā)布的webservice就存在前面描述的問(wèn)題。

          因此用同樣的wsdl分別用.netjava發(fā)布,通過(guò).net客戶端去調(diào)用,前者不存在問(wèn)題,后者有問(wèn)題,截獲soap相應(yīng)報(bào)文如下:

          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只有一個(gè)內(nèi)容就是Account數(shù)組,java的定義應(yīng)該比較符合定義內(nèi)容。

          部分結(jié)論:

          也就是說(shuō)在第一種配置情況下,wsdl中包含一個(gè)xsdschema.net客戶端不存在任何問(wèn)題。wsdl中存在多個(gè)schema的情況下,數(shù)組對(duì)象無(wú)法構(gòu)造成功,但是對(duì)于單個(gè)對(duì)象返回可以正常解析

          解決方案:

          1.修改服務(wù)框架服務(wù)端代碼適應(yīng).net客戶端(不可行,會(huì)導(dǎo)致java的出現(xiàn)問(wèn)題)

          2.將這種特殊接口的schema中定義的類都放在一個(gè)包里(覺得不是很合適)

          3.把對(duì)象都序列化然后作為結(jié)果返回,個(gè)人感覺性能比較低,不過(guò)可以真的減小跨平臺(tái)的問(wèn)題。

          配置二的情況:

                 不存在客戶端調(diào)用的構(gòu)造問(wèn)題,不過(guò)需要改造客戶端代碼(其實(shí)就是獲得了xml的數(shù)據(jù)片斷,自己去解析xml的數(shù)據(jù)來(lái)構(gòu)造客戶端對(duì)象)。此類方法在網(wǎng)上也很通用,可以參看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();

           } 

          }

          此時(shí),我們的客戶端代碼修改成為:

          原來(lái)的代碼:

          jdk2Service.AccountService service5 = new jdk2Service.AccountService();

          jdk2Service.Account[] re = service5.getUserAccountArr("demo");

          jdk2Service.Account re2 = service5.checkUserAccount("test");

          現(xiàn)在的代碼:

          jdkService.AccountService service3 = new jdkService.AccountService();

          jdkService.ArrayOfAccountBean res = service3.getUserAccountArr("tea");

          string name = res.Any[0].FirstChild.InnerText;//獲取了第一個(gè)返回對(duì)象的第一個(gè)屬性值。

          這種模式比較通用在現(xiàn)在的跨平臺(tái)的客戶端調(diào)用webservice

          因此考慮AEP接口改造成為這種方式,同時(shí)可以給客戶封裝類似的構(gòu)造函數(shù)庫(kù)提供給客戶使用。

          結(jié)束語(yǔ):

                 這個(gè)報(bào)告發(fā)給了我們的架構(gòu)師們以及相關(guān)人員,晚上下班到家,收到了老大的郵件,讓我們總架構(gòu)師向微軟提出這個(gè)問(wèn)題,看是否真的是這樣的情況,能否有好的方法解決。這讓我想起了前一陣子誰(shuí)說(shuō)的一句話:“有多少人打過(guò)微軟的客戶服務(wù)電話反映過(guò)情況”。赫赫,我們這就算是反映了,效果么……,覺得求人不如求己,開源好啊^_^

          更多的內(nèi)容請(qǐng)?jiān)L問(wèn)我的bloghttp://blog.csdn.net/cenwenchu79

          posted on 2007-11-21 22:16 岑文初 閱讀(1723) 評(píng)論(2)  編輯  收藏

          評(píng)論

          # re: 在路上---基于SCA規(guī)范的應(yīng)用服務(wù)框架成長(zhǎng)記(三)(連載中...) 2008-05-06 17:26 hi
          請(qǐng)教關(guān)于tuscany中spring實(shí)現(xiàn)component的問(wèn)題,
          我現(xiàn)在遇到的問(wèn)題是:我們的系統(tǒng)中關(guān)于連接池,事務(wù)管理器的spring配置文件是由核心模塊提供的,業(yè)務(wù)模塊可以直接引用核心模塊提供的bean。這樣就造成了spring的application context分布在多個(gè)配置文件中,而tuscany只能指向一個(gè)單獨(dú)的spring配置文件來(lái)構(gòu)造application的context,不知道您是如何解決這個(gè)問(wèn)題的,謝謝!  回復(fù)  更多評(píng)論
            

          # re: 在路上---基于SCA規(guī)范的應(yīng)用服務(wù)框架成長(zhǎng)記(三)(連載中...) 2008-05-06 17:44 岑文初
          改造Spring的組件,支持import  回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 福泉市| 晋江市| 聊城市| 深州市| 庆云县| 阿拉尔市| 荆门市| 大方县| 江源县| 枣阳市| 阿克陶县| 南陵县| 阜新| 宕昌县| 柏乡县| 吉林省| 长沙县| 顺平县| 天气| 永宁县| 大兴区| 扎赉特旗| 达日县| 长沙市| 鹰潭市| 友谊县| 集贤县| 尖扎县| 阜康市| 晋城| 西和县| 岳西县| 彭阳县| 襄城县| 新巴尔虎左旗| 合阳县| 宜昌市| 沙湾县| 湖口县| 防城港市| 霍邱县|