JBoss EAP / WildFly classloading explained
As mandated by Java EE specifications, an application server should ideally give its deployed applications the freedom to use whatever utility library and whatever version of it, regardless of the presence of concurrent applications that want to use the same library.
This is also known as namespace isolation. However, loading classes from different namespaces can raises some issues which are not easy to solve: for example, what happens if I pack a newer version of an utility library with my application, while an older version of the same library was loaded by the application server? Or also, how can I use two different versions of the same utility library, simultaneously, within the same instance of the application server?
The JBoss/WildFly classloading strategy has changed sensibly through the years: basically, the 4.x releases of the application server used a UnifiedClassLoader, which aimed at reducing communications overhead between running applications, as class data could be shared by reference or simple copies.
One of the major outstanding issues not resolved with the UnifiedClassLoader is classloading dependencies. The idea being that if one application (A) uses the classes of another application (B), the system should know to redeploy A when B gets redeployed, otherwise it will be referencing stale classes. There were actually two different attempts to try to make this work, without the user having to configure anything. Neither attempt really worked and both were dropped.
Since JBoss AS 5.0 was introduced, a new class loader based on the new Virtual File System (VFS). The VFS was implemented to simplify and unify file handling within the application server. The new class loader, named the VFS class loader, uses VFS to locate JAR and class files. Even though this represented a significant change in how classes are loaded in JBoss AS 5.0, the resulting behavior is much the same as for prior versions of JBoss AS.
A common source of errors is including API classes in a deployment that are also provided by the container. This can result in multiple versions of the class being created and the deployment failing to deploy properly.
Class loading in WildFly/JBoss marks a radical departure from previous attempts. Class loading is now based on the JBoss modules project and any application that is deployed is in effect a module. This affirmation raises some questions from a careful reader: which is the module name assigned to a deployed application ? And also, how are dependencies between modules handled by the AS ? Let's answer each question in a separate section
Getting to know module names
Getting to know the module name is not an academic exercise. As a matter of fact, we can establish dependencies between modules, So for many cases, it's needed to know which is the module name assigned to an application.
Therefore, applications that are packaged as top-level archives (such as WAR, JAR, and SAR) are assigned the following module name:
For example, a Web application named WebExample1.war will be deployed as module name:
On the other hand, on applications that contain nested modules (such as the EAR archive), every single archive will be assigned a module name using this classification:
deployment.[ear archive name].[sub deployment archive name]
So, the same Web application, if contained in the archive EnterpriseApp.ear, will be deployed with the name:
Finding the isolation level
The general rule is that every deployed application module is isolated from other modules; this means that, by default, it does not have visibility on the AS modules, nor the AS modules have visibility on the application.
Using the application server modules is, however ,pretty easy and can be summarized in a simple sentence: add a dependency to the required module and the AS will use it. The application server automatically adds dependencies, or they need to be signaled by the user:
- The core module libraries (namely the Enterprise classes) are qualified as implicit dependencies, so they are automatically added to your application, when the deployer detects their usage
- The other module libraries need to be explicitly declared by the user in the application's MANIFEST file or in a custom JBoss's deployment file named jboss-deployment-structure.xml (more about this file in the Advanced deployment strategies section).
Pointing out dependencies for Api that are commonly used by Enterprise application can be tedious. So, as we have anticipated, they are automatically added for you by the application server. Some are added when the application server detects some annotations or configuration files which are typical of that module. For example, adding a beans.xml file triggers the automatic Weld dependency.
The following mind map shows a synthetic view of the modules that are implicitly added to your application:
The meaning of this image is simple: if your application uses any of the core modules indicated, then you don't need specifying any dependency, as the application server is able to link the module automatically.
Modules that are not qualified as implicit dependencies, need to be declared by the user. In our initial example, the log4j library is not mentioned as implicit dependency, so we had to package the log4j JAR along with our application. We can, however, instruct the deployer to use the log4j library, which is bundled in the application server distribution. The simplest and recommended approach to achieve it, is including into the META-INF/MANIFEST.MF the Dependencies: [module] declaration. In our case, to make your application dependent on log4j, just include in your manifest file the following code:
Please note that the module name does not always matches with the package name of the library. The actual module name is specified in the module.xml file by the name attribute of the module element.
Users will typically use the Eclipse (or any other IDE) to update the manifest file, without the need of performing any tedious archive update:
You are not limited to a single dependency, as you can add multiple dependencies separated by a comma. For example, in order to add a dependency on both log4j and Apache Velocity Api, you would use the following:
You can even export the dependencies used by one application module to other applications, by adding the export keyword. For example, in the earlier application, we're now exporting the dependencies to other modules:
Applications that are marked as dependent to the deployment.WebApp.war module will also have access to its dependencies:
The export parameter can also be used to export a dependency to all sub-deployments contained in the ear. Consequently, if you export a dependency from the top-level of the ear (or by a jar in the ear/lib directory) then the dependency will be available to all sub-deployment units as well.
Within the META-INF/MANIFEST.MF, you can also specify additional commands that can modify the server deployer's behavior. For example, the optional attribute can be added to specify that the deployment will not fail, if the module is not found at deployment time.
Finally, when the services keyword is specified, the deployer will try to load services that are placed within the META-INF/services of the archive.
The service Api has become public in Java SE 6. A service can be defined as a set of programming interfaces and classes that provides access to some specific application functionality or feature. A Service Provider Interface (SPI) is the set of public interfaces and abstract classes that a service defines.
You can define a service provider, by implementing the service provider API. Usually, you will create a JAR file to hold your provider. To register your provider, you must create a provider configuration file in the JAR file's META-INF/services directory. When adding the services attribute to your META-INF/MANIFEST.MF, you will be actually able to load the services contained in the META-INF/services directory.
One excellent introduction to the SPI Api is available at: http://java.sun.com/developer/technicalArticles/javase/extensible.
- Next >>