Maven’s versions Plugin Updates All Your Dependencies with a Single Command

Table of Contents

Intro

It’s a good habit to use the latest versions of the dependencies you use. Not only because the updated version might contain new features or better performance (one can hope), but also because bugs are fixed and security issues are tackled. If you go about it by hand, however, updating dependencies is a boring and tedious task. Luckily for us, there is the Maven versions plugin to help us.

Check Which Dependencies/Plugins/Properties Need Updating

If you need a report on which dependencies, plugins or properties used for versioning can be upgraded, run one of these:

versions:display-dependency-updates scans a project’s dependencies and produces a report of those dependencies which have newer versions available.

versions:display-plugin-updates scans a project’s plugins and produces a report of those plugins which have newer versions available, taking care of Maven version prerequisites.

versions:display-property-updates scans a project and produces a report of those properties which are used to control artifact versions and which properties have newer versions available.

Update Parent

If you have a parent section, the following command updates the version to the latest available:

$ mvn versions:update-parent

Update To Latest Versions

To upgrade all your dependencies to the latest versions, use:

$ mvn versions:use-latest-versions

Update a Particular Property with Bounds

To upgrade dependencies that get their version from the <properties> section of your POM to version that has bounds:

$ mvn versions:update-property -Dproperty='cucumber.version' -DnewVersion='(,4.99]'

In this case, the lower bound is unspecified (and exclusive as inidicated by the () and the upper bound in 4.99 inclusive. You can tweak -DnewVersion to your liking.

Conclusion

The Maven versions plugin makes it easy for us the see which dependencies are outdated and can even update the dependencies for us with a single command. After updating, don’t forget to run your test suite and run the application to make sure nothing broke!

Reference

This has been tested on MacOS with the following Maven and versions versions:

$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/local/Cellar/maven/3.6.3_1/libexec
Java version: 13.0.2, vendor: N/A, runtime: /usr/local/Cellar/openjdk/13.0.2+8_2/libexec/openjdk.jdk/Contents/Home
Default locale: en_NL, platform encoding: UTF-8
OS name: "mac os x", version: "10.15.4", arch: "x86_64", family: "mac"
$ mvn help:describe -Dplugin=versions -Dminimal | grep '^Version: '
Version: 2.7

The documentation can be found here.

Skipping or Selectively Running Tests with Maven

Table of Contents

Skip Unit or Integration Tests

Skip the execution of unit tests:

$ mvn compile -DskipTests

This will still compile the unit tests, but not run them. To skip both compilation and execution, use -Dmaven.test.skip. Note that this setting is also honored by the Failsafe plugin.

Skip executing integration tests:

$ mvn verify -DskipITs

Execute a Single Unit or Integration Test Class

Execute a single unit test class with Surefire:

$ mvn test -Dtest=MyUnitTest

In a multi-module project, we need to add -DfailIfNoTests=false, otherwise a module will fail where a class with that name is not available.

Also note that we do not execute integration tests because we instruct Maven to stop after the test phase. To run a single unit test class and skip all integration tests, add -DskipITs.

Execute a single integration test class with Failsafe:

$ mvn verify -Dit.test=MyIntegrationTest

As you can guess by now, this will run the single integration test by the name of MyIntegrationTest, and will also run all unit tests. To prevent this, you can add -DskipTests. Again, don’t forget to set -DfailIfNoTests if you run Maven on a multi-module project.

Execute a Single Unit or Integration Test Within a Test Class

Both the Surefire and Failsafe Maven plugins allow you to go crazy in their syntax for filtering which individual test classes or methods should be run. Specifying the parameters -Dtest and -Dit.test will override all defined include all defined includes and excludes from your pom.xml. Maven will create an include that looks like **/${test}.java.

We have seen how we can run single test classes by specifying the class name after -Dtest or -Dit.test. The next most simple format (since version 2.7.3) takes the form of ClassName#methodName, so if you have a test class named OrderTest.java and a unit test method testEmptyOrderShouldThrow, you can run this test method by itself, excluding all other test by typing:

$ mvn test -Dtest=OrderTest#testEmptyOrderShouldThrow

Complexer syntax with both glob and regex patterns is possible since version 2.19. The documentation gives this example:

$ mvn test '-Dtest=???Test, !Unstable*, pkg/**/Ci*leTest.java,
> *Test#test*One+testTwo?????, #fast*+slowTest'

Let’s break this down. As you can see, we specify multiple patterns in a single parameter (to prevent shell expansion, they are put inside single quotes):

  • ???Test will run any test that has a name that starts with any three characters and ends with Test (? is a glob character meaning any single character).
  • !Unstable* will not run any test that starts with Unstable (the initial ! negates the pattern).
  • pkg/**/Ci*leTest.java will run any test that has pkg somewhere in its package name, followed by any number of other child package names, while the class name should start with Ci followed by zero or more wildcard characters and ending with leTest.java (pkg/CileTest.java matches, as does foo/bar/pkg/baz/CiqwertyleTest.java).
  • *Test#test*One+testTwo????? will run any test whose class name ends in Test and whose method name matches test + any amount of characters + One or testTwo + any five characters. So this will run two test methods in every test class called *Test.
  • #fast*+slowTest will run the methods fast* and slowTest where ever there are found.

We can even have more fun by throwing in regular expressions:

$ mvn test '-Dtest=Basic*, !%regex[.*.Unstable.*],
> !%regex[.*.MyTest.class#one.*|two.*], %regex[#fast.*|slow.*]'
  • !%regex[.*.Unstable.*] will not run any tests that contain Unstable somewhere in their path.
  • !%regex[.*.MyTest.class#one.*|two.*] will run methods named one and two in a class called MyTest. We see that when using globs, we can use forward slashes to delimit packages. However, when using regex, we use dots.
  • %regex[#fast.*|slow.*] will run the test methods fast and slow wherever they are found. Note that (at least in version 3.0.0-M4), the trailing .* wildcard pattern is not needed.

Further Reading

You can read more about Surefire and Failsafe from the comfort of your terminal:

$ mvn surefire:help -Ddetail
$ mvn failsafe:help -Ddetail

You can also read the Surefire and Failsafe online documentation:

tomcat7-maven-plugin: Invalid byte tag in constant pool: 19

I use tomcat7-maven-plugin to spin up a Tomcat 7 container where I can run my web application. When I added dependencies for log4j2 (version 2.11.0) to my project, I got the error:

org.apache.tomcat.util.bcel.classfile.ClassFormatException:
Invalid byte tag in constant pool: 19

Apparently, log4j2 is a multi-release jar and older versions of Tomcat can’t handle that. So I needed to upgrade my Tomcat maven plugin.

Solution: Update your Tomcat

But how do you update your Tomcat? Its information page shows that it hasn’t been updated for a while, the latest version being 2.2 which runs 7.0.47 by default. Maven Central, on the other hand, shows that the latest version at the moment of this writing is 7.0.86. That’s the version we want.

Change your pom.xml in the following way:

<project>
  <properties>
    <tomcat.version>7.0.86</tomcat.version>
  </properties>
  <plugins>
    <plugin>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <path>/</path>
          <port>7777</port>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-util</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-coyote</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-api</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-dbcp</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jsp-api</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jasper</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jasper-el</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-el-api</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-tribes</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina-ha</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-annotations-api</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-juli</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-logging-juli</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-logging-log4j</artifactId>
            <version>${tomcat.version}</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugin>
  </plugins>
</project>

Using Groovy’s AntBuilder to zip and unzip files

Suppose we need to zip a bunch of Python files (without their compiled counterparts *.pyc). The next (admittedly contrived) example shows how we could go about doing something like this in Groovy:

File srcDir = new File('/home/neftas/Downloads/scripts/')
File destDir = new File(srcDir, 'antexample')
File zippedFile = new File(srcDir, 'antzipped.zip')

def allPythonFilenames = srcDir.list().sort().findAll { it.endsWith('.py') }
assert ['a.py', 'b.py', 'c.py'] == allPythonFilenames

def ant = new AntBuilder()

ant.with {
    echo 'begin zipping and unzipping'
    zip(destfile: zippedFile, basedir: srcDir, includes: '*.py')
    mkdir(dir: destDir)
    unzip(src: zippedFile, dest: destDir)
    echo 'done zipping and unzipping'
}

// zip file is created
assert zippedFile.exists()
// contents of zip file should match content of source directory
def commands = ['bash', '-c', "zipinfo -1 ${zippedFile.name}"]
def process = commands.execute(null, srcDir)
def contentsOfZip = process.text.split(System.lineSeparator)
assert contentsOfZip.sort() == allPythonFilenames

// all files should be unpacked from the zip into the right directory
assert destDir.list().sort() == allPythonFilenames

ant.with {
    echo 'deleting created files'
    // notice nested Ant task
    delete(includeEmptyDirs: true) {
        fileset(dir: destDir)
        fileset(file: zippedFile)
    }
    echo 'deleted created files'
}

We can use all the power of Ant in our Groovy scripts. Ant task names map to Groovy methods (see the zip method above) and the attributes are passed as maps to these methods. What’s even better, there is no need to stringify the arguments of these attributes (as is required in Ant’s XML), but we can directly use the correct datatypes (e.g. in the key-value pair destfile: zippedFile, zippedFile is of type java.util.File). All attributes of Ant tasks are available to use with AntBuilder (see Ant manual for zip task).

To use nested Ant tasks, we create closures, so that Ant’s:

<delete includeEmptyDirs="true">
    <fileset dir="lib">
    <fileset file="file.ext">
</delete>

results in the following Groovy code:

ant.delete(includeEmptyDirs: true) {
    fileset(dir: 'lib')
    fileset(file: 'file.ext')
}

Invoking from Gradle

AntBuilder is also available in Gradle. This will come in handy when you don’t know the "Gradle way" of doing something, but do know how to tackle it in Ant.

Dependencies

Groovy (and Gradle) come with a bundled version of AntBuilder, so you should be good shape when creating a Groovy script, but if you want to use AntBuilder in a project, you should add ant and ant-launcher to your build.gradle:

dependencies {
    compile group: 'ant', name: 'ant',  version: '1.7.0'
    compile group: 'ant', name: 'ant-launcher',  version: '1.6.5'
}

Adding a file to the root of your Gradle distribution

A while ago, I needed to write a program that retrieved some information from the database by using SQL queries located in files. I wanted to add a file that would contain the settings of the app, like the port, SID, login and password for the database, but also the location of those queries. The problem was I couldn’t add those settings to a file in the standard src/groovy/resources folder, because when creating a distribution with Gradle ./gradlew distZip the file would be part of the jar, and thus it would be difficult to modify the contents. I was basically after a run-control file, a settings file in the root of my distribution, that I could easily change on each run (if necessary).

Gradle provides several ways to accomplish this:

  1. Add the file to src/dist and the file will be put in the root of the distribution after invoking ./gradlew installDist. Any folders you create will also be put in the distribution, so you can create src/dist/config, for example, to put your configuration in, and a folder config will be created in the distribution.
  2. Indicate to Gradle that you want a certain file or folder copied:

    applicationDistribution.from('config.properties') {
        into ''
    }
    
  3. Use the distribution plugin:

    apply plugin: 'distribution'
    
    distributions {
        main {
            contents {
                from {
                    'settings.groovy'
                }
            }
        }
    }