程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 利用Eclipse Modeling Framework加強JAX-RPC類型映射的功能

利用Eclipse Modeling Framework加強JAX-RPC類型映射的功能

編輯:關於JAVA

引言

JAX-RPC,也稱為 JSR-101,是完成標准編程模型的一個重要步驟,該標准編程模型簡化了 Java™ 平台上可互操作的 Web 服務的構建。由 XML 向 Java 類型映射模型的轉換是 JAX-RPC 的關鍵,該轉換是 Web 服務產品提供者的一個實現標准。沒有這樣的模型,Web 服務產品提供者會陷入定義專用類型映射的陷阱中,從而嚴重影響 Java 的互操作性問題。

雖然 JAX-RPC 在支持 XML 數據類型方面做了大量的工作,但是還有很多地方需要改進。而且,JAX-RPC 需要將任何不被支持的 XML 數據類型映射到 javax.xml.soap.SOAPElement 接口。javax.xml.soap.SOAPElement 接口沒有為用戶提供強類型的 Java 模型,也就是說用戶必須編寫自定義代碼,然後通過 SOAPElement 實例來解析。這對初學者來說比較難,特別是當處理大的 XML 片段的時候。本文演示了如何使用 EMF 來支持沒有標准 JAX-RPC 類型映射的 XML 數據類型。使用不支持 XML 數據類型的 JAX-RPC 生成 Web 服務並非易事,但是本文把 Web 服務工具和 IBM® WebSphere® Studio Application 以及 Site Developer V5.1.2 (Application and Site Developer) 中的 EMF 工具結合起來使用,提供了一個有效的解決方案。

創建供應鏈 Web 服務

要實現本文所介紹的方法,必須安裝 WebSphere Application 和 Site Developer V5.1.2。如果需要的話,可以下載一個 60 天的試用版。

創建一個 Web 項目。單擊菜單File>New>Project...>Web > Dynamic Web Project>Next,打開 New Dynamic Web Project wizard。

輸入SupplyChainWeb作為 Web 項目的名稱,選中Configure advance options復選框,然後單擊Next。

輸入SupplyChainEAR作為 EAR 項目的名稱,然後單擊Finish。

單擊本文頂部的Code圖標,下載 SupplyChainService.wsdl 和 SupplyChainSchema.xsd 到本地文件系統中。

將 SupplyChainService.wsdl 和 SupplyChainSchema.xsd 導入或復制到 SupplyChainWeb 項目的根目錄下。

在 navigator 視圖中,右鍵單擊SupplyChainService.wsdl>Web Services>Generate Java bean skeleton打開圖 1所示的 WSDL to Java Bean Skeleton wizard。該向導生成一個基於 WSDL 文檔中定義的信息的 Java 架構代碼實現。接受所有的默認設置,然後單擊Finish。

圖 1.WSDL to Java Bean Skeleton wizard

向導完成之後,您會在 tasks 視圖中看見一些 WSDL 驗證錯誤,這是由於 XML 模式文件 (SupplyChainSchema.xsd) 沒有被復制到正確的地方。要更正這些錯誤,將 SupplyChainSchema.xsd 從 SupplyChainWeb 項目的根目錄下復制到 /SupplyChainWeb/WebContent/WEB-INF/wsdl/ 和 /SupplyChainWeb/WebContent/wsdl/com/example/supplychain/www/ 這兩個目錄中。右鍵單擊SupplyChainService.wsdl>Run validation,再次運行驗證。

創建供應鏈 EMF 模型

WSDL to Java Bean Skeleton wizard 生成帶一個或多個映射到 SOAPElement (具體的,PurchaseOrderType.java、PurchaseReferenceType.java 以及 ShippingNoticeType.java)屬性的 JavaBean。在本部分中,將生成一個供應鏈 Web 服務的 EMF 模型來支持映射到 SOAPElement 的 XML 數據類型。

創建一個 EMF 項目。單擊菜單File>New>Project...>Eclipse Modeling Framework>EMF Project>Next,打開 New EMF Project wizard。

輸入SupplyChainEMF作為項目的名稱,然後單擊Next。

選擇Load from an XML schema,然後單擊Next。

單擊Browse Workspace...打開文件選擇對話框。查找並選擇SupplyChainSchema.xsd,然後單擊OK。單擊Next。

選擇supplychain包,然後單擊Finish。參閱圖 2。

圖 2.New EMF Project wizard

New EMF Project wizard 完成後,系統將打開 EMF Generator Editor。在這個編輯器中,右鍵單擊SupplyChainSchema節點,選擇Generate Model Code。您已經成功生成了供應鏈 EMF 模型。在下一部分中,將學習如何將 EMF 代碼集成到供應鏈 Web 服務中。

集成供應鏈 Web 服務與 EMF 模型

為 SupplyChainWeb 項目設置所有的依賴關系。將 SupplyChainEMF 項目添加到 SupplyChainEAR 作為一個實用的 JAR 文件,並指定從 SupplyChainWeb 項目到該實用 JAR 文件的 JAR 文件依賴性。

在應用程序部署描述編輯器 (Application Deployment Descriptor Editor) 中打開 /SupplyChainEAR/META-INF/application.xml。單擊Module選項卡。

在 Project Utility JARs 欄中,單擊Add...,選擇SupplyChainEMF,然後單擊Finish。保存並關閉應用程序部署描述編輯器。

在 JAR Dependency Editor 中打開 /SupplyChainWeb/WebContent/META-INF/MANIFEST.MF。在 Dependencies 欄中選擇SupplyChainEMF.jar。保存並關閉應用程序部署描述編輯器。

將 EMF 庫添加到 SupplyChainWeb 項目的 Java 構建路徑中。右鍵單擊SupplyChainWebproject >Properties>Java Build Path。單擊Libraries選項卡,選擇Add Variable...。

選擇EMF_COMMON、EMF_ECORE 以及 EMF_ECORE_XMI。單擊OK>OK。

清單 1顯示了用到的所有導入語句。在 Java 編輯器中打開 /SupplyChainWeb/JavaSource/com/example/supplychain/www/SupplyChainBindingImpl.java 並添加這些導入語句。

清單 1.導入語句

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Random;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import com.example.supplychain.ItemType;
import com.example.supplychain.PaymentMethodType;
import com.example.supplychain.ProcessingType;
import com.example.supplychain.ShippingItemType;
import com.example.supplychain.StatusType;
import com.example.supplychain.SupplychainFactory;
import com.example.supplychain.SupplychainPackage;
import com.example.supplychain.impl.SupplychainPackageImpl;
import com.example.supplychain.util.SupplychainResourceFactoryImpl;

使用生成的供應鏈 EMF 模型之前必須先初始化。初始化過程在 XML 模式 (SupplyChainSchema.xsd) 中聲明的元素和 EMF 代碼生成器創建的 Java 類之間建立了一個映射。該映射用於 XML 片段與相應的基於 EMF 的 Java 類之間的相互轉換。要初始化供應鏈 EMF 模型,將下面的靜態代碼塊添加到 SupplyChainBindingImpl.java 中。

清單 2.初始化 EMF 包

static
{
 SupplychainPackageImpl.init();
}

接下來,在 SupplyChainBindingImpl.java 中添加 4 個方法,這些方法將 SOAPElement 轉換為 DOMElement,然後再轉換為相應的基於 EMF 的 Java 類,也可以反過來轉換。清單3、4、5以及6 顯示了這些方法。soapElement2DOMElement(SOAPElement soapElement) 方法和 domElement2SOAPElement(Element e) 方法利用兩個特定於應用程序和站點開發者實現的方法: getAsDOM() 和 setAlternateContent(e) ,來負責 SOAPElement 到 DOMElement 的轉換。要從特定於提供商的代碼中清除這些方法,可以手動的遍歷 SOAPElement 並構造相應的 DOMElement。

在本文中,可以使用現成的方法,也就是說,可以使用應用程序和站點開發者實現提供的方法。事實上,如果 SOAP 附帶了 Java V1.2 (SAAJ)- 兼容實現的附加 API 函數,那麼就不再需要將 SOAPElement 轉換為 DOMElement,這是因為 SAAJ V1.2 需要 SOAPElement 以直接擴展 DOMElement。

清單 3.將 SOAPElement 轉換為 DOMElement

public Element soapElement2DOMElement(SOAPElement soapElement)
throws Exception
{
 return ((com.ibm.ws.webservices.engine.xmlsoap.SOAPElement)soapElement).getAsDOM();
}

清單 4.將 DOMElement 轉換為 EMF 對象

public EObject domElement2EObject(Element e)
throws TransformerConfigurationException, TransformerException, IOException
{
 DOMSource domSource = new DOMSource(e);
 Transformer serializer = TransformerFactory.newInstance().newTransformer();
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 serializer.transform(domSource, new StreamResult(baos));
 byte[] b = baos.toByteArray();
 System.out.println(new String(b));
 URI uri = URI.createURI(SupplychainPackage.eNS_URI);
 SupplychainResourceFactoryImpl factory = new SupplychainResourceFactoryImpl();
 Resource res = factory.createResource(uri);
 ByteArrayInputStream bais = new ByteArrayInputStream(b);
 res.load(bais, null);
 List contents = res.getContents();
 return (!contents.isEmpty()) ? (EObject)contents.get(0) : null;
}

清單 5.將 EMF 對象轉換為 DOMElement

public Element eObject2DOMElement(EObject eObject)
throws IOException, ParserConfigurationException, SAXException
{
 URI uri = URI.createURI(SupplychainPackage.eNS_URI);
 SupplychainResourceFactoryImpl factory = new SupplychainResourceFactoryImpl();
 Resource res = factory.createResource(uri);
 res.getContents().add(eObject);
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 res.save(baos, null);
 byte[] b = baos.toByteArray();
 System.out.println(new String(b));
 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
 DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
 Document doc = docBuilder.parse(new ByteArrayInputStream(b));
 return doc.getDocumentElement();
}

清單 6.將 DOMElement 轉換為 SOAPElement

public SOAPElement domElement2SOAPElement(Element e)
throws SOAPException
{
 SOAPFactory soapFactory = SOAPFactory.newInstance();
 com.ibm.ws.webservices.engine.xmlsoap.SOAPElement soapElement =
  (com.ibm.ws.webservices.engine.xmlsoap.SOAPElement)soapFactory.createElement(
  "temp");
 soapElement.setAlternateContent(e);
 return soapElement;
}

全局元素和局部元素

正如前面所提到的,供應鏈 EMF 模型依靠映射到 Java 的元素將 XML 片段轉換為相應的基於 EMF 的 Java 類。但是,默認的情況是,EMF 代碼生成器只為全局元素生成映射條目,而不為局部元素生成。全局元素是 XML 模式文檔中作為模式元素的子元素來聲明的元素,而局部元素卻不是。默認的映射清單不包括局部元素,因此,供應鏈 EMF 模型不能轉換描述局部元素實例的 XML 片段。研究一下清單 7中的示例 XML 模式。相應的 EMF 模型識別清單 8中的全局元素實例。相反,清單 9中的局部元素實例卻導致異常。要支持局部元素的轉換,必須在 Java 映射中添加自定義元素。

清單 7.XML 模式示例

<schema>
 <element name="GlobalElement">
  <complexType>
   <sequence>
    <element name="LocalElement" type="xsd:string"/>
   </sequence>
  </complexType>
 </element>
</schema>

清單 8.全局元素實例

<GlobalElement>
 <LocalElement>Some String</LocalElement>
</GlobalElement>

清單 9.局部元素實例

<LocalElement>Some String</LocalElement>

考慮 SupplyChainSchema.xsd 文檔和 WSDL to Java Bean Skeleton wizard 生成的 JavaBean 時,您將看見有三個局部元素被映射到 SOAPElement:

來自 <PurchaseOrderType> 復雜類型的 <paymentMethod> 元素

來自 <PurchaseOrderType> 復雜類型的 <item> 元素

來自 <ShippingNoticeType> 復雜類型的 <item> 元素

要在 <paymentMethod> 這個局部元素和 com.example.supplychain.PaymentMethodType 這個 Java 類之間建立自定義映射,請在 SupplyChainEMF 項目中打開 /SupplyChainEMF/src/com/example/supplychain/impl /SupplychainPackageImpl.java。將清單 10 中的代碼片段添加到 initializePackageContents() 方法的尾部。該方法將作為初始化的一部分被調用。

清單 10.添加一個局部元素映射

initEClass(paymentMethodTypeEClass, PaymentMethodType.class,
 "paymentMethod", !IS_ABSTRACT, !IS_INTERFACE);

接下來,將為兩個 <item> 局部元素建立自定義映射。和 <paymentMethod> 元素不同的是,不能在 initializePackageContents() 方法中添加靜態映射條目,這是因為 EMF 模型對每個局部元素名稱只允許一個映射。要克服這個缺點,可以象使用映射那樣動態注冊並移除必要的映射。清單 11 顯示了 4 個方法,這 4 個方法允許您從 <PurchaseOrderType> 復雜類型中注冊和移除 <item> 元素映射,以及從 <ShippingNoticeType> 復雜類型中注冊和移除 <item> 元素映射。在 SupplyChainEMF 項目中,打開 SupplychainPackageImpl.java 並添加清單 11所示的代碼片段。

清單 11.添加一個局部元素映射

private EClass purchaseItem;
public void initPurchaseItem()
{
 purchaseItem = initEClass(createEClass(ITEM_TYPE),
  ItemType.class, "item", !IS_ABSTRACT, !IS_INTERFACE);
}
public void removePurchaseItem()
{
 if (purchaseItem != null)
  this.eClassifiers.remove(purchaseItem);
}
private EClass shippingItem;
public void initShippingItem()
{
 shippingItem = initEClass(createEClass(SHIPPING_ITEM_TYPE),

  ShippingItemType.class, "item", !IS_ABSTRACT, !IS_INTERFACE);
}
public void removeShippingItem()
{
 if (shippingItem != null)
  this.eClassifiers.remove(shippingItem);
}

最後,如清單 12 所示,執行 SupplyChainBindingImpl.java 中的 submitPurchaseOrder(com.example.supplychain.www.PurchaseOrderType purchaseOrder) 方法。該清單演示了如何使用前面創建的方法。

清單 12.執行 submitPurchaseOrder 方法示例

public com.example.supplychain.www.PurchaseReferenceType 
submitPurchaseOrder(com.example.supplychain.www.PurchaseOrderType purchaseOrder)
throws java.rmi.RemoteException
{
 try 
 {
  String customerReference = purchaseOrder.getCustomerReference();
  /*
   * Converting SOAPElement to PaymentMethodType. The local element
   * mapping for paymentMethod is statically registered in the 
   * initializePackageContents() method of SupplychainPackageImpl.java
   */
  PaymentMethodType paymentMethod =
   (PaymentMethodType)domElement2EObject(soapElement2DOMElement((
   SOAPElement)purchaseOrder.getPaymentMethod()));
  /*
   * Converting SOAPElement to ItemType. The local element mapping 
   * for item is dynamically registered and removed using the 
   * initPurchaseItem() and removePurchaseItem() methods.
   */
  ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).initPurchaseItem();
  ItemType item = (ItemType)domElement2EObject(soapElement2DOMElement((
  SOAPElement)purchaseOrder.getItem()));
  ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).removePurchaseItem();
  ShippingNoticeType shippingNotice = purchaseOrder.getShippingNotice();
  String recipient = shippingNotice.getRecipient();
  String address = shippingNotice.getAddress();
  /*
   * Converting SOAPElement to ShippingItemType.
   */
  ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).initShippingItem();
  ShippingItemType shippingItem =
   (ShippingItemType)domElement2EObject(soapElement2DOMElement((
   SOAPElement)shippingNotice.getItem()));
  ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).removeShippingItem();
  float height = shippingItem.getHeight();
  float length = shippingItem.getLength();
  float width = shippingItem.getWidth();
  float weight = shippingItem.getWeight();
  boolean fragile = shippingItem.isFragile();
  float total = 0;
  total += item.getQuantity() * item.getPrice();
  total += weight;
  if (fragile)
   total += 100;
  ProcessingType processingType =
  SupplychainFactory.eINSTANCE.createProcessingType();
  StatusType status = SupplychainFactory.eINSTANCE.createStatusType();
  status.setProcessing(processingType);
  PurchaseReferenceType purchaseReference = new PurchaseReferenceType();
  purchaseReference.setReferenceNumber(String.valueOf(Math.abs((
  new Random()).nextInt())));
  /*
   * Converting StatusType to SOAPElement.
   */
  purchaseReference.setStatus(domElement2SOAPElement(eObject2DOMElement(status)));
  purchaseReference.setTotal(total);
  return purchaseReference;
 }
 catch (Throwable t)
 {
  t.printStackTrace();
 }
 return null;
}

測試供應鏈 Web 服務

您已經完成了供應鏈 Web 服務。現在使用 Web Services Explorer 對其進行測試。

啟動部署了供應鏈 Web 服務的服務器。打開 server 視圖。單擊菜單Window > Show View>Other...。展開Server文件夾,然後單擊Servers>OK。

在 Servers 視圖中,右鍵單擊WebSphere v5.1 Test Environment>Start。

右鍵單擊/SupplyChainWeb/WebContent/wsdl/com/example/supplychain/www/SupplyChainService.wsdl > Web Services>Test with Web Services Explorer啟動 Web Services Explorer。

在操作欄中,單擊submitPurchaseOrder鏈接。

輸入如表 1 所示的參數值。

表 1.參數值 參數 值 customerReference John Doe paymentMethod tns:creditCard creditCardType VISA creditCardNumber 12345-67890 expiration 2004-06-17 id Plasma TV description 42-inch quantity 1 price 3000 recipient John Doe address 123 Fake street height 40 width 25 length 10 weight 60 fragile true

單擊Go調用 submitPurchaseOrder 操作。圖 3 顯示了調用結果。

圖 3.調用 submitPurchaseOrder 操作的結果

結束語

JAX-RPC 定義了一個 XML 到 Java 類型映射的標准模型,但是,該模型還需要為所有的 XML 數據類型提供標准映射。本文演示了如何聯合 EMF 和 JAX-RPC 的功能來支持沒有標准映射的 XML 數據類型。雖然 EMF 提供了一個解決方案,但是該方法需要用戶同時使用兩種不同的編程模型。以後,新興技術服務數據對象 (Service Data Objects) 將會針對該問題提供更好的解決方案。

獲取本文中所使用的產品和工具

如果您是一個 developerWorks 訂閱者,那麼您將具有一個單用戶許可證,可以使用 WebSphere Studio Application and Site Developer 和其他的 DB2®、Lotus®、Rational®、Tivoli®,以及 WebSphere® 產品 —— 其中包括基於 Eclipse 的 WebSphere Studio IDE 來開發、測試、評估和演示您的應用程序。

本文配套源碼

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