diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index de2a883e7..3f12d5b1d 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -17,7 +17,6 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; -import org.eclipse.lsp4j.TextDocumentIdentifier; import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider; import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider; import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine; @@ -162,7 +161,9 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { @Override public CompletableFuture shutdown() { - liveHoverWatchdog.shutdown(); + this.liveHoverWatchdog.shutdown(); + this.indexer.shutdown(); + return super.shutdown(); } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index 7c58b34c4..2c3038611 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -19,10 +19,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -63,6 +65,9 @@ public class SpringIndexer { private CompletableFuture initializeTask; + private final Thread updateWorker; + private final BlockingQueue updateQueue; + public SpringIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; @@ -70,9 +75,26 @@ public class SpringIndexer { this.symbols = Collections.synchronizedList(new ArrayList<>()); this.symbolsByDoc = new ConcurrentHashMap<>(); + + this.updateQueue = new LinkedBlockingQueue<>(); + this.updateWorker = new Thread(new Runnable() { + @Override + public void run() { + try { + while (true) { + UpdateItem updateItem = updateQueue.take(); + scanFile(updateItem.getDocURI(), updateItem.getContent(), updateItem.getClasspathEntries()); + updateItem.getFuture().complete(null); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + }, "Spring Annotation Index Update Worker"); } - public void initialize(final Path workspaceRoot) { + public CompletableFuture initialize(final Path workspaceRoot) { synchronized(this) { if (this.initializeTask == null) { this.initializeTask = CompletableFuture.runAsync(new Runnable() { @@ -81,31 +103,59 @@ public class SpringIndexer { System.out.println("start initial scan..."); scanFiles(workspaceRoot.toFile()); System.out.println("initial scan done...!!!"); + + updateWorker.start(); } }); } + + return this.initializeTask; } } - public void updateDocument(String docURI, String content) { - if (docURI.endsWith(".java")) { + public void shutdown() { + try { + if (this.initializeTask != null) { + initializeTask.cancel(true); + } + + if (updateWorker.isAlive()) { + updateWorker.interrupt(); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public CompletableFuture updateDocument(String docURI, String content) { + if (docURI.endsWith(".java") && initializeTask != null) { try { + initializeTask.get(); + IJavaProject project = projectFinder.find(new File(new URI(docURI))); if (project != null) { String[] classpathEntries = getClasspathEntries(project); - scanFile(docURI, content, classpathEntries); + + CompletableFuture future = new CompletableFuture<>(); + UpdateItem updateItem = new UpdateItem(docURI, content, classpathEntries, future); + updateQueue.put(updateItem); + return future; } } catch (Exception e) { e.printStackTrace(); } } + + return null; } public List getAllSymbols(String query) { if (initializeTask != null) { try { initializeTask.get(); + if (query != null && query.length() > 0) { return searchMatchingSymbols(this.symbols, query); } else { @@ -362,4 +412,39 @@ public class SpringIndexer { .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); } + /** + * inner class to capture items for the update worker + */ + private static class UpdateItem { + + private final String docURI; + private final String content; + private final String[] classpathEntries; + + private final CompletableFuture future; + + public UpdateItem(String docURI, String content, String[] classpathEntries, CompletableFuture future) { + this.docURI = docURI; + this.content = content; + this.classpathEntries = classpathEntries; + this.future = future; + } + + public String getDocURI() { + return docURI; + } + + public String getContent() { + return content; + } + + public String[] getClasspathEntries() { + return classpathEntries; + } + + public CompletableFuture getFuture() { + return future; + } + + } } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java index 5a09ad17a..46a256b91 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerTest.java @@ -20,6 +20,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.eclipse.lsp4j.SymbolInformation; @@ -158,7 +160,9 @@ public class SpringIndexerTest { // update document and update index String changedDocURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/SimpleMappingClass.java"; String newContent = FileUtils.readFileToString(new File(new URI(changedDocURI))).replace("mapping1", "mapping1-CHANGED"); - indexer.updateDocument(changedDocURI, newContent); + CompletableFuture updateFuture = indexer.updateDocument(changedDocURI, newContent); + + updateFuture.get(5, TimeUnit.SECONDS); // check for updated index per document List symbols = indexer.getSymbols(changedDocURI);