org.mule.module \--> .api.* \--> .internal.*
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):
-
.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
andOperation
implementations, and internal classes.
Knowing the Exported Packages and Return Types.
MuleSoft recommends 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:
public JsonObject getEmployee(@Connection EmployeesServiceConnection connection, String id) {
return new JsonObject(connection.getById(id));
}
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
.
public Object getEmployee() {
// Here JsonObject is from GSON 1.1
return new JsonObject();
}
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. |