diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java index 18646a86b..17b36692b 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java @@ -67,11 +67,6 @@ public class ClasspathListenerManager { return "done"; })); - // 2. call the client to ask it to call that callback - CompletableFuture future1 = server.getClient().addClasspathListener( - new ClasspathListenerParams(callbackCommandId) - ); - // 2. register the callback command with the client String registrationId = UUID.randomUUID().toString(); RegistrationParams params = new RegistrationParams(ImmutableList.of( @@ -80,19 +75,20 @@ public class ClasspathListenerManager { ImmutableMap.of("commands", ImmutableList.of(callbackCommandId)) ) )); - CompletableFuture future2 = server.getClient().registerCapability(params); + server.getClient().registerCapability(params).join(); - // Wait for async work - future1.join(); - future2.join(); + // 3. call the client to ask it to call that callback + server.getClient().addClasspathListener( + new ClasspathListenerParams(callbackCommandId) + ).join(); // Cleanups: return () -> { unregisterCommand.dispose(); thenLog(log, this.server.getClient().removeClasspathListener(new ClasspathListenerParams(callbackCommandId))); - this.server.getClient().unregisterCapability(new UnregistrationParams(ImmutableList.of( + thenLog(log, this.server.getClient().unregisterCapability(new UnregistrationParams(ImmutableList.of( new Unregistration(registrationId, WORKSPACE_EXECUTE_COMMAND) - ))); + )))); }; } diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerHandler.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerHandler.java index ecd20813f..6788f59fc 100644 --- a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerHandler.java +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerHandler.java @@ -1,11 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2018 Pivotal, Inc. + * 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: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ package org.springframework.tooling.jdt.ls.extension; import static org.springframework.tooling.jdt.ls.extension.Logger.log; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; import org.eclipse.jdt.ls.core.internal.JavaClientConnection; @@ -15,58 +29,67 @@ import org.springframework.tooling.jdt.ls.extension.ClasspathListenerManager.Cla @SuppressWarnings("restriction") public class ClasspathListenerHandler implements IDelegateCommandHandler { - static class MyClasspathListener implements ClasspathListener { - - private ClasspathListenerManager manager = null; - private List subscribers = new ArrayList<>(1); + static class Subscribptions { + private static Map subscribers = null; + public synchronized void subscribe(String callbackCommandId) { Logger.log("subscribing to classpath changes: " + callbackCommandId); - if (manager == null) { - this.manager = new ClasspathListenerManager(this); + if (subscribers==null) { + subscribers = new HashMap<>(1); } - subscribers.add(callbackCommandId); - Logger.log("subsribers = " + subscribers); - } - - @Override - public void classpathChanged(IJavaProject jp) { - log("Classpath changed " + jp.getElementName()); - String project = jp.getProject().getLocationURI().toString(); - boolean deleted = !jp.exists(); - JavaClientConnection conn = JavaLanguageServerPlugin.getInstance().getClientConnection(); - String projectName = jp.getElementName(); - - for (String callbackCommandId : subscribers) { - Classpath classpath = null; - if (!deleted) { - try { - classpath = ClasspathUtil.resolve(jp); - } catch (Exception e) { - Logger.log(e); - } + subscribers.computeIfAbsent(callbackCommandId, (cid) -> new ClasspathListenerManager(new ClasspathListener() { + @Override + public void classpathChanged(IJavaProject jp) { + sendNotification(callbackCommandId, jp); + } + }, true)); + Logger.log("subsribers = " + subscribers.keySet()); + } + + private void sendNotification(String callbackCommandId, IJavaProject jp) { + //TODO: make one Job to accumulate all requested notification and work more efficiently by batching + // and avoiding multiple executions of duplicated requests. + new Job("SendClasspath notification") { + @Override + protected IStatus run(IProgressMonitor monitor) { + log("Classpath changed " + jp.getElementName()); + String project = jp.getProject().getLocationURI().toString(); + boolean deleted = !jp.exists(); + JavaClientConnection conn = JavaLanguageServerPlugin.getInstance().getClientConnection(); + String projectName = jp.getElementName(); + + Classpath classpath = null; + if (!deleted) { + try { + classpath = ClasspathUtil.resolve(jp); + } catch (Exception e) { + Logger.log(e); + } + } + conn.executeCommand(callbackCommandId, project, projectName, deleted, classpath); + return Status.OK_STATUS; } - conn.executeCommand(callbackCommandId, project, projectName, deleted, classpath); } + .schedule(); } public synchronized void unsubscribe(String callbackCommandId) { Logger.log("unsubscribing from classpath changes: " + callbackCommandId); if (subscribers != null) { - subscribers.remove(callbackCommandId); + ClasspathListenerManager mgr = subscribers.remove(callbackCommandId); + if (mgr!=null) { + mgr.dispose(); + } if (subscribers.isEmpty()) { subscribers = null; - if (manager != null) { - manager.dispose(); - manager = null; - } } } - Logger.log("subsribers = " + subscribers); + Logger.log("subsribers = " + subscribers.keySet()); } } - - private static MyClasspathListener classpathListener = new MyClasspathListener(); + + private static Subscribptions subscribptions = new Subscribptions(); @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { @@ -83,14 +106,14 @@ public class ClasspathListenerHandler implements IDelegateCommandHandler { private Object removeClasspathListener(String callbackCommandId) { log("ClasspathListenerHandler addClasspathListener " + callbackCommandId); - classpathListener.unsubscribe(callbackCommandId); + subscribptions.unsubscribe(callbackCommandId); log("ClasspathListenerHandler addClasspathListener " + callbackCommandId + " => OK"); return "ok"; } private Object addClasspathListener(String callbackCommandId) { log("ClasspathListenerHandler addClasspathListener " + callbackCommandId); - classpathListener.subscribe(callbackCommandId); + subscribptions.subscribe(callbackCommandId); log("ClasspathListenerHandler addClasspathListener " + callbackCommandId + " => OK"); return "ok"; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java index 57c67b9aa..55b052b97 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/WebfluxRouteHighlightProdivder.java @@ -15,6 +15,8 @@ import java.util.List; import org.eclipse.lsp4j.DocumentHighlight; import org.eclipse.lsp4j.Position; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.handlers.HighlightProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; @@ -24,6 +26,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; * @author Martin Lippert */ public class WebfluxRouteHighlightProdivder implements HighlightProvider { + + private static final Logger log = LoggerFactory.getLogger(WebfluxRouteHighlightProdivder.class); private final SpringIndexer springIndexer; @@ -33,7 +37,7 @@ public class WebfluxRouteHighlightProdivder implements HighlightProvider { @Override public void provideHighlights(TextDocument document, Position position, List resultAccumulator) { - System.out.println(" PROVIDE HIGHLIGHTS: " + position.getLine() + "/" + position.getCharacter()); + log.info("PROVIDE HIGHLIGHTS: {} / {}", position.getLine(), position.getCharacter()); this.springIndexer.getAdditonalInformation(document.getUri()) .stream() diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 0a88b0246..095f6326e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -51,8 +51,6 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver { @Override public void changed(Event event) { log.info("Classpath changed: "+ event.projectUri); - - } })) );