Purchase Order Web Service

This example shows how to mix XML contents with Java. Mixing XML contents means that one can pass org.w3c.dom.Element or javax.xml.transform.Source objects as a parameter instead of a Java type. The wsdl2java compiler generates the remote interface, a stub and a skeleton for the service. If the XML to Java type mappings are generated or provided through the xmlrpc.type.mappings file, the wsdl2java generates methods with appropriate types as provided by the mappings.

If the -notypes flag in specified when running wsdl2java or if no type mapping information is available for a type, the compiler generates the methods with parameters having java.lang.Object as their types. This means that either a typed or a "raw" org.w3c.dom.Element (or javax.xml.transform.Source) object can be passed in as the parameter. The org.w3c.dom.Element object is serialized as is using normal XML serialization. On the server side the method return type is again java.lang.Object and an org.w3c.dom.Element (or javax.xml.transform.Source) object can be returned back.

PurchaseOrder WSDL Document

Below is the WSDL for the purchase order application.

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="PurchaseService"
 targetNamespace="http://www.example.com"
 xmlns="http://schemas.xmlsoap.org/wsdl/"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
 xmlns:tns="http://www.example.com"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <types>
  <schema elementFormDefault="qualified">
   <element name="PurchaseOrder" type="tns:PurchaseOrderType"/>
   <element name="OrderNumber" type="xsd:long"/>
   <element name="SKU" type="xsd:string"/>
   <element name="Customer" type="tns:CustomerType"/>
   <complexType name="PurchaseOrderType">
    <sequence>
     <element name="time" type="xsd:dateTime" minOccurs="0"/>
     <element name="item" type="xsd:string" minOccurs="0"/>
    </sequence>
    <attribute name="number" type="xsd:long"/>
   </complexType>
   <complexType name="CustomerType">
    <all>
     <element name="number" type="xsd:long"/>
     <element name="name" type="xsd:string" minOccurs="0"/>
     <element name="address" type="tns:AddressType"/>
    </all>
   </complexType>
   <complexType name="AddressType">
    <all>
     <element name="street" type="xsd:string"/>
     <element name="number" type="xsd:string"/>
     <element name="state" type="xsd:string"/>
     <element name="zip" type="xsd:string"/>
     <element name="country" type="xsd:string"/>
     <element name="phone" type="xsd:string"/>
    </all>
   </complexType>
  </schema>
 </types>
 <message name="getOrderRequest">
  <part name="number" element="tns:OrderNumber"/>
 </message>
 <message name="getOrderResponse">
  <part name="result" element="tns:PurchaseOrder"/>
 </message>
 <message name="placeOrderRequest">
  <part name="item" element="tns:SKU"/>
  <part name="customer" element="tns:Customer"/>
 </message>
 <message name="placeOrderResponse">
  <part name="result" element="tns:OrderNumber"/>
 </message>
 <portType name="Purchase">
  <operation name="getOrder">
   <input message="tns:getOrderRequest"/>
   <output message="tns:getOrderResponse"/>
  </operation>
  <operation name="placeOrder">
   <input message="tns:placeOrderRequest"/>
   <output message="tns:placeOrderResponse"/>
  </operation>
 </portType>
 <binding name="PurchaseBinding" type="tns:Purchase">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="getOrder">
   <soap:operation soapAction="http://www.example.com/getOrder"/>
   <input>
    <soap:body
     encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
     namespace="http://www.example.com" use="literal"/>
   </input>
   <output>
    <soap:body
     encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
     namespace="http://www.example.com" use="literal"/>
   </output>
  </operation>
  <operation name="placeOrder">
   <soap:operation soapAction="http://www.example.com/placeOrder"/>
   <input>
    <soap:body namespace="http://www.example.com" use="literal"/>
   </input>
   <output>
    <soap:body namespace="http://www.example.com" use="literal"/>
   </output>
  </operation>
 </binding>
 <service name="PurchaseService">
  <port binding="tns:PurchaseBinding" name="PurchasePort">
   <soap:address location="http://localhost:9090/cooked"/>
  </port>
 </service>
</definitions>

The "Raw" Client

The purchase order interface has three complex types. When wsdl2java is run with the -notypes flag these types are all mapped to java.lang.Object. Below is a raw client that treats all objects as XML elements.

package raw;
                                                                           
import javax.naming.InitialContext;
                                                                           
import org.w3c.dom.Document;
import org.w3c.dom.Element;
                                                                           
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
                                                                           
import javax.xml.rpc.Stub;
                                                                           
/**
   A raw (untyped) client, which uses org.w3c.dom.Element data types
   to communicate with Web Services. The untyped client must manually
   convert between Element and a suitable Java data type.
 */
                                                                           
public class Client
{
    public static void main(String[] args) throws Exception
    {
    |   // lookup the Web Service
    |   InitialContext ctx = new InitialContext();
    |   PurchaseService service = (PurchaseService)
    |       ctx.lookup("xmlrpc:soap:raw.PurchaseService");
    |   Purchase purchase = service.getPurchasePort();
    |                                                                      
    |   // set the end point address
    |   ((Stub)purchase)._setProperty("javax.xml.rpc.service.endpoint.address",
    |       args.length > 0 ? args[0] :"http://localhost:9090/raw");
    |                                                                      
    |   // create the document with customer
    |   Document document = Util.getParser().newDocument();
    |   Element customer = document.createElement("Customer");
    |   document.appendChild(customer);
    |                                                                      
    |   Util.addStringValue(customer, "number", "1234");
    |   Util.addStringValue(customer, "name", "Nash");
    |                                                                      
    |   Element at = document.createElement("address");
    |   customer.appendChild(at);
    |   Util.addStringValue(at, "street",  "Palm Tree Ln");
    |   Util.addStringValue(at, "number",  "42");
    |   Util.addStringValue(at, "state",   "HI");
    |   Util.addStringValue(at, "zip",     "12345");
    |   Util.addStringValue(at, "country", "United States");
    |   Util.addStringValue(at, "phone",   "555-1234");
    |                                                                      
    |   // invoke the Web Service
    |   Element me = document.getDocumentElement();
me.setAttribute("xmlns", "http://www.example.com");
    |   Element number = (Element) purchase.placeOrder("SurfBoard", me);
    |                                                                      
    |   long nid = Long.parseLong(number.getFirstChild().getNodeValue());
    |   System.out.println("order number " + nid);
    |                                                                      
    |   Element po = (Element) purchase.getOrder(number);
    |                                                                      
    |   // dump the raw purchase order
    |   OutputFormat format  = new OutputFormat(po.getOwnerDocument());
    |   format.setIndent(3);
    |   format.setLineWidth(60);
    |   XMLSerializer serial = new XMLSerializer(System.out, format);
    |   serial.asDOMSerializer();
    |   serial.serialize(po);
    }
}

The "Raw" Call Client

The call client demonstrate how the JAX-RPC Call API can be used in conjunction with DOM. Note that parameters added to Call are of type java.lang.Object. As with the stub, either org.w3c.dom.Element or javax.xml.transform.Source objects can be used.

package raw;
                                                                           
import javax.naming.InitialContext;
                                                                           
import org.w3c.dom.Document;
import org.w3c.dom.Element;
                                                                           
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
                                                                           
                                                                           
public class CallClient
{
    public static void main(java.lang.String[] args) throws java.lang.Exception
    {
    |   java.lang.System.setProperty(
    |       javax.xml.rpc.ServiceFactory.SERVICEFACTORY_PROPERTY,
    |       "com.sssw.jbroker.web.xml.rpc.ServiceFactoryDelegate");
    |                                                                      
    |   javax.xml.rpc.ServiceFactory factory = 
    |       javax.xml.rpc.ServiceFactory.newInstance();
    |                                                                      
    |   javax.xml.namespace.QName qname = new javax.xml.namespace.QName(
    |       "http://www.example.com", "PurchaseService");       
    |   javax.xml.rpc.Service service = factory.createService(qname);
    |   javax.xml.namespace.QName portName = new javax.xml.namespace.QName(
    |       "http://www.example.com", "PurchasePort");
    |                                                                      
    |   java.lang.String url = args.length > 0 ? args[0] :
    |       "http://localhost:9090/raw";
    |                                                                      
    |                                                                      
    |   // create the document with customer
    |   Document document = Util.getParser().newDocument();
    |   Element customer = document.createElement("Customer");
    |   document.appendChild(customer);
    |                                                                      
    |   Util.addStringValue(customer, "number", "1234");
    |   Util.addStringValue(customer, "name", "Nash");
    |                                                                      
    |   Element at = document.createElement("address");
    |   customer.appendChild(at);
    |   Util.addStringValue(at, "street",  "Palm Tree Ln");
    |   Util.addStringValue(at, "number",  "42");
    |   Util.addStringValue(at, "state",   "HI");
    |   Util.addStringValue(at, "zip",     "12345");
    |   Util.addStringValue(at, "country", "United States");
    |   Util.addStringValue(at, "phone",   "555-1234");
    |                                                                      
    |   Element me = document.getDocumentElement();
    |   me.setAttribute("xmlns", "http://www.example.com");
    |                                                                      
    |   // invoke the Web Service       
    |   Element number = (Element) placeOrder(
    |       service, portName, url, "SurfBoard", me);
    |                                                                      
    |   long nid = Long.parseLong(number.getFirstChild().getNodeValue());
    |   System.out.println("order number " + nid);
    |                                                                      
    |   Element po = (Element) getOrder(service, portName, url, number);
    |                                                                      
    |   // dump the raw purchase order
    |   OutputFormat format  = new OutputFormat(po.getOwnerDocument());
    |   format.setIndent(3);
    |   format.setLineWidth(60);
    |   XMLSerializer serial = new XMLSerializer(System.out, format);
    |   serial.asDOMSerializer();
    |   serial.serialize(po);
    }
                                                                           
    public static Object placeOrder(javax.xml.rpc.Service service, 
        javax.xml.namespace.QName portName, java.lang.String url, 
        String param0, Object param1) throws java.lang.Exception
    {
    |   javax.xml.rpc.Call call = service.createCall(portName);
    |   javax.xml.namespace.QName i0 = new javax.xml.namespace.QName(
    |       "http://www.example.com", "SKU");
    |   call.addParameter("{http://www.example.com}SKU", i0, 
    |       java.lang.Object.class, javax.xml.rpc.ParameterMode.IN);
    |                                                                      
    |   javax.xml.namespace.QName i1 = new javax.xml.namespace.QName(
    |       "http://www.example.com", "Customer");
    |   call.addParameter("{http://www.example.com}Customer", i1, 
    |       java.lang.Object.class, javax.xml.rpc.ParameterMode.IN);
    |                                                                      
    |   javax.xml.namespace.QName r = new javax.xml.namespace.QName(
    |       "http://www.example.com", "OrderNumber");
    |                                                                      
    |   call.setReturnType(r, java.lang.Object.class);            
    |                                                                      
    |   Object[] parms = { param0, param1 };
    |                                                                      
    |   call.setTargetEndpointAddress(url);
    |   call.setProperty(javax.xml.rpc.Call.SOAPACTION_URI_PROPERTY, 
    |       "http://www.example.com/placeOrder");
    |   call.setProperty(
    |       javax.xml.rpc.Call.OPERATION_STYLE_PROPERTY, "document");
    |   call.setProperty(
    |       javax.xml.rpc.Call.ENCODINGSTYLE_URI_PROPERTY, null);
    |   call.setOperationName(new javax.xml.namespace.QName(
    |       "http://www.example.com", "placeOrder"));   
    |   return call.invoke(parms);
    }
                                                                           
    public static Object getOrder(javax.xml.rpc.Service service, 
        javax.xml.namespace.QName portName, java.lang.String url,
        Object param) throws java.lang.Exception
    {
    |   javax.xml.rpc.Call call = service.createCall(portName);
    |                                                                      
    |   javax.xml.namespace.QName i0 = new javax.xml.namespace.QName(
    |       "http://www.example.com", "OrderNumber");
    |   call.addParameter("{http://www.example.com}OrderNumber", i0, 
    |       java.lang.Object.class, javax.xml.rpc.ParameterMode.IN);
    |                                                                      
    |   javax.xml.namespace.QName r = new javax.xml.namespace.QName(
    |       "http://www.example.com", "PurchaseOrder");
    |                                                                      
    |   call.setReturnType(r, java.lang.Object.class);
    |   long iv0 = 84;
    |                                                                      
    |   Object[] parms = { param };
    |                                                                      
    |   call.setTargetEndpointAddress(url);
    |   call.setProperty(javax.xml.rpc.Call.SOAPACTION_URI_PROPERTY, 
    |       "http://www.example.com/getOrder");
    |   call.setProperty(
    |       javax.xml.rpc.Call.OPERATION_STYLE_PROPERTY, "document");
    |   call.setProperty(
    |       javax.xml.rpc.Call.ENCODINGSTYLE_URI_PROPERTY, null);
    |   call.setOperationName(new javax.xml.namespace.QName(
    |       "http://www.example.com", "getOrder"));
    |                                                                      
    |   return call.invoke(parms);
    }
}

The "Cooked" Client

When running wsdl2java normally, JavaBeans and marshalers are automatically generated for complex types. The normal client can therefore invoke the service like this:

package raw;
                                                                           
import javax.naming.InitialContext;
                                                                           
import org.w3c.dom.Document;
import org.w3c.dom.Element;
                                                                           
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
                                                                           
import javax.xml.rpc.Stub;
                                                                           
/**
   A raw (untyped) client, which uses org.w3c.dom.Element data types
   to communicate with Web Services. The untyped client must manually
   convert between Element and a suitable Java data type.
 */
                                                                           
public class Client
{
    public static void main(String[] args) throws Exception
    {
    |   // lookup the Web Service
    |   InitialContext ctx = new InitialContext();
    |   PurchaseService service = (PurchaseService)
    |       ctx.lookup("xmlrpc:soap:raw.PurchaseService");
    |   Purchase purchase = service.getPurchasePort();
    |                                                                      
    |   // set the end point address
    |   ((Stub)purchase)._setProperty("javax.xml.rpc.service.endpoint.address",
    |       args.length > 0 ? args[0] :"http://localhost:9090/raw");
    |                                                                      
    |   // create the document with customer
    |   Document document = Util.getParser().newDocument();
    |   Element customer = document.createElement("Customer");
    |   document.appendChild(customer);
    |                                                                      
    |   Util.addStringValue(customer, "number", "1234");
    |   Util.addStringValue(customer, "name", "Nash");
    |                                                                      
    |   Element at = document.createElement("address");
    |   customer.appendChild(at);
    |   Util.addStringValue(at, "street",  "Palm Tree Ln");
    |   Util.addStringValue(at, "number",  "42");
    |   Util.addStringValue(at, "state",   "HI");
    |   Util.addStringValue(at, "zip",     "12345");
    |   Util.addStringValue(at, "country", "United States");
    |   Util.addStringValue(at, "phone",   "555-1234");
    |                                                                      
    |   // invoke the Web Service
    |   Element me = document.getDocumentElement();
me.setAttribute("xmlns", "http://www.example.com");
    |   Element number = (Element) purchase.placeOrder("SurfBoard", me);
    |                                                                      
    |   long nid = Long.parseLong(number.getFirstChild().getNodeValue());
    |   System.out.println("order number " + nid);
    |                                                                      
    |   Element po = (Element) purchase.getOrder(number);
    |                                                                      
    |   // dump the raw purchase order
    |   OutputFormat format  = new OutputFormat(po.getOwnerDocument());
    |   format.setIndent(3);
    |   format.setLineWidth(60);
    |   XMLSerializer serial = new XMLSerializer(System.out, format);
    |   serial.asDOMSerializer();
    |   serial.serialize(po);
    }
}

Note that a client is not required to register type mappings when retrieving the stub using the generated service. The service has already populated the type mapping registry with the required type mappings.

The "Raw" Server

Below in the XML element implementation of the service. Note that the service must still output and read elements according to the XML types defined in the WSDL document:

package raw;
                                                                           
import java.util.Calendar;
import java.util.Date;
import java.util.TreeMap;
                                                                           
import java.rmi.Remote;
import java.rmi.RemoteException;
                                                                           
import java.text.SimpleDateFormat;
                                                                           
import org.w3c.dom.Document;
import org.w3c.dom.Element;
                                                                           
/**
   A raw (untyped) Web Service, which uses org.w3c.dom.Element data
   types to communicate with its clients. The untyped server must manually
   convert between Element and a suitable Java data type.
 */
                                                                           
public class PurchaseImpl extends Purchase_ServiceSkeleton
{
    private long    _counter   = 0;
    private TreeMap _orders    = new TreeMap();
                                                                           
    private SimpleDateFormat _sdf =
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                                                                           
    public Object getOrder(Object number) throws RemoteException
    {
    |   Element nel = (Element) number;
    |   long nid = Long.parseLong(nel.getFirstChild().getNodeValue());
    |                                                                      
    |   cooked.PurchaseOrderType pot =
    |       (cooked.PurchaseOrderType) _orders.get(new Long(nid));
    |   if (pot == null) return null;
    |                                                                      
    |   // create a document with this purchase order
    |   Document document = Util.getParser().newDocument();
    |                                                                      
    |   Element po = document.createElement("PurchaseOrder");
    |   document.appendChild(po);
    |   po.setAttribute("number", pot.getNumber().toString());
    |   po.setAttribute("xmlns", "http://www.example.com");
    |                                                                      
    |   Util.addStringValue(po, "time", _sdf.format(pot.getTime().getTime()));
    |   Util.addStringValue(po, "item", pot.getItem());
    |                                                                      
    |   // return the document element
    |   return document.getDocumentElement();
    }
                                                                           
    public Object placeOrder(Object item, Object customer)
        throws RemoteException
    {
    |   // extract a customer from the element
    |   Element SKU = (Element) item;
    |   Element Cel = (Element) customer;
    |                                                                      
    |   String itemNme = SKU.getFirstChild().getNodeValue();
    |                                                                      
    |   Element cel    = Cel;
    |   long cnum      = Long.parseLong(Util.getStringValue(cel, "number"));
    |   String name    = Util.getStringValue(cel, "name");
    |   Element ael    = Util.getElement(cel,     "address");
    |   String street  = Util.getStringValue(ael, "street");
    |   String number  = Util.getStringValue(ael, "number");
    |   String state   = Util.getStringValue(ael, "state");
    |   String zip     = Util.getStringValue(ael, "zip");
    |   String country = Util.getStringValue(ael, "country");
    |   String phone   = Util.getStringValue(ael, "phone");
    |                                                                      
    |   cooked.AddressType addr = new cooked.AddressType();
    |   addr.setStreet(street);
    |   addr.setNumber(number);
    |   addr.setState(state);
    |   addr.setZip(zip);
    |   addr.setCountry(country);
    |   addr.setPhone(phone);
    |                                                                      
    |   cooked.CustomerType custType = new cooked.CustomerType();
    |   custType.setNumber(cnum);
    |   custType.setName(name);
    |   custType.setAddress(addr);
    |                                                                      
    |   cooked.PurchaseOrderType po = new cooked.PurchaseOrderType();
    |   po.setNumber(new Long(_counter++));
    |   po.setTime(Calendar.getInstance());
    |   po.setItem(itemNme);
    |                                                                      
    |   _orders.put(po.getNumber(), po);
    |                                                                      
    |   // create a document with the return value
    |   Document doc = Util.getParser().newDocument();
    |   Element num = doc.createElement("OrderNumber");
    |   num.setAttribute("xmlns", "http://www.example.com");
    |   num.appendChild(doc.createTextNode(po.getNumber().toString()));
    |   return num;
    }
}

The "Cooked" Server

The normal service implementation uses the JavaBeans and marshalers generated by the wsdl2java compiler. By calling the init method of the base class we ensure that all necessary type mappings are loaded.

package cooked;
                                                                           
import java.util.Calendar;
import java.util.Date;
import java.util.TreeMap;
                                                                           
import java.rmi.Remote;
import java.rmi.RemoteException;
                                                                           
import javax.servlet.ServletException;
                                                                           
import com.sssw.jbroker.web.encoding.DefaultTypeMappingRegistry;
                                                                           
/**
   A cooked (typed) Web Service, which uses Java types to communicate
   with its clients. The typed server relies on the Type Mapper to map
   between Java and XML data types.
 */
                                                                           
public class PurchaseImpl extends Purchase_ServiceSkeleton
{
    private long    _counter = 0;
    private TreeMap _orders  = new TreeMap();
                                                                           
    public void init() throws ServletException
    {
    |   super.init();
    }
                                                                           
    public PurchaseOrderType getOrder(long number)
        throws RemoteException
    {
    |   PurchaseOrderType po;
    |   po = (PurchaseOrderType) _orders.get(new Long(number));
    |   return po;
    }
                                                                           
    public long placeOrder(String item, CustomerType customer)
        throws RemoteException
    {
    |   PurchaseOrderType po = new PurchaseOrderType();
    |   po.setNumber(new Long(_counter++));
    |   po.setTime(Calendar.getInstance());
    |   po.setItem(item);
    |   _orders.put(po.getNumber(), po);
    |   return po.getNumber().longValue();
    }
}

Building the Example

The ANT build.xml file can be used to build the example. As part of the example two clients and servers are built. One pair is raw client and server and the other pair is cooked client and server. The raw client and server are working with a remote interface that has java.lang.Object as the types of all the method paramaters. This is done by running wsdl2java compiler with the -notypes flag.

Deploying the Server

The WAR file is created for you once you build the example. The WAR file will contain deployments for two servers, raw and cooked. You can run this in jwebserv or any servlet container.

Running the Clients

The interesting thing about the example is that raw clients can talk not only to a raw server but also to a cooked server and vice versa. That gives us four combinations to try. In the below examples please change the URL to where the WAR is depolyed. The examples prints out messages to the console as a confirmation.

  1. Raw client against raw server:

    java raw.Client http://hostname:port/raw
  2. Raw client against cooked server:

    java raw.Client http://hostname:port/cooked
  3. Raw call client against raw server:

    java raw.CallClient http://hostname:port/raw
  4. Raw call client against cooked server:

    java raw.CallClient http://hostname:port/cooked
  5. Cooked client against raw server:

    java cooked.Client http://hostname:port/raw
  6. Cooked client against cooked server:

    java cooked.Client http://hostname:port/cooked

Please refer to the README for additional instructions on compiling, deploying and running the example.



Copyright © 2003, 2004 Novell, Inc. All rights reserved. Copyright © 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.