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.
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.
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):
- 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
- 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:
- 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
- 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
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.
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.
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:
- 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.
- Relatively little code unique to the XML Schema or schemas from which the SDOs are generated.
- Relatively little code unique to each parameter or return type.
Figure 1 illustrates this pattern.
Figure 1. 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.
The procedure is broken into seven parts:
- Setup
- Generate the EMF SDO code
- Generate the web service skeleton code
- Adapt the web service skeleton to use SDOs
- Generate the web service client code
- Adapt the web service client to use SDOs
- 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:
- Select Window, then open Preferences > Workbench > Capabilities.
- Enable both Eclipse Developer and Web Service Developer capabilities, then click OK.
Figure 2. 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.
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:
- 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
- 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
- Select File > New > Project... > Java > Java Project to open the New Java Project wizard (Figure 3 ).
-
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
- 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".
- Right click on the XML Schema file and choose Generate > Java... to open the Generate Java dialog box, shown in Figure 4 .
-
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
- 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
- Select File > New > Project...> Eclipse Modeling Framework > EMF Project to open the EMF New Project wizard, shown in Figure 5 and Figure 6.
-
Enter "
ClaimModel" as the project name and click Next.
Figure 5. New EMF Project wizard
-
Select Create empty project, then click Finish.
Figure 6. New EMF Project wizard, page 2
- Import Claim.xsd and Claim.wsdl into the root of the project (though we will not actually need Claim.wsdl until Part 2).
- Select File > New > Other... > Eclipse Modelling Framework > EMF Models to open the EMF Models wizard, as illustrated in Figure 7 .
-
Select your new EMF project. Keep all other defaults and click Next.
Figure 7. New EMF Model wizard
-
Select Load from an XML Schema and click Next, as shown in Figure 8 .
Figure 8. New EMF Model wizard, page 2
- 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
-
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
You should be returned to the workbench, and the EMF Gen Model editor should be open on "My.genmodel". - Right click on My Gen Model and select Set SDO defaults, then save the Gen Model.
-
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
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.
- Right click on the ClaimModel project and choose Properties.
-
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
-
Import the following three Java files into package
org.example.model.claim.utilof 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;
}
}
|
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.
-
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
-
Open File > New > Other... > Web Services > Web Service.
- Choose to generate a Skeleton Java bean web service.
- Disable the Generate a proxy, Test the web service, and Monitor the web service options, as shown in Figure 14 .
- Click Next.
Figure 14. Skeleton web service wizard
-
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
- 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.
-
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
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.
-
Open the Application Deployment Descriptor editor for
ClaimServiceEARby double-clicking on/ClaimServiceEAR/META-INF/application.xml. -
Select the
Moduletab. Under Project Utility JARs, clickAdd...and selectClaimModel, then save and close the editor.
Figure 17. Add ClaimModel Module to ClaimServiceEAR
-
Right click on the
ClaimServiceproject and chooseProperties. -
Select
Java JAR Dependencies. In the table under "Available Dependent JARs", select the entry for ClaimModel.jar and clickOK.
Figure 18. Add Java JAR Dependencies
-
Create ClaimBindingImplSDO.java from scratch as follows, import it
in package
org.example.webserviceof the "ClaimService" project:- Right click on ClaimBindingImpl.java and choose Copy.
-
Right click on org.example.webservice and choose Paste.
In the resulting Name Conflict dialog, change the name
to "
ClaimBindingImplSDO". - Right click on ClaimBindingImplSDO.java and choose Open to open the Java editor.
-
Delete "
implements org.example.webservice.ClaimPortType" from the public class declaration. -
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. -
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. -
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;
-
Type
Ctrl-Shift-Oto organize imports. - Save the file.
-
Open or switch to ClaimBindingImpl.java in the Java editor
and update the implementation of the file as follows, (See Downloads):
-
Just above the "
processClaim" method, insert these two lines of code:private ClaimBindingImplSDO claimBindingImplSDO = new ClaimBindingImplSDO(); private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil();
-
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; } -
Type
Ctrl-Shift-Oto organize imports. If given a choice of differently packaged SOAPElements, choosejavax.xml.soap.SOAPElement. - Save the file.
-
Just above the "
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.
- 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).
-
Open File > New > Other... > Web Services > Web Service Client. You should see the screen depicted in Figure 19.
- Choose to generate a Java proxy
- Disable the Test the web service and Monitor the web service options
- Click Next
Figure 19. Web Service Client Wizard
-
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
- 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.
-
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
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.
-
Open the Application Deployment Descriptor editor for ClaimClientEAR
by double-clicking on
/ClaimClientEAR/META-INF/application.xml, as in Figure 22. -
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
-
Right click on the ClaimClient project and choose
Properties, as in Figure 23. -
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
-
Create ClaimPortTypeProxySDO.java as follows
(or import it)
in package
org.example.webserviceof the ClaimClient project:- Right-click on ClaimPortTypeProxy.java and choose Copy.
-
Right-click on org.example.webservice and choose Paste.
In the resulting Name Conflict dialog box, change the name
to "
ClaimPortTypeProxySDO". - Right-click on ClaimPortTypeProxySDO.java and choose Open to open the Java editor.
-
Delete "
implements org.example.webservice.ClaimPortType" from the public class declaration. -
Replace all code between the
"
public class ClaimPortTypeProxySDO {" and theprocessClaimmethod with the following:private ClaimPortTypeProxy proxy = new ClaimPortTypeProxy(); private ClaimSOAPElementUtil util = new ClaimSOAPElementUtil(); public ClaimPortTypeProxy getClaimPortTypeProxy() { return proxy; } -
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.. -
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. -
Replace the body of the
processClaimmethod 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; } -
Type
Ctrl-Shift-Oto organize imports. If given a choice of differently packaged SOAPElements, choosejavax.xml.soap.SOAPElement. - 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.
- Download Main.java into
package
org.example.webserviceof the ClaimClient project. - Run webservice.Main.
- 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 |
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.
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 Classes | Generated or Edited | Remarks |
|---|---|---|
| org.example.model.claim.* | Generated | SDO business interfaces, courtesy the Eclipse EMF SDO tools. |
| org.example.model.claim.impl.* | Generated | Private SDO implementations, courtesy the Eclipse EMF SDO tools. |
| org.example.model.claim.util.* | Generated | Except for the next three, utility classes from the Eclipse EMF SDO tools. |
| org.example.model.claim.util.EMFSOAPElementUtil | Edited | Generic converter between SOAPElements and DocumentRoots for any XML Schema and set of SDOs. |
| org.example.model.claim.util.ClaimSOAPElementUtil | Edited | Extends and initializes EMFSOAPElementUtil for the specific XML Schema(s) and SDOs in use. |
| org.example.model.claim.util.ClaimUtil | Edited | A handy application-specific utility class for the "Claim" example. |
| ClaimService Project Packages and Classes | Generated or Edited | Remarks |
| org.example.webservice.ClaimBindingImpl | Both | Skeleton 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.ClaimBindingImplSDO | Edited | Hand-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.ClaimPortType | Generated | JAX-RPC SEI generated by the web service wizard. |
| ClaimClient Project Packages and Classes | Generated or Edited | Remarks |
| org.example.webservice.ClaimBindingStub | Generated | WebSphere SEI Stub class generated by the web service wizard. |
| org.example.webservice.ClaimPortType | Generated | JAX-RPC SEI generated by the web service wizard. |
| org.example.webservice.ClaimPortTypeProxy | Generated | Proxy class generated by the web service wizard. |
| org.example.webservice.ClaimPortTypeProxySDO | Edited | Hand-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.ClaimService | Generated | JAX-RPC SI generated by the web service wizard. |
| org.example.webservice.ClaimServiceInformation | Generated | WebSphere class generated by the web service wizard. |
| org.example.webservice.ClaimServiceLocator | Generated | WebSphere Locator class implementation of the SI generated by the web service wizard. |
| org.example.webservice.Main | Edited | Our garden variety test application. |
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>
|
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:
- 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.
- 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:
-
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". - 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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Claim web service EAR | ClaimServiceEAR.ear | 100KB | HTTP |
| Claim web service client EAR | ClaimClientEAR.ear | 115KB | HTTP |
| Claim Schema file | claim.xsd | 3KB | HTTP |
| Claim WSDL file | claim.wsdl | 2KB | HTTP |
| SOAPElement/DocumentRoot converter | EMFSOAPElementUtil.java | 4KB | HTTP |
| EMFSOAPElementUtil subclass for the Claim schema | ClaimSOAPElementUtil.java | 2KB | HTTP |
| Handy application utility methods | ClaimUtil.java | 7KB | HTTP |
| Implemented SOAPElement skeleton | ClaimBindingImpl.java | 2KB | HTTP |
| Sample SDO service | ClaimBindingImplSDO.java | 1KB | HTTP |
| Implemented SDO client proxy | ClaimPortTypeProxySDO.java | 2KB | HTTP |
| Sample SDO Application Client | Main.java | 2KB | HTTP |
| WSDL that uses OAGIS 9.0 XML schema | OAGISWSDLFile.wsdl | 2KB | HTTP |
Information about download methods
Learn
-
Visit the
W3C Architecture Domain web site
for XML Schema or have a look at
XML Schema Part 0: Primer
to learn more about XML Schema.
-
To learn more about WSDL and SOAP,
read the WSDL 1.1
and SOAP 1.1
W3C Notes.
-
What does it mean to be WS-I compliant? Visit the
Web Services Interoperability Organization
to learn more about it.
-
Learn more about JAX-RPC
and its successor JAX-WS.
-
Read
Introduction to Service Objects
to learn more about SDO.
-
Read the
SDO 1.0 and 2.0 specifications
.
-
Visit the
developerWorks SOA and web services zone
to find many more resources for web services developers.
-
Stay current with
developerWorks technical events and webcasts
.
Get products and technologies
-
Download a free trial version of
Rational Application Developer 6.0
-
Explore Eclipse and
download software from the
Eclipse Project,
EMF Project and the
Eclipse WTP Project.
-
Build your next development project with
IBM trial software
,
available for download directly from developerWorks.
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.




