jws

JAX-WS SOAP Handler Example

In this example we are going to see how to Use SOAP Handler to intercept SOAP messages form the client – server communication. A SOAP handler can also change the SOAP messages from/to client/server. In this example we are going to create a Handler in the Client Side that will add a header to the SOAP message containing a random property String. At the other end, there is a Handler in the Server Side that will exctract this property String and decide whether to serve him or not. For example you can have a list of your trusted IP addresses (or any kind of filter for that mater), and only serve SOAP messages containing these trusted IP addresses at their headers.

For invalid SOAP messages, an Exception will be thrown and a SOAP Fault message will be attached to the SOAP envelope.

Service Endpoint

In the Web Server Endpoint there will be a SOAPHandler that will extract the special header from the SOAP message, will read the property that the client put it that header, and decide whether to serve him or not.

But let’s see the Endpoint Creation step by step :

In order to create our Web Service Endpoint:

  • First you have to create a Web Service Endpoint Interface. This interface will contain the declerations of all the methods you want to include in the Web Service.
  • Then you have to create a class that actually implements the above interface, which will be your Endpoint implementation.
  • Next we are going to create an IpValidator class that will implement SOAPHandler<SOAPMessageContext>.
  • We will create an XML file (handlers.xml) that will define the IpValidator class as a SOAP handler. This file can also chain other handlers. The SOAP message will be handled by all of those handlers, one by one. Then, we are going to add a @HandlerChain annotation to the  Server Endpoint Implementation class, to state to the web service to use a specific SOAP handler (obviously the one that we’ve created)
  • Finally you create your Endpoint publisher which actually deploys the web service and creates and publishes the endpoint for the specified implementor object at a given address. The necessary server infrastructure will be created and configured by the JAX-WS implementation. You have to run the publisher to make your Web Service available to clients.

Web Service Endpoint Interface

WebServiceInterface.java:

package com.javacodegeeks.enterprise.ws;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style = Style.DOCUMENT)
public interface WebServiceInterface {

	@WebMethod
	String printMessage();

}

Web Service Endpoint Implementation

WebServiceImpl.java:

package com.javacodegeeks.enterprise.ws;

import javax.jws.HandlerChain;
import javax.jws.WebService;

@WebService(endpointInterface = "com.javacodegeeks.enterprise.ws.WebServiceInterface")
public class WebServiceImpl implements WebServiceInterface{

	@Override
	public String printMessage() {
		return "Hello from Java Code Geeks Server";
	}

}

SOAP Handler

PropertyValidator.java:

package com.javacodegeeks.enterprise.ws.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 PropertyValidator implements SOAPHandler<SOAPMessageContext> {

	private final String VALID_PROPERTY = "RANDOM";

	@Override
	public boolean handleMessage(SOAPMessageContext context) {

		System.out.println("Server executing SOAP Handler");

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

		// if this is an incoming message from the client
		if (!outBoundProperty) {

			try {

				// Get the SOAP Message and grab the headers
				SOAPMessage soapMsg = context.getMessage();
				SOAPEnvelope soapEnv = soapMsg.getSOAPPart().getEnvelope();
				SOAPHeader soapHeader = soapEnv.getHeader();

				// Grab an iterator to go through the headers
				Iterator<?> headerIterator = soapHeader
						.extractHeaderElements(SOAPConstants.URI_SOAP_ACTOR_NEXT);

				// if there is no additional header
				if (headerIterator != null && headerIterator.hasNext()) {

					// Extract the property node of the header
					Node propertyNode = (Node) headerIterator.next();

					String property = null;

					if (propertyNode != null)
						property = propertyNode.getValue();

					if (VALID_PROPERTY.equals(property)) {
						// Output the message to the Console -- for debug
						soapMsg.writeTo(System.out);
					} else {
						// Restrict the execution of the Remote Method
						// Attach an error message as a response
						SOAPBody soapBody = soapMsg.getSOAPPart().getEnvelope().getBody();
						SOAPFault soapFault = soapBody.addFault();
						soapFault.setFaultString("Invalid Property");
						
						throw new SOAPFaultException(soapFault);
					}
				}
			} catch (SOAPException e) {
				System.err.println(e);
			} catch (IOException e) {
				System.err.println(e);
			}

		}

		return true;
	}

	@Override
	public boolean handleFault(SOAPMessageContext context) {
		return true;
	}

	@Override
	public void close(MessageContext context) {
	}

	@Override
	public Set<QName> getHeaders() {
		return null;
	}
}

SOAP Handler XML File

handlers.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<javaee:handler-chain>
		<javaee:handler>
			<javaee:handler-class>com.javacodegeeks.enterprise.ws.handler.PropertyValidator</javaee:handler-class>
		</javaee:handler>
	</javaee:handler-chain>
</javaee:handler-chains>

Add @HandlerChain annotation to SEI

WebServiceImpl.java:

package com.javacodegeeks.enterprise.ws;

import javax.jws.HandlerChain;
import javax.jws.WebService;

@WebService(endpointInterface = "com.javacodegeeks.enterprise.ws.WebServiceInterface")
@HandlerChain(file="./handler/handlers.xml")
public class WebServiceImpl implements WebServiceInterface{

	@Override
	public String printMessage() {
		return "Hello from Java Code Geeks Server";
	}

}

Web Service Endpoint Publisher

WebServicePublisher.java:

package com.javacodegeeks.enterprise.ws;

import javax.xml.ws.Endpoint;
import com.javacodegeeks.enterprise.ws.WebServiceImpl;

public class WebServicePublisher{

	public static void main(String[] args) {
	   Endpoint.publish("http://localhost:8888/webservice/validator", new WebServiceImpl());
    }

}

Now when you run the publisher the Web Service will be available to the clients, deployed in the URL:

http://localhost:8888/webservice/validator

Ok, that’s it for the Server Side. Let’s see the project structure od the Eclipse project to get a clear view of all the files and their location:

ws-server

Client Side

In the Client Side there will be a SOAP Handler to attach a new Header to every outgoing SOAP message. But first, we are going to use wsimport tool to create all necessary file for the Client Side program (see JAX-WS Hello World Example – RPC Style).

So, this is the command that I issued :

wsimport -keep http://localhost:8888/webservice/validator?wsdl -d F:\nikos7\Desktop
  • -keep : is an options that lets you keep the generated files
  • -d : you can sepcify the folder where the generated files will be stored

This command will create six java source files. In our case we are only going to manipulate WebServiceImplService.java

This is how that file looks like:

WebServiceImplService.java:

package com.javacodegeeks.enterprise.ws;

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.jws.HandlerChain;

/**
 * This class was generated by the JAX-WS RI. JAX-WS RI 2.2.4-b01 Generated
 * source version: 2.2
 * 
 */
@WebServiceClient(name = "WebServiceImplService", targetNamespace = "http://ws.enterprise.javacodegeeks.com/", wsdlLocation = "http://localhost:8888/webservice/validator?wsdl")
public class WebServiceImplService extends Service {

...

}

We are going to create the SOAP Handler and the SOAP Handler XML file and then add a @HandlerChain annotation to the above file

SOAP Handler

PropertyInjector.java:

package com.javacodegeeks.enterprise.ws.handler;

import java.io.IOException;
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 PropertyInjector implements SOAPHandler<SOAPMessageContext> {
	@Override
	public boolean handleMessage(SOAPMessageContext context) {
		
		System.out.println("Client executing SOAP Handler");

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

		// If it is an outgoing message
		if (outboundProperty.booleanValue()) {

			try {
				SOAPMessage soapMessagg = context.getMessage();
				SOAPEnvelope soapEnvelope = soapMessagg.getSOAPPart().getEnvelope();
				
				// Grab the header of the SOAP envelop
				SOAPHeader soapHeader = soapEnvelope.getHeader();

				// Attach a new header if there is none...
				if (soapHeader == null) {
					soapHeader = soapEnvelope.addHeader();
				}
				
				// add the property to the header
				QName qname = new QName("http://ws.enterprise.javacodegeeks.com/", "PROPERTY");
				
				// Create a new HeaderElement in order to place the new property
				SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(qname);

				soapHeaderElement.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
				soapHeaderElement.addTextNode("RANDOM");
				soapMessagg.saveChanges();

				// Output the message to the Console -- for debug   
				soapMessagg.writeTo(System.out);

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

		}
		return true;
	}

	@Override
	public boolean handleFault(SOAPMessageContext context) {
		return true;
	}

	@Override
	public void close(MessageContext context) {
	}

	@Override
	public Set<QName> getHeaders() {
		return null;
	}
}

This Handler basically adds a new Header element to the SOAP envelope with a simple String in it. It will also add an Actor to the Header. A SOAP message may travel from a sender to a receiver by passing different endpoints along the message path. However, not all parts of a SOAP message may be intended for the ultimate endpoint, instead, it may be intended for one or more of the endpoints on the message path. The SOAP actor attribute is used to address the Header element to a specific endpoint.

The SOAP actor global attribute can be used to indicate the recipient of a header element. The value of the SOAP actor attribute is a URI. The special URI “http://schemas.xmlsoap.org/soap/actor/next” as the value of actor attribute indicates that the next SOAP node in the message path must process the header element.

SOAP 1.1 defines only one single role named http://schemas.xmlsoap.org/soap/actor/next (URI_SOAP_ACTOR_NEXT, for short). Every SOAP node is required to assume the next role. Thus, when a SOAP message arrives at any SOAP node, the node must process all headers marked with the next role. In addition to next, SOAP 1.2 defines a few more roles and applications are allowed to define custom roles as well.

The absence of the SOAP actor attributes, in the header block, indicates that the recipient is the ultimate destination of the SOAP message. This attribute must appear in the SOAP message instance in order to be effective.

SOAP Handler XML File

handlers.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<javaee:handler-chains xmlns:javaee="http://java.sun.com/xml/ns/javaee"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<javaee:handler-chain>
		<javaee:handler>
			<javaee:handler-class>com.javacodegeeks.enterprise.ws.handler.PropertyInjector</javaee:handler-class>
		</javaee:handler>
	</javaee:handler-chain>
</javaee:handler-chains>

Add @HandlerChain annotation to WebServiceImplService

WebServiceImplService .java:

package com.javacodegeeks.enterprise.ws;

import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
import javax.jws.HandlerChain;

/**
 * This class was generated by the JAX-WS RI. JAX-WS RI 2.2.4-b01 Generated
 * source version: 2.2
 * 
 */
@WebServiceClient(name = "WebServiceImplService", targetNamespace = "http://ws.enterprise.javacodegeeks.com/", wsdlLocation = "http://localhost:8888/webservice/validator?wsdl")
@HandlerChain(file="./handler/handlers.xml")
public class WebServiceImplService extends Service {

...

}

WSClient.java

package com.javacodegeeks.enterprise.ws;

import com.javacodegeeks.enterprise.ws.WebServiceImplService;

public class WSClient {
	public static void main(String[] args) {

	WebServiceImplService webService = new WebServiceImplService();
	WebServiceInterface serviceInterface = webService.getWebServiceImplPort();

	System.out.println(serviceInterface.printMessage());
 }
}

Let’s see the Eclipse project structure:

client

Test the Application

Now let’s see what happens if we run the above program.

Output for a valid Property

Client executing SOAP Handler
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Header>
		<PROPERTY xmlns="http://ws.enterprise.javacodegeeks.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">
			RANDOM
		</PROPERTY>
	</S:Header>
	<S:Body>
		<ns2:printMessage xmlns:ns2="http://ws.enterprise.javacodegeeks.com/"/>
	</S:Body>
</S:Envelope>
Client executing SOAP Handler
Hello from Java Code Geeks Server

Output for a invalid Property

Client executing SOAP Handler
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Header>
		<PROPERTY xmlns="http://ws.enterprise.javacodegeeks.com/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:actor="http://schemas.xmlsoap.org/soap/actor/next">
			ascascasc
		</PROPERTY>
	</S:Header>
	<S:Body>
		<ns2:printMessage xmlns:ns2="http://ws.enterprise.javacodegeeks.com/"/>
	</S:Body>
</S:Envelope>
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Invalid Property
	at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(Unknown Source)
	at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(Unknown Source)
	at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(Unknown Source)
	at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(Unknown Source)
	at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(Unknown Source)
	at com.sun.proxy.$Proxy22.printMessage(Unknown Source)
	at com.javacodegeeks.enterprise.ws.WSClient.main(WSClient.java:11)

This was an example on JAX-WS SOAP Handler. Download the Eclipse Projects of this Example : SOAPHandlerExample.zip

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button