8

Since Java 11, a PREVIEW-MODE on the Java Compiler and Runtime can be enabled. It allows to preview new features. (JEP 12)

java --enable-preview

How can I detect from within Java whether the JVM has been started with preview-mode enabled?

The intention is to describe the running container on an in-application status page/json - for devops overview.

So far I looked into system-properties and flags, neither contained a hint.

alfonx
  • 6,936
  • 2
  • 49
  • 58
  • 2
    Try running code explicitly using the feature you need and handle if the JVM complains. – Thorbjørn Ravn Andersen Oct 10 '19 at 16:21
  • 2
    Just wondering, why would you want to do it? Since such a code is bound to compile only with the flag set or else it wouldn't even compile, forget execution. – Naman Oct 10 '19 at 16:39
  • perhaps [this answer](https://stackoverflow.com/a/14646721/11374957) could work, either directly or maybe as a starting point. haven't done this myself, but it appears to be similar to what you're asking about. – Kaan Oct 10 '19 at 16:45
  • 2
    @naman you can compile most code for java 11 and then have the rest compiled for whatever the preview holds. If that code is loaded inside a try-block you can handle failure. – Thorbjørn Ravn Andersen Oct 10 '19 at 16:53
  • 2
    @kaan this explicitly asks for a hotspot feature making the code vendor dependent. – Thorbjørn Ravn Andersen Oct 10 '19 at 16:54

1 Answers1

4

You can check the Java Class File minor_version. If 0xFFFF the class file is compiled with --enable-preview. For details, see https://stackoverflow.com/a/58821511/868941.

A simple program to check this is given below (beware, Java 13 code with preview features enabled!).

public final class ClassFileVersion {

    private final int major;
    private final int minor;

    private ClassFileVersion(int major, int minor) {
        this.major = major;
        this.minor = minor;
    }

    public static ClassFileVersion of(Class<?> classFile) throws IOException {
        try (InputStream is = classFile.getResourceAsStream("/%s.class".formatted(classFile.getName().replace('.', '/')))) {
            var buffer = new byte[8];
            if (is.read(buffer) != buffer.length) {
                throw new AssertionError("Not a Java Class File!");
            }
            return new ClassFileVersion(readUnsignedShort(buffer, 6), readUnsignedShort(buffer, 4));
        }
    }

    public String getVersionNumber() {
        return "%d.%d".formatted(major, minor);
    }

    public boolean isEnablePreview() {
        return major >= 55 && minor == 0xFFFF;
    }

    @Override
    public String toString() {
        return (major < 49 ? "JDK " : "Java SE ") +
            switch(major) {
                case 45 -> "1.1";
                case 46 -> "1.2";
                case 47 -> "1.3";
                case 48 -> "1.4";
                case 49 -> "5";
                case 50 -> "6";
                case 51 -> "7";
                case 52 -> "8";
                case 53 -> "9";
                case 54 -> "10";
                case 55 -> "11";
                case 56 -> "12";
                case 57 -> "13";
                case 58 -> "14";
                default -> throw wrongVersion();
            } +
            switch(minor) {
                case 0 -> "";
                case 3 -> {
                    if (major != 45) {
                        throw wrongVersion();
                    }
                    yield "";
                }
                case 0xFFFF -> " --enable-preview";
                default -> throw wrongVersion();
            };
    }

    private static int readUnsignedShort(byte[] buffer, int offset) {
        return ((buffer[offset] & 0xff) << 8) + (buffer[++offset] & 0xff);
    }

    private AssertionError wrongVersion() {
        return new AssertionError("Wrong Java Class File Version: %d.%d".formatted(major, minor));
    }
    // to run this code (JDK 13 needed):
    // java --enable-preview --source 13 ClassFileVersion.java 
    public static void main(String[] args) throws IOException {
        // prints "Java SE 13 --enable-preview"
        System.out.println(ClassFileVersion.of(ClassFileVersion.class));
    }

}
rmuller
  • 12,062
  • 4
  • 64
  • 92
  • 1
    This tests whether the class file has been compiled with `--enable-preview`, not whether preview has been enabled at runtime. Obviously, it must have worked if `true`, but when the class file has been compiled without preview, but preview has been enabled at runtime, the result will be wrong. – Holger Sep 29 '22 at 13:44