Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8349373: Support JavaFX preview features #1359

Closed
wants to merge 11 commits into from
Closed
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions PREVIEW-FEATURES.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -47,17 +47,13 @@ public enum PreviewFeature {
private final String featureName;

private static final String ENABLE_PREVIEW_PROPERTY = "javafx.enablePreview";
private static final String SUPPRESS_WARNING_PROPERTY = "javafx.suppressPreviewBanner";

private static final boolean enabled = Boolean.getBoolean(ENABLE_PREVIEW_PROPERTY);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JDK requires that you opt into preview features for a specific version. That is, rather than a boolean, the JDK uses an integer feature release value that must match the current release. They do this by using the --release option (in connection with the --enable-preview), and compiling that into the class file, which we can't directly use. Maybe we can do something similar, though?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only done at compilation time, not at runtime. JEP 12 elaborates on this:

--enable-preview itself does not take a version number because it would be easy to misinterpret. For example, on JDK 18, the (hypothetical) flag --enable-preview 19 would appear to suggest support for the preview features of JDK 19, but those features are not known at the time of JDK 18's release.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's what I came to realize as well. So our property should remain boolean.

The only other thing I could think of is for us to provide a new utility method (in some class in javafx.base) that an application must call to register the version of JavaFX API that are compiling against. For example, imagine a java.javafx.util.PreviewFeatures class with the following method:

    public void setVersion(int featureVersion) {}

An application would need to call PreviewFeatures.setVersion(25) to use JavaFX preview features from JavaFX 25. That method would unlock preview features only if the version passed in matches the runtime feature version. This would be in addition to the boolean system property.

The question is whether it is worth the additional complexity (not for us to implement, that's trivial unless I'm missing something), but rather than documentation and burden on the app developer using a preview feature. The docs for each new preview feature would need to link to the PreviewFeatures utility class to describe how to unlock the features. On the plus side, it would provide a common place to document how to unlock preview features -- "call this method from the application and set that system property on the command line when running the app".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be in favor of requiring application developers to call a method to unlock a preview API. It seems a bit too cumbersome and intrusive to me, since it requires you to embed build information into your source code. I've also never seen this in other libraries or frameworks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be in favor ... since it requires you to embed build information into your source code

That seems reason enough to abandon this idea.

I've also never seen this in other libraries or frameworks.

True. Significantly, I didn't propose anything like this for the incubator modules, which can have the same problem.

So I think this is a good minimal solution that provide a clue to the developer that they are relying on API that is unstable and will change in the future (meaning that using such API is a risk they need to be willing to take).

private static final boolean suppressWarning = Boolean.getBoolean(SUPPRESS_WARNING_PROPERTY);
private static final Set<PreviewFeature> enabledFeatures = new HashSet<>();

/**
* Verifies that preview features are enabled, and throws an exception otherwise.
* <p>
* Unless suppressed with the {@code javafx.suppressPreviewBanner=true} system property, this method
* prints a one-time warning to the error output stream for every feature for which it is called.
* This method prints a one-time warning to the error output stream for every feature for which it is called.
*
* @throws RuntimeException if preview features are not enabled
*/
@@ -68,12 +64,11 @@ public void checkEnabled() {
Preview features may be removed in a future release, or upgraded to permanent features of JavaFX.
Programs can only use preview features when the following system property is set: -D%s=true
""".formatted(featureName, VersionInfo.getVersion(), ENABLE_PREVIEW_PROPERTY));
} else if (!suppressWarning && enabledFeatures.add(this)) {
} else if (enabledFeatures.add(this)) {
System.err.printf("""
Note: This program uses the following preview feature of JavaFX %s: %s
Preview features may be removed in a future release, or upgraded to permanent features of JavaFX.
This warning can be disabled with the following system property: -D%s=true
""", VersionInfo.getVersion(), featureName, SUPPRESS_WARNING_PROPERTY);
""", VersionInfo.getVersion(), featureName);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,9 @@
package com.sun.javafx.application;

import com.sun.javafx.PlatformUtil;
import com.sun.javafx.PreviewFeature;
import com.sun.javafx.SecurityUtil;
import com.sun.javafx.util.Utils;
import javafx.application.Application;
import javafx.application.Preloader;
import javafx.application.Preloader.ErrorNotification;
@@ -60,6 +62,9 @@ public class LauncherImpl {
static {
// Check for security manager (throws exception if enabled)
SecurityUtil.checkSecurityManager();

// Initialize the PreviewFeature class to ensure that the corresponding system property is read early.
Utils.forceInit(PreviewFeature.class);
}

/**
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package com.sun.javafx.application;

import com.sun.javafx.PlatformUtil;
import com.sun.javafx.PreviewFeature;
import com.sun.javafx.SecurityUtil;
import com.sun.javafx.application.preferences.PlatformPreferences;
import com.sun.javafx.application.preferences.PreferenceMapping;
@@ -35,6 +36,7 @@
import com.sun.javafx.tk.Toolkit;
import com.sun.javafx.util.Logging;
import com.sun.javafx.util.ModuleHelper;
import com.sun.javafx.util.Utils;

import java.lang.module.ModuleDescriptor;
import java.lang.reflect.InvocationTargetException;
@@ -62,6 +64,9 @@ public class PlatformImpl {
static {
// Check for security manager (throws exception if enabled)
SecurityUtil.checkSecurityManager();

// Initialize the PreviewFeature class to ensure that the corresponding system property is read early.
Utils.forceInit(PreviewFeature.class);
}

private static AtomicBoolean initialized = new AtomicBoolean(false);