Friday, July 25, 2014

Proxying VFS file system to HTTP with WSO2 ESB or API Manager

I ran into a very tiny issue today when I was trying to proxy a file system to an http backend using WSO2 ESB.  It would be the same situation with WSO2 AM proxies.  I know, this seems like a little bit of an obscure scenario.  And, there is excellent documentation already available on this feature here, here and here.

The Symptoms

Let's say that you're adding a custom proxy for vfs -> https.  You've already enabled vfs in your axis2.xml file.  You've created a custom proxy with the vfs properties.  You've pointed the file at your endpoint with a send mediator.  Yet, the backend is not receiving the file at all!  It will be picked up, and placed in the vfs error directory.  Mysterious.

The Solution

There is a property necessary for the outbound endpoint handling a vfs file that isn't often necessary with http to http proxies.  The type of the document with vfs does not default to soap, as it does for http input.  It must be assigned manually!  This is laid out in the examples, but it's not apparent.  Here is the property that is probably missing for you:

<definitions xmlns="http://ws.apache.org/ns/synapse">
    <proxy name="StockQuoteProxy" transports="vfs">
        <parameter name="transport.vfs.FileURI">file:///home/user/test/in</parameter> <!--CHANGE-->
        <parameter name="transport.vfs.ContentType">text/xml</parameter>
        <parameter name="transport.vfs.FileNamePattern">.*\.xml</parameter>
        <parameter name="transport.PollInterval">15</parameter>
        <parameter name="transport.vfs.MoveAfterProcess">file:///home/user/test/original</parameter> <!--CHANGE-->
        <parameter name="transport.vfs.MoveAfterFailure">file:///home/user/test/original</parameter> <!--CHANGE-->
        <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter>
        <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter>
        <target>
            <endpoint>
                <address format="soap12" uri="http://localhost:9000/services/SimpleStockQuoteService"/>
            </endpoint>
            <outSequence>
                <property name="transport.vfs.ReplyFileName"
                          expression="fn:concat(fn:substring-after(get-property('MessageID'), 'urn:uuid:'), '.xml')"
                          scope="transport"/>
                <property action="set" name="OUT_ONLY" value="true"/>
                <send>
                    <endpoint>
                        <address uri="vfs:file:///home/user/test/out"/> <!--CHANGE-->
                    </endpoint>
                </send>
            </outSequence>
        </target>
        <publishWSDL uri="file:repository/samples/resources/proxy/sample_proxy_1.wsdl"/>
    </proxy>
</definitions>

This format="soap12" (or soap11) is necessary for the message to be constructed properly for a soap endpoint.  If you miss it, the message can't be sent.  It's just a small property, and it's not always necessary... so it's easy to miss.  

Cheers, Y'all. 

WSO2 DSS in Windows - accessing Oracle 11g requires the orai18n.jar

Okay, I should preface this post with the fact that I've seen as many connection strings and ways of connecting to Oracle as there are DB clients.  I don't think there is anything unusual about having trouble connecting to Oracle on a new application, and I expect connection methods may change with future oracle versions.

The Symptoms

You are attemting to connect to Oracle 11g R2 using the WSO2 DSS server running on Windows.  When attempting to add a datasource, you see an unusual error:

TID: [0] [DSS] [2014-07-24 17:47:05,125] ERROR {org.apache.axis2.rpc.receivers.RPCMessageReceiver} -  oracle.i18n.text.converter.CharacterConverterOGS.getInstance(I)Loracle/i18n/text/converter/CharacterConverter; {org.apache.axis2.rpc.receivers.RPCMessageReceiver}
java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.apache.axis2.rpc.receivers.RPCUtil.invokeServiceClass(RPCUtil.java:212)
...
Caused by: java.lang.NoSuchMethodError: oracle.i18n.text.converter.CharacterConverterOGS.getInstance(I)Loracle/i18n/text/converter/CharacterConverter;
 

The Solution

Just like the stack dump indicates, there may be a missing method.  This appears to have something to do with Oracle 11g jdbc in Windows environments.  Oracle offers an additional jar for this scenario, namely orai18n.jar.  This file is needed in addition to the jdbc6 driver.  I found it offered on the same Oracle 11g site that offered the jdbc6 driver for that version.  Adding the orail18n.jar along with the driver should take care of the missing method, and allow you to connect to Oracle 11g.

Or, you could try running WSO2 DSS from *nix instead... Works like a charm there.

Cheers, Y'all!

WSO2 API Manager proxing without chunking

I recently was working with a client who had a backend server which could not accept chunked content. For example, this may occur with an early version Windows .NET framework backend.  If you have any backend server that can't chew a chunked http body, perhaps this post will help you.

The Symptoms: 

The backend server is not able to return a response to post or put HTTP calls.  If you disable chunking globally, the backend server may be unable to parse get requests.


The Solution:

Disable chunking, but only for post and put requests.  This can be done through customizing the velocity template by adding some custom logic.  The velocity_template.xml file is located in <InstallDir>/repository/resources/api_templates/ . Changing this file will affect future APIs created in API manager.

Changes to the Velocity template:

The sections of the API template that handle responses can filter them by HTTP method.  I've added a simple choice based on HTTP method, and added a new block for Posts and Puts that has this property:
   <property name="DISABLE_CHUNKING" value="true" scope="axis2"/>

The exact code was a bit big for a blog, so I put it in a Google Doc.  Please note that this will be out-dated pretty quickly.  It is written for WSO2 APIM 1.7.0.

https://drive.google.com/file/d/0B0dBKhEyYLmVQXlUbm84QWFNNzQ/edit?usp=sharing


Cheers, Y'all!

Monday, July 21, 2014

Accessing the API Manager Gateway using the WSO2 ELB

Every day I'm setting up the WSO2 API manager in different configurations.  Some issues seem to come up time and again.  This post is about a simple one:  using the proper host names to connect to the Gateway nodes through the WSO2 Elastic Load Balancer.

When setting up the loadbalancer.conf file in <ELB_HOME>/repository/conf , you set host names for the worker and manager clusters:

  esb {
        # multiple hosts should be separated by a comma.
    #url_suffix         esb.wso2.com;
        domains   {
            wso2.esb.domain {
                tenant_range    *;
        group_mgt_port 4500;
        worker {
            hosts esb.wso2.com;
        }
        mgt {
            hosts mgt.esb.wso2.com;
        }
            }
        }
    }

At first, I thought that these host names were internal to the application, and would only be used in the API node setup.  But they aren't!  The domain name is used in the worker/manager cluster configuration, but the host names are not.  Instead, the host names are for external use only.  They are the method through which traffic is directed to the correct group (worker or mgt).

If you have trouble connecting to a worker or manager through an ELB, check that the host name you're using matches exactly one of the hosts defined in the ELB.  Otherwise, you may get a confusing error:

ERROR - DynamicLoadbalanceEndpoint application member not available

This doesn't always mean that the worker member isn't connected to the ELB (although that can happen).  In some cases, it can mean that you tried connecting to the ELB using a host name (or IP, or local host) that wasn't explicitly assigned in loadbalancer.conf.

Cheers, y'all!