PT #152867134 Progress and Error feedback for Maven/Gradle project load

This commit is contained in:
BoykoAlex
2017-11-17 18:32:12 -05:00
parent adc4d4f978
commit 08c1cd33ef
22 changed files with 581 additions and 261 deletions

View File

@@ -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));

View File

@@ -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;

View File

@@ -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));

View File

@@ -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>

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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());

View File

@@ -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>

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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()+")";

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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);
}
}
}