PT #151547534 Cache classpath in a file

This commit is contained in:
BoykoAlex
2017-11-14 12:06:26 -05:00
parent 5511552253
commit cee1005bb8
30 changed files with 786 additions and 176 deletions

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Arrays;
@@ -21,6 +22,7 @@ import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
import org.springframework.ide.vscode.commons.gradle.GradleCore;
import org.springframework.ide.vscode.commons.gradle.GradleProjectCache;
import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
@@ -60,10 +62,10 @@ public class BootJavaLanguageServerParams {
// Initialize project finders, project caches and project observers
FileObserver fileObserver = server.getWorkspaceService().getFileObserver();
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault());
MavenProjectCache mavenProjectCache = new MavenProjectCache(fileObserver, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault());
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
@@ -78,5 +80,29 @@ 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.setAlwaysFireEventOnFileChanged(true);
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
GradleProjectCache gradleProjectCache = new GradleProjectCache(fileObserver, GradleCore.getDefault(), false, null);
gradleProjectCache.setAlwaysFireEventOnFileChanged(true);
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
return new BootJavaLanguageServerParams(
javaProjectFinder.filter(BootProjectUtil::isBootProject),
projectObserver,
new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver),
RunningAppProvider.DEFAULT,
SpringLiveHoverWatchdog.DEFAULT_INTERVAL
);
};
}
}

View File

@@ -111,7 +111,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine {
private String[] getClasspathEntries(IDocument doc) throws Exception {
IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get();
IClasspath classpath = project.getClasspath();
Stream<Path> classpathEntries = classpath.getClasspathEntries();
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
return classpathEntries
.filter(path -> path.toFile().exists())
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);

View File

@@ -213,7 +213,7 @@ public class BootJavaHoverProvider implements HoverHandler {
try {
IClasspath classpath = project.getClasspath();
if (classpath!=null) {
return classpath.getClasspathEntries().anyMatch(cpe -> {
return classpath.getClasspathEntries().stream().anyMatch(cpe -> {
String name = cpe.getFileName().toString();
return name.startsWith("spring-boot-actuator-");
});

View File

@@ -126,7 +126,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler {
private String[] getClasspathEntries(IDocument doc) throws Exception {
IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get();
IClasspath classpath = project.getClasspath();
Stream<Path> classpathEntries = classpath.getClasspathEntries();
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
return classpathEntries
.filter(path -> path.toFile().exists())
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);

View File

@@ -22,7 +22,7 @@ public class BootProjectUtil {
try {
IClasspath cp = jp.getClasspath();
if (cp!=null) {
return cp.getClasspathEntries().anyMatch(cpe -> isBootEntry(cpe));
return cp.getClasspathEntries().stream().anyMatch(cpe -> isBootEntry(cpe));
}
} catch (Exception e) {
Log.log(e);

View File

@@ -133,7 +133,7 @@ public final class CompilationUnitCache {
return new String[0];
} else {
IClasspath classpath = project.getClasspath();
Stream<Path> classpathEntries = classpath.getClasspathEntries();
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
return classpathEntries
.filter(path -> path.toFile().exists())
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);

View File

@@ -456,7 +456,7 @@ public class SpringIndexer {
private String[] getClasspathEntries(IJavaProject project) throws Exception {
IClasspath classpath = project.getClasspath();
Stream<Path> classpathEntries = classpath.getClasspathEntries();
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
return classpathEntries
.filter(path -> path.toFile().exists())
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);

View File

@@ -43,7 +43,7 @@ public class BootLanguageServerHarness extends LanguageServerHarness<BootJavaLan
public static class Builder {
LSFactory<BootJavaLanguageServerParams> defaultsFactory = BootJavaLanguageServerParams.createDefault();
LSFactory<BootJavaLanguageServerParams> defaultsFactory = BootJavaLanguageServerParams.createTestDefault();
private JavaProjectFinder projectFinder = null;
private ProjectObserver projectObserver = null;
private SpringPropertyIndexProvider indexProvider = null;
@@ -92,7 +92,7 @@ public class BootLanguageServerHarness extends LanguageServerHarness<BootJavaLan
private BootLanguageServerHarness(Builder builder) throws Exception {
super(() -> {
LSFactory<BootJavaLanguageServerParams> params = (server) -> {
BootJavaLanguageServerParams defaults = BootJavaLanguageServerParams.createDefault().create(server);
BootJavaLanguageServerParams defaults = BootJavaLanguageServerParams.createTestDefault().create(server);
return new BootJavaLanguageServerParams(
builder.projectFinder==null?defaults.projectFinder:builder.projectFinder,
builder.projectObserver==null?defaults.projectObserver:builder.projectObserver,

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot;
import java.nio.file.Paths;
import java.util.Arrays;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
@@ -25,6 +26,7 @@ import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconci
import org.springframework.ide.vscode.commons.gradle.GradleCore;
import org.springframework.ide.vscode.commons.gradle.GradleProjectCache;
import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;
@@ -120,10 +122,10 @@ public class BootPropertiesLanguageServer extends SimpleLanguageServer {
documents.onHover(hoverEngine::getHover);
// Initialize project finders, project caches and project observers
MavenProjectCache mavenProjectCache = new MavenProjectCache(getWorkspaceService().getFileObserver(), MavenCore.getDefault());
MavenProjectCache mavenProjectCache = new MavenProjectCache(getWorkspaceService().getFileObserver(), MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
GradleProjectCache gradleProjectCache = new GradleProjectCache(getWorkspaceService().getFileObserver(), GradleCore.getDefault());
GradleProjectCache gradleProjectCache = new GradleProjectCache(getWorkspaceService().getFileObserver(), GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));

View File

@@ -63,7 +63,7 @@ public class ResourceHintProvider implements ValueProviderStrategy {
private static class ClasspathHints extends CachingValueProvider {
@Override
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create));
return Flux.fromStream(javaProject.getClasspath().getClasspathResources().stream().distinct().map(StsValueHint::create));
}
}

View File

@@ -11,9 +11,10 @@
package org.springframework.ide.vscode.commons.gradle;
import java.io.File;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
/**
* Implementation of Gradle Java project
@@ -21,16 +22,18 @@ import org.springframework.ide.vscode.commons.util.Log;
* @author Alex Boyko
*
*/
public class GradleJavaProject implements IJavaProject {
public class GradleJavaProject extends AbstractJavaProject {
private GradleCore gradle;
private GradleProjectClasspath classpath;
private DelegatingCachedClasspath<GradleProjectClasspath> classpath;
private File projectDir;
public GradleJavaProject(GradleCore gradle, File projectDir) throws GradleException {
this.gradle = gradle;
public GradleJavaProject(GradleCore gradle, File projectDir, Path projectDataCache) {
super(projectDataCache);
this.projectDir = projectDir;
this.classpath = new GradleProjectClasspath(gradle, projectDir);
this.classpath = new DelegatingCachedClasspath<GradleProjectClasspath>(
() -> new GradleProjectClasspath(gradle, projectDir),
projectDataCache == null ? null : projectDataCache.resolve(DelegatingCachedClasspath.CLASSPATH_DATA_CACHE_FILE).toFile()
);
}
public File getLocation() {
@@ -38,16 +41,13 @@ public class GradleJavaProject implements IJavaProject {
}
@Override
public GradleProjectClasspath getClasspath() {
public DelegatingCachedClasspath<GradleProjectClasspath> getClasspath() {
return classpath;
}
void update() {
try {
this.classpath = new GradleProjectClasspath(gradle, projectDir);
} catch (GradleException e) {
Log.log(e);
}
boolean update() {
return classpath.update();
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.gradle;
import java.io.File;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
import org.springframework.ide.vscode.commons.util.FileObserver;
@@ -25,19 +26,25 @@ public class GradleProjectCache extends AbstractFileToProjectCache<GradleJavaPro
private GradleCore gradle;
public GradleProjectCache(FileObserver fileObserver, GradleCore gradle) {
super(fileObserver);
public GradleProjectCache(FileObserver fileObserver, GradleCore gradle, boolean asyncUpdate, Path projectCacheFolder) {
super(fileObserver, asyncUpdate, projectCacheFolder);
this.gradle = gradle;
}
@Override
protected void update(GradleJavaProject project) {
project.update();
protected boolean update(GradleJavaProject project) {
return project.update();
}
@Override
protected GradleJavaProject createProject(File gradleBuild) throws Exception {
return new GradleJavaProject(gradle, gradleBuild.getParentFile());
File gradleFile = gradleBuild.getParentFile();
GradleJavaProject gradleJavaProject = new GradleJavaProject(gradle, gradleFile,
projectCacheFolder == null ? null : gradleFile.toPath().resolve(projectCacheFolder));
if (gradleJavaProject.getClasspath().isCached()) {
performUpdate(gradleJavaProject, asyncUpdate);
}
return gradleJavaProject;
}
}

View File

@@ -34,6 +34,7 @@ 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;
/**
* Implementation of {@link IClasspath} for Gradle projects
@@ -47,12 +48,19 @@ 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 EclipseProject gradleProject;
private Supplier<EclipseProject> gradleProject;
private Supplier<BuildEnvironment> buildEnvironment;
public GradleProjectClasspath(GradleCore gradle, File projectDir) throws GradleException {
public GradleProjectClasspath(GradleCore gradle, File projectDir) {
super();
this.gradleProject = gradle.getModel(projectDir, EclipseProject.class);
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);
@@ -84,7 +92,10 @@ public class GradleProjectClasspath extends JandexClasspath {
}
public EclipseProject getRootProject() {
EclipseProject root = this.gradleProject;
EclipseProject root = this.gradleProject.get();
if (root == null) {
return root;
}
while(root.getParent() != null) {
root = root.getParent();
}
@@ -92,15 +103,21 @@ public class GradleProjectClasspath extends JandexClasspath {
}
@Override
public Stream<Path> getClasspathEntries() throws Exception {
public ImmutableList<Path> getClasspathEntries() throws Exception {
EclipseProject root = getRootProject();
return Stream.concat(gradleProject.getClasspath().stream().map(dep -> dep.getFile().toPath()),
gradleProject.getProjectDependencies().stream()
.map(d -> findPeer(root, d.getTargetProject().getName()))
.filter(o -> o.isPresent())
.map(o -> o.get())
.map(p -> p.getProjectDirectory().toPath().resolve(p.getOutputLocation().getPath()))
);
EclipseProject project = gradleProject.get();
if (project == null) {
return ImmutableList.of();
} else {
ImmutableList<Path> classpathEntries = ImmutableList.copyOf(Stream.concat(project.getClasspath().stream().map(dep -> dep.getFile().toPath()),
project.getProjectDependencies().stream()
.map(d -> findPeer(root, d.getTargetProject().getName()))
.filter(o -> o.isPresent())
.map(o -> o.get())
.map(p -> p.getProjectDirectory().toPath().resolve(p.getOutputLocation().getPath()))
).collect(Collectors.toList()));
return classpathEntries;
}
}
private Optional<? extends EclipseProject> findPeer(EclipseProject root, String name) {
@@ -108,26 +125,33 @@ public class GradleProjectClasspath extends JandexClasspath {
}
@Override
public Stream<String> getClasspathResources() {
return gradleProject.getSourceDirectories().stream().map(sourceDirectory -> sourceDirectory.getDirectory()).flatMap(folder -> {
try {
return Files.walk(folder.toPath())
.filter(path -> Files.isRegularFile(path))
.map(path -> folder.toPath().relativize(path))
.map(relativePath -> relativePath.toString())
.filter(pathString -> !pathString.endsWith(".java") && !pathString.endsWith(".class"));
} catch (IOException e) {
return Stream.empty();
}
});
public ImmutableList<String> getClasspathResources() {
EclipseProject project = gradleProject.get();
if (project == null) {
return ImmutableList.of();
} else {
return ImmutableList.copyOf(project.getSourceDirectories().stream().map(sourceDirectory -> sourceDirectory.getDirectory()).flatMap(folder -> {
try {
return Files.walk(folder.toPath())
.filter(path -> Files.isRegularFile(path))
.map(path -> folder.toPath().relativize(path))
.map(relativePath -> relativePath.toString())
.filter(pathString -> !pathString.endsWith(".java") && !pathString.endsWith(".class"));
} catch (IOException e) {
return Stream.empty();
}
}).toArray(String[]::new));
}
}
public Path getOutputFolder() {
return gradleProject.getProjectDirectory().toPath().resolve(gradleProject.getOutputLocation().getPath());
EclipseProject project = gradleProject.get();
return project == null ? null : project.getProjectDirectory().toPath().resolve(project.getOutputLocation().getPath());
}
public String getName() {
return gradleProject.getName();
EclipseProject project = gradleProject.get();
return project == null ? null : project.getName();
}
public boolean exists() {
@@ -136,20 +160,23 @@ public class GradleProjectClasspath extends JandexClasspath {
@Override
protected IJavadocProvider createParserJavadocProvider(File classpathResource) {
if (classpathResource.isDirectory()) {
Optional<File> classpathFolder = gradleProject.getSourceDirectories().stream()
.map(dir -> dir.getDirectory())
.filter(dir -> classpathResource.toPath().startsWith(dir.toPath()))
.findFirst();
if (classpathFolder.isPresent()) {
return new ParserJavadocProvider(type -> {
return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER
.sourceUrl(classpathFolder.get().toURI().toURL(), type);
});
EclipseProject project = gradleProject.get();
if (project != null) {
if (classpathResource.isDirectory()) {
Optional<File> classpathFolder = project.getSourceDirectories().stream()
.map(dir -> dir.getDirectory())
.filter(dir -> classpathResource.toPath().startsWith(dir.toPath()))
.findFirst();
if (classpathFolder.isPresent()) {
return new ParserJavadocProvider(type -> {
return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER
.sourceUrl(classpathFolder.get().toURI().toURL(), type);
});
}
} else {
}
} else {
}
return null;
}
@@ -222,4 +249,13 @@ public class GradleProjectClasspath extends JandexClasspath {
return new File(JandexIndex.getIndexFolder().toString(), jarFile.getName() + "-" + suffix + ".jdx");
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GradleProjectClasspath) {
return super.equals(obj);
}
return false;
}
}

View File

@@ -13,21 +13,26 @@ package org.springframework.ide.vscode.commons.gradle;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
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 java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
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.util.BasicFileObserver;
import com.google.common.collect.ImmutableList;
/**
* Tests covering Gradle project data
*
@@ -36,15 +41,27 @@ import org.springframework.ide.vscode.commons.util.BasicFileObserver;
*/
public class GradleProjectTest {
private static void writeContent(File file, String content) throws IOException {
FileWriter writer = null;
try {
writer = new FileWriter(file);
writer.write(content);
} finally {
writer.close();
}
}
private GradleJavaProject getGradleProject(String projectName) throws Exception {
Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/" + projectName).toURI());
return new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile());
GradleJavaProject gradleJavaProject = new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile(), null);
gradleJavaProject.update();
return gradleJavaProject;
}
@Test
public void testEclipseGradleProject() throws Exception {
GradleJavaProject project = getGradleProject("empty-gradle-project");
Set<Path> calculatedClassPath = project.getClasspath().getClasspathEntries().collect(Collectors.toSet());
ImmutableList<Path> calculatedClassPath = project.getClasspath().getClasspathEntries();
assertEquals(48, calculatedClassPath.size());
}
@@ -57,7 +74,7 @@ public class GradleProjectTest {
@Test
public void gradleClasspathResource() throws Exception {
GradleJavaProject project = getGradleProject("test-app-1");
List<String> resources = project.getClasspath().getClasspathResources().collect(Collectors.toList());
List<String> resources = project.getClasspath().getClasspathResources();
assertArrayEquals(new String[] {"test-resource-1.txt"}, resources.toArray(new String[resources.size()]));
}
@@ -65,38 +82,57 @@ public class GradleProjectTest {
public void testGradleFileChanges() throws Exception {
Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/empty-gradle-project").toURI());
File gradleFile = testProjectPath.resolve(GradleCore.GRADLE_BUILD_FILE).toFile();
BasicFileObserver fileObserver = new BasicFileObserver();
GradleProjectCache manager = new GradleProjectCache(fileObserver, GradleCore.getDefault());
IJavaProject[] projectChanged = new IJavaProject[] { null };
IJavaProject[] projectDeleted = new IJavaProject[] { null };
manager.addListener(new Listener() {
@Override
public void created(IJavaProject project) {}
@Override
public void changed(IJavaProject project) {
projectChanged[0] = project;
}
@Override
public void deleted(IJavaProject project) {
projectDeleted[0] = project;
}
});
// Get the project from cache
GradleJavaProject cachedProject = manager.project(gradleFile);
assertNotNull(cachedProject);
String gradelFileContents = Files.contentOf(gradleFile, Charset.defaultCharset());
fileObserver.notifyFileChanged(gradleFile.toURI().toString());
assertEquals(cachedProject, projectChanged[0]);
fileObserver.notifyFileDeleted(gradleFile.toURI().toString());
assertEquals(cachedProject, projectDeleted[0]);
try {
BasicFileObserver fileObserver = new BasicFileObserver();
GradleProjectCache manager = new GradleProjectCache(fileObserver, 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) {}
@Override
public void changed(IJavaProject project) {
projectChanged[0] = project;
}
@Override
public void deleted(IJavaProject project) {
projectDeleted[0] = project;
}
});
ImmutableList<Path> calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
assertEquals(48, calculatedClassPath.size());
fileObserver.notifyFileChanged(gradleFile.toURI().toString());
assertNull(projectChanged[0]);
writeContent(gradleFile, Files.contentOf(testProjectPath.resolve("build.newgradle").toFile(), Charset.defaultCharset()));
fileObserver.notifyFileChanged(gradleFile.toURI().toString());
assertNotNull(projectChanged[0]);
assertEquals(cachedProject, projectChanged[0]);
calculatedClassPath = cachedProject.getClasspath().getClasspathEntries();
assertEquals(49, calculatedClassPath.size());
fileObserver.notifyFileDeleted(gradleFile.toURI().toString());
assertEquals(cachedProject, projectDeleted[0]);
} finally {
writeContent(gradleFile, gradelFileContents);
}
}
@Test
public void findGradleProjectWithStandardBuildFile() throws Exception {
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), GradleCore.getDefault()));
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), 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());
@@ -107,7 +143,7 @@ public class GradleProjectTest {
@Test
public void findGradleProjectWithNonStandardBuildFile() throws Exception {
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), GradleCore.getDefault()));
GradleProjectFinder finder = new GradleProjectFinder(new GradleProjectCache(new BasicFileObserver(), 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

@@ -0,0 +1,34 @@
buildscript {
ext {
springBootVersion = '1.5.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
jar {
baseName = 'empty-boot-1.4.0-web-app'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
}

View File

@@ -46,5 +46,10 @@
<artifactId>reactor-core</artifactId>
<version>${reactor-version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
</dependencies>
</project>

View File

@@ -35,7 +35,7 @@ public abstract class JandexClasspath implements IClasspath {
protected JandexIndex createIndex() {
Stream<Path> classpathEntries = Stream.empty();
try {
classpathEntries = getClasspathEntries();
classpathEntries = getClasspathEntries().stream();
} catch (Exception e) {
Log.log(e);
}

View File

@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.java;
import java.nio.file.Path;
/**
* Abstract java project. Has a folder to store some project calculated data to speed up access
*
* @author Alex Boyko
*
*/
public abstract class AbstractJavaProject implements IJavaProject {
final protected Path projectDataCache;
public AbstractJavaProject(Path projectDataCache) {
this.projectDataCache = projectDataCache;
}
}

View File

@@ -0,0 +1,247 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.json.JSONArray;
import org.json.JSONObject;
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;
import reactor.util.function.Tuple2;
/**
* A wrapper around the classpath created from a Java project using the data in the project file (maven, gradle)
* The wrapper caches some of classpath data such as
* <li> Classpath entries </li>
* <li> Classpath resources </li>
* <li> Output folder </li>
* <li> Projects' name </li>
*
* The cached classpath data is written to ".sts4-cache/classpath-data.json" and loadedd from it when intance of this classpath is created
*
* Implementation is somewhat experimental at the moment...
*
* @author Alex Boyko
*
* @param <T> a subclass of {@link IClasspath} the delegated to classpath created from current data
*/
public class DelegatingCachedClasspath<T extends IClasspath> implements IClasspath {
public static final String CLASSPATH_DATA_CACHE_FILE = "classpath-data.json";
private static final String OUTPUT_FOLDER_PROPERTY = "outputFolder";
private static final String CLASSPATH_RESOURCES_PROPERTY = "classpathResources";
private static final String CLASSPATH_ENTRIES_PROPERTY = "classpathEntries";
private static final String NAME_PROPERTY = "name";
protected static class ClasspathData {
final public String name;
final public Set<Path> classpathEntries;
final public Set<String> classpathResources;
final public Path outputFolder;
public ClasspathData(String name, Set<Path> classpathEntries, Set<String> classpathResources, Path outputFolder) {
this.name = name;
this.classpathEntries = classpathEntries;
this.classpathResources = classpathResources;
this.outputFolder = outputFolder;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ClasspathData) {
ClasspathData other = (ClasspathData) obj;
try {
return Objects.equal(name, other.name)
&& Objects.equal(classpathEntries, other.classpathEntries)
&& Objects.equal(classpathResources, other.classpathResources)
&& Objects.equal(outputFolder, outputFolder);
} catch (Throwable t) {
Log.log(t);
}
}
return false;
}
}
private AtomicReference<ClasspathData> cachedData;
private Supplier<T> delegateCreator;
private AtomicReference<T> cachedDelegate;
final private File cacheFile;
public DelegatingCachedClasspath(Supplier<T> delegateCreator, File cacheFile) {
super();
this.cacheFile = cacheFile;
this.cachedDelegate = new AtomicReference<>(delegateCreator.get());
this.cachedData = new AtomicReference<>(init());
this.delegateCreator = delegateCreator;
if (!isCached()) {
update();
}
}
public T delegate() {
return cachedDelegate.get();
}
@Override
public String getName() {
return cachedData.get().name;
}
@Override
public Path getOutputFolder() {
return cachedData.get().outputFolder;
}
@Override
public ImmutableList<Path> getClasspathEntries() throws Exception {
return ImmutableList.copyOf(cachedData.get().classpathEntries);
}
@Override
public ImmutableList<String> getClasspathResources() {
return ImmutableList.copyOf(cachedData.get().classpathResources);
}
public boolean isCached() {
return cacheFile != null && cacheFile.exists();
}
private synchronized ClasspathData loadCachedData() {
if (cacheFile != null && cacheFile.exists()) {
try {
JSONObject json = new JSONObject(new JSONTokener(new FileInputStream(cacheFile)));
String name = json.getString(NAME_PROPERTY);
JSONArray classpathEntriesJson = json.optJSONArray(CLASSPATH_ENTRIES_PROPERTY);
JSONArray classpathResourcesJson = json.optJSONArray(CLASSPATH_RESOURCES_PROPERTY);
String outputFolderStr = json.optString(OUTPUT_FOLDER_PROPERTY);
return new ClasspathData(
name,
classpathEntriesJson == null ? Collections.emptySet() : classpathEntriesJson.toList().stream()
.filter(o -> o instanceof String)
.map(o -> (String) o)
.map(s -> new File(s).toPath())
.collect(Collectors.toSet()),
classpathResourcesJson == null ? Collections.emptySet() : classpathResourcesJson.toList().stream()
.filter(o -> o instanceof String)
.map(o -> (String) o)
.collect(Collectors.toSet()),
outputFolderStr == null ? null : new File(outputFolderStr).toPath()
);
} catch (Throwable e) {
Log.log(e);
}
}
return null;
}
private ClasspathData init() {
ClasspathData data = loadCachedData();
return data == null ? new ClasspathData(null, Collections.emptySet(), Collections.emptySet(), null) : data;
}
private synchronized void persistCachedData(ClasspathData data) {
if (cacheFile != null && data != null) {
FileWriter writer = null;
try {
Files.createDirectories(cacheFile.getParentFile().toPath());
JSONObject json = new JSONObject();
json.put(NAME_PROPERTY, data.name);
json.put(CLASSPATH_ENTRIES_PROPERTY, data.classpathEntries.stream().map(e -> e.toString()).collect(Collectors.toList()));
json.put(CLASSPATH_RESOURCES_PROPERTY, data.classpathResources);
json.put(OUTPUT_FOLDER_PROPERTY, data.outputFolder);
writer = new FileWriter(cacheFile);
json.write(writer);
} catch (IOException e) {
Log.log(e);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
Log.log(e);
}
}
}
}
}
public boolean update() {
final ClasspathData newData = createClasspathData();
if (!Objects.equal(cachedData.get(), newData)) {
cachedData.set(newData);
persistCachedData(newData);
return true;
}
return false;
}
@Override
public boolean exists() {
return cachedDelegate.get().exists();
}
@Override
public IType findType(String fqName) {
return cachedDelegate.get().findType(fqName);
}
@Override
public Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter) {
return cachedDelegate.get().fuzzySearchTypes(searchTerm, typeFilter);
}
@Override
public Flux<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
return cachedDelegate.get().fuzzySearchPackages(searchTerm);
}
@Override
public Flux<IType> allSubtypesOf(IType type) {
return cachedDelegate.get().allSubtypesOf(type);
}
protected ClasspathData createClasspathData() {
T newDelegate = delegateCreator.get();
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());
}
}
}

View File

@@ -12,7 +12,8 @@ package org.springframework.ide.vscode.commons.java;
import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.util.function.Tuple2;
@@ -46,12 +47,12 @@ public interface IClasspath {
* @return collection of classpath entries in a form file/folder paths
* @throws Exception
*/
Stream<Path> getClasspathEntries() throws Exception;
ImmutableList<Path> getClasspathEntries() throws Exception;
/**
* Classpath resources paths relative to the source folder path
* @return classpath resource relative paths
*/
Stream<String> getClasspathResources();
ImmutableList<String> getClasspathResources();
}

View File

@@ -14,6 +14,8 @@ import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
public interface IJavaProject extends IJavaElement {
final static String PROJECT_CACHE_FOLDER = ".sts4-cache";
IClasspath getClasspath();
@Override

View File

@@ -11,14 +11,16 @@
package org.springframework.ide.vscode.commons.languageserver.java;
import java.io.File;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.FileObserver;
/**
* Cache fo java projects. The key for the cache is a "project" specific file
* Cache for java projects. The key for the cache is a "project" specific file
*
* @author Alex Boyko
*
@@ -28,19 +30,26 @@ public abstract class AbstractFileToProjectCache<P extends IJavaProject> extends
private String changeSubscription;
private String deleteSubscription;
public AbstractFileToProjectCache(FileObserver fileObserver) {
protected boolean asyncUpdate;
protected final Path projectCacheFolder;
private boolean alwaysFireEventOnFileChanged;
public AbstractFileToProjectCache(FileObserver fileObserver, boolean asyncUpdate, Path projectCacheFolder) {
super(fileObserver);
this.projectCacheFolder = projectCacheFolder;
this.asyncUpdate = asyncUpdate;
}
final public void setAlwaysFireEventOnFileChanged(boolean alwaysFireEventOnFileChanged) {
this.alwaysFireEventOnFileChanged = alwaysFireEventOnFileChanged;
}
@Override
protected void attachListeners(File file, P project) {
super.attachListeners(file, project);
List<String> globPattern = Arrays.asList(file.toString());
changeSubscription = getFileObserver().onFileChanged(globPattern, (uri) -> {
update(project);
notifyProjectChanged(project);
});
changeSubscription = getFileObserver().onFileChanged(globPattern, (uri) -> performUpdate(project, asyncUpdate));
deleteSubscription = getFileObserver().onFileDeleted(globPattern, (uri) -> {
cache.invalidate(file);
notifyProjectDeleted(project);
@@ -49,6 +58,20 @@ public abstract class AbstractFileToProjectCache<P extends IJavaProject> extends
});
}
abstract protected void update(P project);
final protected void performUpdate(P project, boolean async) {
if (async) {
CompletableFuture.supplyAsync(() -> update(project)).thenAccept((changed) -> {
if (changed || alwaysFireEventOnFileChanged) {
notifyProjectChanged(project);
}
});
} else {
if (update(project) || alwaysFireEventOnFileChanged) {
notifyProjectChanged(project);
}
}
}
abstract protected boolean update(P project);
}

View File

@@ -11,8 +11,10 @@
package org.springframework.ide.vscode.commons.maven.java;
import java.io.File;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
import org.springframework.ide.vscode.commons.maven.MavenCore;
/**
@@ -21,21 +23,29 @@ import org.springframework.ide.vscode.commons.maven.MavenCore;
* @author Alex Boyko
*
*/
public class MavenJavaProject implements IJavaProject {
private MavenProjectClasspath classpath;
public MavenJavaProject(MavenCore maven, File pom) {
this.classpath = new MavenProjectClasspath(maven, pom);
public class MavenJavaProject extends AbstractJavaProject {
private DelegatingCachedClasspath<MavenProjectClasspath> classpath;
public MavenJavaProject(MavenCore maven, File pom, Path projectDataCache) {
super(projectDataCache);
this.classpath = new DelegatingCachedClasspath<>(
() -> new MavenProjectClasspath(maven, pom),
projectDataCache == null ? null : projectDataCache.resolve(DelegatingCachedClasspath.CLASSPATH_DATA_CACHE_FILE).toFile()
);
}
public MavenJavaProject(MavenCore maven, File pom) {
this(maven, pom, null);
}
@Override
public MavenProjectClasspath getClasspath() {
public DelegatingCachedClasspath<MavenProjectClasspath> getClasspath() {
return classpath;
}
void update(MavenCore maven) {
this.classpath = new MavenProjectClasspath(maven, classpath.getPomFile());
boolean update() {
return classpath.update();
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.maven.java;
import java.io.File;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
import org.springframework.ide.vscode.commons.maven.MavenCore;
@@ -25,19 +26,24 @@ public class MavenProjectCache extends AbstractFileToProjectCache<MavenJavaProje
private MavenCore maven;
public MavenProjectCache(FileObserver fileObserver, MavenCore maven) {
super(fileObserver);
public MavenProjectCache(FileObserver fileObserver, MavenCore maven, boolean asyncUpdate, Path projectCacheFolder) {
super(fileObserver, asyncUpdate, projectCacheFolder);
this.maven = maven;
}
@Override
protected void update(MavenJavaProject project) {
project.update(maven);
protected boolean update(MavenJavaProject project) {
return project.update();
}
@Override
protected MavenJavaProject createProject(File pomFile) throws Exception {
return new MavenJavaProject(maven, pomFile);
MavenJavaProject mavenJavaProject = new MavenJavaProject(maven, pomFile,
projectCacheFolder == null ? null : pomFile.getParentFile().toPath().resolve(projectCacheFolder));
if (mavenJavaProject.getClasspath().isCached()) {
performUpdate(mavenJavaProject, asyncUpdate);
}
return mavenJavaProject;
}
}

View File

@@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.artifact.Artifact;
@@ -36,6 +37,7 @@ 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;
/**
* Classpath for a maven project
@@ -81,6 +83,10 @@ public class MavenProjectClasspath extends JandexClasspath {
return pom;
}
MavenCore maven() {
return maven;
}
public boolean exists() {
return pom.exists();
}
@@ -91,11 +97,13 @@ public class MavenProjectClasspath extends JandexClasspath {
}
@Override
public Stream<Path> getClasspathEntries() throws Exception {
public ImmutableList<Path> getClasspathEntries() throws Exception {
// return Stream.concat(maven.resolveDependencies(project, null).stream().map(artifact -> {
// return artifact.getFile().toPath();
// }), projectResolvedOutput());
return Stream.concat(projectDependencies().stream().map(a -> a.getFile().toPath()), projectOutput().stream().map(f -> f.toPath()));
ImmutableList<Path> classpathEntries = ImmutableList.copyOf(Stream.concat(projectDependencies().stream().map(a -> a.getFile().toPath()),
projectOutput().stream().map(f -> f.toPath())).collect(Collectors.toList()));
return classpathEntries;
}
private Set<Artifact> projectDependencies() {
@@ -123,12 +131,12 @@ public class MavenProjectClasspath extends JandexClasspath {
}
@Override
public Stream<String> getClasspathResources() {
public ImmutableList<String> getClasspathResources() {
MavenProject project = projectSupplier.get();
if (project == null) {
return Stream.empty();
return ImmutableList.of();
}
return project.getBuild().getResources().stream().flatMap(resource -> {
return ImmutableList.copyOf(project.getBuild().getResources().stream().filter(resource -> new File(resource.getDirectory()).exists()).flatMap(resource -> {
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir(resource.getDirectory());
if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) {
@@ -140,7 +148,7 @@ public class MavenProjectClasspath extends JandexClasspath {
scanner.setCaseSensitive(false);
scanner.scan();
return Arrays.stream(scanner.getIncludedFiles());
});
}).toArray(String[]::new));
}
/*
@@ -253,4 +261,20 @@ public class MavenProjectClasspath extends JandexClasspath {
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MavenProjectClasspath) {
MavenProjectClasspath other = (MavenProjectClasspath) obj;
try {
if (pom.equals(other.pom)
&& projectSupplier.get().equals(other.projectSupplier.get())) {
return super.equals(obj);
}
} catch (Throwable t) {
Log.log(t);
}
}
return false;
}
}

View File

@@ -13,12 +13,15 @@ package org.springframework.ide.vscode.commons.maven.java.classpathfile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.util.function.Tuple2;
@@ -37,15 +40,15 @@ public class FileClasspath implements IClasspath {
}
@Override
public Stream<Path> getClasspathEntries() throws Exception {
return Stream.concat(MavenCore.readClassPathFile(classpathFilePath),
public ImmutableList<Path> getClasspathEntries() throws Exception {
return ImmutableList.copyOf(Stream.concat(MavenCore.readClassPathFile(classpathFilePath),
Stream.of(classpathFilePath.getParent().resolve("target/classes"),
classpathFilePath.getParent().resolve("target/test-classes")));
classpathFilePath.getParent().resolve("target/test-classes"))).collect(Collectors.toList()));
}
@Override
public Stream<String> getClasspathResources() {
return Stream.empty();
public ImmutableList<String> getClasspathResources() {
return ImmutableList.of();
}
@Override

View File

@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.commons.maven.java.classpathfile;
import java.io.File;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IJavaProject;
@@ -67,8 +68,18 @@ public class JavaProjectWithClasspathFile implements IJavaProject {
return true;
}
void update() {
this.classpath = new FileClasspath(Paths.get(cpFile.toURI()));
CompletableFuture<Boolean> update() {
return CompletableFuture.supplyAsync(() -> doUpdate());
}
private synchronized boolean doUpdate() {
FileClasspath newClasspath = new FileClasspath(Paths.get(cpFile.toURI()));
if (newClasspath.equals(classpath)) {
return false;
} else {
this.classpath = newClasspath;
return true;
}
}
}

View File

@@ -18,12 +18,13 @@ import org.springframework.ide.vscode.commons.util.FileObserver;
public class JavaProjectWithClasspathFileCache extends AbstractFileToProjectCache<JavaProjectWithClasspathFile> {
public JavaProjectWithClasspathFileCache(FileObserver fileObserver) {
super(fileObserver);
super(fileObserver, false, null);
}
@Override
protected void update(JavaProjectWithClasspathFile project) {
project.update();
protected boolean update(JavaProjectWithClasspathFile project) {
project.update();
return true;
}
@Override

View File

@@ -12,11 +12,16 @@ 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;
@@ -24,6 +29,8 @@ 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}
*
@@ -31,38 +38,66 @@ import org.springframework.ide.vscode.commons.util.BasicFileObserver;
*
*/
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();
BasicFileObserver fileObserver = new BasicFileObserver();
MavenProjectCache cache = new MavenProjectCache(fileObserver, MavenCore.getDefault());
IJavaProject[] projectChanged = new IJavaProject[] { null };
IJavaProject[] projectDeleted = new IJavaProject[] { null };
cache.addListener(new Listener() {
@Override
public void created(IJavaProject project) {}
String pomFileContents = Files.contentOf(pomFile, Charset.defaultCharset());
@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);
fileObserver.notifyFileChanged(pomFile.toURI().toString());
assertEquals(cachedProject, projectChanged[0]);
fileObserver.notifyFileDeleted(pomFile.toURI().toString());
assertEquals(cachedProject, projectDeleted[0]);
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);
}
}

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>empty-boot-1.4.0-web-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>empty-boot-project-with-classpath-file</name>
<description>Empty Boot project generating classpath.txt file</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFile>classpath.txt</outputFile>
</configuration>
</plugin>
</plugins>
</build>
</project>