Programmatically detect --preview Mode in JRE
Asked Answered
A

1

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.

Aerology answered 10/10, 2019 at 16:3 Comment(5)
Try running code explicitly using the feature you need and handle if the JVM complains.Preceptive
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.Shipboard
perhaps this answer 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.Biggerstaff
@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.Preceptive
@kaan this explicitly asks for a hotspot feature making the code vendor dependent.Preceptive
R
4

You can check the Java Class File minor_version. If 0xFFFF the class file is compiled with --enable-preview. For details, see https://mcmap.net/q/1471550/-minor-version-of-a-java-class-file.

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));
    }

}
Raimund answered 12/11, 2019 at 16:10 Comment(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.Homebrew

© 2022 - 2024 — McMap. All rights reserved.