From 9b8f95f21d54131cf8023a20830481ad5c2d03f3 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 16 Nov 2017 18:15:18 -0500 Subject: [PATCH] PT #152867306 Boot properties index reacts on project changes --- .../java/BootJavaLanguageServerParams.java | 2 +- .../utils/test/SpringPropertyIndexTest.java | 2 +- .../boot-properties-language-server/pom.xml | 7 + .../boot/BootPropertiesLanguageServer.java | 47 +++--- .../BootPropertiesLanguageServerParams.java | 140 ++++++++++++++++++ .../springframework/ide/vscode/boot/Main.java | 17 +-- .../DefaultSpringPropertyIndexProvider.java | 6 +- .../SpringPropertiesIndexManager.java | 79 ++++++---- .../boot/metadata/PropertiesIndexTest.java | 12 +- .../test/ApplicationPropertiesEditorTest.java | 5 +- .../boot/test/ApplicationYamlEditorTest.java | 5 +- .../BootPropertiesLanguageServerTest.java | 4 +- .../boot/test/SpringPropertiesIndexTest.java | 83 +++++++++++ .../vscode/commons/java}/BootProjectUtil.java | 4 +- 14 files changed, 327 insertions(+), 86 deletions(-) create mode 100644 headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServerParams.java create mode 100644 headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SpringPropertiesIndexTest.java rename headless-services/{boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils => commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java}/BootProjectUtil.java (85%) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerParams.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerParams.java index 721f5add3..0951d80e9 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerParams.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerParams.java @@ -15,13 +15,13 @@ import java.time.Duration; import java.util.Arrays; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; -import org.springframework.ide.vscode.boot.java.utils.BootProjectUtil; import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog; import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.gradle.GradleCore; import org.springframework.ide.vscode.commons.gradle.GradleProjectCache; import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder; +import org.springframework.ide.vscode.commons.java.BootProjectUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver; diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringPropertyIndexTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringPropertyIndexTest.java index e2c9102cd..00ef3c865 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringPropertyIndexTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringPropertyIndexTest.java @@ -48,7 +48,7 @@ public class SpringPropertyIndexTest { } @Test - public void testScanningAllAnnotationsSimpleProjectUpfront() throws Exception { + public void testPropertiesIndexRefreshOnProjectChange() throws Exception { harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI())); propertyIndexProvider = (DefaultSpringPropertyIndexProvider) harness.getServer().getSpringPropertyIndexProvider(); diff --git a/headless-services/boot-properties-language-server/pom.xml b/headless-services/boot-properties-language-server/pom.xml index 845171fcc..2b1974590 100644 --- a/headless-services/boot-properties-language-server/pom.xml +++ b/headless-services/boot-properties-language-server/pom.xml @@ -80,6 +80,13 @@ ${dependencies.version} test + + + org.mockito + mockito-all + ${mockito-version} + test + 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 e0983655d..5bfa8c41b 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 @@ -10,9 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot; -import java.nio.file.Paths; -import java.util.Arrays; - import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory; import org.springframework.ide.vscode.boot.common.RelaxedNameConfig; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; @@ -23,24 +20,17 @@ 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.GradleProjectCache; -import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder; -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.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.CompositeJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; +import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; 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.MavenCore; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder; 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; @@ -72,8 +62,8 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { public boolean includeDeindentedProposals() { return false; }; }; // Shared: - private final CompositeProjectOvserver projectObserver; - private final CompositeJavaProjectFinder javaProjectFinder; + private final ProjectObserver projectObserver; + private final JavaProjectFinder javaProjectFinder; private final SpringPropertyIndexProvider indexProvider; private final TypeUtilProvider typeUtilProvider; private final VscodeCompletionEngineAdapter completionEngine; @@ -88,11 +78,16 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { private final YamlStructureProvider yamlStructureProvider= YamlStructureProvider.DEFAULT; private YamlAssistContextProvider yamlAssistContextProvider; - public BootPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, CompositeJavaProjectFinder javaProjectFinder) { + public BootPropertiesLanguageServer(LSFactory _params) { super("vscode-boot-properties"); - this.indexProvider = indexProvider; - this.typeUtilProvider = typeUtilProvider; - this.javaProjectFinder = javaProjectFinder; + + BootPropertiesLanguageServerParams serverParams = _params.create(this); + + this.indexProvider = serverParams.indexProvider; + this.typeUtilProvider = serverParams.typeUtilProvider; + this.javaProjectFinder = serverParams.projectFinder; + this.projectObserver = serverParams.projectObserver; + this.completionFactory = new PropertyCompletionFactory(javaProjectFinder); this.yamlAssistContextProvider = new YamlAssistContextProvider() { @Override @@ -119,17 +114,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { HoverInfoProvider hoverInfoProvider = getHoverProvider(); hoverEngine = new VscodeHoverEngineAdapter(this, hoverInfoProvider); - documents.onHover(hoverEngine::getHover); - - // Initialize project finders, project caches and project observers - MavenProjectCache mavenProjectCache = new MavenProjectCache(getWorkspaceService().getFileObserver(), MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); - javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); - - GradleProjectCache gradleProjectCache = new GradleProjectCache(getWorkspaceService().getFileObserver(), GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); - javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); - - projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); - + documents.onHover(hoverEngine::getHover); } private ICompletionEngine getCompletionEngine() { @@ -197,4 +182,8 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { public ProjectObserver getProjectObserver() { return projectObserver; } + + public SpringPropertyIndexProvider getPropertiesIndexProvider() { + return indexProvider; + } } diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServerParams.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServerParams.java new file mode 100644 index 000000000..acfc4ba88 --- /dev/null +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServerParams.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * 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.boot; + +import java.nio.file.Paths; +import java.util.Arrays; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; +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.gradle.GradleCore; +import org.springframework.ide.vscode.commons.gradle.GradleProjectCache; +import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder; +import org.springframework.ide.vscode.commons.java.BootProjectUtil; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; +import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; +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.MavenProjectCache; +import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * Parameters for creating Boot Properties language server + * + * @author Alex Boyko + * + */ +public class BootPropertiesLanguageServerParams { + + public final JavaProjectFinder projectFinder; + public final ProjectObserver projectObserver; + public final SpringPropertyIndexProvider indexProvider; + public final TypeUtilProvider typeUtilProvider; + + public BootPropertiesLanguageServerParams( + JavaProjectFinder projectFinder, + ProjectObserver projectObserver, + SpringPropertyIndexProvider indexProvider, + TypeUtilProvider typeUtilProvider + ) { + super(); + this.projectFinder = projectFinder; + this.projectObserver = projectObserver; + this.indexProvider = indexProvider; + this.typeUtilProvider = typeUtilProvider; + } + + public static LSFactory createDefault() { + return (SimpleLanguageServer server) -> { + // Initialize project finders, project caches and project observers + FileObserver fileObserver = server.getWorkspaceService().getFileObserver(); + CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); + javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); + + GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER)); + javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); + + CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); + + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver); + indexProvider.setProgressService(server.getProgressService()); + + return new BootPropertiesLanguageServerParams( + javaProjectFinder.filter(BootProjectUtil::isBootProject), + projectObserver, + indexProvider, + (IDocument doc) -> new TypeUtil(javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()))) + ); + }; + } + + public static LSFactory createTestDefault(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) { + return (SimpleLanguageServer server) -> { + // Initialize project finders, project caches and project observers + FileObserver fileObserver = server.getWorkspaceService().getFileObserver(); + CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null); + mavenProjectCache.setAlwaysFireEventOnFileChanged(true); + javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); + + GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null); + gradleProjectCache.setAlwaysFireEventOnFileChanged(true); + javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); + + CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); + + return new BootPropertiesLanguageServerParams( + javaProjectFinder.filter(BootProjectUtil::isBootProject), + projectObserver, + indexProvider, + typeUtilProvider + ); + }; + } + + public static LSFactory createTestDefault() { + return (SimpleLanguageServer server) -> { + // Initialize project finders, project caches and project observers + FileObserver fileObserver = server.getWorkspaceService().getFileObserver(); + CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null); + mavenProjectCache.setAlwaysFireEventOnFileChanged(true); + javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); + + GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null); + gradleProjectCache.setAlwaysFireEventOnFileChanged(true); + javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); + + CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); + + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver); + indexProvider.setProgressService(server.getProgressService()); + + return new BootPropertiesLanguageServerParams( + javaProjectFinder.filter(BootProjectUtil::isBootProject), + projectObserver, + indexProvider, + (IDocument doc) -> new TypeUtil(javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()))) + ); + }; + } + +} 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 14d056a1e..e24f911f9 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 @@ -12,14 +12,7 @@ package org.springframework.ide.vscode.boot; import java.io.IOException; -import org.eclipse.lsp4j.TextDocumentIdentifier; -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.languageserver.LaunguageServerApp; -import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; -import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; -import org.springframework.ide.vscode.commons.util.text.IDocument; /** * Starts up Language Server process @@ -31,14 +24,8 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; public class Main { public static void main(String[] args) throws IOException, InterruptedException { - LaunguageServerApp.start("boot-properties", () -> { - CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); - DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); - TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()))); - SimpleLanguageServer server = new BootPropertiesLanguageServer(indexProvider, typeUtilProvider, javaProjectFinder); - indexProvider.setProgressService(server.getProgressService()); - return server; - }); + LaunguageServerApp.start("boot-properties", + () -> new BootPropertiesLanguageServer(BootPropertiesLanguageServerParams.createDefault())); } } 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 c7a4571a4..2f1dea806 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 @@ -17,6 +17,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; 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.ProjectObserver; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -25,12 +26,13 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null, null); private JavaProjectFinder javaProjectFinder; - private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); + private SpringPropertiesIndexManager indexManager; private ProgressService progressService = (id, msg) -> { /*ignore*/ }; - public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder, ProjectObserver projectObserver) { this.javaProjectFinder = javaProjectFinder; + this.indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault(), projectObserver); } @Override diff --git a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java index 7da6c2086..857ad7126 100644 --- a/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java +++ b/headless-services/boot-properties-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java @@ -10,16 +10,18 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.metadata; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.ExecutionException; import org.springframework.ide.vscode.boot.metadata.util.Listener; import org.springframework.ide.vscode.boot.metadata.util.ListenerManager; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; -import org.springframework.ide.vscode.commons.util.FuzzyMap; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.util.Log; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + /** * Support for Reconciling, Content Assist and Hover Text in spring properties * file all make use of a per-project index of spring properties metadata extracted @@ -30,41 +32,68 @@ import org.springframework.ide.vscode.commons.util.Log; */ public class SpringPropertiesIndexManager extends ListenerManager> { - private Map indexes = null; + private Cache indexes; private final ValueProviderRegistry valueProviders; private static int progressIdCt = 0; - public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders) { + public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders, ProjectObserver projectObserver) { this.valueProviders = valueProviders; + this.indexes = CacheBuilder.newBuilder().build(); + if (projectObserver != null) { + projectObserver.addListener(new ProjectObserver.Listener() { + + @Override + public void created(IJavaProject project) { + // ignore + } + + @Override + public void changed(IJavaProject project) { + indexes.invalidate(project); + } + + @Override + public void deleted(IJavaProject project) { + indexes.invalidate(project); + } + + }); + } + } - public synchronized FuzzyMap get(IJavaProject project, ProgressService progressService) { - if (indexes==null) { - indexes = new HashMap<>(); + public synchronized SpringPropertyIndex get(IJavaProject project, ProgressService progressService) { + try { + return indexes.get(project, () -> initIndex(project, progressService)); + } catch (ExecutionException e) { + Log.log(e); + return null; } - SpringPropertyIndex index = indexes.get(project); - if (index==null) { - Log.info("Indexing Spring Boot Properties for "+project.getElementName()); - String progressId = getProgressId(); - if (progressService != null) { - progressService.progressEvent(progressId, "Indexing Spring Boot Properties..."); - } - - index = new SpringPropertyIndex(valueProviders, project.getClasspath()); - indexes.put(project, index); - - if (progressService != null) { - progressService.progressEvent(progressId, null); - } - Log.info("Indexing Spring Boot Properties for "+project.getElementName()+" DONE"); - Log.info("Indexed "+index.size()+" properties."); + } + + private SpringPropertyIndex initIndex(IJavaProject project, ProgressService progressService) { + Log.info("Indexing Spring Boot Properties for "+project.getElementName()); + + String progressId = getProgressId(); + if (progressService != null) { + progressService.progressEvent(progressId, "Indexing Spring Boot Properties..."); } + + SpringPropertyIndex index = new SpringPropertyIndex(valueProviders, project.getClasspath()); + + if (progressService != null) { + progressService.progressEvent(progressId, null); + } + + Log.info("Indexing Spring Boot Properties for "+project.getElementName()+" DONE"); + Log.info("Indexed "+index.size()+" properties."); + return index; } public synchronized void clear() { if (indexes!=null) { - indexes.clear(); + indexes.invalidateAll(); for (Listener l : getListeners()) { l.changed(this); } diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java index f0df01135..9171b9e7d 100644 --- a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java @@ -36,7 +36,7 @@ public class PropertiesIndexTest { @Test public void springStandardPropertyPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject, progressService); PropertyInfo propertyInfo = index.get("server.port"); @@ -48,7 +48,7 @@ public class PropertiesIndexTest { @Test public void customPropertyPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject, progressService); PropertyInfo propertyInfo = index.get("demo.settings.user"); @@ -60,7 +60,7 @@ public class PropertiesIndexTest { @Test public void propertyNotPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject, progressService); PropertyInfo propertyInfo = index.get("my.server.port"); @@ -70,7 +70,7 @@ public class PropertiesIndexTest { @Test public void springStandardPropertyPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject, progressService); PropertyInfo propertyInfo = index.get("server.port"); @@ -82,7 +82,7 @@ public class PropertiesIndexTest { @Test public void customPropertyPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject, progressService); PropertyInfo propertyInfo = index.get("demo.settings.user"); @@ -94,7 +94,7 @@ public class PropertiesIndexTest { @Test public void propertyNotPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( - ValueProviderRegistry.getDefault()); + ValueProviderRegistry.getDefault(), null); IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject, progressService); PropertyInfo propertyInfo = index.get("my.server.port"); diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index 7b16749b0..3fc4f10fc 100644 --- a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -25,6 +25,7 @@ import org.eclipse.lsp4j.Diagnostic; import org.junit.Ignore; import org.junit.Test; import org.springframework.ide.vscode.boot.BootPropertiesLanguageServer; +import org.springframework.ide.vscode.boot.BootPropertiesLanguageServerParams; import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; @@ -1585,7 +1586,9 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { @Override protected SimpleLanguageServer newLanguageServer() { - BootPropertiesLanguageServer server = new BootPropertiesLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder); + BootPropertiesLanguageServer server = new BootPropertiesLanguageServer( + s -> new BootPropertiesLanguageServerParams(javaProjectFinder, null, md.getIndexProvider(), + typeUtilProvider)); server.setMaxCompletionsNumber(-1); return server; } diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index c8f431a57..c220c928e 100644 --- a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.Diagnostic; import org.junit.Ignore; import org.junit.Test; import org.springframework.ide.vscode.boot.BootPropertiesLanguageServer; +import org.springframework.ide.vscode.boot.BootPropertiesLanguageServerParams; import org.springframework.ide.vscode.boot.editor.harness.AbstractPropsEditorTest; import org.springframework.ide.vscode.boot.editor.harness.StyledStringMatcher; import org.springframework.ide.vscode.boot.metadata.CachingValueProvider; @@ -3670,7 +3671,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Override protected SimpleLanguageServer newLanguageServer() { - BootPropertiesLanguageServer server = new BootPropertiesLanguageServer(md.getIndexProvider(), typeUtilProvider, javaProjectFinder); + BootPropertiesLanguageServer server = new BootPropertiesLanguageServer( + s -> new BootPropertiesLanguageServerParams(javaProjectFinder, null, md.getIndexProvider(), + typeUtilProvider)); server.setMaxCompletionsNumber(-1); return server; } 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 d9a598010..624205caf 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 @@ -21,7 +21,7 @@ import org.eclipse.lsp4j.InitializeResult; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.junit.Test; import org.springframework.ide.vscode.boot.BootPropertiesLanguageServer; -import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; +import org.springframework.ide.vscode.boot.BootPropertiesLanguageServerParams; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; @@ -38,7 +38,7 @@ public class BootPropertiesLanguageServerTest { } private LanguageServerHarness newHarness() throws Exception { - Callable f = () -> new BootPropertiesLanguageServer((d) -> null, (d) -> null, new CompositeJavaProjectFinder()); + Callable f = () -> new BootPropertiesLanguageServer(BootPropertiesLanguageServerParams.createTestDefault()); return new LanguageServerHarness(f); } diff --git a/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SpringPropertiesIndexTest.java b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SpringPropertiesIndexTest.java new file mode 100644 index 000000000..28198f3ac --- /dev/null +++ b/headless-services/boot-properties-language-server/src/test/java/org/springframework/ide/vscode/boot/test/SpringPropertiesIndexTest.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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.boot.test; + +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import java.io.File; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.ide.vscode.boot.BootPropertiesLanguageServer; +import org.springframework.ide.vscode.boot.BootPropertiesLanguageServerParams; +import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.commons.util.text.TextDocument; +import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; + +/** + * Tests for Boot properties index + * + * @author Alex Boyko + * + */ +public class SpringPropertiesIndexTest { + + private LanguageServerHarness harness; + + private DefaultSpringPropertyIndexProvider propertyIndexProvider; + + @Before + public void setup() throws Exception { + harness = new LanguageServerHarness<>(() -> new BootPropertiesLanguageServer(BootPropertiesLanguageServerParams.createTestDefault())); + } + + @Test + public void testPropertiesIndexRefreshOnProjectChange() throws Exception { + harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/boot-1.2.0-properties-live-metadta/").toURI())); + propertyIndexProvider = (DefaultSpringPropertyIndexProvider) harness.getServer().getPropertiesIndexProvider(); + + File directory = new File(ProjectsHarness.class.getResource("/test-projects/boot-1.2.0-properties-live-metadta/").toURI()); + + File javaFile = new File(directory, "/src/main/java/demo/Application.java"); + + TextDocument doc = new TextDocument(javaFile.toURI().toString(), LanguageId.JAVA); + + // Not cached yet, hence progress service invoked + ProgressService progressService = mock(ProgressService.class); + propertyIndexProvider.setProgressService(progressService); + propertyIndexProvider.getIndex(doc); + verify(progressService, atLeastOnce()).progressEvent(anyObject(), anyObject()); + + // Should be cached now, so progress service should not be touched + progressService = mock(ProgressService.class); + propertyIndexProvider.setProgressService(progressService); + propertyIndexProvider.getIndex(doc); + verify(progressService, never()).progressEvent(anyObject(), anyObject()); + + // Change POM file for the project + harness.changeFile(new File(directory, MavenCore.POM_XML).toURI().toString()); + + // POM has changed, hence project needs to be reloaded, cached value is cleared + progressService = mock(ProgressService.class); + propertyIndexProvider.setProgressService(progressService); + propertyIndexProvider.getIndex(doc); + verify(progressService, atLeastOnce()).progressEvent(anyObject(), anyObject()); + } + +} diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/BootProjectUtil.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java similarity index 85% rename from headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/BootProjectUtil.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java index dd3272316..f9545e954 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/BootProjectUtil.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java @@ -8,12 +8,10 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.utils; +package org.springframework.ide.vscode.commons.java; import java.nio.file.Path; -import org.springframework.ide.vscode.commons.java.IClasspath; -import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Log; public class BootProjectUtil {