Java Development News:

Part 1 - Developing my first Web Service in 30 minutes

By Zdenek Svoboda

01 Dec 2001 | TheServerSide.com

You've heard the hype, and your head is probably dizzy from all the acronyms. So just what are Web Services, and how can you use them? This series of articles is intended to demystify Web Services and show, step-by-step, how to build, deploy, use, and find them.

Basic Web Services aren't very difficult to create. To prove this point, we'll show you, in this first article, how to construct a Web Service in about 30 minutes. In subsequent articles we'll delve deeper into Web Services and explain the following topics in detail:

  • SOAP messaging
  • WSDL definitions and their relationship to code
  • Publishing services to a UDDI directory
  • Exposing legacy applications as Web Services
  • Advanced topics such as security

In this introductory article, we begin with a programmatic definition of Web Services, then quickly move on to show a simple Java class that calls and executes a Web Service. All of our examples will be in Java. We've created our examples using a free set of tools and a runtime environment from Systinet (details on how to access and download this software is in the Installing software chapter). You don't have to use these products to understand the examples, 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.

We believe that J2EE is the most mature architecture for business logic implementation, and our goal is to introduce Web Services as the natural extension to the existing J2EE component model, providing an industry standard XML-based protocol along with unified component description and discovery. This gives existing J2EE-based systems a much broader reach than ever before and makes J2EE a much better option for implementation of core business logic within the typically heterogenous environment of corporate information systems.


The Web Service - a programmatic definition

A Web Service is a software component with the following features:

  • It is accessible through a SOAP (Simple Object Access Protocol) interface.
  • It's interface is described in a WSDL (Web Service Description Language) document.

SOAP is an extensible XML messaging protocol that forms the foundation for Web Services. SOAP provides a simple and consistent mechanism that allows one application to send an XML message to another application. A SOAP message is a one-way transmission from a SOAP sender to a SOAP receiver, and any application can participate in an exchange as either sender or receiver. SOAP messages may be combined to support many communication behaviors, including request/response, solicit response, one-way asynchronous messaging, or event notification. SOAP is a high-level protocol that defines only the message structure and a few rules for message processing. It is completely independent of the underlying transport protocol, so SOAP messages can be exchanged over HTTP, JMS, or mail transport protocols. Currently the HTTP protocol is the most frequently used transport for SOAP messages. We'll show some sample SOAP messages later in this article.

WSDL is an XML document that contains a set of definitions that describes a Web Service. It provides all the information needed to access and use a Web Service. A WSDL document describes what the Web Service does, how it communicates, and where it resides. You use the WSDL document at develeopment-time to create your service interfaces. Some SOAP implementations, including Systinet WASP, also use WSDL at runtime to support dynamic communications.


Installing the software

REQUIREMENTS:We assume that you have a Java 1.3.x SDK and a standard HTTP browser installed on your system. The JAVA_HOME environment variable should point to your Java 1.3.x SDK installation directory.

If you want to follow along with the demo, you'll need to download WASP Advanced from Systinet. Unpack the downloaded package to a local disk (preferably c:) and run the install script from the bin subdirectory of the WASP Advanced Advanced installation.

In our examples we assume that we have unpacked WASP to the c:wasp-advanced directory. You'll also need to download the demo sources and unpack it into the c:wasp_demo directory. If you choose different directory names, please update the env.bat script appropriately (change the WASP_HOME and WASP_DEMO environment variables to point to the WASP installation directory and demo directory respectively).


Implementing a simple Web Service

We'll follow these steps to create our simple Web Service:

  • Create the Web Service business logic. First we need to write a Java class that implements the Web Service business logic. In this case, our business logic will be a simple Java class that simulates a stock quote service.

  • Deploy the Java class to the SOAP server. Next we need to turn the Java class into a Web Service. We'll show how to deploy the Java class to a SOAP server using the WASP deployment tool.

  • Generate client access classes. A client application uses a proxy object to access a Web Service. At request time, the proxy accepts a Java method call from the application and translates it into an XML message. At response time, the proxy receives the SOAP reply message, translates it into Java objects, and returns the results to the client application.

  • Client application development. The client application treats the proxy as a standard Java object that facilitates the communication with a Web Service.

NOTE: We're using MS Windows notation for our commandline commands. If you have a Unix-based environment, please make appropriate adjustments to these scripts.

So let's start with a simple Java class that implements a stock quote lookup function. Please look at the Java code below:

NOTE: All Java sources mentioned in this example can be found in the src subdirectory of the unpacked demo sources archive. All of them reside in the com.systinet.demos.stock package.



/*
 * StockQuoteService.java
 *
 * Created on Sat 13th Oct 2001, 15:25
 */

package com.systinet.demos.stock;

/**
 * Simple stock quote service
 * @author  zdenek
 * @version 1.0
 */
public class StockQuoteService {


    public double getQuote(String symbol) {
        if(symbol!=null && symbol.equalsIgnoreCase("SUNW"))
            return 10;
        if(symbol!=null && symbol.equalsIgnoreCase("MSFT"))
            return 50;
        if(symbol!=null && symbol.equalsIgnoreCase("BEAS"))
            return 11;
        return 0;
    }

    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 1: Web Service code (StockQuoteService.java)

Our example is yet another simple stock quote system (we've seen so many of these, developers should be registered traders by now), but it illustrates how easily Web Services can be created and deployed. In our example, we're going to retrieve the price of three stocks (BEAS, MSFT, and SUNW).

The easiest way to turn our class into a Web Service is to compile our Java classes and then use the deployment tool to deploy them to the Web Services runtime.

NOTE: You'll find all scripts in the bin subdirectory of the unpacked demo sources archive.

NOTE: Before running the demo you need to install the Systinet SOAP framework. Please see the Installation chapter of this document for step-by-step installation.

First we start the Web Service runtime server with the startserver.bat script. Then we compile StockQuoteService.java and deploy the compiled class to the SOAP server using the deploy.bat commandline script.

Next we will make sure that everything worked properly by opening the administration console in the HTTP browser. Click on the Refresh button to show a list of all the packages deployed on the server. We should see the StockService package with one StockQuoteService deployed on the server. Notice that the Web Service runtime automatically generated the WSDL file and made it publicly available at http://localhost:6060/StockQuoteService/.

<?xml version='1.0'?>
<wsdl:definitions name='com.systinet.demos.stock.StockQuoteService'
targetNamespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/'
    xmlns:mime='http://schemas.xmlsoap.org/wsdl/mime/'
    xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
    xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xmlns:xsd='http://www.w3.org/2001/XMLSchema'
    xmlns:ns0='http://idoox.com/containers'
    xmlns:http='http://schemas.xmlsoap.org/wsdl/http/'
    xmlns:tns='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/'
    xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/'>
    <wsdl:message name='StockQuoteService_getQuote_Request'>
        <wsdl:part name='p0' type='xsd:string'/>
    </wsdl:message>
    <wsdl:message name='StockQuoteService_getQuote_Response'>
        <wsdl:part name='response' type='xsd:double'/>
    </wsdl:message>
    <wsdl:message name='StockQuoteService_getAvailableStocks_Request'/>
    <wsdl:message name='StockQuoteService_getAvailableStocks_Response'>
        <wsdl:part name='response' type='ns0:LinkedList'/>
    </wsdl:message>
    <wsdl:portType name='StockQuoteService'>
        <wsdl:operation name='getAvailableStocks'>
            <wsdl:input name='getAvailableStocks' message='tns:StockQuoteService_getAvailableStocks_Request'/>
            <wsdl:output name='getAvailableStocks' message='tns:StockQuoteService_getAvailableStocks_Response'/>
        </wsdl:operation>
        <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:operation>
    </wsdl:portType>
    <wsdl:binding name='StockQuoteService' type='tns:StockQuoteService'>
        <soap:binding transport='http://schemas.xmlsoap.org/soap/http' style='rpc'/>
        <wsdl:operation name='getAvailableStocks'>
            <soap:operation soapAction='' style='rpc'/>
            <wsdl:input name='getAvailableStocks'>
                <soap:body use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
   namespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/'/>
            </wsdl:input>
            <wsdl:output name='getAvailableStocks'>
                <soap:body use='encoded' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'
   namespace='http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/'/>
            </wsdl:output>
        </wsdl:operation>
        <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/stock/'/>
            </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/stock/'/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name='JavaService'>
        <wsdl:port name='StockQuoteService' binding='tns:StockQuoteService'>
            <soap:address location='http://localhost:6061/StockQuoteService/'/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

Figure 2: Generated WSDL file (StockQuoteService.wsdl)

The WSDL file contains a full description of the deployed Web Service. Basically there are three parts in a WSDL file:

  • The WHAT part, consisting of the types, message, and portType elements, defines the messages and data types exchanged between client and server. A message is the basic communication element of SOAP. A message can consist of one or more parts, each part representing a typed parameter. There are two messages (input and output) for each method of our stock quote Java class. Since we don't use any complex or compound types in our example, there are no compound type definitions in this WSDL (don't worry, we'll see many of them in future examples). All messages are grouped into operations in an entity called a portType. A portType represents the interface -- a concrete set of operations supported by the Web Service. A Web Service can have multiple interfaces represented by different portTypes. Look at the StockQuoteService portType in the sample WSDL file. It includes two operations: getAvailableStocks and getQuote. To invoke the getQuote method, the client sends a StockQuote_getQuote_Request message. (You'll find this message defined earlier in the file.) Notice that the StockQuote_getQuote_Request message consists of one part (the input parameter) called p0, which is defined as an XML Schema string type (xsd:string). The Web Service is supposed to reply with the StockQuote_getQuote_Response message, which contains one part (the return value) called response, which is an XML Schema double type (xsd:double).


  • The HOW part, consisting of the binding elements, describes the technical implementation details of our Web Service. The binding binds a portType to a specific communication protocol (in this case, SOAP over HTTP). Since we're using SOAP, we use a number of WSDL extensibility elements for SOAP to define the specifics of our SOAP binding. (Notice that many of the elements in this section use the soap: namespace prefix. These elements are SOAP extensions to WSDL.) The soapAction attribute in the soap:operation element is an HTTP-specific attribute that can be used to specify the intent of the SOAP message. It can contain a message routing parameter or value that helps the SOAP runtime determine which application or method should be executed. The value specified in this attribute must also be specified in the SOAPAction: attribute in the HTTP header of the SOAP request message. In our case this attribute contains no value. A SOAP binding requires that we specify the communication style used for each operation in the portType. SOAP supports two possible communication styles: RPC and Document. The RPC style supports automatic marshalling and demarshalling of messages, permitting developers to express a request as a method call with a set of parameters, which returns a response containing a return value. The Document style does not support automatic marshalling and demarshalling of messages. It assumes that the contents of the SOAP message are well-formed XML data. A SOAP binding also requires that we specify how our messages are expressed in XML. We can use either literal values or encoded data types. The use='literal' attribute indicates that the SOAP runtime should send the XML as provided. The use='encoded' attribute indicates that the SOAP runtime should serialize the data for us using a particular encoding style. An encoding style defines a set of rules for expressing programming language types in XML. In this case we use the encoding style defined by SOAP in section 5 of the SOAP specification. Other encoding styles can also be used.


  • Finally the WHERE part, consisting of the service element, pulls together the port type, the binding, and the actual location (a URI) of the Web Service. Check out the service element at the very end of the WSDL document.

As you can see, a WSDL file completely describes a Web Service. Given this WSDL file, we have all the information needed to create a client application that can access our stock quote Web Service.


Implementing a Java web service client

A client binds to a remote Web Service using a proxy Java component. When using Systinet WASP, this proxy is generated at runtime from the WSDL file. We need a Java interface that can keep a reference to this dynamically created object. We can either create the interface ourselves, or we can use WASP's WSDLCompiler to generate one for us. The interface creation is easy since the only requirement is that the interface methods must be a subset of methods of the Web Service business logic Java class. Let's look at the code below. First, the client creates a WebServiceLookup object. This object is then used to create the Web Service proxy by invoking the lookup method. The lookup method requires two parameters: a reference to a WSDL file and the class of the Java interface that will reference the proxy instance. The lookup method returns the proxy that is used to invoke the Web Service.

/**
 * Stock Client
 *
 * @created July 17, 2001
 * @author zdenek
 */

package com.systinet.demos.stock;

import org.idoox.wasp.Context;
import org.idoox.webservice.client.WebServiceLookup;

public class StockClient {

  /**
   * Web service client main method.
   * Finds the web service and
   * @param args  not used.
   */
    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("");

    }

}

Figure 3: Web Service client code (StockClient.bat)

Run the runJavaclient.bat script. This script will run WSDLcompiler to generate the Java interface, then it will compile and run the client application. You should see the output from the getAvailableStocks and getQuote methods on the console.


Developing and running the JavaScript Web Service client

NOTE: Please note that the JavaScript Web Service client currently requires Microsoft Internet Explorer 6.0 or Microsoft Internet Explorer 5.0 with Microsoft XML Parser 3.0 SP2 installed.

We can generate a browser-based JavaScript client using the runJScriptClient.bat script. This script will open an IE browser with a generated HTML page. You can then invoke all the Web Service methods from this page.


SOAP messages at a glance

Now we can use the WASP Administration console to view the SOAP messages that are exchanged between client and server. First, we need to open the administration console in the browser. Then click on the Refresh button to see all deployed packages. We should see our StockQuoteService Web Service deployed on the server. Enable debugging of all SOAP requests by clicking on the "enable" link (near the "Debug is OFF:" label in the StockQuoteService section of the administration console). Then re-run the Java Web Service client runJavaclient.bat script and click on the show SOAP conversation link in the admin console. This should open a browser window that displays two pairs of input and output SOAP messages.


==== INPUT ==== http://localhost:6060/StockQuoteService/ ==== 11/7/01 3:45 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:getAvailableStocks xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/"/>
  </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:getAvailableStocksResponse xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet
  /demos/stock/">
    <response xsi:type="ns1:LinkedList" xmlns:ns1="http://idoox.com/containers">
        <item xsi:type="xsd:string">SUNW</item>
        <item xsi:type="xsd:string">MSFT</item>
        <item xsi:type="xsd:string">BEAS</item>
    </response>
    </ns0:getAvailableStocksResponse>
  </ns0:Body>
</ns0:Envelope>
==== CLOSE =====================================================================

==== INPUT ==== http://localhost:6060/StockQuoteService/ ==== 11/7/01 3:45 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/stock/">
      <p0 xsi:type="xsd:string">SUNW</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:getQuoteResponse xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/">
      <response xsi:type="xsd:double">10.0</response>
    </ns0:getQuoteResponse>
  </ns0:Body>
</ns0:Envelope>
==== CLOSE =====================================================================

Figure 4: SOAP messages

SOAP messages follow the following basic structure:

<ENVELOPE attrs>
  <HEADER attrs>
    <directives/>
  </HEADER>
  <BODY attrs>
    <payload/>
  </BODY>
  <FAULT attrs>
    <errors/>
  </FAULT>
</ENVELOPE>

The message content is enclosed in the ENVELOPE. In this simple case, our SOAP messages contain only a BODY section. There can be also two other sections, namely the HEADER and FAULT sections. The HEADER section is usually used for propagation of various context information (e.g. payment details, transaction context, security credentials etc). If an error occurs the FAULT section should carry information about the nature of the fault. The BODY section carries the main information payload (in our example, the stock value and related data). Generally, SOAP doesn't mandate any rules for the BODY section. We already mentioned two possible styles for the BODY section: Document and RPC. The Document style has no rigid formatting requirements beyond standard XML rules, while the RPC style defines rules for marking up the method call with all its parameters. The SOAP specification recommends but doesn't mandate an encoding style, the basic framework for expressing typed values in a SOAP message. The SOAP encoding style is based on the data types defined in the XML Schema, Part 2 Recommendation which includes primitive programming language types such as int, float, double or string. SOAP Encoding also defines rules for building complex types (e.g. arrays, structures etc.) on top of these primitives. In our case (we are using the RPC style with SOAP encoding), the BODY section of the input message contains the invoked method name with encoded parameters:

<ns0:getQuote xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/">
  <p0 xsi:type="xsd:string">SUNW</p0>
</ns0:getQuote>

The output message contains the result of the method call:

<ns0:getQuoteResponse xmlns:ns0="http://idoox.com/wasp/tools/java2wsdl/output/com/systinet/demos/stock/">
  <response xsi:type="xsd:double">10.0</response>
</ns0:getQuoteResponse>

Cleanup

At the end we should undeploy our simple service from the server by running the undeploy.bat script.


Review

In this first article we've hopefully demonstrated that creating a simple Web Service isn't difficult - in fact, one of the benefits of using Web Services is that they're relatively easy to make and deploy. In the process of creating our stock trading system we've introduced some fundamental concepts, including SOAP and WSDL. We've shown how SOAP messages are constructed, and we've created and analysed a WSDL file that provides the instructions on how to interact with a Web Service. In the next article we'll build on this and examine how SOAP handles complex types, error messaging and also remote references.