Modify ResourceLoader to close underlying JarFile if using
JarURLConnection to avoid open file leaks. Clarify on JavaDoc that
caller must close the returned InputStream.

Minor test usage cleanups.

(cherry picked from commit effe71ed54)
This commit is contained in:
Andreas Dangel
2017-04-19 21:42:33 +02:00
parent 4784787afe
commit 13e0d6e287
3 changed files with 57 additions and 21 deletions

View File

@@ -11,7 +11,9 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
*/
@@ -36,10 +38,14 @@ public final class ResourceLoader {
}
/**
* Method to find a file, first by finding it as a file
* (either by the absolute or relative path), then as
* a URL, and then finally seeing if it is on the classpath.
* @param name String
* Method to find a file, first by finding it as a file (either by the
* absolute or relative path), then as a URL, and then finally seeing if it
* is on the classpath.
* <p>
* Caller is responsible for closing the {@link InputStream}.
*
* @param name
* String
* @return InputStream
* @throws RuleSetNotFoundException
*/
@@ -52,10 +58,15 @@ public final class ResourceLoader {
}
/**
* Uses the ClassLoader passed in to attempt to load the
* resource if it's not a File or a URL
* @param name String
* @param loader ClassLoader
* Uses the ClassLoader passed in to attempt to load the resource if it's
* not a File or a URL
* <p>
* Caller is responsible for closing the {@link InputStream}.
*
* @param name
* String
* @param loader
* ClassLoader
* @return InputStream
* @throws RuleSetNotFoundException
*/
@@ -83,8 +94,30 @@ public final class ResourceLoader {
if (resource == null) {
// Don't throw RuleSetNotFoundException, keep API compatibility
return null;
} else {
final URLConnection connection = resource.openConnection();
final InputStream inputStream = connection.getInputStream();
if (connection instanceof JarURLConnection) {
// Wrap the InputStream to also close the underlying JarFile if from a JarURLConnection.
// See https://github.com/pmd/pmd/issues/337
return new InputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public void close() throws IOException {
inputStream.close();
if (connection instanceof JarURLConnection) {
((JarURLConnection) connection).getJarFile().close();
}
}
};
} else {
return inputStream;
}
}
return resource.openStream();
} catch (IOException e1) {
// Ignored
}

View File

@@ -50,18 +50,19 @@ public class RuleSetFactoryTest {
assertNotNull(rs.getRuleByName("TestRuleRef"));
}
@Test
public void testExtendedReferences() throws Exception {
InputStream in = ResourceLoader.loadResourceAsStream("net/sourceforge/pmd/rulesets/reference-ruleset.xml",
this.getClass().getClassLoader());
Assert.assertNotNull("Test ruleset not found - can't continue with test!", in);
@Test
public void testExtendedReferences() throws Exception {
InputStream in = ResourceLoader.loadResourceAsStream("net/sourceforge/pmd/rulesets/reference-ruleset.xml",
this.getClass().getClassLoader());
Assert.assertNotNull("Test ruleset not found - can't continue with test!", in);
in.close();
RuleSetFactory rsf = new RuleSetFactory();
RuleSets rs = rsf.createRuleSets("net/sourceforge/pmd/rulesets/reference-ruleset.xml");
// added by referencing a complete ruleset (TestRuleset1.xml)
assertNotNull(rs.getRuleByName("MockRule1"));
assertNotNull(rs.getRuleByName("MockRule2"));
assertNotNull(rs.getRuleByName("MockRule3"));
RuleSetFactory rsf = new RuleSetFactory();
RuleSets rs = rsf.createRuleSets("net/sourceforge/pmd/rulesets/reference-ruleset.xml");
// added by referencing a complete ruleset (TestRuleset1.xml)
assertNotNull(rs.getRuleByName("MockRule1"));
assertNotNull(rs.getRuleByName("MockRule2"));
assertNotNull(rs.getRuleByName("MockRule3"));
assertNotNull(rs.getRuleByName("TestRuleRef"));
// added by specific reference

View File

@@ -222,7 +222,9 @@ public abstract class AbstractRuleSetFactoryTest {
List<String> ruleSetFileNames = new ArrayList<>();
try {
Properties properties = new Properties();
properties.load(ResourceLoader.loadResourceAsStream("rulesets/" + language + "/rulesets.properties"));
try (InputStream is = ResourceLoader.loadResourceAsStream("rulesets/" + language + "/rulesets.properties")) {
properties.load(is);
}
String fileNames = properties.getProperty("rulesets.filenames");
StringTokenizer st = new StringTokenizer(fileNames, ",");
while (st.hasMoreTokens()) {