Thursday, 9 May 2013

JAX-WS SOAP Handler

JAX-WS Soap Handler

SOAP handler is a SOAP message interceptor, which is able to intercept incoming or outgoing SOAP message
and manipulate its values.
For example, A SOAP handler in client side, which will inject client’s computer MAC address into the SOAP
header block for every outgoing SOAP message that is send by the client. In server side, attach another SOAP handler,
to retrieve back the client’s MAC address in SOAP header block from every incoming SOAP message.
So that the server side is able to determine which computer is allow to access the published service.

We understand the soap handler in 2 parts.

Part 1 : SOAP handler in server side


Step 1 :Web Service

package com.test.ws;

import javax.jws.HandlerChain;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public class ServerInfo{
@WebMethod
public String getServerName() {
return "test server";
}
}

Step 2 : Generate necessary Java files for the web service deployment

wsgen -keep -verbose -cp . com.test.ws.ServerInfo

Two files are generated
**********GetServerName.java************
package com.test.ws.jaxws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "getServerName", namespace = "http://ws.test.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getServerName", namespace = "http://ws.test.com/")
public class GetServerName {
}

*********GetServerNameResponse.java**************

package com.test.ws.jaxws;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name = "getServerNameResponse", namespace = "http://ws.test.com/")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "getServerNameResponse", namespace = "http://ws.test.com/")
public class GetServerNameResponse {
@XmlElement(name = "return", namespace = "")
private String _return;
/**
*
* @return
* returns String
*/
public String getReturn() {
return this._return;
}
/**
*
* @param _return
* the value for the _return property
*/
public void setReturn(String _return) {
this._return = _return;
}
}

Step 3 : SOAP Handler
Create a SOAP handler to retrieve the value in SOAP header block, for every incoming SOAP message.

*********MacAddressValidatorHandler.java****************

package com.test.handler;

import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.soap.SOAPFaultException;

public class MacAddressValidatorHandler implements SOAPHandler{

@Override
public boolean handleMessage(SOAPMessageContext context) {

System.out.println("Server : handleMessage()......");

Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

//for response message only, true for outbound messages, false for inbound
if(!isRequest){

try{
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();

//if no header, add one
if (soapHeader == null){
soapHeader = soapEnv.addHeader();
//throw exception
generateSOAPErrMessage(soapMsg, "No SOAP header.");
}

//Get client mac address from SOAP header
Iterator it = soapHeader.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);

//if no header block for next actor found? throw exception
if (it == null || !it.hasNext()){
generateSOAPErrMessage(soapMsg, "No header block for next actor.");
}

//if no mac address found? throw exception
Node macNode = (Node) it.next();
String macValue = (macNode == null) ? null : macNode.getValue();

if (macValue == null){
generateSOAPErrMessage(soapMsg, "No mac address in header block.");
}

//if mac address is not match, throw exception
if(!macValue.equals("90-4C-E5-44-B9-8F")){
generateSOAPErrMessage(soapMsg, "Invalid mac address, access is denied.");
}
//tracking
soapMsg.writeTo(System.out);

}catch(SOAPException e){
System.err.println(e);
}catch(IOException e){
System.err.println(e);
}

}
//continue other handler chain
return true;
}

@Override
public boolean handleFault(SOAPMessageContext context) {

System.out.println("Server : handleFault()......");

return true;
}

@Override
public void close(MessageContext context) {
System.out.println("Server : close()......");
}

@Override
public Set getHeaders() {
System.out.println("Server : getHeaders()......");
return null;
}
private void generateSOAPErrMessage(SOAPMessage msg, String reason) {
try {
SOAPBody soapBody = msg.getSOAPPart().getEnvelope().getBody();
SOAPFault soapFault = soapBody.addFault();
soapFault.setFaultString(reason);
throw new SOAPFaultException(soapFault);
}
catch(SOAPException e) { }
}
}

Step 4 : SOAP Handler XML File

Create a SOAP handler XML File, and puts your SOAP handler declaration.
File : handler-chain.xml


Step 5 : Attach SOAP Handler with web service

package com.test.ws;
import javax.jws.HandlerChain;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
@HandlerChain(file="handler-chain.xml")
public class ServerInfo{
@WebMethod
public String getServerName() {
return "test server";
}
}
Step 6 : Web Service Publisher
package com.test.endpoint;
import javax.xml.ws.Endpoint;
import com.test.ws.ServerInfo;
//Endpoint publisher
public class TestWsPublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:8089/ws/server", new ServerInfo());
System.out.println("Service is published!");
}
}

Part 2 : SOAP handler in Client side


In this article, You will develop a web service client to access the published service in previous article,
and attach a handler to inject client’s MAC address into header block, for every outgoing SOAP message that’s
send by client side
Step 1 : Web Service Client
Use wsimport command to parse the published service WSDL file (http://localhost:8089/ws/server?wsdl) and generate
all required files to access the service.

wsimport -keep -verbose http://localhost:8089/ws/server?wsdl

Six files are generated automatically.

******* ServerInfoService.java************
@WebServiceClient(name = "ServerInfoService",
targetNamespace = "http://ws.test.com/",
wsdlLocation = "http://localhost:8089/ws/server?wsdl")
public class ServerInfoService extends Service
{
}

A client to access the published web service.

******* WsClient.java************

package com.test.client;
import com.test.ws.ServerInfo;
import com.test.ws.ServerInfoService;
public class WsClient{
public static void main(String[] args) throws Exception {
ServerInfoService sis = new ServerInfoService();
ServerInfo si = sis.getServerInfoPort();
System.out.println(si.getServerName());
}
}
Step 2 : SOAP Handler
Create a SOAP handler to inject client’s MAC address into the SOAP header block, for every outgoing SOAP message.

********** MacAddressInjectHandler.java ****************

package com.test.handler;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class MacAddressInjectHandler implements SOAPHandler{
@Override
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("Client : handleMessage()......");
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
//if this is a request, true for outbound messages, false for inbound
if(isRequest){
try{
SOAPMessage soapMsg = context.getMessage();
SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnv.getHeader();

//if no header, add one
if (soapHeader == null){
soapHeader = soapEnv.addHeader();
}
//get mac address
String mac = getMACAddress();
//add a soap header, name as "mac address"
QName qname = new QName("http://ws.test.com/", "macAddress");
SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(qname);

soapHeaderElement.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
soapHeaderElement.addTextNode(mac);
soapMsg.saveChanges();
//tracking
soapMsg.writeTo(System.out);

}catch(SOAPException e){
System.err.println(e);
}catch(IOException e){
System.err.println(e);
}
}
//continue other handler chain
return true;
}

@Override
public boolean handleFault(SOAPMessageContext context) {
System.out.println("Client : handleFault()......");
return true;
}

@Override
public void close(MessageContext context) {
System.out.println("Client : close()......");
}
@Override
public Set getHeaders() {
System.out.println("Client : getHeaders()......");
return null;
}
// return current client mac address
private String getMACAddress(){
InetAddress ip;
StringBuilder sb = new StringBuilder();
try {
ip = InetAddress.getLocalHost();
System.out.println("Current IP address : " + ip.getHostAddress());
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
System.out.print("Current MAC address : ");

for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
System.out.println(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();

} catch (SocketException e){
e.printStackTrace();
}
return sb.toString();
}
}

Step 3 : SOAP Handler XML File
Create a SOAP handler XML file, and puts your SOAP handler declaration.
***** File : handler-chain.xml **********

Step 4 : Attach SOAP Handler in Web Service Client
To attach above SOAP handler to web service client, edit the ServerInfoService.java file (generated via wsimport),
and annotate with @HandlerChain and specify the SOAP handler file name inside.
******** File : ServerInfoService.java **********
@WebServiceClient(name = "ServerInfoService",
targetNamespace = "http://ws.test.com/",
wsdlLocation = "http://localhost:8089/ws/server?wsdl")
@HandlerChain(file="handler-chain.xml")
public class ServerInfoService extends Service
{
}

No comments:

Post a Comment