blob: 6c60a43f777c16d79423a02849cdd5062a06b1aa [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2016-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,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
******************************************************************************/
package org.eclipse.lsp4j.jsonrpc.debug;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
import org.eclipse.lsp4j.jsonrpc.Endpoint;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.jsonrpc.MessageConsumer;
import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint;
import org.eclipse.lsp4j.jsonrpc.debug.json.DebugMessageJsonHandler;
import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod;
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler;
import org.eclipse.lsp4j.jsonrpc.json.StreamMessageConsumer;
import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints;
import org.eclipse.lsp4j.jsonrpc.validation.ReflectiveMessageValidator;
import com.google.gson.GsonBuilder;
/**
* This is the entry point for applications that use the debug protocol. A DebugLauncher does
* all the wiring that is necessary to connect your endpoint via an input stream
* and an output stream.
*/
public final class DebugLauncher {
private DebugLauncher() {}
/**
* Create a new Launcher for a given local service object, a given remote
* interface and an input and output stream.
*
* @param localService
* - an object on which classes RPC methods are looked up
* @param remoteInterface
* - an interface on which RPC methods are looked up
* @param in
* - inputstream to listen for incoming messages
* @param out
* - outputstream to send outgoing messages
*/
public static <T> Launcher<T> createLauncher(Object localService, Class<T> remoteInterface, InputStream in,
OutputStream out) {
return new Builder<T>()
.setLocalService(localService)
.setRemoteInterface(remoteInterface)
.setInput(in)
.setOutput(out)
.create();
}
/**
* Create a new Launcher for a given local service object, a given remote
* interface and an input and output stream, and set up message validation and
* tracing.
*
* @param localService
* - an object on which classes RPC methods are looked up
* @param remoteInterface
* - an interface on which RPC methods are looked up
* @param in
* - inputstream to listen for incoming messages
* @param out
* - outputstream to send outgoing messages
* @param validate
* - whether messages should be validated with the
* {@link ReflectiveMessageValidator}
* @param trace
* - a writer to which incoming and outgoing messages are traced, or
* {@code null} to disable tracing
*/
public static <T> Launcher<T> createLauncher(Object localService, Class<T> remoteInterface, InputStream in,
OutputStream out, boolean validate, PrintWriter trace) {
return new Builder<T>()
.setLocalService(localService)
.setRemoteInterface(remoteInterface)
.setInput(in)
.setOutput(out)
.validateMessages(validate)
.traceMessages(trace)
.create();
}
/**
* Create a new Launcher for a given local service object, a given remote
* interface and an input and output stream. Threads are started with the given
* executor service. The wrapper function is applied to the incoming and
* outgoing message streams so additional message handling such as validation
* and tracing can be included.
*
* @param localService
* - an object on which classes RPC methods are looked up
* @param remoteInterface
* - an interface on which RPC methods are looked up
* @param in
* - inputstream to listen for incoming messages
* @param out
* - outputstream to send outgoing messages
* @param executorService
* - the executor service used to start threads
* @param wrapper
* - a function for plugging in additional message consumers
*/
public static <T> Launcher<T> createLauncher(Object localService, Class<T> remoteInterface, InputStream in,
OutputStream out, ExecutorService executorService, Function<MessageConsumer, MessageConsumer> wrapper) {
return createIoLauncher(localService, remoteInterface, in, out, executorService, wrapper);
}
/**
* Create a new Launcher for a given local service object, a given remote
* interface and an input and output stream. Threads are started with the given
* executor service. The wrapper function is applied to the incoming and
* outgoing message streams so additional message handling such as validation
* and tracing can be included.
*
* @param localService
* - an object on which classes RPC methods are looked up
* @param remoteInterface
* - an interface on which RPC methods are looked up
* @param in
* - inputstream to listen for incoming messages
* @param out
* - outputstream to send outgoing messages
* @param executorService
* - the executor service used to start threads
* @param wrapper
* - a function for plugging in additional message consumers
*/
public static <T> Launcher<T> createIoLauncher(Object localService, Class<T> remoteInterface, InputStream in,
OutputStream out, ExecutorService executorService, Function<MessageConsumer, MessageConsumer> wrapper) {
return new Builder<T>()
.setLocalService(localService)
.setRemoteInterface(remoteInterface)
.setInput(in)
.setOutput(out)
.setExecutorService(executorService)
.wrapMessages(wrapper)
.create();
}
/**
* Create a new Launcher for a given local service object, a given remote
* interface and an input and output stream. Threads are started with the given
* executor service. The wrapper function is applied to the incoming and
* outgoing message streams so additional message handling such as validation
* and tracing can be included. The {@code configureGson} function can be used
* to register additional type adapters in the {@link GsonBuilder} in order to
* support protocol classes that cannot be handled by Gson's reflective
* capabilities.
*
* @param localService
* - an object on which classes RPC methods are looked up
* @param remoteInterface
* - an interface on which RPC methods are looked up
* @param in
* - inputstream to listen for incoming messages
* @param out
* - outputstream to send outgoing messages
* @param executorService
* - the executor service used to start threads
* @param wrapper
* - a function for plugging in additional message consumers
* @param configureGson
* - a function for Gson configuration
*/
public static <T> Launcher<T> createIoLauncher(Object localService, Class<T> remoteInterface, InputStream in,
OutputStream out, ExecutorService executorService, Function<MessageConsumer, MessageConsumer> wrapper,
Consumer<GsonBuilder> configureGson) {
return new Builder<T>()
.setLocalService(localService)
.setRemoteInterface(remoteInterface)
.setInput(in)
.setOutput(out)
.setExecutorService(executorService)
.wrapMessages(wrapper)
.configureGson(configureGson)
.create();
}
/**
* Launcher builder for the debug protocol. Adapts the JSON-RPC message classes to the JSON format used
* by the debug protocol.
*/
public static class Builder<T> extends Launcher.Builder<T> {
@Override
protected MessageJsonHandler createJsonHandler() {
Map<String, JsonRpcMethod> supportedMethods = getSupportedMethods();
if (configureGson != null)
return new DebugMessageJsonHandler(supportedMethods, configureGson);
else
return new DebugMessageJsonHandler(supportedMethods);
}
@Override
protected RemoteEndpoint createRemoteEndpoint(MessageJsonHandler jsonHandler) {
MessageConsumer outgoingMessageStream = new StreamMessageConsumer(output, jsonHandler);
outgoingMessageStream = wrapMessageConsumer(outgoingMessageStream);
Endpoint localEndpoint = ServiceEndpoints.toEndpoint(localServices);
RemoteEndpoint remoteEndpoint;
if (exceptionHandler == null)
remoteEndpoint = new DebugRemoteEndpoint(outgoingMessageStream, localEndpoint);
else
remoteEndpoint = new DebugRemoteEndpoint(outgoingMessageStream, localEndpoint, exceptionHandler);
jsonHandler.setMethodProvider(remoteEndpoint);
return remoteEndpoint;
}
}
}