如何在运行中的JVM内判断是否可用JDK环境
Got it, let's solve this problem cleanly—we need to check if the running JVM is part of a full JDK (not a standalone JRE) directly from within the JVM, with external process spawning as a last resort, and implement this in a Liferay plugin to show friendly errors without touching core Liferay code. Here are the most reliable methods:
1. Load JDK-Exclusive Classes (Most Reliable Cross-Version Approach)
The safest in-process method is to attempt loading a class that only exists in the JDK's bundled JRE, not in a standalone JRE. A classic example is com.sun.tools.javac.Main—part of the JDK's development tools but absent in regular JRE distributions.
Here's a reusable code snippet:
public static boolean isRunningOnJdkJre() { try { // Attempt to load a JDK-only class Class.forName("com.sun.tools.javac.Main"); return true; } catch (ClassNotFoundException e) { // Class not found = we're on a standalone JRE return false; } }
Why this works:
- Standalone JREs don't include the
tools.jar(pre-JDK 9) or thejdk.compilermodule (JDK 9+), so these classes won't be available. - This runs entirely within the current JVM, no external processes needed—perfect for your plugin's constraints.
2. Check System Properties (Version-Specific Checks)
You can also inspect JVM system properties to infer the environment, though this requires handling JDK 8 and JDK 9+ separately due to modularization:
For JDK 8 and earlier:
Check if java.home points to a subdirectory of a JDK installation, and if sun.boot.class.path includes tools.jar:
public static boolean isJdkJrePre9() { String javaHome = System.getProperty("java.home"); String bootClassPath = System.getProperty("sun.boot.class.path"); // JDK's java.home is typically <jdk>/jre; standalone JRE's is just <jre> boolean isJdkSubdir = javaHome.contains(File.separator + "jdk") || javaHome.endsWith(File.separator + "jre"); boolean hasToolsJar = bootClassPath.contains("tools.jar"); return isJdkSubdir && hasToolsJar; }
For JDK 9 and later:
Check if the jdk.compiler module is available (standalone JREs exclude development modules):
public static boolean isJdkJrePost9() { try { ModuleLayer.boot().findModule("jdk.compiler"); return true; } catch (Exception e) { return false; } }
Combine both checks for full cross-version coverage.
3. Fallback: Execute External Process (If Needed)
If the above methods don't cover your edge cases, you can fall back to checking if javac is available via a lightweight process spawn. This is less ideal but acceptable as a last option:
public static boolean hasJavacAvailable() { try { Process process = new ProcessBuilder("javac", "-version").start(); int exitCode = process.waitFor(); return exitCode == 0; } catch (IOException | InterruptedException e) { // javac not found or process failed = standalone JRE return false; } }
Implementing in Liferay Plugin
To add friendly error handling in your plugin:
- Use a
PortalLifecycleListenerto run the check during Liferay's startup phase. - If the check fails, log a clear error and optionally show an alert for portal admins.
Example listener snippet:
@Component(immediate = true) public class JdkCheckLifecycleListener implements PortalLifecycleListener { @Override public void portalLifecycleEvent(PortalLifecycleEvent event) { if (event.getLifecycle() == PortalLifecycle.PRE_INIT) { if (!isRunningOnJdkJre()) { _log.error("ERROR: Liferay requires a JDK-bundled JRE to run correctly. Please switch to a full JDK installation."); // Optionally add a visible alert for admins addAdminAlert("Critical JRE Issue", "Liferay must run on a JDK-bundled JRE. Please update your runtime environment."); } } } // Include the isRunningOnJdkJre() method from above here private static final Log _log = LogFactoryUtil.getLog(JdkCheckLifecycleListener.class); }
This approach gives you a clean, in-process check that works across JDK versions, requires no core code changes, and provides clear feedback to users running the wrong runtime.
内容的提问来源于stack exchange,提问作者Olaf Kock

