From 76af873708d4c8aecbfd5b1b7d8e9a7859ad54bd Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 23 Oct 2014 20:00:57 +0200 Subject: [PATCH] #1274 ant integration broken with pmd-5.2.0 Separate PMDTask and PMDTaskImpl and set the context classloader when executing PMD For CPD, the validation of the language is deferred, until the context class loader has been switched --- .../java/net/sourceforge/pmd/ant/PMDTask.java | 492 +++++++----------- .../sourceforge/pmd/ant/SourceLanguage.java | 2 +- .../pmd/ant/internal/PMDTaskImpl.java | 279 ++++++++++ .../java/net/sourceforge/pmd/cpd/CPDTask.java | 31 +- .../pmd/util/log/AntLogHandler.java | 12 +- .../net/sourceforge/pmd/ant/PMDTaskTest.java | 2 +- .../sourceforge/pmd/ant/xml/cpdtasktest.xml | 2 +- src/site/markdown/overview/changelog.md | 1 + 8 files changed, 484 insertions(+), 337 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java index 49ead072db..4b1f915434 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/PMDTask.java @@ -3,44 +3,14 @@ */ package net.sourceforge.pmd.ant; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Handler; -import java.util.logging.Level; -import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.PMDConfiguration; -import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.RulePriority; -import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RuleSets; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.renderers.AbstractRenderer; -import net.sourceforge.pmd.renderers.Renderer; -import net.sourceforge.pmd.util.StringUtil; -import net.sourceforge.pmd.util.datasource.DataSource; -import net.sourceforge.pmd.util.datasource.FileDataSource; -import net.sourceforge.pmd.util.log.AntLogHandler; -import net.sourceforge.pmd.util.log.ScopedLogHandlersManager; +import net.sourceforge.pmd.ant.internal.PMDTaskImpl; -import org.apache.commons.io.IOUtils; -import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.DirectoryScanner; -import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; @@ -48,330 +18,222 @@ import org.apache.tools.ant.types.Reference; public class PMDTask extends Task { - private Path classpath; - private Path auxClasspath; - private final List formatters = new ArrayList(); - private final List filesets = new ArrayList(); - private final PMDConfiguration configuration = new PMDConfiguration(); - private boolean failOnError; - private boolean failOnRuleViolation; - private int maxRuleViolations = 0; - private String failuresPropertyName; - private final Collection nestedRules = new ArrayList(); + private Path classpath; + private Path auxClasspath; + private final List formatters = new ArrayList(); + private final List filesets = new ArrayList(); + private boolean failOnError; + private boolean failOnRuleViolation; + private boolean shortFilenames; + private String suppressMarker; + private String rulesetFiles; + private String encoding; + private int threads; + private int minimumPriority; + private int maxRuleViolations = 0; + private String failuresPropertyName; + private SourceLanguage sourceLanguage; + private final Collection nestedRules = new ArrayList(); - public void setShortFilenames(boolean reportShortNames) { - configuration.setReportShortNames(reportShortNames); - } + @Override + public void execute() throws BuildException { + validate(); - public void setSuppressMarker(String suppressMarker) { - configuration.setSuppressMarker(suppressMarker); - } + ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(PMDTask.class.getClassLoader()); + try { + PMDTaskImpl mirror = new PMDTaskImpl(this); + mirror.execute(); + } finally { + Thread.currentThread().setContextClassLoader(oldClassloader); + } + } - public void setFailOnError(boolean fail) { - this.failOnError = fail; - } + private void validate() throws BuildException { + if (formatters.isEmpty()) { + Formatter defaultFormatter = new Formatter(); + defaultFormatter.setType("text"); + defaultFormatter.setToConsole(true); + formatters.add(defaultFormatter); + } else { + for (Formatter f : formatters) { + if (f.isNoOutputSupplied()) { + throw new BuildException("toFile or toConsole needs to be specified in Formatter"); + } + } + } - public void setFailOnRuleViolation(boolean fail) { - this.failOnRuleViolation = fail; - } + if (rulesetFiles == null) { + if (nestedRules.isEmpty()) { + throw new BuildException("No rulesets specified"); + } + rulesetFiles = getNestedRuleSetFiles(); + } + } - public void setMaxRuleViolations(int max) { - if (max >= 0) { - this.maxRuleViolations = max; - this.failOnRuleViolation = true; - } - } + private String getNestedRuleSetFiles() { + final StringBuilder sb = new StringBuilder(); + for (Iterator it = nestedRules.iterator(); it.hasNext();) { + RuleSetWrapper rs = it.next(); + sb.append(rs.getFile()); + if (it.hasNext()) { + sb.append(','); + } + } + return sb.toString(); + } - public void setRuleSetFiles(String ruleSets) { - configuration.setRuleSets(ruleSets); - } + public void setShortFilenames(boolean reportShortNames) { + this.shortFilenames = reportShortNames; + } - public void setEncoding(String sourceEncoding) { - configuration.setSourceEncoding(sourceEncoding); - } + public void setSuppressMarker(String suppressMarker) { + this.suppressMarker = suppressMarker; + } - public void setThreads(int threads) { - configuration.setThreads(threads); - } + public void setFailOnError(boolean fail) { + this.failOnError = fail; + } - public void setFailuresPropertyName(String failuresPropertyName) { - this.failuresPropertyName = failuresPropertyName; - } + public void setFailOnRuleViolation(boolean fail) { + this.failOnRuleViolation = fail; + } - public void setMinimumPriority(int minPriority) { - configuration.setMinimumPriority(RulePriority.valueOf(minPriority)); - } + public void setMaxRuleViolations(int max) { + if (max >= 0) { + this.maxRuleViolations = max; + this.failOnRuleViolation = true; + } + } - public void addFileset(FileSet set) { - filesets.add(set); - } + public void setRuleSetFiles(String ruleSets) { + this.rulesetFiles = ruleSets; + } - public void addFormatter(Formatter f) { - formatters.add(f); - } + public void setEncoding(String sourceEncoding) { + this.encoding = sourceEncoding; + } - public void addConfiguredSourceLanguage(SourceLanguage version) { - LanguageVersion languageVersion = LanguageRegistry.findLanguageVersionByTerseName(version.getName() + " " + version.getVersion()); - if (languageVersion == null) { - throw new BuildException("The following language is not supported:" + version + "."); - } - configuration.setDefaultLanguageVersion(languageVersion); - } + public void setThreads(int threads) { + this.threads = threads; + } - public void setClasspath(Path classpath) { - this.classpath = classpath; - } + public void setFailuresPropertyName(String failuresPropertyName) { + this.failuresPropertyName = failuresPropertyName; + } - public Path getClasspath() { - return classpath; - } + public void setMinimumPriority(int minPriority) { + this.minimumPriority = minPriority; + } - public Path createClasspath() { - if (classpath == null) { - classpath = new Path(getProject()); - } - return classpath.createPath(); - } + public void addFileset(FileSet set) { + filesets.add(set); + } - public void setClasspathRef(Reference r) { - createClasspath().setRefid(r); - } + public void addFormatter(Formatter f) { + formatters.add(f); + } - public void setAuxClasspath(Path auxClasspath) { - this.auxClasspath = auxClasspath; - } + public void addConfiguredSourceLanguage(SourceLanguage version) { + this.sourceLanguage = version; + } - public Path getAuxClasspath() { - return auxClasspath; - } + public void setClasspath(Path classpath) { + this.classpath = classpath; + } - public Path createAuxClasspath() { - if (auxClasspath == null) { - auxClasspath = new Path(getProject()); - } - return auxClasspath.createPath(); - } + public Path getClasspath() { + return classpath; + } - public void setAuxClasspathRef(Reference r) { - createAuxClasspath().setRefid(r); - } + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath.createPath(); + } - private void doTask() { - setupClassLoader(); + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } - // Setup RuleSetFactory and validate RuleSets - RuleSetFactory ruleSetFactory = new RuleSetFactory(); - ruleSetFactory.setClassLoader(configuration.getClassLoader()); - try { - // This is just used to validate and display rules. Each thread will create its own ruleset - ruleSetFactory.setMinimumPriority(configuration.getMinimumPriority()); - ruleSetFactory.setWarnDeprecated(true); - String ruleSets = configuration.getRuleSets(); - if (StringUtil.isNotEmpty(ruleSets)) { - // Substitute env variables/properties - configuration.setRuleSets(getProject().replaceProperties(ruleSets)); - } - RuleSets rules = ruleSetFactory.createRuleSets(configuration.getRuleSets()); - ruleSetFactory.setWarnDeprecated(false); - logRulesUsed(rules); - } catch (RuleSetNotFoundException e) { - throw new BuildException(e.getMessage(), e); - } + public void setAuxClasspath(Path auxClasspath) { + this.auxClasspath = auxClasspath; + } - if (configuration.getSuppressMarker() != null) { - log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); - } + public Path getAuxClasspath() { + return auxClasspath; + } - // Start the Formatters - for (Formatter formatter : formatters) { - log("Sending a report to " + formatter, Project.MSG_VERBOSE); - formatter.start(getProject().getBaseDir().toString()); - } + public Path createAuxClasspath() { + if (auxClasspath == null) { + auxClasspath = new Path(getProject()); + } + return auxClasspath.createPath(); + } - //log("Setting Language Version " + languageVersion.getShortName(), Project.MSG_VERBOSE); + public void setAuxClasspathRef(Reference r) { + createAuxClasspath().setRefid(r); + } - // TODO Do we really need all this in a loop over each FileSet? Seems like a lot of redundancy - RuleContext ctx = new RuleContext(); - Report errorReport = new Report(); - final AtomicInteger reportSize = new AtomicInteger(); - final String separator = System.getProperty("file.separator"); + public void addRuleset(RuleSetWrapper r) { + nestedRules.add(r); + } - for (FileSet fs : filesets) { - List files = new LinkedList(); - DirectoryScanner ds = fs.getDirectoryScanner(getProject()); - String[] srcFiles = ds.getIncludedFiles(); - for (String srcFile : srcFiles) { - File file = new File(ds.getBasedir() + separator + srcFile); - files.add(new FileDataSource(file)); - } + public List getFormatters() { + return formatters; + } - final String inputPaths = ds.getBasedir().getPath(); - configuration.setInputPaths(inputPaths); + public List getFilesets() { + return filesets; + } - Renderer logRenderer = new AbstractRenderer("log", "Logging renderer") { - public void start() { - // Nothing to do - } + public boolean isFailOnError() { + return failOnError; + } - public void startFileAnalysis(DataSource dataSource) { - log("Processing file " + dataSource.getNiceFileName(false, inputPaths), Project.MSG_VERBOSE); - } + public boolean isFailOnRuleViolation() { + return failOnRuleViolation; + } - public void renderFileReport(Report r) { - int size = r.size(); - if (size > 0) { - reportSize.addAndGet(size); - } - } + public boolean isShortFilenames() { + return shortFilenames; + } - public void end() { - // Nothing to do - } + public String getSuppressMarker() { + return suppressMarker; + } - public String defaultFileExtension() { return null; } // not relevant - }; - List renderers = new LinkedList(); - renderers.add(logRenderer); - for (Formatter formatter : formatters) { - renderers.add(formatter.getRenderer()); - } - try { - PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers); - } catch (RuntimeException pmde) { - handleError(ctx, errorReport, pmde); - } - } + public String getRulesetFiles() { + return rulesetFiles; + } - int problemCount = reportSize.get(); - log(problemCount + " problems found", Project.MSG_VERBOSE); + public String getEncoding() { + return encoding; + } - for (Formatter formatter : formatters) { - formatter.end(errorReport); - } + public int getThreads() { + return threads; + } - if (failuresPropertyName != null && problemCount > 0) { - getProject().setProperty(failuresPropertyName, String.valueOf(problemCount)); - log("Setting property " + failuresPropertyName + " to " + problemCount, Project.MSG_VERBOSE); - } + public int getMinimumPriority() { + return minimumPriority; + } - if (failOnRuleViolation && problemCount > maxRuleViolations) { - throw new BuildException("Stopping build since PMD found " + problemCount + " rule violations in the code"); - } - } + public int getMaxRuleViolations() { + return maxRuleViolations; + } - private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) { - - pmde.printStackTrace(); - log(pmde.toString(), Project.MSG_VERBOSE); - - Throwable cause = pmde.getCause(); - - if (cause != null) { - StringWriter strWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(strWriter); - cause.printStackTrace(printWriter); - log(strWriter.toString(), Project.MSG_VERBOSE); - IOUtils.closeQuietly(printWriter); - - if (StringUtil.isNotEmpty(cause.getMessage())) { - log(cause.getMessage(), Project.MSG_VERBOSE); - } - } - - if (failOnError) { - throw new BuildException(pmde); - } - errorReport.addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename())); - } + public String getFailuresPropertyName() { + return failuresPropertyName; + } - private void setupClassLoader() { + public SourceLanguage getSourceLanguage() { + return sourceLanguage; + } - if (classpath == null) { - log("Using the normal ClassLoader", Project.MSG_VERBOSE); - } else { - log("Using the AntClassLoader", Project.MSG_VERBOSE); - // must be true, otherwise you'll get ClassCastExceptions as classes are loaded twice - // and exist in multiple class loaders - boolean parentFirst = true; - configuration.setClassLoader( - new AntClassLoader(Thread.currentThread().getContextClassLoader(), getProject(), - classpath, parentFirst)); - } - try { - /* - * 'basedir' is added to the path to make sure that relative paths - * such as "resources/custom_ruleset.xml" still - * work when ant is invoked from a different directory using "-f" - */ - configuration.prependClasspath(getProject().getBaseDir().toString()); - if (auxClasspath != null) { - log("Using auxclasspath: " + auxClasspath, Project.MSG_VERBOSE); - configuration.prependClasspath(auxClasspath.toString()); - } - } catch (IOException ioe) { - throw new BuildException(ioe.getMessage(), ioe); - } - } - - @Override - public void execute() throws BuildException { - validate(); - final Handler antLogHandler = new AntLogHandler(this); - final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(Level.FINEST, antLogHandler); - try { - doTask(); - } finally { - logManager.close(); - } - } - - private void logRulesUsed(RuleSets rules) { - log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE); - - RuleSet[] ruleSets = rules.getAllRuleSets(); - for (RuleSet ruleSet : ruleSets) { - for (Rule rule : ruleSet.getRules()) { - log("Using rule " + rule.getName(), Project.MSG_VERBOSE); - } - } - } - - private void validate() throws BuildException { - if (formatters.isEmpty()) { - Formatter defaultFormatter = new Formatter(); - defaultFormatter.setType("text"); - defaultFormatter.setToConsole(true); - formatters.add(defaultFormatter); - } else { - for (Formatter f : formatters) { - if (f.isNoOutputSupplied()) { - throw new BuildException("toFile or toConsole needs to be specified in Formatter"); - } - } - } - - if (configuration.getRuleSets() == null) { - if (nestedRules.isEmpty()) { - throw new BuildException("No rulesets specified"); - } - configuration.setRuleSets(getNestedRuleSetFiles()); - } - } - - private String getNestedRuleSetFiles() { - final StringBuilder sb = new StringBuilder(); - for (Iterator it = nestedRules.iterator(); it.hasNext();) { - RuleSetWrapper rs = it.next(); - sb.append(rs.getFile()); - if (it.hasNext()) { - sb.append(','); - } - } - return sb.toString(); - } - - public void addRuleset(RuleSetWrapper r) { - nestedRules.add(r); - } + public Collection getNestedRules() { + return nestedRules; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/SourceLanguage.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/SourceLanguage.java index e3fb3fb768..bd0e7fb4e4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/ant/SourceLanguage.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/SourceLanguage.java @@ -28,6 +28,6 @@ public class SourceLanguage { } public String toString() { - return ""; + return ""; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java new file mode 100644 index 0000000000..50a21ab297 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -0,0 +1,279 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd.ant.internal; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Handler; +import java.util.logging.Level; + +import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.ant.Formatter; +import net.sourceforge.pmd.ant.PMDTask; +import net.sourceforge.pmd.ant.SourceLanguage; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.renderers.AbstractRenderer; +import net.sourceforge.pmd.renderers.Renderer; +import net.sourceforge.pmd.util.StringUtil; +import net.sourceforge.pmd.util.datasource.DataSource; +import net.sourceforge.pmd.util.datasource.FileDataSource; +import net.sourceforge.pmd.util.log.AntLogHandler; +import net.sourceforge.pmd.util.log.ScopedLogHandlersManager; + +import org.apache.commons.io.IOUtils; +import org.apache.tools.ant.AntClassLoader; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; + +public class PMDTaskImpl { + + private Path classpath; + private Path auxClasspath; + private final List formatters = new ArrayList(); + private final List filesets = new ArrayList(); + private final PMDConfiguration configuration = new PMDConfiguration(); + private boolean failOnError; + private boolean failOnRuleViolation; + private int maxRuleViolations = 0; + private String failuresPropertyName; + private Project project; + + public PMDTaskImpl(PMDTask task) { + configuration.setReportShortNames(task.isShortFilenames()); + configuration.setSuppressMarker(task.getSuppressMarker()); + this.failOnError = task.isFailOnError(); + this.failOnRuleViolation = task.isFailOnRuleViolation(); + this.maxRuleViolations = task.getMaxRuleViolations(); + if (this.maxRuleViolations > 0) { + this.failOnRuleViolation = true; + } + configuration.setRuleSets(task.getRulesetFiles()); + if (task.getEncoding() != null) { + configuration.setSourceEncoding(task.getEncoding()); + } + configuration.setThreads(task.getThreads()); + this.failuresPropertyName = task.getFailuresPropertyName(); + configuration.setMinimumPriority(RulePriority.valueOf(task.getMinimumPriority())); + + SourceLanguage version = task.getSourceLanguage(); + if (version != null) { + LanguageVersion languageVersion = LanguageRegistry.findLanguageVersionByTerseName(version.getName() + " " + + version.getVersion()); + if (languageVersion == null) { + throw new BuildException("The following language is not supported:" + version + "."); + } + configuration.setDefaultLanguageVersion(languageVersion); + } + + classpath = task.getClasspath(); + auxClasspath = task.getAuxClasspath(); + + filesets.addAll(task.getFilesets()); + formatters.addAll(task.getFormatters()); + + project = task.getProject(); + } + + private void doTask() { + setupClassLoader(); + + // Setup RuleSetFactory and validate RuleSets + RuleSetFactory ruleSetFactory = new RuleSetFactory(); + ruleSetFactory.setClassLoader(configuration.getClassLoader()); + try { + // This is just used to validate and display rules. Each thread will + // create its own ruleset + ruleSetFactory.setMinimumPriority(configuration.getMinimumPriority()); + ruleSetFactory.setWarnDeprecated(true); + String ruleSets = configuration.getRuleSets(); + if (StringUtil.isNotEmpty(ruleSets)) { + // Substitute env variables/properties + configuration.setRuleSets(project.replaceProperties(ruleSets)); + } + RuleSets rules = ruleSetFactory.createRuleSets(configuration.getRuleSets()); + ruleSetFactory.setWarnDeprecated(false); + logRulesUsed(rules); + } catch (RuleSetNotFoundException e) { + throw new BuildException(e.getMessage(), e); + } + + if (configuration.getSuppressMarker() != null) { + project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); + } + + // Start the Formatters + for (Formatter formatter : formatters) { + project.log("Sending a report to " + formatter, Project.MSG_VERBOSE); + formatter.start(project.getBaseDir().toString()); + } + + // log("Setting Language Version " + languageVersion.getShortName(), + // Project.MSG_VERBOSE); + + // TODO Do we really need all this in a loop over each FileSet? Seems + // like a lot of redundancy + RuleContext ctx = new RuleContext(); + Report errorReport = new Report(); + final AtomicInteger reportSize = new AtomicInteger(); + final String separator = System.getProperty("file.separator"); + + for (FileSet fs : filesets) { + List files = new LinkedList(); + DirectoryScanner ds = fs.getDirectoryScanner(project); + String[] srcFiles = ds.getIncludedFiles(); + for (String srcFile : srcFiles) { + File file = new File(ds.getBasedir() + separator + srcFile); + files.add(new FileDataSource(file)); + } + + final String inputPaths = ds.getBasedir().getPath(); + configuration.setInputPaths(inputPaths); + + Renderer logRenderer = new AbstractRenderer("log", "Logging renderer") { + public void start() { + // Nothing to do + } + + public void startFileAnalysis(DataSource dataSource) { + project.log("Processing file " + dataSource.getNiceFileName(false, inputPaths), Project.MSG_VERBOSE); + } + + public void renderFileReport(Report r) { + int size = r.size(); + if (size > 0) { + reportSize.addAndGet(size); + } + } + + public void end() { + // Nothing to do + } + + public String defaultFileExtension() { + return null; + } // not relevant + }; + List renderers = new LinkedList(); + renderers.add(logRenderer); + for (Formatter formatter : formatters) { + renderers.add(formatter.getRenderer()); + } + try { + PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers); + } catch (RuntimeException pmde) { + handleError(ctx, errorReport, pmde); + } + } + + int problemCount = reportSize.get(); + project.log(problemCount + " problems found", Project.MSG_VERBOSE); + + for (Formatter formatter : formatters) { + formatter.end(errorReport); + } + + if (failuresPropertyName != null && problemCount > 0) { + project.setProperty(failuresPropertyName, String.valueOf(problemCount)); + project.log("Setting property " + failuresPropertyName + " to " + problemCount, Project.MSG_VERBOSE); + } + + if (failOnRuleViolation && problemCount > maxRuleViolations) { + throw new BuildException("Stopping build since PMD found " + problemCount + " rule violations in the code"); + } + } + + private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) { + + pmde.printStackTrace(); + project.log(pmde.toString(), Project.MSG_VERBOSE); + + Throwable cause = pmde.getCause(); + + if (cause != null) { + StringWriter strWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(strWriter); + cause.printStackTrace(printWriter); + project.log(strWriter.toString(), Project.MSG_VERBOSE); + IOUtils.closeQuietly(printWriter); + + if (StringUtil.isNotEmpty(cause.getMessage())) { + project.log(cause.getMessage(), Project.MSG_VERBOSE); + } + } + + if (failOnError) { + throw new BuildException(pmde); + } + errorReport.addError(new Report.ProcessingError(pmde.getMessage(), ctx.getSourceCodeFilename())); + } + + private void setupClassLoader() { + + if (classpath == null) { + project.log("Using the normal ClassLoader", Project.MSG_VERBOSE); + } else { + project.log("Using the AntClassLoader", Project.MSG_VERBOSE); + // must be true, otherwise you'll get ClassCastExceptions as classes + // are loaded twice + // and exist in multiple class loaders + boolean parentFirst = true; + configuration.setClassLoader(new AntClassLoader(Thread.currentThread().getContextClassLoader(), project, + classpath, parentFirst)); + } + try { + /* + * 'basedir' is added to the path to make sure that relative paths + * such as "resources/custom_ruleset.xml" still + * work when ant is invoked from a different directory using "-f" + */ + configuration.prependClasspath(project.getBaseDir().toString()); + if (auxClasspath != null) { + project.log("Using auxclasspath: " + auxClasspath, Project.MSG_VERBOSE); + configuration.prependClasspath(auxClasspath.toString()); + } + } catch (IOException ioe) { + throw new BuildException(ioe.getMessage(), ioe); + } + } + + public void execute() throws BuildException { + final Handler antLogHandler = new AntLogHandler(project); + final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(Level.FINEST, antLogHandler); + try { + doTask(); + } finally { + logManager.close(); + } + } + + private void logRulesUsed(RuleSets rules) { + project.log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE); + + RuleSet[] ruleSets = rules.getAllRuleSets(); + for (RuleSet ruleSet : ruleSets) { + for (Rule rule : ruleSet.getRules()) { + project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE); + } + } + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDTask.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDTask.java index 3d843c7e6d..266af0ae95 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDTask.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDTask.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cpd; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -53,6 +54,9 @@ public class CPDTask extends Task { private List filesets = new ArrayList(); public void execute() throws BuildException { + ClassLoader oldClassloader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(CPDTask.class.getClassLoader()); + try { validateFields(); @@ -82,6 +86,8 @@ public class CPDTask extends Task { re.printStackTrace(); log(re.toString(), Project.MSG_ERR); throw new BuildException("ReportException during task execution", re); + } finally { + Thread.currentThread().setContextClassLoader(oldClassloader); } } @@ -146,9 +152,16 @@ public class CPDTask extends Task { private void validateFields() throws BuildException { if (minimumTokenCount == 0) { throw new BuildException("minimumTokenCount is required and must be greater than zero"); - } else if (filesets.isEmpty()) { + } + + if (filesets.isEmpty()) { throw new BuildException("Must include at least one FileSet"); } + + if (!Arrays.asList(LanguageFactory.supportedLanguages).contains(language)) { + throw new BuildException("Language " + language + " is not supported. Available languages: " + + Arrays.toString(LanguageFactory.supportedLanguages)); + } } public void addFileset(FileSet set) { @@ -184,15 +197,15 @@ public class CPDTask extends Task { } public void setFormat(FormatAttribute formatAttribute) { - format = formatAttribute.getValue(); + this.format = formatAttribute.getValue(); } - public void setLanguage(LanguageAttribute languageAttribute) { - language = languageAttribute.getValue(); + public void setLanguage(String language) { + this.language = language; } - public void setEncoding(String encodingValue) { - encoding = encodingValue; + public void setEncoding(String encoding) { + this.encoding = encoding; } public static class FormatAttribute extends EnumeratedAttribute { @@ -201,10 +214,4 @@ public class CPDTask extends Task { return FORMATS; } } - - public static class LanguageAttribute extends EnumeratedAttribute { - public String[] getValues() { - return LanguageFactory.supportedLanguages; - } - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java index 62d00a3b56..26332000cd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/log/AntLogHandler.java @@ -10,9 +10,7 @@ import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; - import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; /** * AntLogHandler sends log messages to an Ant Task, so the regular Ant logging @@ -21,12 +19,12 @@ import org.apache.tools.ant.Task; * @author Wouter Zelle */ public class AntLogHandler extends Handler { - private Task antTask; + private Project project; private static final Formatter FORMATTER = new PmdLogFormatter(); - public AntLogHandler(Task antTask) { - this.antTask = antTask; + public AntLogHandler(Project project) { + this.project = project; } public void publish(LogRecord logRecord) { @@ -47,12 +45,12 @@ public class AntLogHandler extends Handler { throw new IllegalStateException("Unknown logging level"); //shouldn't get ALL or NONE } - antTask.log(FORMATTER.format(logRecord), antLevel); + project.log(FORMATTER.format(logRecord), antLevel); if (logRecord.getThrown() != null) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter, true); logRecord.getThrown().printStackTrace(printWriter); - antTask.log(stringWriter.toString(), antLevel); + project.log(stringWriter.toString(), antLevel); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java index d6979a3a34..eba3591edd 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java @@ -31,6 +31,6 @@ public class PMDTaskTest extends BuildFileTest { @Test public void testInvalidLanguageVersion() { - expectBuildExceptionContaining("testInvalidLanguageVersion", "Fail requested.", "The following language is not supported:."); + expectBuildExceptionContaining("testInvalidLanguageVersion", "Fail requested.", "The following language is not supported:."); } } diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml index e5d72645d4..96caefd243 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml @@ -8,7 +8,7 @@ - + diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index 09b6eaa0c3..1a24312a54 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -17,3 +17,4 @@ * [#1263](https://sourceforge.net/p/pmd/bugs/1263/): PMD reports CheckResultSet violation in completely unrelated source files. * [#1272](https://sourceforge.net/p/pmd/bugs/1272/): varargs in methods are causing IndexOutOfBoundException when trying to process files * [#1273](https://sourceforge.net/p/pmd/bugs/1273/): CheckResultSet false positive in try-with-resources nested in if +* [#1274](https://sourceforge.net/p/pmd/bugs/1274/): ant integration broken with pmd-5.2.0