Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/daemon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { promisify } from "util";
import * as vscode from "vscode";
import { sendError } from "vscode-extension-telemetry-wrapper";
import { sendError, sendInfo } from "vscode-extension-telemetry-wrapper";
import { LSDaemon } from "./daemon";

const delay = promisify(setTimeout);
Expand All @@ -25,12 +25,19 @@ async function checkJavaExtActivated(_context: vscode.ExtensionContext): Promise
return false;
}

vscode.workspace.onDidGrantWorkspaceTrust(() => {
checkIfJavaServerCrashed(30 * 1000 /*ms*/);
});

// wait javaExt to activate
const timeout = 30 * 60 * 1000; // wait 30 min at most
let count = 0;
while(!javaExt.isActive && count < timeout) {
await delay(1000);
count += 1000;
if (count % 10000 === 0) {
checkIfJavaServerCrashed();
}
}

if (!javaExt.isActive) {
Expand All @@ -41,6 +48,10 @@ async function checkJavaExtActivated(_context: vscode.ExtensionContext): Promise

// on ServiceReady
javaExt.exports.onDidServerModeChange(async (mode: string) => {
if (mode === "Hybrid") { // begin to start standard language server
checkIfJavaServerCrashed(30 * 1000 /*ms*/);
}

if (mode === "Standard") {
daemon.logWatcher.sendStartupMetadata("jdtls standard server ready");

Expand All @@ -57,3 +68,35 @@ async function checkJavaExtActivated(_context: vscode.ExtensionContext): Promise

return true;
}

let corruptedCacheDetected: boolean = false;
async function checkIfJavaServerCrashed(wait: number = 0/*ms*/) {
if (corruptedCacheDetected) {
return;
}

// wait Java Language Server to start
if (wait) {
await delay(wait);
}

const corruptedCache = !await daemon.processWatcher.start() && await daemon.logWatcher.checkIfWorkspaceCorrupted();
if (!corruptedCacheDetected && corruptedCache) {
corruptedCacheDetected = true;
sendInfo("", {
name: "corrupted-cache",
});
const ans = await vscode.window.showErrorMessage("Java extension cannot start due to corrupted workspace cache, please try to clean the workspace.",
"Clean and Restart", "Later");
if (ans === "Clean and Restart") {
sendInfo("", {
name: "clean-cache-action",
});
vscode.commands.executeCommand("java.clean.workspace", true);
} else {
sendInfo("", {
name: "clean-cache-cancel-action",
});
}
}
}
7 changes: 7 additions & 0 deletions src/daemon/serverLog/logUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const MESSAGE_BUILD_JOBS_FINISHED = "!MESSAGE >> build jobs finished";

const STACK_INDICATOR = `${EOL}!STACK `;
const MESSAGE_INDICATOR = `${EOL}!MESSAGE `;
const CORRUPTED_WORKSPACE_INDICATOR = "Caused by: org.eclipse.core.internal.dtree.ObjectNotFoundException:";

export async function logsForLatestSession(logFilepath: string): Promise<string> {
const content = await fs.promises.readFile(logFilepath, { encoding: 'utf-8' });
Expand Down Expand Up @@ -135,6 +136,12 @@ export function parseTimestamp(entry: string): LogEntry {
}
}

export function containsCorruptedException(log: string): boolean {
const lines = log.split(`${EOL}`);
const find = lines.find(line => line.startsWith(CORRUPTED_WORKSPACE_INDICATOR));
return !!find;
}

function getMessage(entry: string) {
const start = entry.indexOf(MESSAGE_INDICATOR);
if (start < 0) { return ""; }
Expand Down
11 changes: 10 additions & 1 deletion src/daemon/serverLog/logWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as path from "path";
import * as vscode from "vscode";
import { sendInfo } from "vscode-extension-telemetry-wrapper";
import { LSDaemon } from "../daemon";
import { collectErrors, collectErrorsSince, logsForLatestSession, sessionMetadata } from "./logUtils";
import { collectErrors, collectErrorsSince, containsCorruptedException, logsForLatestSession, sessionMetadata } from "./logUtils";
import { toElapsed } from "./utils";
import { redact } from "./whitelist";

Expand Down Expand Up @@ -122,4 +122,13 @@ export class LogWatcher {
}
}
}

public async checkIfWorkspaceCorrupted(): Promise<boolean> {
if (this.serverLogUri) {
const logs = await logsForLatestSession(path.join(this.serverLogUri?.fsPath, ".log"));
return containsCorruptedException(logs);
}

return false;
}
}