JAX-RPC, also known as JSR-101, is an important step forward in achieving a standard programming model that facilitates the building of interoperable Web services on the Java™ platform. A key aspect of JAX-RPC is its XML to Java type mapping model, which serves as an implementation standard for Web services product providers. Without such a model, Web services product providers might fall into the trap of defining proprietary type mappings, which can be a serious source of Java interoperability problems.
Although JAX-RPC devotes a considerable amount of effort in supporting XML data types, room for improvement still exists. Moreover, JAX-RPC requires the mapping of any unsupported XML data types to the javax.xml.soap.SOAPElement interface. This interface does not provide a strongly typed Java model for users to work with, meaning they might have to write custom code to parse through a SOAPElement instance. This might not be intuitive for novice users, especially when dealing with large XML fragments. This article demonstrates how to use EMF to support XML data types that do not have standard JAX-RPC type mappings. Generating a Web service that has substantial usage of JAX-RPC unsupported XML data types is not easy, but this article provides you with an effective solution by combining the use of Web services tools and EMF tools in IBM® WebSphere® Studio Application and Site Developer V5.1.2 (Application and Site Developer).
Create the supply-chain Web service
To follow this article, you must install WebSphere Application and Site Developer V5.1.2. If necessary, you can download a 60-day trial version.
- Create a Web project. Click on menu File > New > Project... > Web > Dynamic Web Project > Next to open the New Dynamic Web Project wizard.
- Enter SupplyChainWeb as the Web project name, select the Configure advance options checkbox and click Next.
- Enter SupplyChainEAR as the EAR project name and click Finish.
- Click on the Code icon at the top of this article and download SupplyChainService.wsdl and SupplyChainSchema.xsd to your file system.
- Import or copy SupplyChainService.wsdl and SupplyChainSchema.xsd into the root of the SupplyChainWeb project.
- In the navigator view, right click on SupplyChainService.wsdl > Web Services > Generate Java bean skeleton to open the WSDL to Java Bean Skeleton wizard Figure 1 shows. This wizard generates a Java skeleton code implementation based on the information defined in the WSDL document. Accept the default settings and click Finish.
Figure 1. WSDL to Java Bean Skeleton wizard
- After the wizard completes, you see a few WSDL validation errors in the tasks view because the XML schema file, SupplyChainSchema.xsd, was not copied to the proper places. To fix these errors, copy SupplyChainSchema.xsd from the root of the SupplyChainWeb project to the /SupplyChainWeb/WebContent/WEB-INF/wsdl/ directory and the /SupplyChainWeb/WebContent/wsdl/com/example/supplychain/www/ directory. Rerun validation by right clicking on SupplyChainService.wsdl > Run validation.
Create the supply-chain EMF model
The WSDL to Java Bean Skeleton wizard generated JavaBeans with one or more properties mapped to SOAPElement (specifically, PurchaseOrderType.java, PurchaseReferenceType.java, and ShippingNoticeType.java). In this section, you generate an EMF model for the supply-chain Web service to support the XML data types mapped to SOAPElement.
- Create an EMF project. Click on menu File > New > Project... > Eclipse Modeling Framework > EMF Project > Next to open the New EMF Project wizard.
- Enter SupplyChainEMF as the project name and click Next.
- Select Load from an XML schema and click Next.
- Click Browse Workspace... to open the file selection dialog. Find and select SupplyChainSchema.xsd and click OK. Click Next.
- Select the supplychain package and click Finish. Refer to Figure 2.
Figure 2. New EMF Project wizard
- After the New EMF Project wizard completes, the EMF Generator Editor opens. In this editor, right click on the SupplyChainSchema node > Generate Model Code. You've successfully generated the supply-chain EMF model. In the next section, you learn how to integrate the EMF code into the supply-chain Web service.
Integrate the supply-chain Web service and EMF model
- Set up all the dependencies for the SupplyChainWeb project. Add the SupplyChainEMF project to the SupplyChainEAR as a utility JAR file and specify a JAR file dependency from the SupplyChainWeb project to this utility JAR file.
- Open /SupplyChainEAR/META-INF/application.xml in the Application Deployment Descriptor Editor. Click on the Module tab.
- In the Project Utility JARs section, click Add..., select SupplyChainEMF and click Finish. Save and close the Application Deployment Descriptor Editor.
- Open /SupplyChainWeb/WebContent/META-INF/MANIFEST.MF in the JAR Dependency Editor. In the Dependencies section, select SupplyChainEMF.jar. Save and close the JAR Dependency Editor.
- Add the EMF libraries to the Java build path of the SupplyChainWeb project. Right click on the SupplyChainWeb project > Properties > Java Build Path. Click on the Libraries tab > Add Variable....
- Select EMF_COMMON, EMF_ECORE and EMF_ECORE_XMI. Click OK > OK.
- Listing 1 shows all the import statements you use. Open /SupplyChainWeb/JavaSource/com/example/supplychain/www/SupplyChainBindingImpl.java in the Java Editor and add these import statements.
Listing 1. Import statementsimport 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;
- You must initialize the generated supply-chain EMF model before you use it. The initialization process builds a mapping between elements declared in the XML schema (SupplyChainSchema.xsd) and Java classes that the EMF code generator created. This mapping serves as the recipe for converting XML fragments to the corresponding EMF-based Java classes and vice versa. To initialize the supply-chain EMF model, add the following static block to SupplyChainBindingImpl.java.
Listing 2. Initializing the EMF packagestatic { SupplychainPackageImpl.init(); } - Next, you add four methods to SupplyChainBindingImpl.java that convert a SOAPElement to a DOMElement, and then a DOMElement to the corresponding EMF-based Java class and vice versa. Listings 3, 4, 5, and 6 show these methods. The
soapElement2DOMElement(SOAPElement soapElement)method and thedomElement2SOAPElement(Element e)method, which are responsible for the SOAPElement to DOMElement conversion, utilize a couple of proprietary methods that are specific to WebSphere Application Server V5.x. Unfortunately at this time, there's no public methods for such conversion. To avoid using version specific code, you can manually traverse the SOAPElement and build the DOMElement accordingly.In the future, when a SOAP with Attachments API for Java V1.2 (SAAJ)-compatible implementation becomes available, you no longer need to use version specific code to do SOAPElement to DOMElement conversion because SAAJ V1.2 requires SOAPElement to extend DOMElement directly.
Listing 3. Converting a SOAPElement to a DOMElementpublic Element soapElement2DOMElement(SOAPElement soapElement) throws Exception { return ((com.ibm.ws.webservices.engine.xmlsoap.SOAPElement)soapElement).getAsDOM(); }
Listing 4. Converting a DOMElement to an EMF objectpublic 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; }
Listing 5. Converting an EMF object to a DOMElementpublic 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(); }
Listing 6. Converting a DOMElement to a SOAPElementpublic 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; }
As mentioned earlier, the supply-chain EMF model relies on the element to Java mapping to convert an XML fragment to the corresponding EMF-based Java class. However, by default, the EMF code generator only generates mapping entries for global elements, not for local elements. Global elements are element declarations that appear as children of the schema element in an XML schema document, whereas local elements are not. The default list of mappings excludes local elements, and therefore, the supply-chain EMF model can't convert an XML fragment that represents an instance of a local element. Consider the sample XML schema Listing 7 shows. The corresponding EMF model recognizes the global element instance Listing 8 shows. Conversely, the local element instance Listing 9 shows results in an exception. To support conversion of local elements, you must add custom elements to Java mappings.
Listing 7. Sample XML schema
<schema>
<element name="GlobalElement">
<complexType>
<sequence>
<element name="LocalElement" type="xsd:string"/>
</sequence>
</complexType>
</element>
</schema>
|
Listing 8. GlobalElement instance
<GlobalElement> <LocalElement>Some String</LocalElement> </GlobalElement> |
Listing 9. LocalElement instance
<LocalElement>Some String</LocalElement> |
- When you look at the SupplyChainSchema.xsd document and the JavaBeans that the WSDL to Java Bean Skeleton wizard generated, you see three local elements that are mapped to SOAPElement:
<paymentMethod>element from the<PurchaseOrderType>complex type<item>element from the<PurchaseOrderType>complex type<item>element from the<ShippingNoticeType>complex type
<paymentMethod>local element and the com.example.supplychain.PaymentMethodType Java class, open /SupplyChainEMF/src/com/example/supplychain/impl/SupplychainPackageImpl.java in the SupplyChainEMF project. Add the code snippet from Listing 10 to the end of theinitializePackageContents()method. This method will be called as part of the initialization.
Listing 10. Adding a local element mappinginitEClass(paymentMethodTypeEClass, PaymentMethodType.class, "paymentMethod", !IS_ABSTRACT, !IS_INTERFACE);
- Next, you set up custom mappings for the two
<item>local elements. Unlike the<paymentMethod>element, you cannot add a static mapping entry in theinitializePackageContents()method because the EMF model only allows one mapping for each local element name. To overcome this problem, you dynamically register and remove the necessary mappings as you use them. Listing 11 shows four methods that allow you to register and remove the<item>element mapping from the<PurchaseOrderType>complex type, and register and remove the<item>element mapping from the<ShippingNoticeType>complex type. In the SupplyChainEMF project, open SupplychainPackageImpl.java and add the code snippet Listing 11 shows.
Listing 11. Adding a local element mappingprivate 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); } - Finally, implement the
submitPurchaseOrder(com.example.supplychain.www.PurchaseOrderType purchaseOrder)method in SupplyChainBindingImpl.java, as Listing 12 shows. It demonstrates how use the methods you created earlier.
Listing 12. Sample implementation of the submitPurchaseOrder methodpublic 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; }
Test the supply-chain Web service
You've completed the supply-chain Web service. Now use the Web Services Explorer to test it.
- Start the server where you deployed the supply-chain Web service. Open the server view. Click on menu Window > Show View > Other.... Expand the Server folder and click Servers > OK.
- In the Servers view, right click on the WebSphere v5.1 Test Environment > Start.
- Right click on /SupplyChainWeb/WebContent/wsdl/com/example/supplychain/www/SupplyChainService.wsdl > Web Services > Test with Web Services Explorer to launch the Web Services Explorer.
- In the operations table, click on the submitPurchaseOrder link.
- Enter the parameter values Table 1 shows.
Table 1. Parameter values Parameter Value 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 - Click Go to invoke the submitPurchaseOrder operation. Figure 3 shows the result of the invocation.
Figure 3. Result of invoking the submitPurchaseOrder operation
JAX-RPC defines a standard XML to Java type mapping model, however, this model has yet to provide standard mappings for the exhaustive list of XML data types. This article demonstrated how to unite the power of EMF and JAX-RPC to support XML data types that do not have standard mappings. Although EMF provides a solution, this approach requires users to work with two different programming models. In the future, the emerging technology Service Data Objects should provide a more elegant solution to this problem.
Get the products used in this article
If you are a developerWorks subscriber, you have a single user license to use WebSphere Studio Application and Site Developer, and other DB2®, Lotus®, Rational®, Tivoli®, and WebSphere® products -- including the Eclipse-based WebSphere Studio IDE -- to develop, test, evaluate, and demonstrate your applications. If you are not a subscriber, you can subscribe today.
| Name | Size | Download method |
|---|---|---|
| ws-mapprobcode.zip | HTTP |
Information about download methods
- Check out these specifications:
- JAX-RPC (JSR-101)
- Java APIs for XML Messaging: SOAP with Attachments API for Java (JSR-67)
- Service Data Objects (JSR-235)
- Read XML Schema Part 0: Primer for a description of the XML Schema facilities (W3C, May 2, 2001).
- Better understand the structural part of the XML Schema definition language by reading XML Schema Part 1: Structures (W3C, May 2, 2001).
- Read XML Schema Part 2: Datatypes for a discussion of the datatypes used in an XML Schema (W3C, May 2, 2001).
- Download a 60-day trial version of IBM WebSphere Studio Application and Site Developer V5.1.2.
- Go to
Eclipse.org for downloads of the Eclipse project, articles on the Eclipse tools, newsgroups, and more.
- Browse for books on these and other technical topics.
Jeffrey Liu is a software developer on the Rational Studio Web Services Tools Team at the IBM Toronto Lab. You can reach Jeffrey at: jeffliu@ca.ibm.com.
Comments (Undergoing maintenance)





