diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 7d06b05944..c1163fabac 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -101,6 +101,11 @@ runtime + + com.github.tomakehurst + wiremock + test + junit junit diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java index dc15e14ed7..256b494665 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -4,16 +4,20 @@ package net.sourceforge.pmd; import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; import java.util.ArrayList; import java.util.List; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; - import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.StringUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + /** * This class is used to parse a RuleSet reference value. Most commonly used for specifying a * RuleSet to process, or in a Rule 'ref' attribute value in the RuleSet XML. The RuleSet reference @@ -119,7 +123,13 @@ public class RuleSetReferenceId { // Damn this parsing sucks, but my brain is just not working to let me // write a simpler scheme. - if (isHttpUrl(id) || isFullRuleSetName(id)) { + if (isValidUrl(id)) { + // A full RuleSet name + external = true; + ruleSetFileName = StringUtils.strip(id); + allRules = true; + ruleName = null; + } else if (isFullRuleSetName(id)) { // A full RuleSet name external = true; ruleSetFileName = id; @@ -130,7 +140,20 @@ public class RuleSetReferenceId { String tempRuleSetFileName = tempRuleName != null && id != null ? id.substring(0, id.length() - tempRuleName.length() - 1) : id; - if (isFullRuleSetName(tempRuleSetFileName)) { + if (isValidUrl(tempRuleSetFileName)) { + // remaining part is a xml ruleset file, so the tempRuleName is probably a real rule name + external = true; + ruleSetFileName = StringUtils.strip(tempRuleSetFileName); + ruleName = StringUtils.strip(tempRuleName); + allRules = tempRuleName == null; + } else if (isHttpUrl(id)) { + // it's a url, we can't determine whether it's a full ruleset or a single rule - so falling back to + // a full RuleSet name + external = true; + ruleSetFileName = StringUtils.strip(id); + allRules = true; + ruleName = null; + } else if (isFullRuleSetName(tempRuleSetFileName)) { // remaining part is a xml ruleset file, so the tempRuleName is probably a real rule name external = true; ruleSetFileName = tempRuleSetFileName; @@ -256,6 +279,25 @@ public class RuleSetReferenceId { return false; } + + private static boolean isValidUrl(String name) { + if (isHttpUrl(name)) { + String url = StringUtils.strip(name); + try { + HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection(); + connection.setRequestMethod("HEAD"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + int responseCode = connection.getResponseCode(); + if (responseCode == 200) { + return true; + } + } catch (IOException e) { + return false; + } + } + return false; + } private static boolean isFullRuleSetName(String name) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java index be4726fdb5..80293da5cc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetReferenceIdTest.java @@ -3,14 +3,28 @@ */ package net.sourceforge.pmd; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.findAll; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.head; +import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; +import java.io.InputStream; import java.util.List; +import org.apache.commons.io.IOUtils; import org.junit.Test; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + public class RuleSetReferenceIdTest { private static void assertRuleSetReferenceId(final boolean expectedExternal, final String expectedRuleSetFileName, @@ -83,12 +97,63 @@ public class RuleSetReferenceIdTest { public void constructor_GivenHttpUrlId_SucceedsAndProcessesIdCorrectly() { final String sonarRulesetUrlId = - " http://localhost:9000/profiles/export?format=pmd&language=java&name=Sonar%2520way "; + "http://localhost:54321/profiles/export?format=pmd&language=java&name=Sonar%2520way"; - RuleSetReferenceId ruleSetReferenceId = new RuleSetReferenceId(sonarRulesetUrlId); + RuleSetReferenceId ruleSetReferenceId = new RuleSetReferenceId(" " + sonarRulesetUrlId + " "); assertRuleSetReferenceId(true, sonarRulesetUrlId, true, null, sonarRulesetUrlId, ruleSetReferenceId); } + @org.junit.Rule + public WireMockRule wireMockRule = new WireMockRule(0); + + @Test + public void constructor_GivenHttpUrl_InputStream() throws Exception { + String path = "/profiles/export?format=pmd&language=java&name=Sonar%2520way"; + String rulesetUrl = "http://localhost:" + wireMockRule.port() + path; + stubFor(head(urlEqualTo(path)).willReturn(aResponse().withStatus(200))); + stubFor(get(urlEqualTo(path)).willReturn(aResponse().withStatus(200).withHeader("Content-type", "text/xml").withBody("xyz"))); + + RuleSetReferenceId ruleSetReferenceId = new RuleSetReferenceId(" " + rulesetUrl + " "); + assertRuleSetReferenceId(true, rulesetUrl, true, null, rulesetUrl, ruleSetReferenceId); + + InputStream inputStream = ruleSetReferenceId.getInputStream(RuleSetReferenceIdTest.class.getClassLoader()); + String loaded = IOUtils.toString(inputStream, "UTF-8"); + assertEquals("xyz", loaded); + + verify(1, headRequestedFor(urlEqualTo(path))); + verify(0, headRequestedFor(urlEqualTo("/profiles"))); + verify(1, getRequestedFor(urlEqualTo(path))); + assertEquals(1, findAll(headRequestedFor(urlMatching(".*"))).size()); + assertEquals(1, findAll(getRequestedFor(urlMatching(".*"))).size()); + } + + @Test + public void constructor_GivenHttpUrl_SingleRule_InputStream() throws Exception { + String path = "/profiles/export?format=pmd&language=java&name=Sonar%2520way"; + String completePath = path + "/DummyBasicMockRule"; + String hostpart = "http://localhost:" + wireMockRule.port(); + String basicRuleSet = IOUtils.toString(RuleSetReferenceId.class.getResourceAsStream("/rulesets/dummy/basic.xml")); + + stubFor(head(urlEqualTo(completePath)).willReturn(aResponse().withStatus(404))); + stubFor(head(urlEqualTo(path)).willReturn(aResponse().withStatus(200).withHeader("Content-type", "text/xml"))); + stubFor(get(urlEqualTo(path)).willReturn(aResponse().withStatus(200).withHeader("Content-type", "text/xml").withBody(basicRuleSet))); + + + RuleSetReferenceId ruleSetReferenceId = new RuleSetReferenceId(" " + hostpart + completePath + " "); + assertRuleSetReferenceId(true, hostpart + path, false, "DummyBasicMockRule", hostpart + completePath, ruleSetReferenceId); + + InputStream inputStream = ruleSetReferenceId.getInputStream(RuleSetReferenceIdTest.class.getClassLoader()); + String loaded = IOUtils.toString(inputStream, "UTF-8"); + assertEquals(basicRuleSet, loaded); + + verify(1, headRequestedFor(urlEqualTo(completePath))); + verify(1, headRequestedFor(urlEqualTo(path))); + verify(1, getRequestedFor(urlEqualTo(path))); + verify(0, getRequestedFor(urlEqualTo(completePath))); + assertEquals(2, findAll(headRequestedFor(urlMatching(".*"))).size()); + assertEquals(1, findAll(getRequestedFor(urlMatching(".*"))).size()); + } + @Test public void testOneSimpleRuleSet() { diff --git a/pom.xml b/pom.xml index cead6de97a..550f2b1f6f 100644 --- a/pom.xml +++ b/pom.xml @@ -714,6 +714,11 @@ slf4j-api 1.7.7 + + com.github.tomakehurst + wiremock + 1.52 +