diff --git a/atom-extensions/atom-commons/.gitignore b/atom-extensions/atom-commons/.gitignore index 246d599d5..6274b8aca 100644 --- a/atom-extensions/atom-commons/.gitignore +++ b/atom-extensions/atom-commons/.gitignore @@ -1,2 +1,3 @@ /dist -/node_modules \ No newline at end of file +/node_modules +/build \ No newline at end of file diff --git a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/META-INF/MANIFEST.MF index e82533c6b..fff073fd9 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/META-INF/MANIFEST.MF @@ -15,7 +15,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.ui.workbench, org.eclipse.jface, org.eclipse.xtext.xbase.lib, - org.springframework.tooling.ls.eclipse.commons + org.springframework.tooling.ls.eclipse.commons, + org.eclipse.core.resources Import-Package: com.google.gson;version="2.7.0", org.eclipse.jface.preference, org.eclipse.lsp4j.jsonrpc.messages;version="0.1.0.v20170117-0759", diff --git a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/DelegatingStreamConnectionProvider.java index 6ce301fc5..66c813c1d 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/DelegatingStreamConnectionProvider.java @@ -13,6 +13,7 @@ package org.springframework.tooling.boot.java.ls; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URI; import org.eclipse.lsp4e.server.StreamConnectionProvider; @@ -41,7 +42,12 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi this.provider = new SpringBootJavaLanguageServer(); } } - + + @Override + public Object getInitializationOptions(URI rootUri) { + return provider.getInitializationOptions(rootUri); + } + @Override public void start() throws IOException { this.provider.start(); diff --git a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/ResourceListener.java b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/ResourceListener.java new file mode 100644 index 000000000..f6d621c92 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/ResourceListener.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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.tooling.boot.java.ls; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.PathMatcher; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.lsp4e.LanguageServersRegistry; +import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; +import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4e.ProjectSpecificLanguageServerWrapper; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.FileChangeType; +import org.eclipse.lsp4j.FileEvent; + +/** + * Resource listener for LSP4E taken from CPP-LS + * https://github.com/eclipse/cdt/blob/master/lsp4e-cpp/org.eclipse.lsp4e.cpp.language/src/org/eclipse/lsp4e/cpp/language/CPPResourceChangeListener.java + * + * @author Alex Boyko + * + */ +@SuppressWarnings("restriction") +public class ResourceListener implements IResourceChangeListener { + + private final String lsId; + private final IProject fProject; + private List pathMatchers; + + ResourceListener(String lsId, IProject project, List pathMatchers) { + this.lsId = lsId; + fProject = project; + this.pathMatchers = pathMatchers; + } + + @Override + public void resourceChanged(IResourceChangeEvent event) { + LanguageServerDefinition definition = LanguageServersRegistry.getInstance().getDefinition(lsId); + ProjectSpecificLanguageServerWrapper wrapper = getLanguageSeverWrapper(definition); + if (event.getType() != IResourceChangeEvent.POST_CHANGE || !isRelevantDelta(event.getDelta()) + || wrapper == null) { + return; + } + + sendFileEvents(wrapper, createFileEventsFromResourceEvent(event)); + } + + private static void sendFileEvents(ProjectSpecificLanguageServerWrapper wrapper, List fileEvents) { + if (!fileEvents.isEmpty()) { + DidChangeWatchedFilesParams params = new DidChangeWatchedFilesParams(fileEvents); + wrapper.getServer().getWorkspaceService().didChangeWatchedFiles(params); + } + } + + private List createFileEventsFromResourceEvent(IResourceChangeEvent event) { + List fileEvents = new ArrayList<>(); + try { + event.getDelta().accept((delta) -> { + if (delta.getResource() instanceof IFile && isRelevantDelta(delta) && isApplicableFile((IFile) delta.getResource())) { + FileEvent fileEvent = createFileEventFromDelta(delta); + if (fileEvent != null) { + fileEvents.add(fileEvent); + } + } + return true; + }, false); + } catch (CoreException e) { + // Do nothing + } + return fileEvents; + } + + private boolean isApplicableFile(IFile resource) { + return pathMatchers.stream().filter(m -> m.matches(resource.getLocation().toFile().toPath())).findFirst().isPresent(); + } + + private ProjectSpecificLanguageServerWrapper getLanguageSeverWrapper(LanguageServerDefinition definition) { + try { + return LanguageServiceAccessor.getLSWrapperForConnection(fProject, definition); + } catch (IOException e) { + // Do nothing + return null; + } + } + + private static boolean isRelevantDelta(IResourceDelta delta) { + int kind = delta.getKind(); + int flags = delta.getFlags(); + if (delta.getResource() instanceof IFile && kind == IResourceDelta.CHANGED) { + return (flags & IResourceDelta.CONTENT) != 0; + } + + return kind == IResourceDelta.ADDED || kind == IResourceDelta.CHANGED || kind == IResourceDelta.REMOVED; + } + + private static FileEvent createFileEventFromDelta(IResourceDelta delta) { + URI locationURI = delta.getResource().getLocationURI(); + if (locationURI == null) { + return null; + } + + FileChangeType changeType = null; + if (delta.getKind() == IResourceDelta.ADDED) { + changeType = FileChangeType.Created; + } else if (delta.getKind() == IResourceDelta.CHANGED) { + changeType = FileChangeType.Changed; + } else if (delta.getKind() == IResourceDelta.REMOVED) { + changeType = FileChangeType.Deleted; + } else { + throw new IllegalStateException("Unsupported resource delta kind: " + delta.getKind()); //$NON-NLS-1$ + } + + return new FileEvent(locationURI.toString(), changeType); + } +} diff --git a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java index 701da48bc..135e13514 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.java.ls/src/org/springframework/tooling/boot/java/ls/SpringBootJavaLanguageServer.java @@ -13,30 +13,30 @@ package org.springframework.tooling.boot.java.ls; import java.io.File; import java.io.InputStream; import java.net.URI; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.internal.launching.StandardVMType; -import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; -import org.eclipse.lsp4j.jsonrpc.messages.Message; -import org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage; -import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.ui.PlatformUI; import org.osgi.framework.Bundle; -import com.google.gson.JsonObject; - /** * @author Martin Lippert */ @SuppressWarnings("restriction") public class SpringBootJavaLanguageServer extends ProcessStreamConnectionProvider { + + private ResourceListener fResourceListener; public SpringBootJavaLanguageServer() { List commands = new ArrayList<>(); @@ -107,4 +107,44 @@ public class SpringBootJavaLanguageServer extends ProcessStreamConnectionProvide Files.copy(stream, dataFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } + @Override + public void stop() { + super.stop(); + if (fResourceListener != null) { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(fResourceListener); + fResourceListener = null; + } + } + + @Override + public Object getInitializationOptions(URI rootPath) { + installResourceChangeListener(rootPath); + return super.getInitializationOptions(rootPath); + } + + private void installResourceChangeListener(URI rootPath) { + if (rootPath == null || fResourceListener != null) { + return; + } + + IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(rootPath); + if (containers.length == 0) { + return; + } + + for (IContainer c : containers) { + if (!(c instanceof IProject)) { + continue; + } + IProject project = (IProject) c; + fResourceListener = new ResourceListener("org.eclipse.languageserver.languages.springbootjava", project, Arrays.asList( + FileSystems.getDefault().getPathMatcher("glob:**/pom.xml"), + FileSystems.getDefault().getPathMatcher("glob:**/build.gradle") + )); + project.getWorkspace().addResourceChangeListener(fResourceListener); + + break; + } + } + } 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 2deaf805a..8920c659f 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 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -44,15 +45,24 @@ 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.GradleProjectCache; +import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder; 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.JavaProjectManager; +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.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.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.text.TextDocument; import com.google.common.collect.ImmutableList; @@ -69,10 +79,11 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { private final VscodeCompletionEngineAdapter completionEngine; private final SpringIndexer indexer; private final SpringLiveHoverWatchdog liveHoverWatchdog; + private final CompositeProjectOvserver projectObserver; private final WordHighlighter testHightlighter = null; //new WordHighlighter("foo"); - public BootJavaLanguageServer(JavaProjectManager javaProjectManager, SpringPropertyIndexProvider indexProvider) { + public BootJavaLanguageServer(CompositeJavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { super("vscode-boot-java"); System.setProperty(LANGUAGE_SERVER_PROCESS_PROPERTY, LANGUAGE_SERVER_PROCESS_PROPERTY); @@ -86,13 +97,13 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { validateWith(doc.getId(), reconcileEngine); }); - ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectManager, indexProvider); + ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectFinder, indexProvider); completionEngine = createCompletionEngineAdapter(this, bootCompletionEngine); completionEngine.setMaxCompletions(100); documents.onCompletion(completionEngine::getCompletions); documents.onCompletionResolve(completionEngine::resolveCompletion); - BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectManager); + BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectFinder); documents.onHover(hoverInfoProvider); liveHoverWatchdog = new SpringLiveHoverWatchdog(this, hoverInfoProvider); @@ -106,10 +117,10 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { } }); - ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectManager); + ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder); documents.onReferences(referencesHandler); - indexer = createAnnotationIndexer(this, javaProjectManager); + indexer = createAnnotationIndexer(this, javaProjectFinder); documents.onDidSave(params -> { String docURI = params.getDocument().getId().getUri(); String content = params.getDocument().get(); @@ -119,10 +130,18 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(indexer)); workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer)); - BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectManager); + BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder); documents.onCodeLens(codeLensHandler::createCodeLenses); documents.onCodeLensResolve(codeLensHandler::resolveCodeLens); + // Initialize project finders, project caches and project observers + MavenProjectCache mavenProjectCache = new MavenProjectCache(getWorkspaceService().getFileObserver(), MavenCore.getDefault()); + javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); + + GradleProjectCache gradleProjectCache = new GradleProjectCache(getWorkspaceService().getFileObserver(), GradleCore.getDefault()); + javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); + + projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); } public void setMaxCompletionsNumber(int number) { @@ -156,7 +175,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { return super.shutdown(); } - protected ICompletionEngine createCompletionEngine(JavaProjectManager javaProjectManager, SpringPropertyIndexProvider indexProvider) { + protected ICompletionEngine createCompletionEngine(JavaProjectFinder javaProjectFinder, 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)); @@ -220,10 +239,10 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { " return ${entity};\n" + "}" )); - return new BootJavaCompletionEngine(javaProjectManager, providers, snippetManager); + return new BootJavaCompletionEngine(javaProjectFinder, providers, snippetManager); } - protected BootJavaHoverProvider createHoverHandler(JavaProjectManager javaProjectFinder) { + protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder) { HashMap providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider()); @@ -241,7 +260,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { return new BootJavaHoverProvider(this, javaProjectFinder, providers); } - protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectManager projectManager) { + protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder) { 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()); @@ -253,19 +272,22 @@ 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, projectManager, providers); + return new SpringIndexer(this, projectFinder, providers); } - protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectManager projectManager) { + protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) { Map providers = new HashMap<>(); providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValuePropertyReferencesProvider(server)); - return new BootJavaReferencesHandler(server, projectManager, providers); + return new BootJavaReferencesHandler(server, projectFinder, providers); } - protected BootJavaCodeLensEngine createCodeLensEngine(SimpleLanguageServer server, JavaProjectManager projectFinder) { + protected BootJavaCodeLensEngine createCodeLensEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder) { return new BootJavaCodeLensEngine(server, projectFinder); } + public ProjectObserver getProjectObserver() { + return projectObserver; + } } 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 1272bc37f..a78afc5d3 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,15 +13,9 @@ 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.CompositeJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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 @@ -32,14 +26,9 @@ public class Main { public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { - 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()); + CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); + SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexProvider); 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 e9920ea32..41bace3bb 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager projectFinder; + private final JavaProjectFinder projectFinder; - public BootJavaCodeLensEngine(SimpleLanguageServer server, JavaProjectManager projectFinder) { + public BootJavaCodeLensEngine(SimpleLanguageServer server, JavaProjectFinder 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 2af8e5f9e..f47bd2042 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager projectManager; + private JavaProjectFinder projectFinder; private Map completionProviders; private JavaSnippetManager snippets; - public BootJavaCompletionEngine(JavaProjectManager projectManager, Map specificProviders, JavaSnippetManager snippets) { - this.projectManager = projectManager; + public BootJavaCompletionEngine(JavaProjectFinder projectFinder, Map specificProviders, JavaSnippetManager snippets) { + this.projectFinder = projectFinder; 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.projectManager.find(doc); + IJavaProject project = this.projectFinder.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 d45231119..d719a838f 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager projectFinder; + private JavaProjectFinder projectFinder; private SimpleLanguageServer server; private Map hoverProviders; - public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectManager projectFinder, Map specificProviders) { + public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectFinder 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 562536edb..05558835b 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager projectManager; + private JavaProjectFinder projectFinder; private SimpleLanguageServer server; private Map referenceProviders; - public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectManager projectManager, Map specificProviders) { + public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; - this.projectManager = projectManager; + this.projectFinder = projectFinder; this.referenceProviders = specificProviders; } @@ -124,7 +124,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler { } private String[] getClasspathEntries(IDocument doc) throws Exception { - IJavaProject project = this.projectManager.find(doc); + IJavaProject project = this.projectFinder.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 360f231d5..03ba54e5b 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 @@ -44,12 +44,13 @@ import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; 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.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.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; +import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -58,8 +59,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; */ public class SpringIndexer { - private SimpleLanguageServer server; - private JavaProjectManager projectManager; + private BootJavaLanguageServer server; + private JavaProjectFinder projectFinder; private Map symbolProviders; private List symbols; @@ -91,9 +92,9 @@ public class SpringIndexer { }; - public SpringIndexer(SimpleLanguageServer server, JavaProjectManager projectManager, Map specificProviders) { + public SpringIndexer(BootJavaLanguageServer server, JavaProjectFinder projectFinder, Map specificProviders) { this.server = server; - this.projectManager = projectManager; + this.projectFinder = projectFinder; this.symbolProviders = specificProviders; this.symbols = Collections.synchronizedList(new ArrayList<>()); @@ -105,8 +106,8 @@ public class SpringIndexer { synchronized(this) { if (this.initializeTask == null) { initializing.set(true); - if (projectManager != null) { - projectManager.addListener(projectListener); + if (server.getProjectObserver() != null) { + server.getProjectObserver().addListener(projectListener); } this.initializeTask = CompletableFuture.runAsync(new Runnable() { @Override @@ -126,6 +127,9 @@ public class SpringIndexer { updateItem.getFuture().complete(null); } } + catch (InterruptedException e) { + // ignore + } catch (Exception e) { e.printStackTrace(); } @@ -152,6 +156,7 @@ public class SpringIndexer { initializeTask = null; symbols.clear(); symbolsByDoc.clear(); + Log.info("Rebuilding SpringIndexer..."); initialize(server.getWorkspaceRoot()); } } @@ -166,8 +171,8 @@ public class SpringIndexer { updateWorker.interrupt(); } - if (projectManager != null) { - projectManager.removeListener(projectListener); + if (server.getProjectObserver() != null) { + server.getProjectObserver().removeListener(projectListener); } } catch (Exception e) { @@ -180,7 +185,7 @@ public class SpringIndexer { try { initializeTask.get(); - IJavaProject project = projectManager.find(new File(new URI(docURI))); + IJavaProject project = projectFinder.find(new File(new URI(docURI))); if (project != null) { String[] classpathEntries = getClasspathEntries(project); @@ -263,7 +268,7 @@ public class SpringIndexer { .filter(path -> path.getFileName().toString().endsWith(".java")) .filter(Files::isRegularFile) .map(path -> path.toAbsolutePath().toString()) - .collect(Collectors.groupingBy((javaFile) -> projectManager.find(new File(javaFile)))); + .collect(Collectors.groupingBy((javaFile) -> projectFinder.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 f91c19725..2871fb420 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager javaProjectManager; + private JavaProjectFinder javaProjectFinder; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(); private ProgressService progressService = (id, msg) -> { /* ignore */ }; - public DefaultSpringPropertyIndexProvider(JavaProjectManager javaProjectManager) { - this.javaProjectManager = javaProjectManager; + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { + this.javaProjectFinder = javaProjectFinder; } @Override public FuzzyMap getIndex(IDocument doc) { - IJavaProject jp = javaProjectManager.find(doc); + IJavaProject jp = javaProjectFinder.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/autowired/test/AutowiredHoverProviderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index 096124017..f7f1f2498 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -45,12 +45,8 @@ import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider import org.springframework.ide.vscode.boot.java.autowired.SpringBootAppProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -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.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; import org.springframework.ide.vscode.commons.util.BadLocationException; -import org.springframework.ide.vscode.commons.util.BasicFileObserver; 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; @@ -62,22 +58,19 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; */ public class AutowiredHoverProviderTest { - private JavaProjectManager projectManager; + private CompositeJavaProjectFinder projectFinder; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; - private BasicFileObserver fileObserver; @Before public void setup() throws Exception { - projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); - fileObserver = new BasicFileObserver(); - projectManager.setFileObserver(fileObserver); + projectFinder = new CompositeJavaProjectFinder(); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()); return server; } }) { @@ -94,7 +87,7 @@ public class AutowiredHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); @@ -120,7 +113,7 @@ public class AutowiredHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); @@ -139,7 +132,7 @@ public class AutowiredHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); @@ -160,7 +153,7 @@ public class AutowiredHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/ComponentHoverProviderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/ComponentHoverProviderTest.java index a574c7ca6..1378c0316 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/ComponentHoverProviderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/ComponentHoverProviderTest.java @@ -45,12 +45,8 @@ import org.springframework.ide.vscode.boot.java.autowired.SpringBootAppProvider; import org.springframework.ide.vscode.boot.java.beans.ComponentHoverProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -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.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; import org.springframework.ide.vscode.commons.util.BadLocationException; -import org.springframework.ide.vscode.commons.util.BasicFileObserver; 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; @@ -62,22 +58,19 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; */ public class ComponentHoverProviderTest { - private JavaProjectManager projectManager; + private CompositeJavaProjectFinder projectFinder; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; - private BasicFileObserver fileObserver; @Before public void setup() throws Exception { - projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); - fileObserver = new BasicFileObserver(); - projectManager.setFileObserver(fileObserver); + projectFinder = new CompositeJavaProjectFinder(); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()); return server; } }) { @@ -94,7 +87,7 @@ public class ComponentHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutomaticallyWiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); @@ -120,7 +113,7 @@ public class ComponentHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); @@ -141,7 +134,7 @@ public class ComponentHoverProviderTest { harness.intialize(directory); String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutomaticallyWiredComponent.java"; - IJavaProject project = projectManager.find(directory); + IJavaProject project = projectFinder.find(directory); TextDocument document = createTempTextDocument(docURI); CompilationUnit cu = parse(document, project); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java index 52e5ffc12..99fb2b5f6 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java @@ -29,10 +29,7 @@ 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.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.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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 +40,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class SpringIndexerBeansTest { private Map symbolProviders; - private JavaProjectManager projectManager; + private CompositeJavaProjectFinder projectManager; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; @@ -53,7 +50,7 @@ public class SpringIndexerBeansTest { symbolProviders.put(Constants.SPRING_BEAN, new BeansSymbolProvider()); symbolProviders.put(Constants.SPRING_COMPONENT, new ComponentSymbolProvider()); - projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); + projectManager = new CompositeJavaProjectFinder(); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { 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 fd037452e..8842b1058 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,10 +28,7 @@ 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.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.java.MavenProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; @@ -42,7 +39,7 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class RequestMappingSymbolProviderTest { private Map symbolProviders; - private JavaProjectManager projectManager; + private CompositeJavaProjectFinder projectManager; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; @@ -51,7 +48,7 @@ public class RequestMappingSymbolProviderTest { symbolProviders = new HashMap<>(); symbolProviders.put(Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider()); - projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); + projectManager = new CompositeJavaProjectFinder(); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java index e241d36db..dc1a6ace0 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java @@ -15,6 +15,7 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.io.InputStream; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; @@ -24,8 +25,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.AbstractJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 ScopeCompletionTest { - protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { + protected final CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(Arrays.asList(new JavaProjectFinder() { @Override public boolean isProjectRoot(File file) { return false; @@ -51,7 +52,7 @@ public class ScopeCompletionTest { public IJavaProject find(IDocument doc) { return getTestProject(); } - }; + })); private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; 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 5a40b62bd..9f6b49203 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 @@ -33,11 +33,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.CompositeJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +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.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; @@ -48,25 +47,28 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; public class SpringIndexerTest { private Map symbolProviders; - private JavaProjectManager projectManager; + private CompositeJavaProjectFinder projectFinder; private LanguageServerHarness harness; private PropertyIndexHarness indexHarness; - private BasicFileObserver fileObserver; + private SpringIndexer indexer; @Before public void setup() throws Exception { symbolProviders = new HashMap<>(); symbolProviders.put(Constants.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider()); - projectManager = new CompositeJavaProjectManager(new JavaProjectManager[] {new MavenProjectManager(MavenCore.getDefault())}); - fileObserver = new BasicFileObserver(); - projectManager.setFileObserver(fileObserver); + projectFinder = new CompositeJavaProjectFinder(); indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(projectManager, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer(projectFinder, indexHarness.getIndexProvider()) { + @Override + protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectFinder projectManager) { + return indexer = new SpringIndexer((BootJavaLanguageServer) server, projectFinder, symbolProviders); + } + }; return server; } }) { @@ -81,9 +83,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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); List allSymbols = indexer.getAllSymbols(""); @@ -105,9 +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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); String uriPrefix = "file://" + directory.getAbsolutePath(); List symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/MainClass.java"); @@ -132,9 +130,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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/").toURI()); - indexer.initialize(directory.toPath()); List allSymbols = indexer.getAllSymbols(""); @@ -156,10 +152,7 @@ public class SpringIndexerTest { public void testUpdateChangedDocument() throws Exception { 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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); // update document and update index String changedDocURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/SimpleMappingClass.java"; @@ -194,9 +187,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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); List allSymbols = indexer.getAllSymbols("mapp"); @@ -216,9 +207,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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); List allSymbols = indexer.getAllSymbols("@/foo-root-mapping -- (no method defined)"); @@ -234,9 +223,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(), projectManager, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()); - indexer.initialize(directory.toPath()); List allSymbols = indexer.getAllSymbols("@/foo-root-mapping/embedded-foo-mapping-with-root -- (no method defined)"); @@ -268,9 +255,7 @@ public class SpringIndexerTest { 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()); @@ -278,7 +263,7 @@ public class SpringIndexerTest { File pomFile = directory.toPath().resolve(MavenCore.POM_XML).toFile(); assertFalse(indexer.isInitializing()); - fileObserver.notifyFileChanged(pomFile.toURI().toString()); + harness.changeFile(pomFile.toURI().toString()); // Refresh in progress assertTrue(indexer.isInitializing()); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java index f61acbf66..317e5941e 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueCompletionTest.java @@ -15,6 +15,8 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; @@ -25,8 +27,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.AbstractJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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; @@ -39,21 +41,6 @@ import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; */ public class ValueCompletionTest { - protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { - @Override - public boolean isProjectRoot(File file) { - return false; - } - @Override - public IJavaProject find(File file) { - return null; - } - @Override - public IJavaProject find(IDocument doc) { - return getTestProject(); - } - }; - private LanguageServerHarness harness; private IJavaProject testProject; @@ -69,7 +56,24 @@ public class ValueCompletionTest { harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexHarness.getIndexProvider()); + BootJavaLanguageServer server = new BootJavaLanguageServer( + new CompositeJavaProjectFinder(new ArrayList<>(Collections.singleton(new AbstractJavaProjectFinder() { + @Override + public boolean isProjectRoot(File file) { + return false; + } + + @Override + public IJavaProject find(File file) { + return null; + } + + @Override + public IJavaProject find(IDocument doc) { + return getTestProject(); + } + }))), + indexHarness.getIndexProvider()); return server; } }) { 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 3ea96c9dd..422915941 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,6 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot; +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; @@ -20,15 +22,23 @@ 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.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.JavaProjectManager; +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.ProjectObserver; 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.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; @@ -60,7 +70,8 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { public boolean includeDeindentedProposals() { return false; }; }; // Shared: - private final JavaProjectManager javaProjectManager; + private final CompositeProjectOvserver projectObserver; + private final CompositeJavaProjectFinder javaProjectFinder; private final SpringPropertyIndexProvider indexProvider; private final TypeUtilProvider typeUtilProvider; private final VscodeCompletionEngineAdapter completionEngine; @@ -75,12 +86,12 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { private final YamlStructureProvider yamlStructureProvider= YamlStructureProvider.DEFAULT; private YamlAssistContextProvider yamlAssistContextProvider; - public BootPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectManager javaProjectManager) { + public BootPropertiesLanguageServer(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, CompositeJavaProjectFinder javaProjectFinder) { super("vscode-boot-properties"); this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; - this.javaProjectManager = javaProjectManager; - this.completionFactory = new PropertyCompletionFactory(javaProjectManager); + this.javaProjectFinder = javaProjectFinder; + this.completionFactory = new PropertyCompletionFactory(javaProjectFinder); this.yamlAssistContextProvider = new YamlAssistContextProvider() { @Override public YamlAssistContext getGlobalAssistContext(YamlDocument ydoc) { @@ -107,10 +118,20 @@ 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()); + javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache)); + + GradleProjectCache gradleProjectCache = new GradleProjectCache(getWorkspaceService().getFileObserver(), GradleCore.getDefault()); + javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache)); + + projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache)); + } private ICompletionEngine getCompletionEngine() { - ICompletionEngine propertiesCompletions = new SpringPropertiesCompletionEngine(indexProvider, typeUtilProvider, javaProjectManager); + ICompletionEngine propertiesCompletions = new SpringPropertiesCompletionEngine(indexProvider, typeUtilProvider, javaProjectFinder); ICompletionEngine yamlCompletions = new YamlCompletionEngine(yamlStructureProvider, yamlAssistContextProvider, COMPLETION_OPTIONS); return (IDocument document, int offset) -> { String uri = document.getUri(); @@ -126,7 +147,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { } protected HoverInfoProvider getHoverProvider() { - HoverInfoProvider propertiesHovers = new PropertiesHoverInfoProvider(indexProvider, typeUtilProvider, javaProjectManager); + HoverInfoProvider propertiesHovers = new PropertiesHoverInfoProvider(indexProvider, typeUtilProvider, javaProjectFinder); HoverInfoProvider ymlHovers = new YamlHoverInfoProvider(parser, yamlStructureProvider, yamlAssistContextProvider); return (IDocument document, int offset) -> { @@ -171,5 +192,7 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer { }; } - + public ProjectObserver getProjectObserver() { + return projectObserver; + } } 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 77432d99b..9a42bf802 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 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 @@ -15,15 +15,9 @@ 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.CompositeJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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; /** @@ -37,15 +31,10 @@ public class Main { public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { - 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()); + CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(); + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); + TypeUtilProvider typeUtilProvider = (IDocument doc) -> new TypeUtil(javaProjectFinder.find(doc)); + SimpleLanguageServer server = new BootPropertiesLanguageServer(indexProvider, typeUtilProvider, javaProjectFinder); 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 8465342f6..4557ee4c9 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014-2016 Pivotal, Inc. + * Copyright (c) 2014-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 @@ -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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager documentContextFinder; + private JavaProjectFinder documentContextFinder; - public PropertyCompletionFactory(JavaProjectManager documentContextFinder) { + public PropertyCompletionFactory(JavaProjectFinder 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 ba3ee6f2d..685fc56f9 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager javaProjectManager; + private JavaProjectFinder javaProjectFinder; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); private ProgressService progressService = (id, msg) -> { /*ignore*/ }; - public DefaultSpringPropertyIndexProvider(JavaProjectManager javaProjectManager) { - this.javaProjectManager = javaProjectManager; + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { + this.javaProjectFinder = javaProjectFinder; } @Override public FuzzyMap getIndex(IDocument doc) { - IJavaProject jp = javaProjectManager.find(doc); + IJavaProject jp = javaProjectFinder.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 838a9a098..54264edbd 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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, JavaProjectManager projectManager) { + public SpringPropertiesCompletionEngine(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder projectFinder) { this.indexProvider = indexProvider; this.typeUtilProvider = typeUtilProvider; - this.completionFactory = new PropertyCompletionFactory(projectManager); + this.completionFactory = new PropertyCompletionFactory(projectFinder); } /** 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 0ddf1b81a..a12562845 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.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; 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 JavaProjectManager projectFinder; + private JavaProjectFinder projectFinder; - public PropertiesHoverInfoProvider(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectManager projectFinder) { + public PropertiesHoverInfoProvider(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider, JavaProjectFinder 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 8c4bec7b0..743784975 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,8 +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.AbstractJavaProjectManager; -import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectManager; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder; 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; @@ -46,7 +46,7 @@ public abstract class AbstractPropsEditorTest { private ProjectsHarness projects = ProjectsHarness.INSTANCE; protected PropertyIndexHarness md; - protected final JavaProjectManager javaProjectFinder = new AbstractJavaProjectManager() { + protected final CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder(Arrays.asList(new AbstractJavaProjectFinder() { @Override public boolean isProjectRoot(File file) { return false; @@ -59,7 +59,7 @@ public abstract class AbstractPropsEditorTest { public IJavaProject find(IDocument doc) { return getTestProject(); } - }; + })); private LanguageServerHarness harness; private IJavaProject testProject; 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 2683123de..d9a598010 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,11 +21,8 @@ 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.java.IJavaProject; -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.java.CompositeJavaProjectFinder; 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; /** @@ -41,23 +38,7 @@ public class BootPropertiesLanguageServerTest { } private LanguageServerHarness newHarness() throws Exception { - JavaProjectManager nullJavaProjectFinder = new AbstractJavaProjectManager() { - @Override - public boolean isProjectRoot(File file) { - return false; - } - @Override - public IJavaProject find(File file) { - return null; - } - @Override - public IJavaProject find(IDocument doc) { - return null; - } - }; - - - Callable f = () -> new BootPropertiesLanguageServer((d) -> null, (d) -> null, nullJavaProjectFinder); + Callable f = () -> new BootPropertiesLanguageServer((d) -> null, (d) -> null, new CompositeJavaProjectFinder()); return new LanguageServerHarness(f); } 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 7ee33a2cf..03596a937 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 @@ -23,18 +23,26 @@ import org.springframework.ide.vscode.commons.util.Log; */ public class GradleJavaProject implements IJavaProject { + private GradleCore gradle; private GradleProjectClasspath classpath; + private File projectDir; public GradleJavaProject(GradleCore gradle, File projectDir) throws GradleException { + this.gradle = gradle; + this.projectDir = projectDir; this.classpath = new GradleProjectClasspath(gradle, projectDir); } + + public File getLocation() { + return projectDir; + } @Override public GradleProjectClasspath getClasspath() { return classpath; } - void update(GradleCore gradle, File projectDir) { + void update() { try { this.classpath = new GradleProjectClasspath(gradle, projectDir); } catch (GradleException e) { diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java new file mode 100644 index 000000000..da2185273 --- /dev/null +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache; +import org.springframework.ide.vscode.commons.util.FileObserver; + +/** + * Tests whether document belongs to a Gradle project + * + * @author Alex Boyko + * + */ +public class GradleProjectCache extends AbstractFileToProjectCache { + + private GradleCore gradle; + + public GradleProjectCache(FileObserver fileObserver, GradleCore gradle) { + super(fileObserver); + this.gradle = gradle; + } + + @Override + protected void update(GradleJavaProject project) { + project.update(); + } + + @Override + protected GradleJavaProject createProject(File gradleBuild) throws Exception { + return new GradleJavaProject(gradle, gradleBuild.getParentFile()); + } + +} diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinder.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinder.java new file mode 100644 index 000000000..a3be58b87 --- /dev/null +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinder.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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 org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectFinder; +import org.springframework.ide.vscode.commons.util.FileUtils; + +/** + * Finder for Gradle Projects + * + * @author Alex Boyko + * + */ +public class GradleProjectFinder extends AbstractJavaProjectFinder { + + private GradleProjectCache cache; + + public GradleProjectFinder(GradleProjectCache cache) { + super(); + this.cache = cache; + } + + @Override + public IJavaProject find(File file) { + File gradlebuild = FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE); + return cache.project(gradlebuild); + } + + @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 deleted file mode 100644 index 747afb668..000000000 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectManager.java +++ /dev/null @@ -1,115 +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.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 b7777120b..312cd0588 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 @@ -24,7 +24,7 @@ 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.languageserver.java.ProjectObserver.Listener; import org.springframework.ide.vscode.commons.util.BasicFileObserver; /** @@ -64,9 +64,8 @@ public class GradleProjectTest { 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); + GradleProjectCache manager = new GradleProjectCache(fileObserver, GradleCore.getDefault()); IJavaProject[] projectChanged = new IJavaProject[] { null }; IJavaProject[] projectDeleted = new IJavaProject[] { null }; manager.addListener(new Listener() { @@ -84,7 +83,7 @@ public class GradleProjectTest { }); // Get the project from cache - GradleJavaProject cachedProject = manager.find(gradleFile); + GradleJavaProject cachedProject = manager.project(gradleFile); assertNotNull(cachedProject); fileObserver.notifyFileChanged(gradleFile.toURI().toString()); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java new file mode 100644 index 000000000..2512338ff --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.util.Arrays; +import java.util.List; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FileObserver; + +/** + * Cache fo java projects. The key for the cache is a "project" specific file + * + * @author Alex Boyko + * + * @param

java project sub-class + */ +public abstract class AbstractFileToProjectCache

extends AbstractJavaProjectCache { + + private String changeSubscription; + private String deleteSubscription; + + public AbstractFileToProjectCache(FileObserver fileObserver) { + super(fileObserver); + } + + @Override + protected void attachListeners(File file, P project) { + super.attachListeners(file, project); + List globPattern = Arrays.asList(file.toString()); + changeSubscription = getFileObserver().onFileChanged(globPattern, (uri) -> { + update(project); + notifyProjectChanged(project); + }); + deleteSubscription = getFileObserver().onFileDeleted(globPattern, (uri) -> { + cache.invalidate(file); + notifyProjectDeleted(project); + getFileObserver().unsubscribe(changeSubscription); + getFileObserver().unsubscribe(deleteSubscription); + }); + } + + abstract protected void update(P project); + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java new file mode 100644 index 000000000..b237d9b8c --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectCache.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FileObserver; +import org.springframework.ide.vscode.commons.util.ListenerList; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Abstract implementation of java project cache indexed by keys + * + * @author Alex Boyko + * + * @param key class + * @param

project class + */ +public abstract class AbstractJavaProjectCache implements JavaProjectCache { + + private FileObserver fileObserver; + + private ListenerList listeners = new ListenerList<>(); + + protected Cache cache = CacheBuilder.newBuilder().build(); + + public AbstractJavaProjectCache(FileObserver fileObserver) { + this.fileObserver = fileObserver; + } + + @Override + public P project(K key) { + if (key != null) { + try { + return cache.get(key, () -> { + try { + P project = createProject(key); + attachListeners(key, project); + return project; + } catch (Throwable t) { + throw new ExecutionException(t); + } + }); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + } + return null; + } + + abstract protected P createProject(K key) throws Exception; + + protected void attachListeners(K key, P project) { + + } + + @Override + public void addListener(Listener listener) { + listeners.add(listener); + } + + @Override + public void removeListener(Listener listener) { + listeners.remove(listener); + } + + final protected void notifyProjectCreated(P project) { + listeners.forEach(l -> l.created(project)); + } + + final protected void notifyProjectChanged(P project) { + listeners.forEach(l -> l.changed(project)); + } + + final protected void notifyProjectDeleted(P project) { + listeners.forEach(l -> l.deleted(project)); + } + + final protected FileObserver getFileObserver() { + return fileObserver; + } +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectFinder.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectFinder.java new file mode 100644 index 000000000..5543863ac --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectFinder.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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.Log; +import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * Abstract implementation of Java project finder interface + * + * @author Alex Boyko + * + */ +public abstract class AbstractJavaProjectFinder implements JavaProjectFinder { + + @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; + } + +} 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 deleted file mode 100644 index 6c4db536a..000000000 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractJavaProjectManager.java +++ /dev/null @@ -1,109 +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.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/CompositeJavaProjectFinder.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectFinder.java new file mode 100644 index 000000000..1d7741fb0 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectFinder.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * Composite project manager that acts a single project manager but consissts of many project managers + * + * @author Alex Boyko + * + */ +public class CompositeJavaProjectFinder implements JavaProjectFinder { + + private final List projectFinders; + + public CompositeJavaProjectFinder(Collection projectFinders) { + this.projectFinders = new ArrayList<>(projectFinders); + } + + public CompositeJavaProjectFinder() { + this(Collections.emptyList()); + } + + public boolean addJavaProjectFinder(JavaProjectFinder javaProjectFinder) { + return projectFinders.add(javaProjectFinder); + } + + public boolean removeJavaProjectFinder(JavaProjectFinder javaProjectFinder) { + return projectFinders.remove(javaProjectFinder); + } + + @Override + public IJavaProject find(IDocument doc) { + return projectFinders.stream().map(finder -> finder.find(doc)).filter(Objects::nonNull).findFirst().orElse(null); + } + + @Override + public IJavaProject find(File file) { + return projectFinders.stream().map(finder -> finder.find(file)).filter(Objects::nonNull).findFirst().orElse(null); + } + + @Override + public boolean isProjectRoot(File file) { + return projectFinders.stream().filter(finder -> finder.isProjectRoot(file)) != null; + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java deleted file mode 100644 index 39f1b7393..000000000 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeJavaProjectManager.java +++ /dev/null @@ -1,101 +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.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; - -/** - * Composite project manager that acts a single project manager but consissts of many project managers - * - * @author Alex Boyko - * - */ -public class CompositeJavaProjectManager implements JavaProjectManager { - - private final JavaProjectManager[] projectManagers; - - public CompositeJavaProjectManager(JavaProjectManager[] projectManagers) { - this.projectManagers = projectManagers; - } - - @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 IJavaProject find(File file) { - for (JavaProjectManager strategy : projectManagers) { - try { - IJavaProject project = strategy.find(file); - if (project != null) { - return project; - } - } catch (Exception e) { - Log.log(e); - } - } - return null; - } - - @Override - public boolean isProjectRoot(File file) { - for (JavaProjectManager strategy : projectManagers) { - try { - if (strategy.isProjectRoot(file)) { - return true; - } - } catch (Exception e) { - Log.log(e); - } - } - 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/CompositeProjectOvserver.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeProjectOvserver.java new file mode 100644 index 000000000..c3f9b3060 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/CompositeProjectOvserver.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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.util.List; + +/** + * Project Observer that is able to act as a single project observer for a list of observers + * + * @author Alex Boyko + * + */ +public class CompositeProjectOvserver implements ProjectObserver { + + private List observers; + + public CompositeProjectOvserver(List observers) { + this.observers = observers; + } + + @Override + public void addListener(Listener listener) { + observers.forEach(o -> o.addListener(listener)); + } + + @Override + public void removeListener(Listener listener) { + observers.forEach(o -> o.removeListener(listener)); + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectCache.java new file mode 100644 index 000000000..e8caaab45 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectCache.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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 org.springframework.ide.vscode.commons.java.IJavaProject; + +/** + * Java Projects Cache + * + * @author Alex Boyko + * + * @param key class + * @param

java project class + */ +public interface JavaProjectCache extends ProjectObserver { + + P project(K key); + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java similarity index 64% rename from headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java index 46e6ef874..48bda3361 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectManager.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectFinder.java @@ -13,29 +13,17 @@ 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; /** - * Java project manager. Able to find a java project for a file or document and - * provide project change listening mechanism + * Java project finder. Able to find a java project for a file or document * * @author Alex Boyko */ -public interface JavaProjectManager { +public interface JavaProjectFinder { - 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/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java new file mode 100644 index 000000000..5a37ae412 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.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.languageserver.java; + +import org.springframework.ide.vscode.commons.java.IJavaProject; + +/** + * Projects Observer. Able to add/remove project listeners which are notified on project changes + * + * @author Alex Boyko + * + */ +public interface ProjectObserver { + + interface Listener { + void created(IJavaProject project); + void changed(IJavaProject project); + void deleted(IJavaProject project); + } + + 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 deleted file mode 100644 index 85a66afa1..000000000 --- a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/java/test/CompositeJavaProjectManagerTest.java +++ /dev/null @@ -1,181 +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.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/json/DidChangeWatchedFilesRegistrationOptions.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/json/DidChangeWatchedFilesRegistrationOptions.java new file mode 100644 index 000000000..620108b54 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/json/DidChangeWatchedFilesRegistrationOptions.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.commons.languageserver.json; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; +import org.eclipse.xtext.xbase.lib.Pure; +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * Model object to be used in client/registerCapability JSON message to register file watchers + * + * @author Alex Boyko + * + */ +public class DidChangeWatchedFilesRegistrationOptions { + + @NonNull + private List watchers; + + public DidChangeWatchedFilesRegistrationOptions() { + this(new ArrayList()); + } + + public DidChangeWatchedFilesRegistrationOptions(@NonNull final List watchers) { + this.watchers = watchers; + } + + @Pure + @NonNull + public List getRegistrations() { + return this.watchers; + } + + public void setRegistrations(@NonNull final List watchers) { + this.watchers = watchers; + } + + @Override + @Pure + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("watchers", this.watchers); + return b.toString(); + } + + @Override + @Pure + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DidChangeWatchedFilesRegistrationOptions other = (DidChangeWatchedFilesRegistrationOptions) obj; + if (this.watchers == null) { + if (other.watchers != null) + return false; + } else if (!this.watchers.equals(other.watchers)) + return false; + return true; + } + + @Override + @Pure + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.watchers == null) ? 0 : this.watchers.hashCode()); + return result; + } +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/json/FileSystemWatcher.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/json/FileSystemWatcher.java new file mode 100644 index 000000000..0fdfb82bb --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/json/FileSystemWatcher.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * 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.json; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; +import org.eclipse.xtext.xbase.lib.Pure; +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +/** + * Model object to be used in client/registerCapability JSON message to register a file watcher + * + * @author Alex Boyko + * + */ +public class FileSystemWatcher { + + /** + * Interested in create events + */ + public static final int CREATE = 1; + + /** + * Interested in change events + */ + public static final int CHANGE = 2; + + /** + * Interested in delete events + */ + public static final int DELETE = 4; + + @NonNull + private String globPattern; + + private int kind = CREATE | CHANGE | DELETE; + + public FileSystemWatcher() { + } + + public FileSystemWatcher(@NonNull final String globPattern) { + this(globPattern, CREATE | CHANGE | DELETE); + } + + public FileSystemWatcher(@NonNull final String globPattern, final int kind) { + this.globPattern = globPattern; + this.kind = kind; + } + + @Pure + @NonNull + public String getGlobPattern() { + return this.globPattern; + } + + public void setGlobPattern(@NonNull final String globPattern) { + this.globPattern = globPattern; + } + + @Pure + @NonNull + public int getKind() { + return this.kind; + } + + /** + * The method / capability to unregister for. + */ + public void setKind(final int kind) { + this.kind = kind; + } + + @Override + @Pure + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("globPattern", this.globPattern); + b.add("kind", this.kind); + return b.toString(); + } + + @Override + @Pure + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FileSystemWatcher other = (FileSystemWatcher) obj; + if (this.globPattern == null) { + if (other.globPattern != null) + return false; + } else if (!this.globPattern.equals(other.globPattern)) + return false; + if (this.kind != other.kind) + return false; + return true; + } + + @Override + @Pure + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.globPattern == null) ? 0 : this.globPattern.hashCode()); + result = prime * result + (this.kind); + return result; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 7de3d0e94..328134aff 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -103,6 +103,8 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl private boolean hasExecuteCommandSupport; + private boolean hasFileWatcherRegistrationSupport; + @Override public void connect(LanguageClient _client) { this.client = (STS4LanguageClient) _client; @@ -175,6 +177,7 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl } this.hasCompletionSnippetSupport = safeGet(false, () -> params.getCapabilities().getTextDocument().getCompletion().getCompletionItem().getSnippetSupport()); this.hasExecuteCommandSupport = safeGet(false, () -> params.getCapabilities().getWorkspace().getExecuteCommand()!=null); + this.hasFileWatcherRegistrationSupport = safeGet(false, () -> params.getCapabilities().getWorkspace().getDidChangeWatchedFiles().getDynamicRegistration()); Log.debug("workspaceRoot = "+workspaceRoot); Log.debug("hasCompletionSnippetSupport = "+hasCompletionSnippetSupport); Log.debug("hasExecuteCommandSupport = "+hasExecuteCommandSupport); @@ -295,6 +298,7 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl @Override public CompletableFuture shutdown() { + getWorkspaceService().dispose(); return CompletableFuture.completedFuture(new Object()); } @@ -459,4 +463,8 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl Assert.isLegal(this.testListener==null); testListener = languageServerTestListener; } + + public boolean canRegisterFileWatchersDynamically() { + return hasFileWatcherRegistrationSupport; + } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java new file mode 100644 index 000000000..5785947f5 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleServerFileObserver.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.Registration; +import org.eclipse.lsp4j.RegistrationParams; +import org.eclipse.lsp4j.Unregistration; +import org.eclipse.lsp4j.UnregistrationParams; +import org.springframework.ide.vscode.commons.languageserver.json.DidChangeWatchedFilesRegistrationOptions; +import org.springframework.ide.vscode.commons.languageserver.json.FileSystemWatcher; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; + +/** + * File Observer implementation for a language server. Able to register listeners to specific files via LS protocol + * + * @author Alex Boyko + * + */ +public class SimpleServerFileObserver extends BasicFileObserver { + + private static final String WORKSPACE_DID_CHANGE_WATCHED_FILES = "workspace/didChangeWatchedFiles"; + + private SimpleLanguageServer server; + + public SimpleServerFileObserver(SimpleLanguageServer server) { + this.server = server; + } + + @Override + public String onFileCreated(List globPattern, Consumer handler) { + String subscriptionId = super.onFileCreated(globPattern, handler); + subscribe(subscriptionId, globPattern, FileSystemWatcher.CREATE); + return subscriptionId; + } + + @Override + public String onFileChanged(List globPattern, Consumer handler) { + String subscriptionId = super.onFileChanged(globPattern, handler); + subscribe(subscriptionId, globPattern, FileSystemWatcher.CHANGE); + return subscriptionId; + } + + @Override + public String onFileDeleted(List globPattern, Consumer handler) { + String subscriptionId = super.onFileDeleted(globPattern, handler); + subscribe(subscriptionId, globPattern, FileSystemWatcher.DELETE); + return subscriptionId; + } + + private void subscribe(String subscriptionId, List globPattern, int kind) { + if (server.canRegisterFileWatchersDynamically()) { + List watchers = globPattern.stream().map(pattern -> new FileSystemWatcher(pattern, kind)).collect(Collectors.toList()); + Registration registration = new Registration(subscriptionId, WORKSPACE_DID_CHANGE_WATCHED_FILES, new DidChangeWatchedFilesRegistrationOptions(watchers)); + server.getClient().registerCapability(new RegistrationParams(Arrays.asList(registration))); + } + } + + @Override + public boolean unsubscribe(String subscriptionId) { + if (server.canRegisterFileWatchersDynamically()) { + server.getClient().unregisterCapability(new UnregistrationParams(Arrays.asList(new Unregistration(subscriptionId, WORKSPACE_DID_CHANGE_WATCHED_FILES)))); + } + return super.unsubscribe(subscriptionId); + } + + private void unsubscribeAll(Collection subscriptions) { + List unregisterations = subscriptions.stream().map(s -> new Unregistration(s, WORKSPACE_DID_CHANGE_WATCHED_FILES)).collect(Collectors.toList()); + if (!unregisterations.isEmpty()) { + server.getClient().unregisterCapability(new UnregistrationParams(unregisterations)); + } + } + + void dispose() { + if (server.canRegisterFileWatchersDynamically()) { + unsubscribeAll(createRegistry.keySet()); + unsubscribeAll(changeRegistry.keySet()); + unsubscribeAll(deleteRegistry.keySet()); + } + } + +} 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 65407653e..9b37b43ff 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 @@ -28,17 +28,18 @@ import com.google.common.collect.ImmutableList; import reactor.core.publisher.Mono; -public class SimpleWorkspaceService implements WorkspaceService, FileObserver { +public class SimpleWorkspaceService implements WorkspaceService { 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<>(); + private SimpleServerFileObserver fileObserver; public SimpleWorkspaceService(SimpleLanguageServer server) { this.server = server; + this.fileObserver = new SimpleServerFileObserver(server); } @Override @@ -62,32 +63,29 @@ public class SimpleWorkspaceService implements WorkspaceService, FileObserver { @Override public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { - params.getChanges().forEach(event -> fileListeners.forEach(listener -> { + params.getChanges().forEach(event -> { 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; - - } + switch (event.getType()) { + case Created: + fileObserver.notifyFileCreated(uri); + break; + case Changed: + fileObserver.notifyFileChanged(uri); + break; + case Deleted: + fileObserver.notifyFileDeleted(uri); + break; + default: + Log.log("Uknown file change type '" + event.getType() + "' for file: " + uri); + break; } } } catch (Throwable t) { Log.log(t); } - })); + }); } @Override @@ -116,14 +114,12 @@ public class SimpleWorkspaceService implements WorkspaceService, FileObserver { return this.workspaceSymbolHandler != null; } - @Override - public void addListener(FileListener listener) { - fileListeners.add(listener); + public FileObserver getFileObserver() { + return fileObserver; } - @Override - public void removeListener(FileListener listener) { - fileListeners.remove(listener); + public void dispose() { + fileObserver.dispose(); } } 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 2435308ab..8e0728bb4 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 @@ -34,8 +34,8 @@ public class MavenJavaProject implements IJavaProject { return classpath; } - void update(MavenCore maven, File pom) { - this.classpath = new MavenProjectClasspath(maven, pom); + void update(MavenCore maven) { + this.classpath = new MavenProjectClasspath(maven, classpath.getPomFile()); } } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java new file mode 100644 index 000000000..bae929e28 --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileObserver; + +/** + * Cache for Maven projects + * + * @author Alex Boyko + */ +public class MavenProjectCache extends AbstractFileToProjectCache { + + private MavenCore maven; + + public MavenProjectCache(FileObserver fileObserver, MavenCore maven) { + super(fileObserver); + this.maven = maven; + } + + @Override + protected void update(MavenJavaProject project) { + project.update(maven); + } + + @Override + protected MavenJavaProject createProject(File pomFile) throws Exception { + return new MavenJavaProject(maven, pomFile); + } + +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 07400d374..636bf8550 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -77,6 +77,10 @@ public class MavenProjectClasspath extends JandexClasspath { } } + public File getPomFile() { + return pom; + } + public boolean exists() { return pom.exists(); } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectFinder.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectFinder.java new file mode 100644 index 000000000..08a0f6d76 --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectFinder.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.java; + +import java.io.File; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectFinder; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileUtils; + +/** + * Finds Maven projects. Looks for pom.xml file + * + * @author Alex Boyko + * + */ +public class MavenProjectFinder extends AbstractJavaProjectFinder { + + private MavenProjectCache cache; + + public MavenProjectFinder(MavenProjectCache cache) { + this.cache = cache; + } + + @Override + public IJavaProject find(File file) { + File pomFile = FileUtils.findFile(file, MavenCore.POM_XML); + return cache.project(pomFile); + } + + @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/MavenProjectManager.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectManager.java deleted file mode 100644 index d9179f5e9..000000000 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectManager.java +++ /dev/null @@ -1,115 +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.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 9a0d9f965..7e764ce8c 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 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 @@ -67,8 +67,7 @@ public class JavaProjectWithClasspathFile implements IJavaProject { return true; } - void update(File cpFile) { - this.cpFile = cpFile; + void update() { this.classpath = new FileClasspath(Paths.get(cpFile.toURI())); } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileCache.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileCache.java new file mode 100644 index 000000000..95fc1897e --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileCache.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache; +import org.springframework.ide.vscode.commons.util.FileObserver; + +public class JavaProjectWithClasspathFileCache extends AbstractFileToProjectCache { + + public JavaProjectWithClasspathFileCache(FileObserver fileObserver) { + super(fileObserver); + } + + @Override + protected void update(JavaProjectWithClasspathFile project) { + project.update(); + } + + @Override + protected JavaProjectWithClasspathFile createProject(File cpFile) throws Exception { + return new JavaProjectWithClasspathFile(cpFile); + } + + + +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileFinder.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileFinder.java new file mode 100644 index 000000000..e29cbc954 --- /dev/null +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileFinder.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.java.classpathfile; + +import java.io.File; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.AbstractJavaProjectFinder; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileUtils; + +/** + * Finder for projects with classpath.txt file that contains classpath entries + * For test purposes only. + * + * @author Alex Boyko + * + */ +public class JavaProjectWithClasspathFileFinder extends AbstractJavaProjectFinder { + + private JavaProjectWithClasspathFileCache cache; + + public JavaProjectWithClasspathFileFinder(JavaProjectWithClasspathFileCache cache) { + super(); + this.cache = cache; + } + + @Override + public IJavaProject find(File file) { + File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT); + return cache.project(cpFile); + } + + @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/java/classpathfile/JavaProjectWithClasspathFileManager.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileManager.java deleted file mode 100644 index dd37c861c..000000000 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFileManager.java +++ /dev/null @@ -1,103 +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.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 index db0746eca..b30302b1a 100644 --- 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 @@ -19,13 +19,13 @@ 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.languageserver.java.ProjectObserver.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.maven.java.MavenProjectCache; import org.springframework.ide.vscode.commons.util.BasicFileObserver; /** - * Tests for {@link MavenProjectManager} + * Tests for {@link MavenProjectCache} * * @author Alex Boyko * @@ -36,12 +36,11 @@ public class MavenProjectManagerTest { 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); + MavenProjectCache cache = new MavenProjectCache(fileObserver, MavenCore.getDefault()); IJavaProject[] projectChanged = new IJavaProject[] { null }; IJavaProject[] projectDeleted = new IJavaProject[] { null }; - manager.addListener(new Listener() { + cache.addListener(new Listener() { @Override public void created(IJavaProject project) {} @@ -56,7 +55,7 @@ public class MavenProjectManagerTest { }); // Get the project from cache - MavenJavaProject cachedProject = manager.find(pomFile); + MavenJavaProject cachedProject = cache.project(pomFile); assertNotNull(cachedProject); fileObserver.notifyFileChanged(pomFile.toURI().toString()); 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 index 9596a02c1..bf52adb86 100644 --- 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 @@ -10,6 +10,20 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.util; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.ImmutablePair; + /** * Basic implementation of File Observer interface * @@ -18,52 +32,65 @@ package org.springframework.ide.vscode.commons.util; */ public class BasicFileObserver implements FileObserver { - private ListenerList listeners = new ListenerList<>(); - + protected ConcurrentHashMap, Consumer>> createRegistry = new ConcurrentHashMap<>(); + protected ConcurrentHashMap, Consumer>> deleteRegistry = new ConcurrentHashMap<>(); + protected ConcurrentHashMap, Consumer>> changeRegistry = new ConcurrentHashMap<>(); + @Override - public void addListener(FileListener listener) { - listeners.add(listener); + public String onFileCreated(List globPattern, Consumer handler) { + return registerFileListener(createRegistry, globPattern, handler); } @Override - public void removeListener(FileListener listener) { - listeners.remove(listener); + public String onFileChanged(List globPattern, Consumer handler) { + return registerFileListener(changeRegistry, globPattern, handler); + } + + @Override + public String onFileDeleted(List globPattern, Consumer handler) { + return registerFileListener(deleteRegistry, globPattern, handler); + } + + @Override + public boolean unsubscribe(String subscriptionId) { + if (createRegistry.remove(subscriptionId) != null) { + return true; + } + if (changeRegistry.remove(subscriptionId) != null) { + return true; + } + if (deleteRegistry.remove(subscriptionId) != null) { + return true; + } + return false; } - public void notifyFileChanged(String uri) { - listeners.forEach(l -> { - try { - if (l.accept(uri)) { - l.changed(uri); - } - } catch (Throwable t) { - Log.log(t); - } - }); + private static String registerFileListener(Map, Consumer>> registry, List globPattern, Consumer handler) { + String subscriptionId = UUID.randomUUID().toString(); + registry.put(subscriptionId, ImmutablePair.of(globPattern.stream().map(g -> FileSystems.getDefault().getPathMatcher("glob:" + g)).collect(Collectors.toList()), handler)); + return subscriptionId; } - public void notifyFileCreated(String uri) { - listeners.forEach(l -> { - try { - if (l.accept(uri)) { - l.created(uri); - } - } catch (Throwable t) { - Log.log(t); - } - }); + final public void notifyFileCreated(String uri) { + notify(createRegistry, uri); } - public void notifyFileDeleted(String uri) { - listeners.forEach(l -> { - try { - if (l.accept(uri)) { - l.deleted(uri); - } - } catch (Throwable t) { - Log.log(t); - } - }); + final public void notifyFileChanged(String uri) { + notify(changeRegistry, uri); } + final public void notifyFileDeleted(String uri) { + notify(deleteRegistry, uri); + } + + private static void notify(Map, Consumer>> registry, String uri) { + Path path = Paths.get(URI.create(uri)); + registry.values().stream() + .filter(pair -> pair.left.stream() + .filter(matcher -> matcher.matches(path)) + .findFirst() + .isPresent()) + .forEach(pair -> pair.right.accept(uri)); + } + } 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 index 69e4fe9f5..a73f0d78d 100644 --- 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 @@ -10,6 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.util; +import java.util.List; +import java.util.function.Consumer; + /** * Object able to add/remove {@link FileListener} objects and fire events via these listeners * @@ -18,15 +21,12 @@ package org.springframework.ide.vscode.commons.util; */ public interface FileObserver { - public interface FileListener { - boolean accept(String uri); - void changed(String uri); - void deleted(String uri); - void created(String uri); - } + String onFileCreated(List globPattern, Consumer handler); - void addListener(FileListener listener); + String onFileChanged(List globPattern, Consumer handler); + + String onFileDeleted(List globPattern, Consumer handler); + + boolean unsubscribe(String subscriptionId); - void removeListener(FileListener listener); - } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 7ba98f330..66566fec0 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -47,10 +48,13 @@ import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; import org.eclipse.lsp4j.DidOpenTextDocumentParams; import org.eclipse.lsp4j.DocumentSymbolParams; import org.eclipse.lsp4j.ExecuteCommandCapabilities; import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.FileChangeType; +import org.eclipse.lsp4j.FileEvent; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; @@ -306,6 +310,21 @@ public class LanguageServerHarness { return documents.get(uri); } + public synchronized void changeFile(String uri) { + FileEvent fileEvent = new FileEvent(uri, FileChangeType.Changed); + getServer().getWorkspaceService().didChangeWatchedFiles(new DidChangeWatchedFilesParams(Arrays.asList(fileEvent))); + } + + public synchronized void createFile(String uri) { + FileEvent fileEvent = new FileEvent(uri, FileChangeType.Created); + getServer().getWorkspaceService().didChangeWatchedFiles(new DidChangeWatchedFilesParams(Arrays.asList(fileEvent))); + } + + public synchronized void deleteFile(String uri) { + FileEvent fileEvent = new FileEvent(uri, FileChangeType.Deleted); + getServer().getWorkspaceService().didChangeWatchedFiles(new DidChangeWatchedFilesParams(Arrays.asList(fileEvent))); + } + public TextDocumentInfo changeDocument(String uri, String newContent) throws Exception { TextDocumentItem textDocument = setDocumentContent(uri, newContent); DidChangeTextDocumentParams didChange = new DidChangeTextDocumentParams();