From 81025fb07b1ba15b1adad294cc37193597c07669 Mon Sep 17 00:00:00 2001 From: Nick Beattie Date: Mon, 5 May 2025 17:41:20 +1000 Subject: [PATCH 1/2] Enhance JDK detection logic and add validation for configured Java home --- src/daemon/processWatcher.ts | 69 +++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/src/daemon/processWatcher.ts b/src/daemon/processWatcher.ts index 9e8c1b7f..cdb132bf 100644 --- a/src/daemon/processWatcher.ts +++ b/src/daemon/processWatcher.ts @@ -34,17 +34,55 @@ export class ProcessWatcher { if (!javaExt) { return false; } - // get embedded JRE Home + + // First check java.jdt.ls.java.home setting let jreHome: string | undefined; - try { - const jreFolder = path.join(javaExt.extensionPath, "jre"); - const jreDistros = await fs.promises.readdir(jreFolder); - if (jreDistros.length > 0) { - jreHome = path.join(jreFolder, jreDistros[0]); + let configJavaHome = vscode.workspace.getConfiguration().get('java.jdt.ls.java.home'); + + // If user has explicitly configured a Java home, use that + if (configJavaHome) { + if (await this.isValidJdkPath(configJavaHome)) { + jreHome = configJavaHome; + } else { + // Log warning but continue with fallback + console.warn(`Configured Java home ${configJavaHome} is not valid or not accessible. Checking other options.`); + } + } + + // If not found, check for default runtime in java.configuration.runtimes + if (!jreHome) { + const runtimes = vscode.workspace.getConfiguration().get('java.configuration.runtimes'); + if (Array.isArray(runtimes) && runtimes.length > 0) { + // First look for one marked as default + const defaultRuntime = runtimes.find(r => r.default === true); + if (defaultRuntime && defaultRuntime.path) { + if (await this.isValidJdkPath(defaultRuntime.path)) { + jreHome = defaultRuntime.path; + } + } + + // If no default is set or default is invalid, try the first one + if (!jreHome && runtimes[0].path) { + if (await this.isValidJdkPath(runtimes[0].path)) { + jreHome = runtimes[0].path; + } + } + } + } + + // If no valid JDK is found in settings, fall back to embedded JRE + if (!jreHome) { + try { + const jreFolder = path.join(javaExt.extensionPath, "jre"); + const jreDistros = await fs.promises.readdir(jreFolder); + if (jreDistros.length > 0) { + jreHome = path.join(jreFolder, jreDistros[0]); + } + } catch (error) { + // do nothing when jre is not embedded, to avoid spamming logs } - } catch (error) { - // do nothing when jre is not embedded, to avoid spamming logs } + if (!jreHome) { return false; } @@ -96,6 +134,21 @@ export class ProcessWatcher { return [y, o].join(os.EOL); } + private async isValidJdkPath(jdkPath: string): Promise { + try { + // Check if path exists + await fs.promises.access(jdkPath, fs.constants.R_OK); + + // Check if the jps tool exists in the bin directory + const jpsPath = path.join(jdkPath, "bin", "jps" + (os.platform() === 'win32' ? '.exe' : '')); + await fs.promises.access(jpsPath, fs.constants.X_OK); + + return true; + } catch (err) { + return false; + } + } + private onDidJdtlsCrash(lastHeartbeat?: string) { sendInfo("", { name: "jdtls-last-heartbeat", From f803e2ac57fde4b1fcdb6b38ec63d8c77856f234 Mon Sep 17 00:00:00 2001 From: Nick Beattie Date: Mon, 5 May 2025 17:52:17 +1000 Subject: [PATCH 2/2] Updated function naming --- src/daemon/processWatcher.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/daemon/processWatcher.ts b/src/daemon/processWatcher.ts index cdb132bf..41763a7f 100644 --- a/src/daemon/processWatcher.ts +++ b/src/daemon/processWatcher.ts @@ -41,7 +41,7 @@ export class ProcessWatcher { // If user has explicitly configured a Java home, use that if (configJavaHome) { - if (await this.isValidJdkPath(configJavaHome)) { + if (await this.isValidJpsPath(configJavaHome)) { jreHome = configJavaHome; } else { // Log warning but continue with fallback @@ -56,14 +56,14 @@ export class ProcessWatcher { // First look for one marked as default const defaultRuntime = runtimes.find(r => r.default === true); if (defaultRuntime && defaultRuntime.path) { - if (await this.isValidJdkPath(defaultRuntime.path)) { + if (await this.isValidJpsPath(defaultRuntime.path)) { jreHome = defaultRuntime.path; } } // If no default is set or default is invalid, try the first one if (!jreHome && runtimes[0].path) { - if (await this.isValidJdkPath(runtimes[0].path)) { + if (await this.isValidJpsPath(runtimes[0].path)) { jreHome = runtimes[0].path; } } @@ -134,7 +134,7 @@ export class ProcessWatcher { return [y, o].join(os.EOL); } - private async isValidJdkPath(jdkPath: string): Promise { + private async isValidJpsPath(jdkPath: string): Promise { try { // Check if path exists await fs.promises.access(jdkPath, fs.constants.R_OK);