PT #166597649: Send classpath when project is built if necessary
This commit is contained in:
@@ -23,7 +23,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.java.ClasspathIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaModuleData;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.java.JavaUtils;
|
||||
import org.springframework.ide.vscode.commons.javadoc.JdtLsJavadocProvider;
|
||||
@@ -65,31 +64,23 @@ public class JdtLsIndex implements ClasspathIndex {
|
||||
|
||||
final private Cache<JavaTypeHierarchyParams, CompletableFuture<List<IType>>> supertypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
final private Cache<JavaTypeHierarchyParams, CompletableFuture<List<IType>>> subtypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
|
||||
final private Listener projectListener = new Listener() {
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
binaryTypeCache.invalidateAll();
|
||||
sourceTypeCache.invalidateAll();
|
||||
}
|
||||
};
|
||||
|
||||
final private Listener projectListener;
|
||||
|
||||
public JdtLsIndex(STS4LanguageClient client, URI projectUri, ProjectObserver projectObserver) {
|
||||
this.client = client;
|
||||
this.projectUri = projectUri;
|
||||
this.projectObserver = projectObserver;
|
||||
this.javadocProvider = new JdtLsJavadocProvider(client, projectUri.toString());
|
||||
|
||||
this.projectListener = ProjectObserver.onAny(project -> {
|
||||
if (Objects.equals(project.getLocationUri(), projectUri)) {
|
||||
binaryTypeCache.invalidateAll();
|
||||
sourceTypeCache.invalidateAll();
|
||||
supertypesCache.invalidateAll();
|
||||
subtypesCache.invalidateAll();
|
||||
}
|
||||
});
|
||||
|
||||
this.projectObserver.addListener(projectListener);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, Inc.
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -13,6 +13,11 @@ package org.springframework.tooling.jdt.ls.commons.classpath;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResourceChangeEvent;
|
||||
import org.eclipse.core.resources.IResourceChangeListener;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
@@ -51,7 +56,8 @@ public class ClasspathListenerManager {
|
||||
};
|
||||
|
||||
public interface ClasspathListener {
|
||||
public abstract void classpathChanged(IJavaProject jp);
|
||||
void classpathChanged(IJavaProject jp);
|
||||
default void projectBuilt(IJavaProject jp) {};
|
||||
}
|
||||
|
||||
private class MyListener implements IElementChangedListener {
|
||||
@@ -102,12 +108,31 @@ public class ClasspathListenerManager {
|
||||
private ClasspathListener listener;
|
||||
private MyListener myListener;
|
||||
private final Logger logger;
|
||||
|
||||
private IResourceChangeListener workspaceListener = (event) -> {
|
||||
if (event.getSource() instanceof IProject) {
|
||||
projectBuilt((IProject) event.getSource());
|
||||
} else if (event.getSource() instanceof IWorkspace) {
|
||||
for (IProject p : ((IWorkspace)event.getSource()).getRoot().getProjects()) {
|
||||
projectBuilt(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ClasspathListenerManager(Logger logger, ClasspathListener listener) {
|
||||
this.logger = logger;
|
||||
logger.log("Setting up ClasspathListenerManager");
|
||||
this.listener = listener;
|
||||
JavaCore.addElementChangedListener(myListener=new MyListener(), ElementChangedEvent.POST_CHANGE);
|
||||
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||
workspace.addResourceChangeListener(workspaceListener, IResourceChangeEvent.POST_BUILD);
|
||||
}
|
||||
|
||||
private void projectBuilt(IProject project) {
|
||||
IJavaProject jp = JavaCore.create(project);
|
||||
if (jp != null) {
|
||||
listener.projectBuilt(jp);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
@@ -115,6 +140,7 @@ public class ClasspathListenerManager {
|
||||
JavaCore.removeElementChangedListener(myListener);
|
||||
myListener = null;
|
||||
}
|
||||
ResourcesPlugin.getWorkspace().removeResourceChangeListener(workspaceListener);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,6 +65,12 @@ public class ReusableClasspathListenerHandler {
|
||||
public void classpathChanged(IJavaProject jp) {
|
||||
sendNotification(jp, subscribers.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectBuilt(IJavaProject jp) {
|
||||
sendNotificationOnProjectBuilt(jp, subscribers.keySet());
|
||||
}
|
||||
|
||||
});
|
||||
subscribers.put(callbackCommandId, new SendClasspathNotificationsJob(logger, conn, callbackCommandId, isBatched));
|
||||
logger.log("subsribers = " + subscribers);
|
||||
@@ -106,6 +112,16 @@ public class ReusableClasspathListenerHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void sendNotificationOnProjectBuilt(IJavaProject jp, Collection<String> callbackIds) {
|
||||
if (subscribers!=null) {
|
||||
for (String callbackId : callbackIds) {
|
||||
SendClasspathNotificationsJob sendNotificationJob = subscribers.get(callbackId);
|
||||
sendNotificationJob.builtProjectQueue.add(jp);
|
||||
sendNotificationJob.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void unsubscribe(String callbackCommandId) {
|
||||
logger.log("unsubscribing from classpath changes: " + callbackCommandId);
|
||||
if (subscribers != null) {
|
||||
|
||||
@@ -14,9 +14,11 @@ import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
@@ -26,6 +28,7 @@ import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE;
|
||||
import org.springframework.tooling.jdt.ls.commons.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -49,7 +52,10 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
*/
|
||||
private Map<String, URI> projectLocations = new HashMap<>();
|
||||
public final Queue<IJavaProject> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public final Queue<IJavaProject> builtProjectQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final Set<IJavaProject> notReadyProjects = new HashSet<>();
|
||||
|
||||
public SendClasspathNotificationsJob(Logger logger, ClientCommandExecutor conn, String callbackId, boolean isBatched) {
|
||||
super("Send Classpath Notifications");
|
||||
this.logger = logger;
|
||||
@@ -99,8 +105,19 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
synchronized (projectLocations) { //Could use some Eclipse job rule. But its really a bit of a PITA to create the right one.
|
||||
try {
|
||||
// Try to see if classpath needs to be sent for the projects that have been
|
||||
// built since classpath JAR may not have existed (not downloaded) at the time
|
||||
// of classpath changed event
|
||||
for (IJavaProject jp = builtProjectQueue.poll(); jp!=null; jp = builtProjectQueue.poll()) {
|
||||
if (notReadyProjects.remove(jp)) {
|
||||
queue.add(jp);
|
||||
}
|
||||
}
|
||||
for (IJavaProject jp = queue.poll(); jp!=null; jp = queue.poll()) {
|
||||
logger.log("Preparing classpath changed notification " + jp.getElementName());
|
||||
// Project wasn't ready before but now it's about to be processed for Classpath again.
|
||||
// Remove it from the set of not readt projects
|
||||
notReadyProjects.remove(jp);
|
||||
URI projectLoc = getProjectLocation(jp);
|
||||
if (projectLoc==null) {
|
||||
logger.log("Could not send event for project because no project location: "+jp.getElementName());
|
||||
@@ -117,11 +134,26 @@ public class SendClasspathNotificationsJob extends Job {
|
||||
|
||||
Classpath classpath = Classpath.EMPTY;
|
||||
if (deleted) {
|
||||
// Project has been removed no need to keep in not ready projects set
|
||||
notReadyProjects.remove(jp);
|
||||
// projectLocations.remove(projectName);
|
||||
} else {
|
||||
projectLocations.put(projectName, projectLoc);
|
||||
try {
|
||||
classpath = ClasspathUtil.resolve(jp, logger);
|
||||
List<CPE> filteredCPEs = new ArrayList<>(classpath.getEntries().size());
|
||||
for (CPE cpe : classpath.getEntries()) {
|
||||
// Some classpath entries don't exist yet (to be downloaded during the build). Filter them out as these JARs won't be indexed until they exist
|
||||
if (!Classpath.isBinary(cpe) || new File(cpe.getPath()).exists()) {
|
||||
filteredCPEs.add(cpe);
|
||||
}
|
||||
}
|
||||
if (filteredCPEs.size() != classpath.getEntries().size()) {
|
||||
// If some entries in the classpath don't exist yet add the project to not ready projects set to process later when project is built
|
||||
notReadyProjects.add(jp);
|
||||
// Only send effective classpath that has all entries physically present.
|
||||
classpath = new Classpath(filteredCPEs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user