JBoss AS 7 classloading explained
Recommended by 0 users
JBoss AS 7 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 (Java EE 5 specifications, section EE.8.4). 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, how can I use two different versions of the same utility library, simultaneously, within the same instance of the application server?
The JBoss AS classloading strategy has changed sensibly through the years: basically, the 4.x releases of the application server used a UnifiedClassLoader , which aimed to reduce 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 re-deploy A when B gets re-deployed, 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 classloader was based on the new Virtual File System (VFS). The VFS was implemented to simplify and unify file handling within the application server. The new classloader, named the VFS classloader, 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.
Classloading in AS 7 marks a radical departure from previous attempts. Classloading 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 the following 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 following name:
Finding the isolation level
In before tutorials, Configuring the Application Server, we have intentionally deployed an application that used log4j Api, adding the log4j library into the WEB-INF/lib folder. The application was deployed without a hitch, leaving the question: why do we need to add libraries that are already included as a module in the application server (in our case, in the modules\org\apache\log4j\main path)?
The general rule is that on JBoss AS 7, every deployed application module is isolated from other modules; this means that, by default, it does not have visibility on the AS modules, nor do 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 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 the 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 that 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 to specify 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 an 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 in 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 match 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 to perform 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 provide 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 actually be 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.
Setting up global modules
This option resembles a bit of the old AS approach for loading common libraries, where you used to place them in the folder JBOSS_HOME/common/lib.
If you define a section named global-modules within your standalone.xml/domain.xml, then you will make the module accessible to other AS modules. For example, instead of declaring a dependency on log4j, you could alternatively use the following section:
<module name=”org.apache.log4j” />
Although this approach is not generally recommended, as it brings us back to a concept of monolithic application server, it can still yield some benefits. For example, if you are migrating some older applications, and you don’t want or simply cannot specify dependencies into the archive.