Skip to content

Commit

Permalink
Start tidying up classloading code; IDEA-Gradle 1.8.0 changes things …
Browse files Browse the repository at this point in the history
…and breaks the unit tests, this is a start towards fixing it
  • Loading branch information
jshiell committed Aug 6, 2022
1 parent f0ac43c commit b5df240
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 41 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -88,27 +95,13 @@ private static Properties loadClassPathInfos() {
@NotNull
private ClassLoader buildClassLoader(@NotNull final String classPathFromProps,
@NotNull final List<URL> thirdPartyClasspath) {
final String basePath = getBasePath();
final File classesDir4UnitTesting = new File(basePath, "classes/java/csaccess");
final boolean unitTesting = classesDir4UnitTesting.exists();
final String basePluginPath = getBasePluginPath();

List<URL> 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<URL> urls;
if (basePluginPath != null) {
urls = baseClasspathUrlsForPackagedPlugin(classPathFromProps, basePluginPath);
} else {
urls = baseClasspathUrlsForIDEAUnitTests(classPathFromProps);
}

urls.addAll(thirdPartyClasspath);
Expand All @@ -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<URL> baseClasspathUrlsForPackagedPlugin(@NotNull final String classPathFromProps,
@NotNull final String basePath) {
try {
final List<URL> 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<URL> baseClasspathUrlsForIDEAUnitTests(@NotNull final String classPathFromProps) {
try {
final List<URL> 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");
Expand Down Expand Up @@ -152,39 +206,34 @@ private List<URL> 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<URL> urls = getUrls(getClass().getClassLoader());
String result = guessFromClassPath(urls, CLASSES_URL_2017_2_LATER);
if (result != null) {
result += "/build";
} else {
result = guessFromClassPath(urls, CLASSES_URL_2017_1_EARLIER);
}
if (result == null) {
result = guessFromClassPath(urls, CLASSES_URL_GRADLE);
}
if (result != null) {
result = urlDecode(result);
}
Expand Down Expand Up @@ -218,10 +267,10 @@ private String getPreinstalledPluginPath() {
}

@Nullable
private String guessFromClassPath(@NotNull final List<URL> pUrls, @NotNull final Pattern pPattern) {
private String guessFromClassPath(@NotNull final List<URL> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="https://stackoverflow.com/a/6424879">...</a>
Expand Down Expand Up @@ -113,4 +110,8 @@ public InputStream getResourceAsStream(final String name) {
return null;
}

@Override
public String toString() {
return "ChildFirstURLClassLoader: URLs " + Arrays.toString(getURLs()) + "; Parent " + getParent();
}
}

0 comments on commit b5df240

Please sign in to comment.