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.
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 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 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); } }
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.
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 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(); } }
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.
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.
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.
Raw client against raw server:
java raw.Client http://hostname:port/raw
Raw client against cooked server:
java raw.Client http://hostname:port/cooked
Raw call client against raw server:
java raw.CallClient http://hostname:port/raw
Raw call client against cooked server:
java raw.CallClient http://hostname:port/cooked
Cooked client against raw server:
java cooked.Client http://hostname:port/raw
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.