Skip to content

Commit 52af240

Browse files
authored
update tooling jdk validation mechanism (#851)
1 parent 0e4d139 commit 52af240

File tree

4 files changed

+140
-40
lines changed

4 files changed

+140
-40
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@
378378
"expand-tilde": "^2.0.2",
379379
"fs-extra": "^9.0.1",
380380
"highlight.js": "10.5.0",
381-
"jdk-utils": "^0.2.1",
381+
"jdk-utils": "^0.3.1",
382382
"jquery": "^3.5.1",
383383
"lodash": "^4.17.21",
384384
"minimatch": "^3.0.4",

src/java-runtime/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ export async function validateJavaRuntime() {
196196
// * option b) use the same way to check java_home as vscode-java
197197
try {
198198
const runtime = await resolveRequirements();
199-
if (runtime.java_version >=11 && runtime.java_home) {
199+
if (runtime.tooling_jre_version >=11 && runtime.tooling_jre) {
200200
return true;
201201
}
202202
} catch (error) {
@@ -226,8 +226,8 @@ export async function findJavaRuntimeEntries(): Promise<{
226226
let javaHomeError;
227227
try {
228228
const runtime = await resolveRequirements();
229-
javaDotHome = runtime.java_home;
230-
const javaVersion = runtime.java_version;
229+
javaDotHome = runtime.tooling_jre;
230+
const javaVersion = runtime.tooling_jre_version;
231231
if (!javaVersion || javaVersion < 11) {
232232
javaHomeError = `Java 11 or more recent is required by the Java language support (redhat.java) extension. Preferred JDK "${javaDotHome}" (version ${javaVersion}) doesn't meet the requirement. Please specify or install a recent JDK.`;
233233
}
Lines changed: 133 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
// based on https://github.com/redhat-developer/vscode-java/blob/fcbe9204638610ce773cedaef445f19032a785cb/src/requirements.ts
4+
// based on https://github.com/redhat-developer/vscode-java/blob/c4cdbed1190fc705c364179dab525645acf03898/src/requirements.ts
55

66
import * as fse from "fs-extra";
7+
import { findRuntimes, getRuntime, getSources, IJavaRuntime, JAVAC_FILENAME, JAVA_FILENAME } from 'jdk-utils';
78
import * as path from "path";
89
import * as vscode from "vscode";
9-
import { env } from "vscode";
10+
import { env, workspace } from 'vscode';
1011

1112
const expandHomeDir = require("expand-home-dir");
1213
const REQUIRED_JDK_VERSION = 11;
13-
import { findJavaHomes, getJavaVersion, JavaRuntime } from "./findJavaRuntime";
1414

15-
16-
const isWindows = process.platform.indexOf("win") === 0;
17-
const JAVAC_FILENAME = "javac" + (isWindows ? ".exe" : "");
18-
19-
export async function resolveRequirements(): Promise<any> {
15+
export async function resolveRequirements(): Promise<{
16+
tooling_jre: string | undefined; // Used to launch Java extension.
17+
tooling_jre_version: number;
18+
java_home: string | undefined; // Used as default project JDK.
19+
java_version: number;
20+
}> {
21+
const javaExtPath: string | undefined = vscode.extensions.getExtension("redhat.java")?.extensionPath
22+
let toolingJre: string | undefined = await findEmbeddedJRE(javaExtPath);
23+
let toolingJreVersion: number = await getMajorVersion(toolingJre);
2024
return new Promise(async (resolve, reject) => {
2125
let source: string;
2226
let javaVersion: number = 0;
23-
let javaHome = await checkJavaPreferences();
24-
if (javaHome) {
25-
// java.home explictly specified
27+
let javaHome = checkJavaPreferences();
28+
if (!toolingJre && javaHome) { // "java.home" setting is only used for the universal version.
2629
source = `java.home variable defined in ${env.appName} settings`;
27-
javaHome = expandHomeDir(javaHome) as string;
30+
javaHome = expandHomeDir(javaHome);
2831
if (!await fse.pathExists(javaHome)) {
2932
invalidJavaHome(reject, `The ${source} points to a missing or inaccessible folder (${javaHome})`);
30-
} else if (!await fse.pathExists(path.resolve(javaHome, "bin", JAVAC_FILENAME))) {
33+
} else if (!await fse.pathExists(path.resolve(javaHome, 'bin', JAVAC_FILENAME))) {
3134
let msg: string;
3235
if (await fse.pathExists(path.resolve(javaHome, JAVAC_FILENAME))) {
3336
msg = `'bin' should be removed from the ${source} (${javaHome})`;
@@ -36,36 +39,117 @@ export async function resolveRequirements(): Promise<any> {
3639
}
3740
invalidJavaHome(reject, msg);
3841
}
39-
javaVersion = await getJavaVersion(javaHome);
42+
javaVersion = await getMajorVersion(javaHome);
43+
toolingJre = javaHome;
44+
toolingJreVersion = javaVersion;
4045
} else {
41-
// java.home not specified, search valid JDKs from env.JAVA_HOME, env.PATH, Registry(Window), Common directories
42-
const javaRuntimes = await findJavaHomes();
43-
const validJdks = javaRuntimes.filter(r => r.version >= REQUIRED_JDK_VERSION);
44-
if (validJdks.length > 0) {
45-
sortJdksBySource(validJdks);
46-
javaHome = validJdks[0].home;
47-
javaVersion = validJdks[0].version;
46+
// java.home not specified, search valid JDKs from env.JAVA_HOME, env.PATH, SDKMAN, jEnv, jabba, Common directories
47+
const javaRuntimes = await findRuntimes({checkJavac: true, withVersion: true, withTags: true});
48+
if (!toolingJre) { // universal version
49+
// as latest version as possible.
50+
sortJdksByVersion(javaRuntimes);
51+
const validJdks = javaRuntimes.filter(r => r.version && r.version.major >= REQUIRED_JDK_VERSION);
52+
if (validJdks.length > 0) {
53+
sortJdksBySource(validJdks);
54+
javaHome = validJdks[0].homedir;
55+
javaVersion = validJdks[0].version?.major ?? 0;
56+
toolingJre = javaHome;
57+
toolingJreVersion = javaVersion;
58+
}
59+
} else { // pick a default project JDK/JRE
60+
/**
61+
* For legacy users, we implicitly following the order below to
62+
* set a default project JDK during initialization:
63+
* java.home > env.JDK_HOME > env.JAVA_HOME > env.PATH
64+
*
65+
* We'll keep it for compatibility.
66+
*/
67+
if (javaHome && (await getRuntime(javaHome) !== undefined)) {
68+
const runtime = await getRuntime(javaHome, {withVersion: true});
69+
if (runtime) {
70+
javaHome = runtime.homedir;
71+
javaVersion = runtime.version?.major ?? 0;
72+
}
73+
} else if (javaRuntimes.length) {
74+
sortJdksBySource(javaRuntimes);
75+
javaHome = javaRuntimes[0].homedir;
76+
javaVersion = javaRuntimes[0].version?.major ?? 0;
77+
} else if (javaHome = (await findDefaultRuntimeFromSettings() ?? "")) {
78+
javaVersion = await getMajorVersion(javaHome);
79+
} else {
80+
/**
81+
* Originally it was:
82+
* invalidJavaHome(reject, "Please download and install a JDK to compile your project. You can configure your projects with different JDKs by the setting ['java.configuration.runtimes'](https://github.com/redhat-developer/vscode-java/wiki/JDK-Requirements#java.configuration.runtimes)");
83+
*
84+
* here we focus on tooling jre, so we swallow the error.
85+
*
86+
*/
87+
}
4888
}
4989
}
5090

51-
if (javaVersion < REQUIRED_JDK_VERSION) {
52-
let message = `Java ${REQUIRED_JDK_VERSION} or more recent is required by the Java language support (redhat.java) extension.`;
53-
if (javaHome) {
54-
message += `(Current JDK: ${javaHome})`;
55-
}
56-
invalidJavaHome(reject, message);
91+
if (!toolingJre || toolingJreVersion < REQUIRED_JDK_VERSION) {
92+
// For universal version, we still require users to install a qualified JDK to run Java extension.
93+
invalidJavaHome(reject, `Java ${REQUIRED_JDK_VERSION} or more recent is required to run the Java extension. Please download and install a recent JDK. You can still compile your projects with older JDKs by configuring ['java.configuration.runtimes'](https://github.com/redhat-developer/vscode-java/wiki/JDK-Requirements#java.configuration.runtimes)`);
5794
}
5895

59-
resolve({ java_home: javaHome, java_version: javaVersion });
96+
resolve({
97+
tooling_jre: toolingJre, // Used to launch Java extension.
98+
tooling_jre_version: toolingJreVersion,
99+
java_home: javaHome, // Used as default project JDK.
100+
java_version: javaVersion,
101+
});
60102
});
61103
}
62104

63-
function sortJdksBySource(jdks: JavaRuntime[]) {
64-
const rankedJdks = jdks as Array<JavaRuntime & { rank: number }>;
65-
const sources = ["env.JDK_HOME", "env.JAVA_HOME", "env.PATH"];
105+
async function findEmbeddedJRE(javaExtPath?: string): Promise<string | undefined> {
106+
if (!javaExtPath) {
107+
return undefined;
108+
}
109+
const jreHome = path.join(javaExtPath, "jre");
110+
if (fse.existsSync(jreHome) && fse.statSync(jreHome).isDirectory()) {
111+
const candidates = fse.readdirSync(jreHome);
112+
for (const candidate of candidates) {
113+
if (fse.existsSync(path.join(jreHome, candidate, "bin", JAVA_FILENAME))) {
114+
return path.join(jreHome, candidate);
115+
}
116+
}
117+
}
118+
119+
return;
120+
}
121+
122+
async function findDefaultRuntimeFromSettings(): Promise<string | undefined> {
123+
const runtimes = workspace.getConfiguration().get("java.configuration.runtimes");
124+
if (Array.isArray(runtimes) && runtimes.length) {
125+
let candidate: string | undefined;
126+
for (const runtime of runtimes) {
127+
if (!runtime || typeof runtime !== 'object' || !runtime.path) {
128+
continue;
129+
}
130+
131+
const jr = await getRuntime(runtime.path);
132+
if (jr) {
133+
candidate = jr.homedir;
134+
}
135+
136+
if (runtime.default) {
137+
break;
138+
}
139+
}
140+
141+
return candidate;
142+
}
143+
144+
return undefined;
145+
}
146+
147+
function sortJdksBySource(jdks: IJavaRuntime[]) {
148+
const rankedJdks = jdks as Array<IJavaRuntime & { rank: number }>;
149+
const sources = ["JDK_HOME", "JAVA_HOME", "PATH"];
66150
for (const [index, source] of sources.entries()) {
67151
for (const jdk of rankedJdks) {
68-
if (jdk.rank === undefined && jdk.sources.includes(source)) {
152+
if (jdk.rank === undefined && getSources(jdk).includes(source)) {
69153
jdk.rank = index;
70154
}
71155
}
@@ -74,10 +158,26 @@ function sortJdksBySource(jdks: JavaRuntime[]) {
74158
rankedJdks.sort((a, b) => a.rank - b.rank);
75159
}
76160

161+
/**
162+
* Sort by major version in descend order.
163+
*/
164+
function sortJdksByVersion(jdks: IJavaRuntime[]) {
165+
jdks.sort((a, b) => (b.version?.major ?? 0) - (a.version?.major ?? 0));
166+
}
167+
168+
77169
function checkJavaPreferences(){
78-
return vscode.workspace.getConfiguration("java").get<string>("home");
170+
return vscode.workspace.getConfiguration("java").get<string>("home") ?? "";
79171
}
80172

81173
function invalidJavaHome(reject: any, reason: string) {
82174
reject(new Error(reason));
83175
}
176+
177+
async function getMajorVersion(javaHome?: string): Promise<number> {
178+
if (!javaHome) {
179+
return 0;
180+
}
181+
const runtime = await getRuntime(javaHome, { withVersion: true });
182+
return runtime?.version?.major || 0;
183+
}

0 commit comments

Comments
 (0)