How-To Develop a Web Service
From an Existing Java Class
First Created: 04-Nov-2004
Last
Updated: 07-Jul-2005
Author: Jon Maron
Introduction
The Oracle Application Server provides the facilities for making a
standard java object accessible via a web service interface.
This
how-to will detail the steps required for exposing an existing class as
a web service available to remote clients.
The process of exposing an existing java object as a web service and
creating an
associated client application involves:
- Creating a service
interface. This interface exposes the
functionality and operations the service provides.
- Creating a service
implementation class. This class simply
provides implementations for each of the methods declared in the
service interface. In this example the implementation class
will
instantiate, and delegate method calls to, the underlying bank object.
- Generating the web service's
WSDL file and associated artifacts
(mapping file, etc.). A WSDL file is generated based on the
service interface provided so that clients can now be created to access
the web service.
- Packaging the service as an
application module. The service
classes and all associated generated artifacts are packaged up as
standard J2EE deployment modules.
- Deploying the
service. The service module is deployed to
the Oracle Application Server.
- Generating the client
proxies. Client proxies/stubs are
generated from the WSDL file for the deployed service.
- Writing a client
application. An application is written
that leverages the client proxies to invoke remote service operations.
Admittedly, the argument could be made that it may be simpler to simply
modify the existing java class and expose it directly as a web service
(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?
- Some familiarity with the
standard J2EE deployment model.
What
are the Software
Requirements?
- OracleAS 10.1.3 is
installed, available from OTN
- Sun's JDK 1.4.2_03 or above,
available here
- Apache Ant 1.6.2 or above,
to build the application, available from here
What
are the Notations?
The following conventions are used throughout the document:
- %ORACLE_HOME%
- The directory you installed OC4J too.
- %J2EE_HOME%
- The directory where the oc4j.jar file exists
within OC4J. This is typically 2 directories under %ORACLE_HOME%.
For
example, if you installed OC4J to c:\
then %J2EE_HOME% would
be c:\j2ee\home
- %JAVA_HOME%
- The directory where your JDK is installed
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 we will:
- Create a service interface.
The service interface 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.
- 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:
- bottomup/src/
- contains all Java source code for the
example.
- client/bottomup/client
- BankAccountClient.java
- A client
application that invokes the bank service's methods.
- service/bottomup/service
- Account.java
- A bank account
object.
- AccountException.java
- An exception for
indicating account transgressions
(insufficient funds, maximum withdrawl exceeded, etc.).
- Bank.java
-
The interface exposed by the bank object wrapped by the service
implementation.
- BankFactory.java
- A factory that
instantiates objects that implement
the Bank interface.
- BankMemDB.java - An
in-memory implementation of the Bank interface.
- BankService.java -
The implementation of the bank web service (implements
BankServiceInterface).
- BankServiceInteface.java
- The interface exposed by
the bank web service.
- bottomup/bottomup-how-to.html
- This document.
- bottomup/build.xml
- An Ant build file.
- bottomup/ant-oracle.xml
- Used by build.xml to
execute
the various Oracle Application Server tasks required for assembly and
deployment of the web service and client.
- botomup/ant-oracle.properties
- Properties required for
the proper execution of the sample's build script.
Setting
Up the Application
Make sure the following
properties are configured correctly in the oracle-ant.properties
file located in the root directory of this how-to 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 variables to
the proper values for you environment:
- oracle.home
- the root directory of oracle installation. Defaults to the
ORACLE_HOME environment variable.
- java.home -
the root directory of JDK installation. Defaults to JAVA_HOME
environment variable.
- oc4j.host
- the hostname of the platform on which the target OC4J instance is
running. Defaults to localhost.
- oc4j.http.port
- the port on which the OC4J HTTP listener is listening.
Defaults to 8888.
- oc4j.admin.port
- the port on which the OC4J administration processor is
listening. Defaults to 23791.
- oc4j.admin.user
- the name of the OC4J administrator. Defaults to "oc4jadmin".
- oc4j.admin.password
- the password for the OC4J administrator. Defaults to
"welcome".
- oc4j.binding.module
- the name of the HTTP web site to which the deployed application is
bound. Defaults to "default-web-site".
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 OC4J 10.1.3 instance
as follows:
- %ORACLE_HOME%/bin/oc4j
start
Note that the oc4j command
expects the JAVA_HOME environment variable to point to a full JDK
installation.
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:
- Compiles all the web service
source files (bank and service
interfaces and implementations) located in src/service
subdirectories.
- Generates the WSDL file and
associated artifacts (JAX-RPC mapping
file, etc.). The generation is performed utilizing the assemble
ant task:
<oracle:assemble
appName="${app.name}"
serviceName="${app.name}"
interfaceName="bottomup.service.BankService"
className="bottomup.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:assemble>
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.
- Deploys the application
module. The .ear file generated in
the previous step is now deployed to the server using the ant
deploy
ant task:
<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}"/>
- Creates the client proxy
source files. Once the service is
deployed and available the client proxy can be generated from the
available WSDL file (you can actually view the generated WSDL file of
the service by pointing a browser to the location http://<OC4J
host>:<OC4J http
port>/bank/bank?wsdl).
The genProxy
ant task is utilized for this
purpose:
<oracle:genProxy
wsdl="http://${oc4j.host}:${oc4j.http.port}/${app.name}/${app.name}?wsdl"
output="${src.cli.dir}"
packageName="bottomup.client.proxy"
>
<classpath>
<pathelement path="${bld.cli.dir}"/>
<pathelement
location="${ORACLE_HOME}/webservices/lib/wsa.jar"/>
</classpath>
</oracle:genProxy>
- Compiles the client
application and associated proxy files.
The proxy files generated in the previous step and the client
application utilizing the proxy for remote communication are compiled
and placed in the build/bank/bank-client
directory.
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):
- Creates an
account for
DemoUser1 with an initial balance of
$2000.50:
- Deposits an additional
amount of
$500.50 into the account for
DemoUser1:
- Queries for the current
balance:
- Withdraws $250.00 from the
account of DemoUser1:
- Attempts to create an
account
with $0.00 for DemoUser2.
This attempt fails since the amount of funds is instufficient:
- Creates
an account for DemoUser3 with
an initial balance of
$3000.00:
- Attempts to withdraw
$2500.00 from the account of
DemoUser3. This attempt fails since the withdrawl amount
exceeds
the maximum allowed of $2000.00:
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 access to an existing java
class can be facilitated using a web service interface. We
have
seen that the process involves:
- Creating a service interface
that exposes the desired
functionality.
- Creating an implementation
of the service interface that utilizes
an instance of the existing class to service requests.
- Generating the required
service artifacts (most imporantly, the
WSDL file describing the service).
- Deploying the service to the
Oracle Application Server.
- Generating the client proxy
code based on the service WSDL file.
- Writing a client application
the leverages the client proxies to
interact with the deployed service.