/********************************************************************************
 * 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 { ConsoleLogger , createWebSocketConnection , Logger } from 'vscode-ws-jsonrpc' ; import { Emitter , Event } from '../event' ; import { ConnectionHandler } from './handler' ; import { JsonRpcProxy , JsonRpcProxyFactory } from './proxy-factory' ; import { WebSocketChannel } from './web-socket-channel' ; /**
 * Factor common logic according to `ElectronIpcConnectionProvider` and
 * `WebSocketConnectionProvider`. This class handles channels in a somewhat
 * generic way.
 */ @ injectable ( ) 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 , path , factory ) ; /**
     * 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 , path , target ? ) { throw new Error ( 'abstract' ) ; } protected channelIdSeq = 0 ; protected readonly channels = new Map < number , WebSocketChannel > ( ) ; 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 , factory ) ; /**
     * 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 , target ? ) ; createProxy < T extends object > ( path , arg ? ) { const factory = arg instanceof JsonRpcProxyFactory ? arg : new JsonRpcProxyFactory < T > ( arg ) ; this . listen ( { path , onConnection : c => factory . listen ( c ) } ) ; return factory . createProxy ( ) ; } /**
     * Install a connection handler for the given path.
     */ listen ( handler , options ? ) { this . openChannel ( handler . path , channel => { const connection = createWebSocketConnection ( channel , this . createLogger ( ) ) ; connection . onDispose ( ( ) => channel . close ( ) ) ; handler . onConnection ( connection ) ; } , options ) ; } openChannel ( path , handler , options ? ) { const id = this . channelIdSeq ++ ; const channel = this . createChannel ( id ) ; this . channels . set ( id , channel ) ; channel . onClose ( ( ) => { if ( this . channels . delete ( channel . id ) ) { const { reconnecting } = { reconnecting : true , ... options } ; if ( reconnecting ) { this . openChannel ( path , handler , options ) ; } } else { console . error ( 'The ws channel does not exist' , channel . id ) ; } } ) ; channel . onOpen ( ( ) => handler ( channel ) ) ; channel . open ( path ) ; } protected abstract createChannel ( id ) ; protected handleIncomingRawMessage ( data ) { const message = JSON . parse ( data ) ; const channel = this . channels . get ( message . id ) ; if ( channel ) { channel . handleMessage ( message ) ; } else { console . error ( 'The ws channel does not exist' , message . id ) ; } this . onIncomingMessageActivityEmitter . fire ( undefined ) ; } protected createLogger ( ) { return new ConsoleLogger ( ) ; } }