Monday, April 17, 2017

How to access transport headers inside the custom message formatter.

Find the blow custom formatter class and you can see the way we access the transport headers.

package com.wso2.sample.formatter;

import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.MessageFormatter;

import java.io.OutputStream;
import java.net.URL;
import java.util.Map;
import java.util.Set;

public class CustomMessageFormatter implements MessageFormatter {

    public void writeTo(MessageContext messageContext, OMOutputFormat omOutputFormat, OutputStream outputStream, boolean b) throws AxisFault {
        Map headerMap = (Map) messageContext.getProperty(MessageContext.TRANSPORT_HEADERS);
        Set keySet = headerMap.keySet();
        for (String key : keySet) {
            System.out.println(key + " >> " + headerMap.get(key));
        }
    }

    public String getContentType(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

    public URL getTargetAddress(MessageContext messageContext, OMOutputFormat omOutputFormat, URL url) throws AxisFault {
        return null;
    }

    public String formatSOAPAction(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

    public byte[] getBytes(MessageContext messageContext, OMOutputFormat omOutputFormat) throws AxisFault {
        return new byte[0];
    }
}

Note: You can get the transport headers using getProperty(MessageContext.TRANSPORT_HEADERS). Transport headers comes as key and value pairs (Map).

According to the above sample you can print the headers as below

Accept >> */*
Accept-Encoding >> gzip, deflate
Accept-Language >> en-US,en;q=0.8
Cache-Control >> no-cache
Content-Type >> application/xml
Host >> tharanga:8280
Origin >> chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Postman-Token >> 8e78f643-842c-175a-69c0-3a94e1ba1950
Sample-Custom-header >> Test value

How to use custom handler in WSO2 APIM

In this article, I'm going to explain how to custom handler in WSO2 APIM

You can follow below steps
  1. Implement sample handler. You can follow this blog to write a custom handler.
  2. Create a jar file
  3. Add the jar file to the <APIM_HOME>/repository/components/lib
  4.  Login to the management console 
  5. Go to the Main menu -> Service Bus -> Source View
  6. Add the below configuration to the API, handlers configuration section
<handler class="com.wso2.sample.handler.CustomHandler"></handler>

How to write handler class for WSO2 APIM?

In this article, I'm going to cover below areas
  1. How to write custom handler 
  2. How to get the request and response payload
Find the below sample custom handler to cover above scenarios.

package com.wso2.sample.handler;

import org.apache.axis2.context.MessageContext;
import org.apache.commons.io.IOUtils;

import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.rest.AbstractHandler;
import org.apache.synapse.transport.passthru.util.RelayUtils;

import java.io.InputStream;
import java.io.StringWriter;

public class CustomHandler extends AbstractHandler {

    public boolean handleRequest(org.apache.synapse.MessageContext messageContext) {
        try {
            RelayUtils.buildMessage(((Axis2MessageContext) messageContext).getAxis2MessageContext());
            InputStream jsonPaylodStream = (InputStream) ((Axis2MessageContext) messageContext)
                    .getAxis2MessageContext().getProperty(
                            "org.apache.synapse.commons.json.JsonInputStream");
            StringWriter writer = new StringWriter();
            IOUtils.copy(jsonPaylodStream, writer);
            // You can get the request message here
            String payloadMessge = writer.toString();
            System.out.println("Request payload message :" + payloadMessge);
            return true;
        } catch (Exception ex) {
            return true;
        }
    }

    public boolean handleResponse(org.apache.synapse.MessageContext messageContext) {
        try {
            RelayUtils.buildMessage(((Axis2MessageContext) messageContext).getAxis2MessageContext());

            InputStream jsonPaylodStream = (InputStream) ((Axis2MessageContext) messageContext)
                    .getAxis2MessageContext().getProperty(
                            "org.apache.synapse.commons.json.JsonInputStream");
            StringWriter writer = new StringWriter();
            IOUtils.copy(jsonPaylodStream, writer);
            // You can get the response message here
            String payloadMessge = writer.toString();
            System.out.println("Response payload message :" + payloadMessge);
            return true;
        } catch (Exception ex) {
            return true;
        }
    }
}

Before access the payload, we need to build the message, so, we can build the message as below
RelayUtils.buildMessage(((Axis2MessageContext) messageContext).getAxis2MessageContext());

After that you can access the payload with below code segment.
  InputStream jsonPaylodStream = (InputStream) ((Axis2MessageContext) messageContext)
          .getAxis2MessageContext().getProperty(
                   "org.apache.synapse.commons.json.JsonInputStream");
  StringWriter writer = new StringWriter();
  IOUtils.copy(jsonPaylodStream, writer);
  // You can get the response message here
  String payloadMessge = writer.toString();

Tuesday, April 11, 2017

How to access transport headers in WSO2 custom formatter?

You can access the transport header using messageContext.getProperty as below. (Transport header stored under TRANSPORT_HEADERS property.)

Map<String, Object> headerMap = 
      (Map<String, Object>) messageContext.getProperty(MessageContext.TRANSPORT_HEADERS);

Find the below sample formatter

package com.wso2.sample.formatter;

import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.MessageFormatter;

import java.io.OutputStream;
import java.net.URL;
import java.util.Map;
import java.util.Set;

public class SampleFormatter implements MessageFormatter {

    public void writeTo(MessageContext messageContext, OMOutputFormat omOutputFormat, OutputStream outputStream, boolean b) throws AxisFault {
        Map<String, Object> headerMap = (Map<String, Object>) messageContext.getProperty(MessageContext.TRANSPORT_HEADERS);
        Set<String> keySet = headerMap.keySet();
        for (String key : keySet) {
            System.out.println(key + " >> " + headerMap.get(key));
        }
    }

    public String getContentType(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

    public URL getTargetAddress(MessageContext messageContext, OMOutputFormat omOutputFormat, URL url) throws AxisFault {
        return null;
    }

    public String formatSOAPAction(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

    public byte[] getBytes(MessageContext messageContext, OMOutputFormat omOutputFormat) throws AxisFault {
        return new byte[0];
    }
}

Now you can see the transport headers in as below. (According to the above sample it displayed in the carbon log file)

Accept >> */*
Accept-Encoding >> gzip, deflate
Accept-Language >> en-US,en;q=0.8
Cache-Control >> no-cache
Content-Type >> application/xml
Host >> tharanga:8280
Origin >> chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Postman-Token >> 8e78f643-842c-175a-69c0-3a94e1ba1950


Monday, March 27, 2017

How to use the custom Axis2 handler in WSO2 ESB

In this article, I'm going to explain how to use Axis2 handler in WSO2 ESB

1. Implement a sample Synapse handler. You can follow my previous blog to write a Synapse handler

2. Implement a sample module

package com.wso2.handler.sample;

import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.description.AxisDescription;
import org.apache.axis2.description.AxisModule;
import org.apache.axis2.modules.Module;
import org.apache.neethi.Assertion;
import org.apache.neethi.Policy;

/**
 * Created by tharanga.
 */
public class SampleAxis2Module implements Module {
    public void init(ConfigurationContext configurationContext, AxisModule axisModule) throws AxisFault {
        System.out.println("==================================================");
    }

    public void engageNotify(AxisDescription axisDescription) throws AxisFault {

    }

    public boolean canSupportAssertion(Assertion assertion) {
        return true;
    }

    public void applyPolicy(Policy policy, AxisDescription axisDescription) throws AxisFault {

    }

    public void shutdown(ConfigurationContext configurationContext) throws AxisFault {

    }
}

3. Created module.xml as below (in resources folder /META-INF/module.xm)


<module name="CustomLoggingModule" class="com.wso2.handler.sample.SampleAxis2Module">
    <InFlow>
        <handler name="InFlowLogHandler" class="com.wso2.handler.sample.SampleAxis2Handler">
            <order phase="CustomLoggingModulePhase" />
        </handler>
    </InFlow>
</module>

4. Build the jar file and rename to mar and put it into the ESB_HOME/repository/deployment/server/axis2modules/ folder

5. Added the module to the axis2.xml as below
<module ref="CustomLoggingModule"/>

6. To test the scenario I added the phase to the InFlow as below

<phaseOrder type="InFlow">
    ....
    ....
    <phase name="CustomLoggingModulePhase"/>
 </phaseOrder>

7. Start the ESB

How to write Axis2 handler to validate the request payload and send back the response to the client?

In this article I'm going to cover below areas...
  1. How to write a custom Axis2 handler
  2. How to get the request payload
  3. How to validate payload (In this example, I'm checking whether the payload is a valid JSON or not)
  4. Respond to the customer without invoking the mediation flow.
Implement the handler class with extending the AbstractHandler class and implementing the Handler interface.

package com.wso2.handler.sample;

import net.minidev.json.parser.JSONParser;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.engine.AxisEngine;
import org.apache.axis2.engine.Handler;
import org.apache.axis2.handlers.AbstractHandler;
import org.apache.commons.io.IOUtils;
import org.apache.synapse.transport.passthru.util.RelayUtils;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultCode;
import org.apache.axiom.soap.SOAPFaultDetail;
import org.apache.axiom.soap.SOAPFaultReason;
import org.apache.axis2.util.MessageContextBuilder;

import javax.xml.namespace.QName;

import java.io.InputStream;
import java.io.StringWriter;

/**
 * Created by tharanga on 3/24/17.
 */
public class SampleAxis2Handler extends AbstractHandler implements Handler {

    public InvocationResponse invoke(MessageContext messageContext) throws AxisFault {
        try {

            RelayUtils.buildMessage(messageContext);

            InputStream jsonPaylodStream = (InputStream) (messageContext).getProperty(
                            "org.apache.synapse.commons.json.JsonInputStream");
            StringWriter writer = new StringWriter();
            IOUtils.copy(jsonPaylodStream, writer);
            String payloadMessge = writer.toString();

            try {
                //Validate the json message.
                JSONParser jsonParser = new JSONParser();
                jsonParser.parse(payloadMessge);

                System.out.println(messageContext.getEnvelope().getBody().toString());
                return InvocationResponse.CONTINUE;
            } catch (Exception ex) {
                AxisFault _axisFault = createAxisFault(messageContext, ex);
                MessageContext _faultContext = MessageContextBuilder.createFaultMessageContext(messageContext, _axisFault);
                _faultContext.setProperty("HTTP_SC", "400");

                AxisEngine.sendFault(_faultContext);
                return InvocationResponse.ABORT;
            }


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

        return InvocationResponse.ABORT;
    }

    private AxisFault createAxisFault(MessageContext context, Throwable e) {

        SOAPFactory soapFactory;
        String namespace;
        if (context.isSOAP11()) {
            soapFactory = OMAbstractFactory.getSOAP11Factory();
            namespace = SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI;
        } else {
            soapFactory = OMAbstractFactory.getSOAP12Factory();
            namespace = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
        }
        SOAPFault fault = soapFactory.createSOAPFault();

        SOAPFaultCode soapFaultCode = soapFactory.createSOAPFaultCode();
        soapFaultCode.setText(new QName(namespace, "Server", "soapenv"));

        SOAPFaultReason soapFaultReason = soapFactory.createSOAPFaultReason();
        soapFaultReason.setText("Error while mediate message: " + e.getMessage());

        SOAPFaultDetail soapFaultDetail = soapFactory.createSOAPFaultDetail();
        soapFaultDetail.setText("Invalid message");
        fault.setCode(soapFaultCode);
        fault.setDetail(soapFaultDetail);
        fault.setReason(soapFaultReason);

        return new AxisFault(fault);
    }
}


In the above sample, I'm validating the JSON message and if it fails, send the 400 HTTP response code to the client.

Find the sample test result below



  • Payload with valid JSON message
[2017-03-24 17:10:18,271] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "POST /services/Proxy1 HTTP/1.1[\r][\n]"
[2017-03-24 17:10:18,271] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Host: tharanga:8280[\r][\n]"
[2017-03-24 17:10:18,271] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Connection: keep-alive[\r][\n]"
[2017-03-24 17:10:18,271] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Length: 19[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Postman-Token: e2997992-9a63-6858-3afc-9095f7348130[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Cache-Control: no-cache[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Type: application/json[\r][\n]"
[2017-03-24 17:10:18,272] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept: */*[\r][\n]"
[2017-03-24 17:10:18,273] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Encoding: gzip, deflate[\r][\n]"
[2017-03-24 17:10:18,273] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2017-03-24 17:10:18,273] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "[\r][\n]"
[2017-03-24 17:10:18,273] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "{"name":"tharanga"}"
tharanga
[2017-03-24 17:10:18,275]  INFO - LogMediator status = 11111111111111111111111111111111111111111111
[2017-03-24 17:10:18,276] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "HTTP/1.1 202 Accepted[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Date: Fri, 24 Mar 2017 11:40:18 GMT[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Transfer-Encoding: chunked[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Connection: keep-alive[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "0[\r][\n]"
[2017-03-24 17:10:18,277] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"



  • Payload with invalid JSON message
[2017-03-24 17:10:26,709] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "POST /services/Proxy1 HTTP/1.1[\r][\n]"
[2017-03-24 17:10:26,709] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Host: tharanga:8280[\r][\n]"
[2017-03-24 17:10:26,709] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Connection: keep-alive[\r][\n]"
[2017-03-24 17:10:26,709] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Length: 17[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Postman-Token: 9f7fc1bb-be03-e321-b773-62bea5fce577[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Cache-Control: no-cache[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Type: application/json[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept: */*[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Encoding: gzip, deflate[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2017-03-24 17:10:26,710] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "[\r][\n]"
[2017-03-24 17:10:26,711] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "{"name":"tharanga"
[2017-03-24 17:10:26,742] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "HTTP/1.1 400 Bad Request[\r][\n]"
[2017-03-24 17:10:26,742] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Content-Type: application/json; charset=UTF-8[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Date: Fri, 24 Mar 2017 11:40:26 GMT[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Transfer-Encoding: chunked[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Connection: Close[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "99[\r][\n]"
[2017-03-24 17:10:26,743] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "{"Fault":{"faultcode":"soapenv:Server","faultstring":"Error while mediate message: Unexpected End Of File position 17: null","detail":"Invalid message"}}[\r][\n]"
[2017-03-24 17:10:26,744] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "0[\r][\n]"
[2017-03-24 17:10:26,744] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"



How to use the custom Synapse handler in WSO2 ESB

In this article, I'm going to explain how to use Synapse handler in WSO2 ESB
  1. Implement a Synapse handler. You can follow my previous blog to write a Synapse handler
  2. Build a JAR file (If use maven, you can run: mvn clean install)
  3. Edit the <ESB_HOME>/repository/conf/synapse-handlers.xml file as below
<handlers>
    <handler class="com.wso2.handler.sample.SampleMessageHandler" name="SampleMessageHandler">
</handler></handlers>

Start the ESB server