This article will show you how to create a custom connector for reading data from Dell Boomi. The connector will read the list of GitHub follower’s from the public GitHub API. This should provide an overview of how to write your own custom connector for a unique I/O source.

Prerequisites

To follow along, you need to have a valid Boomi licence (or free trial) to setup the Boomi Connector SDK. The SDK is written in Java so Java development experience is assumed.

The SDK must be downloaded from your Boomi account using by clicking on Setup, then opening the Developer tab. There will be a link to download the Connector SDK.

Downloading the Boomi connector SDK

To continue development, you need to add the Jar file to your Java development environment. Depending on your toolchain, this can be done in a number of ways. The rest of this article assumes you have the downloaded SDK as part of your Java path.

Javadoc for the SDK is available online. Javadoc for the utility libraries is available here.

Terminology

A custom connector is built using a Connector, a Connection, a Browser, and Operations.

The Connector acts as the central factory class for the custom connector. The Connector is responsible for instantiating Operations and Browsers.

The Connection implements common behaviour required to interact with your connection target. This may include handling authorization credentials for an API that can be reused across different operations.

A Browser implements browsing functionality for retrieving the different operations and data types available within your custom connection.

Lastly, an Operation performs actions against your connection to retrieve or modify data. An Operation executes actions against the actual integration service.

Writing a Basic Connector

Custom connectors start with a factory for creating browsers and operations for accessing the API. In our case, we can create a simple connector that includes a get operation for retrieving followers using the GitHub API.

package sookocheff.boomi.connector;

import com.boomi.connector.api.BrowseContext;
import com.boomi.connector.api.Browser;
import com.boomi.connector.api.Operation;
import com.boomi.connector.api.OperationContext;
import com.boomi.connector.util.BaseConnector;

/**
 * A Boomi connector to read GitHub follower's using the GitHub API.
 */
public class GitHubFollowerConnector extends BaseConnector {

    @Override
    public Browser createBrowser(BrowseContext context) {
        return new GitHubFollowerBrowser(new GitHubFollowerConnection(context));
    }

    @Override
    public Operation createGetOperation(OperationContext context) {
        return new GitHubFollowerGetOperation(new GitHubFollowerConnection(context));
    }
}

The implemented methods return a Browser and a Get Operation. We will write those implementations later.

Implementing the Connection

Next up is writing a basic Connection. A connection holds behaviour and data that are common across all the different interactions with the service you are connecting to. This can include common code for making and HTTP requests, or parsing XML or JSON, as an example. For our GitHub follower API, we can still keep this simple because we are only implementing a single operation.

package sookocheff.boomi.connector;

import com.boomi.connector.api.BrowseContext;
import com.boomi.connector.util.BaseConnection;

/**
 * GitHub follower connection.
 * Maintains any behaviour useful across all operations.
 */
public class GitHubFollowerConnection extends BaseConnection
{
    public GitHubFollowerConnection(BrowseContext context) {
        super(context);
    }
}

Implementing the Browser

The Browser provides the ability to list the object types and formats for your connector. We only have a single object type for this connection, the Follower. To specify the full browser, we also need to list the schema that our object types follow. Since our connector is based on a JSON API, we need to set the schema using JSON schema (Boomi defaults to XML schemas). In this tutorial, the JSON schema for a list of followers is listed in a separate file and loaded as a string when defining the object types.

The JSON schema:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Followers",
  "type": "array",
  "items": {
    "title": "Follower",
    "type": "object",
    "properties": {
      "login": {
        "type": "string"
      },
      "id": {
        "type": "number"
      },
      "avatar_url": {
        "type": "string"
      },
      "gravatar_id": {
        "type": "string"
      },
      "url": {
        "type": "string"
      },
      "html_url": {
        "type": "string"
      },
      "followers_url": {
        "type": "string"
      },
      "following_url": {
        "type": "string"
      },
      "gists_url": {
        "type": "string"
      },
      "starred_url": {
        "type": "string"
      },
      "subscriptions_url": {
        "type": "string"
      },
      "organizations_url": {
        "type": "string"
      },
      "repos_url": {
        "type": "string"
      },
      "received_events_url": {
        "type": "string"
      },
      "type": {
        "type": "string"
      },
      "site_admin": {
        "type": "string"
      }
    }
  }
}

The Connector Browser:

package sookocheff.boomi.connector;

import com.boomi.connector.api.ContentType;
import com.boomi.connector.api.ObjectDefinition;
import com.boomi.connector.api.ObjectDefinitionRole;
import com.boomi.connector.api.ObjectDefinitions;
import com.boomi.connector.api.ObjectType;
import com.boomi.connector.api.ObjectTypes;
import com.boomi.connector.util.BaseBrowser;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;

import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Implements browse functionality for the connector.
 * Browsing allows a user to discover the types and object definitions
 * supported by this API.
 */
public class GitHubFollowerBrowser extends BaseBrowser {

    public GitHubFollowerBrowser(GitHubFollowerConnection conn) {
        super(conn);
    }

    @Override
    public ObjectTypes getObjectTypes() {
        List<String> typeNames = Collections.singletonList("Followers");

        ObjectTypes types = new ObjectTypes();
        for (String typeName : typeNames) {
            ObjectType type = new ObjectType();
            type.setId(typeName);
            types.getTypes().add(type);
        }

        return types;
    }

    @Override
    public ObjectDefinitions getObjectDefinitions(String objectTypeId, Collection<ObjectDefinitionRole> roles) {
        String schema = null;
        URL url = Resources.getResource("followerSchema.json");
        try {
            schema = Resources.toString(url, Charsets.UTF_8);
        } catch (IOException e) {
            ObjectDefinitions defs = new ObjectDefinitions();
            return defs;
        }

        if (schema == null) {
            ObjectDefinitions defs = new ObjectDefinitions();
            return defs;
        }

        ObjectDefinition followerDefinition = new ObjectDefinition();
        followerDefinition.setElementName("Followers");
        followerDefinition.setInputType(ContentType.JSON);
        followerDefinition.setOutputType(ContentType.JSON);
        followerDefinition.setJsonSchema(schema);

        ObjectDefinitions definitions = new ObjectDefinitions();

        definitions.getDefinitions().add(followerDefinition);
        return definitions;
    }

    @Override
    public GitHubFollowerConnection getConnection() {
        return (GitHubFollowerConnection) super.getConnection();
    }

}

Implementing Operations

Finally, we get to the heart of our problem — implementing API operations. This is done by extending the relevant BaseOperation. In our example, we can get GitHub followers by extending the BaseGetOperation class.

The implementation is fairly straight-forward — we simply make an HTTP request to the correct endpoint.

package sookocheff.boomi.connector;

import com.boomi.connector.api.GetRequest;
import com.boomi.connector.api.ObjectIdData;
import com.boomi.connector.api.OperationResponse;
import com.boomi.connector.api.ResponseUtil;
import com.boomi.connector.util.BaseGetOperation;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class GitHubFollowerGetOperation extends BaseGetOperation {

    public GitHubFollowerGetOperation(GitHubFollowerConnection conn) {
        super(conn);
    }

    @Override
    protected void executeGet(GetRequest request, OperationResponse response) {
        ObjectIdData requestData = request.getObjectId();
        String objectId = requestData.getObjectId();

        String requestUrl = "https://api.github.com/users/" + objectId + "/followers";
        try {
            HttpURLConnection httpConnection = (HttpURLConnection) new URL(requestUrl).openConnection();
            InputStream httpResponse = httpConnection.getInputStream();
            int httpStatusCode = httpConnection.getResponseCode();

            if (httpStatusCode == HttpURLConnection.HTTP_OK) {
                ResponseUtil.addSuccess(response, requestData, String.valueOf(httpStatusCode),
                        ResponseUtil.toPayload(httpResponse));
            } else {
                ResponseUtil.addFailure(response, requestData, String.valueOf(httpStatusCode));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public GitHubFollowerConnection getConnection() {
        return (GitHubFollowerConnection) super.getConnection();
    }
}

Where to go from here?

At this point, we have all the logic necessary to implement a Boomi connector. If you are interested in learning more, make sure to view the more complex example provided in the official Boomi SDK.