Make spring-boot-ls request batched classpath events ...
...and make it handle them properly, but also retain support for non-batched events for backwards compatibility with clients that don't support batched events.
This commit is contained in:
@@ -29,6 +29,7 @@ import org.springframework.ide.vscode.commons.util.AsyncRunner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import reactor.core.Disposable;
|
||||
@@ -57,19 +58,38 @@ public class ClasspathListenerManager {
|
||||
log.debug("callback {} received {}", callbackCommandId, callbackParams);
|
||||
List<Object> args = callbackParams.getArguments();
|
||||
log.debug("args = {}", args);
|
||||
//Note: not sure... but args might be deserialized as com.google.gson.JsonElement's.
|
||||
//If so the code below is not correct (casts will fail).
|
||||
String projectUri = ((JsonElement) args.get(0)).getAsString();
|
||||
log.debug("projectUri = {}", args);
|
||||
String name = ((JsonElement) args.get(1)).getAsString();
|
||||
log.debug("name = {}", args);
|
||||
boolean deleted = ((JsonElement)args.get(2)).getAsBoolean();
|
||||
log.debug("deleted = {}", deleted);
|
||||
//Args are deserialized as com.google.gson.JsonElements.
|
||||
if (((JsonElement) args.get(0)).isJsonArray()) {
|
||||
// If events are batched... then they will arrive as a array of arrays.
|
||||
for (Object arg : args) {
|
||||
JsonArray event = (JsonArray) arg;
|
||||
|
||||
Classpath classpath = gson.fromJson((JsonElement)args.get(3), Classpath.class);
|
||||
log.debug("classpath = {}", classpath);
|
||||
String projectUri = event.get(0).getAsString();
|
||||
log.debug("projectUri = {}", event);
|
||||
String name = event.get(1).getAsString();
|
||||
log.debug("name = {}", event);
|
||||
boolean deleted = event.get(2).getAsBoolean();
|
||||
log.debug("deleted = {}", deleted);
|
||||
|
||||
classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath));
|
||||
Classpath classpath = gson.fromJson((JsonElement)event.get(3), Classpath.class);
|
||||
log.debug("classpath = {}", classpath);
|
||||
classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath));
|
||||
}
|
||||
} else {
|
||||
//Still support non-batched events for backwards compatibility with clients
|
||||
// that don't provide batched event support (e.g. IDEA client may only adopt this
|
||||
// later, or not adopt it at all).
|
||||
String projectUri = ((JsonElement) args.get(0)).getAsString();
|
||||
log.debug("projectUri = {}", args);
|
||||
String name = ((JsonElement) args.get(1)).getAsString();
|
||||
log.debug("name = {}", args);
|
||||
boolean deleted = ((JsonElement)args.get(2)).getAsBoolean();
|
||||
log.debug("deleted = {}", deleted);
|
||||
|
||||
Classpath classpath = gson.fromJson((JsonElement)args.get(3), Classpath.class);
|
||||
log.debug("classpath = {}", classpath);
|
||||
classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath));
|
||||
}
|
||||
return "done";
|
||||
}));
|
||||
|
||||
@@ -86,7 +106,7 @@ public class ClasspathListenerManager {
|
||||
server.getClient().registerCapability(params)
|
||||
));
|
||||
Mono<Object> registerClasspathListener = Mono.defer(() -> Mono.fromFuture(
|
||||
server.getClient().addClasspathListener(new ClasspathListenerParams(callbackCommandId))
|
||||
server.getClient().addClasspathListener(new ClasspathListenerParams(callbackCommandId, true))
|
||||
));
|
||||
|
||||
Disposable cleanups = () -> {
|
||||
|
||||
@@ -12,7 +12,9 @@ package org.springframework.tooling.jdt.ls.commons.classpath;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@@ -26,20 +28,19 @@ import org.eclipse.jdt.core.JavaCore;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
|
||||
import org.springframework.tooling.jdt.ls.commons.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class SendClasspathNotificationsJob extends Job {
|
||||
|
||||
private final ClientCommandExecutor conn;
|
||||
private final Logger logger;
|
||||
private String callbackCommandId;
|
||||
private boolean isBatched;
|
||||
|
||||
public SendClasspathNotificationsJob(Logger logger, ClientCommandExecutor conn, String callbackId, boolean isBatched) {
|
||||
super("Send Classpath Notifications");
|
||||
this.logger = logger;
|
||||
this.conn = conn;
|
||||
this.callbackCommandId = callbackId;
|
||||
this.isBatched = isBatched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only if caller has requested 'batched' events. This buffer, accumulates messsages to be sent out all at once,
|
||||
* rather than one by one.
|
||||
*/
|
||||
private List<Object> buffer;
|
||||
|
||||
/**
|
||||
* To keep track of project locations. Without this we can't properly handle deletion events because
|
||||
@@ -47,6 +48,18 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
* was deleted' event if we keep track of project locations ourselves.
|
||||
*/
|
||||
private Map<String, URI> projectLocations = new HashMap<>();
|
||||
public final Queue<IJavaProject> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public SendClasspathNotificationsJob(Logger logger, ClientCommandExecutor conn, String callbackId, boolean isBatched) {
|
||||
super("Send Classpath Notifications");
|
||||
this.logger = logger;
|
||||
this.conn = conn;
|
||||
this.callbackCommandId = callbackId;
|
||||
if (isBatched) {
|
||||
buffer = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private URI getProjectLocation(IJavaProject jp) {
|
||||
URI loc = jp.getProject().getLocationURI();
|
||||
@@ -60,7 +73,6 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
}
|
||||
}
|
||||
|
||||
public final Queue<IJavaProject> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private boolean projectExists(IJavaProject jp) {
|
||||
//We can't really deal with projects that don't exist in disk. So using this more strict 'exists' check
|
||||
@@ -114,20 +126,45 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
logger.log(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
logger.log("executing callback "+callbackCommandId+" "+projectName+" "+deleted+" "+ classpath.getEntries().size());
|
||||
Object r = conn.executeClientCommand(callbackCommandId, projectLoc.toString(), projectName, deleted, classpath);
|
||||
logger.log("executing callback "+callbackCommandId+" SUCCESS ["+r+"]");
|
||||
} catch (Exception e) {
|
||||
logger.log("executing callback "+callbackCommandId+" FAILED");
|
||||
logger.log(e);
|
||||
}
|
||||
bufferMessage(projectLoc, deleted, projectName, classpath);
|
||||
}
|
||||
}
|
||||
flush();
|
||||
} catch (Exception e) {
|
||||
logger.log(e);
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
protected void bufferMessage(URI projectLoc, boolean deleted, String projectName, Classpath classpath) {
|
||||
if (buffer!=null) {
|
||||
logger.log("buffering callback "+callbackCommandId+" "+projectName+" "+deleted+" "+ classpath.getEntries().size());
|
||||
buffer.add(ImmutableList.of(projectLoc.toString(), projectName, deleted, classpath));
|
||||
} else {
|
||||
try {
|
||||
logger.log("executing callback "+callbackCommandId+" "+projectName+" "+deleted+" "+ classpath.getEntries().size());
|
||||
Object r = conn.executeClientCommand(callbackCommandId, projectLoc.toString(), projectName, deleted, classpath);
|
||||
logger.log("executing callback "+callbackCommandId+" SUCCESS ["+r+"]");
|
||||
} catch (Exception e) {
|
||||
logger.log("executing callback "+callbackCommandId+" FAILED");
|
||||
logger.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void flush() {
|
||||
if (buffer!=null && !buffer.isEmpty()) {
|
||||
try {
|
||||
logger.log("executing callback "+callbackCommandId+" "+buffer.size()+" batched events");
|
||||
Object r = conn.executeClientCommand(callbackCommandId, buffer.toArray(new Object[buffer.size()]));
|
||||
logger.log("executing callback "+callbackCommandId+" SUCCESS ["+r+"]");
|
||||
} catch (Exception e) {
|
||||
logger.log("executing callback "+callbackCommandId+" FAILED");
|
||||
logger.log(e);
|
||||
} finally {
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user