From 0c70f4200e152a0f273e0e89c69f6e5f00f330c8 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 26 Mar 2018 17:05:35 -0700 Subject: [PATCH] Fix race condition in SpringIndexer initialization --- .../java/AbstractJavaProjectCache.java | 33 ++++++++++++++----- .../java/JavaProjectFinder.java | 1 + .../vscode/boot/BootLanguageServerParams.java | 2 +- .../vscode/boot/java/utils/SpringIndexer.java | 5 +-- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 21 ++++++++---- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java index 604695c9c..57a95a87a 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java @@ -13,7 +13,10 @@ package org.springframework.ide.vscode.commons.languageserver.java; import java.util.Optional; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer; import org.springframework.ide.vscode.commons.util.FileObserver; @@ -33,6 +36,8 @@ import com.google.common.cache.CacheBuilder; */ public abstract class AbstractJavaProjectCache implements JavaProjectCache { + private static final Logger log = LoggerFactory.getLogger(AbstractJavaProjectCache.class); + protected Sts4LanguageServer server; private ListenerList listeners = new ListenerList<>(); @@ -47,15 +52,23 @@ public abstract class AbstractJavaProjectCache implem public P project(K key) { if (key != null) { try { - return cache.get(key, () -> { - try { - P project = createProject(key); - attachListeners(key, project); - return project; - } catch (Throwable t) { - throw new ExecutionException(t); + AtomicReference

createdProject = new AtomicReference

(null); + try { + return cache.get(key, () -> { + try { + P project = createProject(key); + createdProject.set(project); + attachListeners(key, project); + return project; + } catch (Throwable t) { + throw new ExecutionException(t); + } + }); + } finally { + if (createdProject.get()!=null) { + notifyProjectCreated(createdProject.get()); } - }); + } } catch (ExecutionException e) { Log.log(e); return null; @@ -84,6 +97,7 @@ public abstract class AbstractJavaProjectCache implem @Override public void addListener(Listener listener) { + log.info("Add listener {} to {}", listener, this); listeners.add(listener); } @@ -93,14 +107,17 @@ public abstract class AbstractJavaProjectCache implem } final protected void notifyProjectCreated(P project) { + log.info("project created {}", project); listeners.forEach(l -> l.created(project)); } final protected void notifyProjectChanged(P project) { + log.info("project changed {}", project); listeners.forEach(l -> l.changed(project)); } final protected void notifyProjectDeleted(P project) { + log.info("project deleted {}", project); listeners.forEach(l -> l.deleted(project)); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java index d59ab8567..9f84a9463 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.languageserver.java; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; import org.eclipse.lsp4j.TextDocumentIdentifier; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java index 5bdfbe593..d0e69a23c 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServerParams.java @@ -83,7 +83,7 @@ public class BootLanguageServerParams { public static LSFactory createDefault() { return (SimpleLanguageServer server) -> { // Initialize project finders, project caches and project observers - JdtLsProjectCache jdtProjectCache = new JdtLsProjectCache(server, createFallbackProjectCache(server)); + JdtLsProjectCache jdtProjectCache = new JdtLsProjectCache(server, () -> createFallbackProjectCache(server)); DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(jdtProjectCache, jdtProjectCache); indexProvider.setProgressService(server.getProgressService()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 4e877276a..f6992a874 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -150,10 +150,7 @@ public class SpringIndexer { } } }, "Spring Annotation Index Update Worker"); - server.onInitialized(() -> { - log.debug("onServerInitialized {}", this); - updateWorker.start(); - }); + server.onInitialized(updateWorker::start); getWorkspaceService().onDidChangeWorkspaceFolders(evt -> { log.debug("workspace roots have changed event arrived - added: " + evt.getEvent().getAdded() + " - removed: " + evt.getEvent().getRemoved()); refresh(); 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 4bc3d4ff0..1f10d4e45 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,8 @@ import org.springframework.ide.vscode.commons.util.CollectorUtil; import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.ide.vscode.commons.util.UriUtil; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import reactor.core.Disposable; @@ -45,17 +47,17 @@ import reactor.core.Disposable; public class JdtLsProjectCache implements JavaProjectsService { private CompletableFuture initialized = new CompletableFuture(); - + private SimpleLanguageServer server; private Map table = new HashMap(); private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class); private List listeners = new ArrayList<>(); - private final JavaProjectsService fallback; + private final Supplier fallback; - public JdtLsProjectCache(SimpleLanguageServer server, JavaProjectsService fallback) { + public JdtLsProjectCache(SimpleLanguageServer server, Supplier fallback) { Assert.isNotNull(fallback); - this.fallback = fallback; + this.fallback = Suppliers.memoize(fallback); this.server = server; CompletableFuture disposable = new CompletableFuture(); this.server.onInitialized(() -> { @@ -110,7 +112,7 @@ public class JdtLsProjectCache implements JavaProjectsService { public void addListener(Listener listener) { initialized.handle((success, failed) -> { if (failed!=null) { - fallback.addListener(listener); + fallback.get().addListener(listener); } else { synchronized (listeners) { listeners.add(listener); @@ -124,7 +126,7 @@ public class JdtLsProjectCache implements JavaProjectsService { public void removeListener(Listener listener) { initialized.handle((success, failed) -> { if (failed!=null) { - fallback.removeListener(listener); + fallback.get().removeListener(listener); } else { synchronized (listeners) { listeners.remove(listener); @@ -171,9 +173,13 @@ public class JdtLsProjectCache implements JavaProjectsService { @Override public Optional find(TextDocumentIdentifier doc) { + log.info("find {} ",doc.getUri()); if (initialized.isDone()) { if (initialized.isCompletedExceptionally()) { - return fallback.find(doc); + log.info("find {} delegating to fallback", doc.getUri()); + Optional result = fallback.get().find(doc); + log.info("find => {}", result); + return result; } String uri = UriUtil.normalize(doc.getUri()); @@ -186,6 +192,7 @@ public class JdtLsProjectCache implements JavaProjectsService { } } } + log.info("find => NOT INITIALIZED YET"); return Optional.empty(); }