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 3f12d5b1d..8131c3265 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 @@ -42,22 +42,15 @@ import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider; import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; -import org.springframework.ide.vscode.commons.gradle.GradleCore; -import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; -import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService; -import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; -import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; @@ -71,19 +64,13 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { public static final String LANGUAGE_SERVER_PROCESS_PROPERTY = "spring-boot-language-server"; - public static final JavaProjectFinder DEFAULT_PROJECT_FINDER = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] { - new MavenProjectFinderStrategy(MavenCore.getDefault()), - new GradleProjectFinderStrategy(GradleCore.getDefault()), - new JavaProjectWithClasspathFileFinderStrategy() - }); - private final VscodeCompletionEngineAdapter completionEngine; private final SpringIndexer indexer; private final SpringLiveHoverWatchdog liveHoverWatchdog; private final WordHighlighter testHightlighter = null; //new WordHighlighter("foo"); - public BootJavaLanguageServer(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { + public BootJavaLanguageServer(JavaProjectManager javaProjectManager, SpringPropertyIndexProvider indexProvider) { super("vscode-boot-java"); System.setProperty(LANGUAGE_SERVER_PROCESS_PROPERTY, LANGUAGE_SERVER_PROCESS_PROPERTY); @@ -97,13 +84,13 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { validateWith(doc.getId(), reconcileEngine); }); - ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectFinder, indexProvider); + ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectManager, indexProvider); completionEngine = createCompletionEngineAdapter(this, bootCompletionEngine); completionEngine.setMaxCompletions(100); documents.onCompletion(completionEngine::getCompletions); documents.onCompletionResolve(completionEngine::resolveCompletion); - BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectFinder); + BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectManager); documents.onHover(hoverInfoProvider); liveHoverWatchdog = new SpringLiveHoverWatchdog(this, hoverInfoProvider); @@ -117,10 +104,10 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { } }); - ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder); + ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectManager); documents.onReferences(referencesHandler); - indexer = createAnnotationIndexer(this, javaProjectFinder); + indexer = createAnnotationIndexer(this, javaProjectManager); documents.onDidSave(params -> { String docURI = params.getDocument().getId().getUri(); String content = params.getDocument().get(); @@ -130,7 +117,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(indexer)); workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer)); - BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder); + BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectManager); documents.onCodeLens(codeLensHandler::createCodeLenses); documents.onCodeLensResolve(codeLensHandler::resolveCodeLens); @@ -167,7 +154,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { return super.shutdown(); } - protected ICompletionEngine createCompletionEngine(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { + protected ICompletionEngine createCompletionEngine(JavaProjectManager javaProjectManager, SpringPropertyIndexProvider indexProvider) { Map providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.scope.Constants.SPRING_SCOPE, new ScopeCompletionProcessor()); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueCompletionProcessor(indexProvider)); @@ -231,10 +218,10 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { " return ${entity};\n" + "}" )); - return new BootJavaCompletionEngine(javaProjectFinder, providers, snippetManager); + return new BootJavaCompletionEngine(javaProjectManager, providers, snippetManager); } - protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder) { + protected BootJavaHoverProvider createHoverHandler(JavaProjectManager javaProjectFinder) { HashMap providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingHoverProvider()); @@ -247,7 +234,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { return new BootJavaHoverProvider(this, javaProjectFinder, providers); } - protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectManager projectManager) { HashMap providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider()); providers.put(org.springframework.ide.vscode.boot.java.requestmapping.Constants.SPRING_GET_MAPPING, new RequestMappingSymbolProvider()); @@ -259,17 +246,17 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { providers.put(org.springframework.ide.vscode.boot.java.beans.Constants.SPRING_BEAN, new BeansSymbolProvider()); providers.put(org.springframework.ide.vscode.boot.java.beans.Constants.SPRING_COMPONENT, new ComponentSymbolProvider()); - return new SpringIndexer(this, projectFinder, providers); + return new SpringIndexer(this, projectManager, providers); } - protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectManager projectManager) { Map providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValuePropertyReferencesProvider(server)); - return new BootJavaReferencesHandler(server, projectFinder, providers); + return new BootJavaReferencesHandler(server, projectManager, providers); } - protected BootJavaCodeLensEngine createCodeLensEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + protected BootJavaCodeLensEngine createCodeLensEngine(SimpleLanguageServer server, JavaProjectManager projectFinder) { return new BootJavaCodeLensEngine(server, projectFinder); } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Main.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Main.java index a1a5664de..1272bc37f 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Main.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Main.java @@ -13,22 +13,33 @@ package org.springframework.ide.vscode.boot.java; import java.io.IOException; import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; +import org.springframework.ide.vscode.commons.gradle.GradleCore; +import org.springframework.ide.vscode.commons.gradle.GradleProjectManager; import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFileManager; /** * Starts up Language Server process - * + * * @author Martin Lippert */ public class Main { - + public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { - JavaProjectFinder javaProjectFinder = BootJavaLanguageServer.DEFAULT_PROJECT_FINDER; - DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); - SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexProvider); + CompositeJavaProjectManager javaProjectManager = new CompositeJavaProjectManager(new JavaProjectManager[] { + new MavenProjectManager(MavenCore.getDefault()), + new GradleProjectManager(GradleCore.getDefault()), + new JavaProjectWithClasspathFileManager() + }); + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectManager); + SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectManager, indexProvider); + javaProjectManager.setFileObserver(server.getWorkspaceService()); return server; }); } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCodeLensEngine.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCodeLensEngine.java index 41bace3bb..e9920ea32 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCodeLensEngine.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCodeLensEngine.java @@ -19,7 +19,7 @@ import org.eclipse.lsp4j.CodeLensParams; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -30,9 +30,9 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public class BootJavaCodeLensEngine { private final SimpleLanguageServer server; - private final JavaProjectFinder projectFinder; + private final JavaProjectManager projectFinder; - public BootJavaCodeLensEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + public BootJavaCodeLensEngine(SimpleLanguageServer server, JavaProjectManager projectFinder) { this.server = server; this.projectFinder = projectFinder; } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java index f47bd2042..2af8e5f9e 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java @@ -30,7 +30,7 @@ import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.text.IDocument; /** @@ -38,12 +38,12 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; */ public class BootJavaCompletionEngine implements ICompletionEngine { - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private Map completionProviders; private JavaSnippetManager snippets; - public BootJavaCompletionEngine(JavaProjectFinder projectFinder, Map specificProviders, JavaSnippetManager snippets) { - this.projectFinder = projectFinder; + public BootJavaCompletionEngine(JavaProjectManager projectManager, Map specificProviders, JavaSnippetManager snippets) { + this.projectManager = projectManager; this.completionProviders = specificProviders; this.snippets = snippets; } @@ -108,7 +108,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine { } private String[] getClasspathEntries(IDocument doc) throws Exception { - IJavaProject project = this.projectFinder.find(doc); + IJavaProject project = this.projectManager.find(doc); IClasspath classpath = project.getClasspath(); Stream classpathEntries = classpath.getClasspathEntries(); return classpathEntries diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index d719a838f..d45231119 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -36,7 +36,7 @@ import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; @@ -49,11 +49,11 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class BootJavaHoverProvider implements HoverHandler { - private JavaProjectFinder projectFinder; + private JavaProjectManager projectFinder; private SimpleLanguageServer server; private Map hoverProviders; - public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { + public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectManager projectFinder, Map specificProviders) { this.server = server; this.projectFinder = projectFinder; this.hoverProviders = specificProviders; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java index 05558835b..562536edb 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java @@ -28,7 +28,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.ReferenceParams; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; @@ -40,13 +40,13 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class BootJavaReferencesHandler implements ReferencesHandler { - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private SimpleLanguageServer server; private Map referenceProviders; - public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { + public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectManager projectManager, Map specificProviders) { this.server = server; - this.projectFinder = projectFinder; + this.projectManager = projectManager; this.referenceProviders = specificProviders; } @@ -124,7 +124,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { } private String[] getClasspathEntries(IDocument doc) throws Exception { - IJavaProject project = this.projectFinder.find(doc); + IJavaProject project = this.projectManager.find(doc); IClasspath classpath = project.getClasspath(); Stream classpathEntries = classpath.getClasspathEntries(); return classpathEntries 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 2c3038611..360f231d5 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 @@ -25,6 +25,7 @@ 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.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -46,7 +47,8 @@ import org.eclipse.lsp4j.SymbolKind; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager.Listener; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -57,7 +59,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public class SpringIndexer { private SimpleLanguageServer server; - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private Map symbolProviders; private List symbols; @@ -65,38 +67,47 @@ public class SpringIndexer { private CompletableFuture initializeTask; - private final Thread updateWorker; - private final BlockingQueue updateQueue; + private Thread updateWorker; + private BlockingQueue updateQueue; - public SpringIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { + private AtomicBoolean initializing = new AtomicBoolean(false); + + private final Listener projectListener = new Listener() { + + @Override + public void created(IJavaProject project) { + refresh(); + } + + @Override + public void changed(IJavaProject project) { + refresh(); + } + + @Override + public void deleted(IJavaProject project) { + refresh(); + } + + }; + + public SpringIndexer(SimpleLanguageServer server, JavaProjectManager projectManager, Map specificProviders) { this.server = server; - this.projectFinder = projectFinder; + this.projectManager = projectManager; this.symbolProviders = specificProviders; 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 CompletableFuture initialize(final Path workspaceRoot) { synchronized(this) { if (this.initializeTask == null) { + initializing.set(true); + if (projectManager != null) { + projectManager.addListener(projectListener); + } this.initializeTask = CompletableFuture.runAsync(new Runnable() { @Override public void run() { @@ -104,7 +115,25 @@ public class SpringIndexer { scanFiles(workspaceRoot.toFile()); System.out.println("initial scan done...!!!"); + SpringIndexer.this.updateQueue = new LinkedBlockingQueue<>(); + SpringIndexer.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"); + updateWorker.start(); + initializing.set(false); } }); } @@ -113,15 +142,33 @@ public class SpringIndexer { } } + public boolean isInitializing() { + return initializing.get(); + } + + private void refresh() { + synchronized (this) { + shutdown(); + initializeTask = null; + symbols.clear(); + symbolsByDoc.clear(); + initialize(server.getWorkspaceRoot()); + } + } + public void shutdown() { try { if (this.initializeTask != null) { initializeTask.cancel(true); } - if (updateWorker.isAlive()) { + if (updateWorker != null && updateWorker.isAlive()) { updateWorker.interrupt(); } + + if (projectManager != null) { + projectManager.removeListener(projectListener); + } } catch (Exception e) { e.printStackTrace(); @@ -133,7 +180,7 @@ public class SpringIndexer { try { initializeTask.get(); - IJavaProject project = projectFinder.find(new File(new URI(docURI))); + IJavaProject project = projectManager.find(new File(new URI(docURI))); if (project != null) { String[] classpathEntries = getClasspathEntries(project); @@ -216,7 +263,7 @@ public class SpringIndexer { .filter(path -> path.getFileName().toString().endsWith(".java")) .filter(Files::isRegularFile) .map(path -> path.toAbsolutePath().toString()) - .collect(Collectors.groupingBy((javaFile) -> projectFinder.find(new File(javaFile)))); + .collect(Collectors.groupingBy((javaFile) -> projectManager.find(new File(javaFile)))); System.out.println("scan directory done!!!"); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 2871fb420..f91c19725 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -14,7 +14,7 @@ package org.springframework.ide.vscode.boot.metadata; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -22,19 +22,19 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null); - private JavaProjectFinder javaProjectFinder; + private JavaProjectManager javaProjectManager; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(); private ProgressService progressService = (id, msg) -> { /* ignore */ }; - public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { - this.javaProjectFinder = javaProjectFinder; + public DefaultSpringPropertyIndexProvider(JavaProjectManager javaProjectManager) { + this.javaProjectManager = javaProjectManager; } @Override public FuzzyMap getIndex(IDocument doc) { - IJavaProject jp = javaProjectFinder.find(doc); + IJavaProject jp = javaProjectManager.find(doc); if (jp != null) { return indexManager.get(jp, progressService); } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java index 20f501ee7..98e0875b3 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java @@ -24,7 +24,8 @@ import org.junit.Before; import org.junit.Test; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.languageserver.testharness.Editor; @@ -37,7 +38,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; */ public class ScopeCompletionTest { - protected final JavaProjectFinder javaProjectFinder = new JavaProjectFinder() { + protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { @Override public boolean isProjectRoot(File file) { return false; diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java index 5a312f1d2..78ead4e6a 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java @@ -25,7 +25,8 @@ import org.junit.Test; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.languageserver.testharness.Editor; @@ -38,7 +39,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; */ public class ValueCompletionTest { - protected final JavaProjectFinder javaProjectFinder = new JavaProjectFinder() { + protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { @Override public boolean isProjectRoot(File file) { return false; diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingSymbolProviderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingSymbolProviderTest.java index 8ae8d944d..fd037452e 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingSymbolProviderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingSymbolProviderTest.java @@ -28,11 +28,10 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.requestmapping.Constants; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; -import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; @@ -43,7 +42,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class RequestMappingSymbolProviderTest { private Map symbolProviders; - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; @@ -52,13 +51,13 @@ public class RequestMappingSymbolProviderTest { symbolProviders = new HashMap<>(); symbolProviders.put(Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider()); - projectFinder = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] {new MavenProjectFinderStrategy(MavenCore.getDefault())}); + projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); return server; } }) { @@ -73,7 +72,7 @@ public class RequestMappingSymbolProviderTest { public void testSimpleRequestMappingSymbol() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()); indexer.initialize(directory.toPath()); @@ -87,7 +86,7 @@ public class RequestMappingSymbolProviderTest { public void testParentRequestMappingSymbol() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()); indexer.initialize(directory.toPath()); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerBeansTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerBeansTest.java index 54f2a0145..c1e150164 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerBeansTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerBeansTest.java @@ -29,11 +29,10 @@ import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider; import org.springframework.ide.vscode.boot.java.beans.Constants; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; -import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; @@ -44,7 +43,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class SpringIndexerBeansTest { private Map symbolProviders; - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; @@ -54,13 +53,13 @@ public class SpringIndexerBeansTest { symbolProviders.put(Constants.SPRING_BEAN, new BeansSymbolProvider()); symbolProviders.put(Constants.SPRING_COMPONENT, new ComponentSymbolProvider()); - projectFinder = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] {new MavenProjectFinderStrategy(MavenCore.getDefault())}); + projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); return server; } }) { @@ -74,7 +73,7 @@ public class SpringIndexerBeansTest { @Test public void testScanSimpleConfigurationClass() throws Exception { - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); indexer.initialize(directory.toPath()); @@ -86,7 +85,7 @@ public class SpringIndexerBeansTest { @Test public void testScanSimpleFunctionBean() throws Exception { - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); indexer.initialize(directory.toPath()); @@ -98,7 +97,7 @@ public class SpringIndexerBeansTest { @Test public void testScanSimpleComponentClass() throws Exception { - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); indexer.initialize(directory.toPath()); 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 46a256b91..5a40b62bd 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 @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.java.utils.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.File; @@ -32,11 +33,11 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.requestmapping.Constants; import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; -import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; @@ -47,22 +48,25 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class SpringIndexerTest { private Map symbolProviders; - private JavaProjectFinder projectFinder; + private JavaProjectManager projectManager; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; + private BasicFileObserver fileObserver; @Before public void setup() throws Exception { symbolProviders = new HashMap<>(); symbolProviders.put(Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider()); - projectFinder = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] {new MavenProjectFinderStrategy(MavenCore.getDefault())}); + projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); + fileObserver = new BasicFileObserver(); + projectManager.setFileObserver(fileObserver); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); return server; } }) { @@ -77,7 +81,7 @@ public class SpringIndexerTest { public void testScanningAllAnnotationsSimpleProjectUpfront() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -101,7 +105,7 @@ public class SpringIndexerTest { public void testRetrievingSymbolsPerDocument() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -128,7 +132,7 @@ public class SpringIndexerTest { public void testScanningAllAnnotationsMultiModuleProjectUpfront() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/").toURI()); indexer.initialize(directory.toPath()); @@ -153,7 +157,7 @@ public class SpringIndexerTest { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); // create initial index content - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -190,7 +194,7 @@ public class SpringIndexerTest { public void testFilterSymbolsUsingQueryString() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -212,7 +216,7 @@ public class SpringIndexerTest { public void testFilterSymbolsUsingQueryStringSplittedResult() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -230,7 +234,7 @@ public class SpringIndexerTest { public void testFilterSymbolsUsingQueryStringFullSymbolString() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); - SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders); + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); indexer.initialize(directory.toPath()); @@ -260,4 +264,27 @@ public class SpringIndexerTest { return false; } + @Test + public void testRefreshOnProjectChange() throws Exception { + harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); + + SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectManager, symbolProviders); + File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); + indexer.initialize(directory.toPath()).get(); + + List allSymbols = indexer.getAllSymbols(""); + assertEquals(8, allSymbols.size()); + + File pomFile = directory.toPath().resolve(MavenCore.POM_XML).toFile(); + + assertFalse(indexer.isInitializing()); + fileObserver.notifyFileChanged(pomFile.toURI().toString()); + // Refresh in progress + assertTrue(indexer.isInitializing()); + + allSymbols = indexer.getAllSymbols(""); + assertFalse(indexer.isInitializing()); + assertEquals(8, allSymbols.size()); + } + } diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java index ad52baecb..3ea96c9dd 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java @@ -20,22 +20,15 @@ import org.springframework.ide.vscode.boot.properties.hover.PropertiesHoverInfoP import org.springframework.ide.vscode.boot.properties.reconcile.SpringPropertiesReconcileEngine; import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext; import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine; -import org.springframework.ide.vscode.commons.gradle.GradleCore; -import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter.HoverType; -import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; -import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; -import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -60,12 +53,6 @@ import com.google.common.collect.ImmutableList; */ public class BootPropertiesLanguageServer extends SimpleLanguageServer { - public static final JavaProjectFinder DEFAULT_PROJECT_FINDER = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] { - new MavenProjectFinderStrategy(MavenCore.getDefault()), - new GradleProjectFinderStrategy(GradleCore.getDefault()), - new JavaProjectWithClasspathFileFinderStrategy() - }); - private static final String YML = ".yml"; private static final String PROPERTIES = ".properties"; @@ -73,7 +60,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { public boolean includeDeindentedProposals() { return false; }; }; // Shared: - private final JavaProjectFinder javaProjectFinder; + private final JavaProjectManager javaProjectManager; private final SpringPropertyIndexProvider indexProvider; private final TypeUtilProvider typeUtilProvider; private final VscodeCompletionEngineAdapter completionEngine; @@ -88,12 +75,12 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { private final YamlStructureProvider yamlStructureProvider= YamlStructureProvider.DEFAULT; private YamlAssistContextProvider yamlAssistContextProvider; - public BootPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder javaProjectFinder) { + public BootPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectManager javaProjectManager) { super("vscode-boot-properties"); this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; - this.javaProjectFinder = javaProjectFinder; - this.completionFactory = new PropertyCompletionFactory(javaProjectFinder); + this.javaProjectManager = javaProjectManager; + this.completionFactory = new PropertyCompletionFactory(javaProjectManager); this.yamlAssistContextProvider = new YamlAssistContextProvider() { @Override public YamlAssistContext getGlobalAssistContext(YamlDocument ydoc) { @@ -123,7 +110,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { } private ICompletionEngine getCompletionEngine() { - ICompletionEngine propertiesCompletions = new SpringPropertiesCompletionEngine(indexProvider, typeUtilProvider, javaProjectFinder); + ICompletionEngine propertiesCompletions = new SpringPropertiesCompletionEngine(indexProvider, typeUtilProvider, javaProjectManager); ICompletionEngine yamlCompletions = new YamlCompletionEngine(yamlStructureProvider, yamlAssistContextProvider, COMPLETION_OPTIONS); return (IDocument document, int offset) -> { String uri = document.getUri(); @@ -139,7 +126,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { } protected HoverInfoProvider getHoverProvider() { - HoverInfoProvider propertiesHovers = new PropertiesHoverInfoProvider(indexProvider, typeUtilProvider, javaProjectFinder); + HoverInfoProvider propertiesHovers = new PropertiesHoverInfoProvider(indexProvider, typeUtilProvider, javaProjectManager); HoverInfoProvider ymlHovers = new YamlHoverInfoProvider(parser, yamlStructureProvider, yamlAssistContextProvider); return (IDocument document, int offset) -> { diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java index e77b0f41e..77432d99b 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/Main.java @@ -15,9 +15,15 @@ import java.io.IOException; import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.commons.gradle.GradleCore; +import org.springframework.ide.vscode.commons.gradle.GradleProjectManager; import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFileManager; import org.springframework.ide.vscode.commons.util.text.IDocument; /** @@ -31,10 +37,15 @@ public class Main { public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { - JavaProjectFinder javaProjectFinder = BootPropertiesLanguageServer.DEFAULT_PROJECT_FINDER; - DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); - TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(doc)); - SimpleLanguageServer server = new BootPropertiesLanguageServer(indexProvider, typeUtilProvider, javaProjectFinder); + CompositeJavaProjectManager javaProjectManager = new CompositeJavaProjectManager(new JavaProjectManager[] { + new MavenProjectManager(MavenCore.getDefault()), + new GradleProjectManager(GradleCore.getDefault()), + new JavaProjectWithClasspathFileManager() + }); + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectManager); + TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectManager.find(doc)); + SimpleLanguageServer server = new BootPropertiesLanguageServer(indexProvider, typeUtilProvider, javaProjectManager); + javaProjectManager.setFileObserver(server.getWorkspaceService()); indexProvider.setProgressService(server.getProgressService()); return server; }); diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java index 0d84eb5b9..8465342f6 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java @@ -19,7 +19,7 @@ import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -113,9 +113,9 @@ public class PropertyCompletionFactory { return proposal; } - private JavaProjectFinder documentContextFinder; + private JavaProjectManager documentContextFinder; - public PropertyCompletionFactory(JavaProjectFinder documentContextFinder) { + public PropertyCompletionFactory(JavaProjectManager documentContextFinder) { this.documentContextFinder = documentContextFinder; } diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 685fc56f9..ba3ee6f2d 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -13,7 +13,7 @@ package org.springframework.ide.vscode.boot.metadata; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -21,18 +21,18 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null, null); - private JavaProjectFinder javaProjectFinder; + private JavaProjectManager javaProjectManager; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); private ProgressService progressService = (id, msg) -> { /*ignore*/ }; - public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { - this.javaProjectFinder = javaProjectFinder; + public DefaultSpringPropertyIndexProvider(JavaProjectManager javaProjectManager) { + this.javaProjectManager = javaProjectManager; } @Override public FuzzyMap getIndex(IDocument doc) { - IJavaProject jp = javaProjectFinder.find(doc); + IJavaProject jp = javaProjectManager.find(doc); if (jp!=null) { return indexManager.get(jp, progressService); } diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/completions/SpringPropertiesCompletionEngine.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/completions/SpringPropertiesCompletionEngine.java index 54264edbd..838a9a098 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/completions/SpringPropertiesCompletionEngine.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/completions/SpringPropertiesCompletionEngine.java @@ -18,7 +18,7 @@ import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.IDocument;; @@ -37,10 +37,10 @@ public class SpringPropertiesCompletionEngine implements ICompletionEngine { * Constructor used in 'production'. Wires up stuff properly for running inside a normal * Eclipse runtime. */ - public SpringPropertiesCompletionEngine(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder projectFinder) { + public SpringPropertiesCompletionEngine(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectManager projectManager) { this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; - this.completionFactory = new PropertyCompletionFactory(projectFinder); + this.completionFactory = new PropertyCompletionFactory(projectManager); } /** diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverInfoProvider.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverInfoProvider.java index a12562845..0ddf1b81a 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverInfoProvider.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverInfoProvider.java @@ -14,7 +14,7 @@ package org.springframework.ide.vscode.boot.properties.hover; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.IRegion; @@ -25,9 +25,9 @@ public class PropertiesHoverInfoProvider implements HoverInfoProvider { private SpringPropertyIndexProvider indexProvider; private TypeUtilProvider typeUtilProvider; - private JavaProjectFinder projectFinder; + private JavaProjectManager projectFinder; - public PropertiesHoverInfoProvider(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder projectFinder) { + public PropertiesHoverInfoProvider(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectManager projectFinder) { this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; this.projectFinder = projectFinder; diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java index 8395cc90e..8c4bec7b0 100644 --- a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/editor/harness/AbstractPropsEditorTest.java @@ -28,7 +28,8 @@ import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -45,7 +46,7 @@ public abstract class AbstractPropsEditorTest { private ProjectsHarness projects = ProjectsHarness.INSTANCE; protected PropertyIndexHarness md; - protected final JavaProjectFinder javaProjectFinder = new JavaProjectFinder() { + protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { @Override public boolean isProjectRoot(File file) { return false; diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/BootPropertiesLanguageServerTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/BootPropertiesLanguageServerTest.java index 574974c99..2683123de 100644 --- a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/BootPropertiesLanguageServerTest.java +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/BootPropertiesLanguageServerTest.java @@ -22,7 +22,8 @@ import org.eclipse.lsp4j.TextDocumentSyncKind; import org.junit.Test; import org.springframework.ide.vscode.boot.BootPropertiesLanguageServer; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; @@ -40,7 +41,7 @@ public class BootPropertiesLanguageServerTest { } private LanguageServerHarness newHarness() throws Exception { - JavaProjectFinder nullJavaProjectFinder = new JavaProjectFinder() { + JavaProjectManager nullJavaProjectFinder = new AbstractJavaProjectManager() { @Override public boolean isProjectRoot(File file) { return false; diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java index ae7121144..7ee33a2cf 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java @@ -13,6 +13,7 @@ package org.springframework.ide.vscode.commons.gradle; import java.io.File; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.Log; /** * Implementation of Gradle Java project @@ -32,5 +33,13 @@ public class GradleJavaProject implements IJavaProject { public GradleProjectClasspath getClasspath() { return classpath; } + + void update(GradleCore gradle, File projectDir) { + try { + this.classpath = new GradleProjectClasspath(gradle, projectDir); + } catch (GradleException e) { + Log.log(e); + } + } } diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java deleted file mode 100644 index bde90e35a..000000000 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.commons.gradle; - -import java.io.File; -import java.util.concurrent.ExecutionException; - -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.util.FileUtils; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -/** - * Tests whether document belongs to a Gradle project - * - * @author Alex Boyko - * - */ -public class GradleProjectFinderStrategy implements IJavaProjectFinderStrategy { - - private Cache cache = CacheBuilder.newBuilder().build(); - - private GradleCore gradle; - - public GradleProjectFinderStrategy(GradleCore gradle) { - this.gradle = gradle; - } - - @Override - public GradleJavaProject find(File file) throws ExecutionException { - File gradlebuild = FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE); - if (gradlebuild != null) { - return cache.get(gradlebuild.getParentFile(), () -> { - System.out.println("create gradle project: " + file.getAbsolutePath()); - GradleJavaProject project = new GradleJavaProject(gradle, gradlebuild.getParentFile()); - System.out.println("gradle project created"); - return project; - }); - } - return null; - } - - @Override - public boolean isProjectRoot(File file) { - return FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE, false) != null; - } - -} diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectManager.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectManager.java new file mode 100644 index 000000000..747afb668 --- /dev/null +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectManager.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.gradle; + +import java.io.File; +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver.FileListener; +import org.springframework.ide.vscode.commons.util.FileUtils; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Tests whether document belongs to a Gradle project + * + * @author Alex Boyko + * + */ +public class GradleProjectManager extends AbstractJavaProjectManager { + + private Cache cache = CacheBuilder.newBuilder().build(); + + private GradleCore gradle; + + public GradleProjectManager(GradleCore gradle) { + this.gradle = gradle; + } + + @Override + public GradleJavaProject find(File file) { + File gradlebuild = FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE); + if (gradlebuild != null) { + try { + return cache.get(gradlebuild.getParentFile(), () -> { + System.out.println("create gradle project: " + file.getAbsolutePath()); + GradleJavaProject project = new GradleJavaProject(gradle, gradlebuild.getParentFile()); + System.out.println("gradle project created"); + return project; + }); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + } + return null; + } + + @Override + public boolean isProjectRoot(File file) { + return FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE, false) != null; + } + + + + @Override + public void setFileObserver(FileObserver fileObserver) { + super.setFileObserver(fileObserver); + cache.invalidateAll(); + } + + @Override + protected FileListener createFileListener() { + return new FileListener() { + + private File getProjectFolderFromUri(String uri) { + return Paths.get(URI.create(uri)).toFile().getParentFile(); + } + + @Override + public void deleted(String uri) { + File gradleProjectFolder = getProjectFolderFromUri(uri); + GradleJavaProject project = cache.getIfPresent(gradleProjectFolder); + if (project != null) { + cache.invalidate(gradleProjectFolder); + notifyProjectDeleted(project); + } + } + + @Override + public void created(String uri) { + // Ignore created projects - cache should take care of newly created ones + } + + @Override + public void changed(String uri) { + File gradleProjectFolder = getProjectFolderFromUri(uri); + GradleJavaProject project = cache.getIfPresent(gradleProjectFolder); + if (project != null) { + project.update(gradle, gradleProjectFolder); + notifyProjectChanged(project); + } + } + + @Override + public boolean accept(String uri) { + return GradleCore.GRADLE_BUILD_FILE.equals(Paths.get(URI.create(uri)).getFileName().toString()); + } + }; + } + +} diff --git a/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java b/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java index f99c08bb8..b7777120b 100644 --- a/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java +++ b/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java @@ -12,8 +12,10 @@ package org.springframework.ide.vscode.commons.gradle; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -21,6 +23,9 @@ import java.util.Set; import java.util.stream.Collectors; import org.junit.Test; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager.Listener; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; /** * Tests covering Gradle project data @@ -54,5 +59,40 @@ public class GradleProjectTest { List resources = project.getClasspath().getClasspathResources().collect(Collectors.toList()); assertArrayEquals(new String[] {"test-resource-1.txt"}, resources.toArray(new String[resources.size()])); } + + @Test + public void testGradleFileChanges() throws Exception { + Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/empty-gradle-project").toURI()); + File gradleFile = testProjectPath.resolve(GradleCore.GRADLE_BUILD_FILE).toFile(); + GradleProjectManager manager = new GradleProjectManager(GradleCore.getDefault()); + BasicFileObserver fileObserver = new BasicFileObserver(); + manager.setFileObserver(fileObserver); + IJavaProject[] projectChanged = new IJavaProject[] { null }; + IJavaProject[] projectDeleted = new IJavaProject[] { null }; + manager.addListener(new Listener() { + @Override + public void created(IJavaProject project) {} + + @Override + public void changed(IJavaProject project) { + projectChanged[0] = project; + } + @Override + public void deleted(IJavaProject project) { + projectDeleted[0] = project; + } + }); + + // Get the project from cache + GradleJavaProject cachedProject = manager.find(gradleFile); + assertNotNull(cachedProject); + + fileObserver.notifyFileChanged(gradleFile.toURI().toString()); + assertEquals(cachedProject, projectChanged[0]); + + fileObserver.notifyFileDeleted(gradleFile.toURI().toString()); + assertEquals(cachedProject, projectDeleted[0]); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectManager.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectManager.java new file mode 100644 index 000000000..6c4db536a --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectManager.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.java; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver.FileListener; +import org.springframework.ide.vscode.commons.util.text.IDocument; +import org.springframework.ide.vscode.commons.util.ListenerList; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.StringUtil; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * Base implementation of {@link JavaProjectManager} + * + * @author Alex Boyko + * + */ +public abstract class AbstractJavaProjectManager implements JavaProjectManager { + + private ListenerList listeners; + + private FileObserver fileObserver; + + private Supplier fileListener; + + public AbstractJavaProjectManager() { + this.fileListener = Suppliers.memoize(() -> createFileListener()); + this.listeners = new ListenerList<>(); + } + + protected FileListener createFileListener() { + return null; + } + + @Override + public IJavaProject find(IDocument doc) { + try { + String uriStr = doc.getUri(); + if (StringUtil.hasText(uriStr)) { + URI uri = new URI(uriStr); + // TODO: This only work with File uri. Should it work with others + // too? + if (uri.getScheme().equalsIgnoreCase("file")) { + File file = new File(uri).getAbsoluteFile(); + return find(file); + } + } + } + catch (URISyntaxException e) { + Log.log(e); + } + return null; + } + + @Override + public void setFileObserver(FileObserver fileObserver) { + FileListener listener= fileListener.get(); + if (this.fileObserver != null && listener != null) { + this.fileObserver.removeListener(listener); + } + this.fileObserver = fileObserver; + if (this.fileObserver != null && listener != null) { + this.fileObserver.addListener(listener); + } + } + + final protected FileObserver getFileObserver() { + return this.fileObserver; + } + + @Override + public void addListener(Listener listener) { + listeners.add(listener); + } + + @Override + public void removeListener(Listener listener) { + listeners.remove(listener); + } + + final protected void notifyProjectCreated(IJavaProject project) { + listeners.forEach(l -> l.created(project)); + } + + final protected void notifyProjectChanged(IJavaProject project) { + listeners.forEach(l -> l.changed(project)); + } + + final protected void notifyProjectDeleted(IJavaProject project) { + listeners.forEach(l -> l.deleted(project)); + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/DefaultJavaProjectFinder.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java similarity index 61% rename from headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/DefaultJavaProjectFinder.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java index ae8705c1d..39f1b7393 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/DefaultJavaProjectFinder.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java @@ -1,32 +1,40 @@ /******************************************************************************* - * Copyright (c) 2016, 2017 Pivotal, Inc. + * Copyright (c) 2017 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Pivotal, Inc. - initial API and implementation + * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.java; import java.io.File; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.IDocument; -public class DefaultJavaProjectFinder implements JavaProjectFinder { - - private final IJavaProjectFinderStrategy[] strategies; +/** + * Composite project manager that acts a single project manager but consissts of many project managers + * + * @author Alex Boyko + * + */ +public class CompositeJavaProjectManager implements JavaProjectManager { - public DefaultJavaProjectFinder(IJavaProjectFinderStrategy[] strategies) { - this.strategies = strategies; + private final JavaProjectManager[] projectManagers; + + public CompositeJavaProjectManager(JavaProjectManager[] projectManagers) { + this.projectManagers = projectManagers; } - + @Override public IJavaProject find(IDocument doc) { try { @@ -46,10 +54,10 @@ public class DefaultJavaProjectFinder implements JavaProjectFinder { } return null; } - + @Override public IJavaProject find(File file) { - for (IJavaProjectFinderStrategy strategy : strategies) { + for (JavaProjectManager strategy : projectManagers) { try { IJavaProject project = strategy.find(file); if (project != null) { @@ -64,7 +72,7 @@ public class DefaultJavaProjectFinder implements JavaProjectFinder { @Override public boolean isProjectRoot(File file) { - for (IJavaProjectFinderStrategy strategy : strategies) { + for (JavaProjectManager strategy : projectManagers) { try { if (strategy.isProjectRoot(file)) { return true; @@ -75,5 +83,19 @@ public class DefaultJavaProjectFinder implements JavaProjectFinder { } return false; } - + + public void setFileObserver(FileObserver fileObserver) { + Arrays.stream(projectManagers).forEach(pm -> pm.setFileObserver(fileObserver)); + } + + @Override + public void addListener(Listener listener) { + Arrays.stream(projectManagers).forEach(pm -> pm.addListener(listener)); + } + + @Override + public void removeListener(Listener listener) { + Arrays.stream(projectManagers).forEach(pm -> pm.removeListener(listener)); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/IJavaProjectFinderStrategy.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/IJavaProjectFinderStrategy.java deleted file mode 100644 index 65453a589..000000000 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/IJavaProjectFinderStrategy.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.commons.languageserver.java; - -import java.io.File; - -import org.springframework.ide.vscode.commons.java.IJavaProject; - -/** - * Strategy foe finding Java project for a document - * - * @author Alex Boyko - */ -public interface IJavaProjectFinderStrategy { - - IJavaProject find(File file) throws Exception; - boolean isProjectRoot(File file); - -} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java similarity index 58% rename from headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java index 1c18b5196..46e6ef874 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 2017 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,12 +13,29 @@ package org.springframework.ide.vscode.commons.languageserver.java; import java.io.File; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.text.IDocument; -public interface JavaProjectFinder { +/** + * Java project manager. Able to find a java project for a file or document and + * provide project change listening mechanism + * + * @author Alex Boyko + */ +public interface JavaProjectManager { + + interface Listener { + void created(IJavaProject project); + void changed(IJavaProject project); + void deleted(IJavaProject project); + } + void setFileObserver(FileObserver fileObserver); IJavaProject find(IDocument doc); IJavaProject find(File file); boolean isProjectRoot(File file); + + void addListener(Listener listener); + void removeListener( Listener listener); } diff --git a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/java/test/CompositeJavaProjectManagerTest.java b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/java/test/CompositeJavaProjectManagerTest.java new file mode 100644 index 000000000..85a66afa1 --- /dev/null +++ b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/java/test/CompositeJavaProjectManagerTest.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.java.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.File; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager.Listener; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver.FileListener; +import org.springframework.ide.vscode.commons.util.FileUtils; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Tests for {@link CompositeJavaProjectManager} + * + * @author Alex Boyko + * + */ +public class CompositeJavaProjectManagerTest { + + private AbstractJavaProjectManager createProjectManagerForFile(String fileName) { + return new AbstractJavaProjectManager() { + + private Cache cache = CacheBuilder.newBuilder().build(); + + @Override + public boolean isProjectRoot(File file) { + return FileUtils.findFile(file, fileName, false) != null; + } + + @Override + public IJavaProject find(File file) { + if (fileName.equals(file.toPath().getFileName().toString())) { + try { + return cache.get(file, () -> { + return new IJavaProject() { + @Override + public IClasspath getClasspath() { + return null; + } + }; + }); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + } + return null; + } + + @Override + protected FileListener createFileListener() { + return new FileListener() { + + private File getFileFromUri(String uri) { + return Paths.get(URI.create(uri)).toFile(); + } + + @Override + public void deleted(String uri) { + File file = getFileFromUri(uri); + IJavaProject project = cache.getIfPresent(file); + if (project != null) { + cache.invalidate(file); + notifyProjectDeleted(project); + } + } + + @Override + public void created(String uri) { + } + + @Override + public void changed(String uri) { + File file = getFileFromUri(uri); + IJavaProject project = cache.getIfPresent(file); + if (project != null) { + notifyProjectChanged(project); + } + } + + @Override + public boolean accept(String uri) { + return fileName.equals(Paths.get(URI.create(uri)).getFileName().toString()); + } + }; + } + + + }; + } + + @Test + public void testListeners() throws Exception { + AbstractJavaProjectManager manager1 = createProjectManagerForFile("test-1"); + AbstractJavaProjectManager manager2 = createProjectManagerForFile("test-2"); + CompositeJavaProjectManager compositeManager = new CompositeJavaProjectManager(new JavaProjectManager[] { + manager1, + manager2 + }); + BasicFileObserver fileObserver = new BasicFileObserver(); + compositeManager.setFileObserver(fileObserver); + + Path containerPath = Paths.get(CompositeJavaProjectManagerTest.class.getResource("/").toURI()); + + File projectFile1 = containerPath.resolve("test-1").toFile(); + IJavaProject p1 = compositeManager.find(projectFile1); + assertNotNull(p1); + + File projectFile2 = containerPath.resolve("test-2").toFile(); + IJavaProject p2 = compositeManager.find(projectFile2); + assertNotNull(p2); + + IJavaProject[] projectChanged = new IJavaProject[] { null }; + IJavaProject[] projectDeleted = new IJavaProject[] { null }; + + compositeManager.addListener(new Listener() { + + @Override + public void created(IJavaProject project) { + } + + @Override + public void changed(IJavaProject project) { + projectChanged[0] = project; + } + + @Override + public void deleted(IJavaProject project) { + projectDeleted[0] = project; + } + + }); + + fileObserver.notifyFileChanged(projectFile1.toURI().toString()); + assertEquals(p1, projectChanged[0]); + assertNull(projectDeleted[0]); + projectChanged[0] = projectDeleted[0] = null; + + fileObserver.notifyFileDeleted(projectFile1.toURI().toString()); + assertNull(projectChanged[0]); + assertEquals(p1, projectDeleted[0]); + projectChanged[0] = projectDeleted[0] = null; + + fileObserver.notifyFileChanged(projectFile2.toURI().toString()); + assertEquals(p2, projectChanged[0]); + assertNull(projectDeleted[0]); + projectChanged[0] = projectDeleted[0] = null; + + fileObserver.notifyFileDeleted(projectFile2.toURI().toString()); + assertNull(projectChanged[0]); + assertEquals(p2, projectDeleted[0]); + projectChanged[0] = projectDeleted[0] = null; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java index 5fc11d861..65407653e 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java @@ -21,18 +21,21 @@ import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.WorkspaceSymbolParams; import org.eclipse.lsp4j.services.WorkspaceService; import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.Log; import com.google.common.collect.ImmutableList; import reactor.core.publisher.Mono; -public class SimpleWorkspaceService implements WorkspaceService { +public class SimpleWorkspaceService implements WorkspaceService, FileObserver { private SimpleLanguageServer server; private ListenerList configurationListeners = new ListenerList<>(); private ExecuteCommandHandler executeCommandHandler; private WorkspaceSymbolHandler workspaceSymbolHandler; + private org.springframework.ide.vscode.commons.util.ListenerList fileListeners = new org.springframework.ide.vscode.commons.util.ListenerList<>(); public SimpleWorkspaceService(SimpleLanguageServer server) { this.server = server; @@ -59,6 +62,32 @@ public class SimpleWorkspaceService implements WorkspaceService { @Override public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { + params.getChanges().forEach(event -> fileListeners.forEach(listener -> { + try { + String uri = event.getUri(); + if (uri != null) { + if (listener.accept(event.getUri())) { + switch (event.getType()) { + case Created: + listener.created(uri); + break; + case Changed: + listener.changed(uri); + break; + case Deleted: + listener.deleted(uri); + break; + default: + Log.log("Uknown file change type '" + event.getType() + "' for file: " + uri); + break; + + } + } + } + } catch (Throwable t) { + Log.log(t); + } + })); } @Override @@ -87,4 +116,14 @@ public class SimpleWorkspaceService implements WorkspaceService { return this.workspaceSymbolHandler != null; } + @Override + public void addListener(FileListener listener) { + fileListeners.add(listener); + } + + @Override + public void removeListener(FileListener listener) { + fileListeners.remove(listener); + } + } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/JavaProjectWithClasspathFileFinderStrategy.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/JavaProjectWithClasspathFileFinderStrategy.java deleted file mode 100644 index dbcb67108..000000000 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/JavaProjectWithClasspathFileFinderStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.commons.maven; - -import java.io.File; -import java.util.concurrent.ExecutionException; - -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile; -import org.springframework.ide.vscode.commons.util.FileUtils; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -public class JavaProjectWithClasspathFileFinderStrategy implements IJavaProjectFinderStrategy { - - public Cache cache = CacheBuilder.newBuilder().build(); - - @Override - public JavaProjectWithClasspathFile find(File file) throws ExecutionException { - File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT); - if (cpFile != null) { - return cache.get(cpFile, () -> { - return new JavaProjectWithClasspathFile(cpFile); - }); - } - return null; - } - - @Override - public boolean isProjectRoot(File file) { - return FileUtils.findFile(file, MavenCore.CLASSPATH_TXT, false) != null; - } - -} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java deleted file mode 100644 index 42929c308..000000000 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 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 - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.commons.maven; - -import java.io.File; -import java.util.concurrent.ExecutionException; - -import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; -import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.commons.util.FileUtils; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -/** - * Finds Maven Project based - * - * @author Alex Boyko - */ -public class MavenProjectFinderStrategy implements IJavaProjectFinderStrategy { - - private Cache cache = CacheBuilder.newBuilder().build(); - - private MavenCore maven; - - public MavenProjectFinderStrategy(MavenCore maven) { - this.maven = maven; - } - - @Override - public MavenJavaProject find(File file) throws ExecutionException { - File pomFile = FileUtils.findFile(file, MavenCore.POM_XML); - if (pomFile != null) { - return cache.get(pomFile, () -> { - System.out.println("create maven project: " + file.getAbsolutePath()); - MavenJavaProject project = new MavenJavaProject(maven, pomFile); - System.out.println("maven project created"); - return project; - }); - } - return null; - } - - @Override - public boolean isProjectRoot(File file) { - return FileUtils.findFile(file, MavenCore.POM_XML, false) != null; - } - -} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index 549312c16..2435308ab 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -33,5 +33,9 @@ public class MavenJavaProject implements IJavaProject { public MavenProjectClasspath getClasspath() { return classpath; } + + void update(MavenCore maven, File pom) { + this.classpath = new MavenProjectClasspath(maven, pom); + } } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectManager.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectManager.java new file mode 100644 index 000000000..d9179f5e9 --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectManager.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2016, 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.maven.java; + +import java.io.File; +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver.FileListener; +import org.springframework.ide.vscode.commons.util.FileUtils; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Finds Maven Project based + * + * @author Alex Boyko + */ +public class MavenProjectManager extends AbstractJavaProjectManager { + + private Cache cache = CacheBuilder.newBuilder().build(); + + private MavenCore maven; + + public MavenProjectManager(MavenCore maven) { + this.maven = maven; + } + + @Override + public MavenJavaProject find(File file) { + File pomFile = FileUtils.findFile(file, MavenCore.POM_XML); + if (pomFile != null) { + try { + return cache.get(pomFile, () -> { + System.out.println("create maven project: " + file.getAbsolutePath()); + MavenJavaProject project = new MavenJavaProject(maven, pomFile); + System.out.println("maven project created"); + return project; + }); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + } + return null; + } + + @Override + public boolean isProjectRoot(File file) { + return FileUtils.findFile(file, MavenCore.POM_XML, false) != null; + } + + @Override + public void setFileObserver(FileObserver fileObserver) { + super.setFileObserver(fileObserver); + cache.invalidateAll(); + } + + @Override + protected FileListener createFileListener() { + return new FileListener() { + + private File getFileFromUri(String uri) { + return Paths.get(URI.create(uri)).toFile(); + } + + @Override + public void deleted(String uri) { + File pomFile = getFileFromUri(uri); + MavenJavaProject project = cache.getIfPresent(pomFile); + if (project != null) { + // Only invalidate and fire event if projects was cached. + cache.invalidate(pomFile); + notifyProjectDeleted(project); + } + } + + @Override + public void created(String uri) { + // Ignore created projects - cache should take care of newly created ones + } + + @Override + public void changed(String uri) { + File pomFile = getFileFromUri(uri); + MavenJavaProject project = cache.getIfPresent(pomFile); + if (project != null) { + // Only update and fire event if project cached + project.update(maven, pomFile); + notifyProjectChanged(project); + } + } + + @Override + public boolean accept(String uri) { + return MavenCore.POM_XML.equals(Paths.get(URI.create(uri)).getFileName().toString()); + } + }; + } + +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java index 808b80a9d..9a0d9f965 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java @@ -66,5 +66,10 @@ public class JavaProjectWithClasspathFile implements IJavaProject { return false; return true; } + + void update(File cpFile) { + this.cpFile = cpFile; + this.classpath = new FileClasspath(Paths.get(cpFile.toURI())); + } } \ No newline at end of file diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileManager.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileManager.java new file mode 100644 index 000000000..dd37c861c --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileManager.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2016, 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.maven.java.classpathfile; + +import java.io.File; +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectManager; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver.FileListener; +import org.springframework.ide.vscode.commons.util.FileUtils; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +public class JavaProjectWithClasspathFileManager extends AbstractJavaProjectManager { + + private Cache cache = CacheBuilder.newBuilder().build(); + + @Override + public JavaProjectWithClasspathFile find(File file) { + File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT); + if (cpFile != null) { + try { + return cache.get(cpFile, () -> { + return new JavaProjectWithClasspathFile(cpFile); + }); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + } + return null; + } + + @Override + public boolean isProjectRoot(File file) { + return FileUtils.findFile(file, MavenCore.CLASSPATH_TXT, false) != null; + } + + @Override + public void setFileObserver(FileObserver fileObserver) { + super.setFileObserver(fileObserver); + cache.invalidateAll(); + } + + @Override + protected FileListener createFileListener() { + return new FileListener() { + + @Override + public boolean accept(String uri) { + return MavenCore.CLASSPATH_TXT.equals(Paths.get(URI.create(uri)).getFileName().toString()); + } + + private File getFileFromUri(String uri) { + return Paths.get(URI.create(uri)).toFile(); + } + + @Override + public void deleted(String uri) { + File cpFile = getFileFromUri(uri); + JavaProjectWithClasspathFile project = cache.getIfPresent(cpFile); + if (project != null) { + // Only invalidate and fire event if projects was cached. + cache.invalidate(cpFile); + notifyProjectDeleted(project); + } + } + + @Override + public void created(String uri) { + // Ignore created projects - cache should take care of newly created ones + } + + @Override + public void changed(String uri) { + File cpFile = getFileFromUri(uri); + JavaProjectWithClasspathFile project = cache.getIfPresent(cpFile); + if (project != null) { + // Only update and fire event if project cached + project.update(cpFile); + notifyProjectChanged(project); + } + } + + + }; + } + +} diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectManagerTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectManagerTest.java new file mode 100644 index 000000000..db0746eca --- /dev/null +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectManagerTest.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.maven; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.Test; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager.Listener; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; + +/** + * Tests for {@link MavenProjectManager} + * + * @author Alex Boyko + * + */ +public class MavenProjectManagerTest { + + @Test + public void testPomFileChanges() throws Exception { + Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI()); + File pomFile = testProjectPath.resolve(MavenCore.POM_XML).toFile(); + MavenProjectManager manager = new MavenProjectManager(MavenCore.getDefault()); + BasicFileObserver fileObserver = new BasicFileObserver(); + manager.setFileObserver(fileObserver); + IJavaProject[] projectChanged = new IJavaProject[] { null }; + IJavaProject[] projectDeleted = new IJavaProject[] { null }; + manager.addListener(new Listener() { + @Override + public void created(IJavaProject project) {} + + @Override + public void changed(IJavaProject project) { + projectChanged[0] = project; + } + @Override + public void deleted(IJavaProject project) { + projectDeleted[0] = project; + } + }); + + // Get the project from cache + MavenJavaProject cachedProject = manager.find(pomFile); + assertNotNull(cachedProject); + + fileObserver.notifyFileChanged(pomFile.toURI().toString()); + assertEquals(cachedProject, projectChanged[0]); + + fileObserver.notifyFileDeleted(pomFile.toURI().toString()); + assertEquals(cachedProject, projectDeleted[0]); + } + + +} diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java new file mode 100644 index 000000000..9596a02c1 --- /dev/null +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.util; + +/** + * Basic implementation of File Observer interface + * + * @author Alex Boyko + * + */ +public class BasicFileObserver implements FileObserver { + + private ListenerList listeners = new ListenerList<>(); + + @Override + public void addListener(FileListener listener) { + listeners.add(listener); + } + + @Override + public void removeListener(FileListener listener) { + listeners.remove(listener); + } + + public void notifyFileChanged(String uri) { + listeners.forEach(l -> { + try { + if (l.accept(uri)) { + l.changed(uri); + } + } catch (Throwable t) { + Log.log(t); + } + }); + } + + public void notifyFileCreated(String uri) { + listeners.forEach(l -> { + try { + if (l.accept(uri)) { + l.created(uri); + } + } catch (Throwable t) { + Log.log(t); + } + }); + } + + public void notifyFileDeleted(String uri) { + listeners.forEach(l -> { + try { + if (l.accept(uri)) { + l.deleted(uri); + } + } catch (Throwable t) { + Log.log(t); + } + }); + } + +} diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java new file mode 100644 index 000000000..69e4fe9f5 --- /dev/null +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2017 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.util; + +/** + * Object able to add/remove {@link FileListener} objects and fire events via these listeners + * + * @author Alex Boyko + * + */ +public interface FileObserver { + + public interface FileListener { + boolean accept(String uri); + void changed(String uri); + void deleted(String uri); + void created(String uri); + } + + void addListener(FileListener listener); + + void removeListener(FileListener listener); + +}