How to write WSO2 ESB connectors

  • By Malaka Silva
  • 3 Mar, 2014

Applies to

WSO2 ESB 4.8.x

Table of contents

Why do you write connectors?

A connector is a way to connect to an API that is provided by a third party. For example, the user can write a cloud connector to connect to Salesforce from WSO2 ESB. The same can be achieved by configuring WSO2 ESB without a connector. However, once you write a connector, you can use WSO2 ESB to seamlessly connect to those APIs by writing simple configurations within the ESB. That will enable the capabilities of WSO2 ESB to be used with the operations supported by those APIs. There are already five connectors available at the time of writing this article and more in the pipeline.

Steps to write a connector?

Writing a cloud connector can be split into 3 main tasks as explained below.

  1. Research on the APIs provided by the specific third-party company.
  2. Decide on which API you are going to use to write the connector. This can be one of the following.
    • REST-based connectors
    • SOAP-based connectors
    • Java API-based connectors
  3. Start writing the connector by using the connector core available with WSO2 ESB.

The rest of the article shows how to write connectors using each of those APIs. The following standard folder structure should be followed.

 

Folder Structure

 

1 connector.xml This defines the connector name and dependant modules. (Metadata of the connector)
2 component.xml This is included in each module. This defines the available methods in the module.
3 init.xml For a connector, this is mandatory. By using this, users can initialize the connector environment, e.g. when writing the Salesforce connector login call can be included here. Sessiontoken and API URL returned as the response can be stored in a property and used with other operations.
4 assemble-connector.xml/filter.properties These files are used at the build time of the connector. You barely need to modify this file. So, in this article, we will not provide a detailed explanation.
5 pom.xml Contains the required dependencies for connector core libraries and relevant synapse libraries as well as maven repositories for a specific connector.
6 <operation-name>.xml This is the actual API operation calling configuration. It contains the steps necessary to call the API that is exposed by the third party. Similar to init.xml, each method of the API can be written. If there are any Java codes those can be included under Java and relevant dependants needs to be added to pom.xml

The above sample folder structure is available in the following link. By using this, the user can start implementing connectors.

https://github.com/wso2/esb-connectors/tree/master/template

Now you have the knowledge about basic structure of a connector. The next sections of the article describes in detail the different types of connectors.

SOAP-based connectors

Since we are writing a connector for SOAP API, we can write the entire connector with Synapse configurations. You can create a maven project from your preferred IDE and the folder structure should be similar to the above.

Following is an example of a SOAP-based connector that is written for Salesforce in order to execute the query method of the API.

  
<template name="query" xmlns="http://ws.apache.org/ns/synapse">

 <parameter name="sessionId"/>

 <parameter name="url"/>

 <parameter name="queryString"/>

 <sequence>

  <payloadFactory>

   <format>

    <soapenv:Envelope xmlns:soapenv="https://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">

     <soapenv:Header>

      <urn:SessionHeader>

       <urn:sessionId>$1</urn:sessionId>

      </urn:SessionHeader>

     </soapenv:Header>

     <soapenv:Body>

      <urn:query>

       <urn:queryString>$2</urn:queryString>

      </urn:query>

     </soapenv:Body>

    </soapenv:Envelope>

   </format>

   <args>

    <arg expression="$func:sessionId"/>

    <arg expression="$func:queryString"/>

   </args>

  </payloadFactory>

  <property name="messageType" scope="axis2" value="text/xml" />

  <header name="Action" value="urn:partner.soap.sforce.com/Soap/queryRequest" />

  <property value="true" name="FORCE_ERROR_ON_SOAP_FAULT" />

  <property name="HTTP_METHOD" scope="axis2" value="POST" />

  <header name="To" expression="$func:url" />

  <call>

   <endpoint>

    <default format="soap11">

     <timeout>

      <duration>60000</duration>

      <responseAction>fault</responseAction>

     </timeout>

     <suspendOnFailure>

      <initialDuration>2000</initialDuration>

      <progressionFactor>1.0</progressionFactor>

      <maximumDuration>3000</maximumDuration>

     </suspendOnFailure>

    </default>

   </endpoint>

  </call>

 </sequence>

</template>

In the above example, we are creating the required payload using payload factory mediator and we send the request using call mediator.

REST-based connectors

Writing a REST-based connector is no different from writing a SOAP connector. The only difference is the underlying communication mechanism. Other than that, you can basically write a REST-based connector just by using synapse configuration similar to SOAP connectors.

Following is an example of a connector calling Twitter rest API.

  

<template xmlns="http://ws.apache.org/ns/synapse" name="search">

 <parameter name="search" />

 <parameter name="accessToken" />

  <sequence>

   <property name="uri.var.twitter.search" expression="$func:search" />

   <property name="Authorization" expression="$func:accessToken" scope="transport" />

   <property name="messageType" value="application/x-www-form-urlencoded" scope="axis2" />

   <payloadFactory media-type="xml">

    <format>

     <soapenv:Envelope xmlns:soapenv="https://schemas.xmlsoap.org/soap/envelope/">

      <soapenv:Header />

      <soapenv:Body />

     </soapenv:Envelope>

    </format>

    <args />

   </payloadFactory>

  <call>

   <endpoint>

    <http method="GET" uri-template="https://api.twitter.com/1.1/search/tweets.json?q={uri.var.twitter.search}" />

   </endpoint>

  </call>

 </sequence>

</template>

Java API-based connectors

When you are writing Java API-based connectors, you have to use custom class mediators to implement it. You need to extend the AbstractConnector class and implement its connect method. All the required logic must go within this method. Once you have implemented this method, it can be called using the synapse configuration, which is in the .xml

Following in an example of a connector Java class that is written for Twilio in order to send an SMS.

  
import java.util.HashMap;

import java.util.Map;

import org.apache.axiom.om.OMElement;

import org.apache.synapse.MessageContext;

import org.apache.synapse.SynapseException;

import org.apache.synapse.SynapseLog;

import org.wso2.carbon.connector.core.AbstractConnector;

import org.wso2.carbon.connector.core.util.ConnectorUtils;

import org.wso2.carbon.connector.twilio.util.TwilioUtil;

import com.twilio.sdk.TwilioRestClient;

import com.twilio.sdk.resource.factory.SmsFactory;

import com.twilio.sdk.resource.instance.Sms;

public class SendSms extends AbstractConnector {

 public void connect(MessageContext messageContext) {

  SynapseLog log = getLog(messageContext);

  log.auditLog("Start: send SMS");

  String to = (String) ConnectorUtils.lookupTemplateParamater(messageContext, TwilioUtil.PARAM_TO);

  String from = (String) ConnectorUtils.lookupTemplateParamater(messageContext, TwilioUtil.PARAM_FROM);

  String body = (String) ConnectorUtils.lookupTemplateParamater(messageContext, TwilioUtil.PARAM_BODY);

  String statusCallBackUrl = (String) ConnectorUtils.lookupTemplateParamater(messageContext, TwilioUtil.PARAM_STATUS_CALLBACK_URL);

  String applicationSid = (String) ConnectorUtils.lookupTemplateParamater(messageContext, TwilioUtil.PARAM_APPLICATION_SID);

  Map params = new HashMap();

  params.put(TwilioUtil.TWILIO_TO, to);

  params.put(TwilioUtil.TWILIO_FROM, from);

  params.put(TwilioUtil.TWILIO_BODY, body);

  if (applicationSid != null) {

   params.put(TwilioUtil.TWILIO_APPLICATION_SID, applicationSid);

  }

  if (statusCallBackUrl != null) {

   params.put(TwilioUtil.TWILIO_STATUS_CALLBACK, statusCallBackUrl);

  }

  try {

   TwilioRestClient twilioRestClient = TwilioUtil.getTwilioRestClient(messageContext);

   SmsFactory messageFactory = twilioRestClient.getAccount().getSmsFactory();

   Sms message = messageFactory.create(params);

   OMElement omResponse = TwilioUtil.parseResponse("sms.create.success");

   TwilioUtil.addElement(omResponse, TwilioUtil.PARAM_MESSAGE_SID, message.getSid());

   TwilioUtil.addElement(omResponse, TwilioUtil.PARAM_STATUS, message.getStatus());

   TwilioUtil.preparePayload(messageContext, omResponse);

  } catch (Exception e) {

   log.error(e.getMessage());

   messageContext.setProperty(SynapseConstants.ERROR_EXCEPTION, e);

   messageContext.setProperty(SynapseConstants.ERROR_MESSAGE, e.getMessage());

   messageContext.setProperty(SynapseConstants.ERROR_CODE, "0007");

   throw new SynapseException(e);

  }

  log.auditLog("End: send SMS");

 }

}

Following is the associated sendSms.xml that calls the class mediator.

  

<template name="sendSms" xmlns="http://ws.apache.org/ns/synapse">

 <parameter name="body"/>

 <parameter name="to"/>

 <parameter name="from"/>

 <parameter name="statusCallBackUrl"/>

 <parameter name="applicationSid"/>

 <sequence>

  <class name="org.wso2.carbon.connector.twilio.sms.SendSms"/>

 </sequence>

</template>

So these are the currently available approaches. You should be able to write any connector using one of these approaches.

Summary

WSO2 ESB connectors are components that encapsulate third-party API calls. There are different ways users can write connectors according to the API or user preference. With the use of connectors, integrations becomes easier and much simpler with WSO2 ESB.