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
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:
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:
- 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:
-
Configure application dependencies as shared libraries.
-
Configure additional plugin dependencies for the connector.
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.