Skip to content

Commit b36f6ee

Browse files
authored
align jdk validation with vscode-java (#503)
Signed-off-by: Yan Zhang <[email protected]>
1 parent fc6cb66 commit b36f6ee

File tree

4 files changed

+74
-47
lines changed

4 files changed

+74
-47
lines changed

src/java-runtime/assets/jdk.configure.project.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const ProjectRuntimePanel = (props: {
5858
<h3 className="font-weight-light">Workspace Overview</h3>
5959
{projectRuntimesTable}
6060
<details>
61-
<summary> Click to see how:</summary>
61+
<summary> Click to see how</summary>
6262

6363
<p>For example, if you want to use Java 8, add below lines</p>
6464
<p>In <code>pom.xml</code> of a Maven project:</p>

src/java-runtime/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import * as path from "path";
66
import * as request from "request-promise-native";
77
import * as vscode from "vscode";
88
import { getExtensionContext, loadTextFromFile } from "../utils";
9-
import { findJavaHomes, getJavaVersion, JavaRuntime } from "./utils/findJavaRuntime";
9+
import { findJavaHomes, JavaRuntime } from "./utils/findJavaRuntime";
1010
import architecture = require("arch");
11-
import { checkJavaRuntime } from "./utils/upstreamApi";
11+
import { resolveRequirements } from "./utils/upstreamApi";
1212
import { JavaRuntimeEntry, NatureId, ProjectRuntimeEntry, ProjectType } from "./types";
1313
import { sourceLevelDisplayName } from "./utils/misc";
1414
import { pathExists } from "fs-extra";
@@ -168,12 +168,9 @@ export async function validateJavaRuntime() {
168168
// option a) should check Java LS exported API for java_home
169169
// * option b) use the same way to check java_home as vscode-java
170170
try {
171-
const upstreamJavaHome = await checkJavaRuntime();
172-
if (upstreamJavaHome) {
173-
const version = await getJavaVersion(upstreamJavaHome);
174-
if (version && version >= 11) {
175-
return true;
176-
}
171+
const runtime = await resolveRequirements();
172+
if (runtime.java_version >=11 && runtime.java_home) {
173+
return true;
177174
}
178175
} catch (error) {
179176
}
@@ -200,8 +197,7 @@ export async function findJavaRuntimeEntries(): Promise<{
200197
let javaDotHome;
201198
let javaHomeError;
202199
try {
203-
javaDotHome = await checkJavaRuntime();
204-
const javaVersion = await getJavaVersion(javaDotHome);
200+
const {java_home: javaDotHome, java_version: javaVersion } = await resolveRequirements();
205201
if (!javaVersion || javaVersion < 11) {
206202
javaHomeError = `Java 11 or more recent is required to run the Java extension. Preferred JDK "${javaDotHome}" (version ${javaVersion}) doesn't meet the requirement. Please specify or install a recent JDK.`;
207203
}

src/java-runtime/utils/findJavaRuntime.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const expandHomeDir = require("expand-home-dir");
1010
const WinReg = require("winreg-utf8");
1111
const isWindows: boolean = process.platform.indexOf("win") === 0;
1212
const isMac: boolean = process.platform.indexOf("darwin") === 0;
13+
const isLinux: boolean = process.platform.indexOf("linux") === 0;
1314
const JAVAC_FILENAME = "javac" + (isWindows ? ".exe" : "");
1415
const JAVA_FILENAME = "java" + (isWindows ? ".exe" : "");
1516

@@ -26,8 +27,9 @@ export async function findJavaHomes(): Promise<JavaRuntime[]> {
2627
const ret: JavaRuntime[] = [];
2728
const jdkMap = new Map<string, string[]>();
2829

29-
updateJDKs(jdkMap, await fromJavaHomeEnv(), "env.JAVA_HOME");
30-
updateJDKs(jdkMap, await fromPathEnv(), "env.PATH");
30+
updateJDKs(jdkMap, await fromEnv("JDK_HOME"), "env.JDK_HOME");
31+
updateJDKs(jdkMap, await fromEnv("JAVA_HOME"), "env.JAVA_HOME");
32+
updateJDKs(jdkMap, await fromPath(), "env.PATH");
3133
updateJDKs(jdkMap, await fromWindowsRegistry(), "WindowsRegistry");
3234
updateJDKs(jdkMap, await fromCommonPlaces(), "DefaultLocation");
3335

@@ -59,18 +61,19 @@ function updateJDKs(map: Map<string, string[]>, newJdks: string[], source: strin
5961
}
6062
}
6163

62-
async function fromJavaHomeEnv(): Promise<string[]> {
64+
async function fromEnv(name: string): Promise<string[]> {
6365
const ret: string[] = [];
64-
if (process.env.JAVA_HOME) {
65-
const javaHome = await verifyJavaHome(process.env.JAVA_HOME, JAVAC_FILENAME);
66+
const proposed = process.env[name];
67+
if (proposed) {
68+
const javaHome = await verifyJavaHome(proposed, JAVAC_FILENAME);
6669
if (javaHome) {
6770
ret.push(javaHome);
6871
}
6972
}
7073
return ret;
7174
}
7275

73-
async function fromPathEnv(): Promise<string[]> {
76+
async function fromPath(): Promise<string[]> {
7477
const ret: string[] = [];
7578

7679
const paths = process.env.PATH ? process.env.PATH.split(path.delimiter).filter(Boolean) : [];
@@ -235,25 +238,39 @@ async function fromCommonPlaces(): Promise<string[]> {
235238
}
236239
}
237240

241+
// common place for Linux
242+
if (isLinux) {
243+
const jvmStore = "/usr/lib/jvm";
244+
let jvms: string[] = [];
245+
try {
246+
jvms = await fse.readdir(jvmStore);
247+
} catch (error) {
248+
// ignore
249+
}
250+
for (const jvm of jvms) {
251+
const proposed = path.join(jvmStore, jvm);
252+
const javaHome = await verifyJavaHome(proposed, JAVAC_FILENAME);
253+
if (javaHome) {
254+
ret.push(javaHome);
255+
}
256+
}
257+
}
258+
238259
return ret;
239260
}
240261

241-
242-
243-
async function verifyJavaHome(raw: string, javaFilename: string): Promise<string | undefined> {
262+
export async function verifyJavaHome(raw: string, javaFilename: string): Promise<string | undefined> {
244263
const dir = expandHomeDir(raw);
245264
const targetJavaFile = await findLinkedFile(path.resolve(dir, "bin", javaFilename));
246265
const proposed = path.dirname(path.dirname(targetJavaFile));
247266
if (await fse.pathExists(proposed)
248-
&& (await fse.lstat(proposed)).isDirectory()
249267
&& await fse.pathExists(path.resolve(proposed, "bin", javaFilename))
250268
) {
251269
return proposed;
252270
}
253271
return undefined;
254272
}
255273

256-
257274
// iterate through symbolic links until file is found
258275
async function findLinkedFile(file: string): Promise<string> {
259276
if (!await fse.pathExists(file) || !(await fse.lstat(file)).isSymbolicLink()) {
@@ -262,8 +279,7 @@ async function findLinkedFile(file: string): Promise<string> {
262279
return await findLinkedFile(await fse.readlink(file));
263280
}
264281

265-
266-
export async function getJavaVersion(javaHome: string): Promise<number | undefined> {
282+
export async function getJavaVersion(javaHome: string): Promise<number> {
267283
let javaVersion = await checkVersionInReleaseFile(javaHome);
268284
if (!javaVersion) {
269285
javaVersion = await checkVersionByCLI(javaHome);
@@ -319,7 +335,10 @@ async function checkVersionInReleaseFile(javaHome: string): Promise<number> {
319335
/**
320336
* Get version by parsing `JAVA_HOME/bin/java -version`
321337
*/
322-
function checkVersionByCLI(javaHome: string): Promise<number> {
338+
async function checkVersionByCLI(javaHome: string): Promise<number> {
339+
if (!javaHome) {
340+
return 0;
341+
}
323342
return new Promise((resolve, reject) => {
324343
const javaBin = path.join(javaHome, "bin", JAVA_FILENAME);
325344
cp.execFile(javaBin, ["-version"], {}, (error, stdout, stderr) => {

src/java-runtime/utils/upstreamApi.ts

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,21 @@ import * as vscode from "vscode";
99
import { env } from "vscode";
1010

1111
const expandHomeDir = require("expand-home-dir");
12-
import findJavaHome = require("find-java-home");
12+
const REQUIRED_JDK_VERSION = 11;
13+
import { findJavaHomes, getJavaVersion, JavaRuntime } from "./findJavaRuntime";
1314

1415

1516
const isWindows = process.platform.indexOf("win") === 0;
1617
const JAVAC_FILENAME = "javac" + (isWindows ? ".exe" : "");
1718

18-
export async function checkJavaRuntime(): Promise<string> {
19+
export async function resolveRequirements(): Promise<any> {
1920
return new Promise(async (resolve, reject) => {
2021
let source: string;
22+
let javaVersion: number = 0;
2123
let javaHome = await checkJavaPreferences();
2224
if (javaHome) {
25+
// java.home explictly specified
2326
source = `java.home variable defined in ${env.appName} settings`;
24-
} else {
25-
javaHome = process.env["JDK_HOME"];
26-
if (javaHome) {
27-
source = "JDK_HOME environment variable";
28-
} else {
29-
javaHome = process.env["JAVA_HOME"];
30-
source = "JAVA_HOME environment variable";
31-
}
32-
}
33-
if (javaHome) {
3427
javaHome = expandHomeDir(javaHome) as string;
3528
if (!await fse.pathExists(javaHome)) {
3629
invalidJavaHome(reject, `The ${source} points to a missing or inaccessible folder (${javaHome})`);
@@ -43,20 +36,39 @@ export async function checkJavaRuntime(): Promise<string> {
4336
}
4437
invalidJavaHome(reject, msg);
4538
}
46-
return resolve(javaHome);
47-
}
48-
// No settings, let's try to detect as last resort.
49-
findJavaHome((err, home) => {
50-
if (err) {
51-
invalidJavaHome(reject, "Java runtime (JDK, not JRE) could not be located");
52-
}
53-
else {
54-
resolve(home);
39+
javaVersion = await getJavaVersion(javaHome);
40+
} 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;
5548
}
56-
});
49+
}
50+
51+
if (javaVersion < REQUIRED_JDK_VERSION) {
52+
invalidJavaHome(reject, `Java ${REQUIRED_JDK_VERSION} or more recent is required to run the Java extension. (Current version: ${javaVersion}, JDK: ${javaHome})`);
53+
}
54+
55+
resolve({ java_home: javaHome, java_version: javaVersion });
5756
});
5857
}
5958

59+
function sortJdksBySource(jdks: JavaRuntime[]) {
60+
const rankedJdks = jdks as Array<JavaRuntime & { rank: number }>;
61+
const sources = ["env.JDK_HOME", "env.JAVA_HOME", "env.PATH"];
62+
for (const [index, source] of sources.entries()) {
63+
for (const jdk of rankedJdks) {
64+
if (jdk.rank === undefined && jdk.sources.includes(source)) {
65+
jdk.rank = index;
66+
}
67+
}
68+
}
69+
rankedJdks.filter(jdk => jdk.rank === undefined).forEach(jdk => jdk.rank = sources.length);
70+
rankedJdks.sort((a, b) => a.rank - b.rank);
71+
}
6072

6173
function checkJavaPreferences(){
6274
return vscode.workspace.getConfiguration("java").get<string>("home");

0 commit comments

Comments
 (0)