@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
116 lines (102 loc) • 4.8 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2020 Ericsson 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
// *****************************************************************************
import { injectable, interfaces } from 'inversify';
import { Emitter, Event } from '../event';
import { ConnectionHandler } from './handler';
import { JsonRpcProxy, JsonRpcProxyFactory } from './proxy-factory';
import { Channel, ChannelMultiplexer } from '../message-rpc/channel';
/**
* Factor common logic according to `ElectronIpcConnectionProvider` and
* `WebSocketConnectionProvider`. This class handles channels in a somewhat
* generic way.
*/
()
export abstract class AbstractConnectionProvider<AbstractOptions extends object> {
/**
* Create a proxy object to remote interface of T type
* over an electron ipc connection for the given path and proxy factory.
*/
static createProxy<T extends object>(container: interfaces.Container, path: string, factory: JsonRpcProxyFactory<T>): JsonRpcProxy<T>;
/**
* Create a proxy object to remote interface of T type
* over an electron ipc connection for the given path.
*
* An optional target can be provided to handle
* notifications and requests from a remote side.
*/
static createProxy<T extends object>(container: interfaces.Container, path: string, target?: object): JsonRpcProxy<T> {
throw new Error('abstract');
}
protected readonly onIncomingMessageActivityEmitter: Emitter<void> = new Emitter();
get onIncomingMessageActivity(): Event<void> {
return this.onIncomingMessageActivityEmitter.event;
}
/**
* Create a proxy object to remote interface of T type
* over a web socket connection for the given path and proxy factory.
*/
createProxy<T extends object>(path: string, factory: JsonRpcProxyFactory<T>): JsonRpcProxy<T>;
/**
* Create a proxy object to remote interface of T type
* over a web socket connection for the given path.
*
* An optional target can be provided to handle
* notifications and requests from a remote side.
*/
createProxy<T extends object>(path: string, target?: object): JsonRpcProxy<T>;
createProxy<T extends object>(path: string, arg?: object): JsonRpcProxy<T> {
const factory = arg instanceof JsonRpcProxyFactory ? arg : new JsonRpcProxyFactory<T>(arg);
this.listen({
path,
onConnection: c => factory.listen(c)
});
return factory.createProxy();
}
protected channelMultiplexer?: ChannelMultiplexer;
// A set of channel opening functions that are executed if the backend reconnects to restore the
// the channels that were open before the disconnect occurred.
protected reconnectChannelOpeners: Array<() => Promise<void>> = [];
protected initializeMultiplexer(): void {
const mainChannel = this.createMainChannel();
mainChannel.onMessage(() => this.onIncomingMessageActivityEmitter.fire());
this.channelMultiplexer = new ChannelMultiplexer(mainChannel);
}
/**
* Install a connection handler for the given path.
*/
listen(handler: ConnectionHandler, options?: AbstractOptions): void {
this.openChannel(handler.path, channel => {
handler.onConnection(channel);
}, options);
}
async openChannel(path: string, handler: (channel: Channel) => void, options?: AbstractOptions): Promise<void> {
if (!this.channelMultiplexer) {
throw new Error('The channel multiplexer has not been initialized yet!');
}
const newChannel = await this.channelMultiplexer.open(path);
newChannel.onClose(() => {
const { reconnecting } = { reconnecting: true, ...options };
if (reconnecting) {
this.reconnectChannelOpeners.push(() => this.openChannel(path, handler, options));
}
});
handler(newChannel);
}
/**
* Create the main connection that is used for multiplexing all service channels.
*/
protected abstract createMainChannel(): Channel;
}