From 689da33d90b5eae913ee415a2bb3b0b7e976c543 Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Fri, 4 May 2018 07:47:58 -0700 Subject: [PATCH] Add a first test for jdt.ls.extension --- .gitignore | 3 + .../.classpath | 7 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../META-INF/MANIFEST.MF | 16 ++ .../build.properties | 4 + .../pom.xml | 18 ++ .../commons/ClasspathListenerHandlerTest.java | 120 ++++++++ .../frameworks/test/util/ACondition.java | 264 ++++++++++++++++++ .../frameworks/test/util/Asserter.java | 28 ++ .../.classpath | 6 + .../.project | 17 ++ .../.settings/org.eclipse.jdt.core.prefs | 11 + .../src/com/demo/Something.java | 5 + .../jdt/ls/commons/classpath/Classpath.java | 9 + .../ls/commons/classpath/ClasspathUtil.java | 42 ++- .../classpath/ClientCommandExecutor.java | 10 + .../ReusableClasspathListenerHandler.java | 10 +- headless-services/jdt-ls-extension/pom.xml | 1 + 19 files changed, 603 insertions(+), 3 deletions(-) create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.classpath create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.project create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.settings/org.eclipse.jdt.core.prefs create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/META-INF/MANIFEST.MF create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/build.properties create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/pom.xml create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springframework/tooling/jdt/ls/commons/ClasspathListenerHandlerTest.java create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/ACondition.java create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springsource/ide/eclipse/commons/frameworks/test/util/Asserter.java create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.classpath create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/.project create mode 100644 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 create mode 100644 headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/test-projects/classpath-test-simple-java-project/src/com/demo/Something.java diff --git a/.gitignore b/.gitignore index b3a575078..605d126ab 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ eclipse-language-servers/**/*.jar # Exceptions !**/.mvn/wrapper/maven-wrapper.jar +!**/jdt-ls-extension/**/test-projects/**/.classpath +!**/jdt-ls-extension/**/test-projects/**/.project +!**/jdt-ls-extension/**/test-projects/**/.settings # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.classpath b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.project b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.project new file mode 100644 index 000000000..7919b232d --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.project @@ -0,0 +1,28 @@ + + + org.springframework.tooling.jdt.ls.commons.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.settings/org.eclipse.jdt.core.prefs b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..0c68a61dc --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +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.compliance=1.8 +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/META-INF/MANIFEST.MF b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/META-INF/MANIFEST.MF new file mode 100644 index 000000000..21a8640d2 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Commons +Bundle-SymbolicName: org.springframework.tooling.jdt.ls.commons.test +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: org.springframework.tooling.jdt.ls.commons.test +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.springframework.tooling.jdt.ls.commons, + org.eclipse.core.runtime, + org.eclipse.jdt.core, + org.eclipse.core.resources, + org.apache.commons.lang3, + org.junit, + org.apache.commons.io, + org.eclipse.swt + diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/build.properties b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/pom.xml b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/pom.xml new file mode 100644 index 000000000..cd62db957 --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + eclipse-test-plugin + org.springframework.ide.vscode + org.springframework.tooling.jdt.ls.commons.test + 1.0.0-SNAPSHOT + org.springframework.tooling.jdt.ls.commons + + + org.springframework.tooling + jdt-ls-extension-parent + 0.0.1-SNAPSHOT + ../pom.xml + + + diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springframework/tooling/jdt/ls/commons/ClasspathListenerHandlerTest.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springframework/tooling/jdt/ls/commons/ClasspathListenerHandlerTest.java new file mode 100644 index 000000000..561eabf3a --- /dev/null +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons.test/src/org/springframework/tooling/jdt/ls/commons/ClasspathListenerHandlerTest.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * 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; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.net.URI; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.RandomStringUtils; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.core.JavaCore; +import org.junit.After; +import org.junit.Test; +import org.springframework.tooling.jdt.ls.commons.classpath.Classpath; +import org.springframework.tooling.jdt.ls.commons.classpath.ClientCommandExecutor; +import org.springframework.tooling.jdt.ls.commons.classpath.ReusableClasspathListenerHandler; +import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition; + +public class ClasspathListenerHandlerTest { + + static class Info { + public final String name; + public final Classpath classpath; + + private Info(String name, Classpath cp) { + super(); + this.name = name; + this.classpath = cp; + } + } + + public class MockClasspathCache implements ClientCommandExecutor { + + + String commandId = RandomStringUtils.randomAlphabetic(8); + + Map 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