Customize your TomEE instance with javascript and maven


TomEE Maven plugin got recently a new feature allowing to use javascript (or groovy) to customize the TomEE instance you build using tomee:build goal.

Let’s see why and how to use this feature…

Why scripting instead of using all the customization provided by TomEE? They are several reasons for that but the most common will be:

  • you read the configuration from a particular place to match your company/project standards
  • you need to access external data to customize properly TomEE
  • you need a feature which is not yet provided by TomEE
  • you use a custom TomEE layout
  • you need to access some custom logic to handle security (ciphering etc…)
  • probably more since with scripting you have access to the whole JVM features 😉

The central point of the feature is to wire the JSR223 into TomEE Maven plugin. This is done using inline scripts in jsCustomizer(s) tag:

<plugin>
  <groupId>org.apache.tomee.maven</groupId>
  <artifactId>tomee-maven-plugin</artifactId>
  <version>7.0.0-SNAPSHOT</version>
  <configuration>
    <skipCurrentProject>true</skipCurrentProject>
    <jsCustomizers>
      <jsCustomizer>
        <![CDATA[
        // here you script
        ]]>
      </jsCustomizer>
    </jsCustomizers>
  </configuration>
</plugin>

Side note: CDATA allows to not care about special XML characters escaping writing the script.

A Sample

To illustrate it we’ll create a sample adding hibernate to TomEE and removing OpenJPA.

We’ll split out script in three main parts:

  • the configuration (which libraries to add/remove)
  • the utility (how to add/remove a library)
  • the execution (linking both utility and configuration parts)

The configuration

We’ll keep the configuration easy and just store the list of coordinates to add and the list of prefix to remove:

var Config = {
  added: [
    [ 'org.hibernate', 'hibernate-core', '5.0.2.Final' ],
    [ 'org.hibernate.common', 'hibernate-commons-annotations', '5.0.0.Final' ],
    [ 'org.jboss.logging', 'jboss-logging', '3.3.0.Final' ],
    [ 'org.javassist', 'javassist', '3.18.1-GA' ],
    [ 'antlr', 'antlr', '2.7.7' ],
    [ 'org.jboss', 'jandex', '1.2.2.Final' ],
    [ 'dom4j', 'dom4j', '1.6.1' ]
  ],
  removed: [
    'openjpa'
  ]
};

Side note: keeping the configuration at the top of your script is a little tip to ensure it stays easy to update ;).

The utility part

We don’t need much as utility excepted:

  • being able to resolve a dependency with maven from its coordinates
  • being able to delete a file
  • being able to copy a file

Since our script will use Nashorn we can just reuse java logic for the filesystem abstraction but how to handle dependency resolution? TomEE Maven Plugin provides out of the box a resolver binding allowing to use maven to retrieve a dependency. It has several signatures (with type, with classifier etc..) but we only need the simplest one with the groupId, artifactId and version.

var Libs = (function () {
  var File = Java.type('java.io.File');
  var Files = Java.type('java.nio.file.Files');
  var StandardCopyOption = Java.type('java.nio.file.StandardCopyOption');

  return {
    cp: function (coordinates) {
      // resolver.resolve(...coordinates...) and catalinaBase are provided and first one uses maven resolver 🙂
      var resolved = resolver.resolve(coordinates[0], coordinates[1], coordinates[2]);
      Files.copy(resolved.toPath(), new File(catalinaBase, 'lib/' + resolved.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
    },
    rm: function (prefix) {
      var files = new File(catalinaBase, 'lib').listFiles();
      for each (file in files) { // nashorn extension
        if (file.getName().indexOf(prefix) == 0) {
          file.delete();
        }
      }
    }
  };
})();

Note: it is an immediately invoked function to hide java types used internally in the implementation. You can make it simpler if you desire and just move this code in the execution part.

If you know Files and File API there is nothing clever in this code which means it is easy to write and maintain even for a Java developpers. If not you can replace jsCustomizer by groovyCustomizer and add groovy-all as a plugin dependency and you can just use groovy instead of javascript.

Execution

The execution is quite trivial since we just loop over our configurations and call Libs method to execute the tasks:

for each (coordinate in Config.added) {
  Libs.cp(coordinate);
}
for each (prefix in Config.removed) {
  Libs.rm(prefix);
}

Note: this for each syntax is specific to Nashorn.

All together

Just to make copy/paste easy I put there a whole pom.xml creating a TomEE with hibernate:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>demo</groupId>
  <artifactId>demo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomee.maven</groupId>
        <artifactId>tomee-maven-plugin</artifactId>
        <version>7.0.0-SNAPSHOT</version>
        <configuration>
          <skipCurrentProject>true</skipCurrentProject>
          <jsCustomizers>
            <jsCustomizer>
              <![CDATA[
              // put the config at the beginning of the script to update it quickly later if needed
              var Config = {
                added: [
                  [ 'org.hibernate', 'hibernate-core', '5.0.2.Final' ],
                  [ 'org.hibernate.common', 'hibernate-commons-annotations', '5.0.0.Final' ],
                  [ 'org.jboss.logging', 'jboss-logging', '3.3.0.Final' ],
                  [ 'org.javassist', 'javassist', '3.18.1-GA' ],
                  [ 'antlr', 'antlr', '2.7.7' ],
                  [ 'org.jboss', 'jandex', '1.2.2.Final' ],
                  [ 'dom4j', 'dom4j', '1.6.1' ]
                ],
                removed: [
                  'openjpa'
                ]
              };

              // utility function to add/rm a file resolved from maven to tomee/lib
              var Libs = (function () {
                var File = Java.type('java.io.File');
                var Files = Java.type('java.nio.file.Files');
                var StandardCopyOption = Java.type('java.nio.file.StandardCopyOption');

                return {
                  cp: function (coordinates) {
                    // resolver.resolve(...coordinates...) and catalinaBase are provided and first one uses maven resolver 🙂
                    var resolved = resolver.resolve(coordinates[0], coordinates[1], coordinates[2]);
                    Files.copy(resolved.toPath(), new File(catalinaBase, 'lib/' + resolved.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
                  },
                  rm: function (prefix) {
                    var files = new File(catalinaBase, 'lib').listFiles();
                    for each (file in files) { // nashorn extension
                      if (file.getName().indexOf(prefix) == 0) {
                        file.delete();
                      }
                    }
                  }
                };
              })();

              // now just make the Config meet the utility methods (Libs)
              for each (coordinate in Config.added) {
                Libs.cp(coordinate);
              }
              for each (prefix in Config.removed) {
                Libs.rm(prefix);
              }
              ]]>
            </jsCustomizer>
          </jsCustomizers>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Using skipCurrentProject allows to build a custom TomEE without applications.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s