Free MuleSoft CONNECT Keynote & Expo Pass Available!

Register now+
Nav

About Classloading Isolation

The Mule 4 classloading schema isolates the runtime, apps and modules from each other. Each specifies the packages (rather than the classes) to export as part of its API, making those specific packages visible.

Mule 3 classloading issues:

  • Modules were not isolated from each other.

  • Modules were not isolated from the Mule runtime.

  • Modules were not isolated from Mule apps.

In Mule 3, modules that used some library could conflict with other modules, apps, or runtimes that used another version of the same library. This conflict led to classloading problems with confusing error messages like ClassNotFoundException or NoSuchMethodException that were hard to troubleshoot.

Modules Classloading Isolation

Classloading is automatically filtered by package and not by class, meaning that modules need to specify which packages to export and make visible to the Mule app.

To determine what packages to export, the SDK introspects each configuration, connection, and component return and input types to get their packages. So you need to make sure that any classes that you do not want to export are kept out of exported packages. Exported classes should not share a package with classes that you do not want to export.

The suggested package hierarchy (used internally for MuleSoft modules):

org.mule.module
\--> .api.*
\--> .internal.*
  • .api.* contains all the classes that to be exported and seen by the Mule app: Return Types, Subtypes, Input Types, and Connection interfaces.

  • .internal.* contains all the classes used for specific behavior of the module: Annotated classes, Connection and Operation implementations, and internal classes.

Knowing the Exported Packages and Return Types.

MuleSoft recommends that that all exported classes belong to the module.

To avoid library clashes that make a module incompatible with other modules, app dependencies, or even runtimes, it is highly recommended that you:

  • Never return or receive objects from classes that belong to a library that a module is using.

Consider the scenarios in the next sections.

First Scenario: Exporting the Same Packages

This scenario includes two modules with two very different operations. Both use the same library (GSON) to parse JSON and return an instance of a JsonObject class. One of them uses GSON version 1.1, and the other uses GSON version 2.3. The JsonObject class lives in the same package in both versions, but the different implementations of each version make the classes incompatible.

This example exposes the JsonObject class as the return type of the operation:

First Module Operation

           
        
1
2
3
public JsonObject getEmployee(@Connection EmployeesServiceConnection connection, String id) {
  return new JsonObject(connection.getById(id));
}
Second Module Operation

           
        
1
2
3
public JsonObject getAllProducts(@Connection ProductsServiceConnection connection) {
  return new JsonObject(connection.getProdsJson());
}

When you deploy a Mule app and try to use two modules that export the same packages, Mule will fail because two modules cannot export the same package.

Second Scenario: Returning Two Instances of the Same Class in Different Versions

Building on the previous example, assume that the operations do not export the GSON packages. Instead, they return a java.lang.Object.

First Module Operation

           
        
1
2
3
4
public Object getEmployee() {
  // Here JsonObject is from GSON 1.1
  return new JsonObject();
}
Second Module Operation

           
        
1
2
3
4
public Object getAllProducts() {
  // Here JsonObject is from GSON 2.3
  return new JsonObject();
}

If you use both modules in an app, only one of the two versions will be loaded for the app because both classes have exactly the same fully qualified name. So the fully qualified names will clash, and the module that needs the unloaded version of the package will behave differently and be prone to errors such as ClassCastException or NoSuchMethodException.

The @Export annotation

The @Export annotation can be used for extreme corner cases or complex cases in which a module needs to export a class that is not automatically exported by the SDK.

The @Export annotation receives an array of classes to be exported.

You cannot export a class without exporting all the classes in the same package as the exported class.