From dd103aae56a617b007b666eb1c6ed9099ce79f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Thu, 2 Feb 2017 20:09:54 -0300 Subject: [PATCH] [core] Fix IOException loading rulesets under concurrency - Fixes #234 - When a single JVM runs multiple PMD instances under different threads, connections to PMD jars (and streams opened to read standard rulesets) are shared, causing race conditions where one thread may cause the other to close it's streams. This in tun produces IOExceptions. - This happens in Gradle when enabling parallel builds. I believe it can happen under Maven with -T1C too, but has not been able to reproduce it. - I take the chance to clean up classloader closing code, making it more concise and standard accross CLI and Ant. --- .../src/main/java/net/sourceforge/pmd/PMD.java | 13 +++++++++---- .../net/sourceforge/pmd/util/ResourceLoader.java | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java index ecb5ff9fa4..d4d780a3a1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -275,6 +275,15 @@ public class PMD { return 0; } finally { Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0); + + /* + * Make sure it's our own classloader before attempting to close it.... + * Maven + Jacoco provide us with a cloaseable classloader that if closed + * will throw a ClassNotFoundException. + */ + if (configuration.getClassLoader() instanceof ClasspathClassLoader) { + IOUtil.tryCloseClassLoader(configuration.getClassLoader()); + } } } @@ -362,10 +371,6 @@ public class PMD { } else { new MonoThreadProcessor(configuration).processFiles(ruleSetFactory, files, ctx, renderers); } - - if (configuration.getClassLoader() instanceof ClasspathClassLoader) { - IOUtil.tryCloseClassLoader(configuration.getClassLoader()); - } } private static void sortFiles(final PMDConfiguration configuration, final List files) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java index 2a2defae3d..fc1b68fac7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java @@ -8,6 +8,7 @@ import net.sourceforge.pmd.RuleSetNotFoundException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; @@ -73,7 +74,20 @@ public final class ResourceLoader { connection.setReadTimeout(TIMEOUT); return connection.getInputStream(); } catch (Exception e) { - return loader.getResourceAsStream(name); + try { + /* + * Don't use getResourceAsStream to void reusing connections between threads + * See https://github.com/pmd/pmd/issues/234 + */ + URL resource = loader.getResource(name); + if (resource == null) { + // Don't throw RuleSetNotFoundException, keep API compatibility + return null; + } + return resource.openStream(); + } catch (IOException e1) { + // Ignored + } } } throw new RuleSetNotFoundException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the CLASSPATH");