SCDJWS Study Guide: JAX-RPC
Printer-friendly version |
Mail this to a friend
JAX-RPC Handler
What is JAX-RPC Handler?
When you develop your company's web services, you would like to
have
- A logging component to show how based on SOAP message content specific logging actions can be undertaken;
- A performance track component to calculate the time taken to process a SOAP message;
- An authentucation component to process SOAP headers containing credentials;
- A component to encrypt meesagge before you can send it across the network;
- And more .....
The JAX-RPC sepcification provides us with a feature that can help us
do these things: handlers. A handler is a component that
can be associated with an entire Web service or with a particular Web
service interface. A Web service can have a number of ports—a port is
analogous to a Java interface—and a handler can be associated with just
an interface. JAX-RPC handlers allow you to intercept a SOAP message
at various times during a service invocation. Handlers can be very generic,
such as logging or caching handlers, or very specific, such as handlers
that want to process an application-specific SOAP header.
Handlers can be used in both the client and the server side. If you use
JAX-RPC on the client side, you can have a handler process a SOAP request
message right before it goes on the network, and you can process the
response message before it is returned to the client. Similarly, you
can intercept an incoming SOAP request message on the server before invoking
the service implementation, as well as the outgoing response.
Several handlers can be combined into what is called a "handler chain".
Each handler processes the SOAP message, which is then passed on to
the next handler in the chain. The exact sequence in which this happens
is configurable. If multiple handlers in a handler chain that
are involved in one service invocation need to share information, they can
do so by adding properties to the message context as it is passed from
handler to handler. This message context will be available from the request
to the response. In other words, we can use it to store information on
an incoming request that we can reuse when the response comes back.
A handler is a java class that implements the javax.xml.rpc.handler.Handler
interface. It has three methods (handleRequest, handleResponse,
and handleFault) to handle SOAP requests, responses and faults,
respectively.
public abstract interface javax.xml.rpc.handler.Handler
extends java.lang.Object {
public abstract boolean handleRequest(MessageContext
context);
public abstract boolean handleResponse(MessageContext
context);
public abstract boolean handleFault(MessageContext
context);
public abstract void init(HandleInfo info);
public abstract void destory();
public abstract QName[] getHeaders;
};
In the javax.xml.rpc.handler package, the javax.xml.rpc.handler.GenericHandler
is an abstract class that provides default implementations all the
methods in javax.xml.rpc.handler.Handler interface. You can
extends the javax.xml.rpc.handler.GenericHandler class to write
your own handler and you only overridden methods you want to.
When you design SOAP message JAX-RPC handlers you must decide the number of handlers, the order in which the handlers will be executed, and whether to invoke a back-end component.
Handler Life Cycle
The JAX-RPC runtime system considers all instances of a particular handler
class to be equivalent. This allows the JAX-RPC runtime system to pool
handler instances, although this is not required.
The life cycle of a handler consists of two states. A handler
either does not exist, or is in a ready state. Once a handler is in
its ready state, its various handle() methods can be invoked. The life-cycle
methods of a handler should be used to initialize and clean up resources
within a handler instance. A handler instance has two life-cycle methods.
When a new instance of a handler is created, its init() method is called. That allows
you to set up things that you can use for multiple invocations. Before
the handler is removed, the destroy()
method is called, so that you can do cleanup in there.
On a service client the handleRequest()
method is invoked before the actual SOAP message is sent, and the
handleResponse() method is invoked
before the actual response is received.
On a service endpoint, the handleRequest()
method is invoked before the actual service endpoint is invoked, and
the handleResponse() method is invoked before the actual response is
sent to the client.
On a service client the handleFault()
method is called before receiving a SOAP fault, and on the service endpoint
the handleFault() method is
called before generating a SOAP fault.
Stop JAX-RPC Handler's Processing
There are several ways to stop processing the message. One way is to throw
a JAXRPCException that signals that a run-time error has occurred
in the processing of the message. The server's handler chain will produce
a SOAP fault and send it back to the client.
For another way to return false, the server will stop processing the handleRequest()
side of the chain and instead start processing the handleResponse()
side of the handler chain. The handlers in the chain are responsible for
producing the response SOAP message, either in this handleRequest()
method or in one of the handleResponse() methods called in the
chain. This alternative is intended for a scenario in which there is
no error, but the handler wants to process the response itself.
Handlers can be configured either programmatically or, in case you are
running a J2EE application server, they are configured in the Web
service deployment descriptor. Handlers and handler chains are defined
on a per service basis. They can be defined in both the server and client
side deployment descriptors.
JAX-RPC defines a mechanism with which you can manage service invocations
by intercepting request and response messages without having to change
the actual service consumer or provider. In J2EE, you can configure
handlers in a deployment descriptor, without writing any code, providing
you with a powerful way of controlling SOAP message as they pass through
your system.
Here we will look at how to write generic handlers and how to configure
them for a JAX-RPC client and a JAX-RPC server.
JAX-RPC Server-Side Handler
Here is a sample of a Hello Web Service:
- a) Service Definition Interface
package com.example.service;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloIF extends Remote {
public String sayHello(String s) throws RemoteException;
}
- b) Service Implementation
package com.example.service;
public class HelloImpl implements HelloIF {
public String message ="Hello";
public String sayHello(String s) {
return message + s;
}
}
The following code is a simple performance track handler
on the above Hello Web service. The performance measurement is the
spending time between SOAP request and SOAP response. In this
sample, we store the transaction start time (the current system time)
in the message context as a property called "startTime" in the handleRequest
method. The application server will guarantee that the same message context
object is passed into the handleResponse method, so that we can measure
the elapsed time there:
package com.example.handler;
public class PerformanceHandler extends GenericHandler {
protected HandlerInfo info = null;
protected Logger logger = null;
public void init(HandlerInfo arg) {
info = arg;
logger = Logger.getLogger("c://temp//HelloWorldServiceLog");
}
public void destroy() {
try {
logger.close();
} catch (Exception
x) {}
}
public boolean handleRequest(MessageContext
context) {
try {
Long startTime = new
Long(System.currentTimeMillis())
context.setProperty("startTime", start);
} catch (Exception
x) {
x.printStackTrace();
}
return true;
}
public boolean handleResponse(MessageContext
context) {
try {
Long startTime = (Long)context.getProperty("startTime");
long elapsedTime = System.currentTimeMillis() - startTime.longValue;
logger.write("Elapsed time is " + elapsedTime+"\n");
} catch (Exception
x) {
x.printStackTrace();
}
return true;
}
}
Here is the deployment descriptor for our sample service, showing how
a handler class called handler.PerformanceHandler is registered for
the HelloWorldService service (webservices.xml). The list shows that
webservices.xml is also used for configuring the handlers via the <handler>
element, which contains a logical name for the handler in the <handler-name>
element and the actual Java class handler reference inside the <handler-class>
element.
<webservices>
<webservice-description>
<webservice-description-name>HelloWordService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/HelloWorld.wsdl</wsdl-file>
<jaxrpc-mapping-file>WEB-INF/mapping.xml</jaxrpc-mapping-file>
<port-component>
<port-component-name>....</port-component-name>
<wsdl-port>
<namespaceURI>....</namespaceURI>
<localpart>.........</localpart>
</wsdl-port>
<service-endpoint-interface>pack.HelloWorld</service-endpoint-interface>
<service-impl-bean>
<servlet-link>...</servlet-link>
</service-impl-bean>
<handler>
<handler-name>Performance Handler</handler-name>
<handler-class>com.example.handler.PerformanceHandler</handler-class>
</handler>
</port-component>
</webservice-description>
</webservices>
In the server-programming model, a handler can be configured with initial parameters < init-param>. Handlers can also obtain such configuration information when they are initialized. This configuration information is passed as a part of the HandlerInfo object that is passed to the init() method of the handler instance.
<webservices>
<webservice-description>
..........
<port-component>
.........
<handler>
<handler-name> ... </handler-name>
<handler-class>.... <
/handler-class>
<init-param>
<param-name>loglevel1</param-name>
<param-value>stdout</param-value>
</init-param>
<init-param>
<param-name>loglevel2</param-name>
<param-value>log</param-value>
</init-param>
</handler>
</port-component>
</webservice-description>
</webservices>
Here is code fragment of a JAX-RPC handler to handle the configuration
in the file:
public void init(HandlerInfo handlerInfo) {
Map handlerConfig = handlerInfo.getHandlerConfig();
String logLevel1 = (String)handlerConfig.get("logLevel1");
String logLevel2 = (String)handlerInfo.get("logLevel2");
....
}
JAX-RPC Client-Side Handler
We can also add the above handler into the client code to
trace the SOAP message round-trip time.
The following is a sample of static-stub client with the perfomance
handler:
package hello;
import javax.xml.rpc.Stub;
.........
public class HelloClient {
public static void main(String[] args) {
try {
MyHelloService_Impl
impl = new MyHelloService_Impl();
Stub stub = (Stub)(impl.getHelloIFPort());
stub._setProperty (javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, args[0]);
HelloIF hello = (HelloIF)stub;
//Get Iterator
for all service ports
Iterator iter = impl.getPorts();
// Now create a
new List of HandlerInfo objects - only one really. Our client handler
List handlerChain
= new ArrayList();
handlerChain.add(new
HandlerInfo(PerformanceHandler.class, null, null));
//Get Handler Registry
HandlerRegistry registry
= impl.getHandlerRegistry();
//Register each port
with the handler
while (iter.hasNext())
registry.setHandlerChain((QName)iter.Next(),
handlerChain);
System.out.println(hello.sayHello(args[1]));
} catch (Exception
ex) {
ex.printStackTrace();
}
}
}
The following is a sample of DII client with the perfomance
handler:
package dii;
import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;
import com.example.handler.PerformanceHandler;
public class HelloClient {
private static String qnameService = "MyHelloService";
private static String qnamePort =
"HelloIF";
private static String BODY_NAMESPACE_VALUE
= "urn:Foo";
private static String ENCODING_STYLE_PROPERTY
="javax.xml.rpc.encodingstyle.namespace.uri";
private static String NS_XSD = "http://www.w3.org/2001/XMLSchema";
private static String URI_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/";
public static void main(String[] args)
{
System.out.println("Endpoint
address = " + args[0]);
try {
ServiceFactory factory = ServiceFactory.newInstance();
Service service =factory.createService(
new QName(qnameService));
QName port = new QName(qnamePort);
// Now create
a new List of HandlerInfo objects - only one really. Our client handler
List handlerChain
= new ArrayList();
handlerChain.add(new
HandlerInfo(PerformanceHandler.class, null, null));
HandlerRegistry registry
= service.getHandlerRegistry();
registry.setHandlerChain(port,
handlerChain);
//Create an instance of the Call object
Call call = service.createCall(port);
//Set service endpoint
call.setTargetEndpointAddress(args[0]);
//Configure your Call instance with its setter methods
call.setProperty(Call.SOAPACTION_USE_PROPERTY,
new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
call.setProperty(ENCODING_STYLE_PROPERTY,URI_ENCODING);
//Set definition of the return Type
QName QNAME_TYPE_STRING =new QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);
//Set Name of the method to invoke
call.setOperationName(
new QName(BODY_NAMESPACE_VALUE,"sayHello"));
//Set parameters defintions in the call object
call.addParameter("String_1", QNAME_TYPE_STRING,
ParameterMode.IN);
String[] params = { "Murph!" };
//Finally invoke the method
String result = (String)call.invoke(params);
System.out.println(result);
} catch (Exception
ex) {
ex.printStackTrace();
}
}
}
In the JAX-RPC Server-Side Handler, we have shown how to add init parameters
into HandleInfo class by using the configuration file. In the above stand-alone
JAX-RPC client code, we can use "programmatic configuration: HandlerInfo"
to pass the init parameters to a handler instance:
.........
//Now create a new List of HandlerInfo objects
List handlerChain = new ArrayList();
//Create the init parameters information
Map logConfig = new HashMap();
logConfig.put("logLevel1", "stdout");
logConfig.put("logLevel2", "file");
handlerChain.add(new HandlerInfo(PerformanceHandler.class,
logConfig,
null));
........
Client side handlers can be similarly arranged as a 'handler chain' inside the <service-ref> tag of J2EE1.4 clients (for example web.xml,ejb-jar.xmlor an application-client.xml).
<service-ref>
<service-ref-name>service/MyHelloServiceRef</service-ref-name>
<service-interface>javax.xml.rpc.Service</service-interface>
....
<port-component-ref>
....
</port-component-ref>
<handler>
<handler-name>My
Handler One</handler-name>
<handler-class
>com.example.handler.MyHandlerOne</handler-class>
<port-name>...</port-name>
</handler>
<handler>
<handler-name>My
Handler Two</handler-name>
<handler-class>com.example.handler.MyHandlerTwo<
/handler-class>
<port-name>...</port-name>
</handler>
</service-ref>
Note: Unlike the server-side handlers, client side handlers are associated
with <service-ref> (service references) instead of <port-component>
(port component references). However, they have a configurable parameter,
<port-name>, by which handlers can be associated with the port
of the service which is invoked. With port names, you can restrict which
handlers to run when a service endpoint (WSDL port) is invoked.
Handlers Process
Handler Chains
A HandlerChain delegates processing of the SOAP message to its
configured chain of handlers. When you design your SOAP message handlers,
you must decide the number of handlers needed to perform all the work and
the sequence of handlers excution. You specify the handlers in the web-services.xml
deployment descriptor file. An ordered group of handlers is referred to as
a handler chain.
The handleRequest, handleResponse and handleFault
methods for a SOAP message handler get access to the SOAPMessage
from the SOAPMessageContext. The implementation of these
methods can modify the SOAPMessage including the headers and body elements.
When invoking a Web service, Web service container executes handlers
as follows:
- The handleRequest() methods of the handlers in the handler chain are all executed, in the order specified in the web-services.xml file. Any of these handleRequest() methods might change the SOAP message request.
- When the handleRequest() method of the last handler in the handler chain executes, Web service container invokes the backend component that implements the Web service, passing it the final SOAP message request.
- When the backend component has finished executing, the handleResponse() methods of the handlers in the handler chain are executed in the reverse order specified in the web-services.xml file. Any of these handleResponse() methods might change the SOAP message response.
- When the handleResponse() method of the first handler in the handler chain executes, Web service container returns the final SOAP message response to the client application that invoked the Web service.
handleRequest Method
The handleRequest method performs one of the following steps after performing handler specific processing of the request SOAP message:
- Return true to indicate continued processing of the request handler chain. The HandlerChain takes the responsibility of invoking the next entity. The next entity may be the next handler in the HandlerChain or if this handler is the last handler in the chain, the next entity is the target service endpoint. The mechanism for dispatch or invocation of the target service endpoint depends on whether the request HandlerChain is on the client side or service endpoint side.
- Return false to indicate blocking of the request handler chain. In this case, further processing of the request handler chain is blocked and the target service endpoint is not dispatched. The JAX-RPC runtime system takes the responsibility of invoking the response handler chain next with the appropriate SOAPMessageContext. The Handler implementation class has the responsibility of setting the response SOAP message in the handleRequest method and perform additional processing in the handleResponse method. In the default processing model, the response handler chain starts processing from the same Handler instance (that returned false) and goes backward in the execution sequence.
- Throw the javax.xml.rpc.soap.SOAPFaultException to indicate a SOAP fault. The Handler implementation class has the responsibility of setting the SOAP fault in the SOAP message in either handleRequest and/or handleFault method. If SOAPFaultException is thrown by a server-side request handler's handleRequest method, the HandlerChain terminates the further processing of the request handlers in this handler chain and invokes the handleFault method on the HandlerChain with the SOAP message context. Next, the HandlerChain invokes the handleFault method on handlers registered in the handler chain, beginning with the Handler instance that threw the exception and going backward in execution. The client-side request handler's handleRequest method should not throw the SOAPFaultException. Refer to the SOAP specification for details on the various SOAP faultcode values and corresponding specification.
- Throw the JAXRPCException or any other RuntimeException
for any handler specific runtime error. If JAXRPCException
is thrown by a handleRequest method, the HandlerChain
terminates the further processing of this handler chain. The
Web service container catches the exception, terminates further processing
of the handler request chain, and invokes the handleFault() method
of this handler.
-
On the server side, the HandlerChain generates a SOAP fault that indicates that the message could not be processed for reasons not directly attributable to the contents of the message itself but rather to a runtime error during the processing of the message. Refer to the SOAP specification for details on the various SOAP faultcode values.
-
On the client side, the JAXRPCException or runtime exception is propagated to the client code as a RemoteException or its subtype.
-
handleResponse Method
The handleResponse method performs the processing of the SOAP response message. It does one of the following steps after performing its handler specific processing of the SOAP message:
- Return true to indicate continued processing of the response handler chain. The HandlerChain invokes the handleResponse method on the next Handler in the handler chain.
- Return false to indicate blocking of the response handler chain. In this case, no other response handlers in the handler chain are invoked. On the service endpoint side, this may be useful if response handler chooses to issue a response directly without requiring other response handlers to be invoked.
- Throw the JAXRPCException or any other RuntimeException
for any handler specific runtime error. If JAXRPCException
is thrown by the handleResponse method, the HandlerChain
terminates the further processing of this handler chain. On the
server side, the HandlerChain generates a SOAP fault that indicates
that the message could not be processed for reasons not directly attributable
to the contents of the message itself but rather to a runtime error during
the processing of the message. On the client side, the JAXRPCException
or runtime exception is propagated to the client code as a RemoteException
or its subtype.
handleFault Method
The handleFault method performs the SOAP fault related processing. The JAX-RPC runtime system should invoke the handleFault method if a SOAP fault needs to be processed by either client-side or server-side handlers. The handleFault method does one of the following steps after performing handler specific processing of the SOAP fault:
- Return true to indicate continued processing of the fault handlers in the handler chain. The HandlerChain invokes the handleFault method on the next Handler in the handler chain.
- Return false to indicate blocking of the fault processing in the handler chain. In this case, no other handlers in the handler chain are invoked. The JAX-RPC runtime system takes the further responsibility of processing the SOAP message.
- Throw JAXRPCException or any other RuntimeException for any handler specific runtime error. If JAXRPCException is thrown by the handleFault method, the HandlerChain terminates the further processing of this handler chain. On the server side, the HandlerChain generates a SOAP fault that indicates that the message could not be processed for reasons not directly attributable to the contents of the message itself but rather to a runtime error during the processing of the message. On the client side, the JAXRPCException or runtime exception is propagated to the client code as a RemoteException or its subtype.
Please note that when a JAXRPCException or RuntimeException raised
on the server is converted to a SOAP fault for the purpose of being transmitted
to the client, there are no guarantees that any of the information it contains
will be preserved.