How-To Develop a Web Service From a Web Service Description Language (WSDL) Document

First Publication: 14-Sept-04
Last Update: 18-Jan-06
Author: Jon Maron


Introduction

The Oracle Application Server provides the facilities for generating a web service from an existing Web Service Description Language (WSDL) document.  This top-down approach, although not as simple as the bottom-up approach (exposing an existing class as a web service), is preferable for a number of reasons:

The process of generating a web service from a WSDL document and creating an associated client application involves:

Admittedly, the argument could be made that it may be simpler to simply modify the underlying implementation class and expose it directly as a web service by having it implement the SEI (removing the requirement for creating the service implementation class as noted above).  Although that is a possibility the more general, and potentially more frequent, case is that the existing class can not be modified.  Rather, the functionality provided by the existing class needs to be exposed to remote clients without alteration to the target class.  It is this scenario we will explore in the following sample.

What are the Prerequisites?

What Should You Know?

What are the Software Requirements?

What are the Notations?

The following conventions are used throughout the document:

How to Build the Application?

Let's take a closer look at the steps involved in creating the web service application from an existing Java class.  These steps have already been performed for this sample but are provided for illustrative purposes.

The Existing Bank Implementation

The BankMemDB class is an implementation of the Bank interface and provides standard banking functionality:

public class BankMemDB implements Bank{
    public static BankMemDB newInstance();
    public String addNewAccount(String name,float initBalance) throws AccountException;
    public Account getAccount(String id);
    public List getAccounts();
}

This class is the existing class whose functionality we wish to provide access to via a web service interface.

Instances of the Bank interface (e.g. BankMemDB) are created by a BankFactory:

public class BankFactory {
    public BankFactory();
    public Bank createBank();
}

Exposing the Bank as a web service

In order to expose an instance of the existing Bank as a web service using the top-down development approach we will:

  1. Generate a service interface from the service WSDL. The complete WSDL file for can be found here.   The following portion of the WSDL file shows the operations that end up in the SEI:
    <portType name="BankService">
        <documentation>A service that provides banking operations for client applications.</documentation>
        <operation name="createAccount">
            <documentation>Creates a banking account.</documentation>
            <input message="tns:BankService_createAccount"/>
            <output message="tns:BankService_createAccountResponse"/>
            <fault name="AccountException" message="tns:AccountException"/>
        </operation>
        <operation name="deposit">
            <documentation>Performs a bank deposit.</documentation>
            <input message="tns:BankService_deposit"/>
            <output message="tns:BankService_depositResponse"/>
            <fault name="AccountException" message="tns:AccountException"/>
        </operation>
        <operation name="getAccountID">
            <documentation>Retrieves an account ID.</documentation>
            <input message="tns:BankService_getAccountID"/>
            <output message="tns:BankService_getAccountIDResponse"/>
            <fault name="AccountException" message="tns:AccountException"/>
        </operation>
        <operation name="getBalance">
            <documentation>Retrieves an account balance.</documentation>
            <input message="tns:BankService_getBalance"/>
            <output message="tns:BankService_getBalanceResponse"/>
            <fault name="AccountException" message="tns:AccountException"/>
        </operation>
        <operation name="withdraw">
            <documentation>Withdraws funds from a bank account.</documentation>
            <input message="tns:BankService_withdraw"/>
            <output message="tns:BankService_withdrawResponse"/>
            <fault name="AccountException" message="tns:AccountException"/>
        </operation>
    </portType>

The service interface generated from the WSDL represents the functionality and operations we wish to provide to client applications:

interface BankService extends java.rmi.Remote{
    String createAccount(String acctName,float initBalance) throws RemoteException, AccountException;
    void deposit(String acctID, float amount) throws RemoteException, AccountException;
    void withdraw(String acctID, float amount) throws RemoteException, AccountException;
    float getBalance(String acctID, String acctName) throws RemoteException, AccountException;
    String getAccountID(String acctName) throws RemoteException, AccountException;
}

You will note that this service interface extends the java.rmi.Remote interface and that all methods throw (at least) the java.rmi.RemoteException exception, a requirement of the JAX-RPC specification.
  1. Create a service implementation.  The implementation class (BankServiceImpl) implements the service interface (BankService) and delegates various calls to a Bank instance it creates.  For example, the createAccount() method simply calls the underlying Bank's addNewAccount() method (m_bank is an instance of BankMemDB obtained from the BankFactory):
    public String createAccount(String acctName,float initBalance) throws RemoteException,AccountException {
        return m_bank.addNewAccount(acctName,initBalance);
    }

Creating a client application

Once the service has been created and deployed a client application that leverages the service can be created from the WSDL file generated as part of the service generation process (see Generating, Compiling, and Deploying the Application). 

The Oracle Application Server's client generation tool creates, in addition to the classes required by the JAX-RPC runtime, a convenience class that shields the developer from some of the more mundane JAX-RPC service instantiation tasks.  This class, referred to as a utility client, is leveraged to invoke methods on the remote service by the banking application (BankApplication) (m_endpoint is the class attribute for the utility client):

        void demoGoodAccount() throws Exception {
        String accountID = m_endpoint.createAccount(DEMO_USER1,2000.50f);
        // ... print statements removed for clarity ...
        m_endpoint.deposit(accountID,500.50f);
        float balance = m_endpoint.getBalance(accountID,DEMO_USER1);
        System.out.println("Current balance is now " + balance);
        System.out.println("Withdrawing $250.00 from account");
        m_endpoint.withdraw(accountID,250.00f);
        balance = m_endpoint.getBalance(accountID,DEMO_USER1);
    }

The javadocs for the components above can be found here.

Given the bank implementation and support classes, the service interface, and the service implementation we can proceed with the generation and deployment of a web service.

How to Run the Sample

In this sample we will create a banking web service that wraps and delegates its functionality to an underlying bank object.

Examining the How-To Distribution

The How-To zip file should contain the following:

Setting Up the Application

Please check to make sure that the following properties are configured correctly in the ant-oracle.properties file located in the root of the sample's distribution (NOTE:  Some of these properties will default to the values of corresponding environment variables as noted below.  If you have these variables setup in your environment you may not have to alter the values in the file).  If necessary, modify these variable to the proper values for you environment:
In addition, please make sure that the ant command associated with the OC4J ant distribution is in your execution path (%ORACLE_HOME%/ant/bin).

Configuring the Environment for a Managed OracleAS Instance

If you are running a managed version of the Oracle Application Server 10g, you are using OPMN, you must change the following values to match your configuration:

In addition, please make sure that the ant command associated with the OC4J ant distribution is in your execution path (%ORACLE_HOME%/ant/bin).

Running an OC4J Instance

Start an OracleAS 10g (10.1.3) instance as follows:


Generating, Compiling and Deploying the Application

To generate, compile, and deploy the components of this application simply type the following command from a command prompt in the root directory of the sample:

Executing the default "all" target of the supplied build.xml file performs the following steps:

                  <oracle:genInterface wsdl="${src.webservice.dir}/bank.wsdl"
            output="${src.webservice.dir}"
            packageName="topdown.service">
            <classpath>
                <pathelement location="${ORACLE_HOME}/webservices/lib/wsa.jar"/>
            </classpath>
        </oracle:genInterface>

                  <oracle:topDownAssemble appName="${app.name}"
            wsdl="${src.webservice.dir}/bank.wsdl"
            className="topdown.service.BankServiceImpl"
            input="${bld.webservice.dir}"
            output="${out.dir}"
            ear="${lib.dir}/${ear.name}.ear">
            <classpath>
                <pathelement location="${ORACLE_HOME}/webservices/lib/wsa.jar"/>
            </classpath>
        </oracle:topDownAssemble>

        The output of this task is a J2EE application module (ear file) that contains the various files required for the deployment of the bank service.
            <oracle:deploy moduleType="ear"
            host="${oc4j.host}"
            port="${oc4j.admin.port}"
            userId="${oc4j.admin.user}"
            password="${oc4j.admin.password}"
            file="${lib.dir}/${app.name}.ear"
            deploymentName="${app.name}"
            logFile="deploy-ear.log"/>


          In addition, in order for the service to be accessible via the Oracle Application Server's web tier it must be bound to an web site:
       
        <oracle:bindWebApp deploymentName="${app.name}"
            host="${oc4j.host}"
            port="${oc4j.admin.port}"
            userId="${oc4j.admin.user}"
            password="${oc4j.admin.password}"
            webModule="${web.name}"
            webSiteName="${oc4j.binding.module}"
            contextRoot="/${app.name}"/>

            <oracle:genProxy wsdl="http://${oc4j.host}:${oc4j.http.port}/${app.name}/${app.name}?wsdl"
            output="${src.cli.dir}"
            packageName="topdown.client.proxy"
            >
            <classpath>
                <pathelement path="${bld.cli.dir}"/>
                <pathelement location="${ORACLE_HOME}/webservices/lib/wsa.jar"/>
            </classpath>
        </oracle:genProxy>


Running the Application

Now that the web service is deployed and the client is generated and compiled we are ready to execute the application.  From a command prompt in the root directory of the sample simply type:

This command executes the client application that performs the following operations and message exchanges (the messge exchange figures below were captured by the JDeveloper 10g TCP Packet Monitor and are provided to illustrate the underlying message exchanges performed during the execution of the remote service invocations):

Create account message exchange
Deposit message exchange
Balance query message exchange
Withdrawl message exchange
Bad account creation msg exchange
Large account creation msg exchange
Large withdrawl msg exchanges

The generated output from the "run" target should be as follows:

         run:
        [java] Created an account for DemoUser1 with $2000.50
        [java] AccountID for DemoUser is 138.2.8.242_DemoUser1
        [java] Depositing $500.50 into account.
        [java] Current balance is now 2501.0
        [java] Withdrawing $250.00 from account
        [java] Attempting to create an account for DemoUser2 with no initial funds
        [java] Could not createAccount for DemoUser2
        [java] Insufficient funds for new account.  You must start an account with more than $0
        [java] Created an account for DemoUser3 with $3000.00
        [java] Attempting to withdraw 2500.00 from the account.  The account cap is 2000.00
        [java] Unable to withdraw funds.
        [java] Exceeded maximum withdrawal of $2000.00

    BUILD SUCCESSFUL

Summary

This how-to provided a detailed look at how to generate a web service from a WSDL document.  We have seen that the process involves: