Skip to content

Migration

Martin Desruisseaux edited this page Mar 31, 2025 · 12 revisions

This page lists some usage patterns of Maven 3 which have changed in Maven 4.

Declaration of source directories

The Maven 3 elements are deprecated and should be replaced by the new <sources> element introduced in Maven 4.

Maven 3

<build>
  <sourceDirectory>src/main/java</sourceDirectory>
  <testSourceDirectory>src/test/java</testSourceDirectory>
</build>

Maven 4

<build>
  <sources>
    <source>
      <directory>src/main/java</directory>
    </source>
    <source>
      <scope>test</scope>
      <directory>src/test/java</directory>
    </source>
  </sources>
<build>

Note that the declaration of a <sources> element replaces the default values, hence the need to declare the source and test directories together. The new <source> element allows more flexibility such as specifying many directories, the include/exclude filters, the targeted Java release and more.

Declaration of many source directories

The external plugin used in Maven 3 is no longer needed and should be replaced by the build-in <sources> elements.

Maven 3

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>build-helper-maven-plugin</artifactId>
      <executions>
        <execution>
          <id>add-directory</id>
          <goals>
            <goal>add-source</goal>
          </goals>
          <configuration>
            <sources>
              <source>src/extension/java</source>
            </sources>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Maven 4

<build>
  <sources>
    <source>
      <directory>src/main/java</directory>
    </source>
    <source>
      <directory>src/extension/java</directory>
    </source>
    <source>
      <scope>test</scope>
      <directory>src/test/java</directory>
    </source>
  </sources>
<build>

Reminder: because <sources> replaces the default values, these defaults need to be explicitly specified.

Multi-releases project

The new compiler plugin handles automatically multiple executions of javac with different --release option values, and with automatic adjustments of class-path and output directories for producing a multi-releases project.

Maven 3

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <executions>
        <execution>
          <id>compile-java-17</id>
          <goals>
            <goal>compile</goal>
          </goals>
          <configuration>
            <release>17</release>
          </configuration>
        </execution>
        <execution>
          <id>compile-java-21</id>
          <phase>compile</phase>
          <goals>
            <goal>compile</goal>
          </goals>
          <configuration>
            <release>21</release>
            <compileSourceRoots>
              <compileSourceRoot>${project.basedir}/src/main/java_21</compileSourceRoot>
            </compileSourceRoots>
            <multiReleaseOutput>true</multiReleaseOutput>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Maven 4

<build>
  <sources>
    <source>
      <directory>src/main/java</directory>
      <targetVersion>17</targetVersion>
    </source>
    <source>
      <directory>src/main/java_21</directory>
      <targetVersion>21</targetVersion>
    </source>
    <source>
      <scope>test</scope>
      <directory>src/test/java</directory>
    </source>
  </sources>
</build>

Include/exclude filters

The Maven 3 way to declare include/exclude filters is still supported, but should be replaced by the <sources> element when applicable. Those two ways are not strictly equivalent:

  • The Maven 4 way specifies filters independently for each source directory. These filters will be applied by all plugins that migrated to the Maven 4 API, not only the compiler plugin.
  • Conversely, the Maven 3 way specifies filters which will be applied only by the compiler plugin. However, these filters apply to all source directories.

Maven 3

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <excludes>
          <exclude>**/Foo*.java</exclude>
        </excludes>
      </configuration>
    </plugin>
  </plugins>
</build>

Maven 4

<build>
  <sources>
    <source>
      <directory>src/main/java</directory>
      <excludes>
        <exclude>**/Foo*.java</exclude>
      </excludes>
    </source>
    <source>
      <scope>test</scope>
      <directory>src/test/java</directory>
    </source>
  </sources>
</build>

Modular projects

The Maven 3 way to make a modular project is to put a module-info.java file in the root directory of Java source files. Because the compilation and execution of tests usually require an amended version of module information, Maven 3 allows to overwrite that file with another module-info.java file placed in the test source directory. While this approach is still supported in Maven 4 for compatibility reasons, it is deprecated and may no longer be supported in a future version. Developers are strongly encouraged to migrate to the approach described in this section.

Maven 3

The directory layout was as below:

src
├─ main
│  └─ java
│     ├─ module-info.java
│     └─ my/product/*.java
├─ test
│  └─ java
│     ├─ module-info.java
│     └─ my/product/*.java
└─ target
   └─ classes

An alternative to test/java/module-info.java was to declare compiler arguments such as --add-reads in the <testCompilerArgs> element of the plugin configuration.

Maven 4 with package hierarchy

Same directory layout as Maven 3 with only test/java/module-info.java removed. This approach can be used when compatibility with Maven 3 is required.

src
├─ main
│  └─ java
│     ├─ module-info.java
│     └─ my/product/*.java
├─ test
│  └─ java
│     └─ my/product/*.java
└─ target
   └─ classes

The Maven compiler automatically adds --patch-module and --add-reads arguments for compiling the tests. In many cases (but not always), a <testCompilerArgs> plugin configuration is unnecessary.

Maven 4 with module source hierarchy

See the user guide for more information. This approach is recommended for new projects, (TODO: pending upgrade of other Maven plugins) as it makes the best use of compiler options dedicated to modular projects.

src
├─ java
│  └─ my.product.foo
│     ├─ main
│     │  ├─ module-info.java
│     │  └─ my/product/*.java
│     └─ test
│        └─ my/product/*.java
└─ target
   └─ classes
      └─ my.product.foo

Above layout needs to be declared with the following section in the pom.xml file:

<build>
  <sources>
    <source>
      <module>my.product.foo</module>
      <directory>src/java/my.product.foo/main</directory>
    </source>
    <source>
      <module>my.product.foo</module>
      <directory>src/java/my.product.foo/test</directory>
      <scope>test</scope>
    </source>
  </sources>
</build>

The Maven compiler automatically adds --patch-module and --add-reads arguments for compiling the tests. In many cases (but not always), a <testCompilerArgs> plugin configuration is unnecessary.

Clone this wiki locally