From b2c8f771b0517c87d370b94502ae3f5e78b4bea9 Mon Sep 17 00:00:00 2001 From: Alex Doan Date: Wed, 6 Sep 2023 11:58:47 -0400 Subject: [PATCH] Added external API to contribute start/stop trace server implementation Added new external api for trace server startup and shutdown The new API allows adopting extension to register server startup handler and shutdown handlers and a validator function to check whether a trace file at given location is applicable Fixes #159 Signed-off-by: Alex Doan --- vscode-trace-extension/src/extension.ts | 20 +++-- .../src/external-api/external-api.ts | 13 ++- .../src/utils/trace-server-manager.ts | 84 +++++++++++++++++++ 3 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 vscode-trace-extension/src/utils/trace-server-manager.ts diff --git a/vscode-trace-extension/src/extension.ts b/vscode-trace-extension/src/extension.ts index f5feb55e..117fe514 100644 --- a/vscode-trace-extension/src/extension.ts +++ b/vscode-trace-extension/src/extension.ts @@ -22,9 +22,11 @@ import { VSCODE_MESSAGES } from 'vscode-trace-common/lib/messages/vscode-message import { TraceViewerPanel } from './trace-viewer-panel/trace-viewer-webview-panel'; import { TspClientProvider } from 'vscode-trace-common/lib/client/tsp-client-provider-impl'; import { TraceServerUrlProvider } from 'vscode-trace-common/lib/server/trace-server-url-provider'; +import { TraceServerManager } from './utils/trace-server-manager'; export let traceLogger: TraceExtensionLogger; export const traceExtensionWebviewManager: TraceExtensionWebviewManager = new TraceExtensionWebviewManager(); +export const traceServerManager: TraceServerManager = new TraceServerManager(); const tspClientProvider = new TspClientProvider(getTspClientUrl(), undefined, new TraceServerUrlProvider()); export function activate(context: vscode.ExtensionContext): ExternalAPI { @@ -70,8 +72,8 @@ export function activate(context: vscode.ExtensionContext): ExternalAPI { // TODO: For now, a different command opens traces from file explorer. Remove when we have a proper trace finder const fileOpenHandler = fileHandler(analysisProvider); context.subscriptions.push( - vscode.commands.registerCommand('traces.openTraceFile', async file => { - await startTraceServerIfAvailable(); + vscode.commands.registerCommand('traces.openTraceFile', async (file: vscode.Uri) => { + await startTraceServerIfAvailable(file.fsPath); if (await isUp()) { fileOpenHandler(context, file); } @@ -187,15 +189,23 @@ export function activate(context: vscode.ExtensionContext): ExternalAPI { return traceExtensionAPI; } -export function deactivate(): void { +export async function deactivate(): Promise { + await traceServerManager.stopServer(); + traceServerManager.dispose(); traceLogger.disposeChannel(); traceExtensionWebviewManager.dispose(); } -async function startTraceServerIfAvailable(): Promise { +async function startTraceServerIfAvailable(pathToTrace?: string): Promise { const extensionId = 'vscode-trace-server'; + if (await isUp()) { + return; + } + if (pathToTrace) { + await traceServerManager.startServer(pathToTrace); + } const traceServerExtension = vscode.extensions.getExtension('tracecompass-community.' + extensionId); - if (!traceServerExtension || (await isUp())) { + if (!traceServerExtension) { return; } await vscode.commands.executeCommand(extensionId + '.start-if-stopped'); diff --git a/vscode-trace-extension/src/external-api/external-api.ts b/vscode-trace-extension/src/external-api/external-api.ts index ce161651..b68d8838 100644 --- a/vscode-trace-extension/src/external-api/external-api.ts +++ b/vscode-trace-extension/src/external-api/external-api.ts @@ -6,7 +6,8 @@ import { Experiment } from 'tsp-typescript-client/lib/models/experiment'; import { TraceViewerPanel } from '../trace-viewer-panel/trace-viewer-webview-panel'; import * as vscode from 'vscode'; -import { traceExtensionWebviewManager } from '../extension'; +import { traceExtensionWebviewManager, traceServerManager } from '../extension'; +import { TraceServerContributor } from '../utils/trace-server-manager'; export interface ExternalAPI { getActiveExperiment(): Experiment | undefined; @@ -14,6 +15,7 @@ export interface ExternalAPI { getActiveWebviews(): vscode.WebviewView[]; onWebviewCreated(listener: (data: vscode.WebviewView) => void): void; onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => void): void; + addTraceServerContributor(contributor: TraceServerContributor): void; } export const traceExtensionAPI: ExternalAPI = { @@ -60,5 +62,14 @@ export const traceExtensionAPI: ExternalAPI = { */ onWebviewPanelCreated(listener: (data: vscode.WebviewPanel) => void): void { traceExtensionWebviewManager.onWebviewPanelCreated(listener); + }, + + /** + * Registers a server contributor + * + * @param contributor Contributor object that contains startServer, stopServer handlers and a traceValidator + */ + addTraceServerContributor(contributor: TraceServerContributor): void { + traceServerManager.addTraceServerContributor(contributor); } }; diff --git a/vscode-trace-extension/src/utils/trace-server-manager.ts b/vscode-trace-extension/src/utils/trace-server-manager.ts new file mode 100644 index 00000000..0e48b099 --- /dev/null +++ b/vscode-trace-extension/src/utils/trace-server-manager.ts @@ -0,0 +1,84 @@ +/*************************************************************************************** + * Copyright (c) 2023 BlackBerry Limited and others. + * + * Licensed under the MIT license. See LICENSE file in the project root for details. + ***************************************************************************************/ +export interface TraceServerContributor { + startServer: () => Promise; + stopServer?: () => Promise; + isApplicable?: (pathToTrace: string) => boolean; +} + +export class TraceServerManager { + private traceServersContributors: TraceServerContributor[] = []; + private isManagerDisposed = false; + private indexOfTraceServerContributor = -1; + + /** + * Add new contributor (groups of functions that runs on adopting extension to start/stop its trace server) to the manager + * + * @param contributor Contributor object that contains startServer, stopServer handlers and a traceValidator + */ + addTraceServerContributor(contributor: TraceServerContributor): void { + if (!this.isDisposed()) { + this.traceServersContributors.push(contributor); + } + } + + /** + * Look for appropriate startServer handler and execute it, also assign the index of current contributor + * + * @param pathToTrace path to trace file + */ + async startServer(pathToTrace: string): Promise { + this.indexOfTraceServerContributor = -1; + if (!this.isDisposed()) { + // find an adopting extension that has successfully validated the trace + let index = this.traceServersContributors.findIndex( + traceServerContributor => traceServerContributor.isApplicable?.(pathToTrace) + ); + if (index === -1) { + // find an adopting extension with no validator + index = this.traceServersContributors.findIndex( + traceServerContributor => !traceServerContributor.isApplicable + ); + } + // if found + if (index !== -1) { + await this.traceServersContributors[index].startServer(); + this.indexOfTraceServerContributor = index; + return; + } + } + } + + /** + * execute server stopping handler + */ + async stopServer(): Promise { + if (this.indexOfTraceServerContributor !== -1) { + await this.traceServersContributors[this.indexOfTraceServerContributor].stopServer?.(); + this.indexOfTraceServerContributor = -1; + } + } + + /** + * remove all contributors, set manager to disposed status + */ + dispose(): void { + if (!this.isDisposed()) { + this.traceServersContributors = []; + this.isManagerDisposed = true; + this.indexOfTraceServerContributor = -1; + } + } + + /** + * whether manager is disposed + * + * @returns disposed status + */ + isDisposed(): boolean { + return this.isManagerDisposed; + } +}