PT #152867134 Progress and Error feedback for Maven/Gradle project load
This commit is contained in:
@@ -32,7 +32,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
|
||||
public class BootJavaLanguageServerParams {
|
||||
|
||||
@@ -60,12 +59,11 @@ public class BootJavaLanguageServerParams {
|
||||
public static LSFactory<BootJavaLanguageServerParams> createDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
|
||||
@@ -83,13 +81,12 @@ public class BootJavaLanguageServerParams {
|
||||
public static LSFactory<BootJavaLanguageServerParams> createTestDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null);
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), false, null);
|
||||
mavenProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null);
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), false, null);
|
||||
gradleProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
|
||||
@@ -183,9 +183,8 @@ public class SpringIndexer {
|
||||
if (server.getProjectObserver() != null) {
|
||||
server.getProjectObserver().removeListener(projectListener);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +222,7 @@ public class SpringIndexer {
|
||||
return this.symbols;
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
@@ -64,12 +63,11 @@ public class BootPropertiesLanguageServerParams {
|
||||
public static LSFactory<BootPropertiesLanguageServerParams> createDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
|
||||
@@ -89,13 +87,12 @@ public class BootPropertiesLanguageServerParams {
|
||||
public static LSFactory<BootPropertiesLanguageServerParams> createTestDefault(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null);
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), false, null);
|
||||
mavenProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null);
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), false, null);
|
||||
gradleProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
@@ -113,13 +110,12 @@ public class BootPropertiesLanguageServerParams {
|
||||
public static LSFactory<BootPropertiesLanguageServerParams> createTestDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null);
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), false, null);
|
||||
mavenProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null);
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), false, null);
|
||||
gradleProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
<artifactId>gradle-tooling-api</artifactId>
|
||||
<version>${gradle-tooling.version}</version>
|
||||
</dependency>
|
||||
<!-- Test harness -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -15,6 +15,7 @@ import java.nio.file.Path;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* Implementation of Gradle Java project
|
||||
@@ -36,6 +37,26 @@ public class GradleJavaProject extends AbstractJavaProject {
|
||||
);
|
||||
}
|
||||
|
||||
public GradleJavaProject(GradleCore gradle, File projectDir) {
|
||||
this(gradle, projectDir, null);
|
||||
if (!classpath.isCached()) {
|
||||
try {
|
||||
classpath.update();
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
if (classpath.getName() == null) {
|
||||
return projectDir.getName();
|
||||
} else {
|
||||
return super.getElementName();
|
||||
}
|
||||
}
|
||||
|
||||
public File getLocation() {
|
||||
return projectDir;
|
||||
}
|
||||
@@ -45,7 +66,7 @@ public class GradleJavaProject extends AbstractJavaProject {
|
||||
return classpath;
|
||||
}
|
||||
|
||||
boolean update() {
|
||||
boolean update() throws Exception {
|
||||
return classpath.update();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,11 @@ package org.springframework.ide.vscode.commons.gradle;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.MessageType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
|
||||
|
||||
/**
|
||||
* Tests whether document belongs to a Gradle project
|
||||
@@ -26,24 +29,29 @@ public class GradleProjectCache extends AbstractFileToProjectCache<GradleJavaPro
|
||||
|
||||
private GradleCore gradle;
|
||||
|
||||
public GradleProjectCache(FileObserver fileObserver, GradleCore gradle, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(fileObserver, asyncUpdate, projectCacheFolder);
|
||||
public GradleProjectCache(Sts4LanguageServer server, GradleCore gradle, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(server, asyncUpdate, projectCacheFolder);
|
||||
this.gradle = gradle;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean update(GradleJavaProject project) {
|
||||
return project.update();
|
||||
try {
|
||||
return project.update();
|
||||
} catch (Exception e) {
|
||||
server.getDiagnosticService().diagnosticEvent(new ShowMessageException(
|
||||
new MessageParams(MessageType.Error, "Cannot load Gradle project model from folder: " + project.getLocation()), e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GradleJavaProject createProject(File gradleBuild) throws Exception {
|
||||
File gradleFile = gradleBuild.getParentFile();
|
||||
GradleJavaProject gradleJavaProject = new GradleJavaProject(gradle, gradleFile,
|
||||
projectCacheFolder == null ? null : gradleFile.toPath().resolve(projectCacheFolder));
|
||||
if (gradleJavaProject.getClasspath().isCached()) {
|
||||
performUpdate(gradleJavaProject, asyncUpdate);
|
||||
}
|
||||
projectCacheFolder == null ? null : gradleFile.toPath().resolve(projectCacheFolder)
|
||||
);
|
||||
performUpdate(gradleJavaProject, asyncUpdate, asyncUpdate);
|
||||
return gradleJavaProject;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider;
|
||||
import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
@@ -48,27 +46,13 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
private static final String JAVA_RUNTIME_VERSION = "java.runtime.version";
|
||||
private static final String JAVA_BOOT_CLASS_PATH = "sun.boot.class.path";
|
||||
|
||||
private Supplier<EclipseProject> gradleProject;
|
||||
private Supplier<BuildEnvironment> buildEnvironment;
|
||||
private EclipseProject project;
|
||||
private BuildEnvironment buildEnvironment;
|
||||
|
||||
public GradleProjectClasspath(GradleCore gradle, File projectDir) {
|
||||
public GradleProjectClasspath(GradleCore gradle, File projectDir) throws GradleException {
|
||||
super();
|
||||
this.gradleProject = Suppliers.memoize(() -> {
|
||||
try {
|
||||
return gradle.getModel(projectDir, EclipseProject.class);
|
||||
} catch (GradleException e) {
|
||||
Log.log(e);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
this.buildEnvironment = Suppliers.memoize(() -> {
|
||||
try {
|
||||
return gradle.getModel(projectDir, BuildEnvironment.class);
|
||||
} catch (GradleException e) {
|
||||
Log.log(e);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
this.project = gradle.getModel(projectDir, EclipseProject.class);
|
||||
this.buildEnvironment = gradle.getModel(projectDir, BuildEnvironment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,7 +76,7 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
public EclipseProject getRootProject() {
|
||||
EclipseProject root = this.gradleProject.get();
|
||||
EclipseProject root = project;
|
||||
if (root == null) {
|
||||
return root;
|
||||
}
|
||||
@@ -105,7 +89,6 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
@Override
|
||||
public ImmutableList<Path> getClasspathEntries() throws Exception {
|
||||
EclipseProject root = getRootProject();
|
||||
EclipseProject project = gradleProject.get();
|
||||
if (project == null) {
|
||||
return ImmutableList.of();
|
||||
} else {
|
||||
@@ -126,7 +109,6 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
|
||||
@Override
|
||||
public ImmutableList<String> getClasspathResources() {
|
||||
EclipseProject project = gradleProject.get();
|
||||
if (project == null) {
|
||||
return ImmutableList.of();
|
||||
} else {
|
||||
@@ -145,22 +127,19 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
public Path getOutputFolder() {
|
||||
EclipseProject project = gradleProject.get();
|
||||
return project == null ? null : project.getProjectDirectory().toPath().resolve(project.getOutputLocation().getPath());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
EclipseProject project = gradleProject.get();
|
||||
return project == null ? null : project.getName();
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return gradleProject != null;
|
||||
return project != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IJavadocProvider createParserJavadocProvider(File classpathResource) {
|
||||
EclipseProject project = gradleProject.get();
|
||||
if (project != null) {
|
||||
if (classpathResource.isDirectory()) {
|
||||
Optional<File> classpathFolder = project.getSourceDirectories().stream()
|
||||
@@ -187,18 +166,18 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
public String getGradleVersion() throws GradleException {
|
||||
if (buildEnvironment.get() == null) {
|
||||
if (buildEnvironment == null) {
|
||||
throw new GradleException(new Exception("Cannot find Gradle version"));
|
||||
} else {
|
||||
return buildEnvironment.get().getGradle().getGradleVersion();
|
||||
return buildEnvironment.getGradle().getGradleVersion();
|
||||
}
|
||||
}
|
||||
|
||||
public File getGradleHome() throws GradleException {
|
||||
if (buildEnvironment.get() == null) {
|
||||
if (buildEnvironment == null) {
|
||||
throw new GradleException(new Exception("Cannot find Gradle home folder"));
|
||||
} else {
|
||||
return buildEnvironment.get().getGradle().getGradleUserHome();
|
||||
return buildEnvironment.getGradle().getGradleUserHome();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,10 +197,10 @@ public class GradleProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
private String getJavaHome() {
|
||||
if (buildEnvironment.get() == null) {
|
||||
if (buildEnvironment == null) {
|
||||
return System.getProperty(JAVA_HOME);
|
||||
} else {
|
||||
return buildEnvironment.get().getJava().getJavaHome().toString();
|
||||
return buildEnvironment.getJava().getJavaHome().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
@@ -26,9 +28,12 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.assertj.core.util.Files;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
|
||||
import org.springframework.ide.vscode.commons.util.BasicFileObserver;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -41,6 +46,18 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class GradleProjectTest {
|
||||
|
||||
private Sts4LanguageServer server;
|
||||
private BasicFileObserver fileObserver;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
fileObserver = new BasicFileObserver();
|
||||
server = mock(Sts4LanguageServer.class);
|
||||
SimpleWorkspaceService workspaceService = mock(SimpleWorkspaceService.class);
|
||||
when(workspaceService.getFileObserver()).thenReturn(fileObserver);
|
||||
when(server.getWorkspaceService()).thenReturn(workspaceService);
|
||||
}
|
||||
|
||||
private static void writeContent(File file, String content) throws IOException {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
@@ -53,9 +70,7 @@ public class GradleProjectTest {
|
||||
|
||||
private GradleJavaProject getGradleProject(String projectName) throws Exception {
|
||||
Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/" + projectName).toURI());
|
||||
GradleJavaProject gradleJavaProject = new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile(), null);
|
||||
gradleJavaProject.update();
|
||||
return gradleJavaProject;
|
||||
return new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,15 +101,9 @@ public class GradleProjectTest {
|
||||
String gradelFileContents = Files.contentOf(gradleFile, Charset.defaultCharset());
|
||||
|
||||
try {
|
||||
BasicFileObserver fileObserver = new BasicFileObserver();
|
||||
GradleProjectCache manager = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null);
|
||||
GradleProjectCache manager = new GradleProjectCache(server, GradleCore.getDefault(), false, null);
|
||||
IJavaProject[] projectChanged = new IJavaProject[] { null };
|
||||
IJavaProject[] projectDeleted = new IJavaProject[] { null };
|
||||
|
||||
// Get the project from cache
|
||||
GradleJavaProject cachedProject = manager.project(gradleFile);
|
||||
assertNotNull(cachedProject);
|
||||
|
||||
manager.addListener(new Listener() {
|
||||
@Override
|
||||
public void created(IJavaProject project) {}
|
||||
@@ -109,6 +118,10 @@ public class GradleProjectTest {
|
||||
}
|
||||
});
|
||||
|
||||
// Get the project from cache
|
||||
GradleJavaProject cachedProject = manager.project(gradleFile);
|
||||
assertNotNull(cachedProject);
|
||||
|
||||
ImmutableList<Path> calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
|
||||
assertEquals(48, calculatedClassPath.size());
|
||||
|
||||
@@ -132,7 +145,7 @@ public class GradleProjectTest {
|
||||
|
||||
@Test
|
||||
public void findGradleProjectWithStandardBuildFile() throws Exception {
|
||||
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), GradleCore.getDefault(), false, null));
|
||||
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(server, GradleCore.getDefault(), false, null));
|
||||
File sourceFile = new File(GradleProjectTest.class.getResource("/test-app-1/src/main/java/Library.java").toURI());
|
||||
Optional<IJavaProject> project = finder.find(sourceFile);
|
||||
assertTrue(project.isPresent());
|
||||
@@ -143,7 +156,7 @@ public class GradleProjectTest {
|
||||
|
||||
@Test
|
||||
public void findGradleProjectWithNonStandardBuildFile() throws Exception {
|
||||
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), GradleCore.getDefault(), false, null));
|
||||
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(server, GradleCore.getDefault(), false, null));
|
||||
File sourceFile = new File(GradleProjectTest.class.getResource("/test-app-2/src/main/java/Library.java").toURI());
|
||||
Optional<IJavaProject> project = finder.find(sourceFile);
|
||||
assertTrue(project.isPresent());
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
<artifactId>commons-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-language-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss</groupId>
|
||||
<artifactId>jandex</artifactId>
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -29,7 +30,6 @@ import org.json.JSONTokener;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -93,20 +93,17 @@ public class DelegatingCachedClasspath<T extends IClasspath> implements IClasspa
|
||||
}
|
||||
|
||||
private AtomicReference<ClasspathData> cachedData;
|
||||
private Supplier<T> delegateCreator;
|
||||
private Callable<T> delegateCreator;
|
||||
private AtomicReference<T> cachedDelegate;
|
||||
|
||||
final private File cacheFile;
|
||||
|
||||
public DelegatingCachedClasspath(Supplier<T> delegateCreator, File cacheFile) {
|
||||
public DelegatingCachedClasspath(Callable<T> delegateCreator, File cacheFile) {
|
||||
super();
|
||||
this.cacheFile = cacheFile;
|
||||
this.cachedDelegate = new AtomicReference<>(delegateCreator.get());
|
||||
this.cachedDelegate = new AtomicReference<>(null);
|
||||
this.cachedData = new AtomicReference<>(init());
|
||||
this.delegateCreator = delegateCreator;
|
||||
if (!isCached()) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public T delegate() {
|
||||
@@ -197,34 +194,43 @@ public class DelegatingCachedClasspath<T extends IClasspath> implements IClasspa
|
||||
}
|
||||
}
|
||||
|
||||
public boolean update() {
|
||||
final ClasspathData newData = createClasspathData();
|
||||
if (!Objects.equal(cachedData.get(), newData)) {
|
||||
cachedData.set(newData);
|
||||
persistCachedData(newData);
|
||||
return true;
|
||||
public boolean update() throws Exception {
|
||||
try {
|
||||
final ClasspathData newData = createClasspathData();
|
||||
if (!Objects.equal(cachedData.get(), newData)) {
|
||||
cachedData.set(newData);
|
||||
persistCachedData(newData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
cachedData.set(new ClasspathData(null, Collections.emptySet(), Collections.emptySet(), null));
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return cachedDelegate.get().exists();
|
||||
T t = cachedDelegate.get();
|
||||
return t != null && t.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IType findType(String fqName) {
|
||||
return cachedDelegate.get().findType(fqName);
|
||||
T t = cachedDelegate.get();
|
||||
return t == null ? null : t.findType(fqName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter) {
|
||||
return cachedDelegate.get().fuzzySearchTypes(searchTerm, typeFilter);
|
||||
T t = cachedDelegate.get();
|
||||
return t == null ? Flux.empty() : t.fuzzySearchTypes(searchTerm, typeFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
|
||||
return cachedDelegate.get().fuzzySearchPackages(searchTerm);
|
||||
T t = cachedDelegate.get();
|
||||
return t == null ? Flux.empty() : t.fuzzySearchPackages(searchTerm);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,16 +238,12 @@ public class DelegatingCachedClasspath<T extends IClasspath> implements IClasspa
|
||||
return cachedDelegate.get().allSubtypesOf(type);
|
||||
}
|
||||
|
||||
protected ClasspathData createClasspathData() {
|
||||
T newDelegate = delegateCreator.get();
|
||||
protected ClasspathData createClasspathData() throws Exception {
|
||||
T newDelegate = delegateCreator.call();
|
||||
cachedDelegate.set(newDelegate);
|
||||
try {
|
||||
LinkedHashSet<Path> classpathEntries = new LinkedHashSet<>(newDelegate.getClasspathEntries());
|
||||
return new ClasspathData(newDelegate.getName(), classpathEntries, new LinkedHashSet<>(newDelegate.getClasspathResources()), newDelegate.getOutputFolder());
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return new ClasspathData(newDelegate.getName(), Collections.emptySet(), new LinkedHashSet<>(newDelegate.getClasspathResources()), newDelegate.getOutputFolder());
|
||||
}
|
||||
LinkedHashSet<Path> classpathEntries = new LinkedHashSet<>(newDelegate.getClasspathEntries());
|
||||
return new ClasspathData(newDelegate.getName(), classpathEntries,
|
||||
new LinkedHashSet<>(newDelegate.getClasspathResources()), newDelegate.getOutputFolder());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
|
||||
/**
|
||||
* Cache for java projects. The key for the cache is a "project" specific file
|
||||
@@ -32,24 +34,26 @@ public abstract class AbstractFileToProjectCache<P extends IJavaProject> extends
|
||||
private String deleteSubscription;
|
||||
protected boolean asyncUpdate;
|
||||
protected final Path projectCacheFolder;
|
||||
private boolean alwaysFireEventOnFileChanged;
|
||||
private boolean alwaysFireEventOnUpdate;
|
||||
|
||||
public AbstractFileToProjectCache(FileObserver fileObserver, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(fileObserver);
|
||||
private static AtomicInteger progressIdCt = new AtomicInteger(0);
|
||||
|
||||
public AbstractFileToProjectCache(Sts4LanguageServer server, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(server);
|
||||
this.projectCacheFolder = projectCacheFolder;
|
||||
this.asyncUpdate = asyncUpdate;
|
||||
}
|
||||
|
||||
|
||||
final public void setAlwaysFireEventOnFileChanged(boolean alwaysFireEventOnFileChanged) {
|
||||
this.alwaysFireEventOnFileChanged = alwaysFireEventOnFileChanged;
|
||||
final public void setAlwaysFireEventOnFileChanged(boolean alwaysFireEventOnUpdate) {
|
||||
this.alwaysFireEventOnUpdate = alwaysFireEventOnUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachListeners(File file, P project) {
|
||||
super.attachListeners(file, project);
|
||||
List<String> globPattern = Arrays.asList(file.toString());
|
||||
changeSubscription = getFileObserver().onFileChanged(globPattern, (uri) -> performUpdate(project, asyncUpdate));
|
||||
changeSubscription = getFileObserver().onFileChanged(globPattern, (uri) -> performUpdate(project, asyncUpdate, true));
|
||||
deleteSubscription = getFileObserver().onFileDeleted(globPattern, (uri) -> {
|
||||
cache.invalidate(file);
|
||||
notifyProjectDeleted(project);
|
||||
@@ -58,20 +62,37 @@ public abstract class AbstractFileToProjectCache<P extends IJavaProject> extends
|
||||
});
|
||||
}
|
||||
|
||||
final protected void performUpdate(P project, boolean async) {
|
||||
final protected void performUpdate(P project, boolean async, boolean notify) {
|
||||
final String taskId = getProgressId();
|
||||
final ProgressService progressService = server.getProgressService();
|
||||
if (progressService != null) {
|
||||
progressService.progressEvent(taskId, "Updating data for project `" + project.getElementName() + "'");
|
||||
}
|
||||
if (async) {
|
||||
CompletableFuture.supplyAsync(() -> update(project)).thenAccept((changed) -> {
|
||||
if (changed || alwaysFireEventOnFileChanged) {
|
||||
notifyProjectChanged(project);
|
||||
}
|
||||
});
|
||||
CompletableFuture.supplyAsync(() -> update(project)).thenAccept((changed) -> afterUpdate(project, changed, notify, taskId));
|
||||
} else {
|
||||
if (update(project) || alwaysFireEventOnFileChanged) {
|
||||
boolean changed = update(project);
|
||||
afterUpdate(project, changed, notify, taskId);
|
||||
}
|
||||
}
|
||||
|
||||
private void afterUpdate(P project, boolean changed, boolean notify, String taskId) {
|
||||
final ProgressService progressService = server.getProgressService();
|
||||
if (progressService != null) {
|
||||
progressService.progressEvent(taskId, null);
|
||||
}
|
||||
if (changed || alwaysFireEventOnUpdate) {
|
||||
if (notify) {
|
||||
notifyProjectChanged(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getProgressId() {
|
||||
return AbstractFileToProjectCache.class.getName()+ (progressIdCt.incrementAndGet());
|
||||
}
|
||||
|
||||
|
||||
abstract protected boolean update(P project);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ 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.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
import org.springframework.ide.vscode.commons.util.ListenerList;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
@@ -30,14 +31,14 @@ import com.google.common.cache.CacheBuilder;
|
||||
*/
|
||||
public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implements JavaProjectCache<K, P> {
|
||||
|
||||
private FileObserver fileObserver;
|
||||
protected Sts4LanguageServer server;
|
||||
|
||||
private ListenerList<Listener> listeners = new ListenerList<>();
|
||||
|
||||
protected Cache<K, P> cache = CacheBuilder.newBuilder().build();
|
||||
|
||||
public AbstractJavaProjectCache(FileObserver fileObserver) {
|
||||
this.fileObserver = fileObserver;
|
||||
public AbstractJavaProjectCache(Sts4LanguageServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,6 +91,6 @@ public abstract class AbstractJavaProjectCache<K, P extends IJavaProject> implem
|
||||
}
|
||||
|
||||
final protected FileObserver getFileObserver() {
|
||||
return fileObserver;
|
||||
return server.getWorkspaceService().getFileObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
|
||||
|
||||
/**
|
||||
* Reports error/warnings from the LS to the client via LSP message
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DiagnosticService {
|
||||
|
||||
/**
|
||||
* Sends the error/warning message to the client
|
||||
* @param message
|
||||
*/
|
||||
void diagnosticEvent(ShowMessageException message);
|
||||
|
||||
}
|
||||
@@ -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.languageserver;
|
||||
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
|
||||
|
||||
/**
|
||||
* STS4 language server
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public interface Sts4LanguageServer extends LanguageServer {
|
||||
|
||||
@Override
|
||||
SimpleTextDocumentService getTextDocumentService();
|
||||
|
||||
@Override
|
||||
SimpleWorkspaceService getWorkspaceService();
|
||||
|
||||
/**
|
||||
* Progress Service to report progress info from LS to the client
|
||||
* @return progress service instance
|
||||
*/
|
||||
ProgressService getProgressService();
|
||||
|
||||
/**
|
||||
* Diagnostic service to report errors/warnings from LS to the client
|
||||
* @return
|
||||
*/
|
||||
DiagnosticService getDiagnosticService();
|
||||
|
||||
}
|
||||
@@ -38,10 +38,11 @@ import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.LanguageClientAware;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.DiagnosticService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
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.completion.VscodeCompletionEngineAdapter.LazyCompletionResolver;
|
||||
@@ -75,7 +76,7 @@ import reactor.core.scheduler.Schedulers;
|
||||
* here so we can try to keep the subclass itself more 'clutter free' and focus on
|
||||
* what its really doing and not the 'wiring and plumbing'.
|
||||
*/
|
||||
public abstract class SimpleLanguageServer implements LanguageServer, LanguageClientAware, ServiceNotificationsClient, WorkspaceFoldersProposedService {
|
||||
public abstract class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientAware, ServiceNotificationsClient, WorkspaceFoldersProposedService {
|
||||
|
||||
private static final Scheduler RECONCILER_SCHEDULER = Schedulers.newSingle("Reconciler");
|
||||
|
||||
@@ -96,6 +97,8 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
}
|
||||
};
|
||||
|
||||
private DiagnosticService diagnosticService = message -> onError(null, message);
|
||||
|
||||
private CompletableFuture<Void> busyReconcile = CompletableFuture.completedFuture(null);
|
||||
|
||||
private QuickfixRegistry quickfixRegistry;
|
||||
@@ -514,6 +517,7 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgressService getProgressService() {
|
||||
return progressService;
|
||||
}
|
||||
@@ -532,4 +536,10 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl
|
||||
getWorkspaceService().didChangeWorkspaceFolders(params);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DiagnosticService getDiagnosticService() {
|
||||
return diagnosticService;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,20 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test harness -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.nio.file.Path;
|
||||
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* Wrapper for Maven Core project
|
||||
@@ -26,9 +27,11 @@ import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
public class MavenJavaProject extends AbstractJavaProject {
|
||||
|
||||
private DelegatingCachedClasspath<MavenProjectClasspath> classpath;
|
||||
private File pom;
|
||||
|
||||
public MavenJavaProject(MavenCore maven, File pom, Path projectDataCache) {
|
||||
super(projectDataCache);
|
||||
this.pom = pom;
|
||||
this.classpath = new DelegatingCachedClasspath<>(
|
||||
() -> new MavenProjectClasspath(maven, pom),
|
||||
projectDataCache == null ? null : projectDataCache.resolve(DelegatingCachedClasspath.CLASSPATH_DATA_CACHE_FILE).toFile()
|
||||
@@ -37,6 +40,22 @@ public class MavenJavaProject extends AbstractJavaProject {
|
||||
|
||||
public MavenJavaProject(MavenCore maven, File pom) {
|
||||
this(maven, pom, null);
|
||||
if (!classpath.isCached()) {
|
||||
try {
|
||||
classpath.update();
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
if (classpath.getName() == null) {
|
||||
return pom.getParentFile().getName();
|
||||
} else {
|
||||
return super.getElementName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,10 +63,14 @@ public class MavenJavaProject extends AbstractJavaProject {
|
||||
return classpath;
|
||||
}
|
||||
|
||||
boolean update() {
|
||||
boolean update() throws Exception {
|
||||
return classpath.update();
|
||||
}
|
||||
|
||||
public File pom() {
|
||||
return pom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MavenJavaProject("+classpath.getName()+")";
|
||||
|
||||
@@ -13,9 +13,12 @@ package org.springframework.ide.vscode.commons.maven.java;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.MessageType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
|
||||
/**
|
||||
* Cache for Maven projects
|
||||
@@ -26,23 +29,28 @@ public class MavenProjectCache extends AbstractFileToProjectCache<MavenJavaProje
|
||||
|
||||
private MavenCore maven;
|
||||
|
||||
public MavenProjectCache(FileObserver fileObserver, MavenCore maven, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(fileObserver, asyncUpdate, projectCacheFolder);
|
||||
public MavenProjectCache(Sts4LanguageServer server, MavenCore maven, boolean asyncUpdate, Path projectCacheFolder) {
|
||||
super(server, asyncUpdate, projectCacheFolder);
|
||||
this.maven = maven;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean update(MavenJavaProject project) {
|
||||
return project.update();
|
||||
try {
|
||||
return project.update();
|
||||
} catch (Exception e) {
|
||||
server.getDiagnosticService().diagnosticEvent(new ShowMessageException(
|
||||
new MessageParams(MessageType.Error, "Cannot load Maven project model from Pom file: " + project.pom()), e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MavenJavaProject createProject(File pomFile) throws Exception {
|
||||
MavenJavaProject mavenJavaProject = new MavenJavaProject(maven, pomFile,
|
||||
projectCacheFolder == null ? null : pomFile.getParentFile().toPath().resolve(projectCacheFolder));
|
||||
if (mavenJavaProject.getClasspath().isCached()) {
|
||||
performUpdate(mavenJavaProject, asyncUpdate);
|
||||
}
|
||||
projectCacheFolder == null ? null : pomFile.getParentFile().toPath().resolve(projectCacheFolder)
|
||||
);
|
||||
performUpdate(mavenJavaProject, asyncUpdate, asyncUpdate);
|
||||
return mavenJavaProject;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenException;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
@@ -49,13 +48,13 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
|
||||
private MavenCore maven;
|
||||
private File pom;
|
||||
private Supplier<MavenProject> projectSupplier;
|
||||
private MavenProject project;
|
||||
|
||||
MavenProjectClasspath(MavenCore maven, File pom) {
|
||||
MavenProjectClasspath(MavenCore maven, File pom) throws MavenException {
|
||||
super();
|
||||
this.maven = maven;
|
||||
this.pom = pom;
|
||||
this.projectSupplier = Suppliers.memoize(() -> createMavenProject());
|
||||
this.project = createMavenProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,19 +62,13 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
return new JandexIndex[] { maven.getJavaIndexForJreLibs() };
|
||||
}
|
||||
|
||||
private final MavenProject createMavenProject() {
|
||||
private final MavenProject createMavenProject() throws MavenException {
|
||||
try {
|
||||
// Read with resolved dependencies
|
||||
return maven.readProject(pom, true);
|
||||
} catch (MavenException e) {
|
||||
Log.log(e);
|
||||
try {
|
||||
// Try without resolving dependencies - just read the XML
|
||||
return maven.readProject(pom, false);
|
||||
} catch (MavenException e1) {
|
||||
Log.log(e);
|
||||
return null;
|
||||
}
|
||||
return maven.readProject(pom, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +85,6 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
MavenProject project = projectSupplier.get();
|
||||
return project == null ? null : project.getName();
|
||||
}
|
||||
|
||||
@@ -107,12 +99,10 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
private Set<Artifact> projectDependencies() {
|
||||
MavenProject project = projectSupplier.get();
|
||||
return project == null ? Collections.emptySet() : project.getArtifacts();
|
||||
}
|
||||
|
||||
private List<File> projectOutput() {
|
||||
MavenProject project = projectSupplier.get();
|
||||
if (project == null) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
@@ -121,18 +111,15 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
public Path getOutputFolder() {
|
||||
MavenProject project = projectSupplier.get();
|
||||
return project == null ? null : new File(project.getBuild().getOutputDirectory()).toPath();
|
||||
}
|
||||
|
||||
private Optional<Artifact> getArtifactFromJarFile(File file) throws MavenException {
|
||||
MavenProject project = projectSupplier.get();
|
||||
return project.getArtifacts().stream().filter(a -> file.equals(a.getFile())).findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<String> getClasspathResources() {
|
||||
MavenProject project = projectSupplier.get();
|
||||
if (project == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
@@ -188,7 +175,6 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
// }
|
||||
|
||||
protected IJavadocProvider createParserJavadocProvider(File classpathResource) {
|
||||
MavenProject project = projectSupplier.get();
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -225,7 +211,6 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
}
|
||||
|
||||
protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) {
|
||||
MavenProject project = projectSupplier.get();
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -267,7 +252,7 @@ public class MavenProjectClasspath extends JandexClasspath {
|
||||
MavenProjectClasspath other = (MavenProjectClasspath) obj;
|
||||
try {
|
||||
if (pom.equals(other.pom)
|
||||
&& projectSupplier.get().equals(other.projectSupplier.get())) {
|
||||
&& Objects.equal(project, other.project)) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -12,13 +12,13 @@ package org.springframework.ide.vscode.commons.maven.java.classpathfile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
|
||||
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||
|
||||
public class JavaProjectWithClasspathFileCache extends AbstractFileToProjectCache<JavaProjectWithClasspathFile> {
|
||||
|
||||
public JavaProjectWithClasspathFileCache(FileObserver fileObserver) {
|
||||
super(fileObserver, false, null);
|
||||
public JavaProjectWithClasspathFileCache(Sts4LanguageServer server) {
|
||||
super(server, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.maven;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.DiagnosticService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
|
||||
import org.springframework.ide.vscode.commons.util.BasicFileObserver;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Tests for {@link MavenProjectCache}
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class MavenProjectCacheTest {
|
||||
|
||||
private Sts4LanguageServer server;
|
||||
private BasicFileObserver fileObserver;
|
||||
private Path testProjectPath;
|
||||
private File pomFile;
|
||||
private String pomFileContents;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
fileObserver = new BasicFileObserver();
|
||||
server = mock(Sts4LanguageServer.class);
|
||||
SimpleWorkspaceService workspaceService = mock(SimpleWorkspaceService.class);
|
||||
when(workspaceService.getFileObserver()).thenReturn(fileObserver);
|
||||
when(server.getWorkspaceService()).thenReturn(workspaceService);
|
||||
|
||||
testProjectPath = Paths
|
||||
.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI());
|
||||
pomFile = testProjectPath.resolve(MavenCore.POM_XML).toFile();
|
||||
pomFileContents = new String(Files.readAllBytes(pomFile.toPath()), Charset.defaultCharset());
|
||||
|
||||
Path cacheFolerPath = testProjectPath.resolve(IJavaProject.PROJECT_CACHE_FOLDER);
|
||||
if (cacheFolerPath.toFile().exists()) {
|
||||
Files.walk(cacheFolerPath, FileVisitOption.FOLLOW_LINKS).map(Path::toFile).forEach(File::delete);
|
||||
Files.delete(cacheFolerPath);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
// restore original content
|
||||
writeContent(pomFile, pomFileContents);
|
||||
}
|
||||
|
||||
private static void writeContent(File file, String content) throws IOException {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(file);
|
||||
writer.write(content);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPomFileChanges() throws Exception {
|
||||
MavenProjectCache cache = new MavenProjectCache(server, MavenCore.getDefault(), false, null);
|
||||
IJavaProject[] projectChanged = new IJavaProject[] { null };
|
||||
IJavaProject[] projectDeleted = new IJavaProject[] { null };
|
||||
cache.addListener(new Listener() {
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
projectChanged[0] = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
projectDeleted[0] = project;
|
||||
}
|
||||
});
|
||||
|
||||
// Get the project from cache
|
||||
MavenJavaProject cachedProject = cache.project(pomFile);
|
||||
assertNotNull(cachedProject);
|
||||
|
||||
ImmutableList<Path> calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
|
||||
assertEquals(48, calculatedClassPath.size());
|
||||
|
||||
fileObserver.notifyFileChanged(pomFile.toURI().toString());
|
||||
assertNull(projectChanged[0]);
|
||||
|
||||
writeContent(pomFile,
|
||||
new String(Files.readAllBytes(testProjectPath.resolve("pom.newxml")), Charset.defaultCharset()));
|
||||
fileObserver.notifyFileChanged(pomFile.toURI().toString());
|
||||
assertNotNull(projectChanged[0]);
|
||||
assertEquals(cachedProject, projectChanged[0]);
|
||||
calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
|
||||
assertEquals(49, calculatedClassPath.size());
|
||||
|
||||
fileObserver.notifyFileDeleted(pomFile.toURI().toString());
|
||||
assertEquals(cachedProject, projectDeleted[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClasspathCaching() throws Exception {
|
||||
Path testProjectPath = Paths
|
||||
.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI());
|
||||
|
||||
Path cacheFolder = testProjectPath.resolve(IJavaProject.PROJECT_CACHE_FOLDER);
|
||||
|
||||
final File classpathCacheFile = cacheFolder.resolve(DelegatingCachedClasspath.CLASSPATH_DATA_CACHE_FILE)
|
||||
.toFile();
|
||||
|
||||
AtomicBoolean progressDone = new AtomicBoolean();
|
||||
|
||||
ProgressService progressService = mock(ProgressService.class);
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
progressDone.set(true);
|
||||
return null;
|
||||
}
|
||||
}).when(progressService).progressEvent(any(String.class), (String) isNull());
|
||||
|
||||
when(server.getProgressService()).thenReturn(progressService);
|
||||
|
||||
assertFalse(classpathCacheFile.exists());
|
||||
|
||||
MavenProjectCache cache = new MavenProjectCache(server, MavenCore.getDefault(), true, cacheFolder);
|
||||
MavenJavaProject project = cache.project(pomFile);
|
||||
assertTrue(project.getClasspath().getClasspathEntries().isEmpty());
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
while (!progressDone.get()) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
|
||||
assertTrue(classpathCacheFile.exists());
|
||||
assertEquals(48, project.getClasspath().getClasspathEntries().size());
|
||||
|
||||
progressDone.set(false);
|
||||
|
||||
// Reset the cache
|
||||
cache = new MavenProjectCache(server, MavenCore.getDefault(), true, cacheFolder);
|
||||
|
||||
// Check loaded from cache file
|
||||
project = cache.project(pomFile);
|
||||
assertEquals(48, project.getClasspath().getClasspathEntries().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorLoadingProject() throws Exception {
|
||||
Path testProjectPath = Paths
|
||||
.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI());
|
||||
|
||||
Path cacheFolder = testProjectPath.resolve(IJavaProject.PROJECT_CACHE_FOLDER);
|
||||
|
||||
AtomicBoolean progressDone = new AtomicBoolean();
|
||||
|
||||
ProgressService progressService = mock(ProgressService.class);
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
progressDone.set(true);
|
||||
return null;
|
||||
}
|
||||
}).when(progressService).progressEvent(any(String.class), (String) isNull());
|
||||
|
||||
when(server.getProgressService()).thenReturn(progressService);
|
||||
|
||||
DiagnosticService diagnosticService = mock(DiagnosticService.class);
|
||||
when(server.getDiagnosticService()).thenReturn(diagnosticService);
|
||||
|
||||
MavenProjectCache cache = new MavenProjectCache(server, MavenCore.getDefault(), true, cacheFolder);
|
||||
MavenJavaProject project = cache.project(pomFile);
|
||||
assertTrue(project.getClasspath().getClasspathEntries().isEmpty());
|
||||
|
||||
CompletableFuture.runAsync(() -> {
|
||||
while (!progressDone.get()) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
progressDone.set(false);
|
||||
verify(diagnosticService, never()).diagnosticEvent(any(ShowMessageException.class));
|
||||
|
||||
writeContent(pomFile, "");
|
||||
fileObserver.notifyFileChanged(pomFile.toURI().toString());
|
||||
CompletableFuture.runAsync(() -> {
|
||||
while (!progressDone.get()) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).get(10, TimeUnit.SECONDS);
|
||||
progressDone.set(false);
|
||||
verify(diagnosticService, times(1)).diagnosticEvent(any(ShowMessageException.class));
|
||||
}
|
||||
}
|
||||
@@ -1,104 +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.maven;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.assertj.core.util.Files;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
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.MavenProjectCache;
|
||||
import org.springframework.ide.vscode.commons.util.BasicFileObserver;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Tests for {@link MavenProjectCache}
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class MavenProjectManagerTest {
|
||||
|
||||
private static void writeContent(File file, String content) throws IOException {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(file);
|
||||
writer.write(content);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPomFileChanges() throws Exception {
|
||||
Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI());
|
||||
File pomFile = testProjectPath.resolve(MavenCore.POM_XML).toFile();
|
||||
|
||||
String pomFileContents = Files.contentOf(pomFile, Charset.defaultCharset());
|
||||
|
||||
try {
|
||||
BasicFileObserver fileObserver = new BasicFileObserver();
|
||||
MavenProjectCache cache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), false, null);
|
||||
IJavaProject[] projectChanged = new IJavaProject[] { null };
|
||||
IJavaProject[] projectDeleted = new IJavaProject[] { null };
|
||||
cache.addListener(new Listener() {
|
||||
@Override
|
||||
public void created(IJavaProject project) {}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
projectChanged[0] = project;
|
||||
}
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
projectDeleted[0] = project;
|
||||
}
|
||||
});
|
||||
|
||||
// Get the project from cache
|
||||
MavenJavaProject cachedProject = cache.project(pomFile);
|
||||
assertNotNull(cachedProject);
|
||||
|
||||
ImmutableList<Path> calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
|
||||
assertEquals(48, calculatedClassPath.size());
|
||||
|
||||
fileObserver.notifyFileChanged(pomFile.toURI().toString());
|
||||
assertNull(projectChanged[0]);
|
||||
|
||||
writeContent(pomFile, Files.contentOf(testProjectPath.resolve("pom.newxml").toFile(), Charset.defaultCharset()));
|
||||
fileObserver.notifyFileChanged(pomFile.toURI().toString());
|
||||
assertNotNull(projectChanged[0]);
|
||||
assertEquals(cachedProject, projectChanged[0]);
|
||||
calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
|
||||
assertEquals(49, calculatedClassPath.size());
|
||||
|
||||
fileObserver.notifyFileDeleted(pomFile.toURI().toString());
|
||||
assertEquals(cachedProject, projectDeleted[0]);
|
||||
} finally {
|
||||
//restore original content
|
||||
writeContent(pomFile, pomFileContents);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user