Features in Tutorial

In this tutorial you will learn how to create a connector, using the DevKit, that users can interact with in Mule and Anypoint Studio.

As you move forward you will learn the features that DevKit provides to help you build Mule connectors, and how easy it can be to extend Mule.

What is DevKit?

The DevKit is an important part of the Anypoint Platform. The DevKit is a Maven-based tool that lets you build reusable components that not only can be run as part of a Mule application, but also can be easily configured and consumed from Anypoint Studio.

When to Use DevKit

Here are several scenarios in which you may want to build your own connector:

  • You need to consume an API in one or more applications and you want to make sure everyone uses the same component.

  • You have an API and want to add strategic value to your business by providing a connector and telling the world "I am part of Mule Platform".

  • You want to facilitate integration with SaaS and on-premise Web services, applications, and data sources.

  • The API you consume supports Pagination, Batch, and/or has a SQL capability.

  • The API you consume has different entity types and/or its structure can change.

  • You want to extend Mule core.

What is a Connector?

A MuleSoft connector is a reusable component that interacts with Mule ESB and Anypoint Studio. A connector enables Mule flows to communicate with a target resource. The connector conveys data between a resource and a Mule flow, and transforms the data into a Mule Message.

Using Anypoint Connector DevKit, a connector abstracts developers from the Mule interfaces that are required to run the connector in the Mule platform, and generates a user interface.

A connector also abstracts users from the complexity of initializing required elements such as connecting to a sandbox. A well-developed connector makes Mule apps much simpler for users by handling internal tasks like pagination, session expirations, and input and output metadata. This tutorial shows you how to create a well-developed connector.

Prerequisites

To develop a connector you should have a working knowledge of Mule, Anypoint Studio, and Java development in general, specifically the use of Java annotations.

DevKit’s functionality is to expose to connector developers Java annotations that generate code and files to interact with Mule, and Anypoint Studio. The generated code provides the interface between the connector and Mule that would otherwise require each connector developer to include extensive boilerplate code, as well as the code and files required to interact with your connector in Anypoint Studio.

You can develop a connector using Windows, Mac, or Linux.

Set Up Your Environment

  1. Download and install Java 1.7 JDK.

  2. Download and install Maven. If you’re new to Maven, read Maven in 5 Minutes.

  3. Download Anypoint Studio.

  4. Check Maven configuration in Anypoint Studio.

  5. Install Anypoint DevKit plugin.

  6. Install a GitHub client.

    JRE is not suited for development, you need to have a JDK installed.

For information on setup and DevKit, see Setting up Your Dev Environment and Anypoint Connector DevKit.

Import the Cookbook

To import the cookbook tutorial from Git:

  1. Clone tutorial repository into your local machine. For example, to clone to a dev folder, run:

    cd ~/dev
    git clone https://github.com/mulesoft/mule-cookbook.git
  2. In the cloned directory, run:

    cd ~/dev/mule-cookbook
    mvn install eclipse:eclipse
  3. When done, in Anypoint Studio, import the root project as an existing project with the File > Import > General > Existing Projects into Workspace commands.

Cookbook Description

The Cookbook is a service that helps users organize ingredients and recipes, so that users can easily find recipes for the foods they like to prepare.

API Description

This API allows users to use the create, read, update, and delete (CRUD) operations for single and multiple recipes and ingredients. The API also allows you to view recently added recipes.

The API is exposed as a SOAP Service using a WSDL file, as a Rest API with a RAML file, and through a Java SDK.

Authentication

This connector supports the following authentication types:

  • Custom with a username and password that provides a token to send with each request as part of the request.

  • OAuthV2

Credentials

Custom Authentication:
Username Password

admin

admin

OAuth:
Client ID Client Secret

ePU9CxyEVFIXF9nMoGH16s1lUGe5JYPPzClnnVBG

YeKAsQCdr264tNmDdvTdJUAh9TQraJqZpwbEoTuz

Connecting to the Service

The web service is available online.

You can also run it locally since we provide the source code for the servers already ready to start.

The local SOAP Server can be run from the soap-server project by simply executing the com.cookbook.tutorial.service.MuleStoreServer class.

By default it starts the server on address http://localhost:9090/cook-book

The local Rest Server can be run from the rest-server project by simple executing the com.cookbook.tutorial.Main class.

By default it starts the server on address http://localhost/9091

Getting started

To build a basic connector for the Cookbook service, we need to perform the following steps:

  1. Create a Connector Project.

  2. Add the dependency that contains the client we will use to connect to the service.

  3. Add a configurable URL so that users can specify the URL where the service is hosted.

  4. Add an operation that users can consume in Anypoint Studio.

Create the Cookbook Connector

  1. In Anypoint Studio, click File > New > Anypoint Connector Project or right-click your project name in Package Explorer and click New > Anypoint Connector Project:

    new connector 1
  2. Select the Connector Type you want to create. In this case, select SDK Based:

    new0
  3. Specify the name of your connector and don’t forget to uncheck the default generation before clicking Finish:

    new1

    This generates a project containing the structure and all required elements including a skeleton connector, images, sample docs file, but no basic tests for your connector.

    new connector 3

    You can customize the icons by replacing the generated ones. Just make sure that they have the same size so that they look good in Studio.

    You can enable the DevKit view by clicking from the top bar Window > Show view > Other, and look for DevKit in the list.

    enable view
    Figure 1. DevKit View shows a compact view of the connector.

    You can check a detailed explanation of the relation between connector structure and Studio.

When consuming a service, you may need to configure different values to establish a connection.

To solve this, DevKit provides a pair of annotations that let you modularize in different classes:

  • The behavior you expose to users, using the @Connector annotation.

  • Code related to the connection/configuration is injected into your @Config annotated field. There are several types of configuration you can use as we will see in this tutorial.

When you mark a field with @Config, DevKit ensures that you have an initialized object set when the Mule app runs and that requests are made to your connector.

Adding Dependencies

Let’s start coding by creating a connector that allows you to get the recently added elements.

Since we don’t need any kind of authentication to consume the recently added recipes, this is the best operation we can use to start learning how to build our connector.

  1. Add our client dependency so that we can use it in our connector. This way we can use the Java API built to connect to the Cookbook.

    In your pom.xml file add:

    <dependencies>
      <dependency>
        <groupId>com.cookbook.tutorial</groupId>
        <artifactId>sdk-client</artifactId>
        <version>1.0.0-SNAPSHOT</version>
      </dependency>
    </dependencies>

Adding @Configurable Fields

On the @Configuration ConnectorConfig add a configurable field for the address where the Cookbook service is hosted:

  1. Type conf at the editor of your connector config and use Ctrl+Space to display the templates.

    config1
  2. Add a configurable field in our @Configuration component with a default value for the endpoint our client connects to so that users can connect to different sandboxes.

  3. Create the a setter and a getter, since all @Configurable items must have getters and setters.

    See full Config source code.

Initialize the Client

  1. On your @Connector marked class use the @Start annotation on a method to initialize the client.

            private MuleCookBookClient client;
    
            @Start
            public void initialize() {
                    client = new MuleCookBookClient(config.getAddress());
            }

    Anypoint connectors can be fully aware of Mule’s lifecycle without implementing any Mule-specific interface.

    There is an annotation method for each of the four phases of the lifecycle.

    When a method is annotated with one of these annotations, DevKit invokes it during the lifecycle phase that the annotation represents.

Adding @Processor Methods

  1. Remove the dummy operation the connector has.

  2. To add a processor simply type proc at the editor and use Ctrl+Space to display the templates and pick the simple processor.

    processor1
  3. Change it to reflect the getRecentlyAdded method signature, and there we have our first connector, ready to be tested.

    processor2
  4. Run the Generate Sources Action.

    See full Connector source code.

At this point you can install this Connector and try it in Studio if you want.

As you modify your connector, you may start seeing error markers on the generated folder.

Just ignore them.

Once you regenerate the sources, the errors will go away as the generated code will be refreshed.

Adding Samples to Your Connector

Let’s now add the example for it.

Create the file pointed by the {@sample …​} if it doesn’t exists already

Note: When you add an example, use the same name as the name that narrows the example in the file. Inside of it you have to put an example of the @Processor.

If you have the Javadoc check enabled, DevKit plugin marks the missing example as an error and provide a quick fix for us to easily add the example.

Otherwise, open the file and type < at the editor and use Ctrl+Space to display the templates and pick the one that best suite our operation.

sample1

Our example in this case looks like this:

<!-- BEGIN_INCLUDE(cook-book:getRecentlyAdded) -->
        <cook-book:get-recently-added/>
<!-- END_INCLUDE(cook-book:getRecentlyAdded) -->

Using the Connector in Mule

You will do a very simple app that listens to an HTTP endpoint and when we hit it, it will retrieve the list of recently added elements.

  1. Create a mule app and add a simple http listener. If this is your first mule app, take a look at our Hello World Application.

  2. Drag and drop the Connector, and configure it.

    first mule app config
  3. Drag and drop an Object to JSON transformer and run the app.

first mule app
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd">
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    <cookbook:config name="Cookbook__Configuration" doc:name="Cookbook: Configuration"/>
    <flow name="mule-appFlow">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/get-recently" doc:name="HTTP"/>
        <cookbook:get-recently-added config-ref="Cookbook__Configuration" doc:name="Cookbook"/>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
    </flow>
</mule>

If you hit the url http://localhost:8081/get-recently you will see a reply similar to:

[
   {
      "created":1428678371866,
      "id":2,
      "lastModified":null,
      "name":"Baked Apples",
      "cookTime":20.0,
      "directions":[
         "Cut the Apples",
         "Put them in the oven",
         "Remove from the oven after 20.0 minutes"
      ],
      "ingredients":[
         {
            "created":1428678371866,
            "id":1,
            "lastModified":null,
            "name":"Apple",
            "quantity":0.0,
            "unit":"UNIT"
         }
      ],
      "prepTime":30.0
   }
]

If you want to know how to create tests for your connector check:

Adding Connection Management

Unlike the getRecentlyAdded call doesn’t require authentication, all other operations require that you set a token on each request.

The client you are using provides a login call that initializes the token and uses it in subsequent requests.

Take into account that the session can expire and cause the connector to make a login request again.

DevKit provides a different set of annotations to keep your code clean and handle the connection in a different class for delegating responsibilities.

To implement @ConnectionManagement, you need to move the initialization to @ConnectionManagement, instead of using the @Start annotation as we did before.

  1. In the ConnectorConfig change the @Configuration for a @ConnectionManagement annotation

    @ConnectionManagement(friendlyName = "Configuration")
  2. Add the MuleCookbookClient to the ConnectorConfig and add a getter and getter.

        private MuleCookBookClient client;
    
        public MuleCookBookClient getClient() {
            return client;
        }
    
        public void setClient(MuleCookBookClient client) {
            this.client = client;
        }
  3. Implement these four methods as shown in the code sample that follows:

    • @Connect - Initialize the client, and if you don’t have a login, call the API to verify.

          @Connect
          @TestConnectivity
          public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {
              setClient(new MuleCookBookClient(getAddress()));
              try {
                  getClient().login(username, password);
              } catch (InvalidCredentialsException e) {
                  throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, e.getMessage(), "Invalid credentials");
              }
          }
    • @Disconnect - Release any connection if required.

          @Disconnect
          public void disconnect() {
              setClient(null);
          }
    • @ValidateConnection - Check that your connection is alive.

          @ValidateConnection
          public boolean isConnected() {
              return getClient() != null;
          }
    • @ConnectionIdentifier - Return a string value. This will only be used when debugging.

          @ConnectionIdentifier
          public String connectionId() {
              return "001";
          }
  4. Remove the MuleCookBookClient from the CookbookConnector and the @Start method

  5. On our @Processor use the client provided by the ConnectorConfig

        @Processor
        public List<Recipe> getRecentlyAdded() {
            return config.getClient().getRecentlyAdded();
        }

See:

If you install this version and try to run the mule app we created before, you will see that it fails with a SAXParseException because we need to add a username and password to our configuration.

Just open the global configuration of your connector and check that there are 2 new fields, username and password. Configure them and you can run the app again.

connection management

This is the mule updated xml:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd">
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    <cookbook:config name="Cookbook__Configuration" doc:name="Cookbook: Configuration type config" password="admin" username="admin"/>
    <flow name="mule-appFlow">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/get-recently" doc:name="HTTP"/>
        <cookbook:get-recently-added config-ref="Cookbook__Configuration" doc:name="Cookbook"/>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
    </flow>
</mule>

See:

Improving Error Handling

This section explains how to improve exception handling and reconnecting.

Exception Handling with @Handler

The @Handler feature, is very useful when you want to avoid duplicated code when handling exceptions, and also so that your connector code is cleaner.

When handling messages retrieved from the API, if the message is poor and you know how to improve it, you can use this mechanism, to enrich the error provided to the user.

To see how it works, let’s create a handler for the InvalidEntityException thrown by the create() call of the Cookbook SDK.

  1. Create a new Anypoint Connector Component by doing a right click and selecting the wizzard.

    new component
  2. Select the package, component type and class name and click on Finish.

    new component 2
  3. Improve the error message when InvalidEntityException is thrown.

        @Handle
        public void handleException(Exception ex) throws Exception {
            if (ex instanceof InvalidEntityException) {
                throw new RuntimeException("You cannot provide an Id when creating an Ingredient");
            } else {
                // Just let it go
                throw ex;
            }
        }

    Check the full source code.

  4. Add a new processor to create Ingredients

    @Processor
    @OnException(handler = CookbookHandler.class)
    public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
        return (Ingredient) config.getClient().create(Ingredient);
    }

    Check the full source code.

Handling Session Expiration

There is no need for users to add custom code in their Mule apps to handle session expiration. DevKit provides a mechanism to do this in a clean way.

Just annotate your @Processor method with the @ReconnectOn exception.

@Processor
@OnException(handler = CookbookHandler.class)
@ReconnectOn(exceptions = { SessionExpiredException.class })
public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
    return (Ingredient) config.getClient().create(Ingredient);
}

Check the full source code.

In our Mule app, you can configure a reconnection strategy so that our Mule app is ready to handle session expirations.

Global Config dialog configuration:

reconnect

The XML generated looks like:

<cookbook:config-type doc:name="config" name="config" password="admin" username="admin">
    <reconnect/>
</cookbook:config-type>

Adding DataSense

What is it?

DataSense improves the user experience at design time when creating Mule applications by enabling your connector to determine the API of your target resource.

Even though DataSense is optional, its use enables connector users to acquire metadata of the entities in a service.

See:

In this tutorial, we use a static DataSense model, which means that the entities are fixed and known upfront, and do not change. The fields that this model supports are also fixed.

Using DataSense

To use DataSense in the cookbook:

  1. Analyze the entities in Mule Cookbook service. We only have two simple entities, Recipe and Ingredient that both extend from CookBookEntity.

  2. Look at how our createIngredient operation looks inside Anypoint Studio, and how it interacts with other components.

    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Ingredient createIngredient(@Default("#[payload]") Ingredient Ingredient) throws InvalidEntityException, SessionExpiredException {
        return (Ingredient) config.getClient().create(Ingredient);
    }

Handling Ingredients

We defined the operation so that it receives an Ingredient, and it returns an Ingredient with the extra fields populated from the server.

Let’s handle the ingredients:

  1. Check the input metadata to see that the expected output is a POJO, with the expected fields our Ingredient has:

    datasense expected ingredients
  2. Verify that the output metadata is expected:

    datasense ingredients
  3. Drag and drop a Transform Message element either behind or after our connector. The Transform Message element automatically picks the input or output:

    datasense input
    Figure 2. Transform Message Receiving Connector Output

    Because DevKit auto-generates static metadata, DevKit automatically ensures that your connector knows how to propagate metadata information.

Handling Recipes

We don’t have just Ingredients, we also have Recipes, so we don’t want one method for each entity we have in our model.

Modify your connector to just work with the CookBookEntity class:

  1. Create an operation:

    @Processor
    @OnException (handler=CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public CookBookEntity create(@Default("#[payload]") @RefOnly CookBookEntity entity) throws InvalidEntityException, SessionExpiredException {
        return config.getClient().create(entity);
    }
  2. The @RefOnly annotation is used to tell DevKit that the input can only be specified as a reference (due to a DevKit limitations on handling Abstract classes).

    Let’s see how this affects the UI and user experience.

    Studio, can no longer determine input or output type:

    ref only input
    Figure 3. Connector Input with @RefOnly
    ref only output
    Figure 4. Connector Output with Abstract Class

In the next section we get back our DataSense-friendly user experience.

The full source code of our connector with a create, update, get and delete operation is available here.

Implementing a MetaDataCategory

To implement DataSense using a @MetaDataCategory, you need to separate your implementation in two steps, retrieving the keys and describing the keys.

  1. Use the Anypoint DevKit Component wizard to create a new MetaDataCategory

    new metadatacategory
  2. Modify the method annotate with @MetaDataKeyRetriever to retrieve two keys, one for Recipe and another for Ingredient.

    @MetaDataKeyRetriever
    public List<MetaDataKey> getMetaDataKeys() throws Exception {
        List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
    
        // Generate the keys
        keys.add(new DefaultMetaDataKey("id1", "Ingredient"));
        keys.add(new DefaultMetaDataKey("id2", "Recipe"));
    
        return keys;
    }
  3. Modify the method annotate with @MetaDataRetriever, to retrieve the description. Because we use a static model, we can just create a POJO model:

    @MetaDataRetriever
    public MetaData getMetaData(MetaDataKey key) throws Exception {
        DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
        // Since our model is static and we can simply create the pojo model.
        PojoMetaDataBuilder<?> pojoObject = null;
        if ("id1".equals(key.getId())) {
            pojoObject = builder.createPojo(Ingredient.class);
        } else if ("id2".equals(key.getId())) {
            pojoObject = builder.createPojo(Recipe.class);
        } else {
            throw new RuntimeException("Invalid key:" + key.getId());
        }
        MetaDataModel model = pojoObject.build();
        MetaData metaData = new DefaultMetaData(model);
    
        return metaData;
    }

    Check the full source code.

To use this in our connector, modify our @Processors so that a user can pick the entities.

  1. Annotate our @Connector class with the @MetaDataScope annotation. This sets the default MetaDataCategory that’s used every time users choose a @Processor that has a @MetaDataKeyParam.

    @Connector(name = "cookbook", friendlyName = "Cookbook")
    @MetaDataScope(DataSenseResolver.class)
    public class CookbookConnector {
  2. To describe input and output, add a String annotated with @MetaDataKeyParam, and specify that it affects input and output by adding the affects=MetaDataKeyParamAffectsType.BOTH :

    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public CookBookEntity create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly CookBookEntity entity)
            throws InvalidEntityException, SessionExpiredException {
        return config.getClient().create(entity);
    }
  3. In your get operation you need specify that the affect only applies to the output so we modify it just a little:

    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public CookBookEntity get(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.OUTPUT) String type, @Default("1") Integer id) throws InvalidEntityException,
            SessionExpiredException, NoSuchEntityException {
        return config.getClient().get(id);
    }
  4. Check our new connector looks in Studio. We have a combo after we select the entity type and save it. This automatically refreshes our metadata.

    datasense static

    Now even the Transform Message element knows how to interact with our @Connector:

    datasense static2

View connector full source code here.

Dynamic DataSense

In the previous section we covered the scenario when your model is static. Let’s take a look into a much more complex scenario.

There are APIs that provide a way to get entity definitions dynamically. Salesforce, NetSuite are just some examples of these.

In our case, our Cookbook provides an operation that describes our entities, so let’s use that instead to get the entities and structure:

  1. Get the supported entities, and generate a key that we can use later:

    @MetaDataKeyRetriever
    public List<MetaDataKey> getMetaDataKeys() throws Exception {
        List<MetaDataKey> keys = new ArrayList<MetaDataKey>();
        List<CookBookEntity> entities = getConnector().getConfig()
                .getClient().getEntities();
        // Generate the keys
        for (CookBookEntity entity : entities) {
            keys.add(new DefaultMetaDataKey(entity.getClass().getName() + "#"
                    + entity.getId(), entity.getName()));
        }
    
        return keys;
    }
  2. Use a structure that we can dynamically modify, and the way to do this in Mule is by using a Map<String,Object> as parameters and/or return types of our connector.

    Mule provides a builder that helps us generate the MetaData for the entities.

    @MetaDataRetriever
    public MetaData getMetaData(MetaDataKey key) throws Exception {
        DefaultMetaDataBuilder builder = new DefaultMetaDataBuilder();
        // Since our model is static and we can simply create the pojo model.
        String[] keyParts = key.getId().split("#");
        if (keyParts.length != 2) {
            throw new RuntimeException(
                    "Invalid key. Format should be 'entityType#id'");
        }
        Integer id = Integer.valueOf(keyParts[1]);
        CookBookEntity entity = (CookBookEntity) Class.forName(keyParts[0])
                .newInstance();
        entity.setId(id);
        Description description = getConnector().getConfig().getClient()
                .describeEntity(entity);
    
        DynamicObjectBuilder<?> dynamicObject = builder.createDynamicObject(key
                .getId());
    
        for (Description fields : description.getInnerFields()) {
            addFields(fields, dynamicObject);
        }
    
        MetaDataModel model = builder.build();
        MetaData metaData = new DefaultMetaData(model);
    
        return metaData;
    }
  3. Check the full source code.

  4. In our @Connector now we need to add the code to generate the entities from the Map, and return Map in all our operations. Why is this important? To maintain consistency in our API.

    This is how our new Create looks like:

    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().create(input), Map.class);
    }

    Note that now the UI is a different form before, because the input is now a map.

    datasense ui
  5. In our Mule app, we cannot update the metadata and use our connector. And, as you can see, note that we have a map structure instead of a POJO:

    datasense map

To see how to test DataSense check our Testing DataSense guide.

Adding Pagination

Implement Pagination

In order to show this feature you are going to add a processor that will call the searchWithQuery operation of the SDK.

You need to do 3 things in order to have a paginated operation:

    @Processor
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    @Paged (1)
    public ProviderAwarePagingDelegate<Map<String, Object>, CookbookConnector> queryPaginated( (2)
            final String query, final PagingConfiguration pagingConfiguration) (3)
            throws SessionExpiredException {
        return new CookbookPagingDelegate(query, pagingConfiguration.getFetchSize());
    }
1 Annotated your processor with @Paged.
2 Return a ProviderAwarePagingDelegate
3 Receive as one of your parameters a PagingConfiguration

When implementing your ProviderAwarePagingDelegate you need to specify two elements:

  1. The type of the list you will return in each page. In your case a Map<String,Object>

  2. The type of the Connector.

To create it use the Anypoint DevKit Component wizard and:

  1. Specify the package were you want to create it. In this example org.mule.cookbook.pagination

  2. Specify you want to create a ProviderAwarePagingDelegate.

  3. Set the class name as CookbookPagingDelegate

    pagination component

After that you just need to implement the methods required for you to handle the new page request.

You can find the full source code of the CookbookPagingDelegate.

If you have a reconnection strategy, DevKit will automatically reconnect and retry to get a page. It is important that you handle the state of your PagingDelegate to make sure you retry the last page that was not retrieved.

See:

Using Pagination In Your Mule App

You can for example use a foreach in front of your paged processor:

pagination example
<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
    <cookbook:config name="CookBook__Connection_Managament_Config" username="admin" password="admin" doc:name="CookBook: Connection Managament Config">
        <reconnect/>
    </cookbook:config>
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
    <flow name="mule-appFlow1">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/page" doc:name="HTTP"/>
        <cookbook:query-paginated config-ref="CookBook__Connection_Managament_Config" query="GET ALL FROM INGREDIENT" fetchSize="5" doc:name="CookBook"/>
        <foreach doc:name="For Each">
            <logger message="#[payload]" level="INFO" doc:name="Logger"/>
        </foreach>
        <set-payload value="Finished Processing" doc:name="Set Payload"/>
    </flow>
</mule>

Installing a Connector

Using the Anypoint DevKit plugin

Installing the connector is basically the same as installing any Eclipse plugin.

To install your connector from the DevKit plugin:

  1. Right-click your project name in Package Explorer, and click Anypoint Connector > Install Or Update.

    install1

    This triggers the DevKit Maven build tool, which generates an update site folder.

    The files will be copied under the dropins folder located in the same directory as the AnypointStudio executable, and installed without the need of rebooting your AnypointStudio.

  2. You can now use your connector in a Mule app.

    install2

To uninstall the connectors you can either use the shortcut from the UI or remove the folder from the dropins directory and reboot your AnypointStudio

uninstall

Installing from an UpdateSite

Connectors can be installed manually by selecting the update site generated by DevKit:

  1. Open a command prompt or terminal and change directory to where the pom.xml file is for your project (in the Eclipse workspace).

  2. Run mvn clean package.

  3. Click Help > Install New Software…​.

    install updatesite
  4. Click Add and in the new dialog look for the folder.

  5. Click the UpdateSite file, generated under your project’s target folder.

    install updatesite2

    You can either select the zip file named UpdateSite.zip or the folder update-site.

    install updatesite3

    A popup will open showing the task that is performed.

    install12
  6. Review the installation and update items, and accept the license agreement.

  7. Click Finish and restart Studio for the changes to be recognized.

    JAR files are not signed during this run, so you will see a popup.
    security warning
  8. You can now use your connector in a Mule app.

    install2

Updating a Connector

To update your connector you can repeat the steps made for installing it.

AnypointStudio will detect it is an update and perform the corresponding actions.

Debugging Your Connector

After successfully installing your connector, you can start using your connector in a Mule application. You can add breakpoints in the source code of the connector to debug it. If your connector is a Community connector, the source code ships automatically in the installed connector. If your connector is an Enterprise, you need to manually attach the source code of your JAR.

To correctly debug your code, take into account that the Mule app you are running is using the latest installed version, so if you make changes, and you want to debug the Mule app, you need to re-install the connector.

Debugging your connector when running your test is as simple as debugging any Java class.

You just need to set a breakpoint in your connector code.

Sharing My Connector

Connector, as a component that can be installed in Anypoint Studio, is an Update Site.

Update Sites are used to organize and export features so they can be installed into Eclipse applications.

When the site is built, the included features (along with all plug-ins part of those features) will be exported into an installable form. The exported plug-ins and features will be put into two folders "plug-ins" and "features". Two other files, "content.xml" and "artifacts.xml" will also be generated and contain metadata for the exported files that make installing easier. These files, along with "site.xml", collectively form an Eclipse update site. To make the update site available to others you must make all these files available in a shared directory or web site.

When building a connector, DevKit generates this for you, so that you don’t need to worry about generating it yourself.

You can use the Anypoint DevKit plugin to either install the connector on your current Studio, or export it as an update-site for others to use.

Others

Connector Structure

In order to see how all the components are related, let’s create a Hello World connector:

  1. In Anypoint Studio, click File > New > Anypoint Connector Project or right-click your project name in Package Explorer and click New > Anypoint Connector Project:

    new connector 1
  2. Select the Connector Type you want to create. In this case, select SDK Based:

    new0
  3. Specify the name of your connector and click Finish:

    new connector 2

    This generates a project containing the structure and all required elements including a skeleton connector, images, sample docs, and basic tests for your connector.

    new connector 3
  4. Enable the DevKit view by clicking from the top bar Window > Show view > Other, and look for DevKit in the list.

    enable view

Your connector initially consists of message processors and user interface elements. Users can configure the UI elements in Anypoint Studio.

The DevKit makes it easy to install a connector in Studio. After you install it in Studio, users can search for your connector and drag it into a Mule flow.

Installing only requires right-clicking the name of the connector in Studio’s Package Explorer, and clicking Anypoint Connector > Install or Update, completing the prompts, and restarting Studio, as you can see at the install section. You can install at any time during coding. You can even install the starting skeleton connector.

Let’s check the structure of the skeleton connector.

In this image you can see how most of the code maps into UI elements.

Image1
Figure 1: View Structure and UI

In this example, you can check how the code matches to XML and other UI elements.

Image2
Figure 2: View configuration and XML

@Config annotation is deprecated. Users instead should use @Config. On this views, consider the Config as the ConnectorConfig

Configurable and Parameter Modifiers

@Optional Parameters

Optional parameters or configurable ones are elements that are not required, and therefore, there is no need for users to specify a value.

You can specify configurable fields inside you Connector Configuration classes, or as parameters of @Processor methods inside a @Connector.

import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Optional;

//Inside the Configuration class

/**
 * Optional Field documentation
 */
@Configurable
@Optional
private String  optionalField;
//Getter and Setter of the field are required

//Inside your @Connector

/**
 * Optional parameter
 */
@Processor
public String sayHi(String firstName,@Optional String lastName ) {
    return "Hi "+firstName+" "+((lastName==null) ? "":lastName);
}

@Default

When you want an optional parameter or configurable you can avoid the use of @Optional and just use the @Default annotation.

import org.mule.api.annotations.Configurable;
import org.mule.api.annotations.Processor;
import org.mule.api.annotations.param.Default;

//Inside the Configuration class

/**
 *  Field with Default
 */
@Configurable
@Default("Hi")
private String  greeting;
//Getter and Setter of the field are required

//Inside your @Connector

/**
 * Default parameter
 */
@Processor
public String sayHi(String firstName,@Default("Unknown") String lastName ) {
    return greeting+" "+firstName+" "+lastName;
}

Another very important use of the @Default annotation is when building a connector that has DataSense.

Adding OAuthV2

The Server can also provide the token using OAuth 2.0, instead of doing a login request.

In order to use OAuth in you connector you just need to annotate your strategy with the @OAuth2 annotation.

If you only had the OAuth2 Configuration your Config would look like:

 1package org.mule.modules.cookbook.config;
 2
 3import org.mule.api.annotations.Configurable;
 4import org.mule.api.annotations.oauth.*;
 5import org.mule.api.annotations.param.Default;
 6
 7import com.cookbook.tutorial.client.MuleCookBookClient;
 8
 9@OAuth2(configElementName = "oauth2", friendlyName = "OAuth 2.0", authorizationUrl = "http://devkit-cookbook.cloudhub.io/rest/oauth/authorize", accessTokenUrl = "http://devkit-cookbook.cloudhub.io/rest/oauth/accessToken")
10public class OAuthConfig {
11
12    private MuleCookBookClient client;
13
14    @OAuthAccessToken
15    private String accessToken;
16
17    @Configurable
18    @OAuthConsumerKey
19    private String consumerKey;
20
21    @Configurable
22    @OAuthConsumerSecret
23    private String consumerSecret;
24
25    /**
26     * URL used to connect to the service
27     */
28    @Configurable
29    @Default("http://devkit-cookbook.cloudhub.io/soap")
30    private String address;
31
32    @OAuthPostAuthorization
33    public void postAuthorize() {
34        setClient(new MuleCookBookClient(getAddress()));
35        getClient().setToken(getAccessToken());
36    }
37
38    public void setAccessToken(String accessToken) {
39        this.accessToken = accessToken;
40    }
41
42    public String getAccessToken() {
43        return this.accessToken;
44    }
45
46    public void setConsumerKey(String consumerKey) {
47        this.consumerKey = consumerKey;
48    }
49
50    public String getConsumerKey() {
51        return this.consumerKey;
52    }
53
54    public void setConsumerSecret(String consumerSecret) {
55        this.consumerSecret = consumerSecret;
56    }
57
58    public String getConsumerSecret() {
59        return this.consumerSecret;
60    }
61
62    public MuleCookBookClient getClient() {
63        return client;
64    }
65
66    public void setClient(MuleCookBookClient client) {
67        this.client = client;
68    }
69
70    public String getAddress() {
71        return address;
72    }
73
74    public void setAddress(String address) {
75        this.address = address;
76    }
77
78}

All our operations now need to be marked with the @OAuthProtected, even our @Source.

In our Connector for example you will see:

    @OAuthProtected
    @Processor
    @OnException(handler = CookbookHandler.class)
    @ReconnectOn(exceptions = { SessionExpiredException.class })
    public Map<String, Object> create(@MetaDataKeyParam(affects = MetaDataKeyParamAffectsType.BOTH) String type, @Default("#[payload]") @RefOnly Map<String, Object> entity)
            throws InvalidEntityException, SessionExpiredException {
        ObjectMapper m = new ObjectMapper();
        CookBookEntity input = null;
        if (type.contains("com.cookbook.tutorial.service.Recipe")) {
            input = m.convertValue(entity, Recipe.class);
        } else if (type.contains("com.cookbook.tutorial.service.Ingredient")) {
            input = m.convertValue(entity, Ingredient.class);
        } else {
            throw new InvalidEntityException("Don't know how to handle type:" + type);
        }
        return m.convertValue(this.getConfig().getClient().create(input), Map.class);
    }

Check full source code.

See:

Multiple Configuration

If you want to have multiple connection configurations there are two things to take into account:

  • All Configurations either need to implement the same interface or have a common parent class.

  • The connector Config field needs to be declared either as the interface or as the common parent class.

If your configuration has common fields, they can be defined at a parent class.

In our connector, after the refactor the classes would look like this:

model

See:

Adding @Source

What is a Source

In some cases it is necessary to create Message Sources instead of Message Processors.

Basically, a Message Source receives or generates new messages to be processed by Mule. One of the use cases for a Message Source is implementing Streaming APIs. The @Source annotation marks a method inside a @Connector annotated class as callable from a Mule flow and capable of generating Mule events. Each marked method will have a Message Source generated. The method must receive a SourceCallback as one of its arguments that represents the next message processor in the chain. It does not matter the order in which this parameter appears as long it is present in the method signature.

Implementing a Message Source

As an example, we are going to use the GetRecentlyAdded method.

  1. Inside your @Connector type source and use Ctrl + Space bar to display the templates.

    source template
  2. Create a @Source that consumes the Get Recently Updated on the Connector using a callback

    @Source(sourceStrategy = SourceStrategy.POLLING, pollingPeriod = 10000)
    public void getRecentlyAddedSource(final SourceCallback callback) throws Exception {
    
        if (this.getConfig().getClient() != null) {
            // Every 10 seconds our callback will be executed
            this.getConfig().getClient().getRecentlyAdded(new ICookbookCallback() {
    
                @Override
                public void execute(List<Recipe> recipes) throws Exception {
                    callback.process(recipes);
                }
            });
    
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
    }
  3. Install this new version

  4. On your flow now just drag and drop the Cookbook Connector, and you will see that is automatically while generate a flow.

    source example
  5. Add a logger and debug to see that the payload now has your recipes.

source debug
<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd">
    <cookbook:config name="CookBook__Connection_Managament_Config" username="admin" password="admin" doc:name="CookBook: Connection Managament Config"/>
    <flow name="source-get-recently-added">
        <cookbook:get-recently-added-source config-ref="CookBook__Connection_Managament_Config" doc:name="CookBook (Streaming)"/>
        <logger message="#[payload]" level="INFO" doc:name="Logger"/>
    </flow>
</mule>

Adding a Transformer

What is a Transformer?

Transformers convert message payloads to formats expected by their destinations. Mule ESB provides many standard transformers, which users can configure using elements and attributes in a Mule XML configuration file.

Sometimes it’s useful to build your own custom transformers.

Annotating a method with @Transformer signals the DevKit to export a method functionality as a transformer. You need to declare Transformers in classes annotated with @Module or @Connector and you can declare more than one transformer in a single class. It is possible to declare transformers, message processors, and message sources all in the same class.

An @Transformer annotated method must:

  • Be static

  • Be public

  • Not return void

  • Not return java.lang.Object

  • Receive exactly one argument

  • Be inside a class annotated with @Connector

Creating a Transformer

In our case, we are creating a transformer that converts a List<Recipe> into a List<Map<String,Object>>, that way we don’t need to modify our existing operation, and yet we can use the output of it in operations that recieve a List<Map<String,Object>>.

@Transformer(sourceTypes = { List.class })
public static List<Map<String, Object>> transformJsonToComments(List<Recipe> list) {
    ObjectMapper mapper = new ObjectMapper();
    List<Map<String, Object>> result = mapper.convertValue(list, new TypeReference<List<Map<String, Object>>>() {
    });
    return result;
}

Using Your Transformer in a Mule App

To use your transformer, just drag and drop it from the palette and build a flow.

  • Here we are using it explicitly to map transform the recipes into a Map.

transformer explicit
<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
    <cookbook:config name="CookBook__Connection_Managament_Config" username="admin" password="admin" doc:name="CookBook: Connection Managament Config">
        <reconnect/>
    </cookbook:config>
    <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>

    <flow name="mule-appFlow">
        <http:listener config-ref="HTTP_Listener_Configuration" path="/transform" doc:name="HTTP"/>
        <cookbook:get-recently-added config-ref="CookBook__Connection_Managament_Config" doc:name="CookBook"/>
        <cookbook:recipes-to-maps doc:name="CookBook"/>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
    </flow>
</mule>
  • Transformers can also be used implicitly as long as only one transformer is found to resolve the type.

    1. Define another transformer that can transform Recipe object into Map<String,Object>

      @Transformer(sourceTypes = { Recipe.class })
      public static Map<String, Object> recipeToMap(Recipe recipe) {
          ObjectMapper mapper = new ObjectMapper();
          Map<String, Object> result = mapper.convertValue(recipe, new TypeReference<Map<String, Object>>() {
          });
          return result;
      }
    2. Install the connector.

    3. In your mule app create a flow that takes the first item of the list and add an update operation after it. Don’t use the transformer.

      transformer implicitly
      <?xml version="1.0" encoding="UTF-8"?>
      
      <mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:cookbook="http://www.mulesoft.org/schema/mule/cookbook" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
            xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.6.1"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
      http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
      http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
      http://www.mulesoft.org/schema/mule/cookbook http://www.mulesoft.org/schema/mule/cookbook/current/mule-cookbook.xsd
      http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
          <cookbook:config name="CookBook__Connection_Managament_Config" username="admin" password="admin" doc:name="CookBook: Connection Managament Config">
              <reconnect/>
          </cookbook:config>
          <http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
      
          <flow name="mule-appFlow">
              <http:listener config-ref="HTTP_Listener_Configuration" path="/transform" doc:name="HTTP"/>
              <cookbook:get-recently-added config-ref="CookBook__Connection_Managament_Config" doc:name="CookBook"/>
              <!-- Get the first item of the list -->
              <set-payload value="#[payload.get(0)]" doc:name="Set Payload"/>
              <!-- This operation is expecting a Map. Not a Recipe, but we are not using the transformer in the flow. The transformer will be called automatically -->
              <cookbook:update config-ref="CookBook__Connection_Managament_Config" type="com.cookbook.tutorial.service.Recipe#0" doc:name="CookBook">
                  <!-- Take the payload as input for the connector -->
                  <cookbook:entity ref="#[payload]"/>
              </cookbook:update>
              <json:object-to-json-transformer doc:name="Object to JSON"/>
          </flow>
      </mule>
    4. Run the example and see that the flow will execute successfully.

Using @Password

When using the @Password in a @Connect parameters, it generates a masked input field in the UI.

    @Connect
    @TestConnectivity
    public void connect(@ConnectionKey String username, @Password String password) throws ConnectionException {

In Studio this translates into:

password
== Using an API to Build a Connector

APIs are exposed in several ways. To start using your API, you should set up a few things before you can use it inside your connector.

SDK Client

If you have an SDK all you need to do is to include the Maven dependency for your jar in the pom.xml

For example in our case, to consume the SDK for the Cookbook we can add the dependency.

<dependencies>
  <dependency>
    <groupId>foo.sdk.group.id</groupId>
    <artifactId>foo.sdk.artifact.id</artifactId>
    <version>${sdk.version}</version>
  </dependency>
</dependencies>

SOAP API

If you have a wsdl, the easiest way to build a connector is to create a client using CXF wsdl2java.

You can configure the CXF goal in your pom.xml file very easily. The full documentation is at the Apache CXF site.

For example, in your pom.xml file, you can add the following plus all required dependencies:

<build>
  <plugins>
    <!-- CXF Code generation -->
    <plugin>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-codegen-plugin</artifactId>
      <version>${cxf.version}</version>
      <executions>
        <execution>
          <phase>clean</phase> <!-- This is required for it to work with DevKit -->
          <goals>
            <goal>wsdl2java</goal>
          </goals>
          <configuration>
            <wsdlOptions>
              <wsdlOption>
                <wsdl>${basedir}/src/main/resources/wsdl/IMuleCookBookService.wsdl</wsdl>
                <autoNameResolution>true</autoNameResolution>
                <extendedSoapHeaders>false</extendedSoapHeaders>
                <extraargs>
                  <extraarg>-xjc-Xbg</extraarg>
                   <extraarg>-xjc-Xcollection-setter-injector</extraarg>
                  <extraarg>-p</extraarg>
                  <extraarg>org.mule.modules.wsdl.api</extraarg>
                </extraargs>
              </wsdlOption>
            </wsdlOptions>
          </configuration>
         </execution>
      </executions>
      <dependencies>
        <!-- Boolean Getters -->
        <dependency>
          <groupId>org.apache.cxf.xjcplugins</groupId>
          <artifactId>cxf-xjc-boolean</artifactId>
          <version>${cxf.version.boolean}</version>
        </dependency>
        <!-- Collection Setters -->
        <dependency>
          <groupId>net.java.dev.vcc.thirdparty</groupId>
          <artifactId>collection-setter-injector</artifactId>
          <version>0.5.0-1</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

If you use the DevKit plugin it generates everything for you to get started, you just need to specify the WSDL location on your computer.

REST API

Write the request using any library that helps you make HTTP Requests.

We recommend using Jersey 2.11, which is provided in Mule version 3.6.0 and later.

To make sure you always use the right version, add the following dependency.

<dependencies>
    <dependency>
        <groupId>org.mule.modules</groupId>
        <artifactId>mule-module-jersey</artifactId>
        <version>${mule.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Example GET request:

ClientConfig clientConfig = new ClientConfig();
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://example.com/rest"); // (1)
WebTarget resourceWebTarget = webTarget.path("resource");
WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld"); // (2)
WebTarget helloworldWebTargetWithQueryParam =
        helloworldWebTarget.queryParam("greeting", "Hi World!"); // (3)

Invocation.Builder invocationBuilder =
        helloworldWebTargetWithQueryParam.request(MediaType.APPLICATION_JSON_TYPE); // (4)

Response response = invocationBuilder.get(); // (5)
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
1 The client is ready to make a request to URL http://example.com/rest
2 Add paths for http://example.com/rest/resource/helloworld
3 Configure a query param. It looks like http://example.com/rest/resource/helloworld?greeting=Hi+World%21
4 Specify we want the reply in JSON format
5 Make a GET request

For more information, see the Jersey client documentation.