classpaths = new HashMap<>();
+
+ @Override
+ public synchronized Object executeClientCommand(String id, Object... params) throws Exception {
+ if (id.equals(commandId)) {
+ System.out.println("received: "+Arrays.asList(params));
+ File projectLoc = new File(new URI((String) params[0]));
+ String name = (String) params[1];
+ boolean deleted = (boolean) params[2];
+ if (deleted) {
+ classpaths.remove(projectLoc);
+ } else {
+ Classpath cp = (Classpath) params[3];
+ classpaths.put(projectLoc, new Info(name, cp));
+ }
+ }
+ return "whatever";
+ }
+
+ public synchronized Info getFor(IProject project) {
+ File location = project.getLocation().toFile();
+ return classpaths.get(location);
+ }
+
+ public void dispose() {
+ service.removeClasspathListener(commandId);
+ }
+
+ }
+ private MockClasspathCache classpaths = new MockClasspathCache();
+ private ReusableClasspathListenerHandler service = new ReusableClasspathListenerHandler(classpaths);
+
+ @After
+ public void tearDown() {
+ classpaths.dispose();
+ assertTrue(service.hasNoActiveSubscriptions());
+ }
+
+ @Test
+ public void classpathIsSentForExistingProject() throws Exception {
+ String projectName = "classpath-test-simple-java-project";
+ IProject project = createTestProject(projectName);
+
+ service.addClasspathListener(classpaths.commandId);
+ ACondition.waitFor("Project with classpath to appear", Duration.ofSeconds(5), () -> {
+ Classpath cp = classpaths.getFor(project).classpath;
+ assertTrue(cp.getEntries().stream().filter(cpe -> Classpath.isSource(cpe)).count()==1); //has 1 source entry
+ assertTrue(cp.getEntries().stream().filter(cpe -> Classpath.isBinary(cpe) && cpe.isSystem()).count()>=1); //has some system libraries
+ });
+ }
+
+ private IProject createTestProject(String name) throws Exception {
+ File testProjectLocation = new File(FileLocator.toFileURL(Platform.getBundle("org.springframework.tooling.jdt.ls.commons.test").getEntry("test-projects/"+name)).toURI());
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+ IProjectDescription desc = ResourcesPlugin.getWorkspace().newProjectDescription(null);
+ desc.setName(name);
+ desc.setLocation(Path.fromOSString(testProjectLocation.toString()));
+ project.create(desc, null);
+ project.open(null);
+
+ assertTrue(project.hasNature(JavaCore.NATURE_ID));
+ return project;
+ }
+
+
+}
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/ACondition.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/ACondition.java
new file mode 100644
index 000000000..69a7f96e6
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/ACondition.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * Copyright (c) 2012, 2018 Pivotal Software, 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 Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springsource.ide.eclipse.commons.frameworks.test.util;
+
+import java.time.Duration;
+import java.util.Map;
+
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Abstract class to create a "waitable" condition. This is to be used in implementing
+ * tests where we want some condition to be true eventually, but cannot be sure it will be
+ * true immediately.
+ *
+ * Typical use would like so:
+ *
+ * new ACondition() {
+ * public boolean test() throws Exception {
+ * ... some asserts ...
+ * return ...something to test...;
+ * }
+ * }.waitFor(4000); // wait for 4 seconds or until test passes.
+ *
+ * An exception will be thrown if the test method does not return true before the
+ * timeout limit.
+ *
+ * If the test method throws an exception this is treated the same as returning false.
+ * If condition fails, we will try to rethrow a pertinent exception (typically the
+ * exception thrown by the test method, the last time we tried to run it.
+ *
+ * @author Kris De Volder
+ */
+public abstract class ACondition {
+
+ private String description = null;
+
+ /**
+ * This method is deprecated because it is a common problem that client calls it
+ * and then forgets to call the waitFor method on the created instance. This results
+ * in test code that looks like it is testing something but doesn't because the
+ * asserts inside the ACondition are never actually executed.
+ *
+ * Clients should instead call one of the constructor that takes a timeout
+ * as a parameter (which does the to waitFor automatically).
+ */
+ @Deprecated
+ public ACondition() {
+ }
+
+ /**
+ * This method is deprecated because it is a common problem that client calls it
+ * and then forgets to call the waitFor method on the created instance. This results
+ * in test code that looks like it is testing something but doesn't because the
+ * asserts inside the ACondition are never actually executed.
+ *
+ * Clients should instead call one of the constructor that takes a timeout
+ * as a parameter (which does the to waitFor automatically).
+ */
+ @Deprecated
+ public ACondition(String description) {
+ this.description = description;
+ }
+
+ public ACondition(long timeout) throws Exception {
+ this();
+ waitFor(timeout);
+ }
+
+ public ACondition(String description, long timeout) throws Exception {
+ this(description);
+ waitFor(timeout);
+ }
+
+ Throwable e = null;
+
+ /**
+ * Deprecated. Use one of the constructors that accepts a timeout value and automatically
+ * calls waitFor.
+ */
+ @Deprecated
+ public void waitFor(long timeout) throws Exception {
+ long SLEEP_FOR = Math.min(2000, timeout/5);
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + timeout;
+ boolean result = false;
+ waitForDisplay(); //Make sure this gets called at least once to avoid 'UI thread starvation'.
+ while (!(result = doTest()) && System.currentTimeMillis() < endTime) {
+ waitForDisplay(); // Avoids UI deadlock by allowing UI to process events.
+ try {
+ Thread.sleep(SLEEP_FOR);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (!result) {
+ //Try our best to create a 'nice' exception reflecting the reason for the test failure
+ System.err.println("ACondition "+describe()+" timed out. Dumping current Thread stacks...\n" +
+ getStackDumps()
+ );
+ if (e!=null) {
+ if (e instanceof Exception) {
+ throw (Exception)e;
+ } else if (e instanceof Error) {
+ throw (Error)e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ } else {
+ throw new RuntimeException(getMessage());
+ }
+ }
+ if (description!=null) {
+ System.out.println(description + " succeeded after: " + (System.currentTimeMillis() - startTime));
+ }
+ }
+
+ private String describe() {
+ if (description!=null) {
+ return "["+description+"]";
+ }
+ return "";
+ }
+
+ private boolean doTest() {
+ boolean result = false;
+ try {
+ e = null;
+ result = test();
+ } catch (Throwable e) {
+ this.e = e;
+ }
+ return result;
+ }
+
+ /**
+ * Test something. If the method returns true, the test passes.
+ * If it returns false or throws an exception the test fails (and will be
+ * retried until it passes or timeout is reached).
+ */
+ public abstract boolean test() throws Exception;
+
+ /**
+ * Message used when time out reached without an exception
+ */
+ public String getMessage() {
+ return "timed out";
+ }
+
+ public static void assertJobManagerIdle() {
+ final IJobManager jm = Job.getJobManager();
+ if (jm.isIdle()) {
+ return; //OK!
+ }
+ //Make a nice message listing all the jobs and their present state.
+ Job[] allJobs = jm.find(null);
+ StringBuffer msg = new StringBuffer("JobManager not idle: \n");
+ for (Job job : allJobs) {
+ msg.append(" Job: "+job.getName() + " State: " + stateString(job) +"\n");
+ }
+ throw new AssertionFailedError(msg.toString());
+ }
+
+ public static String stateString(Job job) {
+ int state = job.getState();
+ switch (state) {
+ case Job.RUNNING:
+ return "RUNNING";
+ case Job.SLEEPING:
+ return "SLEEPING";
+ case Job.WAITING:
+ return "WAITING";
+ case Job.NONE:
+ return "NONE";
+ default:
+ return ""+state;
+ }
+ }
+
+ /**
+ * A Java 8 friendlier way of using this class with a lambda.
+ *
+ * Waits for some asserts to pass by periodically checking them until either:
+ * a) the asserts all pass (i.e. asserter returns without throwing); or b)
+ * the timeout is exceeded.
+ */
+ public static void waitFor(String name, Duration timeout, final Asserter asserter) throws Exception {
+ waitFor(name, timeout.toMillis(), asserter);
+ }
+
+ /**
+ * A Java 8 friendlier way of using this class with a lambda.
+ *
+ * Waits for some asserts to pass by periodically checking them until either:
+ * a) the asserts all pass (i.e. asserter returns without throwing); or b)
+ * the timeout is exceeded.
+ */
+ public static void waitFor(String name, long timeout, final Asserter asserter) throws Exception {
+ new ACondition("Wait for "+name, timeout) {
+ @Override
+ public boolean test() throws Exception {
+ asserter.execute();
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Allows Display to process events, so UI can make progress. Tests running
+ * in the UI thread may need to call this to avoid UI deadlocks.
+ *
+ * For convenience, it is allowed to call this method from a non UI thread,
+ * but such calls have no effect.
+ */
+ public static void waitForDisplay() {
+ if (inUIThread()) {
+ try {
+ while (Display.getDefault().readAndDispatch()) {
+ // do nothing
+ }
+ } catch (Throwable e) {
+ // in e 44 this is throwing exceptions... a lot. Log them in case they contain
+ // some valuable hints... but move along. These errors happen because some component
+ // probably unrelated to our tests misbehaved. I suspect GTK3 may be causing
+ // NPEs and other errors in the Eclipse UI code, for example. These errors seem
+ // to propagate out of the 'readAndDispatch' call on e44.
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static boolean inUIThread() {
+ return Display.getDefault().getThread() == Thread.currentThread();
+ }
+
+ public static StringBuffer getStackDumps() {
+ StringBuffer sb = new StringBuffer();
+ Map traces = Thread.getAllStackTraces();
+ for (Map.Entry entry : traces.entrySet()) {
+ sb.append(entry.getKey().toString());
+ sb.append("\n");
+ for (StackTraceElement element : entry.getValue()) {
+ sb.append(" ");
+ sb.append(element.toString());
+ sb.append("\n");
+ }
+ sb.append("\n");
+ }
+ return sb;
+ }
+
+
+
+}
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/Asserter.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/Asserter.java
new file mode 100644
index 000000000..92ee0159f
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/Asserter.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Pivotal Software, 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 Software, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springsource.ide.eclipse.commons.frameworks.test.util;
+
+/**
+ * An {@link Asserter} provides a single method which is expected
+ * to check some stuff during a test (i.e. typically execute a bunch
+ * of asserts).
+ *
+ * @author Kris De Volder
+ */
+@FunctionalInterface
+public interface Asserter {
+ /**
+ * Called on to check some conditions during a test. Should return
+ * normally to indicate 'success' and throw an exception of any type
+ * to indicate 'failure'.
+ */
+ void execute() throws Exception;
+}
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.classpath b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.classpath
new file mode 100644
index 000000000..51a8bbad8
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.project b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.project
new file mode 100644
index 000000000..dbf9e6bbc
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.project
@@ -0,0 +1,17 @@
+
+
+ classpath-test-simple-java-project
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.settings/org.eclipse.jdt.core.prefs b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..3a2153707
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/src/com/demo/Something.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/src/com/demo/Something.java
new file mode 100644
index 000000000..ee10b75be
--- /dev/null
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/src/com/demo/Something.java
@@ -0,0 +1,5 @@
+package com.demo;
+
+public class Something {
+
+}
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java
index aec2b1857..9bf1f96a1 100644
--- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java
@@ -125,10 +125,19 @@ public class Classpath {
public void setSystem(boolean isSystem) {
this.isSystem = isSystem;
}
+
+ @Override
+ public String toString() {
+ return "CPE [kind=" + kind + ", path=" + path + "]";
+ }
}
public static boolean isSource(CPE e) {
return e!=null && Classpath.ENTRY_KIND_SOURCE.equals(e.getKind());
}
+ public static boolean isBinary(CPE e) {
+ return e!=null && Classpath.ENTRY_KIND_BINARY.equals(e.getKind());
+ }
+
}
\ No newline at end of file
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 2111112b7..53be67170 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
@@ -10,11 +10,15 @@
*******************************************************************************/
package org.springframework.tooling.jdt.ls.commons.classpath;
+import static org.springframework.tooling.jdt.ls.commons.Logger.log;
import static org.springframework.tooling.jdt.ls.commons.classpath.Classpath.ENTRY_KIND_BINARY;
import static org.springframework.tooling.jdt.ls.commons.classpath.Classpath.ENTRY_KIND_SOURCE;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -22,17 +26,48 @@ import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.internal.core.JavaProject;
import org.springframework.tooling.jdt.ls.commons.classpath.Classpath.CPE;
-import static 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 Set getSystemLibraryPaths(IJavaProject javaProject) {
+ try {
+ IClasspathEntry jreContainer = getJreContainer(javaProject.getRawClasspath());
+ IClasspathEntry[] resolvedJreEntries = ((JavaProject)javaProject).resolveClasspath(new IClasspathEntry[] {jreContainer});
+ Set paths = new HashSet<>();
+ for (IClasspathEntry systemEntry : resolvedJreEntries) {
+ paths.add(systemEntry.getPath().toString());
+ }
+ return paths;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return Collections.emptySet();
+ }
+
+ private static IClasspathEntry getJreContainer(IClasspathEntry[] rawClasspath) {
+ for (IClasspathEntry cpe : rawClasspath) {
+ if (cpe.getEntryKind()==IClasspathEntry.CPE_CONTAINER) {
+ if (cpe.getPath().segment(0).equals(JRE_CONTAINER_ID)) {
+ return cpe;
+ }
+ }
+ }
+ return null;
+ }
+
public static Classpath resolve(IJavaProject javaProject) throws Exception {
//log("resolving classpath " + javaProject.getElementName() +" ...");
List cpEntries = new ArrayList<>();
+
+
+
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
+ Set systemLibs = getSystemLibraryPaths(javaProject);
if (entries != null) {
for (IClasspathEntry entry : entries) {
@@ -41,6 +76,9 @@ public class ClasspathUtil {
case Classpath.ENTRY_KIND_BINARY: {
String path = entry.getPath().toString();
CPE cpe = CPE.binary(path);
+ if (systemLibs.contains(path)) {
+ cpe.setSystem(true);
+ }
cpEntries.add(cpe);
break;
}
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClientCommandExecutor.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClientCommandExecutor.java
index 19916b650..7dc0cfb3c 100644
--- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClientCommandExecutor.java
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClientCommandExecutor.java
@@ -1,3 +1,13 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Pivotal, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Pivotal, Inc. - initial API and implementation
+ *******************************************************************************/
package org.springframework.tooling.jdt.ls.commons.classpath;
public interface ClientCommandExecutor {
diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ReusableClasspathListenerHandler.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ReusableClasspathListenerHandler.java
index 5d2895fb0..72e2674b4 100644
--- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ReusableClasspathListenerHandler.java
+++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ReusableClasspathListenerHandler.java
@@ -98,7 +98,11 @@ public class ReusableClasspathListenerHandler {
subscribers = null;
}
}
- Logger.log("subsribers = " + subscribers.keySet());
+ Logger.log("subsribers = " + (subscribers == null ? "null" : subscribers.keySet()));
+ }
+
+ public boolean isEmpty() {
+ return subscribers == null || subscribers.isEmpty();
}
}
@@ -118,4 +122,8 @@ public class ReusableClasspathListenerHandler {
return "ok";
}
+ public boolean hasNoActiveSubscriptions() {
+ return subscribptions.isEmpty();
+ }
+
}
diff --git a/headless-services/jdt-ls-extension/pom.xml b/headless-services/jdt-ls-extension/pom.xml
index fb63ac469..1a50cbcf1 100644
--- a/headless-services/jdt-ls-extension/pom.xml
+++ b/headless-services/jdt-ls-extension/pom.xml
@@ -13,6 +13,7 @@
org.springframework.tooling.jdt.ls.extension
org.springframework.tooling.jdt.ls.commons
+ org.springframework.tooling.jdt.ls.commons.test