程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 提高J2EE技術與.NET之間的互操作性,第3部分

提高J2EE技術與.NET之間的互操作性,第3部分

編輯:關於JAVA

引言

正如 Java 包通常用來保證 Java 類,使其只能存在於不同層次的命名空間中,這樣就可避免類、方法等等之間的命名沖突,XML 命名空間也是為相同的目的而服務於 Web 服務。它限定 XML 元素或屬性的名字並幫助它們避免命名沖突。XML 命名空間是基於 URL 應當是全局唯一的基礎之上的。然而,解釋 URL 的方法及在本機代碼的映射對於不同的平台來說是不同的。通常這些不同之處是微妙的,但如果開始時不解決這些的差別,到後來有可能會很難解決。

我將在下面的部分當中討論幾個與命名空間有關的互操作性問題,包括:

使用相關的 URI 引用

使用共享通用域名的唯一的 URI

數組類型中的命名空間問題

在 WSDL 中用相關 URI 引用作為命名空間聲明

在命名空間聲明中並沒有嚴格禁止相關 URI 引用,但在規范中也沒有為它們提供解釋。如果 WSDL 文件是從 J2EE Web 服務中生成的,這通常不是一個問題,因為目標命名空間是從 Java 包名字派生而來,並且工具(例如,Java2WSDL)自動將它們與模式聯系起來。但是在 Microsoft .NET Web 服務實現當中,如果您允許 .NET 框架生成 WSDL 文件,那麼目標命名空間就會直接從您在代碼中的定義生成。您可能會經常看到命名空間屬性被分配到相關 URI 的情況。清單 1 顯示了從庫存中取得產品列表的 C# .NET Web 服務代碼。

清單 1. 有相關命名空間 URI 的庫存 Web 服務

[WebService(Namespace="services.inventory")]
  public class GetProductsService: WebService
  {
   public struct Product {
     public string name;
     public int   qty;
     public float price;
   }
   [WebMethod]
   [XmlInclude(typeof(Product))]
   public Product[] listProducts()
   {
     Product[] products =
     getInventory(); // getInventory() is a private method
to retrieve all products
     return products;
   }
  }

在清單 1 中,Namespace="services.inventory" 屬性在 WSDL 文件中的結果是 targetNamespace="services.inventory"。結果,所有在本地定義的元素、類型及屬性均被映射到命名空間的相關 URI services.inventory 之下。以下顯示了 WSDL 文檔的模式部分:

清單 2. 生成的 WSDL 文件顯示了作為 targetNamespace 的相關 URI 引用

xmlns:s0="services.inventory"
   <types>
     <s:schema elementFormDefault="qualified"
       targetNamespace="services.inventory"
xmlns:s="http://www.w3.org/2001/XMLSchema">
       <s:complexType name="ArrayOfProduct">
         <s:sequence>
           <s:element maxOccurs="unbounded" minOccurs="0"
             name="Product" type="s0:Product"/>
         </s:sequence>
       </s:complexType>
       <s:complexType name="Product">
         <s:sequence>
           <s:element maxOccurs="1" minOccurs="0" name="name"
type="s:string"/>
           <s:element maxOccurs="1" minOccurs="1" name="qty"
type="s:int"/>
           <s:element maxOccurs="1" minOccurs="1" name="price"
type="s:float"/>
         </s:sequence>
       </s:complexType>
       <s:element name="ArrayOfProduct" nillable="true"
type="s0:ArrayOfProduct"/>
     </s:schema>
   </types>

elementFormDefault="qualified" 屬性確保 targetNamespace 限定包括復雜類型 Product 在內的所有局部聲明元素。假設有另一個單位使用相同的相關命名空間實現類似的 Product 類型。就像當使用 IBM® WebSphere® Studio Application Developer Integration Edition(Application Developer)BPEL 設計器從不同的伙伴鏈接中利用普通復雜類型將 Web 服務集成到業務流程中時使用 wsdl:import 及 xsd:import 一樣,從普通模式中將這兩個模式導入到 WSDL 文檔中。

在本場景中,如果導入的兩個模式有相同的目標命名空間,則很有可能發生命名沖突。用於在另一個平台上構建集成的工具必須確保相關 URI 是基於 RFC2396 標准文檔樹中的基本 URI 的。然而,在 WSDL 文檔中基本 URI 並沒有定義完善,默認的基本 URI 的解釋取決於應用程序。最好的習慣是始終使用它自己的組織域名來確保命名空間唯一。

共享通用域名的唯一命名空間 URI

有人說命名空間污染是軟件工程中最糟糕的污染。每個組織都有不同的命名習慣,所以工具通常生成的 Web 服務存根代碼在另一個平台上的 WSDL 命名空間聲明中或許有不同的解釋。Web Services Interoperability Organization(WS-I)規范已經在消除命名空間聲明中的模糊點方面有了重大改進,並且正在向統一命名空間解釋方向前進,但其中仍有一些不足之處。

考慮兩個假設,小型銀行分部及大型銀行投資分部的 .NET Web 服務。一個為客戶創建校驗帳戶,另一個創建投資帳戶:

清單 3. .NET 中的零售 AccountService

namespace Retail
{
 [WebService(Namespace="http://bigbank.com/retail")]
 public class AccountService: WebService
 {
  public struct Customer {
    public string name;
    public string address;
  }
  [WebMethod]
  public bool createCheckingAccount(Customer customer)
  {
    return true;
  }
  }
}

清單 4. .NET 中的投資 AccountService

namespace Investment
{
  [WebService(Namespace="http://bigbank.com/investment")]
  public class AccountService: WebService
  {
   public struct Customer {
     public string name;
     public string address;
     public int collatoral_amt;
   }
   [WebMethod]
   public bool createInvestmentAccount(Customer customer)
   {
     return true;
   }
  }
}

上述兩個帳戶服務有相同的域,但有不同的分支: http://bigbank.com/retail 及 http://bigbank.com/investment。由於不同的需求,零售 AccountService 中有一個比投資 AccountService 中稍有不同的 Customer 復雜類型。兩個 AccountService 類文件都命名為 AccountService.asmx。在 .NET 中這不會有問題,而實際上在 .NET 中是完全順乎其理的,因為兩個帳戶服務都被限定為不同的 URL,他們都准確的反映出他們的域名及分支名。

現在,如果您准備在 Application Developer 中創建客戶端項目並試圖集成兩個 Web 服務,情況就要有所變化。在 Java 代碼中,當創建 Web 服務客戶端時,包名是基於命名空間的域名的。http://bigbank.com/retail 及 http://bigbank.com/investment 有相同的域名:http://bigbank.com。因此,生成的復合數據類型及兩個 Web 服務的代理將有相同的包名:com.bigbank。因為我們將兩個 .NET Web 服務命名為 AccountService.asmx,並且兩個不同的 Customer 結構類型有相同的名字,結果很明確:當 Application Developer 生成代理文件時,為 AccountService 客戶端生成的存根文件(AccountService.java、AccountServiceLocator.java、AccountServiceSoap.java、AccountServiceSoapProxy.java 及 AccountServiceSoapStub.java)將重寫先前生成的同名文件。根據後期生成的結果,現在只有一個 Customer 復合類型而不是兩個。

這個命名沖突是在 .NET 及 Java 技術中命名習慣的不同而導致的。正如您所看到的,命名空間聲明中的唯一 URL 仍不能完全避免命名沖突。解決方法是保證每個 Web 服務擁有唯一的域名。上面的兩個 AccountService 可以使用 http://retail.bigbank.com 及 http://investment.bigbank.com/ 分別作為命名空間限定符,從而使域名唯一。

如果沒有經驗改變現有 .NET Web 服務中的命名空間聲明的話,Application Developer 中的 Web 服務客戶端代理生成向導也會提供一個選項來定義由命名空間到包的自定義映射,如圖 1 所示。

圖 1. 在 Application Developer 中定義由命名空間到包的自定義映射

命名空間及共享 XSD 模式

在 J2EE 技術及 .NET 中,在眾多的 Web 服務中共享 XSD 模式是非常普遍的。實際上,共享 XML 模式是模塊化設計及可重用性考慮的最佳實踐。XML 標簽:import 及 include 的使用也正是由於此目的。例如,您可以為貨物倉庫的 Product 類型設計模式,如清單 5 中所示:

清單 5. Product 類型

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace=
"http://catalog.warehouse.com" xmlns:Product="http://catalog.warehouse.com">
   <complexType name="Product">
     <sequence>
       <element name="_name" type="string"></element>
       <element name="_int" type="int"></element>
     </sequence>
   </complexType>
</schema>

Product 類型被限定於命名空間 http://catalog.warehouse.com 之下。可以為其他 Web 服務將其導入到 WSDL 中管理庫存。假設訂貨部門有 C# 的 Order Web 服務實現,如清單 6 中所示:

清單 6. .NET 中的定購 Service

[WebService(Namespace="http://order.warehouse.com/service")]
   public class OrderProductService: System.Web.Services.WebService
   {
     [WebMethod]
     [XmlInclude(typeof(Product))]
     public string OrderProducts(Product[] products)
     {
       int len = products.Length;
       //do the order
       return "Total number of orders processed: " + len;
     }
   }

庫存部門使用庫存 Web 服務進行重新進貨,它必須重用相同的 Product 類型,如清單 7 中所示:

清單 7. .NET 中的庫存 Service

[WebService(Namespace="http://inventory.warehouse.com/service")]
   public class InventoryProductService: System.Web.Services.WebService
   {
     [WebMethod]
     [XmlInclude(typeof(Product))]
     public string RestockProducts(Product[] products)
     {
       int len = products.Length;
       //add to the inventory
       return "Total number of products added: " + len;
     }
   }

現在有三個命名空間:Product 類型的 http://catalog.warehouse.com、Order Web 服務的 http://order.warehouse.com/service 以及 Inventory Web 服務的 http://inventory.warehouse.com/service。乍一看,似乎沒有潛在的命名沖突。依據前兩部分:這三個 URI 有足夠的資格,每個 Web 服務的域名都是唯一的,甚至 Web 服務類名也是不同的。

但仍然會發生問題。當在清單 6 及清單 7 中的兩個 Web 方法中傳遞數組時會發生問題。

如果您創建 J2EE 項目集成定購服務及庫存服務,並定購一些產品或重新進貨某些產品,只有一個 Web 服務會正確執行。由於接收到產品的空數組,所以另一個服務會悄無聲息的失敗掉,即使 J2EE 客戶端確實為兩個服務都發送了填充了產品的數組也是如此。為什麼會這樣?

您應該研究 J2EE 客戶端與 .NET Web 服務之間的 SOAP 通信來尋找答案。

清單 8. .NET 定購服務的 SOAP 請求

<soapenv:Body>
   <OrderProduct xmlns="http://order.warehouse.com/service">
      <products>
         <Product>
           <_name xmlns="http://catalog.warehouse.com">Computer</_name>
           <_qty xmlns="http://catalog.warehouse.com">10</_qty>
         </Product>
         <Product>
           <_name xmlns="http://catalog.warehouse.com">Monitor</_name>
           <_qty xmlns="http://catalog.warehouse.com">20</_qty>
         </Product>
      </products>
   </OrderProduct>
</soapenv:Body>

清單 9. .NET 庫存服務的 SOAP 請求

<soapenv:Body>
   <RestockProduct xmlns="http://inventory.warehouse.com/service">
       <products>
         <Product xmlns="http://order.warehouse.com/service">
           <_name xmlns="http://catalog.warehouse.com">Computer</_name>
           <_qty xmlns="http://catalog.warehouse.com">10</_qty>
         </Product>
         <Product xmlns="http://order.warehouse.com/service">
           <_name xmlns="http://catalog.warehouse.com">Monitor</_name>
           <_qty xmlns="http://catalog.warehouse.com">20</_qty>
         </Product>
       </products>
   </RestockProduct>
</soapenv:Body>

比較清單 8 及清單 9 中的 orders 數組及 products 數組的 XML 表示。在 Inventory 服務請求中的數組元素由 Order 服務的命名空間 URI 所限定-http://order.warehouse.com/service,所以 Inventory Web 服務不會看到發送給他的任何產品。

那麼,問題的根源在哪裡呢?問題在於 Application Developer JAX-RPC 工具生成的 .NET WSDL 及幫助類。清單 10 顯示了與 ArrayOfProduct[] 有關的定購服務 WSDL:

清單 10. 與 ArrayOfProduct 類型有關的 .NET Order 服務 WSDL 的一部分

xmlns:s1="http://catalog.warehouse.com"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:s0="http://order.warehouse.com/service"
targetNamespace="http://order.warehouse.com/service"
.......
<types>
  <s:schema elementFormDefault="qualified" targetNamespace=
  "http://order.warehouse.com/service">
   <s:import namespace="http://catalog.warehouse.com" />
    .......
   <s:element name="Product" type="s1:Product" />
  </s:schema>
  <s:schema elementFormDefault="qualified" targetNamespace=
  "http://catalog.warehouse.com">
   <s:import namespace=
   "http://order.warehouse.com/service" />
    <s:complexType name="ArrayOfProduct">
     <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" ref="s0:Product" />
     </s:sequence>
    </s:complexType>

ArrayOfProduct 模式位於 targetNamespace http://catalog.warehouse.com 之中,但它的元素引用 s0:Product,其中 s0 是 http://order.warehouse.com/service。

回憶當創建 JAX-RPC 客戶端存根類時,命名空間 URI 如何映射到 Java 包名。在 com.warehouse.catalog 包下面創建了 ArrayOfProduct 類,幫助類 ArrayOfProduct_Helper 將它的 Product 字段綁定到命名空間 s0,即 http://order.warehouse.com/service 上(參見本文中的清單 11)。

這並不是好消息。Inventory 及 Order Web 服務的客戶端共享 com.warehouse.catalog 包下面的相同的 ArrayOfProduct 類,但是它的類字段卻綁定到特定的 Web 服務上。在本例中是 Order 服務。這正是在清單 7 中您可以看到的 SOAP 請求消息,問題的根源就在於此。

在本場景中,沒有任何一方違反規范。沒有理想的解決方法。首先您必須發現問題,您可以做兩件事情來解決這個命名空間綁定沖突的問題:

當創建第一個客戶端代理文件時,將 com.warehouse.catalog 重命名為另外一個包名。當生成第二個客戶端代理文件時,兩個文件將都有正確的命名空間綁定。

當生成客戶端代理文件時,使用如圖 1 所示相同的技術。為每個 Web 服務將 http://catalog.warehouse.com 映射到唯一的命名空間。

共享 XSD 模式是一個最佳實踐,但 Web 服務程序員必須注意諸如此類的潛在的命名空間問題,並且要知道當問題發生時如何去修正它。

結束語

在本文中討論了由 XML 命名空間沖突所導致的某些互操作性問題。然而,相互作用的 Web 服務之間的命名空間沖突沒有停止於此。仍有許多其他的情況,或是微妙或是稀少,但它會發生。當有大量的 Web 服務部署在大型的公司環境中時,很難修正命名空間沖突問題。當開發 Web 服務時,最好要預見並避免不同平台上潛在的沖突。IBM WebSphere Studio Application Developer Integration Edition 也提供了強大的重置工具。如果在集成期間真的有命名沖突發生,該工具可以幫助您進行重置。

本系列的技巧討論了大量的重要議題,可以解決跨平台 Web 服務互操作性並提供最佳實踐,特別是在 XML Schema 類型的使用、命名空間及 Web 服務接口綁定方面。在 WS-I 成員多年的共同努力下,Web 服務互操作性是可以實現的了。第三方 IDE 工具已經成熟,將能更好的集成。但認為有朝一日只是點擊幾下鼠標即可完成所有的 Web 服務集成也是不切合實際的,即使通過最成熟及強大的 IDE 工具的幫助也是如此。畢竟,Web 服務是在不同的平台上開發的。即使每個人都依據相同的 WS-I 規范開發,仍然會有不匹配、曲解及不同的習慣。在產品領域,仍有需要被工程師考慮的許多互操作性問題。某些重要的互操作性問題是:

錯誤處理: 預見不同的錯誤情況及可以發生且被通信方返回的錯誤類型很重要,並將那些 wsdl:faults 定義為 WSDL 中 wsdl:operations 的一部分。

安全互操作性: Web 服務調用需要簽名及加密(WS-Security)。為 Web 服務執行 vendor-neutral 及 transport-independent 安全措施是 WS-Security 的目標,它也是產品環境中 Web 服務集成的重要一步。

我將在以後的技巧中討論其他的互操作性問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved