How to process SOAP headers with JAX-RPC Web Service

Date: 12-Nov-2003

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

  • Understand what is a JAX-RPC Web Service
  • Write a JAX-RPC Service that receives and processes SOAP headers.
  • Expose stateless session bean as Web Service endpoint.

Introduction

This document demonstrates how to process SOAP headers with JAX-RPC Web Service.

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 SOAP Headers

A Web Service request and response are carried in a SOAP message to and from a client. The SOAP message consists of an envelope. The envelope structures the message in two main parts: the SOAP header and the SOAP body. So far, most of the clients and Web Services have been sending data in SOAP body. But there might be requirements where the SOAP message has to pass through multiple recipients before reaching the final destination. In such case the intermediate SOAP servers will ideally be working on the metadata of the message while the original message which is intended for the final recipient will remain intact. SOAP headers are an ideal place to pass such context information or the metadata of the SOAP message. For example, you may pass the security information, the transaction information or the user locale or language preferences in the SOAP header.

SOAP headers can also be used to put information that is optional and may or may not be used by the Web Service. For example, a search Web Service takes a search string and optional context information of the search string in SOAP header. A client may search for "Green" and pass "Name of book authors" as context information and the Web Service will search for all the books having Green in the name of authors. If no context information is passed, the Web Service will still work and return everything that is green in the world.

A SOAP message with headers may look something like the following:

<SOAP-ENV:Envelope>
<SOAP-ENV:Header>
<mr:ContextInfo>Name of book Author</mr:ContextInfo>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<mr:SearchString>Green</mr:SearchString>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Sometimes, it is required that the header information should necessarily be processed. For example, if the application is secured and the header provides the authentication information, the service will have to process it before allowing access. In such cases, the mustUnderstand attribute can be used.The attribute takes values of 1 or true for the header to be understood and a values of 0 or false to specify the optional header.

One can also set the actor attribute to specify which intermediate SOAP server should process a particular the header. SOAP provides two pre defined roles; none stating that the header is intended for the final recipient and next stating that the header should be processed by the next SOAP server that receives the message. Following SOAP header element shows how mustUnderstand and actor attribute can be set :

 <mr:ContextInfo
SOAP-ENV:role="http://www.w3.org/envelope/actor/mesgReceiver"
SOAP-ENV:mustUnderstand="1">
2
</mr:ContextInfo>

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

SOAP Headers with JAX-RPC

The JAX-RPC services provide Handler mechanism to modify SOAP request and response messages at client and service side. The handler class implements handleRequest and handleResponse methods to modify the request and response message. The handler class can be configured to work on request and response either at the client or at the server. This handler class mechanism can be used to send SOAP headers with SOAP message to the Web Service.

The handler class provides access to SOAP message that is being transferred between the client and the service. javax.xml.soap.SOAPMessage class can be used inside the handler to manipulate the SOAP message. A SOAPMessage object consists of a SOAPPart object that contains the actual SOAP XML document (or the envelope) sent in SOAP request. The SOAPEnvelope object (obtained from SOAPMessage object) can be used to access SOAP body and header.

The javax.xml.soap.SOAPHeader object can be used to add headers to the SOAP envelope. A SOAPEnvelope object may contain zero or more headers. The javax.xml.soap.SOAPElement can be used to create a valid SOAP element that represents the header element.

The How-To Example

The example with this how to explains how to send SOAP headers from client to JAX-RPC Web Service.

The example Web Service implements a method that approves Credit Card for the users. The user has to provide the correct authentication information (a username and password) for the card request to be accepted.

The client invokes a simple method getCard on Web Service. The SOAP request is intercepted and modified by the client handler class to add the header element containing user's authentication information to SOAP message being sent to the Web Service. The Web Service has a handler class tied to it that intercepts the request, reads the header sent by the client, checks if the authentication information is valid and sends the SOAP request to the Web Service for further processing. If the authentication information is correct, the Web Service sends a response to that client saying that the Credit Card request is approved.

The example exposes stateless session bean as Web Service using Service Endpoint Interface feature of JAX-RPC Web Services. With this feature, a Web service client can access a stateless session bean through its Web service endpoint interface without having to look up the session bean and obtain remote handle to it. The life cycle of the bean is maintained by the Web Service container. Thus the client invokes methods of session bean like that for any other Java class. The advantage of this is that the client can be written in any language and it need not know what technology has been used to implement the Web Service.

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 information in the config 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 client code invoking the Web Service. View complete code here

/*
* Definition of main class of the client
*/
public static void main(String[] args) throws Exception {

String address = DEFAULT_URL;

// get the Web Service URL from runtime arguments
if( args != null && args.length > 0 ) {
address = args[0];
}

// create new instance of service factory
ServiceFactory factory = ServiceFactory.newInstance();

// define QName for Credit Card Service
QName portQname = new QName("http://oracle.j2ee.ws/ejb/Credit",
"CreditServiceInfPort");

//Load ejb WebSerivce client-side proxy(stub).
Service service = factory.loadService( new URL( address + "?WSDL" ),
 CreditServiceEJB.class, null );

// get the stub for Web Service
CreditServiceInf serviceInf =(CreditServiceInf)
service.getPort(portQname,CreditServiceInf.class);

// set the service endpoint URL on stub
((Stub)serviceInf )._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,address);

 // invoke web service method
System.out.println(serviceInf.getCard(NAME));

}

Listing 1

Listing 2 shows the definition of ClientAuthenticator.java , the client handler class. The handler class implements the javax.xml.rpc.Handler interface. The handleRequest method adds the SOAP header containing authentication information to the SOAP request and sends it to the Web Service . View complete code here.

public class ClientAuthenticator implements javax.xml.rpc.handler.Handler
{
...
public boolean handleRequest (MessageContext context) {
out.println("--- In ClientAuthenticator.handleRequest() at "
+formater.format(new Date()));
try { /* add a header with the authentication info into the SOAP message:
*
* <env:Header>
* <ns0:authHeader1 xmlns:ns0="http://oracle.j2ee.ws/ejb/Credit">
* <ns0:id>SCOTT</ns0:id>
* <ns0:password>TIGER</ns0:password>
* </ns0:authHeader1>
* </env:Header>
*/

// get SOAP message context
SOAPMessageContext smc = (SOAPMessageContext) context;

// get SOAP envelope from SOAP message
SOAPEnvelope se = smc.getMessage().getSOAPPart().getEnvelope();

// create instance of SOAP factory
SOAPFactory sFactory = SOAPFactory.newInstance();

// create SOAP elements specifying prefix and URI
SOAPElement sHelem1 = sFactory.createElement("authHeader1",PREFIX,URI);
SOAPElement sCHelem11 = sFactory.createElement("id",PREFIX,URI);

// attach value to id element
sCHelem11.addTextNode("SCOTT");
SOAPElement sCHelem12 = sFactory.createElement("password",PREFIX,URI); // attach value to password element
sCHelem12.addTextNode("TIGER");

//add child elements to the root element
sHelem1.addChildElement(sCHelem11);
sHelem1.addChildElement(sCHelem12); // create SOAPHeader instance for SOAP envelope
SOAPHeader sh = se.addHeader();

// add SOAP element for header to SOAP header object
sh.addChildElement(sHelem1);
}
catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
...
...
}

Listing 2

The client handler class is configured in client-config.xml file that is used by WSA tool to generate Stub classes to access Web Service. Listing 3 shows the part of client-config.xml file that specifies the client handler class.

<web-services>
<web-service>
<input>build/classes/client</input>
<wsdl-input url="http://localhost:8888/creditService/creditService?WSDL"/>
<source-output-dir>build/src/client</source-output-dir>
<wsdl-port>
<port-name>CreditServiceInfPort</port-name>
<handler>
<handler-name>ClientHeaderHandler</handler-name>
<handler-class>oracle.demo.header.ClientAuthenticator</handler-class>
</handler>
</wsdl-port>
<proxy-gen>
<package-name>oracle.demo.header</package-name>
<output>build/classes/client</output>
</proxy-gen>
</web-service>
</web-services>

Listing 3

Listing 4 shows the definition of AuthenticateHandler.java, the service side handler class. The handler class implements javax.xml.rpc.handler.Handler interface. The handleRequest method retrieves the header information from the SOAP request and authenticates the client. View complete code here.

 /*
*The handleRequest method processes the request message.
* It retrieves the authentication information for the request header
* and validates it.
*/
Public Boolean handleRequest (MessageContext context) {
Boolean exit =true;
String name;
out.println("--- In AuthenticateHandler.handleRequest () at "
+formater.format(new Date()));
try {
// get SOAP message context
SOAPMessageContext smc = (SOAPMessageContext) context;

// get SOAP envelope from SOAP message
SOAPEnvelope se = smc.getMessage().getSOAPPart().getEnvelope();

// get the headers from envelope
SOAPHeader sh = se.getHeader();
if(sh==null){
out.println("--- No headers found in the input SOAP request");
exit = false;
} else
// call method to process header
exit=processSOAPHeader(sh);
}
catch (Exception ex) {
ex.printStackTrace();
}
return exit;
}


private Boolean processSOAPHeader (SOAPHeader sh) {
Boolean authenticated = false;
Boolean found = false;

// get the headers in the SOAPHeader
QName[] headers = getHeaders();
javax.xml.soap.Name sName; // if there are no headers
if(headers.length==0) {
out.println("no headers to process");
}else{
// process each header
for(int x=0;x<headers.length;x++){
out.println("SOAP Header that it can process :"+headers[x]);

//look for authentication header element inside the HEADER block
java.util.Iterator childElems = sh.getChildElements();
SOAPElement child;

// iterate through child elements
while (childElems.hasNext()) {

Object elem = childElems.next();
if(elem instanceof SOAPElement ){

// get child element and its name
child = (SOAPElement) elem;
sName = child.getElementName();

// check if this is required header
if (sName.getLocalName().equals(headers[x].getLocalPart())
&& sName.getURI()Equals(headers[x].getNamespaceURI())) {

found = true;// found a SOAP header by this name

// call method to perform authentication
authenticated= processSOAPHeaderInfo (child);
}
}
}
if(found){
out.println("---- header element "+headers[x]+"found in SOAP req");
break;
}
else
out.println("---- header element "+headers[x]+"not found in SOAP req");
}
}
return authenticated;
}

Listing 4

The Web Service handler class is specified in the service-config.xml file that is used by the WSA tool to generate service artifacts. This file also specifies that the stateless session bean has to be exposed as Web Service endpoint. Listing 5 shows the content of service-config.xml file.

<?xml version="1.0"?>
<web-services mode="jsr-109">
<output type="ear">dist/creditService.jar</output>
<context>/creditService</context>
<temporary-directory>build/tmp</temporary-directory>
<web-service
targetNamespace="http://oracle.j2ee.ws/ejb/Credit"
typeNamespace="http://oracle.j2ee.ws/ejb/Credit/types">
<display-name>Credit ejb</display-name>
<description>credit card webservice </description>
<message-format style="rpc" use="literal"/>
<source-output-dir>build/src/service</source-output-dir>
<input>build/creditService.jar</input>
<ejb-port>
<ejb-name>CreditServiceEJB</ejb-name>
<uri>/creditService</uri>
<handler>
<handler-name>SHandler1</handler-name>
<handler-class>oracle.demo.header.AuthenticateHandler</handler-class>
<init-param>
<param-name>id</param-name>
<param-value>SCOTT</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>TIGER</param-value>
</init-param>
<soap-header xmlns:tns="http://oracle.j2ee.ws/ejb/Credit">
tns:authHeader1</soap-header>
</handler>
</ejb-port>

</web-service>
</web-services>

Listing 5

Preparing and Running the Example

Extract the soapheader.zip file. This will create soapheader 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 prompt as follows:

java -jar oc4j.jar

Step b:

Navigate to soapheader, 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 ORACLE_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 on the stdout of the client.

Step by Step
Step 1:

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 soapheader/build/src/service directory using the package name oracle.demo.header.service. The WSA tool takes soapheader/build/creditService.jar that contains Session EJB classes and deployment descriptor as input and generates soapheader/dist/creditService.ear as output.

Step 4:

Deploy the Service

The service is now ready to be deployed. Examine the creditService.ear in the soapheader/dist directory using WinZip or any zip file browser. It should contain JAR files named creditService.jar. This JAR file contains CreditService EJB classes and deployment descriptor. It also contains the Web Services configuration files that expose EJB as Web Service.

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

After this task is complete you can check your Web Service by typing in the following URL into a Web browser: http://localhost:8888/creditService/creditService

Step 5:

Run the demo.

You are now ready to run the client. Examine the file CreditCardClient.java in soapheader/src/client/oracle/demo/header directory. 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 and server output screens.

To undeploy the Web Service execute the command : ant teardown-demo

Resources

Summary

This how-to document explained how to process SOAP headers in a JAX-RPC Web Service and to expose stateless session bean as Web Service endpoint.


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