Skip to content

gaul/modernizer-maven-plugin

Repository files navigation

Modernizer Maven Plugin

Maven Central

Modernizer Maven Plugin detects uses of legacy APIs which modern Java versions supersede. These modern APIs are often more performant, safer, and idiomatic than the legacy equivalents. For example, Modernizer can detect uses of:

  • Collections.sort instead of List.sort
  • String.getBytes(String) instead of String.getBytes(Charset)
  • Guava ByteStreams.toByteArray(InputStream) instead of InputStream.readAllBytes().

The default configuration detects over 300 legacy APIs, including third-party libraries like Apache Commons, Guava, and Joda-Time.

Requirements

  • Maven 3.6.3 or newer
  • JDK 8 or newer to run the plugin
  • JDK 17 or newer to build the plugin from source

Configuration

To run Modernizer, add the following to the <plugins> stanza in your pom.xml then invoke mvn modernizer:modernizer:

<plugin>
  <groupId>org.gaul</groupId>
  <artifactId>modernizer-maven-plugin</artifactId>
  <version>3.4.0</version>
  <configuration>
    <javaVersion>${maven.compiler.release}</javaVersion>
  </configuration>
</plugin>

The <configuration> stanza can contain several elements:

  • <javaVersion> target Java version, e.g., 8 or 1.8 — both forms are accepted for any release. Modernizer reports a violation only when its <version> is at or below this value, so targeting Java 1.2 flags Vector but targeting Java 1.1 does not. Required parameter; binding it to ${maven.compiler.release} (or ${maven.compiler.target}) keeps it in sync with the rest of the build.
  • <failOnViolations> fail phase if Modernizer detects any violations. Defaults to true.
  • <includeTestClasses> run Modernizer on test classes. Defaults to true.
  • <violationsFile> user-specified violation file. Also disables standard violation checks. See Custom violations below.
  • <violationsFiles> user-specified violation files. Later files override violations from earlier ones, including <violationsFile> and the default violations.
  • <exclusionsFile> disables user-specified violations. This is a text file with one exclusion per line in the javap format: java/lang/String.getBytes:(Ljava/lang/String;)[B. Empty lines and lines starting with # are ignored.
  • <exclusions> violations to disable. Each exclusion should be in the javap format: java/lang/String.getBytes:(Ljava/lang/String;)[B.
  • <exclusionPatterns> violation patterns to disable, specified using <exclusionPattern> child elements. Each exclusion should be a regular expression that matches the javap format: java/lang/.* of a violation.
  • <ignorePackages> package prefixes to ignore, specified using <ignorePackage> child elements. Specifying foo.bar subsequently ignores foo.bar.*, foo.bar.baz.* and so on.
  • <ignoreClassNamePatterns> full qualified class names (incl. package) to ignore, specified using <ignoreClassNamePattern> child elements. Each exclusion should be a regular expression that matches a package and/or class; the package will be / not . separated (ASM's format).
  • <ignoreGeneratedClasses> classes annotated with an annotation whose retention policy is runtime or class and whose simple name is exactly "Generated" will be ignored. Individual methods and constructors carrying such an annotation are also ignored, e.g., members generated by Lombok when lombok.addLombokGeneratedAnnotation = true. (Note: both javax.annotation.Generated and javax.annotation.processing.Generated have retention policy SOURCE (aka discarded by compiler).)

Invoking mvn modernizer:modernizer runs the goal once. For automatic checks during normal builds, bind it to a lifecycle phase via an <execution>. For example, to run Modernizer during the verify phase, add the following to the modernizer <plugin> stanza in your pom.xml:

<executions>
  <execution>
    <id>modernizer</id>
    <phase>verify</phase>
    <goals>
      <goal>modernizer</goal>
    </goals>
  </execution>
</executions>

Command-line flags can override Modernizer configuration and ModernizerMojo documents all of these. The most commonly used flags:

  • -Dmodernizer.failOnViolations - fail phase if violations detected, defaults to true
  • -Dmodernizer.skip - skip plugin execution, defaults to false

Output Formats

For each violation Modernizer emits one line containing the source location and the recommended replacement. Using the default CONSOLE format, output looks like:

[ERROR] src/main/java/org/example/Foo.java:42: Prefer java.util.ArrayList<>()
[ERROR] src/main/java/org/example/Foo.java:57: Prefer java.nio.charset.StandardCharsets.UTF_8

When <failOnViolations> is true (the default), the build fails after the listing with the total violation count.

The plugin can output Modernizer violations in one of many formats which can be configured with the <configuration> stanza using <outputFormat>.

The currently supported formats and their respective configuration options are outlined below:

  • CONSOLE List each violation using Maven's logger. This is the default format.
    • <violationLogLevel> Specify the log level of the logger: ERROR, WARN, INFO or DEBUG. Default is ERROR.
  • CODE_CLIMATE Write the violations according to Code Climate's Spec. GitLab uses this format for its code quality as shown here.
    • <outputFile> The full path the file to output to. Default is ${project.build.directory}/code-quality.json
    • <codeClimateSeverity> Severity of Modernizer violations for CodeClimate: INFO, MINOR, MAJOR, CRITICAL or BLOCKER. Default is MINOR.

Custom violations

Modernizer reads its rules from an XML file in the same format as the bundled modernizer.xml. Point <violationsFile> at your own file to replace the defaults, or list additional files in <violationsFiles> to layer rules on top (later files override earlier ones). Either path can be prefixed with classpath:/ to load from the classpath.

Each <violation> element accepts:

  • <name> API in javap format: java/util/Vector for a type, or java/lang/String.getBytes:(Ljava/lang/String;)[B for a specific method
  • <version> lowest target Java version at which the API becomes legacy
  • <comment> recommended replacement, shown in violation output
  • <until> (optional) target Java version, exclusive, at which the rule stops applying — useful when a newer replacement supersedes an older one
<?xml version="1.0"?>
<modernizer>
  <violation>
    <name>com/example/legacy/OldThing</name>
    <version>11</version>
    <comment>Use com.example.modern.Thing instead</comment>
  </violation>
</modernizer>

Ignoring elements

Code can suppress violations on a class (or other type declaration), constructor, method, or type use via the @SuppressModernizer annotation. First add the following dependency to your pom.xml:

<dependencies>
  <dependency>
    <groupId>org.gaul</groupId>
    <artifactId>modernizer-maven-annotations</artifactId>
    <version>3.4.0</version>
  </dependency>
</dependencies>

Then annotate the element to ignore:

import org.gaul.modernizer_maven_annotations.SuppressModernizer;

@SuppressModernizer
public class Example {
    @SuppressModernizer
    public Example() { ... }

    @SuppressModernizer
    public static void method() { ... }
}

Modernizer matches the annotation by simple name, so any @SuppressModernizer in any package will suppress violations — the modernizer-maven-annotations dependency above is a convenient canonical copy but is not required. Fields and packages cannot be suppressed because the annotation's @Target does not include them; use <exclusions> or <ignoreClassNamePatterns> instead.

References

License

Copyright (C) 2014-2026 Andrew Gaul

Licensed under the Apache License, Version 2.0

Packages

 
 
 

Contributors

Languages