diff --git a/browser-app/package.json b/browser-app/package.json index b3b2b3440..212412a7e 100644 --- a/browser-app/package.json +++ b/browser-app/package.json @@ -8,7 +8,10 @@ "config": { "applicationName": "Theia-Trace Example Application", "preferences": { - "editor.autoSave": "on" + "editor.autoSave": "on", + "trace-viewer.path" : "/trace-compass-server/tracecompass-server", + "trace-viewer.port" : 8080 + } } } diff --git a/electron-app/package.json b/electron-app/package.json index cc4b70041..df4786251 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -17,7 +17,9 @@ "config": { "applicationName": "Theia-Trace Example Application", "preferences": { - "editor.autoSave": "on" + "editor.autoSave": "on", + "trace-viewer.path" : "/trace-compass-server/tracecompass-server", + "trace-viewer.port" : 8080 } } } diff --git a/viewer-prototype/package.json b/viewer-prototype/package.json index 5251c3d92..810a6e692 100644 --- a/viewer-prototype/package.json +++ b/viewer-prototype/package.json @@ -1,49 +1,49 @@ { - "name": "theia-trace-viewer", - "private": "true", - "version": "0.0.0", - "description": "Trace Compass trace viewer Theia Extension", - "keywords": [ - "theia-extension" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/theia-ide/theia-trace-extension" - }, - "files": [ - "lib", - "src" - ], - "dependencies": { - "@trace-viewer/base": "0.0.0", - "@trace-viewer/react-components": "0.0.0", - "@theia/core": "latest", - "@theia/editor": "latest", - "@theia/filesystem": "latest" - }, - "devDependencies": { - "@typescript-eslint/eslint-plugin": "^3.4.0", - "@typescript-eslint/parser": "^3.4.0", - "eslint": "^7.3.0", - "eslint-plugin-import": "^2.21.2", - "eslint-plugin-no-null": "^1.0.2", - "eslint-plugin-react": "^7.20.0", - "rimraf": "latest", - "typescript": "latest" - }, - "scripts": { - "build": "tsc", - "clean": "rimraf lib", - "lint": "eslint .", - "prepare": "yarn run clean && yarn run build", - "test": "echo 'test'", - "watch": "tsc -w" - }, - "theiaExtensions": [ - { - "frontend": "lib/browser/trace-viewer/trace-viewer-frontend-module" - } - ] - } - + "name": "theia-trace-viewer", + "private": "true", + "version": "0.0.0", + "description": "Trace Compass trace viewer Theia Extension", + "keywords": [ + "theia-extension" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/theia-ide/theia-trace-extension" + }, + "files": [ + "lib", + "src" + ], + "dependencies": { + "@trace-viewer/base": "0.0.0", + "@trace-viewer/react-components": "0.0.0", + "@theia/core": "latest", + "@theia/editor": "latest", + "@theia/filesystem": "latest" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^3.4.0", + "@typescript-eslint/parser": "^3.4.0", + "eslint": "^7.3.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-react": "^7.20.0", + "rimraf": "latest", + "typescript": "latest" + }, + "scripts": { + "build": "tsc", + "clean": "rimraf lib", + "lint": "eslint .", + "prepare": "yarn run clean && yarn run build", + "test": "echo 'test'", + "watch": "tsc -w" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/trace-viewer/trace-viewer-frontend-module", + "backend": "lib/node/trace-server-backend-module" + } + ] +} \ No newline at end of file diff --git a/viewer-prototype/src/browser/trace-server-bindings.ts b/viewer-prototype/src/browser/trace-server-bindings.ts new file mode 100644 index 000000000..7d6118faf --- /dev/null +++ b/viewer-prototype/src/browser/trace-server-bindings.ts @@ -0,0 +1,15 @@ +import { interfaces } from 'inversify'; +import { PreferenceService, createPreferenceProxy, PreferenceContribution } from '@theia/core/lib/browser'; +import { TracePreferences, ServerSchema } from './trace-server-preference'; + +export function bindTraceServerPreferences(bind: interfaces.Bind): void { + bind(TracePreferences).toDynamicValue(ctx => { + const preferences = ctx.container.get(PreferenceService); + return createPreferenceProxy(preferences, ServerSchema); + }).inSingletonScope(); + + bind(PreferenceContribution).toConstantValue({ + schema: ServerSchema, + }); + +} diff --git a/viewer-prototype/src/browser/trace-server-preference.ts b/viewer-prototype/src/browser/trace-server-preference.ts new file mode 100644 index 000000000..0853f45a5 --- /dev/null +++ b/viewer-prototype/src/browser/trace-server-preference.ts @@ -0,0 +1,30 @@ +import { PreferenceSchema, PreferenceProxy, PreferenceScope } from '@theia/core/lib/browser'; + +export const TRACE_PATH = 'trace-viewer.path'; +export const TRACE_PORT = 'trace-viewer.port'; + +export const ServerSchema: PreferenceSchema = { + type: 'object', + properties: { + [TRACE_PATH]: { + 'type': 'string', + 'default': '', + 'description': 'The path to trace-server executable, e.g.: /usr/bin/', + }, + [TRACE_PORT]: { + 'type': 'number', + 'default': '', + 'description': 'Specify the port on which you want to execute the server.', + } + + }, + scope: PreferenceScope.Folder, +}; + +interface TracePreferenceContribution { + [TRACE_PATH]: string; + [TRACE_PORT]: number; +} + +export const TracePreferences = Symbol('TracePreferences'); +export type TracePreferences = PreferenceProxy; diff --git a/viewer-prototype/src/browser/trace-server-status.ts b/viewer-prototype/src/browser/trace-server-status.ts index 3ef554503..046ad0ddf 100644 --- a/viewer-prototype/src/browser/trace-server-status.ts +++ b/viewer-prototype/src/browser/trace-server-status.ts @@ -12,6 +12,9 @@ import { Disposable, DisposableCollection } from '@theia/core/lib//common'; import { ConnectionStatusService, ConnectionStatus, AbstractConnectionStatusService } from '@theia/core/lib/browser/connection-status-service'; import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; import { TspClientProvider } from './tsp-client-provider'; +import { TraceServerConfigService } from '../common/trace-server-config'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { TRACE_PATH, TRACE_PORT } from './trace-server-preference'; @injectable() export class TraceServerConnectionStatusService extends AbstractConnectionStatusService { @@ -52,6 +55,20 @@ export class TraceServerConnectionStatusService extends AbstractConnectionStatus @injectable() export class TraceServerConnectionStatusContribution extends DefaultFrontendApplicationContribution { + @inject(PreferenceService) protected readonly preferenceService: PreferenceService; + + private path: string | undefined; + private port: number | undefined; + + @postConstruct() + async init(): Promise { + + this.path = this.preferenceService.get(TRACE_PATH); + this.port = this.preferenceService.get(TRACE_PORT); + } + + @inject(TraceServerConfigService) protected readonly traceServerConfigService: TraceServerConfigService; + protected readonly toDisposeOnOnline = new DisposableCollection(); constructor( @@ -61,6 +78,9 @@ export class TraceServerConnectionStatusContribution extends DefaultFrontendAppl ) { super(); this.connectionStatusService.onStatusChange(state => this.onStateChange(state)); + if (this.connectionStatusService.currentStatus === ConnectionStatus.ONLINE) { + this.handleOnline(); + } } protected onStateChange(state: ConnectionStatus): void { @@ -80,15 +100,34 @@ export class TraceServerConnectionStatusContribution extends DefaultFrontendAppl protected handleOnline(): void { this.toDisposeOnOnline.dispose(); + this.statusBar.setElement(this.statusbarId, { + alignment: StatusBarAlignment.LEFT, + text: '$(fas fa-stop) Stop trace server', + tooltip: 'Click here to stop the trace server', + priority: 5003, + onclick: this.stopServer.bind(this) + }); + + } + + private async startServer() { + await this.traceServerConfigService.startTraceServer(this.path, this.port); + } + + private async stopServer() { + await this.traceServerConfigService.stopTraceServer(this.port); } protected handleOffline(): void { + this.toDisposeOnOnline.dispose(); this.statusBar.setElement(this.statusbarId, { alignment: StatusBarAlignment.LEFT, - text: 'Trace Server Offline', - tooltip: 'Cannot connect to trace server.', - priority: 5000 + text: '$(fas fa-play) Start trace server', + tooltip: 'Click here to start the trace server', + priority: 5001, + onclick: this.startServer.bind(this) }); + this.toDisposeOnOnline.push(Disposable.create(() => this.statusBar.removeElement(this.statusbarId))); document.body.classList.add('traceserver-mod-offline'); this.toDisposeOnOnline.push(Disposable.create(() => document.body.classList.remove('traceserver-mod-offline'))); diff --git a/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts b/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts index e7ea9fc76..b350b8bde 100644 --- a/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts +++ b/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts @@ -1,18 +1,23 @@ import { inject, injectable } from 'inversify'; import { TraceViewerEnvironment } from '../common/trace-viewer-environment'; -import { TraceServerUrlProvider, TRACE_SERVER_DEFAULT_URL } from '../common/trace-server-url-provider'; -import { FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser'; +import { TraceServerUrlProvider, TRACE_SERVER_DEFAULT_URL, TRACE_SERVER_DEFAULT_PORT } from '../common/trace-server-url-provider'; +import { FrontendApplicationContribution, FrontendApplication, PreferenceService } from '@theia/core/lib/browser'; +import { TRACE_PORT } from './trace-server-preference'; @injectable() export class TraceServerUrlProviderImpl implements TraceServerUrlProvider, FrontendApplicationContribution { protected _traceServerUrl: string; protected _listeners: ((url: string) => void)[]; + private port: string|undefined; constructor( - @inject(TraceViewerEnvironment) protected readonly traceViewerEnvironment: TraceViewerEnvironment + @inject(TraceViewerEnvironment) protected readonly traceViewerEnvironment: TraceViewerEnvironment, + @inject(PreferenceService) protected readonly preferenceService: PreferenceService + ) { - this._traceServerUrl = TRACE_SERVER_DEFAULT_URL; + this.port = this.preferenceService.get(TRACE_PORT); + this._traceServerUrl = TRACE_SERVER_DEFAULT_URL.replace(/{}/g,this.port?this.port:TRACE_SERVER_DEFAULT_PORT); this._listeners = []; } diff --git a/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts b/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts index ab79e981c..2f2cf8d4f 100644 --- a/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts +++ b/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts @@ -1,5 +1,5 @@ import { ContainerModule, Container } from 'inversify'; -import { WidgetFactory, OpenHandler, FrontendApplicationContribution, bindViewContribution } from '@theia/core/lib/browser'; +import { WidgetFactory, OpenHandler, FrontendApplicationContribution, bindViewContribution, WebSocketConnectionProvider } from '@theia/core/lib/browser'; import { TraceViewerWidget, TraceViewerWidgetOptions } from './trace-viewer'; import { TraceViewerContribution } from './trace-viewer-contribution'; import { TraceViewerEnvironment } from '../../common/trace-viewer-environment'; @@ -10,16 +10,14 @@ import 'ag-grid-community/dist/styles/ag-grid.css'; import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; -// import 'semantic-ui-css/semantic.min.css'; import { TraceExplorerContribution } from '../trace-explorer/trace-explorer-contribution'; import { TRACE_EXPLORER_ID, TraceExplorerWidget } from '../trace-explorer/trace-explorer-widget'; import { TspClientProvider } from '../tsp-client-provider'; import { TheiaMessageManager } from '../theia-message-manager'; import { TraceServerConnectionStatusService, TraceServerConnectionStatusContribution } from '../../browser/trace-server-status'; import { TraceServerUrlProviderImpl } from '../trace-server-url-provider-frontend-impl'; -// import { TracePropertiesContribution } from '../trace-properties-view/trace-properties-view-contribution'; -// import { TracePropertiesWidget, TRACE_PROPERTIES_ID } from '../trace-properties-view/trace-properties-view-widget'; - +import { bindTraceServerPreferences } from '../trace-server-bindings'; +import { TraceServerConfigService, traceServerPath } from '../../common/trace-server-config'; export default new ContainerModule(bind => { bind(TraceViewerEnvironment).toSelf().inRequestScope(); @@ -53,11 +51,18 @@ export default new ContainerModule(bind => { createWidget: () => context.container.get(TraceExplorerWidget) })); + bind(TraceServerConfigService).toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy(traceServerPath); + }).inSingletonScope(); + bind(TraceServerConnectionStatusService).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(TraceServerConnectionStatusService); bind(TraceServerConnectionStatusContribution).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(TraceServerConnectionStatusContribution); + bindTraceServerPreferences(bind); + // bindViewContribution(bind, TracePropertiesContribution); // bind(TracePropertiesWidget).toSelf(); // bind(WidgetFactory).toDynamicValue(context => ({ diff --git a/viewer-prototype/src/common/trace-server-config.ts b/viewer-prototype/src/common/trace-server-config.ts new file mode 100644 index 000000000..87197324e --- /dev/null +++ b/viewer-prototype/src/common/trace-server-config.ts @@ -0,0 +1,15 @@ + +export const traceServerPath = '/services/trace-server-config'; +export const TraceServerConfigService = Symbol('TraceServerConfigService'); +export interface TraceServerConfigService { + /** + * Spawn the trace server from a given path + */ + startTraceServer(path: string | undefined, port: number | undefined): Promise; + + /** + * Stop the trace server + */ + stopTraceServer(port: number | undefined): Promise; +} + diff --git a/viewer-prototype/src/common/trace-server-url-provider.ts b/viewer-prototype/src/common/trace-server-url-provider.ts index bf5666954..2b90f4dc2 100644 --- a/viewer-prototype/src/common/trace-server-url-provider.ts +++ b/viewer-prototype/src/common/trace-server-url-provider.ts @@ -1,5 +1,7 @@ export const TraceServerUrlProvider = Symbol('TraceServerUrlProvider'); -export const TRACE_SERVER_DEFAULT_URL = 'http://localhost:8080/tsp/api'; +export const TRACE_SERVER_DEFAULT_URL = 'http://localhost:{}/tsp/api'; +export const TRACE_SERVER_DEFAULT_PORT = '8080'; + export interface TraceServerUrlProvider { /** diff --git a/viewer-prototype/src/common/trace-viewer-environment.ts b/viewer-prototype/src/common/trace-viewer-environment.ts index da6a4f393..73fdac9d7 100644 --- a/viewer-prototype/src/common/trace-viewer-environment.ts +++ b/viewer-prototype/src/common/trace-viewer-environment.ts @@ -1,13 +1,18 @@ import { injectable, inject } from 'inversify'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; -import { TRACE_SERVER_DEFAULT_URL } from './trace-server-url-provider'; +import { TRACE_SERVER_DEFAULT_URL, TRACE_SERVER_DEFAULT_PORT } from './trace-server-url-provider'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { TRACE_PORT } from '../browser/trace-server-preference'; @injectable() export class TraceViewerEnvironment { + private port: string | undefined; constructor( - @inject(EnvVariablesServer) protected readonly environments: EnvVariablesServer) { + @inject(EnvVariablesServer) protected readonly environments: EnvVariablesServer, + @inject(PreferenceService) protected readonly preferenceService: PreferenceService) { + this.port = this.preferenceService.get(TRACE_PORT); } protected _traceServerUrl: string | undefined; @@ -16,7 +21,7 @@ export class TraceViewerEnvironment { const traceServerUrl = await this.environments.getValue('TRACE_SERVER_URL'); this._traceServerUrl = traceServerUrl ? this.parseUrl(traceServerUrl.value || TRACE_SERVER_DEFAULT_URL) : TRACE_SERVER_DEFAULT_URL; } - return this._traceServerUrl; + return this._traceServerUrl.replace(/{}/g, this.port ? this.port : TRACE_SERVER_DEFAULT_PORT); } private parseUrl(url: string): string { diff --git a/viewer-prototype/src/node/index.ts b/viewer-prototype/src/node/index.ts new file mode 100644 index 000000000..81103d901 --- /dev/null +++ b/viewer-prototype/src/node/index.ts @@ -0,0 +1,2 @@ +export * from './trace-server-backend-module'; +export * from './trace-server-service'; diff --git a/viewer-prototype/src/node/trace-server-backend-module.ts b/viewer-prototype/src/node/trace-server-backend-module.ts new file mode 100644 index 000000000..834ef6136 --- /dev/null +++ b/viewer-prototype/src/node/trace-server-backend-module.ts @@ -0,0 +1,16 @@ +import { ContainerModule } from 'inversify'; +import { TraceServerServiceImpl } from './trace-server-service'; +import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core/lib/common'; +import { traceServerPath } from '../common/trace-server-config'; +import { TraceServerConfigService } from '../common/trace-server-config'; + +export default new ContainerModule(bind => { + bind(TraceServerServiceImpl).toSelf().inSingletonScope(); + + bind(ConnectionHandler) + .toDynamicValue(ctx => new JsonRpcConnectionHandler( + traceServerPath, + () => ctx.container.get(TraceServerServiceImpl), + )).inSingletonScope(); + +}); diff --git a/viewer-prototype/src/node/trace-server-service.ts b/viewer-prototype/src/node/trace-server-service.ts new file mode 100644 index 000000000..0e2ea22d4 --- /dev/null +++ b/viewer-prototype/src/node/trace-server-service.ts @@ -0,0 +1,14 @@ +import { spawn, exec } from 'child_process'; +import { injectable } from 'inversify'; +import { TraceServerConfigService } from '../common/trace-server-config'; +@injectable() +export class TraceServerServiceImpl implements TraceServerConfigService { + + async startTraceServer(path: string | undefined, port: number | undefined): Promise { + spawn(`..${path}`, ['-vmargs', `-Dtraceserver.port=${port}`]); + } + + async stopTraceServer(port: number | undefined): Promise { + exec(`kill -9 $(lsof -t -i:${port})`); // FIXME: Better approach to kill the server at a given port + } +} diff --git a/viewer-prototype/tsconfig.json b/viewer-prototype/tsconfig.json index d1aaf9bc8..b2f6acaf6 100644 --- a/viewer-prototype/tsconfig.json +++ b/viewer-prototype/tsconfig.json @@ -9,7 +9,7 @@ "moduleResolution": "node", "skipLibCheck": true, "esModuleInterop": true, - "target": "ESNEXT", + "target": "es5", "jsx": "react", "lib": [ "es6", @@ -18,7 +18,11 @@ "sourceMap": true, "rootDir": "src", "outDir": "lib", - "types": ["node", "jest"] + "types": [ + "node", + "jest" + ], + "strictPropertyInitialization": false }, "include": [ "src" @@ -28,4 +32,4 @@ "**/*.spec.ts", "**/*.test.ts" ] -} +} \ No newline at end of file