Support add/remove classpath listener at runtime
This commit is contained in:
@@ -24,7 +24,10 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -35,16 +38,23 @@ import org.springframework.ide.vscode.commons.java.JavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.JdtLsJavaProject;
|
||||
import org.springframework.ide.vscode.commons.javadoc.JdtLsJavadocProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ls.ClasspathListener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ServerCapabilityInitializer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import reactor.core.Disposable;
|
||||
import reactor.core.Disposables;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
public class JdtLsProjectCache implements InitializableJavaProjectsService, ServerCapabilityInitializer {
|
||||
|
||||
private static final String CMD_SPRING_BOOT_ENABLE_CLASSPATH_LISTENING = "sts.vscode-spring-boot.enableClasspathListening";
|
||||
|
||||
private static final Duration INITIALIZE_TIMEOUT = Duration.ofSeconds(10);
|
||||
private static final Object JDT_SCHEME = "jdt";
|
||||
|
||||
@@ -55,11 +65,20 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class);
|
||||
private List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
private boolean projectWatchingSupported = true;
|
||||
|
||||
final private ClasspathListener CLASSPATH_LISTENER = new JstLsClasspathListener();
|
||||
|
||||
final private Disposable.Swap DISPOSABLE = Disposables.swap();
|
||||
|
||||
private Mono<Disposable> classpathListenerRequest;
|
||||
|
||||
private boolean classpathListenerEnabled;
|
||||
|
||||
private boolean initialClasspathLisetnerEnable;
|
||||
|
||||
public JdtLsProjectCache(SimpleLanguageServer server, boolean isJandexIndex) {
|
||||
this.server = server;
|
||||
this.IS_JANDEX_INDEX = isJandexIndex;
|
||||
this.initialClasspathLisetnerEnable = true;
|
||||
this.server
|
||||
.onInitialized(initialize())
|
||||
.doOnSuccess((disposable) -> {
|
||||
@@ -69,7 +88,7 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
})
|
||||
.doOnError(error -> {
|
||||
log.error("JDT-based JavaProject service not available!", error);
|
||||
switchOffProjectObserver();
|
||||
enableClasspathListener(false);
|
||||
})
|
||||
.toFuture();
|
||||
}
|
||||
@@ -133,7 +152,7 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
}
|
||||
|
||||
private void notifyProjectObserverSupported() {
|
||||
log.info("Project Observer is " + (projectWatchingSupported ? "" : "not ") + "supported");
|
||||
log.info("Project Observer is " + (classpathListenerEnabled ? "" : "not ") + "supported");
|
||||
synchronized (listeners) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.supported();
|
||||
@@ -230,61 +249,44 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
|
||||
@Override
|
||||
public Mono<Disposable> initialize() {
|
||||
return Mono.defer(() -> 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");
|
||||
IJavaProject 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());
|
||||
}
|
||||
} else {
|
||||
log.debug("deleted = false");
|
||||
URI projectUri = new URI(uri);
|
||||
ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries());
|
||||
IJavaProject newProject = IS_JANDEX_INDEX
|
||||
? new JavaProject(getFileObserver(), projectUri, classpath,
|
||||
JdtLsProjectCache.this)
|
||||
: new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this);
|
||||
IJavaProject oldProject = table.put(uri, newProject);
|
||||
if (oldProject != null) {
|
||||
notifyChanged(newProject);
|
||||
} else {
|
||||
notifyCreated(newProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.timeout(INITIALIZE_TIMEOUT)
|
||||
.doOnSubscribe(x -> log.info("addClasspathListener ..."))
|
||||
.doOnSuccess(x -> log.info("addClasspathListener DONE"))
|
||||
.doOnError(t -> {
|
||||
log.error("Unexpected error registering classpath listener with JDT.", t);
|
||||
switchOffProjectObserver();
|
||||
}));
|
||||
return Mono.defer(() -> {
|
||||
log.debug("ADD CLASSPATH LISTENER enableClasspath=" + initialClasspathLisetnerEnable);
|
||||
enableClasspathListener(initialClasspathLisetnerEnable);
|
||||
return Mono.just(DISPOSABLE);
|
||||
});
|
||||
}
|
||||
|
||||
private void switchOffProjectObserver() {
|
||||
projectWatchingSupported = false;
|
||||
notifyProjectObserverSupported();
|
||||
private synchronized void enableClasspathListener(boolean enabled) {
|
||||
if (classpathListenerEnabled != enabled) {
|
||||
if (enabled) {
|
||||
log.debug("Adding classpath listener enabled=" + enabled);
|
||||
classpathListenerEnabled = true;
|
||||
notifyProjectObserverSupported();
|
||||
classpathListenerRequest = server.addClasspathListener(CLASSPATH_LISTENER).timeout(INITIALIZE_TIMEOUT)
|
||||
.doOnSubscribe(x -> log.info("addClasspathListener ..."))
|
||||
.doOnSuccess(x -> log.info("addClasspathListener DONE"))
|
||||
.doOnError(t -> {
|
||||
log.error("Unexpected error registering classpath listener with JDT.", t);
|
||||
enableClasspathListener(false);
|
||||
});
|
||||
final Mono<Disposable> oldClasspathSubscription = classpathListenerRequest;
|
||||
classpathListenerRequest.subscribe(d -> {
|
||||
if (oldClasspathSubscription != classpathListenerRequest) {
|
||||
d.dispose();
|
||||
} else {
|
||||
DISPOSABLE.update(d);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.debug("Removing classpath listener enabled=" + enabled);
|
||||
DISPOSABLE.update(Disposables.single());
|
||||
classpathListenerRequest = null;
|
||||
classpathListenerEnabled = false;
|
||||
notifyProjectObserverSupported();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<? extends IJavaProject> all() {
|
||||
return table.values();
|
||||
@@ -292,7 +294,75 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService {
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return projectWatchingSupported;
|
||||
return classpathListenerEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(InitializeParams p, ServerCapabilities cap) {
|
||||
server.onCommand(CMD_SPRING_BOOT_ENABLE_CLASSPATH_LISTENING, params -> {
|
||||
log.debug("CLASSPATH ENABLED CMD EXEC");
|
||||
if (params.getArguments().get(0) instanceof JsonPrimitive) {
|
||||
boolean classpathListeningEnabled = ((JsonPrimitive)params.getArguments().get(0)).getAsBoolean();
|
||||
log.debug("Enable classpath listening: " + classpathListeningEnabled);
|
||||
enableClasspathListener(classpathListeningEnabled);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
});
|
||||
|
||||
log.debug("REGISTER ENABLE CLASSPATH CMD");
|
||||
JsonObject o = (JsonObject) p.getInitializationOptions();
|
||||
if (o != null) {
|
||||
JsonPrimitive enable = o.getAsJsonPrimitive("enableJdtClasspath");
|
||||
if (enable != null) {
|
||||
log.debug("READING INIT VALUE for classpathEnabled=" + enable.getAsBoolean());
|
||||
initialClasspathLisetnerEnable = enable.getAsBoolean();
|
||||
}
|
||||
}
|
||||
log.debug("INIT VALUE for classpathEnabled=" + initialClasspathLisetnerEnable);
|
||||
cap.getExecuteCommandProvider().getCommands().add(CMD_SPRING_BOOT_ENABLE_CLASSPATH_LISTENING);
|
||||
}
|
||||
|
||||
private class JstLsClasspathListener implements 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");
|
||||
IJavaProject 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());
|
||||
}
|
||||
} else {
|
||||
log.debug("deleted = false");
|
||||
URI projectUri = new URI(uri);
|
||||
ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries());
|
||||
IJavaProject newProject = IS_JANDEX_INDEX
|
||||
? new JavaProject(getFileObserver(), projectUri, classpath,
|
||||
JdtLsProjectCache.this)
|
||||
: new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this);
|
||||
IJavaProject oldProject = table.put(uri, newProject);
|
||||
if (oldProject != null) {
|
||||
notifyChanged(newProject);
|
||||
} else {
|
||||
notifyCreated(newProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user