January 2002
In
Part One of the hands-on web services tutorial we introduced the basic concepts behind
web services, including SOAP and
WSDL. We showed how to develop a simple Web Service in
under 30 minutes, and in the process explained SOAP messaging, how to implement a Java
web service client, and how a WSDL is constructed. In this article we'll check out some
more advanced topics including SOAP complex type handling, error processing and remote
references.
NOTE: If you haven't already downloaded the software used to create the tutorial
examples, then please refer to the
installation
chapter in Part One. You'll
also need to download the demo sources.
We'll assume that you unpacked this archive into the c:\wasp_demo directory. All Java sources mentioned in the tutorial
examples can be found in the src subdirectory of the unpacked
demo sources archive.
They all
reside in the com.systinet.demos package. You don't need to download and use the
software to understand these articles, but we strongly recommend it. The concepts we
introduce and the code we create are generally applicable and relatively independent of
the tools used. We assume some knowledge of XML but none of web services. Unless you
have some experience with SOAP and WSDL, we recommend you first read Part One of this
series.
SOAP and complex types
So far, our web services exchanged only primitive data types like strings, int and
doubles. Now we will see how complex types are translated to the SOAP message.
The SOAP protocol recommends usage of so-called SOAP encoding for translation of
complex programming language types to XML. Usually, the following translations occur
automatically:
- Java 2 primitive types.
- Custom classes, which are mapped using the well-known JavaBeans pattern. All public
fields and fields with public getters/setters are translated to XML by Java reflection
serializer.
- Java 2 collections
(HashMap, Hashtable, Vector, List etc.).
The following simple demonstration shows the default JavaBean pattern serialization and
Java 2 collection serialization.
We'll pass a simple OrderRequest structure to the web service. The
OrderRequest structure is
implemented as the simplest possible JavaBean, with getters and setters for
symbol,limitPrice and volume. The service processOrder method accepts the
OrderRequest Java
class as the only parameter. We'll show how the OrderRequest structure is represented
in the SOAP message. We'll also code the getOrders method that returns the collection
of all accepted orders from the web service to the client. We'll use java.util.Hashtable
container as a return value from the getOrders method and look at its XML representation.
In this example, we continue our quest to make serious money on the stock market
with a simple ordering service:
package com.systinet.demos.mapping;
public class OrderService {
private java.util.HashMap orders = new java.util.HashMap();
public String processOrder(OrderRequest order) {
String result = "PROCESSING ORDER";
Long id = new Long(System.currentTimeMillis());
result += "\n----------------------------";
result += "\nID: "+id;
result += "\nTYPE: "+
((order.getType()==order.ORDER_TYPE_SELL)?("SELL"):("BUY"));
result += "\nSYMBOL: "+order.getSymbol();
result += "\nLIMIT PRICE: "+order.getLimitPrice();
result += "\nVOLUME: "+order.getVolume();
this.orders.put(id,order);
return result;
}
public java.util.HashMap getOrders() {
return this.orders;
}
}
Figure 1: Complex types handling example (OrderService.java)
NOTE: You'll find all scripts in the bin subdirectory of unpacked
demo sources
archive.
We'll compile and deploy the ordering web service by running the deployMapping.bat script.
The client-side application simply creates two order requests and sends them to the web
service. It then retrieves the Hashtable populated with these two requests and
displays them on the console. Lets look at the client application code, where once again
we are speculating in technology stocks:
package com.systinet.demos.mapping;
import org.idoox.wasp.Context;
import org.idoox.webservice.client.WebServiceLookup;
public final class TradingClient {
public static void main( String[] args ) throws Exception {
WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP);
OrderServiceProxy service =
(OrderServiceProxy)lookup.lookup("http://localhost:6060/MappingService/",OrderServiceProxy.class);
com.systinet.demos.mapping.struct.OrderRequest order = new com.systinet.demos.mapping.struct.OrderRequest();
order.symbol = "SUNW";
order.type = com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_BUY;
order.limitPrice = 10;
order.volume = 100000;
String result = service.processOrder(order);
System.out.println(result);
order = new com.systinet.demos.mapping.struct.OrderRequest();
order.symbol = "BEAS";
order.type = com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_BUY;
order.limitPrice = 13;
order.volume = 213000;
result = service.processOrder(order);
System.out.println(result);
java.util.HashMap orders = service.getOrders();
java.util.Iterator iter = orders.keySet().iterator();
while(iter.hasNext()) {
Long id = (Long)iter.next();
OrderRequest req = (OrderRequest)orders.get(id);
System.out.println("\n----------------------------");
System.out.println("\nID: "+id);
System.out.println("\nTYPE: "+
((req.getType()==com.systinet.demos.mapping.OrderRequest.ORDER_TYPE_SELL)?("SELL"):("BUY")));
System.out.println("\nSYMBOL: "+req.getSymbol());
System.out.println("\nLIMIT PRICE: "+req.getLimitPrice());
System.out.println("\nVOLUME: "+req.getVolume());
}
}
}
Figure 2: Ordering client source code (TradingClient.java)
Complex type mapping - deeper insight
The first thing to uncover is the Web Services Description Language (WSDL) file that was generated at the time we deployed.
If you deployed the mapping service, you can view the complete WSDL file at
http://localhost:6060/MappingService/.
You'll remember from
Part One of our tutorial that the
WSDL describes what functionality a web service offers, how it
communicates, and where it is accessible. WSDL provides a structured mechanism to describe the operations a web service
can perform, the formats of the messages that it can process, the protocols that it supports, and the access point of an
instance of the web service. In our example, the most important part to note is the definition of the OrderRequest Java
type mapping:
<xsd:complexType name="OrderRequest">
<xsd:sequence>
<xsd:element name="limitPrice" type="xsd:double"/>
<xsd:element name="symbol" type="xsd:string"/>
<xsd:element name="type" type="xsd:short"/>
<xsd:element name="volume" type="xsd:long"/>
</xsd:sequence>
</xsd:complexType>
The mapping of the OrderRequest to the XML is defined as a set of primitive type fields. The HashMap
returned from the getOrders mapping is imported as http://idoox.com/containers:HashMap datatype. Our WSDL file imports
the following definition:
<complexType name="HashMap">
<sequence>
<element name="item" minOccurs="0" maxOccurs="unbounded">
<complexType>
<sequence>
<element name="key" type="anyType" />
<element name="value" type="anyType" />
</sequence>
</complexType>
</element>
</sequence>
</complexType>
Lets now look at the SOAP messages exchanged between the SOAP client and the web service. We'll first start the SOAP
message tracing on the SOAP server by opening the Administration console in the HTTP browser, clicking the Refresh
button and clicking on the enable link in the MappingService section of the console. Next, we'll run the client application by
invoking the runMappingClient.bat script and look at the SOAP messages. The message below is an invocation of the
processOrder method with an instance of OrderRequest as a parameter:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Body
ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<ns0:processOrder xmlns:ns0=
"http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/OrderService">
<p0 xsi:type=
"ns1:OrderRequest" xmlns:ns1="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/">
<limitPrice xsi:type="xsd:double">10.0</limitPrice>
<symbol xsi:type="xsd:string">SUNW</symbol>
<type xsi:type="xsd:short">1</type>
<volume xsi:type="xsd:long">100000</volume>
</p0>
</ns0:processOrder>
</ns0:Body>
</ns0:Envelope>
The following message represents the return value (HashMap populated with processed order requests) from
the getOrders method:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Body
ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<ns0:getOrdersResponse xmlns:ns0=
"http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/mapping/OrderService">
<response xsi:type="ns1:HashMap" xmlns:ns1="http://idoox.com/containers">
<item>
<key xsi:type="xsd:long">1006209071080</key>
<value xsi:type=
"ns2:com.systinet.demos.mapping.OrderRequest" xmlns:ns2="http://idoox.com/package/">
<volume xsi:type="xsd:long">100000</volume>
<symbol xsi:type="xsd:string">SUNW</symbol>
<limitPrice xsi:type="xsd:double">10.0</limitPrice>
<type xsi:type="xsd:short">1</type>
</value>
</item>
<item>
<key xsi:type="xsd:long">1006209071130</key>
<value xsi:type="ns3:com.systinet.demos.mapping.OrderRequest" xmlns:ns3="http://idoox.com/package/">
<volume xsi:type="xsd:long">213000</volume>
<symbol xsi:type="xsd:string">BEAS</symbol>
<limitPrice xsi:type="xsd:double">13.0</limitPrice>
<type xsi:type="xsd:short">1</type>
</value>
</item></response>
</ns0:getOrdersResponse></ns0:Body>
</ns0:Envelope>
The mapping from Java to XML is straightforward. We can see the outer HashMap definition with key amd value elements.
Notice that there is an inner XML definition of the OrderRequest data type.
In the final step we will undeploy the web service from the server by running the undeployMapping.bat script.
SOAP Error Processing
When things go wrong SOAP defines a so-called SOAP Fault XML construct that represents
an error that occured on the server-side. We briefly introduced these error messages in
Part One of our tutorial. Here we'll examine them in more detail. The SOAP Fault
contains three basic elements:
- FAULTCODE that contains an error code or ID.
- FAULTSTRING that carries the short description of the error.
- DETAIL that describes the error in some detail.
To illustrate error processing we'll add some exceptions to our stock quote example.
First, we'll set the getQuote method to throw StockNotFoundException if it can't find a
specified one of our three stock picks:
package com.systinet.demos.fault;
public class StockQuoteService {
public double getQuote(String symbol) throws StockNotFoundException {
if(symbol!=null && symbol.equalsIgnoreCase("SUNW"))
return 10;
if(symbol!=null && symbol.equalsIgnoreCase("MSFT"))
return 50;
if(symbol!=null && symbol.equalsIgnoreCase("BEAS"))
return 11;
throw new StockNotFoundException("Stock symbol "+symbol+" not found.");
}
public java.util.LinkedList getAvailableStocks() {
java.util.LinkedList list = new java.util.LinkedList();
list.add("SUNW");
list.add("MSFT");
list.add("BEAS");
return list;
}
}
Figure 3: SOAP web service Java source (StockQuoteService.java)
Next, we will deploy the web service using the deployFault.bat commandline script.
We will use the SOAP message tracing on the SOAP server by opening the
Administration console in the HTTP browser, clicking the Refresh button and
clicking on the enable link in the StockQuoteService section of the console.
Now we will check out the WSDL file generated by the SOAP server at deployment. Open
the
http://localhost:6060/StockQuoteService/
URL in a browser.
Notice the SOAP fault message definition in the WSDL:
<wsdl:
message name='StockQuoteService_getQuote_com.systinet.demos.fault.StockNotFoundException_Fault'>
<wsdl:part name='idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException' type='xsd:string'/>
</wsdl:message>
The fault message is referenced in the getQuote operation in the WSDL port type element:
<wsdl:operation name='getQuote' parameterOrder='p0'>
<wsdl:input name='getQuote' message='tns:StockQuoteService_getQuote_Request'/>
<wsdl:output name='getQuote' message='tns:StockQuoteService_getQuote_Response'/>
<wsdl:fault name='getQuote_fault1'
message='tns:StockQuoteService_getQuote_com.systinet.demos.fault.StockNotFoundException_Fault'/>
</wsdl:operation>
and in the binding element:
<wsdl:operation name='getQuote'>
<soap:operation soapAction='' style='rpc'/>
<wsdl:input name='getQuote'>
<soap:body use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
namespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/'/>
</wsdl:input>
<wsdl:output name='getQuote'>
<soap:body use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
namespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/'/>
</wsdl:output>
<wsdl:fault name='getQuote_fault1'>
<soap:fault name='getQuote_fault1' use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
namespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/'/>
</wsdl:fault>
</wsdl:operation>
Summarized, the server-side Java exception is represented with a simple SOAP message
that is returned to the client side if the exception occurs.
The next step is to create a simple web service client application:
package com.systinet.demos.fault;
import org.idoox.wasp.Context;
import org.idoox.webservice.client.WebServiceLookup;
public final class StockClient {
public static void main( String[] args ) throws Exception {
// lookup service
WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP);
// bind to StockQuoteService
StockQuoteServiceProxy quoteService = (StockQuoteServiceProxy)lookup.lookup(
"http://localhost:6060/StockQuoteService/",
StockQuoteServiceProxy.class
);
// use StockQuoteService
System.out.println("Getting available stocks");
System.out.println("------------------------");
java.util.LinkedList list = quoteService.getAvailableStocks();
java.util.Iterator iter = list.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
System.out.println("");
System.out.println("Getting SUNW quote");
System.out.println("------------------------");
System.out.println("SUNW "+quoteService.getQuote("SUNW"));
System.out.println("");
System.out.println("Getting IBM quote (warning, this one doesn't exist, so we will get an exception)");
System.out.println("------------------------");
System.out.println("SUNW "+quoteService.getQuote("IBM"));
System.out.println("");
}
}
Figure 4: SOAP client Java source (StockClient.java)
We'll need to generate the client Java interface, compile all Java classes and run the
client application. All these tasks are carried out in the runFaultClient.bat
Our existing stock portfolio is rather sparse, and doesn't include IBM. The client
should first show all available symbols, retrieve the stock quote of the SUNW symbol
and then finally throw the StockNotFound exception complaining that 'Stock symbol IBM
not found'. Now lets look at the Administration console
and click on the
show SOAP conversation link. In a new window the following message is displayed (important
message parts are highlighted):
==== INPUT ==== http://localhost:6060/StockQuoteService/ ==== 11/14/01 4:44 PM =
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Body
ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<ns0:getQuote xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/fault/">
<p0 xsi:type="xsd:string">IBM</p0>
</ns0:getQuote>
</ns0:Body>
</ns0:Envelope>
==== CLOSE =====================================================================
==== OUTPUT ==== http://localhost:6060/StockQuoteService/ ======================
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<ns0:Body
ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<ns0:Fault xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode>ns0:Server</faultcode>
<faultstring>Stock symbol IBM not found.</faultstring>
<detail xmlns:ijm="urn:idoox-java-mapping">
<ijm:idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException>
<ijm:stack-trace>
com.systinet.demos.fault.StockNotFoundException: Stock symbol IBM not found.
at com.systinet.demos.fault.StockQuoteService.getQuote(StockQuoteService.java:24)
at java.lang.reflect.Method.invoke(Native Method)
at com.idoox.wasp.server.adaptor.JavaAdaptorInvoker.invokeService(JavaAdaptorInvoker.java:387)
at com.idoox.wasp.server.adaptor.JavaAdaptorInvoker.invoke(JavaAdaptorInvoker.java:239)
at com.idoox.wasp.server.adaptor.JavaAdaptorImpl.dispatch(JavaAdaptorImpl.java:164)
at com.idoox.wasp.server.AdaptorTemplate.dispatch(AdaptorTemplate.java:178)
at com.idoox.wasp.server.ServiceConnector.dispatch(ServiceConnector.java:217)
at com.idoox.wasp.server.ServiceManager.dispatch(ServiceManager.java:231)
at com.idoox.wasp.server.ServiceManager$DispatcherConnHandler.handlePost(ServiceManager.java:1359)
at com.idoox.transport.http.server.Jetty$WaspHttpHandler.handle(Jetty.java:94)
at com.mortbay.HTTP.HandlerContext.handle(HandlerContext.java:1087)
at com.mortbay.HTTP.HttpServer.service(HttpServer.java:675)
at com.mortbay.HTTP.HttpConnection.service(HttpConnection.java:457)
at com.mortbay.HTTP.HttpConnection.handle(HttpConnection.java:317)
at com.mortbay.HTTP.SocketListener.handleConnection(SocketListener.java:99)
at com.mortbay.Util.ThreadedServer.handle(ThreadedServer.java:254)
at com.mortbay.Util.ThreadPool$PoolThreadRunnable.run(ThreadPool.java:601)
at java.lang.Thread.run(Thread.java:484)
</ijm:stack-trace>
</ijm:idoox-java-mapping.com.systinet.demos.fault.StockNotFoundException>
</detail>
</ns0:Fault>
</ns0:Body>
</ns0:Envelope>
==== CLOSE =====================================================================
Notice the output SOAP message and particularly the FAULT construct. The FAULTCODE contains the generic error code, the
FAULTSTRING element holds the exception message and DETAIL element carries the full stack trace of the exception. All SOAP
fault messages follow this basic format. Hopefully, you won't encounter too many future examples.
Finally, we can undeploy the service using undeployFault.bat script.
Remote references
Remote references are a construct used in most distributed object systems, such as RMI, CORBA, and DCOM. Assume that you
have a client that is invoking methods on a server object. Here's how they work. Let's say that the server object
creates another object, and it has to somehow pass the new object back to the client (see Figure 8 below). It can pass
the new object either by "value" or by "reference". If you pass the object by value, you are passing the entire object.
If you pass by reference, you are just passing a pointer to the object. A remote reference is a reference that works
across a network. Remote references are critical to many distributed computing design patterns, particularly the
Factory pattern. Even though this feature is critical for many distributed computing applications, not all SOAP
implementations support it.
For example, we may define a createLineItem method on an Order Web Service. This method will create a new
LineItem object that holds the catalog number and price of the ordered product and ordered quantity.
The Order potentially references many of the LineItem objects. The LineItem object should be returned to the
client application as the remote reference in order to further specify all necessary data.
Figure 5: Remote references
Implementing simple remote references
To illustrate the remote references feature we're going to create a new example, and spend
our gains from the stock market by ordering some products. We'll start with definitions for
two interfaces: Both Order and LineItem Java interfaces will be used for referencing the
web service on the client-side:
package com.systinet.demos.interref;
public interface LineItem extends java.rmi.Remote {
public String getID();
public long getCount();
public String getProductID();
public void close();
}
Figure 6: LineItem interface
package com.systinet.demos.interref;
public interface Order {
public LineItem addItem(String productID, long count);
public LineItem getItem(String id);
public void removeItem(String id);
}
Figure 7: Order interface
Notice that the LineItem interface extends the java.rmi.Remote interface. This is the
simplest method for handling remote references using WASP. Otherwise, the LineItem
interface is fairly obvious. The addItem method in the Order interface creates and
returns a new order item. The getItem returns an existing item and removeItem removes
the specified item from the order.
Now we will implement both LineItem and Order interfaces:
package com.systinet.demos.interref;
import org.idoox.webservice.server.WebServiceContext;
import org.idoox.webservice.server.LifeCycleService;
public class LineItemImpl implements LineItem {
private String pid;
private String id;
private long count;
public LineItemImpl(String pid, long count) {
System.err.println("Creating new LineItem.");
this.id = pid+System.currentTimeMillis();
this.pid = pid;
this.count = count;
}
public void close() {
System.err.println("close()");
WebServiceContext context = WebServiceContext.getInstance();
LifeCycleService lc = context.getLifeCycleService();
lc.disposeServiceInstance(this);
}
public long getCount() {
System.err.println("getCount()");
return this.count;
}
public String getProductID() {
System.err.println("getProductID()");
return this.pid;
}
public String getID() {
System.err.println("getID()");
return this.id;
}
}
Figure 8: LineItem implementation
package com.systinet.demos.interref;
public class OrderImpl implements Order {
private java.util.HashMap items = new java.util.HashMap();
public LineItem getItem(String id) {
return (LineItem)this.items.get(id);
}
public LineItem addItem(java.lang.String pid, long count) {
LineItem item = new LineItemImpl(pid, count);
this.items.put(item.getID(), item);
return item;
}
public void removeItem(java.lang.String id) {
LineItem item = (LineItem)this.items.remove(id);
item.close();
}
}
Figure 9: Order implementation
We will use deployInterref.bat script for deploying the web service.
Standard implementation, no surprises. Notice that the LineItem prints method call tracing messages.
The client code is also pretty standard:
package com.systinet.demos.interref;
import javax.wsdl.QName;
import org.idoox.wasp.Context;
import org.idoox.webservice.client.WebServiceLookup;
public final class OrderClient {
public static void main( String[] args ) throws Exception {
// lookup service
WebServiceLookup lookup = (WebServiceLookup)Context.getInstance(Context.WEBSERVICE_LOOKUP);
Order order = (Order)lookup.lookup("http://localhost:6060/OrderService/",
new QName("http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/interref/", "OrderService"),
"OrderImpl", Order.class);
String id1 = order.addItem("THNKPDT23", 2).getID();
String id2 = order.addItem("THNKPDT22", 2).getID();
System.out.println("ID1 "+id1);
System.out.println("ID2 "+id2);
LineItem item = order.getItem(id1);
System.out.println("Line ITEM");
System.out.println("---------");
System.out.println("ID: "+item.getID());
System.out.println("Product ID: "+item.getProductID());
System.out.println("Count: "+item.getCount());
item = order.getItem(id2);
System.out.println("Line ITEM");
System.out.println("---------");
System.out.println("ID: "+item.getID());
System.out.println("Product ID: "+item.getProductID());
System.out.println("Count: "+item.getCount());
}
}
Figure 10: Order client
This simple client application creates the dynamic proxy for the ordering web service and then creates two order items,
THNKPDT23 and THNKPDT22. You should see on the server console that all method invocations on these order items take place
at the server-side. So both order items are dynamically created on the server, while the client gets their references.
This is a good example of the Factory pattern we mentioned earlier. In our case, the ordering service acts as the factory
for both order items.
Notice that the line items are stateful, as they keep particular order item data.
Deleting remote references
Unlike stateless web services, stateful web services require special handling codes for deinitialization. We use the
explicit instance removal in our example. This is done by calling the disposeServiceInstance method on the
LifeCycle system web service. Check out the LineItemImpl's close method to see how to delete
a web service explicitly:
public void close() {
System.err.println("close()");
WebServiceContext context = WebServiceContext.getInstance();
LifeCycleService lc = context.getLifeCycleService();
lc.disposeServiceInstance(this);
}
The last step is to undeploy the service using undeployInterref.bat script.
What is next?
In this installment we've mastered SOAP complex type handling, and looked in detail at how SOAP error messages are
created. We've also explored how to use remote references with web services. We now share a good understanding of
SOAP, WSDL and the process of creating and working with web services. Hopefully, we've made it appear straightforward.
In Part Three we'll address the important issue of web services security.
In the meantime we'd welcome feedback, comments and ideas. Please get in touch at
tutorial@systinet.com
PRINTER FRIENDLY VERSION
|