Search code examples
javamavenmulti-release-jar

java9 '-release 8' with internal packages (e.g. sun.misc.Unsafe)


I am trying to create a multi-release jar with Maven (3.8.0). We call sun.misc.Unsafe which compiles fine in java8 and java11. However, compiling with java11 --release 8 raises a Compilation failure: package sun.misc does not exist.


Here is how to reproduce the error:

A simple class that calls Unsafe:

public class ChangeableObjects {
    sun.misc.Unsafe unsafe;
}

A simple pom:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <release>8</release>
            </configuration>
        </plugin>
    </plugins>
</build>

If we comment the <release>8</release> line, it works fine on java8 and java11.

If we compile the code as is, it fails with java8; this is normal since it is a java9 feature. The problem is that it also fails with java11.


I assume the problem is that the sun.misc package has been moved between java8 and java9. But still, the purpose of the release flag is that a code that compiles well with java8 should also compile well with java11.

Am I misunderstanding how to use the release flag? Do I need to link the sun.misc package manually to make it work?


Solution

  • Firstly, as stated in the comments, the way I built the multi-release jar (following the link I provided) was not recommended. Using the example given by nullpointer,

    I would prefer to use something like https://github.com/meterware/multirelease-parent/blob/master/pom.xml

    I used Maven toolchains, which enable to compile using different JDKs. This way, my problem disappeared.

    Secondly, the answer to why I get a compilation error when compiling in jdk11 with --release 8 was actually given by khmarbaise who pointed to https://stackoverflow.com/a/43103038/296328:

    javac provides two command line options, -source and -target, which can be used to select the version of the Java language accepted by the compiler and the version of the class files it produces, respectively. By default, however, javac compiles against the most-recent version of the platform APIs. The compiled program can therefore accidentally use APIs only available in the current version of the platform. Such programs cannot run on older versions of the platform, regardless of the values passed to the -source and `-target. options. This is a long-term usability pain point, since users expect that by using these options they'll get class files that can run on the specified platform version.

    Nevertheless, it seems there are workarounds: see Maven: javac: source release 1.6 requires target release 1.6 for example.