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.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<K, P extends IJavaProject> implements JavaProjectCache<K, P> {
private static final Logger log = LoggerFactory.getLogger(AbstractJavaProjectCache.class);
protected Sts4LanguageServer server;
private ListenerList<Listener> listeners = new ListenerList<>();
@@ -47,15 +52,23 @@ public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> 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<P> createdProject = new AtomicReference<P>(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<K, P extends IJavaProject> 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<K, P extends IJavaProject> 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));
}

View File

@@ -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;

View File

@@ -83,7 +83,7 @@ public class BootLanguageServerParams {
public static LSFactory<BootLanguageServerParams> 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());

View File

@@ -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();

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.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<Void> initialized = new CompletableFuture<Void>();
private SimpleLanguageServer server;
private Map<String, JdtLsProject> table = new HashMap<String, JdtLsProject>();
private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class);
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);
this.fallback = fallback;
this.fallback = Suppliers.memoize(fallback);
this.server = server;
CompletableFuture<Disposable> disposable = new CompletableFuture<Disposable>();
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<IJavaProject> 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<IJavaProject> 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();
}