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 81ab755e3..97ca03a3d 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 @@ -30,6 +30,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import reactor.core.Disposable; +import reactor.core.publisher.Mono; public class ClasspathListenerManager { @@ -46,7 +47,7 @@ public class ClasspathListenerManager { this.async = server.getAsync(); } - public Disposable addClasspathListener(ClasspathListener classpathListener) { + public Mono addClasspathListener(ClasspathListener classpathListener) { String callbackCommandId = "sts4.classpath." + RandomStringUtils.randomAlphabetic(8); // 1. register callback command handler in SimpleLanguageServer @@ -78,29 +79,31 @@ public class ClasspathListenerManager { ImmutableMap.of("commands", ImmutableList.of(callbackCommandId)) ) )); - server.getClient().registerCapability(params).join(); - // 3. call the client to ask it to call that callback - server.getClient().addClasspathListener( - new ClasspathListenerParams(callbackCommandId) - ).join(); + Mono registerCallbackCommand = Mono.defer(() -> Mono.fromFuture( + server.getClient().registerCapability(params) + )); + Mono registerClasspathListener = Mono.defer(() -> Mono.fromFuture( + server.getClient().addClasspathListener(new ClasspathListenerParams(callbackCommandId)) + )); - // Cleanups: - return () -> { - try { - log.info("Unregistering classpath callback "+callbackCommandId +" ..."); - this.server.getClient().removeClasspathListener( - new ClasspathListenerParams(callbackCommandId) - ).join(); - log.info("Unregistering classpath callback "+callbackCommandId +" OK"); + Disposable cleanups = () -> { + log.info("Unregistering classpath callback "+callbackCommandId +" ..."); + AsyncRunner.thenLog(log, + this.server.getClient().removeClasspathListener(new ClasspathListenerParams(callbackCommandId)) + ); + log.info("Unregistering classpath callback "+callbackCommandId +" OK"); + AsyncRunner.thenLog(log, this.server.getClient().unregisterCapability(new UnregistrationParams(ImmutableList.of( new Unregistration(registrationId, WORKSPACE_EXECUTE_COMMAND) - ))).join(); - unregisterCommand.dispose(); - } catch (Exception e) { - log.error("", e); - } + ))) + ); + unregisterCommand.dispose(); }; + return + registerCallbackCommand + .then(registerClasspathListener) + .thenReturn(cleanups); } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 9f39a7ff5..ac7b1b09d 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -657,8 +657,14 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC this.initializeHandler = handler; } - public synchronized void onInitialized(Runnable handler) { - thenLog(log, this.initialized.thenAccept((whocares) -> handler.run())); + public Mono onInitialized(Mono handler) { + return Mono.fromFuture(this.initialized).then(handler) + .doOnError(error -> log.error("", error)); + } + + + public void doOnInitialized(Runnable action) { + onInitialized(Mono.fromRunnable(action)).toFuture(); } public synchronized void onShutdown(Runnable handler) { @@ -677,7 +683,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC return this.async; } - public synchronized Disposable addClasspathListener(ClasspathListener classpathListener) throws Exception { + public synchronized Mono addClasspathListener(ClasspathListener classpathListener) { if (classpathListenerManager == null) { classpathListenerManager = new ClasspathListenerManager(this); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java index 51fe37567..2460c7c62 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java @@ -62,7 +62,7 @@ public class SimpleServerFileObserver extends BasicFileObserver { } private void subscribe(String subscriptionId, List globPattern, int kind) { - server.onInitialized(() -> { + server.doOnInitialized(() -> { if (server.canRegisterFileWatchersDynamically()) { List watchers = globPattern.stream().map(pattern -> new FileSystemWatcher(pattern, kind)).collect(Collectors.toList()); Registration registration = new Registration(subscriptionId, WORKSPACE_DID_CHANGE_WATCHED_FILES, new DidChangeWatchedFilesRegistrationOptions(watchers)); @@ -73,7 +73,7 @@ public class SimpleServerFileObserver extends BasicFileObserver { @Override public boolean unsubscribe(String subscriptionId) { - server.onInitialized(() -> { + server.doOnInitialized(() -> { if (server.canRegisterFileWatchersDynamically()) { server.getClient().unregisterCapability(new UnregistrationParams(Arrays.asList(new Unregistration(subscriptionId, WORKSPACE_DID_CHANGE_WATCHED_FILES)))); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index c8d0ced14..fbc1bbbd3 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -217,7 +217,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent }); server.onInitialize(this::initialize); - server.onInitialized(this::initialized); + server.doOnInitialized(this::initialized); server.onShutdown(this::shutdown); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/InitializableJavaProjectsService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/InitializableJavaProjectsService.java index bc34babfd..3905cb3d4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/InitializableJavaProjectsService.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/InitializableJavaProjectsService.java @@ -11,9 +11,10 @@ package org.springframework.ide.vscode.boot.jdt.ls; import reactor.core.Disposable; +import reactor.core.publisher.Mono; public interface InitializableJavaProjectsService extends JavaProjectsService { - - Disposable initialize() throws Exception; + + Mono initialize(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JavaProjectsServiceWithFallback.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JavaProjectsServiceWithFallback.java index 7b24149d8..bf5989f4e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JavaProjectsServiceWithFallback.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JavaProjectsServiceWithFallback.java @@ -28,10 +28,10 @@ import com.google.common.base.Suppliers; import reactor.core.Disposable; public class JavaProjectsServiceWithFallback implements JavaProjectsService { - + private Logger log = LoggerFactory.getLogger(JavaProjectsServiceWithFallback.class); - final private CompletableFuture initialized = new CompletableFuture(); + final private CompletableFuture mainServiceInitialized; final private SimpleLanguageServer server; private Supplier fallback; @@ -42,30 +42,22 @@ public class JavaProjectsServiceWithFallback implements JavaProjectsService { this.main = main; this.fallback = Suppliers.memoize(fallback); this.server = server; - CompletableFuture disposable = new CompletableFuture(); - this.server.onInitialized(() -> { - try { - disposable.complete(main.initialize()); - initialized.complete(null); - } catch (Throwable e) { - log.info("Fallback classpath provider will be enabled"); - disposable.complete(()-> {}); - initialized.completeExceptionally(e); - } - }); - this.server.onShutdown(() -> - disposable.thenAccept(Disposable::dispose).join() + this.mainServiceInitialized = this.server + .onInitialized(main.initialize()) + .toFuture(); + this.server.onShutdown(() -> + mainServiceInitialized.thenAccept(Disposable::dispose).join() ); } @Override public Optional find(TextDocumentIdentifier doc) { - if (initialized.isDone()) { - if (initialized.isCompletedExceptionally()) { + if (mainServiceInitialized.isDone()) { + if (mainServiceInitialized.isCompletedExceptionally()) { return fallback.get().find(doc); } else { return main.find(doc); - } + } } else { log.debug("find => NOT INITIALIZED YET"); } @@ -75,7 +67,7 @@ public class JavaProjectsServiceWithFallback implements JavaProjectsService { @Override public void addListener(Listener listener) { - initialized.handle((success, failed) -> { + mainServiceInitialized.handle((success, failed) -> { if (failed!=null) { fallback.get().addListener(listener); } else { @@ -87,7 +79,7 @@ public class JavaProjectsServiceWithFallback implements JavaProjectsService { @Override public void removeListener(Listener listener) { - initialized.handle((success, failed) -> { + mainServiceInitialized.handle((success, failed) -> { if (failed!=null) { fallback.get().removeListener(listener); } else { @@ -99,12 +91,12 @@ public class JavaProjectsServiceWithFallback implements JavaProjectsService { @Override public IJavadocProvider javadocProvider(String projectUri, CPE classpathEntry) { - if (initialized.isDone()) { - if (initialized.isCompletedExceptionally()) { + if (mainServiceInitialized.isDone()) { + if (mainServiceInitialized.isCompletedExceptionally()) { return fallback.get().javadocProvider(projectUri, classpathEntry); } else { return main.javadocProvider(projectUri, classpathEntry); - } + } } else { log.debug("javadoc => NOT INITIALIZED YET"); } 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 3f2fbefda..1e65e6d78 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 @@ -38,6 +38,7 @@ import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.UriUtil; import reactor.core.Disposable; +import reactor.core.publisher.Mono; public class JdtLsProjectCache implements InitializableJavaProjectsService { @@ -163,45 +164,43 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService { } @Override - public Disposable initialize() throws Exception { - try { - return server.addClasspathListener(new ClasspathListener() { - @Override - public void changed(Event event) { - log.debug("claspath event received {}", event); - server.onInitialized(() -> { - //log.info("initialized.thenRun block entered"); - try { - synchronized (table) { - String uri = UriUtil.normalize(event.projectUri); - log.debug("uri = {}", uri); - if (event.deleted) { - log.debug("event.deleted = true"); - JavaProject deleted = table.remove(uri); - if (deleted!=null) { - log.debug("removed from table = true"); - notifyDelete(deleted); - } else { - log.warn("Deleted project not removed because uri {} not found in {}", uri, table.keySet()); - } + public Mono initialize() { + return server.addClasspathListener(new ClasspathListener() { + @Override + public void changed(Event event) { + log.debug("claspath event received {}", event); + server.doOnInitialized(() -> { + //log.info("initialized.thenRun block entered"); + try { + synchronized (table) { + String uri = UriUtil.normalize(event.projectUri); + log.debug("uri = {}", uri); + if (event.deleted) { + log.debug("event.deleted = true"); + JavaProject deleted = table.remove(uri); + if (deleted!=null) { + log.debug("removed from table = true"); + notifyDelete(deleted); } else { - log.debug("deleted = false"); - JavaProject newProject = new JavaProject(getFileObserver(), new URI(uri), new ClasspathData(event.name, event.classpath.getEntries()), JdtLsProjectCache.this); - JavaProject oldProject = table.put(uri, newProject); - if (oldProject != null) { - notifyChanged(newProject); - } else { - notifyCreated(newProject); - } + log.warn("Deleted project not removed because uri {} not found in {}", uri, table.keySet()); + } + } else { + log.debug("deleted = false"); + JavaProject newProject = new JavaProject(getFileObserver(), new URI(uri), new ClasspathData(event.name, event.classpath.getEntries()), JdtLsProjectCache.this); + JavaProject oldProject = table.put(uri, newProject); + if (oldProject != null) { + notifyChanged(newProject); + } else { + notifyCreated(newProject); } } - } catch (Exception e) { - log.error("", e); } - }); - } - }); - } catch (Throwable t) { + } catch (Exception e) { + log.error("", e); + } + }); + } + }).doOnError(t -> { if (isNoJdtError(t)) { log.info("JDT Language Server not available. Fallback classpath provider will be used instead."); } else if (isOldJdt(t)) { @@ -209,7 +208,6 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService { } else { log.error("Unexpected error registering classpath listener with JDT. Fallback classpath provider will be used instead.", t); } - throw t; - } + }); } }