diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiver.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiver.java index 1835a7db2a6..68f426c9748 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiver.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiver.java @@ -37,18 +37,21 @@ public class JsonRpcMessageReceiver implements WebSocketMessageReceiver { private final JsonRpcErrorTransmitter errorTransmitter; private final JsonRpcQualifier jsonRpcQualifier; private final JsonRpcUnmarshaller jsonRpcUnmarshaller; + private final RequestProcessor requestProcessor; @Inject public JsonRpcMessageReceiver(RequestDispatcher requestDispatcher, ResponseDispatcher responseDispatcher, JsonRpcErrorTransmitter errorTransmitter, JsonRpcQualifier jsonRpcQualifier, - JsonRpcUnmarshaller jsonRpcUnmarshaller) { + JsonRpcUnmarshaller jsonRpcUnmarshaller, + RequestProcessor requestProcessor) { this.requestDispatcher = requestDispatcher; this.responseDispatcher = responseDispatcher; this.errorTransmitter = errorTransmitter; this.jsonRpcQualifier = jsonRpcQualifier; this.jsonRpcUnmarshaller = jsonRpcUnmarshaller; + this.requestProcessor = requestProcessor; } @Override @@ -67,7 +70,7 @@ public void receive(String endpointId, String message) { List messages = jsonRpcUnmarshaller.unmarshalArray(message); for (String innerMessage : messages) { if (jsonRpcQualifier.isJsonRpcRequest(innerMessage)) { - processRequest(endpointId, innerMessage); + requestProcessor.process(() -> processRequest(endpointId, innerMessage)); } else if (jsonRpcQualifier.isJsonRpcResponse(innerMessage)) { processResponse(endpointId, innerMessage); } else { diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/RequestProcessor.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/RequestProcessor.java new file mode 100644 index 00000000000..f7228db820e --- /dev/null +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/RequestProcessor.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.jsonrpc.commons; + +/** + * Platfrom dependent implementation of of request handler processing + * algorithm. + */ +public interface RequestProcessor { + /** + * Process a runnable interface + * + * @param runnable runnable to be called for processing of a request + */ + void process(Runnable runnable); +} diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorManyToNone.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorManyToNone.java index 2a417c190a8..1ad2e648efb 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorManyToNone.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorManyToNone.java @@ -10,12 +10,12 @@ *******************************************************************************/ package org.eclipse.che.api.core.jsonrpc.commons.reception; -import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcErrorTransmitter; import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager; import org.slf4j.Logger; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; @@ -43,7 +43,7 @@ public class ConsumerConfiguratorManyToNone

{ this.pClass = pClass; } - public void withConsumer(BiConsumer> biConsumer) { + public void withBiConsumer(BiConsumer> biConsumer) { checkNotNull(biConsumer, "Notification consumer must not be null"); LOGGER.debug("Configuring incoming request: " + "binary consumer for method: " + method + ", " + @@ -51,4 +51,8 @@ public void withConsumer(BiConsumer> biConsumer) { handlerManager.registerManyToNone(method, pClass, biConsumer); } + + public void withConsumer(Consumer> consumer) { + withBiConsumer((endpointId, pValue) -> consumer.accept(pValue)); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorOneToNone.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorOneToNone.java index dcafc914350..4e99fa8e78b 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorOneToNone.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/ConsumerConfiguratorOneToNone.java @@ -14,7 +14,9 @@ import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager; import org.slf4j.Logger; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; @@ -42,7 +44,7 @@ public class ConsumerConfiguratorOneToNone

{ this.pClass = pClass; } - public void withConsumer(BiConsumer biConsumer) { + public void withBiConsumer(BiConsumer biConsumer) { checkNotNull(biConsumer, "Notification consumer must not be null"); LOGGER.debug("Configuring incoming request binary: " + @@ -51,4 +53,8 @@ public void withConsumer(BiConsumer biConsumer) { handlerManager.registerOneToNone(method, pClass, biConsumer); } + + public void withConsumer(Consumer

consumer) { + withBiConsumer((endpointId, pValue) -> consumer.accept(pValue)); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToMany.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToMany.java index ec458cfcfee..d4e3dc2288d 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToMany.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToMany.java @@ -10,12 +10,12 @@ *******************************************************************************/ package org.eclipse.che.api.core.jsonrpc.commons.reception; -import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcErrorTransmitter; import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerManager; import org.slf4j.Logger; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Function; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; @@ -49,12 +49,12 @@ public class FunctionConfiguratorOneToMany { } /** - * Define a function to be applied + * Define a binary function to be applied * * @param biFunction * function */ - public void withFunction(BiFunction> biFunction) { + public void withBiFunction(BiFunction> biFunction) { checkNotNull(biFunction, "Request function must not be null"); LOGGER.debug("Configuring incoming request binary: " + @@ -64,4 +64,14 @@ public void withFunction(BiFunction> biFunction) { handlerManager.registerOneToMany(method, pClass, rClass, biFunction); } + + /** + * Define a function to be applied + * + * @param biFunction + * function + */ + public void withFunction(Function> biFunction) { + withBiFunction((s, p) -> biFunction.apply(p)); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToOne.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToOne.java index a203fdde20f..200cba898f6 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToOne.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/commons/reception/FunctionConfiguratorOneToOne.java @@ -15,6 +15,7 @@ import org.slf4j.Logger; import java.util.function.BiFunction; +import java.util.function.Function; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; @@ -48,12 +49,12 @@ public class FunctionConfiguratorOneToOne { } /** - * Define a function to be applied + * Define a binary function to be applied * * @param biFunction * function */ - public void withFunction(BiFunction biFunction) { + public void withBiFunction(BiFunction biFunction) { checkNotNull(biFunction, "Request function must not be null"); LOGGER.debug("Configuring incoming request binary: " + @@ -63,4 +64,15 @@ public void withFunction(BiFunction biFunction) { handlerManager.registerOneToOne(method, pClass, rClass, biFunction); } + + + /** + * Define a function to be applied + * + * @param function + * function + */ + public void withFunction(Function function) { + withBiFunction((s, p) -> function.apply(p)); + } } diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/JsonRpcModule.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/JsonRpcModule.java index 7356e1626c7..02959d85f6b 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/JsonRpcModule.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/JsonRpcModule.java @@ -15,6 +15,7 @@ import com.google.inject.Provides; import com.google.inject.assistedinject.FactoryModuleBuilder; +import org.eclipse.che.api.core.jsonrpc.commons.RequestProcessor; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcComposer; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcMarshaller; @@ -34,6 +35,8 @@ protected void configure() { bind(JsonRpcUnmarshaller.class).to(GsonJsonRpcUnmarshaller.class); bind(JsonRpcQualifier.class).to(GsonJsonRpcQualifier.class); bind(JsonRpcComposer.class).to(GsonJsonRpcComposer.class); + + bind(RequestProcessor.class).to(ServerSideRequestProcessor.class); } @Provides diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/ServerSideRequestProcessor.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/ServerSideRequestProcessor.java new file mode 100644 index 00000000000..b440e8e706f --- /dev/null +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/jsonrpc/impl/ServerSideRequestProcessor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.core.jsonrpc.impl; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestProcessor; +import org.eclipse.che.commons.lang.concurrent.LoggingUncaughtExceptionHandler; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.TimeUnit.SECONDS; + +@Singleton +public class ServerSideRequestProcessor implements RequestProcessor { + private ExecutorService executorService; + + @PostConstruct + private void postConstruct(){ + ThreadFactory factory = new ThreadFactoryBuilder().setUncaughtExceptionHandler(LoggingUncaughtExceptionHandler.getInstance()) + .setNameFormat(ServerSideRequestProcessor.class.getSimpleName()) + .setDaemon(true) + .build(); + + executorService = newCachedThreadPool(factory); + } + + @PreDestroy + private void preDestroy() { + executorService.shutdown(); + try { + if (executorService.awaitTermination(5, SECONDS)) { + executorService.shutdownNow(); + executorService.awaitTermination(5, SECONDS); + } + } catch (InterruptedException ie) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + @Override + public void process(Runnable runnable) { + executorService.execute(runnable); + } +} diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointIdsHolder.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointIdsHolder.java index b836f445331..3a87ebde3f2 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointIdsHolder.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointIdsHolder.java @@ -14,11 +14,9 @@ import javax.inject.Inject; import javax.inject.Singleton; -import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import static com.google.common.collect.Sets.newConcurrentHashSet; import static java.util.Collections.emptySet; @@ -34,7 +32,7 @@ private void configureSubscribeHandler(RequestHandlerConfigurator configurator) .methodName("event:ws-agent-output:subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { endpointIds.putIfAbsent(endpointId, newConcurrentHashSet()); endpointIds.get(endpointId).add(workspaceId); }); @@ -45,7 +43,7 @@ private void configureUnSubscribeHandler(RequestHandlerConfigurator configurator .methodName("event:ws-agent-output:un-subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { endpointIds.getOrDefault(endpointId, emptySet()).remove(workspaceId); endpointIds.entrySet().removeIf(entry -> entry.getValue().isEmpty()); }); diff --git a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointToMachineNameHolder.java b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointToMachineNameHolder.java index 4f4c2838e8c..37a30cfc74e 100644 --- a/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointToMachineNameHolder.java +++ b/core/che-core-api-core/src/main/java/org/eclipse/che/api/core/util/JsonRpcEndpointToMachineNameHolder.java @@ -17,7 +17,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import static com.google.common.collect.Sets.newConcurrentHashSet; import static java.util.Collections.emptySet; @@ -33,7 +32,7 @@ private void configureSubscribeHandler(RequestHandlerConfigurator configurator) .methodName("event:environment-output:subscribe-by-machine-name") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceIdPlusMachineName) -> { + .withBiConsumer((endpointId, workspaceIdPlusMachineName) -> { endpointIds.putIfAbsent(endpointId, newConcurrentHashSet()); endpointIds.get(endpointId).add(workspaceIdPlusMachineName); }); @@ -44,7 +43,7 @@ private void configureUnSubscribeHandler(RequestHandlerConfigurator configurator .methodName("event:environment-output:un-subscribe-by-machine-name") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceIdPlusMachineName) -> { + .withBiConsumer((endpointId, workspaceIdPlusMachineName) -> { endpointIds.getOrDefault(endpointId, emptySet()).remove(workspaceIdPlusMachineName); endpointIds.entrySet().removeIf(entry -> entry.getValue().isEmpty()); }); diff --git a/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiverTest.java b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiverTest.java index 1e84252c32a..4b85763aa31 100644 --- a/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiverTest.java +++ b/core/che-core-api-core/src/test/java/org/eclipse/che/api/core/jsonrpc/commons/JsonRpcMessageReceiverTest.java @@ -42,6 +42,8 @@ public class JsonRpcMessageReceiverTest { JsonRpcQualifier jsonRpcQualifier; @Mock JsonRpcUnmarshaller jsonRpcUnmarshaller; + @Mock + RequestProcessor requestProcessor; @InjectMocks JsonRpcMessageReceiver jsonRpcMessageReceiver; @@ -96,6 +98,6 @@ public void shouldDispatchRequestIfRequestReceived() throws Exception { jsonRpcMessageReceiver.receive(ENDPOINT_ID, MESSAGE); - verify(requestDispatcher).dispatch(eq(ENDPOINT_ID), any(JsonRpcRequest.class)); + verify(requestProcessor).process(any()); } } diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/EditorFileStatusNotificationOperation.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/EditorFileStatusNotificationOperation.java index 3fb4d19824a..2cab731668e 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/EditorFileStatusNotificationOperation.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/EditorFileStatusNotificationOperation.java @@ -71,7 +71,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("event:file-state-changed") .paramsAsDto(FileStateUpdateDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } public void inject(NotificationManager notificationManager) { diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/ProjectTreeStateNotificationOperation.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/ProjectTreeStateNotificationOperation.java index b951eb3a4df..389f376eebe 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/ProjectTreeStateNotificationOperation.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/event/ng/ProjectTreeStateNotificationOperation.java @@ -52,7 +52,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("event:project-tree-state-changed") .paramsAsDto(ProjectTreeStateUpdateDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ConnectedEventHandler.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ConnectedEventHandler.java index 8e6bdddf467..860e857210b 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ConnectedEventHandler.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ConnectedEventHandler.java @@ -32,7 +32,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("connected") .paramsAsDto(ConnectedEventDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessDiedEventHandler.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessDiedEventHandler.java index 67c323b7948..834f37fb30d 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessDiedEventHandler.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessDiedEventHandler.java @@ -31,7 +31,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("process_died") .paramsAsDto(ProcessDiedEventDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStartedEventHandler.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStartedEventHandler.java index ed86018d46b..6b0dcd6568d 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStartedEventHandler.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStartedEventHandler.java @@ -32,7 +32,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("process_started") .paramsAsDto(ProcessStartedEventDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdErrEventHandler.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdErrEventHandler.java index 806d454701a..6faf26d3631 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdErrEventHandler.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdErrEventHandler.java @@ -32,7 +32,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("process_stderr") .paramsAsDto(ProcessStdErrEventDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdOutEventHandler.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdOutEventHandler.java index 8eb1c7692ba..4c09ed6bf33 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdOutEventHandler.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/machine/execagent/ProcessStdOutEventHandler.java @@ -32,7 +32,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("process_stdout") .paramsAsDto(ProcessStdOutEventDto.class) .noResult() - .withConsumer(this); + .withBiConsumer(this); } @Override diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/JsonRpcModule.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/JsonRpcModule.java index 0f49f24b448..9e14834867b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/JsonRpcModule.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/core/JsonRpcModule.java @@ -13,6 +13,7 @@ import com.google.gwt.inject.client.AbstractGinModule; import com.google.gwt.inject.client.assistedinject.GinFactoryModuleBuilder; +import org.eclipse.che.api.core.jsonrpc.commons.RequestProcessor; import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcComposer; import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcMarshaller; @@ -21,6 +22,7 @@ import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; import org.eclipse.che.ide.api.event.ng.JsonRpcWebSocketAgentEventListener; import org.eclipse.che.ide.api.jsonrpc.WorkspaceMasterJsonRpcInitializer; +import org.eclipse.che.ide.jsonrpc.ClientSideRequestProcessor; import org.eclipse.che.ide.jsonrpc.ElementalJsonRpcComposer; import org.eclipse.che.ide.jsonrpc.ElementalJsonRpcMarshaller; import org.eclipse.che.ide.jsonrpc.ElementalJsonRpcQualifier; @@ -49,5 +51,7 @@ protected void configure() { bind(JsonRpcUnmarshaller.class).to(ElementalJsonRpcUnmarshaller.class); bind(JsonRpcComposer.class).to(ElementalJsonRpcComposer.class); bind(JsonRpcQualifier.class).to(ElementalJsonRpcQualifier.class); + + bind(RequestProcessor.class).to(ClientSideRequestProcessor.class); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentOutputHandler.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentOutputHandler.java index cc30ce1f485..a5e0c95a420 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentOutputHandler.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentOutputHandler.java @@ -27,7 +27,7 @@ public EnvironmentOutputHandler(RequestHandlerConfigurator configurator, EventBu .methodName("event:environment-output:message") .paramsAsDto(MachineLogMessageDto.class) .noResult() - .withConsumer((endpointId, log) -> { + .withBiConsumer((endpointId, log) -> { Log.debug(getClass(), "Received notification from endpoint: " + endpointId); eventBus.fireEvent(new EnvironmentOutputEvent(log.getContent(), log.getMachineName())); }); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentStatusEventHandler.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentStatusEventHandler.java index 0a0ba46cba2..627953b0c8e 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentStatusEventHandler.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/EnvironmentStatusEventHandler.java @@ -27,7 +27,7 @@ public void configureEnvironmentStatusHandler(RequestHandlerConfigurator configu .methodName("event:environment-status:changed") .paramsAsDto(MachineStatusEvent.class) .noResult() - .withConsumer((endpointId, event) -> { + .withBiConsumer((endpointId, event) -> { Log.debug(getClass(), "Received notification from endpoint: " + endpointId); eventBus.fireEvent(new MachineStatusChangedEvent(event)); }); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceAgentOutputHandler.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceAgentOutputHandler.java index ae55196e35c..2c4f898f39f 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceAgentOutputHandler.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceAgentOutputHandler.java @@ -46,6 +46,6 @@ public void configureWorkspaceOutputMessageHandler(AppContext appContext, Reques .methodName("event:ws-agent-output:message") .paramsAsListOfString() .noResult() - .withConsumer(operation); + .withBiConsumer(operation); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceStatusEventHandler.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceStatusEventHandler.java index 83464652888..f542cbc1618 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceStatusEventHandler.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/workspace/events/WorkspaceStatusEventHandler.java @@ -27,7 +27,7 @@ public class WorkspaceStatusEventHandler { .methodName("event:workspace-status:changed") .paramsAsDto(WorkspaceStatusEvent.class) .noResult() - .withConsumer((endpointId, event) -> { + .withBiConsumer((endpointId, event) -> { Log.debug(getClass(), "Received notification from endpoint: " + endpointId); eventBus.fireEvent(new WorkspaceStatusChangedEvent(event)); }); diff --git a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/jsonrpc/ClientSideRequestProcessor.java b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/jsonrpc/ClientSideRequestProcessor.java new file mode 100644 index 00000000000..f8cb1274fef --- /dev/null +++ b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/jsonrpc/ClientSideRequestProcessor.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.jsonrpc; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestProcessor; + +import javax.inject.Singleton; + +@Singleton +public class ClientSideRequestProcessor implements RequestProcessor { + + @Override + public void process(Runnable runnable) { + runnable.run(); + } +} diff --git a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitter.java b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitter.java index 1a1eaf59dd5..01a11025914 100644 --- a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitter.java +++ b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitter.java @@ -45,7 +45,7 @@ public void transmit(String endpointId, String message) { } else { Log.debug(getClass(), "Connection is closed, adding to pending: " + message); - reSender.add(url, message); + reSender.add(endpointId, message); } } } diff --git a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/MessagesReSender.java b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/MessagesReSender.java index 1ef93cf665a..b83055b9111 100644 --- a/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/MessagesReSender.java +++ b/ide/commons-gwt/src/main/java/org/eclipse/che/ide/websocket/impl/MessagesReSender.java @@ -33,46 +33,47 @@ public class MessagesReSender { private final Map> messageRegistry = new HashMap<>(); private final WebSocketConnectionManager connectionManager; + private final UrlResolver urlResolver; + @Inject - public MessagesReSender(WebSocketConnectionManager connectionManager) { + public MessagesReSender(WebSocketConnectionManager connectionManager, UrlResolver urlResolver) { this.connectionManager = connectionManager; + this.urlResolver = urlResolver; } /** * Add message that is to be sent when a connection defined be the URL * is opened again. * - * @param url - * url of a web socket connection + * @param endpointId + * endpointId of websocket connection * @param message * plain text message */ - public void add(String url, String message) { - if (!messageRegistry.containsKey(url)) { - final LinkedList newList = new LinkedList<>(); - messageRegistry.put(url, newList); - } + public void add(String endpointId, String message) { + List messages = messageRegistry.computeIfAbsent(endpointId, k -> new LinkedList<>()); - final List webSocketTransmissions = messageRegistry.get(url); - if (webSocketTransmissions.size() <= MAX_MESSAGES) { - webSocketTransmissions.add(message); + if (messages.size() <= MAX_MESSAGES) { + messages.add(message); } } public void reSend(String url) { - if (!messageRegistry.containsKey(url)) { + String endpointId = urlResolver.resolve(url); + + if (!messageRegistry.containsKey(endpointId)) { return; } - final List messages = messageRegistry.get(url); + List messages = messageRegistry.get(endpointId); if (messages.isEmpty()) { return; } - Log.info(getClass(), "Going to resend websocket messaged: " + messages); + Log.debug(getClass(), "Going to resend websocket messaged: " + messages); - final List backing = new ArrayList<>(messages); + List backing = new ArrayList<>(messages); messages.clear(); for (String message : backing) { diff --git a/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitterTest.java b/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitterTest.java index a8959e041f6..a023653754b 100644 --- a/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitterTest.java +++ b/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/BasicWebSocketMessageTransmitterTest.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.che.ide.websocket.impl; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -19,7 +18,6 @@ import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +29,10 @@ */ @RunWith(MockitoJUnitRunner.class) public class BasicWebSocketMessageTransmitterTest { + private static final String ENDPOINT_ID = "endpointId"; + private static final String URL = "url"; + private static final String MESSAGE = "message"; + @Mock private WebSocketConnectionManager connectionManager; @Mock @@ -42,19 +44,20 @@ public class BasicWebSocketMessageTransmitterTest { @Before public void setUp() throws Exception { - when(urlResolver.getUrl("endpointId")).thenReturn("url"); + when(urlResolver.getUrl(ENDPOINT_ID)).thenReturn(URL); + when(urlResolver.resolve(URL)).thenReturn(ENDPOINT_ID); } @Test public void shouldResolveUrlOnTransmit() { - transmitter.transmit("endpointId", "message"); + transmitter.transmit(ENDPOINT_ID, MESSAGE); - verify(urlResolver).getUrl("endpointId"); + verify(urlResolver).getUrl(ENDPOINT_ID); } @Test public void shouldCheckIfConnectionIsOpenOnTransmit() { - transmitter.transmit("endpointId", "message"); + transmitter.transmit(ENDPOINT_ID, MESSAGE); verify(connectionManager).isConnectionOpen(anyString()); } @@ -63,19 +66,19 @@ public void shouldCheckIfConnectionIsOpenOnTransmit() { public void shouldSendMessageIfConnectionIsOpenOnTransmit() { when(connectionManager.isConnectionOpen(anyString())).thenReturn(true); - transmitter.transmit("endpointId", "message"); + transmitter.transmit(ENDPOINT_ID, MESSAGE); - verify(connectionManager).sendMessage("url", "message"); - verify(reSender, never()).add("url", "message"); + verify(connectionManager).sendMessage(URL, MESSAGE); + verify(reSender, never()).add(URL, MESSAGE); } @Test public void shouldAddMessageToReSenderIfConnectionIsNotOpenOnTransmit() { when(connectionManager.isConnectionOpen(anyString())).thenReturn(false); - transmitter.transmit("endpointId", "message"); + transmitter.transmit(ENDPOINT_ID, MESSAGE); - verify(connectionManager, never()).sendMessage("url", "message"); - verify(reSender).add("url", "message"); + verify(connectionManager, never()).sendMessage(URL, MESSAGE); + verify(reSender).add(ENDPOINT_ID, MESSAGE); } } diff --git a/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/MessagesReSenderTest.java b/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/MessagesReSenderTest.java index a4d7d6dc376..f6a895d8a5f 100644 --- a/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/MessagesReSenderTest.java +++ b/ide/commons-gwt/src/test/java/org/eclipse/che/ide/websocket/impl/MessagesReSenderTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.che.ide.websocket.impl; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -31,14 +32,22 @@ public class MessagesReSenderTest { @Mock private WebSocketConnectionManager connectionManager; + @Mock + private UrlResolver urlResolver; @InjectMocks private MessagesReSender reSender; + @Before + public void setUp() throws Exception { + when(urlResolver.getUrl("endpointId")).thenReturn("url"); + when(urlResolver.resolve("url")).thenReturn("endpointId"); + } + @Test public void shouldResendAllMessages() { - reSender.add("url", "1"); - reSender.add("url", "2"); - reSender.add("url", "3"); + reSender.add("endpointId", "1"); + reSender.add("endpointId", "2"); + reSender.add("endpointId", "3"); when(connectionManager.isConnectionOpen("url")).thenReturn(true); @@ -48,9 +57,9 @@ public void shouldResendAllMessages() { @Test public void shouldStopSendingIfSessionIsClosed() { - reSender.add("url", "1"); - reSender.add("url", "2"); - reSender.add("url", "3"); + reSender.add("endpointId", "1"); + reSender.add("endpointId", "2"); + reSender.add("endpointId", "3"); final int[] i = {0}; when(connectionManager.isConnectionOpen("url")).thenAnswer(invocation -> (i[0]++ <= 1)); diff --git a/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/AbstractDebugger.java b/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/AbstractDebugger.java index 7287995e406..a6ff236f0ae 100644 --- a/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/AbstractDebugger.java +++ b/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/AbstractDebugger.java @@ -256,7 +256,7 @@ private void startCheckingEvents() { .methodName(EVENT_DEBUGGER_MESSAGE_SUSPEND) .paramsAsDto(SuspendEventDto.class) .noResult() - .withConsumer((endpointId, event) -> { + .withBiConsumer((endpointId, event) -> { Log.debug(getClass(), "Received suspend message from endpoint: " + endpointId); onEventListReceived(event); }); @@ -265,7 +265,7 @@ private void startCheckingEvents() { .methodName(EVENT_DEBUGGER_MESSAGE_DISCONNECT) .paramsAsDto(DisconnectEventDto.class) .noResult() - .withConsumer((endpointId, event) -> { + .withBiConsumer((endpointId, event) -> { Log.debug(getClass(), "Received disconnect message from endpoint: " + endpointId); onEventListReceived(event); }); @@ -274,7 +274,7 @@ private void startCheckingEvents() { .methodName(EVENT_DEBUGGER_MESSAGE_BREAKPOINT) .paramsAsDto(BreakpointActivatedEventDto.class) .noResult() - .withConsumer((endpointId, event) -> { + .withBiConsumer((endpointId, event) -> { Log.debug(getClass(), "Received breakpoint activated message from endpoint: " + endpointId); onEventListReceived(event); }); diff --git a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitCheckoutStatusNotificationHandler.java b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitCheckoutStatusNotificationHandler.java index 98ac4d1e326..60c0d6c92cf 100644 --- a/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitCheckoutStatusNotificationHandler.java +++ b/plugins/plugin-git/che-plugin-git-ext-git/src/main/java/org/eclipse/che/ide/ext/git/client/GitCheckoutStatusNotificationHandler.java @@ -47,7 +47,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName("event:git-checkout") .paramsAsDto(GitCheckoutEventDto.class) .noResult() - .withConsumer(this::apply); + .withBiConsumer(this::apply); } public void apply(String endpointId, GitCheckoutEventDto dto) { diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml b/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml index 54b480fa4ec..c4313a48973 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/pom.xml @@ -50,6 +50,10 @@ javax.validation validation-api + + org.eclipse.che.core + che-core-api-core + org.eclipse.che.core che-core-api-languageserver-shared @@ -206,7 +210,6 @@ - org.apache.maven.plugins maven-dependency-plugin diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java index 6f6d4bea6f3..5e493439b6b 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/inject/LanguageServerGinModule.java @@ -26,6 +26,8 @@ import org.eclipse.che.plugin.languageserver.ide.editor.signature.LanguageServerSignatureHelpFactory; import org.eclipse.che.plugin.languageserver.ide.location.OpenLocationPresenterFactory; import org.eclipse.che.plugin.languageserver.ide.registry.LanguageServerRegistry; +import org.eclipse.che.plugin.languageserver.ide.service.PublishDiagnosticsReceiver; +import org.eclipse.che.plugin.languageserver.ide.service.ShowMessageJsonRpcReceiver; /** * @author Anatolii Bazko @@ -48,6 +50,9 @@ protected void configure() { GinMapBinder wsAgentComponentsBinder = GinMapBinder.newMapBinder(binder(), String.class, WsAgentComponent.class); wsAgentComponentsBinder.addBinding("Load Language Server file types.").to(LanguageServerFileTypeRegister.class); + + bind(PublishDiagnosticsReceiver.class).asEagerSingleton(); + bind(ShowMessageJsonRpcReceiver.class).asEagerSingleton(); } } diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/PublishDiagnosticsReceiver.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/PublishDiagnosticsReceiver.java new file mode 100644 index 00000000000..2fc6e590206 --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/PublishDiagnosticsReceiver.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.languageserver.ide.service; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.plugin.languageserver.ide.editor.PublishDiagnosticsProcessor; +import org.eclipse.lsp4j.PublishDiagnosticsParams; + +/** + * Subscribes and receives JSON-RPC messages related to 'textDocument/publishDiagnostics' events + */ +@Singleton +public class PublishDiagnosticsReceiver { + @Inject + private void configureReceiver(Provider provider, RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName("textDocument/publishDiagnostics") + .paramsAsDto(PublishDiagnosticsParams.class) + .noResult() + .withConsumer(params -> provider.get().processDiagnostics(params)); + } + + @Inject + private void subscribe(RequestTransmitter transmitter) { + transmitter.newRequest() + .endpointId("ws-agent") + .methodName("textDocument/publishDiagnostics/subscribe") + .noParams() + .sendAndSkipResult(); + } +} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ShowMessageJsonRpcReceiver.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ShowMessageJsonRpcReceiver.java new file mode 100644 index 00000000000..1325465dc89 --- /dev/null +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/ShowMessageJsonRpcReceiver.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.plugin.languageserver.ide.service; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.plugin.languageserver.ide.editor.ShowMessageProcessor; +import org.eclipse.lsp4j.ShowMessageRequestParams; + +/** + * Subscribes and receives JSON-RPC messages related to 'window/showMessage' events + */ +@Singleton +public class ShowMessageJsonRpcReceiver { + @Inject + private void configureReceiver(Provider provider, RequestHandlerConfigurator configurator) { + configurator.newConfiguration() + .methodName("window/showMessage") + .paramsAsDto(ShowMessageRequestParams.class) + .noResult() + .withConsumer(params -> provider.get().processNotification(params)); + } + + @Inject + private void subscribe(RequestTransmitter transmitter) { + transmitter.newRequest() + .endpointId("ws-agent") + .methodName("window/showMessage/subscribe") + .noParams() + .sendAndSkipResult(); + } +} diff --git a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/TextDocumentServiceClient.java b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/TextDocumentServiceClient.java index 2a650f5b1cb..23b029f0c9d 100644 --- a/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/TextDocumentServiceClient.java +++ b/plugins/plugin-languageserver/che-plugin-languageserver-ide/src/main/java/org/eclipse/che/plugin/languageserver/ide/service/TextDocumentServiceClient.java @@ -13,25 +13,14 @@ import com.google.inject.Inject; import com.google.inject.Singleton; +import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcError; +import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; import org.eclipse.che.api.languageserver.shared.model.ExtendedCompletionItem; import org.eclipse.che.api.languageserver.shared.model.ExtendedCompletionList; -import org.eclipse.che.api.promises.client.Operation; -import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.ide.api.app.AppContext; -import org.eclipse.che.ide.api.machine.WsAgentStateController; -import org.eclipse.che.ide.api.notification.NotificationManager; -import org.eclipse.che.ide.api.notification.StatusNotification; -import org.eclipse.che.ide.dto.JsonSerializable; -import org.eclipse.che.ide.rest.AsyncRequestFactory; -import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; -import org.eclipse.che.ide.rest.Unmarshallable; -import org.eclipse.che.ide.util.loging.Log; -import org.eclipse.che.ide.websocket.MessageBus; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.SubscriptionHandler; -import org.eclipse.che.plugin.languageserver.ide.editor.PublishDiagnosticsProcessor; -import org.eclipse.che.plugin.languageserver.ide.editor.ShowMessageProcessor; +import org.eclipse.che.api.promises.client.PromiseError; +import org.eclipse.che.api.promises.client.js.Promises; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.CompletionItem; @@ -46,324 +35,223 @@ import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.ReferenceParams; -import org.eclipse.lsp4j.ShowMessageRequestParams; import org.eclipse.lsp4j.SignatureHelp; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.services.TextDocumentService; import java.util.List; -import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; -import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; -import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; - -/** - * @author Anatolii Bazko - */ @Singleton public class TextDocumentServiceClient { - private final DtoUnmarshallerFactory unmarshallerFactory; - private final AsyncRequestFactory asyncRequestFactory; - private final AppContext appContext; - private final NotificationManager notificationManager; - private final PublishDiagnosticsProcessor publishDiagnosticsProcessor; - private final ShowMessageProcessor showMessageProcessor; + private final RequestTransmitter requestTransmitter; @Inject - public TextDocumentServiceClient( - final DtoUnmarshallerFactory unmarshallerFactory, - final NotificationManager notificationManager, - final AppContext appContext, - final AsyncRequestFactory asyncRequestFactory, - final WsAgentStateController wsAgentStateController, - final PublishDiagnosticsProcessor publishDiagnosticsProcessor, - final ShowMessageProcessor showMessageProcessor) { - this.unmarshallerFactory = unmarshallerFactory; - this.notificationManager = notificationManager; - this.appContext = appContext; - this.asyncRequestFactory = asyncRequestFactory; - this.publishDiagnosticsProcessor = publishDiagnosticsProcessor; - wsAgentStateController.getMessageBus().then(new Operation() { - @Override - public void apply(MessageBus messageBus) throws OperationException { - subscribeToPublishDiagnostics(messageBus); - subscribeToShowMessages(messageBus); - } - }); - this.showMessageProcessor = showMessageProcessor; + public TextDocumentServiceClient(RequestTransmitter requestTransmitter) { + this.requestTransmitter = requestTransmitter; } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#completion(io.typefox.lsapi.TextDocumentPositionParams)} + * GWT client implementation of {@link TextDocumentService#completion(TextDocumentPositionParams)} * * @param position * @return */ public Promise completion(TextDocumentPositionParams position) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/completion"; - Unmarshallable unmarshaller = unmarshallerFactory.newUnmarshaller(ExtendedCompletionList.class); - return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)position).toJson()).send(unmarshaller); + return transmitDtoAndReceiveDto(position, "textDocument/completion", ExtendedCompletionList.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#resolveCompletionItem(CompletionItem)} + * GWT client implementation of {@link TextDocumentService#resolveCompletionItem(CompletionItem)} * * @param completionItem * @return */ public Promise resolveCompletionItem(CompletionItem completionItem) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/completionItem/resolve"; - Unmarshallable unmarshaller = unmarshallerFactory.newUnmarshaller(ExtendedCompletionItem.class); - return asyncRequestFactory.createPostRequest(requestUrl, completionItem) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).send(unmarshaller); + return transmitDtoAndReceiveDto(completionItem, "textDocument/completionItem/resolve", ExtendedCompletionItem.class); + } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#documentSymbol(io.typefox.lsapi.DocumentSymbolParams)} + * GWT client implementation of {@link TextDocumentService#documentSymbol(DocumentSymbolParams)} * * @param params * @return */ public Promise> documentSymbol(DocumentSymbolParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/documentSymbol"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(SymbolInformation.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/documentSymbol", SymbolInformation.class); + } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#references(io.typefox.lsapi.ReferenceParams)} + * GWT client implementation of {@link TextDocumentService#references(ReferenceParams)} * * @param params * @return */ public Promise> references(ReferenceParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/references"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(Location.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/references", Location.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#references(io.typefox.lsapi.ReferenceParams)} + * GWT client implementation of {@link TextDocumentService#references(ReferenceParams)} * * @param params * @return */ public Promise> definition(TextDocumentPositionParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/definition"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(Location.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/definition", Location.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#hover(io.typefox.lsapi.TextDocumentPositionParams)} + * GWT client implementation of {@link TextDocumentService#hover(TextDocumentPositionParams)} * * @param params * @return */ public Promise hover(TextDocumentPositionParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/hover"; - Unmarshallable unmarshaller = unmarshallerFactory.newUnmarshaller(Hover.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDto(params, "textDocument/hover", Hover.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#signatureHelp(io.typefox.lsapi.TextDocumentPositionParams)} + * GWT client implementation of {@link TextDocumentService#signatureHelp(TextDocumentPositionParams)} * * @param params * @return */ public Promise signatureHelp(TextDocumentPositionParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/signatureHelp"; - Unmarshallable unmarshaller = unmarshallerFactory.newUnmarshaller(SignatureHelp.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDto(params, "textDocument/signatureHelp", SignatureHelp.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentFormattingParams)} + * GWT client implementation of {@link TextDocumentService#formatting(DocumentFormattingParams)} * * @param params * @return */ public Promise> formatting(DocumentFormattingParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/formatting"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/formatting", TextEdit.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentRangeFormattingParams)} + * GWT client implementation of {@link TextDocumentService#formatting(DocumentFormattingParams)} * * @param params * @return */ public Promise> rangeFormatting(DocumentRangeFormattingParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/rangeFormatting"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/rangeFormatting", TextEdit.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#formatting(io.typefox.lsapi.DocumentFormattingParams)} + * GWT client implementation of {@link TextDocumentService#formatting(DocumentFormattingParams)} * * @param params * @return */ public Promise> onTypeFormatting(DocumentOnTypeFormattingParams params) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/onTypeFormatting"; - Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(TextEdit.class); - return asyncRequestFactory.createPostRequest(requestUrl, params) - .header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON) - .send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/onTypeFormatting", TextEdit.class); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didChange(io.typefox.lsapi.DidChangeTextDocumentParams)} + * GWT client implementation of {@link TextDocumentService#didChange(DidChangeTextDocumentParams)} * - * @param change + * @param params * @return */ - public void didChange(DidChangeTextDocumentParams change) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didChange"; - asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)change).toJson()).send(); + public void didChange(DidChangeTextDocumentParams params) { + transmitDtoAndReceiveNothing(params, "textDocument/didChange"); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didOpen(io.typefox.lsapi.DidOpenTextDocumentParams)} + * GWT client implementation of {@link TextDocumentService#didOpen(DidOpenTextDocumentParams)} * - * @param openEvent + * @param params * @return */ - public void didOpen(DidOpenTextDocumentParams openEvent) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didOpen"; - asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)openEvent).toJson()).send(); + public void didOpen(DidOpenTextDocumentParams params) { + transmitDtoAndReceiveNothing(params, "textDocument/didOpen"); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didClose(io.typefox.lsapi.DidCloseTextDocumentParams)} + * GWT client implementation of {@link TextDocumentService#didClose(DidCloseTextDocumentParams)} * - * @param closeEvent + * @param params * @return */ - public void didClose(DidCloseTextDocumentParams closeEvent) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didClose"; - asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)closeEvent).toJson()).send(); + public void didClose(DidCloseTextDocumentParams params) { + transmitDtoAndReceiveNothing(params, "textDocument/didClose"); } /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#didSave(io.typefox.lsapi.DidSaveTextDocumentParams)} + * GWT client implementation of {@link TextDocumentService#didSave(DidSaveTextDocumentParams)} * - * @param saveEvent + * @param params * @return */ - public void didSave(DidSaveTextDocumentParams saveEvent) { - String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/didSave"; - asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)saveEvent).toJson()).send(); + public void didSave(DidSaveTextDocumentParams params) { + transmitDtoAndReceiveNothing(params, "textDocument/didSave"); } - /** - * GWT client implementation of {@link io.typefox.lsapi.TextDocumentService#documentHighlight(io.typefox.lsapi.TextDocumentPositionParams + * GWT client implementation of {@link TextDocumentService#documentHighlight(TextDocumentPositionParams * position)} * - * @param position - * @return a {@link Promise} of an array of {@link DocumentHighlightDTO} which will be computed by the language server. + * @param params + * @return a {@link Promise} of an array of {@link DocumentHighlight} which will be computed by the language server. */ - public Promise documentHighlight(TextDocumentPositionParams position) { - final String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/documentHighlight"; - final Unmarshallable unmarshaller = unmarshallerFactory.newUnmarshaller(DocumentHighlight.class); - return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)position).toJson()).send(unmarshaller); + public Promise documentHighlight(TextDocumentPositionParams params) { + return transmitDtoAndReceiveDto(params, "textDocument/documentHighlight", DocumentHighlight.class); } - public Promise> codeAction(CodeActionParams params) { - final String requestUrl = appContext.getDevMachine().getWsAgentBaseUrl() + "/languageserver/textDocument/codeAction"; - final Unmarshallable> unmarshaller = unmarshallerFactory.newListUnmarshaller(Command.class); - return asyncRequestFactory.createPostRequest(requestUrl, null).header(ACCEPT, APPLICATION_JSON) - .header(CONTENT_TYPE, APPLICATION_JSON).data(((JsonSerializable)params).toJson()).send(unmarshaller); + return transmitDtoAndReceiveDtoList(params, "textDocument/codeAction", Command.class); } - /** - * Subscribes to websocket for 'textDocument/publishDiagnostics' notifications. - */ - private void subscribeToPublishDiagnostics(final MessageBus messageBus) { - org.eclipse.che.ide.websocket.rest.Unmarshallable unmarshaller = - unmarshallerFactory.newWSUnmarshaller(PublishDiagnosticsParams.class); - try { - messageBus.subscribe("languageserver/textDocument/publishDiagnostics", - new SubscriptionHandler(unmarshaller) { - @Override - protected void onMessageReceived(PublishDiagnosticsParams statusEvent) { - publishDiagnosticsProcessor.processDiagnostics(statusEvent); - } + private Promise transmitDtoAndReceiveDto(Object jsonSerializable, String name, Class resultDtoClass) { + return Promises.create((resolve, reject) -> requestTransmitter.newRequest() + .endpointId("ws-agent") + .methodName(name) + .paramsAsDto(jsonSerializable) + .sendAndReceiveResultAsDto(resultDtoClass) + .onSuccess(resolve::apply) + .onFailure(error -> reject.apply(getPromiseError(error)))); + } - @Override - protected void onErrorReceived(Throwable exception) { - notificationManager.notify(exception.getMessage(), StatusNotification.Status.FAIL, - StatusNotification.DisplayMode.NOT_EMERGE_MODE); - } - }); - } catch (WebSocketException exception) { - Log.error(getClass(), exception); - } + private Promise> transmitDtoAndReceiveDtoList(Object jsonSerializable, String name, Class resultDtoClass) { + return Promises.create((resolve, reject) -> requestTransmitter.newRequest() + .endpointId("ws-agent") + .methodName(name) + .paramsAsDto(jsonSerializable) + .sendAndReceiveResultAsListOfDto(resultDtoClass) + .onSuccess(resolve::apply) + .onFailure(error -> reject.apply(getPromiseError(error)))); } - /** - * Subscribes to websocket for 'window/showMessage' notifications. - */ - private void subscribeToShowMessages(final MessageBus messageBus) { - final org.eclipse.che.ide.websocket.rest.Unmarshallable unmarshaller = - unmarshallerFactory.newWSUnmarshaller(ShowMessageRequestParams.class); - try { - messageBus.subscribe("languageserver/window/showMessage", - new SubscriptionHandler(unmarshaller) { - @Override - protected void onMessageReceived(ShowMessageRequestParams ShowMessageRequestParams) { - showMessageProcessor.processNotification(ShowMessageRequestParams); - } + private void transmitDtoAndReceiveNothing(Object jsonSerializable, String name) { + requestTransmitter.newRequest() + .endpointId("ws-agent") + .methodName(name) + .paramsAsDto(jsonSerializable) + .sendAndSkipResult(); + } + + private PromiseError getPromiseError(JsonRpcError jsonRpcError) { + return new PromiseError() { + @Override + public String getMessage() { + return jsonRpcError.getMessage(); + } - @Override - protected void onErrorReceived(Throwable exception) { - notificationManager.notify(exception.getMessage(), StatusNotification.Status.FAIL, - StatusNotification.DisplayMode.NOT_EMERGE_MODE); - } - }); - } catch (WebSocketException exception) { - Log.error(getClass(), exception); - } + @Override + public Throwable getCause() { + return new JsonRpcException(jsonRpcError.getCode(), jsonRpcError.getMessage()); + } + }; } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java index 7b88e4f23d8..bc771bf5314 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/LanguageServerModule.java @@ -15,8 +15,8 @@ import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; import org.eclipse.che.api.languageserver.messager.InitializeEventMessenger; -import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger; -import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger; +import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsJsonRpcTransmitter; +import org.eclipse.che.api.languageserver.messager.ShowMessageJsonRpcTransmitter; import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl; import org.eclipse.che.api.languageserver.registry.ServerInitializer; @@ -32,11 +32,12 @@ protected void configure() { bind(LanguageServerRegistry.class).to(LanguageServerRegistryImpl.class); bind(ServerInitializer.class).to(ServerInitializerImpl.class); bind(LanguageRegistryService.class); - bind(TextDocumentService.class); bind(WorkspaceService.class); - bind(PublishDiagnosticsParamsMessenger.class); - bind(ShowMessageMessenger.class); bind(InitializeEventMessenger.class); Multibinder.newSetBinder(binder(), LanguageServerLauncher.class); + + bind(TextDocumentService.class).asEagerSingleton(); + bind(PublishDiagnosticsParamsJsonRpcTransmitter.class).asEagerSingleton(); + bind(ShowMessageJsonRpcTransmitter.class).asEagerSingleton(); } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsJsonRpcTransmitter.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsJsonRpcTransmitter.java new file mode 100644 index 00000000000..6223c91abfa --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsJsonRpcTransmitter.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.languageserver.messager; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.PublishDiagnosticsParamsDto; +import org.eclipse.lsp4j.PublishDiagnosticsParams; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Transmits 'textDocument/publishDiagnostics' over the JSON-RPC + */ +@Singleton +public class PublishDiagnosticsParamsJsonRpcTransmitter { + private final Set endpointIds = new CopyOnWriteArraySet<>(); + + @Inject + private void subscribe(EventService eventService, RequestTransmitter requestTransmitter) { + eventService.subscribe(event -> { + event.setUri(event.getUri().substring(16)); + endpointIds.forEach(endpointId -> requestTransmitter.newRequest() + .endpointId(endpointId) + .methodName("textDocument/publishDiagnostics") + .paramsAsDto(new PublishDiagnosticsParamsDto(event)) + .sendAndSkipResult()); + }, PublishDiagnosticsParams.class); + } + + @Inject + private void configureSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler.newConfiguration() + .methodName("textDocument/publishDiagnostics/subscribe") + .noParams() + .noResult() + .withConsumer(endpointIds::add); + } + + @Inject + private void configureUnSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler.newConfiguration() + .methodName("textDocument/publishDiagnostics/unsubscribe") + .noParams() + .noResult() + .withConsumer(endpointIds::remove); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsMessenger.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsMessenger.java deleted file mode 100644 index de033fd7d5b..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/PublishDiagnosticsParamsMessenger.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2017 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.languageserver.messager; - -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.core.notification.EventSubscriber; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.everrest.websockets.WSConnectionContext; -import org.everrest.websockets.message.ChannelBroadcastMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.websocket.EncodeException; -import java.io.IOException; - -@Singleton -public class PublishDiagnosticsParamsMessenger implements EventSubscriber { - private final static Logger LOG = LoggerFactory.getLogger(PublishDiagnosticsParamsMessenger.class); - - private final EventService eventService; - - @Inject - public PublishDiagnosticsParamsMessenger(EventService eventService) { - this.eventService = eventService; - } - - public void onEvent(final PublishDiagnosticsParams event) { - try { - event.setUri(event.getUri().substring(16)); - final ChannelBroadcastMessage bm = new ChannelBroadcastMessage(); - bm.setChannel("languageserver/textDocument/publishDiagnostics"); - bm.setBody(new DtoServerImpls.PublishDiagnosticsParamsDto(event).toJson()); - WSConnectionContext.sendMessage(bm); - } catch (EncodeException | IOException e) { - LOG.error(e.getMessage(), e); - } - } - - @PostConstruct - public void subscribe() { - eventService.subscribe(this); - } - - @PreDestroy - public void unsubscribe() { - eventService.unsubscribe(this); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageJsonRpcTransmitter.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageJsonRpcTransmitter.java new file mode 100644 index 00000000000..8df5c6ea5f0 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageJsonRpcTransmitter.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.languageserver.messager; + +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; +import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter; +import org.eclipse.che.api.core.notification.EventService; +import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.MessageParamsDto; +import org.eclipse.lsp4j.MessageParams; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Transmits 'textDocument/publishDiagnostics' over the JSON-RPC + */ +@Singleton +public class ShowMessageJsonRpcTransmitter { + private final Set endpointIds = new CopyOnWriteArraySet<>(); + + @Inject + private void subscribe(EventService eventService, RequestTransmitter requestTransmitter) { + eventService.subscribe(event -> endpointIds.forEach(endpointId -> requestTransmitter.newRequest() + .endpointId(endpointId) + .methodName("window/showMessage") + .paramsAsDto(new MessageParamsDto(event)) + .sendAndSkipResult()), + MessageParams.class); + } + + @Inject + private void configureSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler.newConfiguration() + .methodName("window/showMessage/subscribe") + .noParams() + .noResult() + .withConsumer(endpointIds::add); + } + + @Inject + private void configureUnSubscribeHandler(RequestHandlerConfigurator requestHandler) { + requestHandler.newConfiguration() + .methodName("window/showMessage/unsubscribe") + .noParams() + .noResult() + .withConsumer(endpointIds::remove); + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageMessenger.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageMessenger.java deleted file mode 100644 index b4863613533..00000000000 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/messager/ShowMessageMessenger.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2012-2017 Codenvy, S.A. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Codenvy, S.A. - initial API and implementation - *******************************************************************************/ -package org.eclipse.che.api.languageserver.messager; - -import org.eclipse.che.api.core.notification.EventService; -import org.eclipse.che.api.core.notification.EventSubscriber; -import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls; -import org.eclipse.lsp4j.MessageParams; -import org.everrest.websockets.WSConnectionContext; -import org.everrest.websockets.message.ChannelBroadcastMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.websocket.EncodeException; -import java.io.IOException; - -/** - * {@link EventSubscriber} for incoming window/showMessage notifications. - * - * @author xcoulon - */ -@Singleton -public class ShowMessageMessenger implements EventSubscriber { - - private final static Logger LOG = LoggerFactory.getLogger(ShowMessageMessenger.class); - - private final EventService eventService; - - @Inject - public ShowMessageMessenger(EventService eventService) { - this.eventService = eventService; - } - - public void onEvent(final MessageParams event) { - try { - final ChannelBroadcastMessage bm = new ChannelBroadcastMessage(); - bm.setChannel("languageserver/window/showMessage"); - bm.setBody(new DtoServerImpls.MessageParamsDto(event).toJson()); - WSConnectionContext.sendMessage(bm); - } catch (EncodeException | IOException e) { - LOG.error(e.getMessage(), e); - } - } - - @PostConstruct - public void subscribe() { - eventService.subscribe(this); - } - - @PreDestroy - public void unsubscribe() { - eventService.unsubscribe(this); - } -} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java index 8d8832cdaf7..c4aae3534de 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImpl.java @@ -13,10 +13,9 @@ import com.google.inject.Inject; import com.google.inject.Singleton; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.languageserver.exception.LanguageServerException; import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger; -import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger; import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.InitializeParams; @@ -50,7 +49,7 @@ public class ServerInitializerImpl implements ServerInitializer { private static final int PROCESS_ID = getProcessId(); private static final String CLIENT_NAME = "EclipseChe"; - private final List observers; + private final List observers = new ArrayList<>(); private final ConcurrentHashMap languageIdToServers; private final ConcurrentHashMap serversToInitResult; @@ -58,17 +57,15 @@ public class ServerInitializerImpl implements ServerInitializer { private LanguageClient languageClient; @Inject - public ServerInitializerImpl(final PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger, - final ShowMessageMessenger showMessageMessenger) { - this.observers = new ArrayList<>(); + public ServerInitializerImpl(EventService eventService) { this.languageIdToServers = new ConcurrentHashMap<>(); this.serversToInitResult = new ConcurrentHashMap<>(); + languageClient = new LanguageClient() { @Override public void telemetryEvent(Object object) { // TODO Auto-generated method stub - } @Override @@ -78,12 +75,12 @@ public CompletableFuture showMessageRequest(ShowMessageRequestParams reque @Override public void showMessage(MessageParams messageParams) { - showMessageMessenger.onEvent(messageParams); + eventService.publish(messageParams); } @Override public void publishDiagnostics(PublishDiagnosticsParams diagnostics) { - publishDiagnosticsParamsMessenger.onEvent(diagnostics); + eventService.publish(diagnostics); } @Override diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java index b7891736f86..418ea639c5d 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/LanguageRegistryService.java @@ -79,6 +79,6 @@ public List getRegisteredLanguages() { @Path("initialize") public void initialize(@QueryParam("path") String path) throws LanguageServerException { //in most cases starts new LS if not already started - registry.findServer(TextDocumentService.prefixURI(path)); + registry.findServer(TextDocumentServiceUtils.prefixURI(path)); } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java index 681d6c23fa4..558fe175ec7 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentService.java @@ -12,6 +12,8 @@ import com.google.inject.Singleton; +import org.eclipse.che.api.core.jsonrpc.commons.JsonRpcException; +import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator; import org.eclipse.che.api.languageserver.exception.LanguageServerException; import org.eclipse.che.api.languageserver.registry.LanguageServerRegistry; import org.eclipse.che.api.languageserver.registry.LanguageServerRegistryImpl; @@ -23,7 +25,6 @@ import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SignatureHelpDto; import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.SymbolInformationDto; import org.eclipse.che.api.languageserver.server.dto.DtoServerImpls.TextEditDto; -import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.DidChangeTextDocumentParams; import org.eclipse.lsp4j.DidCloseTextDocumentParams; import org.eclipse.lsp4j.DidOpenTextDocumentParams; @@ -35,278 +36,294 @@ import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.ReferenceParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextDocumentPositionParams; import org.eclipse.lsp4j.services.LanguageServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.PostConstruct; import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; +import static org.eclipse.che.api.languageserver.service.TextDocumentServiceUtils.prefixURI; +import static org.eclipse.che.api.languageserver.service.TextDocumentServiceUtils.removePrefixUri; + /** - * REST API for the textDoc + * Json RPC API for the textDoc *

* Dispatches onto the {@link LanguageServerRegistryImpl}. */ @Singleton -@Path("languageserver/textDocument") public class TextDocumentService { + private static final Logger LOG = LoggerFactory.getLogger(TextDocumentService.class); - private static final String FILE_PROJECTS = "file:///projects"; - - private final LanguageServerRegistry languageServerRegistry; + private final LanguageServerRegistry languageServerRegistry; + private final RequestHandlerConfigurator requestHandler; @Inject - public TextDocumentService(LanguageServerRegistry languageServerRegistry) { + public TextDocumentService(LanguageServerRegistry languageServerRegistry, RequestHandlerConfigurator requestHandler) { this.languageServerRegistry = languageServerRegistry; + this.requestHandler = requestHandler; } - static String prefixURI(String relativePath) { - return FILE_PROJECTS + relativePath; + @PostConstruct + public void configureMethods() { + dtoToDtoList("definition", TextDocumentPositionParams.class, LocationDto.class, this::definition); + dtoToDtoList("documentSymbol", DocumentSymbolParams.class, SymbolInformationDto.class, this::documentSymbol); + dtoToDtoList("formatting", DocumentFormattingParams.class, TextEditDto.class, this::formatting); + dtoToDtoList("rangeFormatting", DocumentRangeFormattingParams.class, TextEditDto.class, this::rangeFormatting); + dtoToDtoList("references", ReferenceParams.class, LocationDto.class, this::references); + dtoToDtoList("onTypeFormatting", DocumentOnTypeFormattingParams.class, TextEditDto.class, this::onTypeFormatting); + + dtoToDto("completionItem/resolve", ExtendedCompletionItemDto.class, CompletionItemDto.class, this::completionItemResolve); + dtoToDto("documentHighlight", TextDocumentPositionParams.class, DocumentHighlight.class, this::documentHighlight); + dtoToDto("completion", TextDocumentPositionParams.class, CompletionListDto.class, this::completion); + dtoToDto("hover", TextDocumentPositionParams.class, HoverDto.class, this::hover); + dtoToDto("signatureHelp", TextDocumentPositionParams.class, SignatureHelpDto.class, this::signatureHelp); + + dtoToNothing("didChange", DidChangeTextDocumentParams.class, this::didChange); + dtoToNothing("didClose", DidCloseTextDocumentParams.class, this::didClose); + dtoToNothing("didOpen", DidOpenTextDocumentParams.class, this::didOpen); + dtoToNothing("didSave", DidSaveTextDocumentParams.class, this::didSave); } - static String removePrefixUri(String uri) { - if (uri.startsWith(FILE_PROJECTS)) { - return uri.substring(FILE_PROJECTS.length()); + private CompletionListDto completion(TextDocumentPositionParams textDocumentPositionParams) { + try { + TextDocumentIdentifier textDocument = textDocumentPositionParams.getTextDocument(); + textDocument.setUri(prefixURI(textDocument.getUri())); + textDocumentPositionParams.setUri(prefixURI(textDocumentPositionParams.getUri())); + LanguageServer server = getServer(textDocument.getUri()); + return server != null ? new CompletionListDto(server.getTextDocumentService().completion(textDocumentPositionParams).get()) + : null; + + } catch (LanguageServerException | InterruptedException | ExecutionException e) { + throw new JsonRpcException(-27000, e.getMessage()); } - return uri; } - @SuppressWarnings("deprecation") - @POST - @Path("completion") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public CompletionListDto completion(TextDocumentPositionParams textDocumentPositionParams) - throws InterruptedException, ExecutionException, LanguageServerException { - textDocumentPositionParams.getTextDocument().setUri(prefixURI(textDocumentPositionParams.getTextDocument().getUri())); - textDocumentPositionParams.setUri(prefixURI(textDocumentPositionParams.getUri())); - LanguageServer server = getServer(textDocumentPositionParams.getTextDocument().getUri()); - if (server == null) { - return null; - } - CompletionList result = server.getTextDocumentService().completion(textDocumentPositionParams).get(); + private List documentSymbol(DocumentSymbolParams documentSymbolParams) { + try { + documentSymbolParams.getTextDocument().setUri(prefixURI(documentSymbolParams.getTextDocument().getUri())); + LanguageServer server = getServer(documentSymbolParams.getTextDocument().getUri()); + return server == null ? Collections.emptyList() : server.getTextDocumentService() + .documentSymbol(documentSymbolParams) + .get() + .stream() + .map(SymbolInformationDto::new) + .collect(Collectors.toList()); - return new CompletionListDto(result); - } + } catch (ExecutionException | InterruptedException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); - @POST - @Path("documentSymbol") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List documentSymbol(DocumentSymbolParams documentSymbolParams) - throws ExecutionException, InterruptedException, LanguageServerException { - documentSymbolParams.getTextDocument().setUri(prefixURI(documentSymbolParams.getTextDocument().getUri())); - LanguageServer server = getServer(documentSymbolParams.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); } - - return server.getTextDocumentService().documentSymbol(documentSymbolParams).get().stream().map(o -> new SymbolInformationDto(o)) - .collect(Collectors.toList()); } - @POST - @Path("references") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List references(ReferenceParams params) - throws ExecutionException, InterruptedException, LanguageServerException { - params.getTextDocument().setUri(prefixURI(params.getTextDocument().getUri())); - LanguageServer server = getServer(params.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); - } + private List references(ReferenceParams referenceParams) { + try { + referenceParams.getTextDocument().setUri(prefixURI(referenceParams.getTextDocument().getUri())); + LanguageServer server = getServer(referenceParams.getTextDocument().getUri()); + if (server == null) { + return Collections.emptyList(); + } - List locations = server.getTextDocumentService().references(params).get(); - locations.forEach(o -> { - o.setUri(removePrefixUri(o.getUri())); - }); - return locations.stream().map(o -> new LocationDto(o)).collect(Collectors.toList()); - } + List locations = server.getTextDocumentService().references(referenceParams).get(); + locations.forEach(o -> o.setUri(removePrefixUri(o.getUri()))); + return locations.stream().map(LocationDto::new).collect(Collectors.toList()); + } catch (ExecutionException | InterruptedException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); - @POST - @Path("definition") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List definition(TextDocumentPositionParams params) - throws ExecutionException, InterruptedException, LanguageServerException { - params.getTextDocument().setUri(prefixURI(params.getTextDocument().getUri())); - LanguageServer server = getServer(params.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); } + } - List locations = server.getTextDocumentService().definition(params).get(); - locations.forEach(o -> { - o.setUri(removePrefixUri(o.getUri())); - }); - return locations.stream().map(o -> new LocationDto(o)).collect(Collectors.toList()); + private List definition(TextDocumentPositionParams textDocumentPositionParams) { + try { + textDocumentPositionParams.getTextDocument().setUri(prefixURI(textDocumentPositionParams.getTextDocument().getUri())); + LanguageServer server = getServer(textDocumentPositionParams.getTextDocument().getUri()); + if (server == null) { + return Collections.emptyList(); + } + + List locations = server.getTextDocumentService().definition(textDocumentPositionParams).get(); + locations.forEach(o -> o.setUri(removePrefixUri(o.getUri()))); + return locations.stream().map(LocationDto::new).collect(Collectors.toList()); + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } } + private CompletionItemDto completionItemResolve(ExtendedCompletionItemDto unresolved) { + try { + LanguageServer server = getServer(prefixURI(unresolved.getTextDocumentIdentifier().getUri())); - @POST - @Path("completionItem/resolve") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public CompletionItemDto resolveCompletionItem(ExtendedCompletionItemDto unresolved) - throws InterruptedException, ExecutionException, LanguageServerException { - LanguageServer server = getServer(prefixURI(unresolved.getTextDocumentIdentifier().getUri())); - if (server != null) { - return new CompletionItemDto(server.getTextDocumentService().resolveCompletionItem(unresolved).get()); - } else { - return new CompletionItemDto(unresolved); + return server != null ? new CompletionItemDto(server.getTextDocumentService().resolveCompletionItem(unresolved).get()) + : new CompletionItemDto(unresolved); + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } } - @SuppressWarnings("deprecation") - @POST - @Path("hover") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public HoverDto hover(TextDocumentPositionParams positionParams) - throws LanguageServerException, ExecutionException, InterruptedException { - positionParams.getTextDocument().setUri(prefixURI(positionParams.getTextDocument().getUri())); - positionParams.setUri(prefixURI(positionParams.getUri())); - LanguageServer server = getServer(positionParams.getTextDocument().getUri()); - if (server != null) { - return new HoverDto(server.getTextDocumentService().hover(positionParams).get()); - } else { - return null; + private HoverDto hover(TextDocumentPositionParams positionParams) { + try { + positionParams.getTextDocument().setUri(prefixURI(positionParams.getTextDocument().getUri())); + positionParams.setUri(prefixURI(positionParams.getUri())); + LanguageServer server = getServer(positionParams.getTextDocument().getUri()); + return server != null ? new HoverDto(server.getTextDocumentService().hover(positionParams).get()) : null; + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } } - @SuppressWarnings("deprecation") - @POST - @Path("signatureHelp") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public SignatureHelpDto signatureHelp(TextDocumentPositionParams positionParams) - throws LanguageServerException, ExecutionException, InterruptedException { - positionParams.getTextDocument().setUri(prefixURI(positionParams.getTextDocument().getUri())); - positionParams.setUri(prefixURI(positionParams.getUri())); - LanguageServer server = getServer(positionParams.getTextDocument().getUri()); - if (server != null) { - return new SignatureHelpDto(server.getTextDocumentService().signatureHelp(positionParams).get()); - } else { - return null; + private SignatureHelpDto signatureHelp(TextDocumentPositionParams positionParams) { + try { + positionParams.getTextDocument().setUri(prefixURI(positionParams.getTextDocument().getUri())); + positionParams.setUri(prefixURI(positionParams.getUri())); + LanguageServer server = getServer(positionParams.getTextDocument().getUri()); + return server != null ? new SignatureHelpDto(server.getTextDocumentService().signatureHelp(positionParams).get()) : null; + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } } - @POST - @Path("formatting") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List formatting(DocumentFormattingParams params) - throws InterruptedException, ExecutionException, LanguageServerException { - params.getTextDocument().setUri(prefixURI(params.getTextDocument().getUri())); - LanguageServer server = getServer(params.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); + private List formatting(DocumentFormattingParams documentFormattingParams) { + try { + documentFormattingParams.getTextDocument().setUri(prefixURI(documentFormattingParams.getTextDocument().getUri())); + LanguageServer server = getServer(documentFormattingParams.getTextDocument().getUri()); + return server == null ? Collections.emptyList() + : server.getTextDocumentService() + .formatting(documentFormattingParams) + .get().stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } - return server.getTextDocumentService().formatting(params).get().stream().map(o -> new TextEditDto(o)).collect(Collectors.toList()); - } - @POST - @Path("rangeFormatting") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List rangeFormatting(DocumentRangeFormattingParams params) - throws InterruptedException, ExecutionException, LanguageServerException { - params.getTextDocument().setUri(prefixURI(params.getTextDocument().getUri())); - LanguageServer server = getServer(params.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); + private List rangeFormatting(DocumentRangeFormattingParams documentRangeFormattingParams) { + try { + documentRangeFormattingParams.getTextDocument().setUri(prefixURI(documentRangeFormattingParams.getTextDocument().getUri())); + LanguageServer server = getServer(documentRangeFormattingParams.getTextDocument().getUri()); + return server == null ? Collections.emptyList() + : server.getTextDocumentService() + .rangeFormatting(documentRangeFormattingParams) + .get().stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } - return server.getTextDocumentService().rangeFormatting(params).get().stream().map(o -> new TextEditDto(o)) - .collect(Collectors.toList()); - } - @POST - @Path("onTypeFormatting") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public List onTypeFormatting(DocumentOnTypeFormattingParams params) - throws InterruptedException, ExecutionException, LanguageServerException { - params.getTextDocument().setUri(prefixURI(params.getTextDocument().getUri())); - LanguageServer server = getServer(params.getTextDocument().getUri()); - if (server == null) { - return Collections.emptyList(); + private List onTypeFormatting(DocumentOnTypeFormattingParams documentOnTypeFormattingParams) { + try { + documentOnTypeFormattingParams.getTextDocument().setUri(prefixURI(documentOnTypeFormattingParams.getTextDocument().getUri())); + LanguageServer server = getServer(documentOnTypeFormattingParams.getTextDocument().getUri()); + return server == null ? Collections.emptyList() : server.getTextDocumentService() + .onTypeFormatting(documentOnTypeFormattingParams) + .get().stream() + .map(TextEditDto::new) + .collect(Collectors.toList()); + } catch (InterruptedException | ExecutionException | LanguageServerException e) { + throw new JsonRpcException(-27000, e.getMessage()); } - return server.getTextDocumentService().onTypeFormatting(params).get().stream().map(o -> new TextEditDto(o)) - .collect(Collectors.toList()); - } - @SuppressWarnings("deprecation") - @POST - @Path("didChange") - @Consumes(MediaType.APPLICATION_JSON) - public void didChange(DidChangeTextDocumentParams change) throws LanguageServerException { - change.getTextDocument().setUri(prefixURI(change.getTextDocument().getUri())); - change.setUri(prefixURI(change.getUri())); - LanguageServer server = getServer(change.getTextDocument().getUri()); - if (server != null) { - server.getTextDocumentService().didChange(change); + private void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) { + try { + didChangeTextDocumentParams.getTextDocument().setUri(prefixURI(didChangeTextDocumentParams.getTextDocument().getUri())); + didChangeTextDocumentParams.setUri(prefixURI(didChangeTextDocumentParams.getUri())); + LanguageServer server = getServer(didChangeTextDocumentParams.getTextDocument().getUri()); + if (server != null) { + server.getTextDocumentService().didChange(didChangeTextDocumentParams); + } + } catch (LanguageServerException e) { + LOG.error("Error trying to process textDocument/didChange", e); } } - @POST - @Path("didOpen") - @Consumes(MediaType.APPLICATION_JSON) - public void didOpen(DidOpenTextDocumentParams openEvent) throws LanguageServerException { - openEvent.getTextDocument().setUri(prefixURI(openEvent.getTextDocument().getUri())); - LanguageServer server = getServer(openEvent.getTextDocument().getUri()); - if (server != null) { - server.getTextDocumentService().didOpen(openEvent); + private void didOpen(DidOpenTextDocumentParams openTextDocumentParams) { + try { + openTextDocumentParams.getTextDocument().setUri(prefixURI(openTextDocumentParams.getTextDocument().getUri())); + LanguageServer server = getServer(openTextDocumentParams.getTextDocument().getUri()); + if (server != null) { + server.getTextDocumentService().didOpen(openTextDocumentParams); + } + } catch (LanguageServerException e) { + LOG.error("Error trying to process textDocument/didOpen", e); } } - @POST - @Path("didClose") - @Consumes(MediaType.APPLICATION_JSON) - public void didClose(DidCloseTextDocumentParams closeEvent) throws LanguageServerException { - closeEvent.getTextDocument().setUri(prefixURI(closeEvent.getTextDocument().getUri())); - LanguageServer server = getServer(closeEvent.getTextDocument().getUri()); - if (server != null) { - server.getTextDocumentService().didClose(closeEvent); + private void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) { + try { + didCloseTextDocumentParams.getTextDocument().setUri(prefixURI(didCloseTextDocumentParams.getTextDocument().getUri())); + LanguageServer server = getServer(didCloseTextDocumentParams.getTextDocument().getUri()); + if (server != null) { + server.getTextDocumentService().didClose(didCloseTextDocumentParams); + } + } catch (LanguageServerException e) { + LOG.error("Error trying to process textDocument/didOpen", e); } } - @POST - @Path("didSave") - @Consumes(MediaType.APPLICATION_JSON) - public void didSave(DidSaveTextDocumentParams saveEvent) throws LanguageServerException { - saveEvent.getTextDocument().setUri(prefixURI(saveEvent.getTextDocument().getUri())); - LanguageServer server = getServer(saveEvent.getTextDocument().getUri()); - if (server != null) { - server.getTextDocumentService().didSave(saveEvent); + private void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { + try { + didSaveTextDocumentParams.getTextDocument().setUri(prefixURI(didSaveTextDocumentParams.getTextDocument().getUri())); + LanguageServer server = getServer(didSaveTextDocumentParams.getTextDocument().getUri()); + if (server != null) { + server.getTextDocumentService().didSave(didSaveTextDocumentParams); + } + } catch (LanguageServerException e) { + LOG.error("Error trying to process textDocument/didSave", e); } } - @POST - @Path("documentHighlight") - @Consumes(MediaType.APPLICATION_JSON) - public DocumentHighlight documentHighlight(TextDocumentPositionParams positionParams) - throws LanguageServerException, InterruptedException, ExecutionException { - positionParams.getTextDocument().setUri(prefixURI(positionParams.getTextDocument().getUri())); - LanguageServer server = getServer(positionParams.getTextDocument().getUri()); - if (server != null) { - return server.getTextDocumentService().documentHighlight(positionParams).get().get(0); + private DocumentHighlight documentHighlight(TextDocumentPositionParams textDocumentPositionParams) { + try { + textDocumentPositionParams.getTextDocument().setUri(prefixURI(textDocumentPositionParams.getTextDocument().getUri())); + LanguageServer server = getServer(textDocumentPositionParams.getTextDocument().getUri()); + if (server != null) { + return server.getTextDocumentService().documentHighlight(textDocumentPositionParams).get().get(0); + } + return null; + } catch (LanguageServerException | InterruptedException | ExecutionException e) { + throw new JsonRpcException(-27000, e.getMessage()); + } - return null; } - private LanguageServer getServer(String uri) throws LanguageServerException { return languageServerRegistry.findServer(uri); } + + + private

void dtoToNothing(String name, Class

pClass, Consumer

consumer) { + requestHandler.newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .noResult() + .withConsumer(consumer); + } + + private void dtoToDtoList(String name, Class

pClass, Class rClass, Function> function) { + requestHandler.newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .resultAsListOfDto(rClass) + .withFunction(function); + } + + private void dtoToDto(String name, Class

pClass, Class rClass, Function function) { + requestHandler.newConfiguration() + .methodName("textDocument/" + name) + .paramsAsDto(pClass) + .resultAsDto(rClass) + .withFunction(function); + } } diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentServiceUtils.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentServiceUtils.java new file mode 100644 index 00000000000..092216d57c2 --- /dev/null +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/TextDocumentServiceUtils.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.languageserver.service; + +/** + * Text document service utilities + */ +class TextDocumentServiceUtils { + private static final String FILE_PROJECTS = "file:///projects"; + + static String prefixURI(String relativePath) { + return FILE_PROJECTS + relativePath; + } + + static String removePrefixUri(String uri) { + return uri.startsWith(FILE_PROJECTS) ? uri.substring(FILE_PROJECTS.length()) : uri; + } +} diff --git a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java index fe85291d7c0..0a725ad73c2 100644 --- a/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java +++ b/wsagent/che-core-api-languageserver/src/main/java/org/eclipse/che/api/languageserver/service/WorkspaceService.java @@ -32,6 +32,8 @@ import java.util.stream.Collectors; import static java.util.Collections.emptyList; +import static org.eclipse.che.api.languageserver.service.TextDocumentServiceUtils.prefixURI; +import static org.eclipse.che.api.languageserver.service.TextDocumentServiceUtils.removePrefixUri; /** * REST API for the workspace/* services defined in https://github.com/Microsoft/vscode-languageserver-protocol @@ -57,7 +59,7 @@ public List documentSymbol(ExtendedWorkspaceSymb throws ExecutionException, InterruptedException, LanguageServerException { - LanguageServer server = getServer(TextDocumentService.prefixURI(workspaceSymbolParams.getFileUri())); + LanguageServer server = getServer(prefixURI(workspaceSymbolParams.getFileUri())); if (server == null) { return emptyList(); } @@ -65,7 +67,7 @@ public List documentSymbol(ExtendedWorkspaceSymb List informations = server.getWorkspaceService().symbol(workspaceSymbolParams).get(); informations.forEach(o -> { Location location = o.getLocation(); - location.setUri(TextDocumentService.removePrefixUri(location.getUri())); + location.setUri(removePrefixUri(location.getUri())); }); return informations.stream().map(o -> new SymbolInformationDto(o)).collect(Collectors.toList()); } diff --git a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java index 692c8d1ca12..732844eb441 100644 --- a/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java +++ b/wsagent/che-core-api-languageserver/src/test/java/org/eclipse/che/api/languageserver/registry/ServerInitializerImplTest.java @@ -10,9 +10,8 @@ *******************************************************************************/ package org.eclipse.che.api.languageserver.registry; +import org.eclipse.che.api.core.notification.EventService; import org.eclipse.che.api.languageserver.launcher.LanguageServerLauncher; -import org.eclipse.che.api.languageserver.messager.PublishDiagnosticsParamsMessenger; -import org.eclipse.che.api.languageserver.messager.ShowMessageMessenger; import org.eclipse.che.api.languageserver.shared.model.LanguageDescription; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; @@ -45,10 +44,6 @@ public class ServerInitializerImplTest { @Mock private ServerInitializerObserver observer; @Mock - private PublishDiagnosticsParamsMessenger publishDiagnosticsParamsMessenger; - @Mock - private ShowMessageMessenger showMessageParamsMessenger; - @Mock private LanguageDescription languageDescription; @Mock private LanguageServerLauncher launcher; @@ -56,12 +51,14 @@ public class ServerInitializerImplTest { private LanguageServer server; @Mock private CompletableFuture completableFuture; + @Mock + private EventService eventService; - private ServerInitializerImpl initializer; + private ServerInitializerImpl initializer; @BeforeMethod public void setUp() throws Exception { - initializer = spy(new ServerInitializerImpl(publishDiagnosticsParamsMessenger, showMessageParamsMessenger)); + initializer = spy(new ServerInitializerImpl(eventService)); } @Test diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileOperationHandler.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileOperationHandler.java index adc9b29d85a..20ab4575594 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileOperationHandler.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/EditorFileOperationHandler.java @@ -36,7 +36,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName(INCOMING_METHOD) .paramsAsDto(FileTrackingOperationDto.class) .resultAsEmpty() - .withFunction(this::handleFileTrackingOperation); + .withBiFunction(this::handleFileTrackingOperation); } private Void handleFileTrackingOperation(String endpointId, FileTrackingOperationDto operation) { diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/ProjectTreeTracker.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/ProjectTreeTracker.java index 773518d87b2..186c348f410 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/ProjectTreeTracker.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/impl/file/event/detectors/ProjectTreeTracker.java @@ -64,7 +64,7 @@ public void configureHandler(RequestHandlerConfigurator configurator) { .methodName(INCOMING_METHOD) .paramsAsDto(ProjectTreeTrackingOperationDto.class) .noResult() - .withConsumer(getProjectTreeTrackingOperationConsumer()); + .withBiConsumer(getProjectTreeTrackingOperationConsumer()); } private BiConsumer getProjectTreeTrackingOperationConsumer() { diff --git a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/event/MachineStateJsonRpcMessenger.java b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/event/MachineStateJsonRpcMessenger.java index 5844f07547b..d291e7d6efa 100644 --- a/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/event/MachineStateJsonRpcMessenger.java +++ b/wsmaster/che-core-api-machine/src/main/java/org/eclipse/che/api/machine/server/event/MachineStateJsonRpcMessenger.java @@ -60,7 +60,7 @@ private void configureSubscribeHandler(RequestHandlerConfigurator configurator) .methodName("event:environment-status:subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { endpointIds.putIfAbsent(endpointId, newConcurrentHashSet()); endpointIds.get(endpointId).add(workspaceId); }); @@ -72,7 +72,7 @@ private void configureUnSubscribeHandler(RequestHandlerConfigurator configurator .methodName("event:environment-status:un-subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { Set workspaceIds = endpointIds.get(endpointId); if (workspaceIds != null) { workspaceIds.remove(workspaceId); diff --git a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/event/WorkspaceJsonRpcMessenger.java b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/event/WorkspaceJsonRpcMessenger.java index 009e4e52dc1..fca2960e01a 100644 --- a/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/event/WorkspaceJsonRpcMessenger.java +++ b/wsmaster/che-core-api-workspace/src/main/java/org/eclipse/che/api/workspace/server/event/WorkspaceJsonRpcMessenger.java @@ -63,7 +63,7 @@ private void configureSubscribeHandler(RequestHandlerConfigurator configurator) .methodName("event:workspace-status:subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { endpointIds.putIfAbsent(endpointId, newConcurrentHashSet()); endpointIds.get(endpointId).add(workspaceId); }); @@ -75,7 +75,7 @@ private void configureUnSubscribeHandler(RequestHandlerConfigurator configurator .methodName("event:workspace-status:un-subscribe") .paramsAsString() .noResult() - .withConsumer((endpointId, workspaceId) -> { + .withBiConsumer((endpointId, workspaceId) -> { Set workspaceIds = endpointIds.get(endpointId); if (workspaceIds != null) { workspaceIds.remove(workspaceId);