From ed4eef0e0ad95f8abef559ed7455f50dbecfc4c0 Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 15 Nov 2022 18:52:32 -0500 Subject: [PATCH] Project build data via classpath listener from JDT --- .../commons/gradle/GradleJavaProject.java | 18 ++++--- .../commons/gradle/GradleProjectCache.java | 4 +- .../commons/gradle/GradleProjectTest.java | 4 +- .../commons/java/AbstractJavaProject.java | 11 +++- .../ide/vscode/commons/java/IJavaProject.java | 3 +- .../vscode/commons/java/IProjectBuild.java | 36 +++++++++++++ .../ide/vscode/commons/java/JavaProject.java | 6 +-- .../vscode/commons/java/JdtLsJavaProject.java | 6 +-- .../commons/java/LegacyJavaProject.java | 6 +-- .../java/ls/ClasspathListener.java | 12 +++-- .../java/ls/ClasspathListenerManager.java | 21 ++++++-- .../commons/protocol/java/ProjectBuild.java | 48 +++++++++++++++++ .../commons/maven/java/MavenJavaProject.java | 6 ++- .../ls/commons/classpath/ClasspathUtil.java | 29 ++++++++++- .../SendClasspathNotificationsJob.java | 11 ++-- .../vscode/boot/app/BootVersionValidator.java | 52 ++++++++++--------- .../BootJavaLanguageServerComponents.java | 3 +- .../handlers/BootJavaReconcileEngine.java | 9 +++- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 13 +++-- .../ProjectVersionDiagnosticProvider.java | 26 +--------- .../generations/VersionValidators.java | 3 +- .../boot/java/value/test/MockProjects.java | 8 ++- .../ValueSpelExpressionValidationTest.java | 4 +- .../project/harness/ProjectsHarness.java | 4 +- 24 files changed, 242 insertions(+), 101 deletions(-) create mode 100644 headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IProjectBuild.java create mode 100644 headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/ProjectBuild.java diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java index 1df094b4b..3d1c06c41 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2018 Pivotal, Inc. + * Copyright (c) 2017, 2022 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 @@ -18,8 +18,10 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IProjectBuild; import org.springframework.ide.vscode.commons.java.LegacyJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavadocService; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.ide.vscode.commons.util.FileObserver; /** @@ -32,24 +34,24 @@ public class GradleJavaProject extends LegacyJavaProject { private static final Logger log = LoggerFactory.getLogger(GradleJavaProject.class); - private GradleJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File projectDir, JavadocService javadocService) { - super(fileObserver, projectDir.toURI(), projectDataCache, classpath, javadocService); + private GradleJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File gradleFile, JavadocService javadocService) { + super(fileObserver, gradleFile.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.GRADLE_PROJECT_TYPE, gradleFile.toURI())); } - public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File projectDir, Path projectDataCache, JavadocService javadocService) { + public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File gradleFile, Path projectDataCache, JavadocService javadocService) { File file = projectDataCache == null ? null : projectDataCache.resolve(ClasspathFileBasedCache.CLASSPATH_DATA_CACHE_FILE).toFile(); ClasspathFileBasedCache fileBasedCache = new ClasspathFileBasedCache(file); IClasspath classpath = new DelegatingCachedClasspath( - () -> new GradleProjectClasspath(gradle, projectDir), + () -> new GradleProjectClasspath(gradle, gradleFile.getParentFile()), fileBasedCache ); - return new GradleJavaProject(fileObserver, projectDataCache, classpath, projectDir, javadocService); + return new GradleJavaProject(fileObserver, projectDataCache, classpath, gradleFile, javadocService); } - public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File projectDir, JavadocService javadocService) { - GradleJavaProject thiss = create(fileObserver, gradle, projectDir, null, javadocService); + public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File gradleFile, JavadocService javadocService) { + GradleJavaProject thiss = create(fileObserver, gradle, gradleFile, null, javadocService); if (!thiss.getClasspath().isCached()) { try { thiss.getClasspath().update(); diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java index ada29cd05..c90993e51 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2018 Pivotal, Inc. + * Copyright (c) 2017, 2022 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 @@ -51,7 +51,7 @@ public class GradleProjectCache extends AbstractFileToProjectCache JavaDocProviders.createFor(cpe)); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java index e60fc0db2..d365b3f9d 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Pivotal, Inc. + * Copyright (c) 2019, 2022 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,12 +19,14 @@ public abstract class AbstractJavaProject implements IJavaProject, Disposable { private final IClasspath classpath; private final URI uri; + private final IProjectBuild projectBuild; private ClasspathIndex index; - public AbstractJavaProject(URI uri, IClasspath classpath) { + public AbstractJavaProject(URI uri, IClasspath classpath, IProjectBuild projectBuild) { this.uri = uri; this.classpath = classpath; + this.projectBuild = projectBuild; } @Override public IClasspath getClasspath() { @@ -67,5 +69,10 @@ public abstract class AbstractJavaProject implements IJavaProject, Disposable { public String toString() { return "JavaProject("+uri+")"; } + + @Override + public IProjectBuild getProjectBuild() { + return projectBuild; + } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java index 0f04929b8..857554e60 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2019 Pivotal, Inc. + * Copyright (c) 2017, 2022 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 @@ -17,6 +17,7 @@ public interface IJavaProject { final static String PROJECT_CACHE_FOLDER = ".sts4-cache"; IClasspath getClasspath(); + IProjectBuild getProjectBuild(); ClasspathIndex getIndex(); URI getLocationUri(); boolean exists(); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IProjectBuild.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IProjectBuild.java new file mode 100644 index 000000000..5c73235c5 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IProjectBuild.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.java; + +import java.net.URI; + +public interface IProjectBuild { + + String getType(); + + URI getBuildFile(); + + static IProjectBuild create(String type, URI buildFile) { + return new IProjectBuild() { + + @Override + public String getType() { + return type; + } + + @Override + public URI getBuildFile() { + return buildFile; + } + }; + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java index c394cb401..24bdd293b 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2019 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -23,8 +23,8 @@ public class JavaProject extends AbstractJavaProject { private final FileObserver fileObserver; private final JavadocProviderFactory javadocProviderFactory; - public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath, JavadocService javadocService) { - super(uri, classpath); + public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath, JavadocService javadocService, IProjectBuild projectBuild) { + super(uri, classpath, projectBuild); this.fileObserver = fileObserver; this.javadocProviderFactory = (classpathResource) -> { CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, classpathResource); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java index 6e6f1ad2f..a9176b2c3 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 Pivotal, Inc. + * Copyright (c) 2019, 2022 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 @@ -21,8 +21,8 @@ public class JdtLsJavaProject extends AbstractJavaProject { final private STS4LanguageClient client; final private ProjectObserver projectObserver; - public JdtLsJavaProject(STS4LanguageClient client, URI uri, IClasspath classpath, ProjectObserver projectObserver) { - super(uri, classpath); + public JdtLsJavaProject(STS4LanguageClient client, URI uri, IClasspath classpath, ProjectObserver projectObserver, IProjectBuild projectBuild) { + super(uri, classpath, projectBuild); this.client = client; this.projectObserver = projectObserver; } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java index abaf791a1..d13c48f48 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/LegacyJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2018 Pivotal, Inc. + * Copyright (c) 2017, 2022 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 @@ -29,8 +29,8 @@ public class LegacyJavaProject extends JavaProject { final protected Path projectDataCache; - public LegacyJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath, JavadocService javadocService) { - super(fileObserver, loactionUri, classpath, javadocService); + public LegacyJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath, JavadocService javadocService, IProjectBuild projectBuild) { + super(fileObserver, loactionUri, classpath, javadocService, projectBuild); this.projectDataCache = projectDataCache; } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListener.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListener.java index 1ebf9240f..c613834bb 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListener.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListener.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2019 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.commons.languageserver.java.ls; import org.springframework.ide.vscode.commons.protocol.java.Classpath; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; public interface ClasspathListener { @@ -22,22 +23,23 @@ public interface ClasspathListener { public final String name; public final boolean deleted; public final Classpath classpath; + public final ProjectBuild projectBuild; - public Event(String projectUri, String name, boolean deleted, Classpath classpath) { + public Event(String projectUri, String name, boolean deleted, Classpath classpath, ProjectBuild projectBuild) { super(); this.projectUri = projectUri; this.name = name; this.deleted = deleted; this.classpath = classpath; + this.projectBuild = projectBuild; } @Override public String toString() { return "Event [projectUri=" + projectUri + ", name=" + name + ", deleted=" + deleted + ", classpath=" - + classpath + "]"; + + classpath + ", projectBuild=" + projectBuild + "]"; } + } - - } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListenerManager.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListenerManager.java index 090cd2bdf..ea98d3626 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListenerManager.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ls/ClasspathListenerManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2019 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.protocol.java.Classpath; import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.ide.vscode.commons.util.AsyncRunner; import com.google.common.collect.ImmutableList; @@ -73,7 +74,14 @@ public class ClasspathListenerManager { Classpath classpath = gson.fromJson((JsonElement)event.get(3), Classpath.class); log.debug("classpath = {}", classpath); - classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath)); + + ProjectBuild projectBuild = null; + if (event.size() > 4) { + projectBuild = gson.fromJson((JsonElement)event.get(4), ProjectBuild.class); + log.debug("projectBuild = {}", projectBuild); + } + + classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath, projectBuild)); } } else { //Still support non-batched events for backwards compatibility with clients @@ -88,7 +96,14 @@ public class ClasspathListenerManager { Classpath classpath = gson.fromJson((JsonElement)args.get(3), Classpath.class); log.debug("classpath = {}", classpath); - classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath)); + + ProjectBuild projectBuild = null; + if (args.size() > 4) { + projectBuild = gson.fromJson((JsonElement)args.get(4), ProjectBuild.class); + log.debug("projectBuild = {}", projectBuild); + } + + classpathListener.changed(new ClasspathListener.Event(projectUri, name, deleted, classpath, projectBuild)); } return "done"; })); diff --git a/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/ProjectBuild.java b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/ProjectBuild.java new file mode 100644 index 000000000..c2caa2a7b --- /dev/null +++ b/headless-services/commons/commons-lsp-extensions/src/main/java/org/springframework/ide/vscode/commons/protocol/java/ProjectBuild.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2022 VMware, 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VMware, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.protocol.java; + +public class ProjectBuild { + + public static final String MAVEN_PROJECT_TYPE = "maven"; + public static final String GRADLE_PROJECT_TYPE = "gradle"; + + private String type; + + private String buildFile; + + public ProjectBuild(String type, String buildFile) { + this.type = type; + this.buildFile = buildFile; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getBuildFile() { + return buildFile; + } + + public void setBuildFile(String buildFile) { + this.buildFile = buildFile; + } + + @Override + public String toString() { + return "ProjectBuild [type=" + type + ", buildFile=" + buildFile + "]"; + } + +} diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index bdf8888d0..51c9dfc6f 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 Pivotal, Inc. + * Copyright (c) 2016, 2022 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 @@ -18,9 +18,11 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IProjectBuild; import org.springframework.ide.vscode.commons.java.LegacyJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavadocService; import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.ide.vscode.commons.util.FileObserver; /** @@ -36,7 +38,7 @@ public class MavenJavaProject extends LegacyJavaProject { private final File pom; private MavenJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File pom, JavadocService javadocService) { - super(fileObserver, pom.getParentFile().toURI(), projectDataCache, classpath, javadocService); + super(fileObserver, pom.getParentFile().toURI(), projectDataCache, classpath, javadocService, IProjectBuild.create(ProjectBuild.MAVEN_PROJECT_TYPE, pom.toURI())); this.pom = pom; } diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java index 81284decf..8e627be08 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2019 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -21,6 +21,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; @@ -34,12 +35,15 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.JavaProject; import org.springframework.ide.vscode.commons.protocol.java.Classpath; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.tooling.jdt.ls.commons.Logger; public class ClasspathUtil { private static final Object JRE_CONTAINER_ID = "org.eclipse.jdt.launching.JRE_CONTAINER"; - + private static final String GRADLE_CONTAINER_ID = "org.eclipse.buildship.core.gradleclasspathcontainer"; + private static final String MAVEN_CONTAINER_ID = "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"; + private static Set getSystemLibraryPaths(IJavaProject javaProject) { try { IClasspathEntry jreContainer = getJreContainer(javaProject.getRawClasspath()); @@ -213,4 +217,25 @@ public class ClasspathUtil { return "unknown: " + entry.getContentKind(); } } + + public static ProjectBuild createProjectBuild(IJavaProject jp) { + try { + for (IClasspathEntry e : jp.getRawClasspath()) { + switch (e.getPath().segment(0)) { + case MAVEN_CONTAINER_ID: + IFile f = jp.getProject().getFile("pom.xml"); + return new ProjectBuild(ProjectBuild.MAVEN_PROJECT_TYPE, f.exists() ? f.getLocationURI().toString() : null); + case GRADLE_CONTAINER_ID: + IFile g = jp.getProject().getFile("build.gradle"); + if (!g.exists()) { + g = jp.getProject().getFile("build.gradle.kts"); + } + return new ProjectBuild(ProjectBuild.GRADLE_PROJECT_TYPE, g.exists() ? g.getLocationURI().toString() : null); + } + } + } catch (JavaModelException e) { + // ignore + } + return new ProjectBuild(null, null); + } } diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java index a338b4701..b990b75c4 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/SendClasspathNotificationsJob.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2020 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -28,6 +28,7 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.springframework.ide.vscode.commons.protocol.java.Classpath; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.tooling.jdt.ls.commons.Logger; import com.google.common.collect.ImmutableList; @@ -142,7 +143,7 @@ public class SendClasspathNotificationsJob extends Job { logger.log(e); } } - bufferMessage(projectLoc, deleted, projectName, classpath); + bufferMessage(projectLoc, deleted, projectName, classpath, ClasspathUtil.createProjectBuild(jp)); } } flush(); @@ -153,14 +154,14 @@ public class SendClasspathNotificationsJob extends Job { } } - protected void bufferMessage(URI projectLoc, boolean deleted, String projectName, Classpath classpath) { + protected void bufferMessage(URI projectLoc, boolean deleted, String projectName, Classpath classpath, ProjectBuild projectBuild) { if (buffer!=null) { logger.debug("buffering callback "+callbackCommandId+" "+projectName+" "+deleted+" "+ classpath.getEntries().size()); - buffer.add(ImmutableList.of(projectLoc.toString(), projectName, deleted, classpath)); + buffer.add(ImmutableList.of(projectLoc.toString(), projectName, deleted, classpath, projectBuild)); } else { try { logger.debug("executing callback "+callbackCommandId+" "+projectName+" "+deleted+" "+ classpath.getEntries().size()); - Object r = conn.executeClientCommand(callbackCommandId, projectLoc.toString(), projectName, deleted, classpath); + Object r = conn.executeClientCommand(callbackCommandId, projectLoc.toString(), projectName, deleted, classpath, projectBuild); notificationsSentForProjects = ImmutableList.of(projectName); logger.debug("executing callback "+callbackCommandId+" SUCCESS ["+r+"]"); } catch (Exception e) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java index 2c235d095..10ad95a41 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidator.java @@ -29,9 +29,10 @@ import org.springframework.stereotype.Component; public class BootVersionValidator { private static final Logger log = LoggerFactory.getLogger(BootVersionValidator.class); - + private SimpleLanguageServer server; + public BootVersionValidator(SimpleLanguageServer server, ProjectObserver observer) { - + this.server = server; observer.addListener(new ProjectObserver.Listener() { @Override @@ -40,28 +41,7 @@ public class BootVersionValidator { @Override public void created(IJavaProject project) { - VersionValidationPreferences preferences = new VersionValidationPreferences(); - - String url = getSpringProjectsUrl(preferences); - SpringProjectsClient client = new SpringProjectsClient(url); - SpringProjectsProvider provider = new SpringIoProjectsProvider(client); - VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider()); - - ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(provider, - validators); - - try { - DiagnosticResult result = diagnosticProvider.getDiagnostics(project); - if (result != null && !result.getDiagnostics().isEmpty()) { - server.getTextDocumentService().publishDiagnostics( - new TextDocumentIdentifier(result.getDocumentUri().toString()), - result.getDiagnostics()); - - } - } catch (Exception e) { - log.error("Failed validating Spring Project version", e); - } - + validate(project); } @Override @@ -70,6 +50,30 @@ public class BootVersionValidator { } }); } + + public void validate(IJavaProject project) { + VersionValidationPreferences preferences = new VersionValidationPreferences(); + + String url = getSpringProjectsUrl(preferences); + SpringProjectsClient client = new SpringProjectsClient(url); + SpringProjectsProvider provider = new SpringIoProjectsProvider(client); + VersionValidators validators = new VersionValidators(server.getDiagnosticSeverityProvider()); + + ProjectVersionDiagnosticProvider diagnosticProvider = new ProjectVersionDiagnosticProvider(provider, + validators); + + try { + DiagnosticResult result = diagnosticProvider.getDiagnostics(project); + if (result != null && !result.getDiagnostics().isEmpty()) { + server.getTextDocumentService().publishDiagnostics( + new TextDocumentIdentifier(result.getDocumentUri().toString()), + result.getDiagnostics()); + + } + } catch (Exception e) { + log.error("Failed validating Spring Project version", e); + } + } private String getSpringProjectsUrl(VersionValidationPreferences preferences) { return preferences.getSpringProjectsUrl(); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index 68e353e78..bfc776164 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.app.BootLanguageServerParams; +import org.springframework.ide.vscode.boot.app.BootVersionValidator; import org.springframework.ide.vscode.boot.app.SpringSymbolIndex; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup; import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; @@ -201,7 +202,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] { jdtReconciler, rewriteJavaReconciler - }, documents); + }, documents, appContext.getBean(BootVersionValidator.class)); codeActionProvider = new BootJavaCodeActionProvider( projectFinder, diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java index b1cc3d250..0c1405e78 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java @@ -24,6 +24,7 @@ import java.util.stream.Stream; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.app.BootVersionValidator; import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler; import org.springframework.ide.vscode.commons.java.IClasspathUtil; @@ -48,11 +49,14 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe private final SimpleTextDocumentService documents; private final JavaProjectFinder projectFinder; private JavaReconciler[] javaReconcilers; + + private BootVersionValidator bootVersionValidator; - public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers, SimpleTextDocumentService documents) { + public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers, SimpleTextDocumentService documents, BootVersionValidator bootVersionValidator) { this.documents = documents; this.projectFinder = projectFinder; this.javaReconcilers = javaReconcilers; + this.bootVersionValidator = bootVersionValidator; } @Override @@ -113,6 +117,9 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe @Override public void reconcile(IJavaProject project, Function problemCollectorFactory) { + if (bootVersionValidator != null) { + bootVersionValidator.validate(project); + } Stream files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { return Files.walk(folder.toPath()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 39aaed25d..4d4a46f84 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2020 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.ClasspathData; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.java.IProjectBuild; import org.springframework.ide.vscode.commons.java.JavaProject; import org.springframework.ide.vscode.commons.java.JdtLsJavaProject; import org.springframework.ide.vscode.commons.javadoc.JdtLsJavadocProvider; @@ -41,6 +42,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.ls.ClasspathLi import org.springframework.ide.vscode.commons.languageserver.util.ServerCapabilityInitializer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.UriUtil; @@ -346,10 +348,11 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService, Serv log.debug("deleted = false"); URI projectUri = new URI(uri); ClasspathData classpath = new ClasspathData(event.name, event.classpath.getEntries()); + IProjectBuild projectBuild = from(event.projectBuild); IJavaProject newProject = IS_JANDEX_INDEX ? new JavaProject(getFileObserver(), projectUri, classpath, - JdtLsProjectCache.this) - : new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this); + JdtLsProjectCache.this, projectBuild) + : new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this, projectBuild); IJavaProject oldProject = table.put(uri, newProject); if (oldProject != null) { notifyDelete(oldProject); @@ -365,5 +368,9 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService, Serv }); } } + + private static IProjectBuild from(ProjectBuild projectBuild) { + return projectBuild == null ? null : IProjectBuild.create(projectBuild.getType(), projectBuild.getBuildFile() == null ? null : URI.create(projectBuild.getBuildFile())); + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java index 9bc5ebc78..32c89a1c4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/ProjectVersionDiagnosticProvider.java @@ -12,12 +12,8 @@ package org.springframework.ide.vscode.boot.validation.generations; import java.io.File; import java.net.URI; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import org.eclipse.lsp4j.Diagnostic; import org.springframework.ide.vscode.boot.validation.generations.json.Generation; @@ -26,12 +22,8 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.java.Version; -import com.google.common.collect.ImmutableSet; - public class ProjectVersionDiagnosticProvider { - private static final String[] BUILD_FILES = new String[] { "pom.xml", "build.gradle", "build.gradle.kts" }; - private final SpringProjectsProvider provider; private final VersionValidators validators; @@ -43,7 +35,7 @@ public class ProjectVersionDiagnosticProvider { public DiagnosticResult getDiagnostics(IJavaProject javaProject) throws Exception { - URI buildFileUri = getBuildFileUri(javaProject); + URI buildFileUri = javaProject.getProjectBuild() == null ? null : javaProject.getProjectBuild().getBuildFile(); if (buildFileUri == null) { throw new Exception("Unable to find build file in project while computing version validation for: "); } @@ -91,22 +83,6 @@ public class ProjectVersionDiagnosticProvider { return null; } - protected URI getBuildFileUri(IJavaProject javaProject) throws Exception { - File buildFile = null; - Path projectPath = new File(javaProject.getLocationUri()).toPath(); - if (Files.isDirectory(projectPath, new LinkOption[] { LinkOption.NOFOLLOW_LINKS })) { - ImmutableSet buildFileNames = ImmutableSet.copyOf(BUILD_FILES); - - List results = Files.list(projectPath).filter(Files::isRegularFile) - .filter(file -> buildFileNames.contains(file.toFile().getName())).collect(Collectors.toList()); - - if (results != null && results.size() == 1) { - buildFile = results.get(0).toFile(); - } - } - return buildFile != null ? buildFile.toURI() : null; - } - protected File getSpringBootDependency(IJavaProject project) { List libs = SpringProjectUtil.getLibrariesOnClasspath(project, "spring-boot"); return libs != null && libs.size() > 0 ? libs.get(0) : null; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java index 59fbb5b14..c16b8b176 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/VersionValidators.java @@ -17,6 +17,7 @@ import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; +import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.generations.json.Generation; import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; @@ -157,7 +158,7 @@ public class VersionValidators { CodeAction ca = new CodeAction(); ca.setKind(CodeActionKind.QuickFix); ca.setTitle("Upgrade To Target Version"); - String commandId = "sts/upgrade/spring-boot"; + String commandId = SpringBootUpgrade.CMD_UPGRADE_SPRING_BOOT; ca.setCommand(new Command("Upgrade To Target Version", commandId, ImmutableList.of(javaProject.getLocationUri().toString(), latest.toString()))); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java index 69111014c..9ff953839 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/MockProjects.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2020 Pivotal, Inc. + * Copyright (c) 2018, 2022 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 @@ -31,6 +31,7 @@ import org.junit.Assert; import org.springframework.ide.vscode.commons.java.ClasspathIndex; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IProjectBuild; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; @@ -185,6 +186,11 @@ public class MockProjects { public String uri(String projectRelativePath) { return new File(root, projectRelativePath).toURI().toString(); } + + @Override + public IProjectBuild getProjectBuild() { + return null; + } } public class MockProjectObserver implements ProjectObserver { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java index 5055f36e9..8de13e040 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/value/test/ValueSpelExpressionValidationTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2021 Pivotal, Inc. + * Copyright (c) 2020, 2022 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 @@ -147,7 +147,7 @@ public class ValueSpelExpressionValidationTest { problemCollector = new TestProblemCollector(); reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] { new JdtReconciler(compilationUnitCache) - }, server.getTextDocumentService()); + }, server.getTextDocumentService(), null); } @After diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index 43712362f..2d5b149d9 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 Pivotal, Inc. + * Copyright (c) 2016, 2022 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 @@ -91,7 +91,7 @@ public class ProjectsHarness { public static final IJavaProject dummyProject() throws URISyntaxException { return new LegacyJavaProject(new BasicFileObserver(), new URI("file:///someplace/nonexistent"), null, - new DelegatingCachedClasspath(() -> null, null), (uri, cpe) -> JavaDocProviders.createFor(cpe)); + new DelegatingCachedClasspath(() -> null, null), (uri, cpe) -> JavaDocProviders.createFor(cpe), null); } private ProjectsHarness(FileObserver fileObserver) {