Skip to content

Commit

Permalink
Rename JsonRpcProxyFactory and related types
Browse files Browse the repository at this point in the history
- Rename `JsonRpcProxyFactory` and related types to `RpcProxyFactory` (Rpc*). The naming scheme was a remainder of the old vscode jsonr-rpc based protocol.
  By simply using the `Rpc` suffix the class names are less misleading and protocol agnostic.
- Keep deprecated declarations of the old `JsonRpc*` namespace. The components are heavily used by adopters so we should maintain this deprecated symbols for a while to enable graceful migration without hard API breaks.
Naturally this is open for discussion, but judging from the files I had to touch in the core framework alone I think it's definitely 
a good idea to give adopters a grace period.

Complementary website PR: eclipse-theia/theia-website#422

Fixes #12581
Contributed on behalf of STMicroelectronics
  • Loading branch information
tortmayr committed Jun 1, 2023
1 parent 53b1987 commit e8fe7d2
Show file tree
Hide file tree
Showing 42 changed files with 184 additions and 150 deletions.
4 changes: 2 additions & 2 deletions examples/api-samples/src/common/updater/sample-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
import { RpcServer } from '@theia/core/lib/common/messaging/proxy-factory';

export enum UpdateStatus {
InProgress = 'in-progress',
Expand All @@ -23,7 +23,7 @@ export enum UpdateStatus {

export const SampleUpdaterPath = '/services/sample-updater';
export const SampleUpdater = Symbol('SampleUpdater');
export interface SampleUpdater extends JsonRpcServer<SampleUpdaterClient> {
export interface SampleUpdater extends RpcServer<SampleUpdaterClient> {
checkForUpdates(): Promise<{ status: UpdateStatus }>;
onRestartToUpdateRequested(): void;
disconnectClient(client: SampleUpdaterClient): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import { ContainerModule } from '@theia/core/shared/inversify';
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
import { RpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
import { ElectronMainApplicationContribution } from '@theia/core/lib/electron-main/electron-main-application';
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
import { SampleUpdaterPath, SampleUpdater, SampleUpdaterClient } from '../../common/updater/sample-updater';
Expand All @@ -26,7 +26,7 @@ export default new ContainerModule(bind => {
bind(SampleUpdater).toService(SampleUpdaterImpl);
bind(ElectronMainApplicationContribution).toService(SampleUpdater);
bind(ElectronConnectionHandler).toDynamicValue(context =>
new JsonRpcConnectionHandler<SampleUpdaterClient>(SampleUpdaterPath, client => {
new RpcConnectionHandler<SampleUpdaterClient>(SampleUpdaterPath, client => {
const server = context.container.get<SampleUpdater>(SampleUpdater);
server.setClient(client);
client.onDidCloseConnection(() => server.disconnectClient(client));
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/browser/messaging/ws-connection-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
// *****************************************************************************

import { injectable, interfaces, decorate, unmanaged } from 'inversify';
import { JsonRpcProxyFactory, JsonRpcProxy, Emitter, Event, Channel } from '../../common';
import { RpcProxyFactory, RpcProxy, Emitter, Event, Channel } from '../../common';
import { Endpoint } from '../endpoint';
import { AbstractConnectionProvider } from '../../common/messaging/abstract-connection-provider';
import { io, Socket } from 'socket.io-client';
import { IWebSocket, WebSocketChannel } from '../../common/messaging/web-socket-channel';

decorate(injectable(), JsonRpcProxyFactory);
decorate(unmanaged(), JsonRpcProxyFactory, 0);
decorate(injectable(), RpcProxyFactory);
decorate(unmanaged(), RpcProxyFactory, 0);

export interface WebSocketOptions {
/**
Expand All @@ -44,7 +44,7 @@ export class WebSocketConnectionProvider extends AbstractConnectionProvider<WebS
return this.onSocketDidCloseEmitter.event;
}

static override createProxy<T extends object>(container: interfaces.Container, path: string, arg?: object): JsonRpcProxy<T> {
static override createProxy<T extends object>(container: interfaces.Container, path: string, arg?: object): RpcProxy<T> {
return container.get(WebSocketConnectionProvider).createProxy<T>(path, arg);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/common/logger-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
// *****************************************************************************

import { injectable } from 'inversify';
import { JsonRpcServer } from './messaging/proxy-factory';
import { RpcServer } from './messaging/proxy-factory';

export const ILoggerServer = Symbol('ILoggerServer');

export const loggerPath = '/services/logger';

export interface ILoggerServer extends JsonRpcServer<ILoggerClient> {
export interface ILoggerServer extends RpcServer<ILoggerClient> {
setLogLevel(name: string, logLevel: number): Promise<void>;
getLogLevel(name: string): Promise<number>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { injectable, interfaces } from 'inversify';
import { Emitter, Event } from '../event';
import { ConnectionHandler } from './handler';
import { JsonRpcProxy, JsonRpcProxyFactory } from './proxy-factory';
import { RpcProxy, RpcProxyFactory } from './proxy-factory';
import { Channel, ChannelMultiplexer } from '../message-rpc/channel';

/**
Expand All @@ -32,15 +32,15 @@ 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>;
static createProxy<T extends object>(container: interfaces.Container, path: string, factory: RpcProxyFactory<T>): RpcProxy<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> {
static createProxy<T extends object>(container: interfaces.Container, path: string, target?: object): RpcProxy<T> {
throw new Error('abstract');
}

Expand All @@ -53,17 +53,17 @@ export abstract class AbstractConnectionProvider<AbstractOptions extends object>
* 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>;
createProxy<T extends object>(path: string, factory: RpcProxyFactory<T>): RpcProxy<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);
createProxy<T extends object>(path: string, target?: object): RpcProxy<T>;
createProxy<T extends object>(path: string, arg?: object): RpcProxy<T> {
const factory = arg instanceof RpcProxyFactory ? arg : new RpcProxyFactory<T>(arg);
this.listen({
path,
onConnection: c => factory.listen(c)
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/common/messaging/proxy-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import * as chai from 'chai';
import { JsonRpcProxyFactory, JsonRpcProxy } from './proxy-factory';
import { RpcProxyFactory, RpcProxy } from './proxy-factory';
import { ChannelPipe } from '../message-rpc/channel.spec';

const expect = chai.expect;
Expand Down Expand Up @@ -84,19 +84,19 @@ describe('Proxy-Factory', () => {

function getSetup(): {
client: TestClient;
clientProxy: JsonRpcProxy<TestClient>;
clientProxy: RpcProxy<TestClient>;
server: TestServer;
serverProxy: JsonRpcProxy<TestServer>;
serverProxy: RpcProxy<TestServer>;
} {
const client = new TestClient();
const server = new TestServer();

const serverProxyFactory = new JsonRpcProxyFactory<TestServer>(client);
const serverProxyFactory = new RpcProxyFactory<TestServer>(client);
const pipe = new ChannelPipe();
serverProxyFactory.listen(pipe.right);
const serverProxy = serverProxyFactory.createProxy();

const clientProxyFactory = new JsonRpcProxyFactory<TestClient>(server);
const clientProxyFactory = new RpcProxyFactory<TestClient>(server);
clientProxyFactory.listen(pipe.left);
const clientProxy = clientProxyFactory.createProxy();
return {
Expand Down
111 changes: 73 additions & 38 deletions packages/core/src/common/messaging/proxy-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,29 @@ import { Channel } from '../message-rpc/channel';
import { RequestHandler, RpcProtocol } from '../message-rpc/rpc-protocol';
import { ConnectionHandler } from './handler';
import { Deferred } from '../promise-util';
import { decorate, injectable, unmanaged } from '../../../shared/inversify';

export type JsonRpcServer<Client> = Disposable & {
export type RpcServer<Client> = Disposable & {
/**
* If this server is a proxy to a remote server then
* a client is used as a local object
* to handle JSON-RPC messages from the remote server.
* to handle RPC messages from the remote server.
*/
setClient(client: Client | undefined): void;
getClient?(): Client | undefined;
};

export interface JsonRpcConnectionEventEmitter {
export interface RpcConnectionEventEmitter {
readonly onDidOpenConnection: Event<void>;
readonly onDidCloseConnection: Event<void>;
}
export type JsonRpcProxy<T> = T & JsonRpcConnectionEventEmitter;
export type RpcProxy<T> = T & RpcConnectionEventEmitter;

export class JsonRpcConnectionHandler<T extends object> implements ConnectionHandler {
export class RpcConnectionHandler<T extends object> implements ConnectionHandler {
constructor(
readonly path: string,
readonly targetFactory: (proxy: JsonRpcProxy<T>) => any,
readonly factoryConstructor: new () => JsonRpcProxyFactory<T> = JsonRpcProxyFactory
readonly targetFactory: (proxy: RpcProxy<T>) => any,
readonly factoryConstructor: new () => RpcProxyFactory<T> = RpcProxyFactory
) { }

onConnection(connection: Channel): void {
Expand All @@ -63,12 +64,12 @@ export type RpcProtocolFactory = (channel: Channel, requestHandler: RequestHandl
const defaultRpcProtocolFactory: RpcProtocolFactory = (channel, requestHandler) => new RpcProtocol(channel, requestHandler);

/**
* Factory for JSON-RPC proxy objects.
* Factory for RPC proxy objects.
*
* A JSON-RPC proxy exposes the programmatic interface of an object through
* JSON-RPC. This allows remote programs to call methods of this objects by
* sending JSON-RPC requests. This takes place over a bi-directional stream,
* where both ends can expose an object and both can call methods each other's
* A RPC proxy exposes the programmatic interface of an object through
* Theia's RPC protocol. This allows remote programs to call methods of this objects by
* sending RPC requests. This takes place over a bi-directional stream,
* where both ends can expose an object and both can call methods on each other'
* exposed object.
*
* For example, assuming we have an object of the following type on one end:
Expand All @@ -77,45 +78,45 @@ const defaultRpcProtocolFactory: RpcProtocolFactory = (channel, requestHandler)
* bar(baz: number): number { return baz + 1 }
* }
*
* which we want to expose through a JSON-RPC interface. We would do:
* which we want to expose through a RPC interface. We would do:
*
* let target = new Foo()
* let factory = new JsonRpcProxyFactory<Foo>('/foo', target)
* let factory = new RpcProxyFactory<Foo>('/foo', target)
* factory.onConnection(connection)
*
* The party at the other end of the `connection`, in order to remotely call
* methods on this object would do:
*
* let factory = new JsonRpcProxyFactory<Foo>('/foo')
* let factory = new RpcProxyFactory<Foo>('/foo')
* factory.onConnection(connection)
* let proxy = factory.createProxy();
* let result = proxy.bar(42)
* // result is equal to 43
*
* One the wire, it would look like this:
*
* --> {"jsonrpc": "2.0", "id": 0, "method": "bar", "params": {"baz": 42}}
* <-- {"jsonrpc": "2.0", "id": 0, "result": 43}
*
* --> { "type":"1", "id": 1, "method": "bar", "args": [42]}
* <-- { "type":"3", "id": 1, "res": 43}
*
* Note that in the code of the caller, we didn't pass a target object to
* JsonRpcProxyFactory, because we don't want/need to expose an object.
* RpcProxyFactory, because we don't want/need to expose an object.
* If we had passed a target object, the other side could've called methods on
* it.
*
* @param <T> - The type of the object to expose to JSON-RPC.
* @param <T> - The type of the object to expose to RPC.
*/

export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
export class RpcProxyFactory<T extends object> implements ProxyHandler<T> {

protected readonly onDidOpenConnectionEmitter = new Emitter<void>();
protected readonly onDidCloseConnectionEmitter = new Emitter<void>();

protected rpcDeferred: Deferred<RpcProtocol>;

/**
* Build a new JsonRpcProxyFactory.
* Build a new RpcProxyFactory.
*
* @param target - The object to expose to JSON-RPC methods calls. If this
* @param target - The object to expose to RPC methods calls. If this
* is omitted, the proxy won't be able to handle requests, only send them.
*/
constructor(public target?: any, protected rpcProtocolFactory = defaultRpcProtocolFactory) {
Expand All @@ -135,10 +136,10 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
}

/**
* Connect a MessageConnection to the factory.
* Connect a {@link Channel} to the factory by creating an {@link RpcProtocol} on top of it.
*
* This connection will be used to send/receive JSON-RPC requests and
* response.
* This protocol will be used to send/receive RPC requests and
* responses.
*/
listen(channel: Channel): void {
const protocol = this.rpcProtocolFactory(channel, (meth, args) => this.onRequest(meth, ...args));
Expand All @@ -148,9 +149,9 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
}

/**
* Process an incoming JSON-RPC method call.
* Process an incoming RPC method call.
*
* onRequest is called when the JSON-RPC connection received a method call
* onRequest is called when the RPC connection received a method call
* request. It calls the corresponding method on [[target]].
*
* The return value is a Promise object that is resolved with the return
Expand Down Expand Up @@ -179,7 +180,7 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
}

/**
* Process an incoming JSON-RPC notification.
* Process an incoming RPC notification.
*
* Same as [[onRequest]], but called on incoming notifications rather than
* methods calls.
Expand All @@ -192,37 +193,37 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {

/**
* Create a Proxy exposing the interface of an object of type T. This Proxy
* can be used to do JSON-RPC method calls on the remote target object as
* can be used to do RPC method calls on the remote target object as
* if it was local.
*
* If `T` implements `JsonRpcServer` then a client is used as a target object for a remote target object.
* If `T` implements `RpcServer` then a client is used as a target object for a remote target object.
*/
createProxy(): JsonRpcProxy<T> {
createProxy(): RpcProxy<T> {
const result = new Proxy<T>(this as any, this);
return result as any;
}

/**
* Get a callable object that executes a JSON-RPC method call.
* Get a callable object that executes a RPC method call.
*
* Getting a property on the Proxy object returns a callable that, when
* called, executes a JSON-RPC call. The name of the property defines the
* called, executes a RPC call. The name of the property defines the
* method to be called. The callable takes a variable number of arguments,
* which are passed in the JSON-RPC method call.
* which are passed in the RPC method call.
*
* For example, if you have a Proxy object:
*
* let fooProxyFactory = JsonRpcProxyFactory<Foo>('/foo')
* let fooProxyFactory = RpcProxyFactory<Foo>('/foo')
* let fooProxy = fooProxyFactory.createProxy()
*
* accessing `fooProxy.bar` will return a callable that, when called,
* executes a JSON-RPC method call to method `bar`. Therefore, doing
* executes a RPC method call to method `bar`. Therefore, doing
* `fooProxy.bar()` will call the `bar` method on the remote Foo object.
*
* @param target - unused.
* @param p - The property accessed on the Proxy object.
* @param receiver - unused.
* @returns A callable that executes the JSON-RPC call.
* @returns A callable that executes the RPC call.
*/
get(target: T, p: PropertyKey, receiver: any): any {
if (p === 'setClient') {
Expand Down Expand Up @@ -306,3 +307,37 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {

}

/**
* @deprecated since 1.39.0 use `RpcConnectionEventEmitter` instead
*/
export type JsonRpcConnectionEventEmitter = RpcConnectionEventEmitter;

/**
* @deprecated since 1.39.0 use `RpcServer` instead
*/
export type JsonRpcServer<Client> = RpcServer<Client>;

/**
* @deprecated since 1.39.0 use `RpcProxy` instead
*/
export type JsonRpcProxy<T> = RpcProxy<T>;

/**
* @deprecated since 1.39.0 use `RpcConnectionHandler` instead
*/
export class JsonRpcConnectionHandler<T extends object> extends RpcConnectionHandler<T> {

}

/**
* @deprecated since 1.39.0 use `RpcProxyFactory` instead
*/
export class JsonRpcProxyFactory<T extends object> extends RpcProxyFactory<T> {

}

// eslint-disable-next-line deprecation/deprecation
decorate(injectable(), JsonRpcProxyFactory);
// eslint-disable-next-line deprecation/deprecation
decorate(unmanaged(), JsonRpcProxyFactory, 0);

0 comments on commit e8fe7d2

Please sign in to comment.