Contact Us 1-800-596-4880

Class-loading Isolation

Mule runtime engine is developed using Java and many other third-party libraries. Mule apps can also contain Java libraries, particularly modules that extend Mule to provide integration functionality, such as Anypoint Connector for HTTP and the XML module. These extension modules can also contain Java (JAR) libraries.

The default class-loading mechanism of the JVM makes it possible to have conflicting versions of the same JAR files. In Mule, each artifact (the runtime, the Mule apps, and Mule extensions) is developed and released independently, but the JARs for these artifacts are together in the same location. For example, a Mule app might use Apache Commons Collections 2.1. If an extension you use requires Apache Commons Collections 3.1, the app might not work as expected because Commons Collections 2.1 and Commons Collections 3.1 have conflicting resources.

  • Mule 3 Solution

    To solve the problem of clashes between different apps, Mule 3 relied on hierarchical organization of class loaders. Though Mule apps can see their extensions, the libraries bundled within them, and their runtime libraries, Mule apps cannot access other app libraries.

    Other potential conflicts remained in Mule 3, such as clashes between libraries from different extensions, clashes between extension and app libraries, and clashes between app and runtime libraries.

  • Mule 4 Solution

    In Mule 4, the class-loading mechanism addresses all the problems depicted above for Mule 3.

API Definition

Mule runtime engine and the artifacts (apps, domains, policies, and extensions) need to have a clear API definition that specifies a contract for each artifact and limits the exposure of the artifact content to other artifacts.

A clear API for each artifact provides a way to limit the scope of classes and libraries, which in turn makes it possible to create class loaders that do not share everything.

Mule 4 API

Mule 3 exposes all the classes that are bundled within its libraries. This exposure caused problems when upgrading from one Mule version to another because changes in the runtime could potentially affect any of your custom Java classes.

Mule 4 provides a well-defined API that makes it easier for you to extend Mule and provides clarity on the proper extension points:

  • Core API: Mule message.

  • Extensions API: Modules, message processors, transforms, and other elements to extend Mule Runtime 4.

  • Tooling API: All DataSense metadata and propagation is now part of Mule Runtime 4. This API is bundled with the Runtime Manager agent.

Artifact API

When you create an artifact (app, domain, policy, or extension), you need to declare the public API of that module.

How To Define the Public API

See Export Resources to export a resource from an artifact.

Considerations

  • Resources and classes that are not exported are not visible from other artifacts.

  • Expose the minimum required set of packages and resources from your artifact.

  • Do not export third-party libraries unless required.

  • Never expose common third-party libraries like Guava, Apache common-collections, and so on. Doing so will cause clashes with other artifacts.

  • Ideally, expose only classes from your artifact (or the JDK).

Semver

Mule 4 follows Semantic Versioning 2.0.0 for all the provided APIs.

API restrictions

All public APIs might have restrictions. These restrictions limit how you can use the API. See API annotations module for more details.

You need to take into account the API restrictions defined for the API you are consuming, as they ensure greater flexibility among API changes that can occur between different versions.

Class-loading Isolation Mechanism

Once APIs are clearly defined, you can prevent access to internal classes of the artifact and only make the public API accessible from outside. To protect the APIs, Mule 4 uses a custom class-loading mechanism.

Consider the following extension file structure:

com/example/extension/api/MyClass.class
com/example/extension/internal/Util.class
transformations/customer-to-user.dwl
license.txt
META-INF/mule-artifact/mule-artifact.json

And the following mule-artifact.json descriptor file for the extension:

{
    "name": "my-test-extension",
    "minMuleVersion": "4.0.0",
    "classLoaderModelLoaderDescriptor": {
        "id": "mule",
        "attributes": {
            "exportedResources": [
                "transformations/customer-to-user.dwl"
            ],
            "exportedPackages": [
                "com/example/extension/api"
            ]
        }
    }
}

The following diagram shows how resources exported by mule-artifact.json are applied within an app:

Simple class-loading diagram
Artifact Class Loader

A regular Java class loader pointing to the JAR files included in the extension. This class loader loads all files and classes of the extension.

Artifact Filtering Class Loader

A wrapper created over the Artifact class loader, which enforces access restrictions to the extension code for foreign artifacts (the app or other plugins). It uses the content of the mule-artifact.json descriptor to determine what is public.

Extension Code

The Mule extension code. Uses the Artifact class loader (which does not have any restriction), and it is only able to locate resources of the plugin itself.

Application Code

The Mule app code. It uses the Artifact Filtering class loader of the extension to prevent the app from accessing restricted code or resources.

Adding Dependencies to Connectors

When the connectors in your application need access to a dependency in your project, configure the dependency to be visible by the connector’s class loader. There are two different ways to do this:

Shared Libraries

All dependencies (JAR files, for example) declared in the application’s pom.xml file are visible to the application’s class loader but not visible to the class loader of each connector used in the application. The Mule runtime engine class-loading mechanism isolates each connector to prevent it from accessing classes from other connectors. If a connector’s class loader needs access to an application dependency, declare this dependency as a shared library.

Consider an application that uses Anypoint Connector for Java, and the connector needs to use a class that is part of a JAR dependency declared in the application’s pom.xml file. However, this is not possible, because the connector’s class loader is not able to find that class. To make this class visible to the connector, you must declare the dependency that contains the class as a shared library in the Mule Maven plugin configuration of your application’s pom.xml file.

If you use Anypoint Studio to configure a connector that uses external libraries, the dependencies are automatically added as shared libraries. For example, if you add Anypoint Connector for Database to your application and then configure the connection driver using Anypoint Studio, the driver is automatically added as a shared library in your project’s pom.xml file.

See Configure Shared Libraries for configuration instructions.

Shared libraries are visible to all connectors in the application. Configuring shared libraries can cause issues if the shared library has different versions of the same classes in other connectors or their dependencies.

Additional Plugin Dependencies

Instead of configuring shared libraries to make application dependencies visible to all connectors in the application, you can configure additional plugin dependencies to enrich a specific connector’s classpath. This configuration enables you to effectively add new dependencies to a connector, as if the dependencies were declared in the connector’s pom.xml file.

Configuring additional plugin dependencies ensures that the dependencies are available only for the configured connector, and you don’t need to declare them as application dependencies.

When you configure additional plugin dependencies, you must specify the dependency version.

See Configure Plugin Dependencies for configuration instructions.