Fix race condition in SpringIndexer initialization

This commit is contained in:
Kris De Volder
2018-03-26 17:05:35 -07:00
parent 3d9752cbdb
commit 0c70f4200e
5 changed files with 42 additions and 20 deletions

View File

@@ -13,7 +13,10 @@ package org.springframework.ide.vscode.commons.languageserver.java;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException; 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.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer; import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.FileObserver;
@@ -33,6 +36,8 @@ import com.google.common.cache.CacheBuilder;
*/ */
public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implements JavaProjectCache<K, P> { public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implements JavaProjectCache<K, P> {
private static final Logger log = LoggerFactory.getLogger(AbstractJavaProjectCache.class);
protected Sts4LanguageServer server; protected Sts4LanguageServer server;
private ListenerList<Listener> listeners = new ListenerList<>(); private ListenerList<Listener> listeners = new ListenerList<>();
@@ -47,15 +52,23 @@ public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implem
public P project(K key) { public P project(K key) {
if (key != null) { if (key != null) {
try { try {
return cache.get(key, () -> { AtomicReference<P> createdProject = new AtomicReference<P>(null);
try { try {
P project = createProject(key); return cache.get(key, () -> {
attachListeners(key, project); try {
return project; P project = createProject(key);
} catch (Throwable t) { createdProject.set(project);
throw new ExecutionException(t); attachListeners(key, project);
return project;
} catch (Throwable t) {
throw new ExecutionException(t);
}
});
} finally {
if (createdProject.get()!=null) {
notifyProjectCreated(createdProject.get());
} }
}); }
} catch (ExecutionException e) { } catch (ExecutionException e) {
Log.log(e); Log.log(e);
return null; return null;
@@ -84,6 +97,7 @@ public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implem
@Override @Override
public void addListener(Listener listener) { public void addListener(Listener listener) {
log.info("Add listener {} to {}", listener, this);
listeners.add(listener); listeners.add(listener);
} }
@@ -93,14 +107,17 @@ public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implem
} }
final protected void notifyProjectCreated(P project) { final protected void notifyProjectCreated(P project) {
log.info("project created {}", project);
listeners.forEach(l -> l.created(project)); listeners.forEach(l -> l.created(project));
} }
final protected void notifyProjectChanged(P project) { final protected void notifyProjectChanged(P project) {
log.info("project changed {}", project);
listeners.forEach(l -> l.changed(project)); listeners.forEach(l -> l.changed(project));
} }
final protected void notifyProjectDeleted(P project) { final protected void notifyProjectDeleted(P project) {
log.info("project deleted {}", project);
listeners.forEach(l -> l.deleted(project)); listeners.forEach(l -> l.deleted(project));
} }

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.languageserver.java; package org.springframework.ide.vscode.commons.languageserver.java;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.eclipse.lsp4j.TextDocumentIdentifier; import org.eclipse.lsp4j.TextDocumentIdentifier;

View File

@@ -83,7 +83,7 @@ public class BootLanguageServerParams {
public static LSFactory<BootLanguageServerParams> createDefault() { public static LSFactory<BootLanguageServerParams> createDefault() {
return (SimpleLanguageServer server) -> { return (SimpleLanguageServer server) -> {
// Initialize project finders, project caches and project observers // 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); DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(jdtProjectCache, jdtProjectCache);
indexProvider.setProgressService(server.getProgressService()); indexProvider.setProgressService(server.getProgressService());

View File

@@ -150,10 +150,7 @@ public class SpringIndexer {
} }
} }
}, "Spring Annotation Index Update Worker"); }, "Spring Annotation Index Update Worker");
server.onInitialized(() -> { server.onInitialized(updateWorker::start);
log.debug("onServerInitialized {}", this);
updateWorker.start();
});
getWorkspaceService().onDidChangeWorkspaceFolders(evt -> { getWorkspaceService().onDidChangeWorkspaceFolders(evt -> {
log.debug("workspace roots have changed event arrived - added: " + evt.getEvent().getAdded() + " - removed: " + evt.getEvent().getRemoved()); log.debug("workspace roots have changed event arrived - added: " + evt.getEvent().getAdded() + " - removed: " + evt.getEvent().getRemoved());
refresh(); refresh();

View File

@@ -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.ExceptionUtil;
import org.springframework.ide.vscode.commons.util.UriUtil; 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 com.google.common.collect.ImmutableList;
import reactor.core.Disposable; import reactor.core.Disposable;
@@ -45,17 +47,17 @@ import reactor.core.Disposable;
public class JdtLsProjectCache implements JavaProjectsService { public class JdtLsProjectCache implements JavaProjectsService {
private CompletableFuture<Void> initialized = new CompletableFuture<Void>(); private CompletableFuture<Void> initialized = new CompletableFuture<Void>();
private SimpleLanguageServer server; private SimpleLanguageServer server;
private Map<String, JdtLsProject> table = new HashMap<String, JdtLsProject>(); private Map<String, JdtLsProject> table = new HashMap<String, JdtLsProject>();
private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class); private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class);
private List<Listener> listeners = new ArrayList<>(); private List<Listener> listeners = new ArrayList<>();
private final JavaProjectsService fallback; private final Supplier<JavaProjectsService> fallback;
public JdtLsProjectCache(SimpleLanguageServer server, JavaProjectsService fallback) { public JdtLsProjectCache(SimpleLanguageServer server, Supplier<JavaProjectsService> fallback) {
Assert.isNotNull(fallback); Assert.isNotNull(fallback);
this.fallback = fallback; this.fallback = Suppliers.memoize(fallback);
this.server = server; this.server = server;
CompletableFuture<Disposable> disposable = new CompletableFuture<Disposable>(); CompletableFuture<Disposable> disposable = new CompletableFuture<Disposable>();
this.server.onInitialized(() -> { this.server.onInitialized(() -> {
@@ -110,7 +112,7 @@ public class JdtLsProjectCache implements JavaProjectsService {
public void addListener(Listener listener) { public void addListener(Listener listener) {
initialized.handle((success, failed) -> { initialized.handle((success, failed) -> {
if (failed!=null) { if (failed!=null) {
fallback.addListener(listener); fallback.get().addListener(listener);
} else { } else {
synchronized (listeners) { synchronized (listeners) {
listeners.add(listener); listeners.add(listener);
@@ -124,7 +126,7 @@ public class JdtLsProjectCache implements JavaProjectsService {
public void removeListener(Listener listener) { public void removeListener(Listener listener) {
initialized.handle((success, failed) -> { initialized.handle((success, failed) -> {
if (failed!=null) { if (failed!=null) {
fallback.removeListener(listener); fallback.get().removeListener(listener);
} else { } else {
synchronized (listeners) { synchronized (listeners) {
listeners.remove(listener); listeners.remove(listener);
@@ -171,9 +173,13 @@ public class JdtLsProjectCache implements JavaProjectsService {
@Override @Override
public Optional<IJavaProject> find(TextDocumentIdentifier doc) { public Optional<IJavaProject> find(TextDocumentIdentifier doc) {
log.info("find {} ",doc.getUri());
if (initialized.isDone()) { if (initialized.isDone()) {
if (initialized.isCompletedExceptionally()) { if (initialized.isCompletedExceptionally()) {
return fallback.find(doc); log.info("find {} delegating to fallback", doc.getUri());
Optional<IJavaProject> result = fallback.get().find(doc);
log.info("find => {}", result);
return result;
} }
String uri = UriUtil.normalize(doc.getUri()); String uri = UriUtil.normalize(doc.getUri());
@@ -186,6 +192,7 @@ public class JdtLsProjectCache implements JavaProjectsService {
} }
} }
} }
log.info("find => NOT INITIALIZED YET");
return Optional.empty(); return Optional.empty();
} }