JBoss EAP uses modular-based class loading instead of the more familiar hierarchical class loading. Modules require explicit dependencies to be defined on any other modules they depend on. Deployments on JBoss EAP are also modules.
By default, modules are installed in the $JBOSS_HOME/modules directory. You can add external module directories by setting the JBOSS_MODULEPATH environment variable. Paths are separated using the default file system path separator. For Linux, the path separator is a colon; for Windows, the path separator is a semicolon.
Deployment Module Names
Module names for top-level deployments follow the format deployment.example.war, while sub-deployments are named like deployment.example-ear.ear.example.war.
This means that it is possible for a deployment to import classes from another deployment using the other deployment's module name; the details of how to add an explicit module dependency are explained below.
Class Loading Precedence
A common source of errors in Java applications 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. To prevent this in JBoss EAP, module dependencies are added in a specific order that should prevent this situation from occurring.
In order of highest priority to lowest priority:
- System Dependencies: These are dependencies that are added to the module automatically by the container, including the Java EE APIs.
- User Dependencies: These are dependencies that are added through jboss-deployment-structure.xml or through the Dependencies: manifest entry.
- Local Resource: Class files packaged up inside the deployment itself, e.g. class files from WEB-INF/classes or WEB-INF/lib of a WAR.
- Interdeployment dependencies: These are dependencies on other deployments in an EAR deployment. This can include classes in an EAR's lib directory, or classes defined in other EJB JARs.
WAR Class Loading
A WAR is considered to be a single module. Classes in the dependencies defined in the WEB-INF/lib are treated the same as classes in the WEB-INF/classes. All classes packaged in the WAR will be loaded with the same class loader.
EAR Class Loading
EAR deployments are multi-module deployments. This means that not all classes inside an EAR will necessarily have access to all other classes in the EAR, unless explicit dependencies have been defined. By default, the EAR/lib directory is a single module, and every WAR or EJB JAR deployment is a separate module. Sub-deployments (WARs and EJB JARs) always have a dependency on the parent module, which gives them access to classes in EAR/lib; however, they do not always have an automatic dependency on each other. This behavior is controlled via the ear-subdeployments-isolated attribute in the EE subsystem configuration.
By default, this is set to false, which allows the sub-deployments to see classes belonging to other sub-deployments within the EAR.
For example, consider the following EAR deployment:
If the ear-subdeployments-isolated is set to false, then the classes in web.war can access classes belonging to ejb1.jar and ejb2.jar. Similarly, classes from ejb1.jar can access classes from ejb2.jar (and vice-versa).
If the ear-subdeployments-isolated is set to true, then no automatic module dependencies between the sub-deployments are set up. You must manually set up the dependencies with Class-Path entries or set up explicit module dependencies.
|INFO: The ear-subdeployments-isolated element value has no effect on the isolated class loader of the WAR deployments. Whether this attribute is true or false, the WAR within the EAR will have an isolated class loader, and other sub-deployments within the EAR will not have access to the classes in the WAR. This is per the specification.
It is also possible to override the ear-subdeployments-isolated element value at a per-deployment level in the jboss-deployment-structure.xml described below.
Deployments (or, more correctly, modules within a deployment) may set up dependencies on other modules by adding a Dependencies: manifest entry. This entry consists of a comma separated list of module names that the deployment requires. The available modules can be seen under the modules directory in the application server distribution. For example, to add a dependency on Javassist and Apache Velocity, you can add a manifest entry as follows:
Dependencies: org.javassist export,org.apache.velocity export services,org.antlr
Each dependency entry may also specify some of the following parameters by adding them after the module name:
||This means that the dependencies will be exported, so any module that depends on this module will also get access to the dependency.
||By default, items in the META-INF directory of a dependency are not accessible; this parameter makes items from META-INF/services accessible so services in the modules can be loaded.
||If this is specified, the deployment will not fail if the module is not available.
||This will make the contents of the META-INF directory available (unlike services, which just makes META-INF/services available). In general, this will not cause any deployment descriptors in META-INF to be processed, with the exception of beans.xml. If a beans.xml file is present, this module will be scanned by Weld, and any resulting beans will be available to the application.
||If a Jandex index has be created for the module these annotations will be merged into the deployments annotation index. (This is not commonly used.)
It is also possible to add module dependencies on other modules inside the deployment using the Class-Path manifest entry. This can be used within an EAR to set up dependencies between sub-deployments, and also to allow modules access to additional JARs deployed in an EAR that are not sub-deployments and are not in the EAR/lib directory. If a JAR in the EAR/lib directory references a JAR via Class-Path, then this additional JAR is merged into the parent EAR's module, and is accessible to all sub-deployments in the EAR.
JBoss Deployment Structure Descriptor
The jboss-deployment-structure.xml file is a JBoss-specific deployment descriptor that can be used to control class loading in a fine-grained manner. It should be placed in the top-level deployment in META-INF (or WEB-INF for web deployments). It can do the following:
- Prevent automatic dependencies from being added
- Add additional dependencies
- Define additional modules
- Change an EAR deployment's isolated class loading behavior
- Add additional resource roots to a module
<!-- Make sub-deployments isolated by default, so they cannot see each others classes without a Class-Path entry -->
<!-- This corresponds to the top level deployment. For a WAR this is the WAR's module, for an EAR -->
<!-- This is the top level EAR module, which contains all the classes in the EAR's lib folder -->
<!-- exclude-subsystem prevents a subsystems deployment unit processors running on a deployment -->
<subsystem name="resteasy" />
<!-- Exclusions allow you to prevent the server from automatically adding some dependencies -->
<module name="org.javassist" />
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<module name="deployment.javassist.proxy" />
<module name="deployment.myjavassist" />
<!-- These add additional classes to the module. In this case it is the same as including the JAR in the EAR's lib directory -->
<resource-root path="my-library.jar" />
<!-- This corresponds to the module for a web deployment -->
<!-- it can use all the same tags as the <deployment> entry above -->
<module name="deployment.myear.ear.myejbjar.jar" />
<!-- If the same class is both in the sub-deployment and in another sub-deployment that -->
<!-- is visible to the WAR, then the Class from the other deployment will be loaded, -->
<!-- rather than the class actually packaged in the WAR. -->
<!-- This can be used to resolve ClassCastExceptions if the same class is in multiple sub-deployments-->
<local-last value="true" />
<!-- Define two additional modules for the deployment -->
<module name="deployment.myjavassist" >
<resource-root path="javassist.jar" >
<!-- We want to use the servers version of javassist.util.proxy.* so we filter it out-->
<exclude path="javassist/util/proxy" />
<!-- This is a module that re-exports the containers version of javassist.util.proxy -->
<!-- This means that there is only one version of the Proxy classes defined -->
<module name="deployment.javassist.proxy" >
<module name="org.javassist" >
<include path="javassist/util/proxy" />
<exclude path="/**" />
Adding a Custom Module
For standalone servers, you can use the CLI module add command. This command will copy the resources on the local file system into the JBoss EAP modules directory.
[standalone@localhost:9990 /] module add --name=org.postgresql --resources=~/Downloads/postgresql.jar --dependencies=javax.api,javax.transaction.api
By default, the --resources are separated by the file system's default path separator. Module dependencies are separated by a comma. This command will also generate a module.xml file for you and is the easiest way to add a module for standalone servers.
Modules can also be added manually. Note that this is currently the only way to add modules for managed domains. The following steps show how to create a module on Linux-based operating systems. Note the ending main directory is the slot of the module. This is the default slot for modules.
cp ~/Downloads/postgresql.jar $JBOSS_HOME/modules/org/postgresql/main
Open the module.xml file in a text editor of your choice and add the following content.
<?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="org.postgresql">