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.
|