From b9be4bac655592d3acac5482899cd55f5a474eef Mon Sep 17 00:00:00 2001 From: Morgan Chang Date: Fri, 10 Jul 2026 22:26:41 -0400 Subject: [PATCH 1/2] Fix race when executing Java workspace commands during standard server startup Signed-off-by: Morgan Chang --- src/extension.ts | 142 +++++++++++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 53 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 5deac6e13..aa5ac224c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -52,6 +52,7 @@ const jdtEventEmitter = new EventEmitter(); const extensionName = 'Language Support for Java'; let storagePath: string; let clientLogFile: string; +let standardServerStart: Promise | undefined; const excutable= new Deferred(); @@ -444,6 +445,17 @@ export async function activate(context: ExtensionContext): Promise return apiManager.getApiInstance(); } +async function getStandardLanguageClient(): Promise { + try { + if (standardServerStart) { + return await standardServerStart; + } + } catch (error) { + logger.error(`Failed to initialize the Java language client: ${getMessage(error)}`); + } + return standardClient.getClient(); +} + async function postExtensionStartInit( context: ExtensionContext, requirements: requirements.RequirementsData, @@ -469,7 +481,53 @@ async function postExtensionStartInit( serverStatusBarProvider.showLightWeightStatus(); } - context.subscriptions.push(commands.registerCommand(Commands.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => { + apiManager.getApiInstance().onDidServerModeChange((event: ServerMode) => { + if (event === ServerMode.standard) { + syntaxClient.stop(); + fileEventHandler.setServerStatus(true); + languageStatusBarProvider.initialize(context); + } + commands.executeCommand('setContext', 'java:serverMode', event); + }); + + if (cleanWorkspaceExists) { + const data = {}; + try { + cleanupLombokCache(context); + cleanupWorkspaceState(context); + deleteDirectory(workspacePath); + deleteDirectory(syntaxServerWorkspacePath); + cleanJavaLSConfiguration(context); + } catch (error) { + data['error'] = getMessage(error); + window.showErrorMessage(`Failed to delete ${workspacePath}: ${error}`); + } + await Telemetry.sendTelemetry(Commands.CLEAN_WORKSPACE, data); + } + + if (serverMode === ServerMode.hybrid && !await fse.pathExists(path.join(workspacePath, ".metadata", ".plugins"))) { + const config = getJavaConfiguration(); + const importOnStartupSection: string = "project.importOnFirstTimeStartup"; + const importOnStartup = config.get(importOnStartupSection); + if (importOnStartup === "disabled" || + env.uiKind === UIKind.Web && env.appName.includes("Visual Studio Code")) { + apiManager.getApiInstance().serverMode = ServerMode.lightWeight; + apiManager.fireDidServerModeChange(ServerMode.lightWeight); + requireStandardServer = false; + } else if (importOnStartup === "interactive" && await workspaceContainsBuildFiles()) { + apiManager.getApiInstance().serverMode = ServerMode.lightWeight; + apiManager.fireDidServerModeChange(ServerMode.lightWeight); + requireStandardServer = await promptUserForStandardServer(config); + } else { + requireStandardServer = true; + } + } + + if (requireStandardServer) { + void startStandardServer(context, requirements, clientOptions, workspacePath); + } + + context.subscriptions.push(commands.registerCommand(Commands.EXECUTE_WORKSPACE_COMMAND, async (command, ...rest) => { const api: ExtensionAPI = apiManager.getApiInstance(); if (api.serverMode === ServerMode.lightWeight) { console.warn(`The command: ${command} is not supported in LightWeight mode. See: https://github.com/redhat-developer/vscode-java/issues/1480`); @@ -485,28 +543,18 @@ async function postExtensionStartInit( command, arguments: commandArgs }; + const client: LanguageClient | undefined = await getStandardLanguageClient(); + if (!client) { + console.warn(`Cannot execute Java workspace command '${command}' because the Java language client is not initialized`); + return; + } if (token) { - return standardClient.getClient().sendRequest(ExecuteCommandRequest.type, params, token); + return client.sendRequest(ExecuteCommandRequest.type, params, token); } else { - return standardClient.getClient().sendRequest(ExecuteCommandRequest.type, params); + return client.sendRequest(ExecuteCommandRequest.type, params); } })); - if (cleanWorkspaceExists) { - const data = {}; - try { - cleanupLombokCache(context); - cleanupWorkspaceState(context); - deleteDirectory(workspacePath); - deleteDirectory(syntaxServerWorkspacePath); - cleanJavaLSConfiguration(context); - } catch (error) { - data['error'] = getMessage(error); - window.showErrorMessage(`Failed to delete ${workspacePath}: ${error}`); - } - await Telemetry.sendTelemetry(Commands.CLEAN_WORKSPACE, data); - } - // Register commands here to make it available even when the language client fails context.subscriptions.push(commands.registerCommand(Commands.OPEN_STATUS_SHORTCUT, async (status: string) => { const items: ShortcutQuickPickItem[] = []; @@ -672,37 +720,6 @@ async function postExtensionStartInit( registerClientProviders(context, { contentProviderEvent: jdtEventEmitter.event }); - apiManager.getApiInstance().onDidServerModeChange((event: ServerMode) => { - if (event === ServerMode.standard) { - syntaxClient.stop(); - fileEventHandler.setServerStatus(true); - languageStatusBarProvider.initialize(context); - } - commands.executeCommand('setContext', 'java:serverMode', event); - }); - - if (serverMode === ServerMode.hybrid && !await fse.pathExists(path.join(workspacePath, ".metadata", ".plugins"))) { - const config = getJavaConfiguration(); - const importOnStartupSection: string = "project.importOnFirstTimeStartup"; - const importOnStartup = config.get(importOnStartupSection); - if (importOnStartup === "disabled" || - env.uiKind === UIKind.Web && env.appName.includes("Visual Studio Code")) { - apiManager.getApiInstance().serverMode = ServerMode.lightWeight; - apiManager.fireDidServerModeChange(ServerMode.lightWeight); - requireStandardServer = false; - } else if (importOnStartup === "interactive" && await workspaceContainsBuildFiles()) { - apiManager.getApiInstance().serverMode = ServerMode.lightWeight; - apiManager.fireDidServerModeChange(ServerMode.lightWeight); - requireStandardServer = await promptUserForStandardServer(config); - } else { - requireStandardServer = true; - } - } - - if (requireStandardServer) { - await startStandardServer(context, requirements, clientOptions, workspacePath); - } - const onDidGrantWorkspaceTrust = (workspace as any).onDidGrantWorkspaceTrust; if (onDidGrantWorkspaceTrust !== undefined) { // keep compatibility for old engines < 1.56.0 context.subscriptions.push(onDidGrantWorkspaceTrust(() => { @@ -725,16 +742,34 @@ async function postExtensionStartInit( } context.subscriptions.push(workspace.onDidChangeTextDocument(event => handleTextDocumentChanges(event.document, event.contentChanges))); } -async function startStandardServer(context: ExtensionContext, requirements: requirements.RequirementsData, clientOptions: LanguageClientOptions, workspacePath: string, triggeredByCommand: boolean = false) { +async function startStandardServer( + context: ExtensionContext, + requirements: requirements.RequirementsData, + clientOptions: LanguageClientOptions, + workspacePath: string, + triggeredByCommand: boolean = false +): Promise { + if (standardServerStart) { + const client = await standardServerStart; + if (client || !triggeredByCommand) { + return client; + } + } if (standardClient.getClientStatus() !== ClientStatus.uninitialized) { - return; + return standardClient.getClient(); } + standardServerStart = doStartStandardServer(context, requirements, clientOptions, workspacePath, triggeredByCommand).finally(() => { + standardServerStart = undefined; + }); + return standardServerStart; +} +async function doStartStandardServer(context: ExtensionContext, requirements: requirements.RequirementsData, clientOptions: LanguageClientOptions, workspacePath: string, triggeredByCommand: boolean = false): Promise { const selector: BuildFileSelector = new BuildFileSelector(context, []); const importMode: ImportMode = await getImportMode(context, selector); if (importMode === ImportMode.automatic) { if (!await ensureNoBuildToolConflicts(context, clientOptions)) { - return; + return undefined; } } else { const buildFiles: string[] = []; @@ -749,7 +784,7 @@ async function startStandardServer(context: ExtensionContext, requirements: requ if (buildFiles.length === 0) { commands.executeCommand('setContext', 'java:serverMode', ServerMode.lightWeight); serverStatusBarProvider.showNotImportedStatus(); - return; + return undefined; } clientOptions.initializationOptions.projectConfigurations = buildFiles; } @@ -764,6 +799,7 @@ async function startStandardServer(context: ExtensionContext, requirements: requ standardClient.registerLanguageClientActions(context, await fse.pathExists(path.join(workspacePath, ".metadata", ".plugins")), jdtEventEmitter); }); serverStatusBarProvider.setBusy("Activating..."); + return standardClient.getClient(); } async function workspaceContainsBuildFiles(): Promise { From da8361abb113f9b87e384cdc690adda614678099 Mon Sep 17 00:00:00 2001 From: Morgan Chang Date: Mon, 13 Jul 2026 12:08:59 -0400 Subject: [PATCH 2/2] remove inaccurate comment Signed-off-by: Morgan Chang --- src/extension.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index aa5ac224c..8679e85e9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -555,7 +555,6 @@ async function postExtensionStartInit( } })); - // Register commands here to make it available even when the language client fails context.subscriptions.push(commands.registerCommand(Commands.OPEN_STATUS_SHORTCUT, async (status: string) => { const items: ShortcutQuickPickItem[] = []; if (status === ServerStatusKind.error || status === ServerStatusKind.warning) {