@eclipse-glsp/protocol
Version:
The protocol definition for client-server communication in GLSP
198 lines • 8.63 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonrpcClientProxy = exports.BaseJsonrpcGLSPClient = void 0;
/********************************************************************************
* Copyright (c) 2019-2024 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
const sprotty_protocol_1 = require("sprotty-protocol");
const event_1 = require("../../utils/event");
const glsp_client_1 = require("../glsp-client");
const glsp_jsonrpc_client_1 = require("./glsp-jsonrpc-client");
class BaseJsonrpcGLSPClient {
get onServerInitialized() {
return this.onServerInitializedEmitter.event;
}
get onActionMessageNotification() {
return this.onActionMessageNotificationEmitter.event;
}
get onCurrentStateChanged() {
return this.onCurrentStateChangedEmitter.event;
}
set state(state) {
if (this._state !== state) {
this._state = state;
this.onCurrentStateChangedEmitter.fire(state);
}
}
get state() {
return this._state;
}
get initializeResult() {
return this._initializeResult;
}
constructor(options) {
this.onServerInitializedEmitter = new event_1.Emitter();
this.onActionMessageNotificationEmitter = new event_1.Emitter();
this.onCurrentStateChangedEmitter = new event_1.Emitter();
this.connectionProvider = options.connectionProvider;
this.state = glsp_client_1.ClientState.Initial;
}
async start() {
if (this.state === glsp_client_1.ClientState.Running || this.state === glsp_client_1.ClientState.StartFailed) {
return;
}
else if (this.state === glsp_client_1.ClientState.Starting) {
await event_1.Event.waitUntil(this.onCurrentStateChanged, state => state === glsp_client_1.ClientState.Running || state === glsp_client_1.ClientState.StartFailed);
return;
}
try {
this.state = glsp_client_1.ClientState.Starting;
const connection = await this.resolveConnection();
connection.listen();
this.resolvedConnection = connection;
this.state = glsp_client_1.ClientState.Running;
}
catch (error) {
glsp_jsonrpc_client_1.JsonrpcGLSPClient.error('Failed to start connection to server', error);
this.state = glsp_client_1.ClientState.StartFailed;
}
}
async initializeServer(params) {
if (this.initializeResult) {
return this.initializeResult;
}
else if (this.pendingServerInitialize) {
return this.pendingServerInitialize;
}
const initializeDeferred = new sprotty_protocol_1.Deferred();
try {
this.pendingServerInitialize = initializeDeferred.promise;
this._initializeResult = await this.checkedConnection.sendRequest(glsp_jsonrpc_client_1.JsonrpcGLSPClient.InitializeRequest, params);
this.onServerInitializedEmitter.fire(this._initializeResult);
initializeDeferred.resolve(this._initializeResult);
this.pendingServerInitialize = undefined;
}
catch (error) {
initializeDeferred.reject(error);
this._initializeResult = undefined;
this.pendingServerInitialize = undefined;
}
return initializeDeferred.promise;
}
initializeClientSession(params) {
return this.checkedConnection.sendRequest(glsp_jsonrpc_client_1.JsonrpcGLSPClient.InitializeClientSessionRequest, params);
}
disposeClientSession(params) {
return this.checkedConnection.sendRequest(glsp_jsonrpc_client_1.JsonrpcGLSPClient.DisposeClientSessionRequest, params);
}
onActionMessage(handler, clientId) {
return this.onActionMessageNotification(msg => {
if (!clientId || msg.clientId === clientId) {
handler(msg);
}
});
}
sendActionMessage(message) {
this.checkedConnection.sendNotification(glsp_jsonrpc_client_1.JsonrpcGLSPClient.ActionMessageNotification, message);
}
shutdownServer() {
this.checkedConnection.sendNotification(glsp_jsonrpc_client_1.JsonrpcGLSPClient.ShutdownNotification);
}
stop() {
if (!this.connectionPromise) {
this.state = glsp_client_1.ClientState.Stopped;
return Promise.resolve();
}
if (this.state === glsp_client_1.ClientState.Stopping && this.onStop) {
return this.onStop;
}
this.state = glsp_client_1.ClientState.Stopping;
return (this.onStop = this.resolveConnection().then(connection => {
connection.dispose();
this.state = glsp_client_1.ClientState.Stopped;
this.onStop = undefined;
this.onActionMessageNotificationEmitter.dispose();
this.onCurrentStateChangedEmitter.dispose();
this.connectionPromise = undefined;
this.resolvedConnection = undefined;
}));
}
get checkedConnection() {
if (!this.isConnectionActive()) {
throw new Error(glsp_jsonrpc_client_1.JsonrpcGLSPClient.ClientNotReadyMsg);
}
return this.resolvedConnection;
}
resolveConnection() {
if (!this.connectionPromise) {
this.connectionPromise = this.doCreateConnection();
}
return this.connectionPromise;
}
async doCreateConnection() {
const connection = typeof this.connectionProvider === 'function' ? await this.connectionProvider() : this.connectionProvider;
connection.onError(data => this.handleConnectionError(data[0], data[1], data[2]));
connection.onClose(() => this.handleConnectionClosed());
connection.onNotification(glsp_jsonrpc_client_1.JsonrpcGLSPClient.ActionMessageNotification, msg => this.onActionMessageNotificationEmitter.fire(msg));
return connection;
}
handleConnectionError(error, message, count) {
glsp_jsonrpc_client_1.JsonrpcGLSPClient.error('Connection to server is erroring. Shutting down server.', error);
this.stop();
this.state = glsp_client_1.ClientState.ServerError;
}
handleConnectionClosed() {
if (this.state === glsp_client_1.ClientState.Stopping || this.state === glsp_client_1.ClientState.Stopped) {
return;
}
try {
if (this.resolvedConnection) {
this.resolvedConnection.dispose();
this.connectionPromise = undefined;
this.resolvedConnection = undefined;
}
}
catch (error) {
// Disposing a connection could fail if error cases.
}
glsp_jsonrpc_client_1.JsonrpcGLSPClient.error('Connection to server got closed. Server will not be restarted.');
this.state = glsp_client_1.ClientState.ServerError;
}
isConnectionActive() {
return this.state === glsp_client_1.ClientState.Running && !!this.resolvedConnection;
}
get currentState() {
return this.state;
}
}
exports.BaseJsonrpcGLSPClient = BaseJsonrpcGLSPClient;
/**
* Default {@link GLSPClientProxy} implementation for jsonrpc-based client-server communication with typescript based servers.
*/
class JsonrpcClientProxy {
initialize(clientConnection, enableLogging = false) {
this.clientConnection = clientConnection;
this.enableLogging = enableLogging;
}
process(message) {
var _a;
if (this.enableLogging) {
console.log(`Send action '${message.action.kind}' to client '${message.clientId}'`);
}
(_a = this.clientConnection) === null || _a === void 0 ? void 0 : _a.sendNotification(glsp_jsonrpc_client_1.JsonrpcGLSPClient.ActionMessageNotification, message);
}
}
exports.JsonrpcClientProxy = JsonrpcClientProxy;
//# sourceMappingURL=base-jsonrpc-glsp-client.js.map