Enterprise Java

Get Raw XML from SOAP in Java

SOAP (Simple Object Access Protocol) is widely used in enterprise applications for communication between services. While working with SOAP-based systems in Java, developers often need access to the raw XML of a SOAP message for logging, debugging, auditing, or transformation purposes. Let us delve into understanding how to extract a SOAP message’s raw XML for effective debugging and monitoring in Java applications.

1. SOAP Fundamentals

SOAP (Simple Object Access Protocol) is a protocol used for exchanging structured information between applications over a network. It relies on XML as its message format and typically uses HTTP or HTTPS as the transport protocol. SOAP is widely used in enterprise systems because of its extensibility, strict standards, and built-in error handling mechanisms. A SOAP message is a well-defined XML document that follows a specific structure. Understanding this structure is crucial when working with raw SOAP messages, especially for debugging, logging, or implementing custom handlers.

1.1 Understanding Raw XML in SOAP

Raw XML refers to the complete SOAP message in its original, unprocessed XML format as it is sent over the network. It represents the exact payload exchanged between the client and the server without any abstraction or transformation by frameworks such as JAX-WS or Spring WS. Accessing raw XML is particularly useful in the following scenarios:

  • Debugging request/response issues
  • Logging complete SOAP messages for auditing
  • Validating message structure against XSD schemas
  • Interoperability testing with external systems

A raw SOAP message consists of the following core components:

  • Envelope – The root element that defines the XML document as a SOAP message. It contains all other elements and specifies the namespace.
  • Header (optional) – Contains metadata such as authentication credentials, transaction IDs, security tokens, or routing information. This element is optional but commonly used in enterprise applications.
  • Body – The main part of the message that contains the actual request or response data. This is where the business payload resides.

Below is an example of a raw SOAP XML request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                  xmlns:ns="http://example.com/user">

   <soapenv:Header>
      <ns:AuthToken>abc123</ns:AuthToken>
   </soapenv:Header>

   <soapenv:Body>
      <ns:getUser>
         <ns:id>101</ns:id>
      </ns:getUser>
   </soapenv:Body>

</soapenv:Envelope>

In this example, the Envelope defines the SOAP message boundaries, the Header carries an authentication token, and the Body contains the actual operation (getUser) along with its input parameter.

When we talk about “getting raw XML” in Java, we essentially mean accessing this complete SOAP message as a string or stream before it is parsed into Java objects (or after it is generated but before being sent over the network).

2. Extracting Raw XML Using SAAJ (SOAPMessage)

SAAJ (SOAP with Attachments API for Java) provides low-level control over SOAP messages.

// SaajExample.java
import javax.xml.soap.*;
import java.io.ByteArrayOutputStream;

public class SaajExample {

    public static void main(String[] args) throws Exception {
        MessageFactory factory = MessageFactory.newInstance();
        SOAPMessage message = factory.createMessage();

        SOAPBody body = message.getSOAPBody();
        body.addBodyElement(
            message.getSOAPPart().getEnvelope().createName("test", "ns", "http://example.com")
        ).addTextNode("Hello World");

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        message.writeTo(outputStream);

        String rawXml = new String(outputStream.toByteArray());
        System.out.println(rawXml);
    }
}

2.1 Code Explanation

This code demonstrates how to create and extract raw SOAP XML using the SAAJ API in Java. It starts by creating a MessageFactory instance to generate a new SOAPMessage. Then, it retrieves the SOAPBody from the message and adds a new body element named test with a namespace http://example.com, inserting the text value “Hello World” into it. After constructing the SOAP message, a ByteArrayOutputStream is used to capture the entire SOAP message in its raw XML format by calling message.writeTo(outputStream). Finally, the byte stream is converted into a string representing the complete raw SOAP XML, which is printed to the console.

2.2 Code Output

<SOAP-ENV:Envelope>
  <SOAP-ENV:Body>
    <ns:test>Hello World</ns:test>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The output represents the raw SOAP XML message generated by the SAAJ API. The SOAP-ENV:Envelope is the root element that wraps the entire SOAP message and defines it as a valid SOAP structure. Inside it, the SOAP-ENV:Body contains the actual payload of the message. Within the body, the custom element ns:test is present, which was programmatically added in the code using a namespace and assigned the value “Hello World”. The absence of a Header element indicates that no additional metadata (such as authentication or transaction details) was included in this message. Overall, this output shows a minimal SOAP request structure with a single operation and its corresponding data.

3. Capturing SOAP Messages with JAX-WS Handlers

JAX-WS allows interception of SOAP messages using handlers.

// LoggingSOAPHandler.java
import javax.xml.namespace.QName;
import javax.xml.ws.handler.soap.*;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import java.io.ByteArrayOutputStream;
import java.util.Set;

public class LoggingSOAPHandler implements SOAPHandler<SOAPMessageContext> {

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        try {
            SOAPMessage message = context.getMessage();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            message.writeTo(out);

            String rawXml = new String(out.toByteArray());
            System.out.println("SOAP Message:\n" + rawXml);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

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

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

    @Override
    public void close(MessageContext context) {}
}

3.1 Code Explanation

This code defines a custom SOAP handler named LoggingSOAPHandler that implements the SOAPHandler<SOAPMessageContext> interface to intercept and log SOAP messages in their raw XML form. Inside the handleMessage method, the current SOAPMessage is retrieved from the SOAPMessageContext, and a ByteArrayOutputStream is used to capture the entire SOAP message by invoking message.writeTo(). The resulting byte stream is then converted into a string to obtain the raw XML, which is printed to the console for logging or debugging purposes. The method returns true to allow further processing in the handler chain. The getHeaders method returns null, indicating that this handler does not process specific SOAP headers, while handleFault simply returns true to continue fault processing if an error occurs. The close method is left empty as no resource cleanup is required. Overall, this handler is useful for inspecting inbound and outbound SOAP messages in JAX-WS-based applications.

3.2 Code Output

SOAP Message:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns:getUser xmlns:ns="http://example.com">
         <ns:id>101</ns:id>
      </ns:getUser>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The output shows the complete raw SOAP XML message intercepted by the LoggingSOAPHandler. The line "SOAP Message:" is printed first, followed by the full SOAP envelope captured from the message context. The Envelope element wraps the entire message and defines the SOAP namespace, while the Header is present but empty, indicating that no additional metadata was included. The Body contains the actual business request, in this case the getUser operation with an input parameter id set to 101. This output confirms that the handler successfully intercepted the SOAP message and converted it into raw XML format using writeTo(), making it useful for debugging, logging, or monitoring SOAP requests and responses in real-time.

4. Intercepting SOAP Messages with Apache CXF

Apache CXF provides interceptors to access raw SOAP messages.

// CXFLoggingInterceptor.java
import org.apache.cxf.interceptor.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.message.Message;

import java.io.InputStream;
import java.io.ByteArrayOutputStream;

public class CXFLoggingInterceptor extends AbstractPhaseInterceptor<Message> {

    public CXFLoggingInterceptor() {
        super(Phase.RECEIVE);
    }

    @Override
    public void handleMessage(Message message) {
        try {
            InputStream is = message.getContent(InputStream.class);
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();

            int nRead;
            byte[] data = new byte[1024];

            while ((nRead = is.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }

            String rawXml = buffer.toString("UTF-8");
            System.out.println("Raw SOAP XML:\n" + rawXml);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.1 Code Explanation

This code defines a custom Apache CXF interceptor named CXFLoggingInterceptor that extends AbstractPhaseInterceptor<Message> to intercept SOAP messages at a specific phase in the CXF processing pipeline. In the constructor, the interceptor is registered for the Phase.RECEIVE, meaning it will execute when an incoming SOAP request is received. Inside the handleMessage method, the raw message content is accessed as an InputStream using message.getContent(InputStream.class). The stream is then read in chunks using a byte buffer and written into a ByteArrayOutputStream to reconstruct the full message. After reading the entire stream, it is converted into a UTF-8 encoded string representing the raw SOAP XML, which is printed to the console. This interceptor is particularly useful for logging, debugging, or auditing incoming SOAP requests in Apache CXF-based applications by allowing developers to inspect the exact XML payload being processed.

4.2 Code Output

Raw SOAP XML:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Header/>
   <soap:Body>
      <ns:getUser xmlns:ns="http://example.com">
         <ns:id>101</ns:id>
      </ns:getUser>
   </soap:Body>
</soap:Envelope>

The output displays the complete raw SOAP XML message intercepted by the CXFLoggingInterceptor during the RECEIVE phase. The prefix "Raw SOAP XML:" is printed first, followed by the full SOAP message captured from the input stream. The Envelope element defines the SOAP structure and namespace, enclosing both the Header and Body. The Header is empty, indicating no additional metadata was included, while the Body contains the actual request payload, in this case the getUser operation with an id value of 101. This output confirms that the interceptor successfully read the incoming message stream, reconstructed it into a string, and logged the exact SOAP XML being received by the application, which is highly useful for debugging and monitoring purposes.

5. Handling SOAP Messages in Spring Web Services

Spring Web Services provides built-in interceptors for handling SOAP messages.

// SpringSoapInterceptor.java
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.soap.SoapMessage;

import java.io.ByteArrayOutputStream;

public class SpringSoapInterceptor implements EndpointInterceptor {

    @Override
    public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
        SoapMessage soapMessage = (SoapMessage) messageContext.getRequest();

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        soapMessage.writeTo(out);

        String rawXml = new String(out.toByteArray());
        System.out.println("Request XML:\n" + rawXml);

        return true;
    }

    @Override
    public boolean handleResponse(MessageContext messageContext, Object endpoint) {
        return true;
    }

    @Override
    public boolean handleFault(MessageContext messageContext, Object endpoint) {
        return true;
    }

    @Override
    public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) {}
}

5.1 Code Explanation

This code defines a custom Spring Web Services interceptor named SpringSoapInterceptor that implements the EndpointInterceptor interface to intercept and log SOAP request messages. In the handleRequest method, the incoming request is retrieved from the MessageContext and cast to a SoapMessage, allowing direct access to the SOAP payload. A ByteArrayOutputStream is then used to capture the full SOAP message by invoking soapMessage.writeTo(), which writes the complete XML (including envelope, header, and body) into the stream. This byte data is converted into a string to obtain the raw XML representation, which is printed to the console with the label “Request XML”. The method returns true to allow the request to proceed further in the processing chain. The handleResponse and handleFault methods simply return true, indicating no additional processing for responses or faults, while afterCompletion is left empty as no cleanup is required. This interceptor is useful for logging and debugging SOAP requests in Spring WS applications by exposing the exact XML being received.

5.2 Code Output

Request XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <ns:getUser xmlns:ns="http://example.com">
         <ns:id>101</ns:id>
      </ns:getUser>
   </soapenv:Body>
</soapenv:Envelope>

The output shows the raw SOAP request XML intercepted by the SpringSoapInterceptor during request processing. The label "Request XML:" is printed first, followed by the complete SOAP message captured from the incoming request. The Envelope element defines the SOAP structure and namespace, enclosing both the Header and Body. The Header is empty, indicating no additional metadata is present, while the Body contains the actual request payload, specifically the getUser operation with an id value of 101. This output confirms that the interceptor successfully captured and logged the full SOAP message as raw XML, which is helpful for debugging, auditing, and monitoring incoming requests in Spring Web Services applications.

6. Conclusion

Extracting raw XML from SOAP messages in Java is essential for debugging and monitoring, and depending on your stack, you can use SAAJ for low-level control, JAX-WS handlers for standard SOAP services, Apache CXF interceptors for enterprise setups, and Spring WS interceptors for Spring-based applications; choosing the right approach ultimately depends on your application architecture and performance requirements.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

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

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button