diff --git a/.travis.yml b/.travis.yml index 46fba9a9add6a..8f14f920ae274 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ cache: - examples/browser/node_modules - examples/electron/node_modules - node_modules + - packages/broadcast/node_modules - packages/bunyan/node_modules - packages/callhierarchy/node_modules - packages/console/node_modules diff --git a/packages/broadcast/README.md b/packages/broadcast/README.md new file mode 100644 index 0000000000000..08edb6b0a722c --- /dev/null +++ b/packages/broadcast/README.md @@ -0,0 +1,3 @@ +# Broadcast Messaging + +Work In Progress diff --git a/packages/broadcast/compile.tsconfig.json b/packages/broadcast/compile.tsconfig.json new file mode 100644 index 0000000000000..cad0dee33066c --- /dev/null +++ b/packages/broadcast/compile.tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "baseUrl": "." + }, + "include": [ + "src" + ] +} diff --git a/packages/broadcast/package.json b/packages/broadcast/package.json new file mode 100644 index 0000000000000..6ba9c6d06b335 --- /dev/null +++ b/packages/broadcast/package.json @@ -0,0 +1,48 @@ +{ + "name": "@theia/broadcast", + "version": "0.5.0", + "description": "Theia - Broadcast Messaging Extension", + "dependencies": { + "@theia/core": "^0.5.0" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "backend": "lib/node/broadcast-backend-module" + }, + { + "frontend": "lib/browser/broadcast-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/theia-ide/theia.git" + }, + "bugs": { + "url": "https://github.com/theia-ide/theia/issues" + }, + "homepage": "https://github.com/theia-ide/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "prepare": "yarn run clean && yarn run build", + "clean": "theiaext clean", + "build": "theiaext build", + "watch": "theiaext watch", + "test": "theiaext test" + }, + "devDependencies": { + "@theia/ext-scripts": "^0.5.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/broadcast/src/browser/broadcast-frontend-module.ts b/packages/broadcast/src/browser/broadcast-frontend-module.ts new file mode 100644 index 0000000000000..f75541a3a29bd --- /dev/null +++ b/packages/broadcast/src/browser/broadcast-frontend-module.ts @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 { ContainerModule } from 'inversify'; +import { WebSocketConnectionProvider } from '@theia/core/lib/browser'; +import { servicePath, IBroadcastClientDispatch, BroadcastClientDispatch, IBroadcastServer, BroadcastWatcher } from '../common'; + +export default new ContainerModule(bind => { + bind(IBroadcastClientDispatch).to(BroadcastClientDispatch).inSingletonScope(); + bind(BroadcastWatcher).toSelf().inSingletonScope(); + bind(IBroadcastServer).toDynamicValue(({ container }) => { + const watcher = container.get(BroadcastWatcher); + const connection = container.get(WebSocketConnectionProvider); + return connection.createProxy(servicePath, watcher.getConnectedClient()); + }).inSingletonScope(); +}); diff --git a/packages/broadcast/src/common/broadcast-client.ts b/packages/broadcast/src/common/broadcast-client.ts new file mode 100644 index 0000000000000..2466d8dff9ad6 --- /dev/null +++ b/packages/broadcast/src/common/broadcast-client.ts @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 { inject, injectable } from 'inversify'; +import { IBroadcastProtocol, IBroadcastServer, IBroadcastState } from './broadcast-protocol'; +import { BroadcastWatcher } from './broadcast-watcher'; + +export const IBroadcastClientDispatch = Symbol('IBroadcastClientDispatch'); + +type onStateUpdateHandler = (event: IBroadcastProtocol) => void; + +export interface IBroadcastClientDispatch { + onStateUpdate(callback: onStateUpdateHandler): void; + getState(): Promise; + setState(state: IBroadcastState): Promise; +} + +@injectable() +export class BroadcastClientDispatch implements IBroadcastClientDispatch { + + constructor( + @inject(BroadcastWatcher) private readonly watcher: BroadcastWatcher, + @inject(IBroadcastServer) private readonly server: IBroadcastServer + ) { } + + onStateUpdate(callback: onStateUpdateHandler) { + this.watcher.onStateUpdate(callback); + } + + getState(): Promise { + return this.server.getState(); + } + + async setState(state: IBroadcastState): Promise { + await this.server.getState(); + return this.server.setState(state); + } +} diff --git a/packages/broadcast/src/common/broadcast-protocol.ts b/packages/broadcast/src/common/broadcast-protocol.ts new file mode 100644 index 0000000000000..ea643e14b6150 --- /dev/null +++ b/packages/broadcast/src/common/broadcast-protocol.ts @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 + ********************************************************************************/ + +/** Interface for Broadcast Message State */ +export const IBroadcastState = Symbol('IBroadcastState'); + +export type IBroadcastState = string | Object; + +/** Interface for Broadcast Message Protocol */ +export const IBroadcastProtocol = Symbol('IBroadcastProtocol'); + +export interface IBroadcastProtocol { + prevState: IBroadcastState; + state: IBroadcastState; +} + +/** Interface for Broadcast Client */ +export const IBroadcastClient = Symbol('IBroadcastClient'); + +export interface IBroadcastClient { + onStateUpdate(event: IBroadcastProtocol): void; +} + +/** Interface for Broadcast Server */ +export const IBroadcastServer = Symbol('IBroadcastServer'); + +export interface IBroadcastServer { + addClient(client: IBroadcastClient): void; + removeClient(client: IBroadcastClient): void; + setState(state: IBroadcastState): Promise; + getState(): Promise; +} + +/** API Endpoint for the Broadcast Service */ +export const servicePath = '/services/broadcast'; diff --git a/packages/broadcast/src/common/broadcast-server.ts b/packages/broadcast/src/common/broadcast-server.ts new file mode 100644 index 0000000000000..d76861b39829d --- /dev/null +++ b/packages/broadcast/src/common/broadcast-server.ts @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 } from 'inversify'; +import { IBroadcastServer, IBroadcastClient, IBroadcastState, IBroadcastProtocol } from './broadcast-protocol'; + +@injectable() +export class BroadcastServer implements IBroadcastServer { + + private readonly clients: Set; + private state: string | Object; + + constructor() { + this.clients = new Set(); + this.state = {}; + } + + addClient(client: IBroadcastClient) { + this.clients.add(client); + } + + removeClient(client: IBroadcastClient) { + this.clients.delete(client); + } + + setState(state: IBroadcastState): Promise { + const prevState = this.state; + this.state = state; + const stateUpdateMetadata: IBroadcastProtocol = { prevState, state }; + this.clients.forEach(client => client.onStateUpdate(stateUpdateMetadata)); + return Promise.resolve(stateUpdateMetadata); + } + + getState(): Promise { + return Promise.resolve(this.state); + } +} diff --git a/packages/broadcast/src/common/broadcast-watcher.ts b/packages/broadcast/src/common/broadcast-watcher.ts new file mode 100644 index 0000000000000..95e04cb404c9c --- /dev/null +++ b/packages/broadcast/src/common/broadcast-watcher.ts @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 } from 'inversify'; +import { Emitter, Event } from '@theia/core/lib/common'; +import { IBroadcastClient, IBroadcastProtocol } from './broadcast-protocol'; + +@injectable() +export class BroadcastWatcher { + + private readonly emitter: Emitter; + + constructor() { + this.emitter = new Emitter(); + } + + getConnectedClient(): IBroadcastClient { + const { emitter } = this; + return { + onStateUpdate(event: IBroadcastProtocol) { + emitter.fire(event); + } + }; + } + + get onStateUpdate(): Event { + return this.emitter.event; + } +} diff --git a/packages/broadcast/src/common/index.ts b/packages/broadcast/src/common/index.ts new file mode 100644 index 0000000000000..3bc9387dd282c --- /dev/null +++ b/packages/broadcast/src/common/index.ts @@ -0,0 +1,20 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 + ********************************************************************************/ + +export * from './broadcast-client'; +export * from './broadcast-protocol'; +export * from './broadcast-server'; +export * from './broadcast-watcher'; diff --git a/packages/broadcast/src/node/broadcast-backend-module.ts b/packages/broadcast/src/node/broadcast-backend-module.ts new file mode 100644 index 0000000000000..aaa422d4cc490 --- /dev/null +++ b/packages/broadcast/src/node/broadcast-backend-module.ts @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (C) 2018 TypeFox 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 { ContainerModule } from 'inversify'; +import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core/lib/common'; +import { servicePath, IBroadcastClientDispatch, BroadcastClientDispatch, IBroadcastClient, IBroadcastServer, BroadcastServer, BroadcastWatcher } from '../common'; + +export default new ContainerModule(bind => { + bind(IBroadcastClientDispatch).to(BroadcastClientDispatch).inSingletonScope(); + bind(BroadcastWatcher).toSelf().inSingletonScope(); + bind(BroadcastServer).toSelf().inSingletonScope(); + bind(IBroadcastServer).toService(BroadcastServer); + bind(ConnectionHandler).toDynamicValue(({ container }) => + new JsonRpcConnectionHandler(servicePath, client => { + const server = container.get(IBroadcastServer); + client.onDidOpenConnection(() => server.addClient(client)); + client.onDidCloseConnection(() => server.removeClient(client)); + return container.get(IBroadcastServer); + }) + ).inSingletonScope(); +});