Writing a Custom Message Builder for WSO2 ESB

In WSO2 ESB, when you write a certain mediation logic, a user can do various types of alterations to the message body, headers, etc. Also a user can just passthrough the incoming raw payload without any message processing inside ESB. In situations where the message processing is done, WSO2 ESB uses a universal payload format within ESB. This universal payload format is SOAP message format.

When ESB receives a certain payload, the raw payload is converted to SOAP message format before further processing the content. For this purpose we use Message Builders. Depending on the receiving message’s content type, a particular message builder is selected at the run time, for building the message

By default bellow Message Builders are available in WSO2 ESB

  • SOAPBuilder
  • MIMEBuilder
  • MTOMBuilder
  • ApplicationXMLBuilder
  • XFormURLEncodedBuilder
  • BinaryRelayBuilder
  • PlainTextBuilder

For most of the message content types, these Builders can be used to build the message. If a particular user thinks that they need to furthermore customize the Message building process, WSO2 ESB has the facility to extend by adding custom message builders as well. During the latter part of this post I’ll go through how to write a custom message builder for such requirements.

Users can configure which message builder should be used for a particular content type by configuring the message builders at axis2.xml (This can be found at <carbon_home>/repository/conf/axis2/ directory) . There you can find a section called <messageBuilders></messageBuilders>

Let’s have a look at the bellow configuration element.

<messageBuilder contentType="text/plain" class="org.apache.axis2.format.PlainTextBuilder"/>

We add this configuration element under the messageBuilder element to configure the message builder to be used for a certain content type.

According to the example we have configured ESB to use PlainTextBuilder for the incoming messages having content type text/xml.

Internally in ESB selecting the message Builder is triggered from wso2-synapse Engine. When the message comes to the ESB and then inside the synapse engine You can find class RelayUtils. There you can find the method called builldMessage(). Then through the getDocument() of DeferredMessageBuilder we call the processDocument() method of the Builder which is enabled for the particular builder. You will be able to get a clear idea regarding this procedure by debugging the synapse code base on the particular points I have highlighted here.

If a certain user would like to use his own, custom message builder, instead of the default message builders, WSO2 ESB has been provided the flexibility to add such custom message builders. What the user has to do is write his own custom message builder and then enable the particular message builder for the desired content type. Bellow code snippet shows such custom message builder I have written, in order to BASE64 encode an xml entry field.

When writing the custom Builder you need to implement the Builder Interface and then override the processDocument method. Inside the process document method you can define your specific logic to process the content and accordingly convert it to the SOAP format. Bellow sample code snippet assumes the incoming payload is as <sampleElement>Sample Content</sampleElement>. Then I get the content and encode the text before converting the incoming payload to SOAP before processing the content in the mediation flow.

package org.test.builder;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.OMNodeEx;
import org.apache.axiom.om.impl.builder.StAXBuilder;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.util.StAXParserConfiguration;
import org.apache.axiom.om.util.StAXUtils;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.builder.Builder;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.codec.binary.Base64;

import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

/**
* Created by nadeeshaan on 7/14/16.
*/

public class CustomBuilderForTextXml implements Builder{
public OMElement processDocument(InputStream inputStream, String s, MessageContext messageContext) throws AxisFault {
SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();

PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);

try {
int byteVal = pushbackInputStream.read();
if (byteVal != -1) {
pushbackInputStream.unread(byteVal);

javax.xml.stream.XMLStreamReader xmlReader = StAXUtils.createXMLStreamReader(StAXParserConfiguration.SOAP,
pushbackInputStream, (String) messageContext.getProperty(Constants.Configuration.CHARACTER_SET_ENCODING));

StAXBuilder builder = new StAXOMBuilder(xmlReader);
OMNodeEx documentElement = (OMNodeEx) builder.getDocumentElement();
documentElement.setParent(null);
String elementVal = ((OMElement) documentElement).getText();
byte[] bytesEncoded = Base64.encodeBase64(elementVal.getBytes());
((OMElement) documentElement).setText(new String(bytesEncoded ));
SOAPBody body = soapEnvelope.getBody();
body.addChild(documentElement);
}
} catch (IOException e) {
e.printStackTrace();
} catch (XMLStreamException e) {
e.printStackTrace();
}

return soapEnvelope;
}
}

Similarly you can write your own Message Formatters as well, in order to manipulate the out going payload from ESB. Under the Message formatters section add the bellow configuration element in order to enable the message formatter for the particular content type

<messageFormatter contentType=”text/xml” class=”org.apache.axis2.transport.http.SOAPMessageFormatter”/>

Find more information about the message builders and formatters on Official Documentation