From b5df24062ba005d822e5fc6349b34051bbb3fa0d Mon Sep 17 00:00:00 2001 From: Jamie Shiell Date: Sat, 6 Aug 2022 12:24:18 +0100 Subject: [PATCH] Start tidying up classloading code; IDEA-Gradle 1.8.0 changes things and breaks the unit tests, this is a start towards fixing it --- build.gradle | 2 +- .../CheckstyleClassLoaderContainer.java | 121 ++++++++++++------ .../util/ChildFirstURLClassLoader.java | 9 +- 3 files changed, 91 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index b6478d32..2ebb67c5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'jacoco' id 'idea' - id 'org.jetbrains.intellij' version '1.8.0' + id 'org.jetbrains.intellij' version '1.7.0' id 'com.dorongold.task-tree' version '2.1.0' id 'org.infernus.idea.checkstyle.build' } diff --git a/src/main/java/org/infernus/idea/checkstyle/CheckstyleClassLoaderContainer.java b/src/main/java/org/infernus/idea/checkstyle/CheckstyleClassLoaderContainer.java index 9008d0eb..843a5c41 100644 --- a/src/main/java/org/infernus/idea/checkstyle/CheckstyleClassLoaderContainer.java +++ b/src/main/java/org/infernus/idea/checkstyle/CheckstyleClassLoaderContainer.java @@ -49,6 +49,13 @@ public class CheckstyleClassLoaderContainer { private static final Pattern CLASSES_URL_2017_1_EARLIER = Pattern.compile( "^(.*?)[/\\\\]classes(?:[/\\\\]main)?[/\\\\]?$"); + /** + * Or if we're building in IDEA with Gradle, it'll be something like: + * /Users/foo/Projects/checkstyle-idea/build/classes/java/main/ + */ + private static final Pattern CLASSES_URL_GRADLE = Pattern.compile( + "^(.*?)[/\\\\]classes[/\\\\]java[/\\\\]main[/\\\\]?$"); + private final ClassLoader classLoader; private final Project project; @@ -88,27 +95,13 @@ private static Properties loadClassPathInfos() { @NotNull private ClassLoader buildClassLoader(@NotNull final String classPathFromProps, @NotNull final List thirdPartyClasspath) { - final String basePath = getBasePath(); - final File classesDir4UnitTesting = new File(basePath, "classes/java/csaccess"); - final boolean unitTesting = classesDir4UnitTesting.exists(); + final String basePluginPath = getBasePluginPath(); - List urls = new ArrayList<>(); - try { - if (unitTesting) { - urls.add(classesDir4UnitTesting.toURI().toURL()); - } else { - urls.add(new File(basePath, "checkstyle/classes").toURI().toURL()); - } - for (String jar : classPathFromProps.trim().split("\\s*;\\s*")) { - if (unitTesting) { - String testJarLocation = "tmp/gatherCheckstyleArtifacts" + jar.substring(jar.lastIndexOf('/')); - urls.add(new File(basePath, testJarLocation).toURI().toURL()); - } else { - urls.add(new File(basePath, jar).toURI().toURL()); - } - } - } catch (MalformedURLException e) { - throw new CheckStylePluginException("internal error", e); + List urls; + if (basePluginPath != null) { + urls = baseClasspathUrlsForPackagedPlugin(classPathFromProps, basePluginPath); + } else { + urls = baseClasspathUrlsForIDEAUnitTests(classPathFromProps); } urls.addAll(thirdPartyClasspath); @@ -120,9 +113,70 @@ private ClassLoader buildClassLoader(@NotNull final String classPathFromProps, Notifications.showWarning(project, message("plugin.debugging")); return new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader()); } + return newClassLoader; } + private static List baseClasspathUrlsForPackagedPlugin(@NotNull final String classPathFromProps, + @NotNull final String basePath) { + try { + final List urls = new ArrayList<>(); + + urls.add(new File(basePath, "checkstyle/classes").toURI().toURL()); + for (String jar : splitClassPathFromProperties(classPathFromProps)) { + File jarLocation = new File(basePath, jar); + if (!jarLocation.exists()) { + throw new CheckStylePluginException("Cannot find packaged artefact: " + jarLocation.getAbsolutePath()); + } + urls.add(jarLocation.toURI().toURL()); + } + + return urls; + + } catch (MalformedURLException e) { + throw new CheckStylePluginException("Failed to parse classpath URL", e); + } + } + + private List baseClasspathUrlsForIDEAUnitTests(@NotNull final String classPathFromProps) { + try { + final List urls = new ArrayList<>(); + final String buildPath = guessBuildPathFromClasspath(); + URL unitTestingClassPath = null; + if (buildPath != null) { + final File classesDir4UnitTesting = new File(buildPath, "classes/java/csaccess"); + if (classesDir4UnitTesting.exists()) { + unitTestingClassPath = classesDir4UnitTesting.toURI().toURL(); + } + } + + if (unitTestingClassPath == null) { + throw new CheckStylePluginException("Could not determine plugin directory or build directory"); + } + + urls.add(unitTestingClassPath); + + for (String jar : splitClassPathFromProperties(classPathFromProps)) { + String testJarLocation = "tmp/gatherCheckstyleArtifacts" + jar.substring(jar.lastIndexOf('/')); + File jarLocation = new File(buildPath, testJarLocation); + if (!jarLocation.exists()) { + throw new CheckStylePluginException("Cannot find collected artefact: " + jarLocation.getAbsolutePath()); + } + urls.add(jarLocation.toURI().toURL()); + } + + return urls; + + } catch (MalformedURLException e) { + throw new CheckStylePluginException("Failed to parse classpath URL", e); + } + } + + @NotNull + private static String[] splitClassPathFromProperties(@NotNull final String classPathFromProps) { + return classPathFromProps.trim().split("\\s*;\\s*"); + } + private boolean weAreDebuggingADifferentVersionOfIdea(final ClassLoader classLoaderToTest) { try { return Project.class != classLoaderToTest.loadClass("com.intellij.openapi.project.Project"); @@ -152,32 +206,24 @@ private List getUrls(@NotNull final ClassLoader sourceClassLoader) { /** * Determine the base path of the plugin. When running in IntelliJ, this is something like - * {@code C:/Users/jdoe/.IdeaIC2016.3/config/plugins/CheckStyle-IDEA} (on Windows). When running in a unit test, - * it is this project's build directory, for example {@code D:/Documents/Projects/checkstyle-idea/build} (again - * on Windows). + * {@code C:/Users/jdoe/.IdeaIC2016.3/config/plugins/CheckStyle-IDEA} (on Windows); when running in Gradle, + * it's probably the sandbox directory. * * @return the base path, as absolute path */ - @NotNull - private String getBasePath() { + @Nullable + private String getBasePluginPath() { String result = getPluginPath(); if (result == null) { result = getPreinstalledPluginPath(); } - if (result == null) { - result = guessPluginPathFromClasspath(); - } - - if (result == null) { - throw new CheckStylePluginException("Could not determine plugin directory"); - } return result; } @Nullable - private String guessPluginPathFromClasspath() { + private String guessBuildPathFromClasspath() { final List urls = getUrls(getClass().getClassLoader()); String result = guessFromClassPath(urls, CLASSES_URL_2017_2_LATER); if (result != null) { @@ -185,6 +231,9 @@ private String guessPluginPathFromClasspath() { } else { result = guessFromClassPath(urls, CLASSES_URL_2017_1_EARLIER); } + if (result == null) { + result = guessFromClassPath(urls, CLASSES_URL_GRADLE); + } if (result != null) { result = urlDecode(result); } @@ -218,10 +267,10 @@ private String getPreinstalledPluginPath() { } @Nullable - private String guessFromClassPath(@NotNull final List pUrls, @NotNull final Pattern pPattern) { + private String guessFromClassPath(@NotNull final List urls, @NotNull final Pattern pattern) { String result = null; - for (final URL url : pUrls) { - Matcher matcher = pPattern.matcher(url.getPath()); + for (final URL url : urls) { + Matcher matcher = pattern.matcher(url.getPath()); if (matcher.find()) { result = matcher.group(1); break; diff --git a/src/main/java/org/infernus/idea/checkstyle/util/ChildFirstURLClassLoader.java b/src/main/java/org/infernus/idea/checkstyle/util/ChildFirstURLClassLoader.java index 8ace76d4..fe1565da 100644 --- a/src/main/java/org/infernus/idea/checkstyle/util/ChildFirstURLClassLoader.java +++ b/src/main/java/org/infernus/idea/checkstyle/util/ChildFirstURLClassLoader.java @@ -4,10 +4,7 @@ import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; +import java.util.*; /** * A child-first URL class loader, taken from ... @@ -113,4 +110,8 @@ public InputStream getResourceAsStream(final String name) { return null; } + @Override + public String toString() { + return "ChildFirstURLClassLoader: URLs " + Arrays.toString(getURLs()) + "; Parent " + getParent(); + } }