首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

利用 Eclipse Modeling Framework (EMF) 增強 JAX-RPC 類型映射

     編輯:Java 相關知識

JAX-RPC (JSR-101) 定義了 XML 到 Java 的標準類型映射,但對複雜 XML Schema(如未定義全域元素的區域元素)支援不足,會退化為 SOAPElement,導致開發者需手寫解析代碼。本文展示如何結合 EMF (Eclipse Modeling Framework),為無標準映射的 XML 類型生成強類型 Java 模型,並整合至 WebSphere Studio Application Developer 開發的 Web 服務中。

場景:供應鏈 Web 服務

  • WSDL:SupplyChainService.wsdl
  • Schema:SupplyChainSchema.xsd,包含 PurchaseOrderType、PaymentMethodType、ShippingNoticeType 等複雜類型。
  • 問題:三個區域元素 (paymentMethod、item x2) 無全域宣告,JAX-RPC 僅映射為 SOAPElement。

步驟一:生成 JAX-RPC 骨架

右鍵 WSDL → Web Services → Generate Java bean skeleton,產生 SupplyChainBindingImpl.java 等骨架代碼。修正 Schema 複製路徑後通過驗證。
WSDL to Java Bean Skeleton 向導

步驟二:建立 EMF 模型專案

  1. New → EMF Project → Load from an XML Schema → 選擇 SupplyChainSchema.xsd。
    New EMF Project 向導
  2. 選擇 supplychain 套件 → Finish → 開啟 EMF Generator Editor → 右鍵根節點 → Generate Model Code。
    產生完整的 EMF 實體類、Factory、Package、Resource 實現。

步驟三:整合 EMF 模型到 Web 服務

  1. 將 SupplyChainEMF 專案加入 EAR 作為 Utility JAR,並設定 Web 專案依賴。
    Web Services Explorer 測試結果
  2. Web 專案 Java Build Path 加入 EMF 執行庫變數:EMF_COMMON、EMF_ECORE、EMF_ECORE_XMI。

步驟四:核心轉換代碼 (SupplyChainBindingImpl.java)


// 靜態初始化 EMF Package
static { SupplychainPackageImpl.init(); }

// 1. SOAPElement → DOM Element (WebSphere 專用 API)
public Element soapElement2DOMElement(SOAPElement soapElement) throws Exception {
    return ((com.ibm.ws.webservices.engine.xmlsoap.SOAPElement)soapElement).getAsDOM();
}

// 2. DOM Element → EMF Object (EObject)
public EObject domElement2EObject(Element e) throws ... {
    DOMSource domSource = new DOMSource(e);
    Transformer serializer = TransformerFactory.newInstance().newTransformer();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    serializer.transform(domSource, new StreamResult(baos));
    byte[] xmlBytes = baos.toByteArray();
    URI uri = URI.createURI(SupplychainPackage.eNS_URI);
    Resource res = new SupplychainResourceFactoryImpl().createResource(uri);
    res.load(new ByteArrayInputStream(xmlBytes), null);
    return res.getContents().isEmpty() ? null : (EObject)res.getContents().get(0);
}

// 3. EMF Object → DOM Element
public Element eObject2DOMElement(EObject eObject) throws ... {
    URI uri = URI.createURI(SupplychainPackage.eNS_URI);
    Resource res = new SupplychainResourceFactoryImpl().createResource(uri);
    res.getContents().add(eObject);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    res.save(baos, null);
    byte[] xmlBytes = baos.toByteArray();
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
        .parse(new ByteArrayInputStream(xmlBytes));
    return doc.getDocumentElement();
}

// 4. DOM Element → SOAPElement (WebSphere 專用 API)
public SOAPElement domElement2SOAPElement(Element e) throws SOAPException {
    SOAPFactory factory = SOAPFactory.newInstance();
    com.ibm.ws.webservices.engine.xmlsoap.SOAPElement soapEl = 
        (com.ibm.ws.webservices.engine.xmlsoap.SOAPElement)factory.createElement("temp");
    soapEl.setAlternateContent(e);
    return soapEl;
}

步驟五:處理區域元素映射

EMF 預設僅映射全域元素。需為區域元素手動註冊映射:

  1. 靜態註冊 (paymentMethod):在 SupplychainPackageImpl.initializePackageContents() 尾部添加:
    initEClass(paymentMethodTypeEClass, PaymentMethodType.class, "paymentMethod", !IS_ABSTRACT, !IS_INTERFACE);
  2. 動態註冊/移除 (item x2):因同名區域元素衝突,在 SupplychainPackageImpl 添加四個方法:
    initPurchaseItem()/removePurchaseItem()、initShippingItem()/removeShippingItem(),分別為 PurchaseOrderType.item 與 ShippingNoticeType.item 建立/移除映射。

步驟六:業務邏輯實現 (submitPurchaseOrder)


public PurchaseReferenceType submitPurchaseOrder(PurchaseOrderType purchaseOrder) {
    try {
        // 1. paymentMethod (靜態映射)
        PaymentMethodType paymentMethod = (PaymentMethodType) 
            domElement2EObject(soapElement2DOMElement((SOAPElement)purchaseOrder.getPaymentMethod()));

        // 2. item (動態映射 PurchaseOrderType.item)
        ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).initPurchaseItem();
        ItemType item = (ItemType) 
            domElement2EObject(soapElement2DOMElement((SOAPElement)purchaseOrder.getItem()));
        ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).removePurchaseItem();

        // 3. shippingNotice.item (動態映射 ShippingNoticeType.item)
        ShippingNoticeType shippingNotice = purchaseOrder.getShippingNotice();
        ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).initShippingItem();
        ShippingItemType shippingItem = (ShippingItemType)
            domElement2EObject(soapElement2DOMElement((SOAPElement)shippingNotice.getItem()));
        ((SupplychainPackageImpl)SupplychainPackage.eINSTANCE).removeShippingItem();

        // 業務計算...
        PurchaseReferenceType ref = new PurchaseReferenceType();
        ref.setReferenceNumber(String.valueOf(Math.abs(new Random().nextInt())));
        // 回傳物體轉 SOAPElement
        ref.setStatus(domElement2SOAPElement(eObject2DOMElement(status)));
        return ref;
    } catch (Throwable t) { t.printStackTrace(); return null; }
}

測試與驗證

啟動測試環境 → 右鍵 WSDL → Test with Web Services Explorer → 輸入測試參數 → Go → 獲得正確回傳。
Web Services Explorer 測試結果

總結

  • EMF 提供強類型 XML 數據綁定,解決 JAX-RPC 區域元素映射缺口。
  • 轉換鏈路:SOAPElement ⇄ DOM ⇄ EMF (EObject),利用 Transformer/Resource 序列化。
  • 區域元素需靜態/動態註冊到 EMF Package 映射表。
  • 缺點:雙重編程模型 (JAX-RPC + EMF),後續 SDO (Service Data Objects) 將提供統一解決方案。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved