Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Developing Web Services with EMF SDOs for complex XML schema

Use Rational Application Developer to help replace JAX-RPC Java types with SDOs

Chris Brealey (cbrealey@ca.ibm.com), Senior Advisory Technical Manager, IBM Canada Ltd.
Chris Brealey is the senior advisory development manager and technical team leader for Rational Java Web Services Tools at the IBM Toronto Lab, and a committer on the Eclipse Web Tools Platform (WTP) project. He is responsible for the architecture, design, and delivery of several of the tools for web services in IBM's Rational tools products. He has led the development of web services tools since 2000, with emphasis on standards and best practices for web services. Before working on web services, he was the technical lead on tools for IBM's implementation of CORBA. During his employment with IBM, he has focused on the theory, testing, and invention of development tools for a variety of distributed systems and protocols. Chris joined IBM as a full-time employee in 1989 shortly after graduating from the University of Victoria with a BSc in computer science.
Seng Phung-Lu (sengpl@ca.ibm.com), Staff Software Engineer, IBM Canada Ltd
Seng Phung-Lu is a staff software developer at the IBM Toronto Lab. He has contributed to each release of the web services tools in Rational Application Developer, and he is an active contributor to the web services component of the open-source Eclipse Web Tools Platform (WTP) project. His current areas of interest include application development and improving software quality, particularly software performance and usability.

Summary:  The Java APIs for XML-based RPCs (JAX-RPC) 1.1 specification defines a powerful Java programming model for web service providers and web service requestors and is a key component of the J2EE 1.4 standard. While the JAX-RPC specification offers many benefits, it offers limited support for mapping some of the fancier types from XML Schema to Java. We will show you how to use features of Rational Application Developer v6 and WebSphere Application Server v6 to disable the JAX-RPC type mappings and use instead the Java Eclipse EMF SDO representation of XML Schema.

Date:  03 Mar 2006 (Published 28 Feb 2006)
Level:  Advanced

Activity:  12174 views
Comments:  

This article explains how to generate and glue together Java APIs for XML-based RPCs (JAX-RPC) and Eclipse Modeling Framework Service Data Object (EMF SDO) Java components to develop Java Web services based on XML schemas with types that JAX-RPC on its own does not handle well. The first section, Perceptions and Standards examines some of the history behind web services standards and how they relate to each other. Then, How to create a web service using EMF SDO guides you through a procedure for adapting JAX-RPC web service and client classes to use SDO data types.

Rational Application Developer

The procedure in this article has been tested on Rational Application Developer versions 6.0.1 and 6.0.1.1. At least one defect with the SDO generator's ability to handle extremely large XML Schema documents was fixed in Rational Application Developer 6.0.1. Therefore, we recommend using version Rational Application Developer 6.0.1 or higher.

The How To section depends on the following prerequisites:

  • You need to install Rational Application Developer 6.0.1 or higher with the WebSphere Application Server (Application Server) Version 6 test environment. We only mention Rational Application Developer in the steps below, but Rational Application Developer, Rational Software Architect (RSA), and Rational Web Developer (RWD) can be considered interchangeable.
  • You need to have a Web Services Description Language (WSDL) document handy. Examples of a WSDL file and accompanying XML Schema file are included with this article and available under Downloads.
  • The WSDL document uses a conventional SOAP binding with the Document/Literal style and use.
  • You need to be comfortable with the basics of the Java programming language, WSDL, and XML Schema.

Perceptions and standards

First, let's take a look at a few points about web services standards to provide context for the how-to section that follows.

A bit of historical perspective

The realm of web services enjoyed a modest beginning in the form of two W3C Notes: SOAP 1.1 in May 2000 and WSDL 1.1 in March 2001, both of which remain the foundation for most of today's web service applications, tools, and runtimes. At about the same time, another specification was being developed and was ultimately published as a W3C Recommendation in May 2001: XML Schema. The relative timing of the development of the XML Schema and SOAP specifications, namely that XML Schema was still a working draft during the conception of SOAP 1.1, may have helped lead to the existence of Chapter 5 of the SOAP specification, "SOAP Encoding." After all, SOAP needed a type system, and Chapter 5 describes a basic type system and the rules for serializing and deserializing information into XML fragments consistent with it. The subsequent publication and rapid adoption of the XML Schema recommendation coupled with a number of ambiguities in the SOAP Encoding lead to the following two rather interesting outcomes (among many others, of course):

  1. The formation of the Web Services Interoperability, or WS-I, organization and the final publication of the WS-I Basic Profile in August 2003, which effectively vanquished the use of the SOAP Encoding in web service applications to maximize interoperability
  2. The use of XML Schema and instances thereof in WSDL and SOAP

A similar story unfolded in Java between the JAX-RPC (JSR-101) and JAX-B (JSR-31) specifications. JAX-RPC 1.0 was published as a final specification in June 2002, late enough to read the WS-I tea leaves and support Literal XML Schema, but too early to benefit from JAX-B 1.0 (JSR-31, final, March 2003) or certainly JAX-B 2.0 (JSR-222, proposed final draft, October 2005). Not unlike SOAP and XML Schema, the timing of the aforementioned JSRs in part made it necessary for JAX-RPC to define its own type binding system between XML Schema and Java. Neither JAX-RPC 1.0 nor its successor, JAX-RPC 1.1 (Final, October 2003), defined bindings for all types in XML Schema. Chapter 18, "Appendix: XML Schema Support," explains for various XML Schema types whether a binding to Java is required and specified, optional and unspecified, or unsupported, listing some of the following examples:

  • JAX-RPC maps an xsd:sequence to a Java value type (Bean)
  • JAX-RPC defines no binding for xsd:choice

The adoption of Literal XML Schema in the development of web services was by no means a singular event. Figuratively speaking, however, August 2003 could be considered the time when web services and XML Schema were "married" at the WS-I "altar." As the development of web services using the rich variety of types from XML Schema erupted, so did the limitations in JAX-RPC Chapter 18. This is not a permanent problem for the two following reasons:

  1. JAX-WS 2.0 (JSR-224), the successor to JAX-RPC, uses JAX-B as the preferred binding between XML Schema and Java instead of attempting to define its own binding
  2. JAX-RPC itself provides an escape route from its own incomplete bindings in the form of the SOAPElement data type which can then be coupled to a first class representation in Java of XML Schema, such as SDO -- the subject of the rest of this article

About SOAPElement

SOAPElement

A javax.xml.soap.SOAPElement is an embellished Document Object Model (DOM) Element and is used to represent an XML element in a SOAP message. Since it is a kind of DOM Element, a SOAPElement provides the programmer with access to all the details of the XML element it represents, including nested SOAPElements.

In both JAX-RPC 1.0 and 1.1, chapter 6.4, "Literal Representation," essentially states that XML Schema types with no standard binding to Java must in fact be mapped to a "SOAPElement." JAX-RPC 1.1 goes a step further. It requires that it be possible to disable the standard JAX-RPC bindings in favor of SOAPElement. This allows the programmer to adapt JAX-RPC service endpoint interfaces and their implementations for use with other Java programming models for XML schema such as JAX-B or SDO.

Rational Application Developer v.6 and WebSphere Application Server v.6 provide an option to disable the JAX-RPC standard data bindings and instruct the web service wizards to generate JAX-RPC skeletons and service endpoint interfaces whose methods are equipped entirely with SOAPElements.

About EMF SDO

Service Data Objects (SDO) is an architecture and programming model for the Java platform. It provides an easy yet dynamic and powerful representation for the navigation and manipulation of structured data from a variety of data providers. The providers include XML documents, relational databases, EJBs, and JCA resource adapters, but are by no means limited to those sources.

The Eclipse Modelling Framework (EMF) open source project provides a modelling framework and tools for the Eclipse platform for the development of structured data models and editors. Included in the EMF project is an implementation of the SDO 1.0 specification including tools to generate static Java data model classes for use with the EMF and SDO programming models.

Rational Application Developer v.6, together with the underlying Eclipse and EMF projects on which it is built, provides tools for generating a Java structured data programming model based upon the EMF 2.0.1 framework and the SDO 1.0 specification.

JAX-RPC meets SDO

Through the application of a little Java glue code, the SOAPElements on JAX-RPC skeleton classes and service endpoint interfaces can be readily deserialized to, and serialized from, graphs of SDO objects and Java types. Our approach is to create new skeleton and proxy classes that have the same business method names as the generated skeleton and proxy classes but with SDO or basic Java types instead of SOAPElements for the parameters and return types. The business methods of the hand-written SDO proxy take care of converting SDO parameters to SOAPElement parameters, delegating to the generated SOAPElement proxy, and converting the SOAPElement return object (if any) to an SDO. Similarly, the business methods of the generated SOAPElement skeleton take care of converting SOAPElement parameters to SDO parameters, delegating to the hand-written SDO skeleton containing the real business logic, and converting the SDO return object (if any) to a SOAPElement.

The glue code that converts SOAPElements to SDO objects and vice versa is split into the following three parts:

  1. Relatively complex code that handles the bulk of the conversion work and, luckily, works for all SDOs from all XML schemas. This code is supplied with this article.
  2. Relatively little code unique to the XML Schema or schemas from which the SDOs are generated.
  3. Relatively little code unique to each parameter or return type.

Figure 1 illustrates this pattern.


Figure 1. JAX-RPC Meets SDO at a Glance
JAX-RPC Meets SDO at a Glance

Create a web service using EMF SDO

In this section we will walk through the steps to create and test a new Java web service and client based on the EMF SDO programming model. Our web service is called Claim and consists of a single operation that accepts an XML document describing a health claim and returns an XML document describing an insurance company's denial or payment of the claim.

Clients and services

This procedure deals with adapting both services and clients to use EMF SDOs. If you want to generate only a web service client, bypass parts 3 and 4. If you want to generate only a web service skeleton, bypass parts 5 and 6.

The procedure is broken into seven parts:

  1. Setup
  2. Generate the EMF SDO code
  3. Generate the web service skeleton code
  4. Adapt the web service skeleton to use SDOs
  5. Generate the web service client code
  6. Adapt the web service client to use SDOs
  7. Implement and test the service

Listing 1 offers the listing of the XML schema, Claim.xsd. Listing 2 provides the WSDL, Claim.wsdl that uses the schema." The files are also available for Download.


Listing 1. Claim.xsd
<?xml version="1.0" encoding="UTF-8"?>

<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://model.example.org/Claim"
xmlns:tns="http://model.example.org/Claim">

  <element name="claim" type="tns:Claim"/>

  <element name="claimResponse" type="tns:ClaimResponse"/>

  <complexType name="Receipt">
    <sequence>
      <element name="receiptNumber" type="int"/>
      <element name="description" type="string"/>
      <element name="cost" type="double"/>
    </sequence>
  </complexType>

  <complexType name="Address">
    <sequence>
      <element name="province" type="string"/>
      <element name="city" type="string"/>
      <element name="street" type="string"/>
      <element name="postalCode" type="tns:PostalCode"/>
    </sequence>
  </complexType>

  <complexType name="Person">
    <sequence>
      <element name="first" type="string"/>
      <element name="last" type="string"/>
    </sequence>
  </complexType>

  <complexType name="Claim">
    <sequence>
      <element name="policyNumber" type="int"/>
      <element name="person" type="tns:Person"/>
      <element name="address" type="tns:Address"/>
      <element name="receipt" type="tns:Receipt"/>
    </sequence>
  </complexType>

  <complexType name="Denial">
    <sequence>
      <element name="receiptNumber" type="int"/>
      <element name="reason" type="tns:Reason"/>
      <element name="comment" type="string"/>
    </sequence>
  </complexType>

  <complexType name="Payment">
    <sequence>
      <element name="receiptNumber" type="int"/>
      <element name="amount" type="double"/>
      <element name="method" type="tns:Method"/>
    </sequence>
  </complexType>

  <complexType name="Benefit">
    <choice>
      <element name="payment" type="tns:Payment"/>
      <element name="denial" type="tns:Denial"/>
    </choice>
  </complexType>

  <complexType name="ClaimResponse">
    <sequence>
      <element name="policyNumber" type="int"/>
      <element name="claimNumber" type="int"/>
      <element name="benefit" type="tns:Benefit"/>
    </sequence>
  </complexType>

  <simpleType name="PostalCode">
    <restriction base="string">
      <pattern value="[A-Z][0-9][A-Z][0-9][A-Z][0-9]"/>
    </restriction>
  </simpleType>

  <simpleType name="Method">
    <restriction base="string">
      <enumeration value="Deposit"/>
      <enumeration value="Cheque"/>
      <enumeration value="Credit"/>
    </restriction>
  </simpleType>

  <simpleType name="Reason">
    <restriction base="string">
      <enumeration value="InadequateCoverage"/>
      <enumeration value="UnknownReceipt"/>
      <enumeration value="PolicyExpired"/>
    </restriction>
  </simpleType>

</schema>


Listing 2. Claim.wsdl
<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions
  targetNamespace="http://webservice.example.org"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:impl="http://webservice.example.org"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:tns="http://model.example.org/Claim">

  <wsdl:types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema">
      <import
      namespace="http://model.example.org/Claim"
      schemaLocation="./Claim.xsd" />
    </schema>
  </wsdl:types>

  <wsdl:message name="processClaimRequest">
    <wsdl:part name="claim" element="tns:claim" />
  </wsdl:message>

  <wsdl:message name="processClaimResponse">
    <wsdl:part name="claimResponse" element="tns:claimResponse" />
  </wsdl:message>

  <wsdl:portType name="ClaimPortType">
    <wsdl:operation name="processClaim">
      <wsdl:input
      message="impl:processClaimRequest"
      name="processClaimRequest" />
      <wsdl:output
      message="impl:processClaimResponse"
      name="processClaimResponse" />
    </wsdl:operation>
  </wsdl:portType>

  <wsdl:binding name="ClaimBinding" type="impl:ClaimPortType">
   <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
   <wsdl:operation name="processClaim">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="processClaimRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="processClaimResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
   </wsdl:operation>
  </wsdl:binding>

  <wsdl:service name="ClaimService">
    <wsdl:port name="ClaimPort" binding="impl:ClaimBinding">
      <wsdlsoap:address location="http://tempuri.org" />
    </wsdl:port>
  </wsdl:service>

</wsdl:definitions>

Notice the element attributes of the part elements of the "processClaimRequest" and "processClaimResponse" messages. The values of those two attributes are important later when it comes to writing the snippets of code that convert between SOAPElements and the generated SDOs.

Setup: enable Eclipse Developer and Web Services Developer capabilities

In Eclipse, "capabilities" (see Figure 2) provide a mechanism for selectively enabling or disabling groups of tools plugged into the platform so that we need not be exposed to views, wizards, actions, and other functions that are irrelevant to the tasks we wish to perform. This example involves the Eclipse EMF and web services tools in Rational Application Developer v.6, so two capabilities must be enabled:

  1. Select Window, then open Preferences > Workbench > Capabilities.
  2. Enable both Eclipse Developer and Web Service Developer capabilities, then click OK.

Figure 2. Eclipse capabilities
Eclipse Capabilities

Part 1: Generate the EMF SDO code

To limit redundancy of generated code and help maximize encapsulation, we will generate the SDOs into a standalone Java project that can be shared by both the web service and web service client enterprise applications. Here we will generate the EMF SDO code from the Claim XML Schema, Claim.xsd. We will also provide a pair of utility classes that convert between SDOs and SOAPElements, and another utility class with a few convenient business logic methods.

There actually is a difference

Option one generates one additional utility class that option two does not. "ClaimResourceUtil.java" contains easy-to-use utility methods for serializing and deserializing SDOs to and from XML streams, however, the class is not relevant to the task of serializing and deserializing SDOs to and from SOAPElements.

You have the following two options in the tools for generating equivalent EMF SDO code, one preferred for separate XML Schema files, the other required for WSDL with inline XML Schema:

  1. Option one: You can use the Rational Application Developer SDO Generator to quickly generate EMF SDO classes from an XML Schema file. This is the easier of the two, but cannot be used to generate Java for XML Schema inlined within WSDL
  2. Option two: You can use the EMF Models wizard to generate EMF SDO classes from an XML Schema file or from an XML Schema inlined within a WSDL file. While not complicated, this does require a few more clicks of the mouse than the SDO Generator technique, but it gives you the benefit of being able to process a WSDL file containing inlined XML Schema

Option one: Generate EMF SDO classes using Rational Application Developer SDO Generator

  1. Select File > New > Project... > Java > Java Project to open the New Java Project wizard (Figure 3 ).
  2. Enter "ClaimModel" as the project name. Select the option to Create separate source and output folders. Click Finish to create the new project.
    Figure 3. New Java Project wizard
    The New Java Project wizard
  3. Import Claim.xsd and Claim.wsdl into the root of the project (though we will not actually need Claim.wsdl until Part 2). If you are following your own example, make sure your XML Schema file ends with ".xsd".
  4. Right click on the XML Schema file and choose Generate > Java... to open the Generate Java dialog box, shown in Figure 4 .
  5. Choose SDO Generator, then enter the path to your Java project's source folder, "/ClaimModel/src" in the Container field.
    Figure 4. Generate Java dialog box
    The Generate Java dialog
  6. Click Finish. You should see a dialog box stating that "The Java classes have been successfully generated."

Option two: Generate EMF SDO classes using the EMF Models wizard

  1. Select File > New > Project...> Eclipse Modeling Framework > EMF Project to open the EMF New Project wizard, shown in Figure 5 and Figure 6.
  2. Enter "ClaimModel" as the project name and click Next.
    Figure 5. New EMF Project wizard
    New EMF Project wizard
  3. Select Create empty project, then click Finish.
    Figure 6. New EMF Project wizard, page 2
    New EMF Project wizard, page 2
  4. Import Claim.xsd and Claim.wsdl into the root of the project (though we will not actually need Claim.wsdl until Part 2).
  5. Select File > New > Other... > Eclipse Modelling Framework > EMF Models to open the EMF Models wizard, as illustrated in Figure 7 .
  6. Select your new EMF project. Keep all other defaults and click Next.
    Figure 7. New EMF Model wizard
    New EMF Model wizard
  7. Select Load from an XML Schema and click Next, as shown in Figure 8 .
    Figure 8. New EMF Model wizard, page 2
    New EMF Model wizard, page 2
  8. Select Browse Workspace. Use the resulting dialog to select ClaimModel/claim.xsd. After closing the dialog, the XML Schema URI value should be as shown in Figure 9. Select Create XML Schema to Ecore Map, then click Next. Select the XML Schema or WSDL file. Select Create XML Schema to Ecore Map. then click Next.
    Figure 9. New EMF Model wizard, page 3
    New EMF Model wizard, page 3
  9. In the next screen, shown in Figure 10, click Select All to select all Root packages in the table. Click Finish to create the EMF Gen Model.
    Figure 10. New EMF Model wizard, page 4
    New EMF Model wizard, page 4
    You should be returned to the workbench, and the EMF Gen Model editor should be open on "My.genmodel".
  10. Right click on My Gen Model and select Set SDO defaults, then save the Gen Model.
  11. Right click on My Gen Model again and select Generate Model Code. Your EMF SDO classes should now exist under the org.example.model.claim package of your ClaimModel project's source directory, as shown in Figure 11 .
    Figure 11. Generated model code
    Generated model code

Now that you have generated the SDOs, it is time to import two utility classes into the ClaimModel project that handle most of the conversion work between SDOs and SOAPElements. Steps 1 and 2 associate a J2EE server with the Java project as a means to get classes such as javax.xml.soap.SOAPElement on the build path of the project. If you do not do this, the two classes imported in steps 3 and 4 will fail to compile.

  1. Right click on the ClaimModel project and choose Properties.
  2. Select "Server" and set the Target runtime to WebSphere Application Server v6.0, then click OK, as in Figure 12 .
    Figure 12. Set target server
    Set target server
  3. Import the following three Java files into package org.example.model.claim.util of the ClaimModel project:

    EMFSOAPElementUtil.java (see Listing 3). This class can be used as-is with any SDO model generated from any XML schema. It is not specific to our Claim example.

    ClaimSOAPElementUtil.java (see Listing 4). This class extends EMFSOAPElementUtil, and is specific to this example. If you apply this technique to other XML schemas and generated SDO models, you will need to provide your own version of ClaimSOAPElementUtil. As you will see, it's a very simple class.

    ClaimUtil.java (see Listing 5). Unlike the two classes just mentioned, this one does not contain conversion code for SOAPElements and SDOs. Rather, it contains a few handy business utility methods for the client and service portions of our Claim application. Now is as good a time as any to import it.


Listing 3. EMFSOAPElementUtil.java

package org.example.model.claim.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.sdo.impl.EDataObjectImpl;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.ibm.websphere.webservices.soap.IBMSOAPElement;
import com.ibm.websphere.webservices.soap.IBMSOAPFactory;

/**
 * This class takes care of EMF initialization and defines
 * two methods, serialize and deserialize, used to convert
 * between SOAPElement objects and DocumentRoot objects for
 * all SDOs generated from a single schema.
 */
public class EMFSOAPElementUtil
{
  private XMLResourceFactoryImpl factory;

  /**
   * Constructs a new utility class,
   * with the side effect of initializing EMF.
   * The XMLResourceFactoryImpl object is the factory responsible
   * for constructing the EMF XML resource objects that contain
   * the SDO models. The call to "getExtensionToFactoryMap"
   * registers the factory with the EMF runtime.
   * @param factory The EMF resource factory that will be
   * used to manage the serialization and deserialization
   * of DocumentRoot objects.
   */
  public EMFSOAPElementUtil ( XMLResourceFactoryImpl factory )
  {
    this.factory = factory;
    Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xml",factory);
  }

  /**
   * Serializes a DocumentRoot object to a SOAPElement.
   * @param document The EMF/SDO document to serialize.
   * @return A SOAPElement representation of the XML.
   * @throws IOException
   * @throws SOAPException
   */
  public SOAPElement serialize ( EDataObjectImpl document )
  throws IOException, SOAPException
  {
    XMLResourceImpl res = (XMLResourceImpl)factory.createResource(URI.createURI("*.xml"));
    res.getContents().add(document);
    res.getDefaultSaveOptions().put(XMLResource.OPTION_DECLARE_XML,Boolean.FALSE);
    res.setEncoding("UTF-8");
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    res.save(byteArrayOutputStream,null);
    byte[] buffer = byteArrayOutputStream.toByteArray();
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);
    byteArrayOutputStream.close();
    InputSource inputSource = new InputSource(byteArrayInputStream);
    IBMSOAPFactory soapFactory = (IBMSOAPFactory)SOAPFactory.newInstance();
    return soapFactory.createElementFromInputSource(inputSource);
  }

  /**
   * Deserializes a SOAPElement to a DocumentRoot object.
   * @param element The SOAPElement representation of the XML.
   * @return The resulting EMF/SDO document.
   * @throws IOException
   * @throws SAXException
   */
  public EDataObjectImpl deserialize ( SOAPElement element )
  throws IOException, SAXException
  {
    IBMSOAPElement ibmElement = (IBMSOAPElement)element;
    InputSource inputSource = ibmElement.toInputSource(true);
    InputStream inputStream = inputSource.getByteStream();
    XMLResourceImpl res = (XMLResourceImpl)factory.createResource(URI.createURI("*.xml"));
    res.load(inputStream,null);
    EDataObjectImpl document = (EDataObjectImpl)res.getContents().get(0);
    return document;
  }
}


Listing 4. ClaimSOAPElementUtil.java

package org.example.model.claim.util;

import org.example.model.claim.ClaimPackage;

/**
 * This class takes care of EMF initialization and defines
 * two methods, serialize and deserialize, used to convert
 * between SOAPElement objects and DocumentRoot objects for
 * all SDOs generated from one or more specific schemas.
 */
public class ClaimSOAPElementUtil extends EMFSOAPElementUtil
{
  /**
   * Constructs a new utility class
   * with the side effect of initializing EMF.
   * The "super" call registers our XML resource factory with
   * the EMF runtime (see the EMFSOAPElementUtil constructor),
   * then initializes the "Package" metadata for the Claim Schema.
   * The "Claim" example is based on an XML Schema from one namespace.
   * The "ResourceFactoryImpl" class for any one Schema namespace
   * will actually handle XML resource construction for any Schema
   * namespace, so if you generate EMF SDOs for XML Schema documents
   * from several target namespaces, you can choose any ONE of the
   * Schema "ResourceFactoryImpl" classes to construct and pass up to
   * EMFSOAPElementUtil. The "Package" classes for all the Schema
   * namespaces must, however, all be initialized.
   */
  public ClaimSOAPElementUtil ()
  {
    // Register an XML resource factory with the EMF runtime.
    // Any one Schema's resource factory will handle all Schemas.
    super(new ClaimResourceFactoryImpl());

    // Initialize the EMF package for the "Claim" schema.
    // You need one line like this per Schema.
    ClaimPackage pkg = ClaimPackage.eINSTANCE;
  }
}


Listing 5. ClaimUtil.java

package org.example.model.claim.util;

import org.example.model.claim.Address;
import org.example.model.claim.Benefit;
import org.example.model.claim.Claim;
import org.example.model.claim.ClaimFactory;
import org.example.model.claim.ClaimResponse;
import org.example.model.claim.Denial;
import org.example.model.claim.Method;
import org.example.model.claim.Payment;
import org.example.model.claim.Person;
import org.example.model.claim.Reason;
import org.example.model.claim.Receipt;

/**
 * This example application class has handy methods for
 * creating new claims, process claims and returning
 * claim responses, and printing out the details of
 * claim and claim response SDO data graphs.
 */
public class ClaimUtil
{
  private static int nextClaimNumber = 1;

  /**
   * Processes a Claim and returns a ClaimResponse.
   * Claims over $500 are rejected. Claims under $500
   * are paid at 80% of the submitted cost.
   * @param claim The claim to process.
   * @return How the claim was processed.
   */
  public static ClaimResponse process ( Claim claim )
  {
    // The EMF SDO tools generate a "Factory" class for
    // each XML Schema with factory methods for creating
    // new instances of the various SDO objects.
    // "ClaimFactory" is the factory for the "Claim" schema.

    ClaimFactory claimFactory = ClaimFactory.eINSTANCE;

    // Create a new "Benefit" object followed by either a
    // "Denial" or a "Payment" based on the cost of the claim.
    // Set the attributes on the new objects.
    
    Benefit benefit = claimFactory.createBenefit();
    if (claim.getReceipt().getCost() > 500.00)
    {
      Denial denial = claimFactory.createDenial();
      denial.setReceiptNumber(claim.getReceipt().getReceiptNumber());
      denial.setReason(Reason.INADEQUATE_COVERAGE_LITERAL);
      denial.setComment("Your policy does not cover procedures that expensive.");
      benefit.setDenial(denial);
    }
    else
    {
      Payment payment = claimFactory.createPayment();
      payment.setReceiptNumber(claim.getReceipt().getReceiptNumber());
      payment.setAmount(claim.getReceipt().getCost() * 0.8);
      payment.setMethod(Method.CHEQUE_LITERAL);
      benefit.setPayment(payment);
    }

    // Create the "ClaimResponse" and set its attributes.

    ClaimResponse claimResponse = claimFactory.createClaimResponse();
    claimResponse.setBenefit(benefit);
    claimResponse.setClaimNumber(nextClaimNumber++);
    claimResponse.setPolicyNumber(claim.getPolicyNumber());

    return claimResponse;
  }

  /**
   * Creates a new Claim for the given receipt number,
   * description and cost. The policy number, person and
   * person's address are determined within the method.
   * @param number The receipt number to file.
   * @param description A description of the claim.
   * @param cost The cost of the expense being claim.
   * @return A new claim.
   */
  public static Claim newClaim ( int receiptNumber, String description, double cost )
  {
    // The EMF SDO tools generate a "Factory" class for
    // each XML Schema with factory methods for creating
    // new instances of the various SDO objects.
    // "ClaimFactory" is the factory for the "Claim" schema.

    ClaimFactory claimFactory = ClaimFactory.eINSTANCE;

    // Create a "Claim" and the "Person", "Address" and
    // "Receipt" objects within it.
    // Set the attributes on the new objects.

    Receipt receipt = claimFactory.createReceipt();
    receipt.setReceiptNumber(receiptNumber);
    receipt.setDescription(description);
    receipt.setCost(cost);

    Address address = claimFactory.createAddress();
    address.setProvince("Ontario");
    address.setCity("Markham");
    address.setStreet("8200 Warden Ave");
    address.setPostalCode("L6G1C7");

    Person person = claimFactory.createPerson();
    person.setFirst("Joe");
    person.setLast("Bloe");

    Claim claim = claimFactory.createClaim();
    claim.setReceipt(receipt);
    claim.setAddress(address);
    claim.setPerson(person);
    claim.setPolicyNumber(123);

    return claim;
  }

  /**
   * Writes the details of a Claim to stdout.
   * @param claim The claim to print.
   */
  public static void printClaim ( Claim claim )
  {
    System.out.println("Claim");
    System.out.println("- Policy:  #"
                      +claim.getPolicyNumber());
    System.out.println("- Person:  "
                      +claim.getPerson().getFirst()+" "
                      +claim.getPerson().getLast());
    System.out.println("- Address: "
                      +claim.getAddress().getStreet()+", "
                      +claim.getAddress().getCity()+", "
                      +claim.getAddress().getProvince()+" "
                      +claim.getAddress().getPostalCode());
    System.out.println("- Receipt: #"
                      +claim.getReceipt().getReceiptNumber()+" - "
                      +claim.getReceipt().getDescription()+" - $"
                      +claim.getReceipt().getCost());
  }

  /**
   * Writes the details of a ClaimResponse to stdout.
   * @param claimResponse The claim response to print.
   */
  public static void printClaimResponse ( ClaimResponse claimResponse )
  {
    System.out.println("Claim Response");
    System.out.println("- Policy:  #"+claimResponse.getPolicyNumber());
    System.out.println("- Claim:   #"+claimResponse.getClaimNumber());
    Denial denial = claimResponse.getBenefit().getDenial();
    Payment payment = claimResponse.getBenefit().getPayment();
    if (denial != null)
    {
      System.out.println("- Denial:  #"
                        +denial.getReceiptNumber()+" - "
                        +denial.getReason()+" - "
                        +denial.getComment());
    }
    if (payment != null)
    {
      System.out.println("- Payment: #"
                        +payment.getReceiptNumber()+" - "
                        +payment.getMethod()+" - $"
                        +payment.getAmount());
    }
  }

  /**
   * Example program to exercise the above static methods.
   * Notice the similarity to org.example.webservice.Main in
   * the "ClaimClient" Web project.
   * @param av Not applicable to this program.
   */
  public static void main (String[] av)
  {
    Claim claim1 = ClaimUtil.newClaim(1,"Root canal surgery",800.00);
    ClaimUtil.printClaim(claim1);
    ClaimResponse response1 = ClaimUtil.process(claim1);
    ClaimUtil.printClaimResponse(response1);

    Claim claim2 = ClaimUtil.newClaim(2,"Wooden teeth",125.00);
    ClaimUtil.printClaim(claim2);
    ClaimResponse response2 = ClaimUtil.process(claim2);
    ClaimUtil.printClaimResponse(response2);
  }
}

Part 2: Generate the web service skeleton code

In this part we will generate a Java skeleton service class from Claim.wsdl with methods that strictly use SOAPElements.

  1. If you have not done so already, open Window > Preferences > Web Services > Code Generation > IBM WebSphere Runtime, select Disable data binding and use SOAPElement, then click OK, as in Figure 13 .
    Figure 13. Disable data binding and use SOAPElement
    Disable data binding and use SOAPElement
  2. Open File > New > Other... > Web Services > Web Service.
    1. Choose to generate a Skeleton Java bean web service.
    2. Disable the Generate a proxy, Test the web service, and Monitor the web service options, as shown in Figure 14 .
    3. Click Next.

    Figure 14. Skeleton web service wizard
    Skeleton web service wizard
  3. Browse to and select the Claim.wsdl document in the ClaimModel project as shown in Figure 15 then click Next.
    Figure 15. Skeleton web service wizard, page 2
    Skeleton web service wizard, page 2
  4. Verify that the runtime, server and J2EE level are set to IBM WebSphere, WebSphere Application Server v6.0, and 1.4, respectively. If they are not, click Edit... and update them to these values.
  5. Enter "ClaimService" for the service project and "ClaimServiceEAR" for the EAR project, as shown in Figure 16 . Click Finish.
    Figure 16. Skeleton web service wizard, page 3
    Skeleton web service wizard, page 3

The wizard may take some time to complete since it may need to create and start a new instance of WebSphere Application Server. When the wizard is done, ClaimBindingImpl.java should be automatically opened in the Java editor.

Notice that ClaimBindingImpl.java has one business method, processClaim, which accepts and returns a javax.xml.soap.SOAPElement. If you wanted to, you could stop here and implement your business logic in the skeleton directly in terms of SOAPElements. As it turns out, we are about to implement these methods in the next part with the help of our SDOs, so leave ClaimBindingImpl.java open in the editor for now.

Part 3: Adapt the Web Service Skeleton to use SDOs

In this part we will create a new skeleton class whose methods use SDOs instead of SOAPElements, then write a bit of glue code in the generated skeleton to delegate to our new SDO skeleton. First, however, we need to add the "ClaimModel" project containing our SDOs and utility classes as a J2EE dependency of the "ClaimService" project.

  1. Open the Application Deployment Descriptor editor for ClaimServiceEAR by double-clicking on /ClaimServiceEAR/META-INF/application.xml.
  2. Select the Module tab. Under Project Utility JARs, click Add... and select ClaimModel, then save and close the editor.
    Figure 17. Add ClaimModel Module to ClaimServiceEAR
    Add ClaimModel Module to ClaimServiceEAR
  3. Right click on the ClaimService project and choose Properties.
  4. Select Java JAR Dependencies. In the table under "Available Dependent JARs", select the entry for ClaimModel.jar and click OK.
    Figure 18. Add Java JAR Dependencies
    Add Java JAR Dependencies
  5. Create ClaimBindingImplSDO.java from scratch as follows, import it in package org.example.webservice of the "ClaimService" project:
    1. Right click on ClaimBindingImpl.java and choose Copy.
    2. Right click on org.example.webservice and choose Paste. In the resulting Name Conflict dialog, change the name to "ClaimBindingImplSDO".
    3. Right click on ClaimBindingImplSDO.java and choose Open to open the Java editor.
    4. Delete "implements org.example.webservice.ClaimPortType" from the public class declaration.
    5. Change the return type of the "processClaim" method to "ClaimResponse". "ClaimResponse" is the Java class that was generated by the EMF SDO tools for the "ClaimResponse" complex type of the "claimResponse" element referenced by the "processClaimResponse" message part from Claim.wsdl, in Listing 2.
    6. Change the type of the method's "claim" parameter to "Claim". "Claim" is the Java class that was generated by the EMF SDO tools for the "Claim" complex type of the "claim" element referenced by the "processClaimRequest" message part from Claim.wsdl, in Listing 2.
    7. This is a good time to fill in our business logic. To do so, replace the method body "return null;" with the following:
      ClaimUtil.printClaim(claim);
      ClaimResponse claimResponse = ClaimUtil.process(claim);
      ClaimUtil.printClaimResponse(claimResponse);
      return claimResponse;
      

    8. Type Ctrl-Shift-O to organize imports.
    9. Save the file.
  6. Open or switch to ClaimBindingImpl.java in the Java editor and update the implementation of the file as follows, (See Downloads):
    1. Just above the "processClaim" method, insert these two lines of code:
      private ClaimBindingImplSDO claimBindingImplSDO = new ClaimBindingImplSDO();
      private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil();
      

    2. In the "processClaim" method body, replace "return null;" with the following:
      try
      {
        DocumentRoot claimRoot = (DocumentRoot)util.deserialize(claim);
        Claim claimSDO = claimRoot.getClaim();
        ClaimResponse claimResponseSDO = claimBindingImplSDO.processClaim(claimSDO);
        DocumentRoot claimResponseRoot = ClaimFactory.eINSTANCE.createDocumentRoot();
        claimResponseRoot.setClaimResponse(claimResponseSDO);
        SOAPElement claimResponse = util.serialize((EDataObjectImpl)claimResponseRoot);
        return claimResponse;
      }
      catch (Exception e)
      {
        return null;
      }
      

    3. Type Ctrl-Shift-O to organize imports. If given a choice of differently packaged SOAPElements, choose javax.xml.soap.SOAPElement.
    4. Save the file.

Additional formatting and documentation notwithstanding, the final SDO skeleton file should look something like the code shown in Listing 6. It features a business method that uses EMF SDO objects instead of SOAPElements, leaving the programmer free to focus on the task of writing business logic instead of processing raw SOAP message XML.

Listing 6. The SDO skeleton

package org.example.webservice;

import org.example.model.claim.Claim;
import org.example.model.claim.ClaimResponse;
import org.example.model.claim.util.ClaimUtil;

/**
 * This is the SDO facade skeleton. It has the same business
 * methods as ClaimBindingImpl (generated), only taking and
 * returning SDO objects instead of SOAPElements.
 */
public class ClaimBindingImplSDO
{
  /**
   * Processes a claim. See ClaimUtil.process().
   * SOAPElement to a claim response SDO.
   * @param claim The claim SDO to process.
   * @return The claim response SDO.
   * @throws java.rmi.RemoteException If a system exception occurs.
   */
  public ClaimResponse processClaim(Claim claim) throws java.rmi.RemoteException
  {
    ClaimUtil.printClaim(claim);
    ClaimResponse claimResponse = ClaimUtil.process(claim);
    ClaimUtil.printClaimResponse(claimResponse);
    return claimResponse;
  }
}

The final generated skeleton file should look something like the code shown in Listing 7. The business method contains the hand-crafted glue code that converts the method's SOAPElements to SDOs, invokes the above SDO skeleton or business service class, then converts the resulting SDO back to a SOAPElement.

Listing 7. The generated skeleton

/**
 * ClaimBindingImpl.java
 *
 * This file was auto-generated from WSDL
 * by the IBM web services WSDL2Java emitter.
 * o0516.14 v42205183324
 */

package org.example.webservice;

import javax.xml.soap.SOAPElement;

import org.eclipse.emf.ecore.sdo.impl.EDataObjectImpl;
import org.example.model.claim.util.ClaimSOAPElementUtil;
import org.example.model.claim.Claim;
import org.example.model.claim.ClaimFactory;
import org.example.model.claim.ClaimResponse;
import org.example.model.claim.DocumentRoot;

/**
 * This is the generated skeleton with business methods
 * in terms of SOAPElements and implemented to delegate
 * to the SDO facade skeleton, ClaimBindingImplSDO.
 * Like ClaimPortTypeProxySDO, this class uses
 * ClaimSOAPElementUtil to convert from SDOs to
 * SOAPElements and vice versa.
 */
public class ClaimBindingImpl implements org.example.webservice.ClaimPortType
{
  private ClaimBindingImplSDO claimBindingImplSDO = new ClaimBindingImplSDO();
  private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil();

  /**
   * Processes a claim by deserializing the given SOAPElement
   * to a claim SDO, delegating to ClaimBindingImplSDO's
   * "processClaim" method, then serializing the returned
   * claim response SDO to a SOAPElement.
   * @param claim The claim SOAPElement to process.
   * @return The claim response SOAPElement.
   * @throws java.rmi.RemoteException If a system exception occurs.
   */
  public javax.xml.soap.SOAPElement processClaim(javax.xml.soap.SOAPElement claim)
  throws java.rmi.RemoteException
  {
    try
    {
      // First, convert the input parameter from a SOAPElement to an SDO
      // with the help of ClaimSOAPElementUtil/EMFSOAPElementUtil.

      DocumentRoot claimRoot = (DocumentRoot)util.deserialize(claim);
      Claim claimSDO = claimRoot.getClaim();

      // Second, delegate to the SDO skeleton.

      ClaimResponse claimResponseSDO = claimBindingImplSDO.processClaim(claimSDO);

      // Third and last, convert the returned SDO to a SOAPElement
      // with the help of ClaimSOAPElementUtil/EMFSOAPElementUtil.

      DocumentRoot claimResponseRoot = ClaimFactory.eINSTANCE.createDocumentRoot();
      claimResponseRoot.setClaimResponse(claimResponseSDO);
      SOAPElement claimResponse = util.serialize((EDataObjectImpl)claimResponseRoot);
      return claimResponse;
    }
    catch (Exception e)
    {
      return null;
    }
  }
}

Part 4: Generate the web service client code

In this part we will generate the Java proxy code from the deployed Claim.wsdl with methods that strictly use SOAPElements.

  1. If you have not done so already, open Window > Preferences > Web Services > Code Generation > IBM WebSphere Runtime, and select Disable data binding and use SOAPElement. Click OK. (See Figure 13).
  2. Open File > New > Other... > Web Services > Web Service Client. You should see the screen depicted in Figure 19.
    1. Choose to generate a Java proxy
    2. Disable the Test the web service and Monitor the web service options
    3. Click Next

    Figure 19. Web Service Client Wizard
    Web Service Client Wizard
  3. Browse to and select the Claim.wsdl document in the ClaimService project, as shown in Figure 20 , then click Next. Do not select the Claim.wsdl document in ClaimModel, since it does not contain the endpoint URL of the deployed service.
    Figure 20. Web Service Client wizard, page 2
    Web Service Client wizard, page 2
  4. Verify that the runtime, server and J2EE level are set to IBM WebSphere, WebSphere Application Server v6.0, and 1.4, respectively. If they are not, click Edit... and update them to these values.
  5. Choose a client type of Application Client. Enter "ClaimClient" for the client project, and "ClaimClientEAR" for the EAR project, then click Finish, as in Figure 21.
    Figure 21. Web Service Client wizard, page 3
    Web Service Client wizard, page 3

Open ClaimPortTypeProxy.java in the Java editor and notice that its one business method, processClaim, accepts and returns a javax.xml.soap.SOAPElement. If you want, you can stop here and implement your business application in terms of this proxy class and its SOAPElements. We will, however, use a different technique in the next part that exploits our SDOs.

Part 5: Adapt the web service client to use SDOs

In this part we will create a new proxy class whose methods use SDOs instead of SOAPElements. Then we will write a bit of glue code in the new proxy class to delegate to the generated SOAPElement proxy class. First we need to add the ClaimModel project containing our SDOs and utility classes as a J2EE dependency of the ClaimClient project.

  1. Open the Application Deployment Descriptor editor for ClaimClientEAR by double-clicking on /ClaimClientEAR/META-INF/application.xml, as in Figure 22.
  2. Select the Module tab. Under Project Utility JARs, click Add... and select ClaimModel. Save your work, and close the editor.
    Figure 22. Add ClaimModel Module to ClaimServiceEAR
    Add ClaimModel Module to ClaimServiceEAR
  3. Right click on the ClaimClient project and choose Properties, as in Figure 23.
  4. Select Java JAR Dependencies. In the table under Available Dependent JARs, select the entry for ClaimModel.jar. Click OK.
    Figure 23. Add Java JAR Dependencies
    Add Java JAR Dependencies
  5. Create ClaimPortTypeProxySDO.java as follows (or import it) in package org.example.webservice of the ClaimClient project:
    1. Right-click on ClaimPortTypeProxy.java and choose Copy.
    2. Right-click on org.example.webservice and choose Paste. In the resulting Name Conflict dialog box, change the name to "ClaimPortTypeProxySDO".
    3. Right-click on ClaimPortTypeProxySDO.java and choose Open to open the Java editor.
    4. Delete "implements org.example.webservice.ClaimPortType" from the public class declaration.
    5. Replace all code between the "public class ClaimPortTypeProxySDO {" and the processClaim method with the following:
      private ClaimPortTypeProxy proxy = new ClaimPortTypeProxy();
      private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil();
      
      public ClaimPortTypeProxy getClaimPortTypeProxy()
      {
        return proxy;
      }
      

    6. Change the return type of the "processClaim" method to "ClaimResponse". ClaimResponse is the Java class that was generated by the EMF SDO tools for the ClaimResponse complex type of the claimResponse element referenced by the processClaimResponse message part from Claim.wsdl, in Listing 2..
    7. Change the type of the method's "claim" parameter to "Claim". Claim is the Java class that was generated by the EMF SDO tools for the Claim complex type of the claim element referenced by the processClaimRequest message part from Claim.wsdl, in Listing 2.
    8. Replace the body of the processClaim method with the following:
      try
      {
        DocumentRoot claimRoot = ClaimFactory.eINSTANCE.createDocumentRoot();
        claimRoot.setClaim(claim);
        SOAPElement claimSE = util.serialize((EDataObjectImpl)claimRoot);
        SOAPElement claimResponseSE = proxy.processClaim(claimSE);
        DocumentRoot claimResponseRoot = (DocumentRoot)util.deserialize(claimResponseSE);
        ClaimResponse claimResponse = claimResponseRoot.getClaimResponse();
        return claimResponse;
      }
      catch (Exception e)
      {
        return null;
      }
      

    9. Type Ctrl-Shift-O to organize imports. If given a choice of differently packaged SOAPElements, choose javax.xml.soap.SOAPElement.
    10. Save the file.

Additional formatting and documentation notwithstanding, the final SDO proxy file should look something like the code shown in Listing 8. It features a proxy business method that uses EMF SDO objects instead of SOAPElements, leaving the application programmer free to focus on the task of writing the business logic that needs the web services instead of processing raw SOAP message XML.

Listing 8. The SDO proxy

package org.example.webservice;

import javax.xml.soap.SOAPElement;

import org.eclipse.emf.ecore.sdo.impl.EDataObjectImpl;
import org.example.model.claim.util.ClaimSOAPElementUtil;
import org.example.model.claim.Claim;
import org.example.model.claim.ClaimFactory;
import org.example.model.claim.ClaimResponse;
import org.example.model.claim.DocumentRoot;

/**
 * This is the SDO facade proxy. It has the same business
 * methods as ClaimPortTypeProxy (generated), only taking
 * and returning SDO objects instead of SOAPElements.
 * Like ClaimBindingImpl, this class uses
 * ClaimSOAPElementUtil to convert from SDOs to
 * SOAPElements and vice versa.
 */
public class ClaimPortTypeProxySDO
{
  private ClaimPortTypeProxy proxy = new ClaimPortTypeProxy();
  private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil();

  /**
   * Returns the generated ClaimPortTypeProxy class
   * in case any of its utility methods are needed.
   * @return The ClaimPortTypeProxy.
   */
  public ClaimPortTypeProxy getClaimPortTypeProxy()
  {
    return proxy;
  }

  /**
   * Processes a claim by serializing the given claim SDO
   * to a SOAPElement, delegating to ClaimPortTypeProxy's
   * "processClaim" method, then deserializing the returned
   * SOAPElement to a claim response SDO.
   * @param claim The claim SDO to process.
   * @return The claim response SDO.
   * @throws java.rmi.RemoteException If a system exception occurs.
   */
  public ClaimResponse processClaim(Claim claim) throws java.rmi.RemoteException
  {
    try
    {
      // First, convert the input parameter from an SDO to a SOAPElement
      // with the help of ClaimSOAPElementUtil/EMFSOAPElementUtil.

      DocumentRoot claimRoot = ClaimFactory.eINSTANCE.createDocumentRoot();
      claimRoot.setClaim(claim);
      SOAPElement claimSE = util.serialize((EDataObjectImpl)claimRoot);

      // Second, delegate to the generated proxy.

      SOAPElement claimResponseSE = proxy.processClaim(claimSE);

      // Third and last, conver the returned SOAPElement to an SDO
      // with the help of ClaimSOAPElementUtil/EMFSOAPElementUtil.

      DocumentRoot claimResponseRoot = (DocumentRoot)util.deserialize(claimResponseSE);
      ClaimResponse claimResponse = claimResponseRoot.getClaimResponse();
      return claimResponse;
    }
    catch (Exception e)
    {
      return null;
    }
  }
}

Part 6: Implement and test your service

In this part, we import a Main.java test program and run it.

Listing 9 below shows a simple Java application, Main.java, that you can use to test the web service application.

  1. Download Main.java into package org.example.webservice of the ClaimClient project.
  2. Run webservice.Main.
  3. In the console view for the Main application you should see the output shown in Listing 10. If you switch the console view to WebSphere Application Server v6.0, you should see the same output, albeit prefixed by Application Server dates, codes, and other verbiage.


Listing 9. The testcase, Main.java

package org.example.webservice;

import org.example.model.claim.util.ClaimUtil;
import org.example.model.claim.Claim;
import org.example.model.claim.ClaimResponse;

/**
 * This is a simple example of a program that calls a
 * business method on the SDO facade proxy. It calls
 * the "processClaim" method to process two claims,
 * one that will be denied, and one that will be paid.
 * Under normal circumstances this program will contact
 * the "Claim" web service directly. Optionally, you can
 * run this program with an alternate endpoint URL, such
 * as to a TCP/IP monitor running in front of the service.
 */
public class Main
{
  /**
   * The "main" program.
   * @param av The arguments to the program. Usage:
   * "org.example.webservice.Main [endpoint]"
   * where "endpoint" is an optional endpoint URL for the
   * client to contact instead of the default endpoint URL
   * from the service WSDL document.
   */
  public static void main (String[] av)
  {
    try
    {
      // Get the SDO facade proxy.
      // Set the endpoint if one was given.
      ClaimPortTypeProxySDO proxy = new ClaimPortTypeProxySDO();
      if (av.length >= 1) proxy.getClaimPortTypeProxy().setEndpoint(av[0]);
      System.out.println("Endpoint: "+proxy.getClaimPortTypeProxy().getEndpoint());

      // Create a new "Claim" and call the "processClaim" business method.
      // This claim is over $500 and should be denied by the service.
      Claim claim1 = ClaimUtil.newClaim(1,"Root canal surgery",800.00);
      ClaimUtil.printClaim(claim1);
      ClaimResponse response1 = proxy.processClaim(claim1);
      ClaimUtil.printClaimResponse(response1);

      // Create a new "Claim" and call the "processClaim" business method.
      // This claim is under $500 and should be paid by the service.
      Claim claim2 = ClaimUtil.newClaim(2,"Wooden teeth",125.00);
      ClaimUtil.printClaim(claim2);
      ClaimResponse response2 = proxy.processClaim(claim2);
      ClaimUtil.printClaimResponse(response2);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}


Listing 10. console Output of the "Main" test program.

Endpoint: http://localhost:9080/ClaimService/services/ClaimPort
Claim
- Policy:  #123
- Person:  Joe Bloe
- Address: 8200 Warden Ave, Markham, Ontario L6G1C7
- Receipt: #1 - Root canal surgery - $800.0
Claim Response
- Policy:  #123
- Claim:   #1
- Denial:  #1 - InadequateCoverage - Your policy does not cover procedures that expensive.
Claim
- Policy:  #123
- Person:  Joe Bloe
- Address: 8200 Warden Ave, Markham, Ontario L6G1C7
- Receipt: #2 - Wooden teeth - $125.0
Claim Response
- Policy:  #123
- Claim:   #2
- Payment: #2 - Cheque - $100.0


Conclusions

When the complexity of an XML schema exceeds the boundaries of JAX-RPC and the runtimes and tools that comply with it, you can use other first-class bindings of XML Schema into Java. SOAPElements and EMF SDO types in particular yield a powerful fusion of standard and open source technologies.

While this procedure is fairly specific to the use of the EMF SDO programming model, the basic technique described here can be altered for use with other Java programming models for XML Schema such as JAX-B. The concepts in this article can also be applied to other web service runtimes and application servers.


Appendices

Inventory of Java Files

Table 1 summarizes the key packages and classes that you will find in the ClaimModel, ClaimService, and ClaimClient projects.


Table 1. Classes in the Claim projects
ClaimModel Project Packages and ClassesGenerated or EditedRemarks
org.example.model.claim.*GeneratedSDO business interfaces, courtesy the Eclipse EMF SDO tools.
org.example.model.claim.impl.*GeneratedPrivate SDO implementations, courtesy the Eclipse EMF SDO tools.
org.example.model.claim.util.*GeneratedExcept for the next three, utility classes from the Eclipse EMF SDO tools.
org.example.model.claim.util.EMFSOAPElementUtilEditedGeneric converter between SOAPElements and DocumentRoots for any XML Schema and set of SDOs.
org.example.model.claim.util.ClaimSOAPElementUtilEditedExtends and initializes EMFSOAPElementUtil for the specific XML Schema(s) and SDOs in use.
org.example.model.claim.util.ClaimUtilEditedA handy application-specific utility class for the "Claim" example.
ClaimService Project Packages and ClassesGenerated or EditedRemarks
org.example.webservice.ClaimBindingImplBothSkeleton class generated by the web service wizard then implemented by you. Method signatures are in terms of SOAPElements. Hand-written method bodies are fairly simple, leaving the worst of the conversion work to ClaimSOAPElementUtil and EMFSOAPElementUtil. The equivalent to this class in the ClaimClient project is ClaimPortTypeProxySDO.
org.example.webservice.ClaimBindingImplSDOEditedHand-written class based on ClaimBindingImpl, only with SDOs instead of SOAPElements as parameter and return types on the business methods. Method bodies contain business logic.
org.example.webservice.ClaimPortTypeGeneratedJAX-RPC SEI generated by the web service wizard.
ClaimClient Project Packages and ClassesGenerated or EditedRemarks
org.example.webservice.ClaimBindingStubGeneratedWebSphere SEI Stub class generated by the web service wizard.
org.example.webservice.ClaimPortTypeGeneratedJAX-RPC SEI generated by the web service wizard.
org.example.webservice.ClaimPortTypeProxyGeneratedProxy class generated by the web service wizard.
org.example.webservice.ClaimPortTypeProxySDOEditedHand-written class based on ClaimPortTypeProxy, only with SDOs instead of SOAPElements as parameter and return types on the business methods. Hand-written method modies are fairly simple, leaving the worst of the conversion work to ClaimSOAPElementUtil and EMFSOAPElementUtil. The equivalent to this class in the ClaimService project is ClaimBindingImpl.
org.example.webservice.ClaimServiceGeneratedJAX-RPC SI generated by the web service wizard.
org.example.webservice.ClaimServiceInformationGeneratedWebSphere class generated by the web service wizard.
org.example.webservice.ClaimServiceLocatorGeneratedWebSphere Locator class implementation of the SI generated by the web service wizard.
org.example.webservice.MainEditedOur garden variety test application.

SOAP Messages

Listings 11 through 14 show the SOAP messages sent and received between the client and the service during the execution of Main.java:


Listing 11. The first "processClaim" request message.

<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <claim:claim xmlns:claim="http://model.example.org/Claim">
      <policyNumber>123</policyNumber>
      <person>
        <first>Joe</first>
        <last>Bloe</last>
      </person>
      <address>
        <province>Ontario</province>
        <city>Markham</city>
        <street>8200 Warden Ave</street>
        <postalCode>L6G1C7</postalCode>
      </address>
      <receipt>
        <receiptNumber>1</receiptNumber>
        <description>Root canal surgery</description>
        <cost>800.0</cost>
      </receipt>
    </claim:claim>
  </soapenv:Body>
</soapenv:Envelope>


Listing 12. The first "processClaim"response message.

<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <claim:claimResponse xmlns:claim="http://model.example.org/Claim">
      <policyNumber>123</policyNumber>
      <claimNumber>1</claimNumber>
      <benefit>
        <denial>
          <receiptNumber>1</receiptNumber>
          <reason>InadequateCoverage</reason>
          <comment>Your policy does not cover procedures that expensive.</comment>
        </denial>
      </benefit>
    </claim:claimResponse>
  </soapenv:Body>
</soapenv:Envelope>


Listing 13. The second "processClaim" request message.

<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <claim:claim xmlns:claim="http://model.example.org/Claim">
      <policyNumber>123</policyNumber>
      <person>
        <first>Joe</first>
        <last>Bloe</last>
      </person>
      <address>
        <province>Ontario</province>
        <city>Markham</city>
        <street>8200 Warden Ave</street>
        <postalCode>L6G1C7</postalCode>
      </address>
      <receipt>
        <receiptNumber>2</receiptNumber>
        <description>Wooden teeth</description>
        <cost>125.0</cost>
      </receipt>
    </claim:claim>
  </soapenv:Body>
</soapenv:Envelope>


Listing 14. The second "processClaim" response message.

<soapenv:Envelope
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <claim:claimResponse xmlns:claim="http://model.example.org/Claim">
      <policyNumber>123</policyNumber>
      <claimNumber>2</claimNumber>
      <benefit>
        <payment>
          <receiptNumber>2</receiptNumber>
          <amount>100.0</amount>
          <method>Cheque</method>
        </payment>
      </benefit>
    </claim:claimResponse>
  </soapenv:Body>
</soapenv:Envelope>

OAGIS®

As explained earlier, this article describes how to use Rational Application Developer to adapt JAX-RPC web services and clients for use with Service Data Objects. Reasons for wanting or needing to apply this technique in practice are varied, but one of the most compelling is, again, to support XML schemas that JAX-RPC does not handle so well.

The OAGIS® 9.0 XML Schema by the Open Applications Group, Inc. (OAGi) is an example of an XML schema that the EMF SDO tools and runtime are much more adept at handling than JAX-RPC. This article uses a relatively simple XML schema, Claim, as its example, chiefly to keep the XML schema itself from dominating the exercise. To further prove the concept, however, this technique was successfully applied to a web service application using a sample of the OAGIS 9.0 XML Schema.

Under Downloads is a WSDL document named "OAGISWSDLFile.wsdl" with two operations, "saveCreditStatus" and "getCreditStatus", crafted to use the OAGIS 9.0 "ProcessCreditStatus" element which in turn uses hundreds of types and elements from the OAGIS XML Schema. The technique described in this article was applied successfully to this WSDL.

The instructions and code snippets in this article guided us through the exercise of building a JAX-RPC / EMF SDO web service and client, however, please note the following points:

  1. You may find that the OAGIS 9.0 XML Schema does not validate cleanly in Rational Application Developer v.6. These validation errors do not interfere with the generation of the model and can be ignored.
  2. Some of the generated classes may yield compilation errors. These occur in a subset of EMF classes that provide document validation support. They are not relevant to the generated EMF SDO model itself or to the serialization or deserialization of the model by the EMF runtime, and do not interfere with tasks such as exporting enterprise projects for deployment to servers. The errors can be corrected by editing the EMF Gen Model, though the process for doing so is beyond the scope of this article.

To test the service, we fashioned a Java client application to call "saveCreditStatus" using SDOs representing an instance of a "ProcessCreditStatus" document, followed by "getCreditStatus" to show that there is no loss of data or integrity between the client and the service. In addition, SDOs for "saveCreditStatus" were created in two different ways:

  1. SDOs were created using the factory methods and setters of the EMF SDO programming model, similar to our "Claim" example, to model a fairly simple instance of "ProcessCreditStatus".
  2. SDOs were created by deserializing the much more complex "ProcessCreditStatus.xml" instance document as included in the OAGIS 9.0 download under ".../References/GeneratedBODInstances/".

The OAGIS 9.0 XML Schema is available from OAGi.



Downloads

DescriptionNameSizeDownload method
Claim web service EARClaimServiceEAR.ear100KBHTTP
Claim web service client EARClaimClientEAR.ear115KBHTTP
Claim Schema fileclaim.xsd3KBHTTP
Claim WSDL fileclaim.wsdl2KBHTTP
SOAPElement/DocumentRoot converterEMFSOAPElementUtil.java4KBHTTP
EMFSOAPElementUtil subclass for the Claim schemaClaimSOAPElementUtil.java2KBHTTP
Handy application utility methodsClaimUtil.java7KBHTTP
Implemented SOAPElement skeletonClaimBindingImpl.java2KBHTTP
Sample SDO serviceClaimBindingImplSDO.java1KBHTTP
Implemented SDO client proxyClaimPortTypeProxySDO.java2KBHTTP
Sample SDO Application ClientMain.java2KBHTTP
WSDL that uses OAGIS 9.0 XML schemaOAGISWSDLFile.wsdl2KBHTTP

Information about download methods


Resources

Learn

Get products and technologies

About the authors

Chris Brealey is the senior advisory development manager and technical team leader for Rational Java Web Services Tools at the IBM Toronto Lab, and a committer on the Eclipse Web Tools Platform (WTP) project. He is responsible for the architecture, design, and delivery of several of the tools for web services in IBM's Rational tools products. He has led the development of web services tools since 2000, with emphasis on standards and best practices for web services. Before working on web services, he was the technical lead on tools for IBM's implementation of CORBA. During his employment with IBM, he has focused on the theory, testing, and invention of development tools for a variety of distributed systems and protocols. Chris joined IBM as a full-time employee in 1989 shortly after graduating from the University of Victoria with a BSc in computer science.

Seng Phung-Lu is a staff software developer at the IBM Toronto Lab. He has contributed to each release of the web services tools in Rational Application Developer, and he is an active contributor to the web services component of the open-source Eclipse Web Tools Platform (WTP) project. His current areas of interest include application development and improving software quality, particularly software performance and usability.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services, Rational
ArticleID=104715
ArticleTitle=Developing Web Services with EMF SDOs for complex XML schema
publish-date=03032006
author1-email=cbrealey@ca.ibm.com
author1-email-cc=
author2-email=sengpl@ca.ibm.com
author2-email-cc=