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

How to write custom WSO2 synapse handler to validate the payload and response to the client?

In this article I'm going to cover below areas

  1. How to write a Synapse 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 AbstractSynapseHandler class as below

package com.wso2.handler.sample;

import net.minidev.json.parser.JSONParser;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.AbstractSynapseHandler;
import org.apache.synapse.MessageContext;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.core.axis2.Axis2Sender;
import org.apache.synapse.transport.passthru.util.RelayUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Map;

/**
 * Created by tharanga
 */
public class SampleMessageHandler extends AbstractSynapseHandler {

    private static final Log log = LogFactory.getLog(SampleMessageHandler.class);

    public boolean handleRequestInFlow(MessageContext messageContext) {
        try {

            org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) messageContext)
                    .getAxis2MessageContext();
            Object headers = axis2MessageContext
                    .getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);

            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);

            String payloadMessge = writer.toString();

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

                System.out.println(messageContext.getEnvelope().getBody().toString());

                
                return true;
            } catch (Exception ex) {
                Map headersMap = (Map) headers;
                headersMap.clear();
                axis2MessageContext.setProperty("HTTP_SC", "400");
                messageContext.setProperty("RESPONSE", "true");
                messageContext.setTo(null);

                Axis2Sender.sendBack(messageContext);

                return false;
            }

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

        return false;
    }

    public boolean handleRequestOutFlow(MessageContext messageContext) {
        return true;
    }

    public boolean handleResponseInFlow(MessageContext messageContext) {
        return true;
    }

    public boolean handleResponseOutFlow(MessageContext messageContext) {
        return true;
    }
}

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-23 22:13:55,933] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "POST /services/Proxy1 HTTP/1.1[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Host: tharanga:8280[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Connection: keep-alive[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Length: 19[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Postman-Token: 29dfad86-fa3e-5576-4bfd-25f503687baa[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Cache-Control: no-cache[\r][\n]"
[2017-03-23 22:13:55,952] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop[\r][\n]"
[2017-03-23 22:13:55,956] 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-23 22:13:55,956] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Type: application/json[\r][\n]"
[2017-03-23 22:13:55,956] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept: */*[\r][\n]"
[2017-03-23 22:13:55,956] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Encoding: gzip, deflate[\r][\n]"
[2017-03-23 22:13:55,956] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2017-03-23 22:13:55,957] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "[\r][\n]"
[2017-03-23 22:13:55,957] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "{"name":"tharanga"}"
tharanga
[2017-03-23 22:13:56,239]  INFO - LogMediator status = 11111111111111111111111111111111111111111111
[2017-03-23 22:13:56,250] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "HTTP/1.1 202 Accepted[\r][\n]"
[2017-03-23 22:13:56,251] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Date: Thu, 23 Mar 2017 16:43:56 GMT[\r][\n]"
[2017-03-23 22:13:56,251] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Transfer-Encoding: chunked[\r][\n]"
[2017-03-23 22:13:56,251] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Connection: keep-alive[\r][\n]"
[2017-03-23 22:13:56,251] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"
[2017-03-23 22:13:56,252] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "0[\r][\n]"
[2017-03-23 22:13:56,252] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"
  • Payload with invalid JSON message
[2017-03-23 22:14:10,283] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "POST /services/Proxy1 HTTP/1.1[\r][\n]"
[2017-03-23 22:14:10,283] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Host: tharanga:8280[\r][\n]"
[2017-03-23 22:14:10,283] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Connection: keep-alive[\r][\n]"
[2017-03-23 22:14:10,283] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Length: 17[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Postman-Token: 194a9cd8-35ed-3998-08b7-fb94598797a3[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Cache-Control: no-cache[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop[\r][\n]"
[2017-03-23 22:14:10,284] 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-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Content-Type: application/json[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept: */*[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Encoding: gzip, deflate[\r][\n]"
[2017-03-23 22:14:10,284] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "Accept-Language: en-US,en;q=0.8[\r][\n]"
[2017-03-23 22:14:10,285] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "[\r][\n]"
[2017-03-23 22:14:10,285] DEBUG - wire HTTP-Listener I/O dispatcher-1 >> "{"name":"tharanga"
[2017-03-23 22:14:10,419] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "HTTP/1.1 400 Bad Request[\r][\n]"
[2017-03-23 22:14:10,420] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Content-Type: application/json; charset=UTF-8[\r][\n]"
[2017-03-23 22:14:10,420] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Date: Thu, 23 Mar 2017 16:44:10 GMT[\r][\n]"
[2017-03-23 22:14:10,420] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Transfer-Encoding: chunked[\r][\n]"
[2017-03-23 22:14:10,420] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "Connection: Close[\r][\n]"
[2017-03-23 22:14:10,420] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"
[2017-03-23 22:14:10,421] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "11[\r][\n]"
[2017-03-23 22:14:10,421] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "{"name":"tharanga[\r][\n]"
[2017-03-23 22:14:10,421] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "0[\r][\n]"
[2017-03-23 22:14:10,421] DEBUG - wire HTTP-Listener I/O dispatcher-1 << "[\r][\n]"