Mar 08 2008
How to modify JAX-WS SOAP-Messages


This blog is depending on my previous blog Creating SOAP Web Services with NetBeans 6. You can download the needed sourcecode there.
Here i will show how you can modify the JAX-WS SOAP-Message before the outgoing SOAP-Response will pass the specified Web Service Operation. This is sometimes necessary, when some recipients want to receive their own special SOAP-Headers. You can also modify the incoming Request to read special informations, which are included in the SOAP-Header, but this is not part of this posting (maybe i will show it in an other upcoming blog).
For this example i am using JAX-WS 2.1 with NetBeans 6.0 .
ServerSOAPHandler.java
This is the core-class of this example. Before the Request/Response pass the
Web Service Operation, it can be handled here in the method
public boolean handleMessage(SOAPMessageContext context)
The outbound-part (line 101) adds the SOAP-Header with four additional elements and one element for the SOAP-Body. The generated SOAP-Message is dumped to the console at runtime. Read the included comments to see how it works.

Create the following file:
ServerSOAPHandler.java
package eu.jdevelop.soapwebservices.handler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.namespace.QName;
import java.util.Set;
import java.io.ByteArrayOutputStream;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPPart;
/**
* This class handles the SOAP-Requests before they reach the
* Web Service Operation. It is possible to read and manipulate
* the SOAP-Message.
*
* @author Siegfried Bolz
*/
public class ServerSOAPHandler implements SOAPHandler<soapmessageContext> {
/**
* Is called after constructing the handler and before executing any othe method.
*/
@PostConstruct
public void init() {
}
/**
* Returns the <code>Set</code> of supported SOAP headers
*/
public Set<qname> getHeaders() {
return null;
}
/**
* Returns the message encoding (e.g. utf-8)
*
* @param msg
* @return
* @throws javax.xml.soap.SOAPException
*/
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
/**
* Dump SOAP Message to console
*
* @param msg
*/
private void dumpSOAPMessage(SOAPMessage msg) {
if (msg == null) {
System.out.println("SOAP Message is null");
return;
}
System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
// show included values
String values = msg.getSOAPBody().getTextContent();
System.out.println("Included values:" + values);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* This method handles the incoming and outgoing SOAP-Message.
* It's an excellent point to manipulate the SOAP.
*
* @param SOAPMessageContext
* @return boolean
*/
public boolean handleMessage(SOAPMessageContext context) {
//Inquire incoming or outgoing message.
boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try {
if (outbound) {
// OUTBOUND
System.out.println("Direction=outbound (handleMessage)");
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
// get SOAP-Part
SOAPPart sp = msg.getSOAPPart();
//edit Envelope
SOAPEnvelope env = sp.getEnvelope();
// add namespaces
env.addNamespaceDeclaration("blog", "http://blog.jdevelop.eu");
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
env.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope");
// add the Header with additional Elements
SOAPElement soapElement1 = env.addHeader().addHeaderElement(new QName("http://blog.jdevelop.eu/securityprotocol", "Security"));
soapElement1.addTextNode("1");
SOAPElement soapElement2 = env.getHeader().addHeaderElement(new QName("http://blog.jdevelop.eu/securityprotocol", "Compression"));
soapElement2.addTextNode("gzip");
SOAPElement soapElement3 = env.getHeader().addHeaderElement(new QName("http://blog.jdevelop.eu/securityprotocol", "Username"));
soapElement3.addTextNode("jdevelop");
SOAPElement soapElement4 = env.getHeader().addHeaderElement(new QName("http://blog.jdevelop.eu/securityprotocol", "Password"));
soapElement4.addTextNode("blog");
// get the SOAP-Body
SOAPBody body = env.getBody();
// add an additional Element to the SOAP-Body
SOAPBodyElement bodyElement = body.addBodyElement(new QName("http://blog.jdevelop.eu/securitymessage", "message"));
bodyElement.addTextNode("Security through obscurity");
// print SOAP-Message
dumpSOAPMessage(msg);
} else {
// INBOUND
System.out.println("Direction=inbound (handleMessage)");
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
}
} catch (Exception e) {
//All other unhandled problems.
e.printStackTrace();
}
return true;
}
/**
* Handles SOAP-Errors.
*
* @param context
* @return
*/
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleFault");
boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound (handleFault)");
} else {
System.out.println("Direction=inbound (handleFault)");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName=" + detailName);
} catch (Exception e) {
}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public void close(MessageContext messageContext) {
}
/**
* Is executed before this handler is being destroyed -
* means after close() has been executed.
*/
@PreDestroy
public void destroy() {
}
} // .EOF
serversoaphandler.xml
This is the configuration-file for the handler-chain. Create it at the source-root.
serversoaphandler.xml
<?xml version="1.0" encoding="UTF-8"?>
<jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
<jws:handler-chain>
<jws:handler>
<jws:handler-name>ServerSOAPHandler</jws:handler-name>
<jws:handler-class>eu.jdevelop.soapwebservices.handler.ServerSOAPHandler</jws:handler-class>
</jws:handler>
</jws:handler-chain>
</jws:handler-chains>
ServiceImpl.java
To load the configuration-file, just add this line of code under the “@WebService” -annotation.
@HandlerChain(name = "ServerSOAPHandler", file = "serversoaphandler.xml")
ServiceImpl.java
package eu.jdevelop.soapwebservices.service;
import eu.jdevelop.soapwebservices.CalculateValues;
import eu.jdevelop.soapwebservices.CalculateValuesResponse;
import eu.jdevelop.soapwebservices.SOAPWebServices;
import eu.jdevelop.soapwebservices.wrapper.impl.CalculateValuesWrapper;
import javax.jws.WebService;
import javax.jws.WebService;
import javax.jws.HandlerChain;
/**
* This is the Service-Implementation of the Web Service. Here are the
* operations which can be called from web clients.
*
* @author Siegfried Bolz
*/
@WebService(serviceName = "SOAPService", portName = "WebServices", endpointInterface = "eu.jdevelop.soapwebservices.SOAPWebServices", targetNamespace = "soapwebservices.jdevelop.eu", wsdlLocation = "WEB-INF/wsdl/ServiceImpl/webservices.wsdl")
@HandlerChain(name = "ServerSOAPHandler", file = "serversoaphandler.xml")
public class ServiceImpl implements SOAPWebServices {
public CalculateValuesResponse getCalculateValues(CalculateValues calculateValues) {
try {
CalculateValuesWrapper wrapper = new CalculateValuesWrapper();
return wrapper.getResult(calculateValues);
} catch (Exception x) {
throw new IllegalStateException(x);
}
}
} // .EOF
Your package-structure should now look like this:
![]()
Testing
Build, deploy and start the application. Use soapui to submit a request.
![]()
You will receive the following SOAP-Message:
<s:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:blog="http://blog.jdevelop.eu" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Header>
<security xmlns="http://blog.jdevelop.eu/securityprotocol">1</security>
<compression xmlns="http://blog.jdevelop.eu/securityprotocol">gzip</compression>
<username xmlns="http://blog.jdevelop.eu/securityprotocol">jdevelop</username>
<password xmlns="http://blog.jdevelop.eu/securityprotocol">blog</password>
</s:Header>
<s:Body>
<ns2:calculateValuesResponse xmlns:ns2="soapwebservices.jdevelop.eu">
<result>15.33</result>
</ns2:calculateValuesResponse>
<message xmlns="http://blog.jdevelop.eu/securitymessage">Security through obscurity</message>
</s:Body>
</s:Envelope>
For comparison, the response without using the soaphandler looks like this:
<s:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:blog="http://blog.jdevelop.eu" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Body>
<ns2:calculateValuesResponse xmlns:ns2="soapwebservices.jdevelop.eu">
<result>15.33</result>
</ns2:calculateValuesResponse>
</s:Body>
</s:Envelope>
And here the request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="soapwebservices.jdevelop.eu">
<soapenv:Header>
<security xmlns:soap="http://blog.jdevelop.eu/securityprotocol">1</security>
<username xmlns:soap="http://blog.jdevelop.eu/securityprotocol">jdevelop</username>
<password xmlns:soap="http://blog.jdevelop.eu/securityprotocol">blog</password>
</soapenv:Header>
<soapenv:Body>
<soap:calculateValues>
<value1>10</value1>
<value2>5.33</value2>
</soap:calculateValues>
</soapenv:Body>
</soapenv:Envelope>
Download
NetBeans 6 Project: download




I have followed this but the new added header dont display, but body ones displays, what might be the cause
Please download my example and try it to see if everything is working fine.
Hi Siegfried,
vielen Dank, Dein Beispiel ist aufschlussreich, was das zippen in Jax-Ws angeht. Ich habe aber noch ein anderes Problem zu lösen. Ich muß für einen .net Webservice mit jax-ws eine SOAP-Message in der Version 1.1 anstatt Standard 1.2 erzeugen. Kann ich da was im apt einstellen oder hilft mir die Implementierung des Handlers mit Bezug auf eine SOAP-Version dabei?
Vielen Dank und Grüsse aus Essen
Dieter
Hi Dieter,
es müsste möglich sein die SOAP-Message so zu manipulieren das daraus eine Version 1.1 entsteht, allerdings gibt es da große Unterschiede die du beachten must (siehe Links unten). Mit dem ServerSoapHandler müsste diese Manipulation möglich sein.
http://www.w3.org/2003/06/soap11-soap12.html
http://www.idealliance.org/papers/xmle02/dx_xmle02/papers/02-02-02/02-02-02.html
Viele Grüße
Siegfried
Hi, thanks a lot for this part of the code:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
I was trying to write the content of the message to a log. Previously i was using msg.writeTo(System.out), and when I changed my System.out logs to log4J, it was driving me crazy (I haven’t even think about dropping the content to an Output Stream variable!).
Now with
logger.info(”Message: ” + baos.toString) i can log my messages again.
Best regards,
Diego.
Hi Siegfried,
This code is able to handle the request & response
but when the webservice is throwing an exception it’s not able to handle it.
My WS Code.
@WebService(name = “TestWS”,targetNamespace=”http://ws.test”)
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL)
@HandlerChain(name=”ServerSOAPHandler”,file=”../../handler/serversoaphandler.xml”)
public class TestWS {
public String testWs(String str) throws Exception{
if(some condition){
throw new Exception(”Sidd Exp”);
}
return str;
}
}
I’m trying to get the exception handling to work. I’m trying to get the below-
ABC
xyz
blah
I created my own exception, and I’m able to set the faultstring. but when I use @HandlerChain(file=’xxx.xml’) the faultstring is changed to
Exception raised from invocation of public java.lang.String …..
could you advise why this could be happening.
Thanks in advace,
Rajiv.
oops the xml tags were ripped in the above -
<SOAP-ENV:Fault>
<faultcode>ABC</faultcode>
<faultstring>xyz</faultstring>
</SOAP-ENV:Fault>
Suppose I want to change SOAP response Envelope attribute from to standard format like using handler class. Could you please tell me what changes need to be done here?
Thanks in Advance
More over , is there any way to identify the service name from Soap Fault in the Handler