4

I'm building a java project with maven. I want to make sure several things:

  1. the built jar CAN run on jre8.
  2. the built jar CAN run on jre9, with module/jigsaw.
  3. the built jar CAN be put on maven central.

How should I configure the maven-compiler-plugin? thanks.

the original repo is at https://github.com/cyanpotion/SDL_GameControllerDB_Util

right now I can pass 2 and 3, but the output jar seems cannot run on jre8.

Naman
  • 27,789
  • 26
  • 218
  • 353
XenoAmess
  • 345
  • 1
  • 3
  • 12
  • as broad as this sounds, you might want to look for the toolchains in maven and multi-release jars in maven. (what is the issue with running the jar on Java-8?) – Naman Jun 18 '20 at 05:20
  • @Naman Exception in thread "main" java.lang.UnsupportedClassVersionError: com/xenoamess/cyan_potion/SDL_GameControllerDB_Util has been compiled by a more recent version of the Java Runtime (class file version 53.0), this version of the Java Runtime only recognizes class file versions up to 52.0 – XenoAmess Jun 18 '20 at 05:35
  • @Naman also, even if I use multi release jar and build with jdk14, some error happened.like : Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer; – XenoAmess Jun 18 '20 at 06:43
  • 1
    the exception for unsupported class version is valid if the compiled classes are from a higher java version(9) than the runtime(8)...but using multi-release jars should be an ideal solve for the situation, but the exception shared might need more details..could you [edit the question](https://stackoverflow.com/posts/62442674/edit) with the stack trace over that? on the other side of the things, jar created using java-8 shall also work on JDK-9 and above unless there are classes from JDK in use that are removed or if you don't strictly want to use features from latest JDKs. – Naman Jun 18 '20 at 10:16
  • Take a look at this [answer](https://stackoverflow.com/a/43932328/706317). Probably it will help. – ZhekaKozlov Jun 18 '20 at 10:32
  • You can compile the module file by hand with Java 9 and put it as a resource in your project, and set up the manifest to properly be a multi-release jar. This will probably be the simplest for quite a while yet. – Thorbjørn Ravn Andersen Sep 30 '20 at 11:09

2 Answers2

5

A multi-release jar can be used to accomplish this purpose; however, if you are only after Jigsaw module support, a dual compilation configuration of Java 8 and 9 with the maven-compiler-plugin is sufficient (demonstrated below).

The following is a build configuration that is able to maintain JRE 8 support, but is compatible with being used as a module in JRE 9+. This can be tweaked to support a project as far back as JRE 1.6, if necessary.

Overview of the following build configuration:

  • Enforce a $JAVA_HOME JDK 9+ so that module-info.java can be compiled and validated against the sources (ensures that all module dependencies are correctly referenced).
  • Changes the default-compile execution to NO-OP. By specifying <release>8</release> (or target/source flags), some IDEs during Maven auto-importation (tested with IntelliJ) will assume the language level for this project is Java 8, and produce errors about the project's module-info.java.
  • Compiles all sources (including module-info.java) for Java 9; this ensures that the module-info.java includes any dependencies used without the project sources.
  • Compile all sources (excluding module-info.java) for Java 8, overwriting all previously compiled Java 9 classes with Java 8 classes; this means the only Java 9 class (class level 53+) will be module-info.class. All other classes should now be executable on a compliant JRE 8.

Notes:

  • A caveat is this will compile sources twice, once for Java 8 and another for Java 9. This may significantly increase build times for larger projects (but negligible for smaller projects). This may resolved by placing module-info.java in another source directory (e.g. src/main/java9) and configuring a multi-release JAR (see the first link at the beginning of this message). Note that for proper IDE auto-importation and marking this additional Java 9 source directory correctly, a NO-OP execution with org.codehaus.mojo:build-helper-maven-plugin can be used.
  • Only module-info.java will have Java 9 support, all other classes are limited to classes/methods/fields/code available in JDK/JRE 8.
  • Some tooling may need to be updated in your project configuration if it does not support Java 9 module-info.class. Some older variants of the default Maven plugins do not have sufficient support for modules.
<build>
    <plugins>
        <!-- ensure the project is compiling with JDK 9+ -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-enforcer-plugin</artifactId>
            <version>3.0.0-M3</version>
            <executions>
                <execution>
                    <id>enforce-jdk9</id>
                    <goals>
                        <goal>enforce</goal>
                    </goals>
                    <configuration>
                        <rules>
                            <requireJavaVersion>
                                <version>[1.9,)</version>
                                <message>JDK 9+ is required for compilation</message>
                            </requireJavaVersion>
                        </rules>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- compile sources -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <executions>
                <!-- disable default phase due to fixed id and position in lifecycle -->
                <execution>
                    <id>default-compile</id>
                    <phase>none</phase>
                    <!-- specify source/target for IDE integration -->
                    <configuration>
                        <release>9</release>
                    </configuration>
                </execution>
                <!-- compile sources with Java 9 to generate and validate module-info.java -->
                <execution>
                    <id>java-9-module-compile</id>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <release>9</release>
                    </configuration>
                </execution>
                <!-- recompile sources as Java 8 to overwrite Java 9 class files, except module-info.java -->
                <execution>
                    <id>java-8-compile</id>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <!-- specify JDK 9+ release flag to ensure no classes/methods later than Java 8 are used accidentally -->
                        <release>8</release>
                        <!-- exclude module-info.java from the compilation, as it is unsupported by Java 8 -->
                        <excludes>
                            <exclude>module-info.java</exclude>
                        </excludes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
concision
  • 6,029
  • 11
  • 29
1

I've done it. See my project, the IPAddress Java library. You compile with Java 9 compiler level, then you recompile everything except the module-info.java with Java 8 compile level.. So you have a Java 8 jar with a Java 9 module-info. You can see the compile commands in my ant xml script on GitHub.

There was originally an issue with Android studio that has since been resolved, it would not ignore the module-info. More recent Android studio versions are fine, and so are all Java platforms and environments. Java 9 jres and up will recognize the module-info, Java 8 will not, Java 8 and earlier will ignore it.

My jar is also in maven central, so it is satisfying your 3 requirements. Try it out in your dev environment to see it work, using Java 8 or later jres.

Multi-release jars are not necessary and not worth the trouble.

When I was researching this same question I found this same solution in use with an Apache project, so I am not the only one doing it.

Sean F
  • 4,344
  • 16
  • 30