How to use Handler Chain with JAX-RPC Web Service

Date: 30-Sept-2003

After reading this how-to document you should be able to:

  • Understand what is a JAX-RPC Web Service
  • Write a JAX-RPC Web Service that uses Handler Chain mechanism to modify SOAP messages.

Introduction

This document demonstrates how to use handlers with the JAX-RPC Web Services.

Overview of JAX-RPC Web Services

The Java API for XML-based remote procedure calls (JAX-RPC) provides standard way of building portable and interoperable SOAP based Web Services. It simplifies the process of building Web services that incorporate XML-based RPC. In JAX-RPC, a remote procedure call is represented by an XML-based protocol such as SOAP. The SOAP specification defines envelope structure, encoding rules, and a convention for representing remote procedure calls and responses. These calls and responses are transmitted as SOAP messages over HTTP. Read more on JAX-RPC Web Services here.

The Handler Mechanism

One of the most powerful features of JAX-RPC Web Services is the Handler mechanism. A Handler can be used to modify SOAP request or response message at the client and the server side. A Handler class is tied to the service endpoint and can be configured to intercept the SOAP message and perform various operations on it at any of the following points in the client-service communication:

1. At the client, just after the SOAP request is created and before it is sent to the Web Service.
2. At the Web Service end, before the SOAP request is processed by the Web Service.
3. At the Web Service end before sending the SOAP reponse to the client.
4. At the client just before the JAX-RPC runtime processes the SOAP response received from the Web Service.

The handler class can be used to perform any validations on the SOAP request message, to encrypt or decrypt the message for security reasons, to perform logging at various points while the SOAP request is served.

An implementation of Web Service might require several handler classes to be invoked one after the another before the request or response is served. The JAX-RPC runtime is capable of invoking several handler classes in a chain. This concept is called handler chain.

Software Requirements

  • Oracle Application Server Containers for J2EE 10g or later. You can download the OC4J from Oracle Technology Network. 

  • JDK1.4.x or above This can be downloaded here .

  • Apache Ant v1.5 or later.

  • How To Example Source code zip

Description

The Handler Implementation

The handler class should implement javax.xml.rpc.handler.Handler Interface . The interface consists of following methods that should be defined by the handler implementation class.

  • void init(java.util.Map config) :The init method is used to initialize the handler instance.
  • void handleRequest(MessageContext context, HandlerChain chain): This method handles RPC request messages.
  • void handleResponse(MessageContext context, HandlerChain chain) : This method processes the RPC response messages.
  • void destroy() : This methods ends the life cycle of handler instance.

The MessageContext class provides access to SOAP message passed in request or response. The class javax.xml.soap.SOAPMessage can be used to perform various operations on the SOAP message.

Developers can extend javax.xml.rpc.handler.GenericHandler class which provides default implementation of Handler interface and you can override only the required methods.

The How-To Example

This how-to explains how to implement handler chain mechanism in JAX-RPC services. The example's handler chain consists of three handler classes called just before processing the request at the Web Service end.

The example implements a simple Bank functionality wherein the client can create a new account, enquire details of an existing account, deposit amount and withdraw amount. The Web Service provides implementation of all these operations using following important classes and interfaces:

  • BankMemDB : The class that stores all the details of accounts in memory.
  • Bank : The interface to BankMemDB class.
  • BankBean : The class that provides implementation of operations that can be performed on an account.
  • BanBeanInterface : The interface to the BankBean class.
  • BankFactory : The class that creates an instance of bank.
  • Account : The class that represents an account.

The Web Service is configured to invoke three handlers one after the other in a chain as follows:

  • AccountTransactionHandler : This handler class logs all the operations performed on the Bank Web Service.
  • NewAccountHandler : This handler class verifies the SOAP request to check if the initial balance is provided when creating the account.
  • AccountWithdrawalHandler :This handler checks the limit of withdrawal amount before withrawal operation is performed on the account.
The how to uses Oracle's Web Services Assembler(WSA) tool to generate the service artifacts. The Web Service assembler tool takes as input, an XML config file that describes the service. It uses the definitions in the WSDL file to generate service artifacts like types, interfaces, tie and stub classes, compiles all the classes and produces an Enterprise Archive file that can be deployed to an application server to run the Web Service.

Listing 1 shows the WSA configuration file service-config.xml that defines the handler classes.

<web-services >
<output>./dist/oracleBank.ear</output>
<context>/oracleBank</context>
<web-service>
<display-name>Oracle Bank HandlerChain Test</display-name>
<description>A webservice that demonstrates HandlerChain development in Oracle Application Server 10g</description>
<input>build/classes/service</input>
<source-output-dir>build/src/service</source-output-dir>
<java-port>
<interface-name>oracle.demo.handlerchain.service.BankBeanInterface </interface-name>
<class-name>oracle.demo.handlerchain.service.BankBean</class-name>
<uri>/oracleBank</uri>
<handler>
<handler-name>Account Logger</handler-name>
<handler-class>oracle.demo.handlerchain.service.AccountTransactionHandler </handler-class>
</handler>
<handler>
<handler-name>New Account Handler</handler-name>
<handler-class>oracle.demo.handlerchain.service.NewAccountHandler </handler-class>
</handler>
<handler>
<handler-name>Withdrawal Handler</handler-name>
<handler-class>oracle.demo.handlerchain.service.AccountWithdrawalHandler </handler-class>
</handler>
</java-port>
</web-service>
</web-services>

Listing 1

Listing 2 shows the handleRequest method definition of NewAccountHandler class . As you can see, the class implements GenericHandler class, the defalut implementation of Handler interface. View complete code here.

public class NewAccountHandler extends GenericHandler{


 ...
 ...

 public boolean handleRequest(MessageContext ctx){
  // get the SOAPMessageContext
SOAPMessageContext sctx = (SOAPMessageContext)ctx; // retrieve SOAP message from the context
SOAPMessage sm = sctx.getMessage(); // variable for soap body
SOAPBody sb = null;
try{ // get soap body from soap message
sb = sm.getSOAPBody(); // get all child nodes of soap body element
NodeList nl = sb.getChildNodes(); // iterate through child elements
for(int i = 0; i < nl.getLength(); i++){
// get the node
Node node = nl.item(i); // get the soap element
if(node instanceof SOAPElement){
SOAPElement se = (SOAPElement)node; // get the name of operation being performed
String operationName = se.getLocalName(); // if operation is to create new account
if(operationName.equals("createAccount")){
// call method to check initial balance
checkInitialBalance(sctx,se);
}
}
}
}
// catch any exceptions
catch(SOAPException ex){
System.out.println(ex.getMessage());
}
return true; }

private void checkInitialBalance(SOAPMessageContext sctx,SOAPElement se) {
SOAPFactory sf = null;
Detail det = null;
SOAPMessage sm = null;
MessageFactory mf = null;
try{
// create instance of SOAP factory
sf= SOAPFactory.newInstance();
det = sf.createDetail();

// create instance of Message Factory
mf = MessageFactory.newInstance();
// create message
sm = mf.createMessage();
}
catch(Exception ex){
ex.printStackTrace();
}
// get all child nodes of input SOAP element
NodeList nl = se.getChildNodes(); float amt = 0f;
// iteratre through each element
for(int i = 0; i < nl.getLength();i++){
// get a node
Node node = nl.item(i);
if(node instanceof SOAPElement){
// get SOAP element
SOAPElement s = (SOAPElement)node;
if(s.getLocalName().equals("float_2")){
// get the values of amount
amt = new Float(s.getValue().trim()).floatValue();
}
}
}
if(amt <= 0f){
throw new JAXRPCException(new SOAPFaultException(faultCode,"Insufficient funds for new account. You must start an account with more than $0", "",det));
}
} ... ... }

Listing 2

Listing 3 shows the part of Web Service implementation class, that defines methods to create account, withdraw and deposit amount.

 /*
* Constructor definition
*/
public BankBean(){ // get an instance of Bank db
m_bank = BankFactory.createBank();
}

/*
* This method creates new account in the bank
*/
public String createAccount(String acctName,float initBalance) throws RemoteException,AccountException {
// call method on BankMemDB to create new account
return m_bank.addNewAccount(acctName,initBalance);
}

/*
* This method deposits amount in the existing account in bank
*/
public void deposit(String acctID, float amount) throws
RemoteException, AccountException {
// get instance of existing account from bank
Account theAccount = m_bank.getAccount(acctID); // throw exception if account does not exist
if(theAccount == null){
throw new AccountException("No account found for "+ acctID);
}
// deposit the input amount
theAccount.deposit(amount);
}

/*
* This method withdraws amount from existing account
*/
public void withdraw(String acctID, float amount) throws
RemoteException, AccountException {
// get instance of existing account from bank
Account theAccount = m_bank.getAccount(acctID);

// throw exception if account does not exist
if(theAccount == null){
throw new AccountException("No account found for " + acctID);
} // withdraw amount from bank theAccount.withdraw(amount);
}

Listing 3

The definition of class methods reduces to simply calling methods on Account to perform operations on an account. All the verfication while creating account or withdrawing amount is done in the Handler classes.

The complete Web Service code can be seen here.

Listing 4 shows the part of client code that calls the Web service method to create a new account with insufficient initial balance. The request is intercepted by NewAccountHandler class which raises an exception.


private void demoInsufficientFunds() throws Exception{ // get bank service from Service Factory using the service url
OracleBank bankService = (OracleBank)m_serviceFactory.loadService(new URL(m_serviceURL ),OracleBank.class,null);

// get a handle to the service
BankBeanInterface port = bankService.getBankBeanInterfacePort();
((Stub)port)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,m_serviceURL);
System.out.println("Attempting to create an account for " + DEMO_USER2 + " with no initial funds");
try{
// try to create an account with 0 initial balance
port.createAccount(DEMO_USER2,0f);
}
// catch any exceptions
catch(Exception ex){
System.out.println("Could not createAccount for " + DEMO_USER2);
System.out.println("Exception: " + ex.getMessage());
}
}

Listing 4

The complete client code can be seen here.

Preparing and Running the Example

Extract the handlerchain.zip file. This will create handlerchain directory containing documentation and source code of the how-to.

In the following steps <OC4J> refers to directory where OC4J is installed.

Step a:

Start OC4J from <OC4J>/j2ee/home directory on a command promt as follows:

java -jar oc4j.jar

Step b:

Navigate to handlerchain, the sample home directory on new command window . Include ANT_HOME/bin in the PATH environment variable. Set the following properties required by the ant build script

  • Set the ORACLE_HOME environment variable to your <OC4J> root directory.
    Example:

    Windows : set ORACLE_HOME=c:\oc4j10g
    Unix    : ORACLE_HOME=/home/oc4j10g
              export J2EE_HOME 
  • Set the J2EE_HOME environment variable to your <OC4J>/j2ee/home directory.
    Example:

    Windows : set J2EE_HOME=c:\oc4j10g\j2ee\home
    UNIX    : J2EE_HOME=/home/oc4j10g/j2ee/home
              export J2EE_HOME 
  • Set the JAVA_HOME environment variable to your Java installation directory.
    Example:

    Windows : set JAVA_HOME=c:\j2sdk1.4
    UNIX    : JAVA_HOME=/home/j2sdk1.4
              export JAVA_HOME
Quick Start

Once you have the server configured and running, execute the following command from the prompt in Step b:

>ant

The service artifacts and implementation class will be compiled and placed into a WAR which is then placed into an EAR.

The EAR will be deployed to OC4J. Next the stubs and client will be compiled. Finally the service will be invoked by the client and you should see the output both in the server's message output and on the stdout of the client.

Step by Step
Step 1:

Compile service classes.

This step will compile the implementation and supporting classes of the Web Service.

To compile the Web Service classes type: ant compile-service-artifacts

Step 2:

Generate the service artifacts. Execute the following command:

This step will generate the service artifacts which include interfaces and Tie classes. Examine the service-config.xml file.

To generate the service artifacts type: ant gen-service

The source code for the service-artifacts will be placed in the handlerchain/build/src/service directory using the package name oracle.demo.handlerchain.service. Examine the handlerchain/config/type-mapping.xml file. This file is used to instruct WSA how to map types to package name. A namespace URI is mapped to a specific package name. Notice that this example maps its types to the package oracle.demo.handlerchain.types.

This WSA tool finally builds a Web Service ear file, in handlerchain/dist directory, that can be deployed to OC4J.

Step 3:

Deploy the Service

The service is now ready to be deployed. Examine the oracleBank.ear in the handlerchain/dist directory using WinZip or any zip file browser. It should contain a WAR file named oracleBank-web.war. This WAR file contains all the service artifacts, implementation classes, as well as the web deployment descriptor (web.xml).

To deploy this ear to a running instance of oc4j type: ant deploy-service

After this task is complete you can check your application by typing in the following url into a web browser: http://localhost:8888/oracleBank/oracleBank

You should be able to invoke some of the methods on this service from the web browser. Currently, invoking services using the browser is restricted to simple types.

Step 4:

Generate the stubs (client artifacts)

This step will generate the stubs for the service. A client application uses a stub to invoke operations on a remote service. Examine the file handlerchain/config/client-config.xml. This is the configuration file that the WebServices Assembler (WSA) tool uses to generate the stubs.

To generate the stubs type: ant gen-stubs

The source for the stubs will be placed in handlerchain/build/src/client.

Step 5:

Run the demo.

You are now ready to run the client. Examine the file BankAccountClient.java in handlerchain/src/client/oracle/demo/handlerchain. Notice that this class uses the stubs to set an endpoint address and to invoke methods on the remote service.

To run the client type: ant run-demo

You should see some sample output on client output console and the server output console.

To undeploy the Web Service execute the command : ant undeploy-service

Resources

Summary

This how-to document explained how write Handler class and use Handler Chain in JAX-RPC Web Services.


E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy