What we have learnt so far can be enough for configuring many kinds of applications. If you are using a complex archive configuration, such as an EAR archive with several modules and dependencies, it would prove useful to define your classloading strategy in a single file.
The configuration file jboss-deployment-structure.xml can do exactly this. The advantages of using this file are many:
- You can define the dependencies of all application modules in a single file
- You can load the modules classes using a finer grained manner by including/excluding all or part of modules
- You can define classloading isolation policy for your applications packaged in an Enterprise archive
Let’s see with some practical examples what jboss-deployment-structure.xml can do for you.
Setting up a single module dependency
We have already learnt how to activate a log4j dependency, using the Dependencies attribute in the archive’s MANIFEST file. The same effect can be achieved by using the jboss-deployment-structure.xml file. Let’s recap the archive structure, which is basically made up of a web application named WebApp.war.
As you can see, the file jboss-deployment-structure.xml needs to be placed within the META-INF folder of the EAR.
Here’s its content:
The file jboss-deployment-structure is not for exclusive use of EARs; as a matter of fact, you could also deploy it ithin the WebApp application by placing it within the WEB-INF folder of the archive. It is, however, applicable only as top-level archive. Thus, if a jboss-deployment-structure.xml is placed in the WAR’s WEB-INF folder and the WAR is packaged in a EAR archive, then the jboss-deployment-structure.xml is ignored.
The relevant part of this file is the sub-deployment element, which references the web application, including within it the dependencies element. The expected outcome is that the application server will trigger the dependency to log4j Api, which will therefore be visible by our web application.
Excluding the server automatic dependencies
Subscribe to our youtube channel to get new updates..!
Earlier in this tutorial, we have shown how the application server is able to trigger some dependencies, automatically, when some conditions are met. For example, if you deploy a JSF application (containing the faces-config.xml file), then the JSF 2.1 Api implementation is automatically added.
This might not always be the desired option, for example, because you want to provide another release implementation for that module. You can easily achieve this using the exclusion section in the jboss-deployment-structure.xml, as shown here:
Notice in the dependencies section we have added our alternate JSF 1.2 implementation, which will be used by your application. Actually, this JSF implementation ships with the application server distribution along with the javax.faces.api module path, under the folder specified by the slot attribute. In our case, this corresponds to the JBOSS_HOME/modules/javax/faces/api/1.2 folder.
Supposing you have an application that is made up of a web application, an EJB module, and a JAR file containing utility classes. All sub-deployments are placed at the root of the archive, so that they will be able to see each other. This can be convenient, however, supposing that your web application itself contains some implementations of the same EJB. That’s absolutely possible since Java EE 6 specification allows your web application to include EJB classes within the WEB-INF/classes or WEB-INF/lib folder.
How does the classloader solves this conflict? The application server classloader has a priority list when loading classes that are used to avoid any conflict between loaded classes.
- The maximum priority is given to modules, automatically, by the container, including the Java EE APIs. Libraries that are contained in the modules folder are included in this category.
- Then, libraries that are indicated by the user within the MANIFEST.MF of the packaged archive as Dependencies (or in the jboss-deployment-structure.xml file).
- Next, libraries that are packed within the application itself, such as classes contained in WEB-INF/lib or WEB-INF/classes.
- Finally, libraries that are packed within the same EAR archive (in the EAR’s lib folder).
So, in this example, the EJB libraries located in the WEB-INF folder will “hide” the implementations of EJB.jar top-level deployment. Whether or not this is the desired action from the container, you can still override it:
In this example, we have added a dependency to the EJB.jar, which is placed at the root of the archive and which will override the implementation packed within the web application.
Notice the ear-subdeployments-isolated element placed at the top of the file. By setting the EAR isolation level, you will be able to indicate if the sub-deployments modules are visible to each other.
The default value for this attribute is false, meaning that the sub-deployment modules will be able to see each other. If you are setting isolation to true, each module will then be picked up by a different classloader, so, in our example, the web application will not be able to find the classes contained in the EJB.jar and Utility.jar library.
If you want to keep deployment isolated, but allow visibility for some of them, then you have two choices available:
- Move the library to the EAR/lib folder, so that it will be picked up as a separate module
- Specify a dependency using Dependencies or using Class-Path in the MANIFEST.MF file of the calling application
From the following image, you can see how you could correctly set up your EAR, by placing common libraries in the lib folder, and adding a dependency to the EJB classes:
And this is the corresponding configuration required in jboss-deployment-structure.xml:
Using Class-Path declaration to solve dependencies
Until now, we have solved dependencies between modules using JBoss’ way, which we obviously suggest as first choice. Nevertheless, we should also account for Java’s portable way to reference one or more library included in the EAR file.
This can be done using the Class-Path attribute in the MANIFEST.MF file of a module, which needs to reference another library that could not be otherwise visible to the application (think back to the earlier example of a deployment unit with isolation set to true).
For example, supposing you needed to reference the Utility.jar application from within your web application, then simply add to your META-INF/MANIFEST.MF the following:
Manifest-Version: 1.0 Class-Path: Utility.jar
You can actually include more than one library to the Class-Path, keeping them separated by a comma, much the same way you did with the JBoss’ Dependencies attribute.
Choosing between Class-Path approach and JBoss’ Dependencies approach is a matter of how your application is structured: using JBoss’ Dependencies buys you a richer set of options, in particular the ability to export the Dependencies to other deployments, as we have shown earlier. One more point in favor of the JBoss’ Dependencies approach is the ability to reference modules that are not actually packaged within the application; for example, we have seen how to add a dependency to log4j API that are part of the server distribution.
On the other hand, the main advantage of the Class-Path approach relies on application portability. Thus, if a full-portable solution is a priority for you, you could consider switching to the Class-Path manifest attribute.