Blog




We started with the idea to add some of our project dependencies as modules with the JBoss AS 7. Soon we found out that we also wanted to provide an extension to the JBoss server. But the method of creating the directory of modules proofs to be quite cumbersome – especially if your libraries have a larger number of direct and transitive dependencies.

Then we were looking for a tool to generate the modules directory for us. We found some Ant scripts provided by JBoss, but these where not overwhelmingly documented – and Ant build scripts was not what we were looking for.

We are using Maven and it seem that some of the module information is already available from the project object model (POM). So we began to create our own tool – a pluging for Maven: smartics-jboss-modules-maven-plugin.

 

For more information about about generating JBoss modules, please refer to smartics-jboss-modules.

The Idea

What is the basic idea? The Maven plugin is usually applied to a BOM or a JBoss extension POM. It traverses the dependency tree and generated a module for each dependency. Since you do not want every dependency as a module, usually because a version of the module is already provided in a slot within the JBoss server or several artifacts should be placed in one module, you may add rules on which modules to skip, how to name modules, how to apply additional information – all dependent on the name of the Maven dependency.

POM or BOM?

 

 A POM defines the object model for a project. That is its coordinates for identification, metadata (description, developers, …), URLs to sources, artifact servers, and other information systems and finally the plugins to build the project’s artifacts and the projects dependencies. A BOM, as bill-of-materials is a POM with the intention of defining a set of dependencies to be imported by client projects. Instead of manually selecting appropriate versions of a set of artifacts that work together, the developers of a framework or library provide such a BOM.

Example with smartics exceptions

Here is an example:  smartics exceptions is a library for Java that helps to implement exceptions with unique identifiers, error codes, message text construction (I18N), exceptions metadata and reporting. This was our first candidate we want to provide as a module.

So here is the essential part of the smartics exceptions BOM:

 <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>de.smartics.exceptions</groupId>
        <artifactId>smart-exceptions-core</artifactId>
        <version>${project.version}</version>
      </dependency>
      <dependency>
        <groupId>de.smartics.exceptions</groupId>
        <artifactId>smart-exceptions-i18n</artifactId>
        <version>${project.version}</version>
      </dependency>
      <dependency>
        <groupId>de.smartics.exceptions</groupId>
        <artifactId>smart-exceptions-report</artifactId>
        <version>${project.version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

So we would like to generate a modules folder with these dependencies and its transitive closure. Therefore we add our plugin to the BOM like this:

 <plugin>
  <groupId>de.smartics.maven.plugin</groupId>
  <artifactId>smartics-jboss-modules-maven-plugin</artifactId>
  <version>0.2.0</version>
  <executions>
    <execution>
      <id>create-modules-archive</id>
      <goals>
        <goal>create-modules-archive</goal>
      </goals>
      <phase>package</phase>
    </execution>
  </executions>
</plugin>

And this modules descriptor to src/etc/jboss-modules (the name of the file is actually irrelevant – all XML files in this folder are expected to be modules descriptors for our plugin):

<modules xmlns="http://smartics.de/ns/jboss-modules-descriptor/1">
  <module name="$g1">
    <match>
      <includes>
        <include>
          <groupId>(de.smartics.exceptions)</groupId>
        </include>
      </includes>
    </match>
  </module>
</modules>

It just states that every dependency matching the given groupId should be added to the module named de.smartics.exceptions (the text inside the groupId is a regular expression with a group – therefore the brackets – and the ${g1} signals that the matched group identifier is also the name of the module). One the plugin’s website you’ll find the XSD for the modules descriptor.

 

It may be confusing that we call our modules descriptors ‘modules descriptors’ since JBoss named their module descriptors also ‘module descriptors’, but the two are not the same. Our modules descriptor should be called ‘jboss module descriptor descriptors’ (JMDD). Or – since we are not JBoss: ‘smartics descriptors for jboss modules’. But this term is quite unwieldly so we hope that context will make the meaning. Where unsure we will use the term ‘JBoss module descriptor’ (singular) to refer to the module.xml defined by JBoss and simply ‘modules descriptor’ (plural) to refer to the descriptors to create the folder with JBoss module descriptors and artifacts.

Run Maven:

mvn package

And we are done! This is the generated JBoss module descriptor of the main artifacts:

<module xmlns="urn:jboss:module:1.1" name="de.smartics.exceptions">
  <resources>
    <resource-root path="smart-exceptions-core-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-i18n-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-report-0.12.3-SNAPSHOT.jar" />
  </resources>
  <dependencies>
    <module name="com.google.code.findbugs.jsr305" />
    <module name="com.google.guava" />
    <module name="com.ibm.icu.icu4j" />
    <module name="com.thoughtworks.qdox" />
    <module name="commons-configuration" />
    <module name="commons-io" />
    <module name="commons-lang" />
    <module name="commons-logging" />
    <module name="de.smartics.messages.smart-messages-core" />
    <module name="de.smartics.properties.smart-properties-core" />
    <module name="de.smartics.util.smartics-commons" />
    <module name="ognl" />
    <module name="org.apache.ant" />
    <module name="org.javassist" />
  </dependencies>
</module>

And this a view on the generated directory:

All transitive dependencies are automatically added.

Nice, but not convincing. The modules of the dependencies are not aligned to the naming scheme found in JBoss. That is because the standard naming scheme uses the Maven group and artifact identifiers to construct the module name.

Finetuning – Mapping Dependencies to Modules

We could add something like this to our modules descriptors file to map the dom4j artifact:

<modules xmlns="http://smartics.de/ns/jboss-modules-descriptor/1">
  <module name="org.dom4j">
    <match>
      <includes>
        <include>
          <artifactId>dom4j</artifactId>
        </include>
      </includes>
    </match>
  </module>
</modules>

Just catch the dom4j dependency by its artifact identifier and map it to the name org.dom4j.

And here a bunch of Apache Commons artifacts:

<modules xmlns="http://smartics.de/ns/jboss-modules-descriptor/1">
  <module name="org.apache.commons.$1">
    <match>
      <includes>
      <include>
        <groupId>commons-.*</groupId>
        <artifactId>commons-(.*)</artifactId>
      </include>
      </includes>
    </match>
  </module>
</modules>

We have shown regular expressions for the matcher already. To refer to the first artifact regular expression group, simply use $1.

We catch some artifacts this way, but this seems to be quite cumbersome. Good news: We have already done this for you! To be honest: For a couple of artifacts, but we will expanding the descriptor set over time (and it is very easy for you to do this for artifacts of your company, too – please have a look at config-smartics-jboss-modules for an example).

So let’s include these standard modules descriptors as dependencies to the plugin to get it fixed:

<plugin>
  <groupId>de.smartics.maven.plugin</groupId>
  <artifactId>smartics-jboss-modules-maven-plugin</artifactId>
  <version>0.2.0</version>
  <executions>
    <execution>
      <id>create-modules-archive</id>
      <goals>
        <goal>create-modules-archive</goal>
      </goals>
      <phase>package</phase>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>de.smartics.config</groupId>
      <artifactId>config-smartics-jboss-modules</artifactId>
      <version>2.0.1</version>
    </dependency>
  </dependencies>
</plugin>

Ah! Much better:

<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="de.smartics.exceptions">
  <resources>
    <resource-root path="smart-exceptions-core-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-i18n-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-report-0.12.3-SNAPSHOT.jar" />
  </resources>
  <dependencies>
    <module name="com.google.code.findbugs.jsr305" />
    <module name="com.google.guava" />
    <module name="com.ibm.icu.icu4j" />
    <module name="com.thoughtworks.qdox" />
    <module name="de.smartics.messages.smart-messages-core" />
    <module name="de.smartics.properties.smart-properties-core" />
    <module name="de.smartics.util.smartics-commons" />
    <module name="org.apache.ant" />
    <module name="org.apache.commons.configuration" />
    <module name="org.apache.commons.io" />
    <module name="org.apache.commons.lang" />
    <module name="org.apache.commons.logging" />
    <module name="org.javassist" />
    <module name="org.ognl" />
  </dependencies>
</module>

Ok, it is just cosmetics, but we adhere to the conventions defined by the JBoss team. Here is the directory with commons-* moved to org.apache.commons.*:

 

Moving to our own Slot

Every module is per default written to the main slot. This is nice but will usually collide with what is already provided by the JBoss server. But we can easily move this whole stuff to our own slot.

Here we go:

<plugin>
  <groupId>de.smartics.maven.plugin</groupId>
  <artifactId>smartics-jboss-modules-maven-plugin</artifactId>
  <version>0.2.0</version>
  <executions>
    <execution>
      <id>create-modules-archive</id>
      <goals>
        <goal>create-modules-archive</goal>
      </goals>
      <phase>package</phase>
      <configuration>
        <defaultSlot>smartics-exceptions0</defaultSlot>
      </configuration>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>de.smartics.config</groupId>
      <artifactId>config-smartics-jboss-modules</artifactId>
      <version>2.0.1</version>
    </dependency>
  </dependencies>
</plugin>

By setting the defaultSlot property of the plugin, we direct the modules to a slot named smartics-exceptions0.

But not all Modules to the default Slot?

In case you want to direct some dependency modules to the main slot? Add a modules descriptor for those files like this:

<modules xmlns="http://smartics.de/ns/jboss-modules-descriptor/1">
  <module
    name="$g1"
    slot="smartics-exceptions-0">
    <match>
      <includes>
        <include>
          <groupId>(de.smartics.exceptions)</groupId>
        </include>
      </includes>
    </match>
  </module>
 
  <module name="org.apache.commons.$1" slot="main">
    <match>
      <includes>
        <include>
          <groupId>commons-.*</groupId>
          <artifactId>commons-(.*)</artifactId>
        </include>
      </includes>
    </match>
  </module>
</modules>

The modules.xml:

 <module xmlns="urn:jboss:module:1.1" 
  name="de.smartics.exceptions" 
  slot="smartics-exceptions0">
  <resources>
    <resource-root path="smart-exceptions-core-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-i18n-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-report-0.12.3-SNAPSHOT.jar" />
  </resources>
  <dependencies>
    <module name="com.google.code.findbugs.jsr305" slot="smartics-exceptions0" />
    <module name="com.google.guava" slot="smartics-exceptions0" />
    <module name="com.ibm.icu.icu4j" slot="smartics-exceptions0" />
    <module name="com.thoughtworks.qdox" slot="smartics-exceptions0" />
    <module name="de.smartics.messages.smart-messages-core" slot="smartics-exceptions0" />
    <module name="de.smartics.properties.smart-properties-core" slot="smartics-exceptions0" />
    <module name="de.smartics.util.smartics-commons" slot="smartics-exceptions0" />
    <module name="org.apache.ant" slot="smartics-exceptions0" />
    <module name="org.apache.commons.configuration" />
    <module name="org.apache.commons.io" />
    <module name="org.apache.commons.lang" />
    <module name="org.apache.commons.logging" />
    <module name="org.javassist" slot="smartics-exceptions0" />
    <module name="org.ognl" slot="smartics-exceptions0" />
  </dependencies>
</module>

The generated modules folder:

Skip Module Generation

One use case for modules in the main slot would be that the main folders are not generated and that modules are used that are already provided by the JBoss server in that slot. Simply add the skip directive to those modules you want to reference as dependencies, but not create a modules folder:

<modules xmlns="http://smartics.de/ns/jboss-modules-descriptor/1">
  <module
    name="$g1">
    <match>
      <includes>
        <include>
          <groupId>(de.smartics.exceptions)</groupId>
        </include>
      </includes>
    </match>
  </module>
 
  <module name="org.apache.commons.$1" slot="main">
    <directives>
      <skip>true</skip>
    </directives>
    <match>
      <includes>
        <include>
          <groupId>commons-.*</groupId>
          <artifactId>commons-(.*)</artifactId>
        </include>
      </includes>
    </match>
  </module>
</modules>

As you can see, the dependencies are still there:

<module xmlns="urn:jboss:module:1.1" 
  name="de.smartics.exceptions" 
  slot="smartics-exceptions0">
  <resources>
    <resource-root path="smart-exceptions-core-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-i18n-0.12.3-SNAPSHOT.jar" />
    <resource-root path="smart-exceptions-report-0.12.3-SNAPSHOT.jar" />
  </resources>
  <dependencies>
    <module name="com.google.code.findbugs.jsr305" slot="smartics-exceptions0" />
    <module name="com.google.guava" slot="smartics-exceptions0" />
    <module name="com.ibm.icu.icu4j" slot="smartics-exceptions0" />
    <module name="com.thoughtworks.qdox" slot="smartics-exceptions0" />
    <module name="de.smartics.messages.smart-messages-core" slot="smartics-exceptions0" />
    <module name="de.smartics.properties.smart-properties-core" slot="smartics-exceptions0" />
    <module name="de.smartics.util.smartics-commons" slot="smartics-exceptions0" />
    <module name="org.apache.ant" slot="smartics-exceptions0" />
    <module name="org.apache.commons.configuration" />
    <module name="org.apache.commons.io" />
    <module name="org.apache.commons.lang" />
    <module name="org.apache.commons.logging" />
    <module name="org.javassist" slot="smartics-exceptions0" />
    <module name="org.ognl" slot="smartics-exceptions0" />
  </dependencies>
</module>

Moving the Modules to JBoss

During development you will probably simply want to add the generated folder to the list of JBOSS_MODULEPATH:

set  "JBOSS_MODULEPATH=/path/to/my/modules;%JBOSS_HOME%\modules"

Per default a JAR file with all the modules is attached. This archive may be moved to the target folder and unzipped. During development you may set the plugin property attach to false to skip the generation of the JAR. You may also want to move your plugin configuration to a profile like this:

<profiles>
  <profile>
    <id>modules</id>
 
    <properties>
      <attach.archive>true</attach.archive>
    </properties>
 
    <build>
      <plugins>
        <plugin>
          <groupId>de.smartics.maven.plugin</groupId>
          <artifactId>smartics-jboss-modules-maven-plugin</artifactId>
          <version>0.2.0-SNAPSHOT</version>
          <executions>
            <execution>
              <id>create-modules-archive</id>
              <goals>
                <goal>create-modules-archive</goal>
              </goals>
              <phase>package</phase>
              <configuration>
                <attach>${attach.archive}</attach>
                <defaultSlot>smartics-exceptions0</defaultSlot>
              </configuration>
            </execution>
          </executions>
          <dependencies>
            <dependency>
              <groupId>de.smartics.config</groupId>
              <artifactId>config-smartics-jboss-modules</artifactId>
              <version>2.0.1</version>
            </dependency>
          </dependencies>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

Conclusion

JBoss modules help to keep the deployables smaller (in our case the exception library with all its dependencies weights more than 15 MB!) by adding the library to the application server’s platform services.

What we have seen is a tool to generate JBoss modules from a Maven POM. For sure no rocket science, but a handy tool to get a project’s artifacts into a JBoss modules folder easily.

In the next article we will show an example for a JBoss extension.

 


 

Dear Robert,

Great idea and work. From what I’ve guessed by the description and compared to alternatives (plugins as well as manual procedures) your plugin seems to be the most mature solution currently available.

Therefore I really would really to use your plugin in own open source projects. As you already mentioned your artefacts are not published to the maven central repository although It seems that you meet the necessary requirements.

I therefore would like to encourage you to do so. This seems to me the only way of expressing reliability to your users.

Kind regards,

Frank.

Frank Seidinger. (February 14, 2014 at 12:36 pm)

Hi Frank,

thank you for your feedback!

We are continuing to consider putting our artifacts to Maven Central. Unfortunately we are extremely time constrained, so this will probably not happen very soon.

Maybe you could explain why providing our artifacts via Maven Central would be more reliable than providing them via our own server? If we would quit the support for the plugin (and we are not considering that in any way), I think users would more appreciate to have access to the sources than to artifacts according to reliability. Are we overlooking facts here?

But we count your vote on our list of supporters for Maven Central publishing.

Kind regards,

Robert

Robert. (February 14, 2014 at 1:01 pm)

Hi Robert. We work with JBoss modules a lot and your solution looks very promising, I will give it a try. But I will also add a vote to the pro-central team: We have a very strict governance. All builds must run on the company jenkins machine and this machine is configured to accept only the companies own artifactory repo. So when we want to use additional 3rd party libs that are not in central, we will have to apply for a new “remote repo” entry in the artifactory. Things are much easier when we just can get them via the already configured repos. My 2-c

Jan

Jan. (May 26, 2014 at 2:40 pm)


Link

Link

Posts