From e3708b5030cc9caeb4fcda628dd84d32338f803d Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 17 Dec 2012 10:51:19 +0100 Subject: [PATCH] pmd: fix #1037 Facing a showstopper issue in PMD Report Class * wrapping the report listeners in a thread safe (synchronized) class * overtaking the listeners for each thread / report --- pmd/etc/changelog.txt | 1 + .../main/java/net/sourceforge/pmd/Report.java | 39 ++++++++++++------- .../java/net/sourceforge/pmd/RuleContext.java | 3 +- .../pmd/SynchronizedReportListener.java | 27 +++++++++++++ .../sourceforge/pmd/util/EmptyIterator.java | 12 +++++- 5 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 pmd/src/main/java/net/sourceforge/pmd/SynchronizedReportListener.java diff --git a/pmd/etc/changelog.txt b/pmd/etc/changelog.txt index cd2cd2c480..697f3ca9ab 100644 --- a/pmd/etc/changelog.txt +++ b/pmd/etc/changelog.txt @@ -1,6 +1,7 @@ ???? ??, 2012 - 5.0.2: Fixed bug 1026: PMD doesn't handle 'value =' in SuppressWarnings annotation +Fixed bug 1037: Facing a showstopper issue in PMD Report Class (report listeners) Fixed bug 1043: node.getEndLine() always returns 0 (ECMAscript) Fixed bug 1044: Unknown option: -excludemarker Fixed bug 1047: False Positive in 'for' loops for LocalVariableCouldBeFinal in 5.0.1 diff --git a/pmd/src/main/java/net/sourceforge/pmd/Report.java b/pmd/src/main/java/net/sourceforge/pmd/Report.java index a39e3ea3bf..9608c8c7c6 100644 --- a/pmd/src/main/java/net/sourceforge/pmd/Report.java +++ b/pmd/src/main/java/net/sourceforge/pmd/Report.java @@ -22,15 +22,18 @@ import net.sourceforge.pmd.util.StringUtil; public class Report { - public static Report createReport(RuleContext ctx, String fileName) { - - Report report = new Report(); - ctx.setReport(report); - ctx.setSourceCodeFilename(fileName); - ctx.setSourceCodeFile(new File(fileName)); - return report; - } - + public static Report createReport(RuleContext ctx, String fileName) { + Report report = new Report(); + + // overtake the listener + report.addSynchronizedListeners(ctx.getReport().getSynchronizedListeners()); + + ctx.setReport(report); + ctx.setSourceCodeFilename(fileName); + ctx.setSourceCodeFile(new File(fileName)); + return report; + } + public static class ReadableDuration { private final long duration; @@ -112,7 +115,7 @@ public class Report { // Note that this and the above data structure are both being maintained for a bit private final List violations = new ArrayList(); private final Set metrics = new HashSet(); - private final List listeners = new ArrayList(); + private final List listeners = new ArrayList(); private List errors; private List configErrors; private Map linesToSuppress = new HashMap(); @@ -164,7 +167,7 @@ public class Report { } public void addListener(ReportListener listener) { - listeners.add(listener); + listeners.add(new SynchronizedReportListener(listener)); } public List getSuppressedRuleViolations() { @@ -266,13 +269,13 @@ public class Report { } public Iterator errors() { - return errors == null ? EmptyIterator.instance : errors.iterator(); + return errors == null ? EmptyIterator. instance() : errors.iterator(); } public Iterator configErrors() { - return configErrors == null ? EmptyIterator.instance : errors.iterator(); + return configErrors == null ? EmptyIterator. instance() : configErrors.iterator(); } - + public int treeSize() { return violationTree.size(); } @@ -292,4 +295,12 @@ public class Report { public long getElapsedTimeInMillis() { return end - start; } + + public List getSynchronizedListeners() { + return listeners; + } + + public void addSynchronizedListeners(List synchronizedListeners) { + listeners.addAll(synchronizedListeners); + } } diff --git a/pmd/src/main/java/net/sourceforge/pmd/RuleContext.java b/pmd/src/main/java/net/sourceforge/pmd/RuleContext.java index e6dae35cbf..ce764ebb94 100644 --- a/pmd/src/main/java/net/sourceforge/pmd/RuleContext.java +++ b/pmd/src/main/java/net/sourceforge/pmd/RuleContext.java @@ -42,10 +42,11 @@ public class RuleContext { } /** - * Constructor which shares attributes with the given RuleContext. + * Constructor which shares attributes and report listeners with the given RuleContext. */ public RuleContext(RuleContext ruleContext) { this.attributes = ruleContext.attributes; + this.report.addSynchronizedListeners(ruleContext.getReport().getSynchronizedListeners()); } /** diff --git a/pmd/src/main/java/net/sourceforge/pmd/SynchronizedReportListener.java b/pmd/src/main/java/net/sourceforge/pmd/SynchronizedReportListener.java new file mode 100644 index 0000000000..9183e88f78 --- /dev/null +++ b/pmd/src/main/java/net/sourceforge/pmd/SynchronizedReportListener.java @@ -0,0 +1,27 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +package net.sourceforge.pmd; + +import net.sourceforge.pmd.stat.Metric; + +/** + * Wraps a report listener in order to synchronize calls to it. + */ +public final class SynchronizedReportListener implements ReportListener { + + private final ReportListener wrapped; + + public SynchronizedReportListener(ReportListener listener) { + this.wrapped = listener; + } + + public synchronized void ruleViolationAdded(RuleViolation ruleViolation) { + wrapped.ruleViolationAdded(ruleViolation); + } + + public synchronized void metricAdded(Metric metric) { + wrapped.metricAdded(metric); + } + +} diff --git a/pmd/src/main/java/net/sourceforge/pmd/util/EmptyIterator.java b/pmd/src/main/java/net/sourceforge/pmd/util/EmptyIterator.java index ec9fe8dabf..0f53b327bf 100644 --- a/pmd/src/main/java/net/sourceforge/pmd/util/EmptyIterator.java +++ b/pmd/src/main/java/net/sourceforge/pmd/util/EmptyIterator.java @@ -1,3 +1,6 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ package net.sourceforge.pmd.util; import java.util.Iterator; @@ -9,10 +12,15 @@ import java.util.Iterator; * * @param */ -@SuppressWarnings("rawtypes") public class EmptyIterator implements Iterator { + @SuppressWarnings("rawtypes") public static final Iterator instance = new EmptyIterator(); + + @SuppressWarnings("unchecked") + public static final Iterator instance() { + return instance; + } private EmptyIterator() {} @@ -23,4 +31,4 @@ public class EmptyIterator implements Iterator { public void remove() { throw new UnsupportedOperationException(); } -}; \ No newline at end of file +} \ No newline at end of file