Thursday, July 9, 2015

How to deal with "java.nio.charset.MalformedInputException: Input length = 1" WSO2 ESB

Some times we are receiving unusual characters in our responses from various back ends. Then WSO2 ESB is facing problems in understanding those characters from the response and it tends to throw following exception.

[2015-07-09 12:42:49,651] ERROR - TargetHandler I/O error: Input length = 1
java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:277)
    at org.apache.http.impl.nio.reactor.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:193)
    at org.apache.http.impl.nio.codecs.AbstractMessageParser.parse(AbstractMessageParser.java:171)
    at org.apache.synapse.transport.http.conn.LoggingNHttpClientConnection$LoggingNHttpMessageParser.parse(LoggingNHttpClientConnection.java:210)
    at org.apache.synapse.transport.http.conn.LoggingNHttpClientConnection$LoggingNHttpMessageParser.parse(LoggingNHttpClientConnection.java:192)
    at org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:171)
    at org.apache.synapse.transport.http.conn.LoggingNHttpClientConnection.consumeInput(LoggingNHttpClientConnection.java:106)
    at org.apache.synapse.transport.passthru.ClientIODispatch.onInputReady(ClientIODispatch.java:83)
    at org.apache.synapse.transport.passthru.ClientIODispatch.onInputReady(ClientIODispatch.java:41)
    at org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:119)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:160)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:342)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:320)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:106)
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:604)
    at java.lang.Thread.run(Thread.java:745)


Most of the case,  by adding following property to the "passthru-http.properties" file will solve the case.

Eg:  once i was getting a response like following

[2015-07-09 12:47:55,236] DEBUG - wire >> "HTTP/1.1 100 Continue[\r][\n]"
[2015-07-09 12:47:55,237] DEBUG - wire >> "[\r][\n]"
[2015-07-09 12:47:55,274] DEBUG - wire >> "HTTP/1.1 201 Cr[0xe9]e[\r][\n]"
[2015-07-09 12:47:55,274] DEBUG - wire >> "Set-Cookie: JSESSIONID=87101C43FCABBB97D049C0F0C8DD216D; Path=/api/; Secure; HttpOnly[\r][\n]"
[2015-07-09 12:47:55,274] DEBUG - wire >> "Location: https://abc.foo.com/api/office/files/2038[\r][\n]"
[2015-07-09 12:47:55,274] DEBUG - wire >> "Content-Type: application/json[\r][\n]"
[2015-07-09 12:47:55,274] DEBUG - wire >> "Transfer-Encoding: chunked[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "Vary: Accept-Encoding[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "Date: Thu, 09 Jul 2015 16:47:55 GMT[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "Server: qa-byp[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "1f[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "{"person":john}[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "0[\r][\n]"
[2015-07-09 12:47:55,275] DEBUG - wire >> "[\r][\n]"


Then it thew the above exception.

By adding the above entry as follows, i could get rid of that.

http.protocol.element-charset=iso-8859-1

සිතුවිල්ල





එදා සුදු ගවුම ඇඳන් බස් එකෙන් ඇවිත් බහිනකල්
නොඉවසිල්ලෙන් බස් ස්ටැන්ඩ් එකට වෙලා බලන් උන්නු මම...
අද..
පුංචි පුතාගෙ හුරතල් හිනාව බලන්න
ගෙදර යන්න ඔරලොසුවෙ කටුව කැරකෙනකල් බලන් ඉන්නවා..
කාලය... නුඹේ අරුමය..

Wednesday, July 8, 2015

WSO2 ESB API with JMS Queues - HTTP GET

We are going to discuss on how we can handle HTTP GET methods with JMS Proxy services in WSO2. With this blog we are going to discuss following message flow.


Flow is like follows;


  • Client invokes an API defined in ESB
  • From the API it sends the message to a JMS queue specifying a "ReplyTo" queue
  • There is a JMS proxy consume message from the above queue 
  • After consuming message, it will invoke the backend and response will be sent back to JMS "Reply To" queue
  • Rest API will consume the message from that "Reply To" queue and send back the response to the client.


In order to have this flow working, you need to setup ActiveMQ with wso2 ESB. You can find the documentation to do it in this documentation page.

Lets see the API


<api xmlns="http://ws.apache.org/ns/synapse" name="WeatherAPI" context="/getQuote">
   <resource methods="POST GET" uri-template="/details?*">
      <inSequence>
         <property name="transport.jms.ContentTypeProperty" value="Content-Type" scope="axis2"></property>
         <log>
            <property name="httpMethod" expression="get-property('axis2','HTTP_METHOD')"></property>
         </log>
         <property name="HTTP_METHOD" expression="get-property('axis2','HTTP_METHOD')" scope="transport" type="STRING"></property>
         <send>
            <endpoint>
               <address uri="jms:/SMSStore?transport.jms.ConnectionFactoryJNDIName=QueueConnectionFactory&java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory&java.naming.provider.url=tcp://localhost:61616&transport.jms.DestinationType=queue&transport.jms.ContentTypeProperty=Content-Type&transport.jms.ReplyDestination=SMSReceiveNotificationStore"></address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <property name="TRANSPORT_HEADERS" scope="axis2" action="remove"></property>
         <send></send>
      </outSequence>
   </resource>
</api>

In the above API , there are few things to be focused on.
  • You can see that we are setting the HTTP_METHOD to a property extracted from the incoming message as follows.  By default, for HTTP POST method, API invocation works perfectly with out setting this property at the end. But If we are doing a HTTP GET , we need to obtain the HTTP method at the consumer side of the queue to invoke the back end with a http GET. So we are setting it here. 

<property name="HTTP_METHOD" expression="get-property('axis2','HTTP_METHOD')" scope="transport" type="STRING"></property>


  • In the endpoint of the send mediator we are setting up following information which you should have 
    • transport.jms.ContentTypeProperty=Content-Type  - We are saying that we are passing the content type in JMS transport
    • ransport.jms.ReplyDestination=SMSReceiveNotificationStore - We are saying that ESB is expecting a response to this queue for this request 

Then in the JMS proxy which we consume the message, it will look like follows.


<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="SMSForwardProxy"
       transports="jms"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target faultSequence="fault">
      <inSequence>
         <property name="HTTP_METHOD" expression="$trp:HTTP_METHOD" scope="axis2"/>
         <send>
            <endpoint>
               <address uri="http://localhost:8080/foo">
                  <suspendOnFailure>
                     <errorCodes>-1</errorCodes>
                     <progressionFactor>1.0</progressionFactor>
                  </suspendOnFailure>
                  <markForSuspension>
                     <errorCodes>-1</errorCodes>
                  </markForSuspension>
               </address>
            </endpoint>
         </send>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
   </target>
   <parameter name="transport.jms.ContentType">
      <rules>
         <jmsProperty>contentType</jmsProperty>
         <default>text/xml</default>
      </rules>
   </parameter>
   <parameter name="transport.jms.ConnectionFactory">myQueueConnectionFactory</parameter>
   <parameter name="transport.jms.DestinationType">queue</parameter>
   <parameter name="transport.jms.Destination">SMSStore</parameter>
   <description/>
</proxy>


In the above proxy configuration, you can see following line


 <property name="HTTP_METHOD" expression="$trp:HTTP_METHOD" scope="axis2"/>

In that , what we do is , we read the HTTP_METHOD we already set in previous API before sending the message to JMS Queue and now we are reading it from transport level property.  Then we are setting it to "HTTP_METHOD" property with the "axis2" scope.

With that it will be able to figure out the HTTP method to be used on invoking the actual endpoint.


After getting a response, this Proxy will send the reply to the "SMSReceiveNotificationStore" queue. Then it will consumed from the previous API and send back the response to the client.

This will be working perfectly for HTTP POST requests. If it is a HTTP GET Request , you need to do some additional changes from the client side and the server side.

Client Side Changes


  • We need to pass the content type from the client side as follows. You can set it as a header in SOAP UI
       contentType:  application/x-www-form-urlencoded




Server Side Changes
  • You need to change the builder and formatter for above content type in the axis2.xml file located in WSO2ESB/repository/conf/axis2 directory as follows. 

.....

<messageFormatter contentType="application/x-www-form-urlencoded"
                        class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>

.......

<messageBuilder contentType="application/x-www-form-urlencoded"
                        class="org.wso2.carbon.relay.BinaryRelayBuilder"/>

......

If you does not set above , there will be message building and formatting errors.


With this way you ll be able to get this working for GET http methods.

This is tested with WSO2 ESB 4.8.1 with it's latest patches and Apache Activemq 5.10.0