Back to Blog Archive

Migrating a MuleSoft Connector from Mule 3 to Mule 4

Posted on: July 4, 2019
Author:
Noel

In this article:

  • We will explain (step-by-step) how to migrate a MuleSoft connector from Mule 3 to Mule 4 using the Devkit Migration Tool (DMT).
  • Give an overview of the resulting Mule 4 connector project structure, DataSense and error handling.
  • Share some useful migration tips and resources to help complete the migration of the connector.

Introduction

Mulesoft released the first stable version of Mule 4 (v4.1) in January 2018[1]. Amongst other features, the Mule 4 runtime included Dataweave 2.0[2] which replaced the Mule Expression Language (MEL), a new message structure[3] and a simplified error handling[4] mechanism.

Since then, developers have started migrating Mule 3 connectors to Mule 4. Under Mule 3, connectors were developed using the Mule Development Kit (DevKit), whereas under Mule 4, the Mule SDK is used.

To migrate a connector from Mule 3 to Mule 4, MuleSoft developed the DevKit Migration Tool (DMT)[5], which provides a starting point for the migration process. Once converted, the Mule connector would then require some manual error fixing and modifications.

Migrating to Mule 4 using the DevKit Migration Tool

The following steps will show how to use the DMT to start the migration process.

  • Open the POM file in Anypoint Studio v6.5.x (Mule 3) and replace the parent tag with the following one:
  • Open the terminal, go to the project directory and execute the following command to convert the connector to a Mule 4 one. The converted application will be saved in the Mule 3 Project > target > generated-sources > extension directory.
  • Copy the contents of the extension directory to the desired location. (Note: The tests are not migrated by the DMT.)
  • Open Anypoint Studio v7.3.x (Mule 4) and select File > Import > Anypoint Studio Project from File System​ then click Next. Click on and then from the Finder window locate the copied extension directory and click Finish.

Importing a Mule Application

  • Close Anypoint Studio 7 and open the .project file in the project root directory. Then check that the following configuration is present within the projectDescription tag. Then, reopen the connector project in Anypoint Studio 7.
  • Open the pom.xml file and replace the parent tag with the following one:
  • Open the Problems tab from Window > Show View > Problems to determine the pending issues:
Anypoint Studio - Problems window
  • Additionally, the DMT creates some TODO tasks which can be viewed by selecting Window > Show View > Tasks:

Anypoint Studio - To Do window

Structure of the Mule 4 Connector Project

In the previous step, the DMT tool was used to convert the Mule 3 connector to a Mule 4 one. We shall now describe the project structure of the resulting Mule 4 connector.

The Mule 4 Anypoint Studio Palette

One preliminary thing to note is that under Mule 4, MuleSoft changed the Anypoint studio palette so as to group the operations of a connector under the connector’s name. This allows the Mule application developer to drag operations directly onto the canvas, instead of first dragging the connector onto the canvas and then selecting the required operation. For instance, the image below shows the HTTP connector on the left-hand side and its operations on the right-hand side of the palette.

Anypoint Studio Mule 4 Palette

Class Loading Isolation

In order to understand the project structure generated by the DMT one needs to be aware that Mule 4 uses class loading isolation to isolate the modules, applications and runtime from each other through the “api” and “internal” packages.

The practical result of this is that the following two packages are used in order to define whether the Java classes contained within are externally visible to the users of the component or not.

  • src/main/java/package_name/api
    • Java classes that are externally visible by a Mule application using the connector (e.g. return types)
  • src/main/java/package_name/internal
    • Java classes which define the connector itself (e.g. the Extension and Operation classes – to be described later)

Extensions, Operations and Connection Providers

Having understood the package structure, we can now look at the classes within them. A Mule 4 connector project is mainly based on the following three classes:

  • The Extension class is the main connector class that replaces the Mule 3 Connector class. This class should have a number of annotations. These include the @Extension and @Configurations annotations. The former is used to define the connector and vendor names; while the latter is used to specify the configuration class which in-turn defines the operations Java classes via the @Operations annotation, and the connection providers Java classes via the @ConnectionProviders annotation. The following is a sample extension class (MyConnectorExtension.java) and configuration class (BasicConfiguration.java):
  • One or more Operation classes which define the connector operations (these were previously defined in the Mule 3 *Connector.java class). This class must be defined inside the @Operations annotation of the configuration class and must contain one or more operations (public Java methods). As can be seen from the snippet below (MyConnectorOperations.java), annotations are used to specify the component’s behaviour when this is being used inside a Mule Application. The @DisplayName annotation is used to specify the name which the operation should have in the Mule Palette and flows. The @Summary annotation is used to provide the developer with an overview of what the component does. In addition, the @Placement and @DisplayName annotations are used to specify how the parameters of the operation appear when configuring the connector.

Add Operation Configuration

  • One or more Connection Provider classes, where each one is responsible for managing a connection (e.g. OAuth) by defining the connection configuration section of the component (for instance, the image below shows the configuration section of HTTP Listener). To achieve this, each class must implement the PoolingConnectionProvider<T> class and specify the connection strategy (i.e. T). This class is composed of multiple variables with the @Parameter annotation and also overrides the connect, disconnect, and validate methods. Moreover, the parameters may be annotated using the @DisplayName annotation to specify how these should appear in the Global Element Properties window, and with the @Optional annotation to specify that the value is not required (a default value may be provided instead).
Connection Provider

DataSense

DataSense[6] is used to assist the developer in determining what the input and output types of a particular operation are (this is also referred to as metadata). For instance, below we can see an example of an operation with the input DataSense of type Number and output DataSense of type MyCustomObject.

Input DataSense Output DataSense

The simplest method to implement DataSense for a particular operation is to create a Java class which defines both of the corresponding input and output metadata.

This is achieved by having the class implement the InputTypeResolver and OutputTypeResolver interfaces as in the example below. Note that the getInputMetadata method will return the input metadata, while the getOutputMetadata method will return the output metadata.

Once the above class has been defined, the operation can be annotated with the @TypeResolver(MyDataSenseResolver.class) annotation to reference the input DataSense, and with the @OutputResolver(output = MyDataSenseResolver.class) annotation to reference the output DataSense.

A more complex scenario of where DataSense may be required is when a selector’s choice is dependent on another one — this is referred to as multi-level DataSense[7]. As an example, the image below depicts an operation with two selectors, where the Second Selector values are dependent on the chosen First Selector value.

Multi-level DataSense

In order to handle such cases, a Java class corresponding to a selector group must be created as follows:

The next step is to create a new Java class which implements the TypeKeysResolver interface and defines the selector keys/options. In this case MetadataKeyBuilder is used to create a hierarchy of keys, where the parent keys are the first selector keys, while the child keys are the second selector keys (i.e. dependent on the parent key/first selector selection).

Finally, the following must be added (as a parameter) to the respective operation:

Error Handling

In Mule 4, a developer can execute different error routines depending on the type of error thrown by the respective operation[8].

In order to achieve this, we first define an enum implementing the ErrorTypeDefinition and specify the custom errors thrown by the connector (e.g. CUSTOM_ERROR_1, CUSTOM_ERROR_2, and CUSTOM_ERROR_3) as follows:

Subsequently, each operation must define which of the errors defined above it may throw using the @Throws(CustomErrorProvider.class) annotation.

This annotation references a class that implements ErrorTypeProvider and specifies the respective error types. (Note that each error provider may be used by multiple operations that throw the same set of errors.)

This is followed by adding the @ErrorTypes(CustomConnectorErrorType.class) annotation to the Extension class to define which errors may be thrown by the connector.

Once this is done, the custom exceptions must be modified to extend ModuleException (used to define the error type). Otherwise, encapsulate the exception in a ModuleException as follows:

Further Migration Tips

The following are some additional points that may be found useful in order to resolve any issues encountered during the migration process:

  • For paid versions of the connector, add the following annotations in the Extension class where my-connector refers to the connector name:
  • Customise the Mule application connector schema attributes (sample XML below) by adding the following annotation in the Extension class:

For instance, the above would result in the my-connector namespace prefixing the `add` operation as shown below.

  • Define the connector name (in the Extension class) as it would be displayed in the Mule Palette and the vendor name as follows:
  • Add the following annotation above operations whose return type is InputStream or a String, replacing APPLICATION_JSON with the appropriate media type:
  • If the following error is raised, enclose the respective Java class with a wrapper class containing the respective getter and setter methods. (The wrapper class is placed in src/main/java/api).
  • Use the @Content annotation on an operation parameter to set its value to the current payload.
  • When defining input and output DataSense, use the following to return metadata for simple types.
  • On the other hand, use the following to return metadata for objects.
  • The group attribute @Placement(group="My Group"), which was used to group the elements in the connector configuration section (as in the image below, where the First Group Parameter and Second Group Parameter have been grouped under the group My Group) has been removed in Mule 4.

Operation Group Sample

To achieve the same result, the respective parameters must be placed in a new Java class such as the one below.

Following this, the class can then be referenced through the @ParameterGroup(name = "My Group") MyGroupClass myGroup annotation instead.

  • Existing annotations and imports are replaced as follows.
Replace…​ With…​
@Processor(friendlyName = “…​”) @DisplayName(“…​”)
@Mime(“application/json”) @MediaType(value = MediaType.APPLICATION_JSON, strict = true)
@Configurable
@Disconnect
@Requried (All parameters are required by default)
@Optional @Optional(defaultValue=”…​”)
@Default @Optional(defaultValue=”…​”)
@FriendlyName @DisplayName(“…​”)
org.mule.api.annotations.Config org.mule.runtime.extension.api.annotation.param.Config
org.mule.api.annotations.Connector org.mule.runtime.extension.api.annotation.Extension
org.mule.api.*
org.mule.tools.devkit.*
  • Due to Mule 4 SDK class loading isolation, the following warning may be displayed when the project is built using Maven.

To address this, rename the respective packages from internal to api. As an example, the package org.mule.extension.myconn.internal.selectors becomes org.mule.extension.myconn.api.selectors.

  • The connector icon must be in Scalable Vector Graphics (SVG) format and placed in ./icon/icon.svg.

Conclusion

Once the migration is complete, install the connector to the local repository using mvn clean install and then use it in the desired Mule application by adding the connector’s dependency inside the application’s pom.xml file as shown below:

Once saved, the Mule Palette will be automatically updated to include the respective connector.

Resources

The following are some additional resources which may prove useful for migrating a Mule 3 connector to Mule 4.

Author:
Noel

Comments

Send our experts a message

Need Help?
Ask our Experts!