利用 Eclipse Modeling Framework 加强 JAX-RPC 类型映射的功能

类别:Java 点击:0 评论:0 推荐:
利用 Eclipse Modeling Framework 加强 JAX-RPC 类型映射的功能
英文原文 内容: 引言 创建供应链 Web 服务 创建供应链 EMF 模型 集成供应链 Web 服务与 EMF 模型 测试供应链 Web 服务 结束语 获取产品 参考资料 关于作者 对本文的评价

级别: 中级

Jeffrey Liu ([email protected]
软件开发人员, IBM
2004 年 8 月

本文演示了如何使用 Eclipse Modeling Framework (EMF) 来加强 JAX-RPC 类型映射模型的功能。本文还提供了示例代码指导您从 Web 服务描述语言 (Web Services Description Language,WSDL) 文档创建 Web 服务的整个过程,Web 服务描述语言文档使用的是不支持 XML 数据类型的 JAX-RPC。

引言
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 构建路径中。右键单击 SupplyChainWeb project > 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 模式示例

清单 8.全局元素实例

Some String 清单 9.局部元素实例

Some String 考虑 SupplyChainSchema.xsd 文档和 WSDL to Java Bean Skeleton wizard 生成的 JavaBean 时,您将看见有三个局部元素被映射到 SOAPElement: 来自

复杂类型的

元素 来自

复杂类型的 元素 来自 复杂类型的 元素 要在

这个局部元素和 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);
接下来,将为两个 局部元素建立自定义映射。和

元素不同的是,不能在 initializePackageContents() 方法中添加静态映射条目,这是因为 EMF 模型对每个局部元素名称只允许一个映射。要克服这个缺点,可以象使用映射那样动态注册并移除必要的映射。清单 11 显示了 4 个方法,这 4 个方法允许您从

复杂类型中注册和移除 元素映射,以及从 复杂类型中注册和移除 元素映射。在 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 来开发、测试、评估和演示您的应用程序。如果您不是一个订阅者,您可以现在订阅。

参考资料 查看下列规范: JAX-RPC (JSR-101) Java APIs for XML Messaging: SOAP with Attachments API for Java (JSR-67) Service Data Objects (JSR-235)
阅读 XML Schema Part 0: Primer 关于 XML 模式工具的描述 (W3C,2001 年 5 月 2 日)。

通过阅读 XML Schema Part 1: Structures(W3C,2001 年 5 月 2 日)更好的理解 XML 模式定义语言的结构。

阅读 XML Schema Part 2: Datatypes,讨论 XML 模式(W3C,2001 年 5 月 2 日)中的数据类型。

下载 60 天试用版的 IBM WebSphere Studio Application and Site Developer V5.1.2。

在 Eclipse.org 上下载 Eclipse 项目、关于 Eclipse 工具的文章、新闻组及更多相关内容。

在 Developer Bookstore 购买关于各种的技术主题的打折图书。

下载源代码

关于作者
Jeffrey Liu 是 IBM 多伦多实验室 Rational Studio Web 服务工具小组的一位软件开发人员,您可以通过 [email protected] 与 Jeffrey 联系。

本文地址:http://com.8s8s.com/it/it16088.htm