Skip to content

Commit

Permalink
Bump PMD to 7.8.0 and spotbugs to 4.8.6
Browse files Browse the repository at this point in the history
- Bump the pmd dependency from 6.0.0 to the latest version 7.8.0.
- This is needed in particular for Java 21 support.
- Changed the PmdProcessor to be compatible with the new PMD 7.8.0
  version.
- For PMD we now just use the PmdAnalysis class to perform the PMD
  checks based on our PMDConfiguration, giving back the Report.
- Therefore, we don't need to do our own manual CollectorRenderer
  anymore, so that is now removed.
- Note that the rulesets have changed to be category based in the test
  properties, this will also need to be updated in the properties of end
  users.
- Bump the spotbugs dependency from 4.2.0 to the latest version 4.8.6.
- This is needed for Java 21 support, see spotbugs releasenotes:
  https://github.com/spotbugs/spotbugs/releases/tag/4.8.0
- Need to exclude logback from spotbugs since it's a newer version
  compiled with class version 55 (we are still on class version 52 with
  JDK 1.8).
- The PMD and spotbugs bumps are done in unison as they both use the
  saxon dependency.
  • Loading branch information
Jim Verheijde committed Dec 30, 2024
1 parent 17d84ff commit 85c3258
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 281 deletions.
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ dependencies {
}

// PMD dependencies
implementation('net.sourceforge.pmd:pmd-java:6.0.0') {
implementation('net.sourceforge.pmd:pmd-java:7.8.0') {
exclude group: 'jaxen'
exclude group: 'xerces'
exclude group: 'junit'
Expand All @@ -102,8 +102,9 @@ dependencies {
}

// SpotBugs dependencies
implementation('com.github.spotbugs:spotbugs:4.2.0') {
implementation('com.github.spotbugs:spotbugs:4.8.6') {
exclude group: 'org.slf4j'
exclude group: 'ch.qos.logback'
}

// Scalastyle http://www.scalastyle.org/
Expand Down
102 changes: 0 additions & 102 deletions src/main/java/pl/touk/sputnik/processor/pmd/CollectorRenderer.java

This file was deleted.

164 changes: 81 additions & 83 deletions src/main/java/pl/touk/sputnik/processor/pmd/PmdProcessor.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
package pl.touk.sputnik.processor.pmd;

import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RulesetsFactoryUtils;
import net.sourceforge.pmd.benchmark.Benchmark;
import net.sourceforge.pmd.benchmark.Benchmarker;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.LanguageVersionDiscoverer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.util.ResourceLoader;
import net.sourceforge.pmd.util.datasource.DataSource;

import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.rule.RulePriority;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import net.sourceforge.pmd.reporting.Report;
import net.sourceforge.pmd.reporting.RuleViolation;
import pl.touk.sputnik.configuration.Configuration;
import pl.touk.sputnik.configuration.GeneralOption;
import pl.touk.sputnik.review.Review;
import pl.touk.sputnik.review.ReviewException;
import pl.touk.sputnik.review.ReviewProcessor;
import pl.touk.sputnik.review.ReviewResult;
import pl.touk.sputnik.review.Severity;
import pl.touk.sputnik.review.Violation;
import pl.touk.sputnik.review.filter.PmdFilter;
import pl.touk.sputnik.review.transformer.FileNameTransformer;

import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class PmdProcessor implements ReviewProcessor {
private static final char LINE_SEPARATOR = '\n';
private static final String SOURCE_NAME = "PMD";
private static final char PMD_INPUT_PATH_SEPARATOR = ',';
private Renderer renderer;
private static final String PMD_INPUT_PATH_SEPARATOR = ",";

@NotNull
private final Configuration config;
Expand All @@ -51,22 +47,41 @@ public PmdProcessor(Configuration configuration) {
@Nullable
@Override
public ReviewResult process(@NotNull Review review) {
List<String> filesToReview = review.getFiles(new PmdFilter(), new FileNameTransformer());
List<Path> filesToReview = review.getFiles(new PmdFilter(), new FileNameTransformer()).stream()
.map(file -> {
Path path = FileSystems.getDefault().getPath(file);
if (!path.toFile().exists()) {
throw new ReviewException("File [" + file + "] does not exist");
}
return path;
})
.collect(Collectors.toList());
if (filesToReview.isEmpty()) {
return null;
}

try {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setReportFormat(CollectorRenderer.class.getCanonicalName());
configuration.setRuleSets(getRulesets());
configuration.setInputPaths(Joiner.on(PMD_INPUT_PATH_SEPARATOR).join(filesToReview));
doPMD(configuration);
configuration.setInputPathList(filesToReview);
Report report = doPMD(configuration);
return convertReportToReview(report);
} catch (RuntimeException e) {
log.error("PMD processing error. Something wrong with configuration or analyzed files are not in workspace.", e);
throw new ReviewException("PMD processing error", e);
}
return renderer != null ? ((CollectorRenderer)renderer).getReviewResult() : null;
}

@NotNull
private ReviewResult convertReportToReview(Report report) {
ReviewResult reviewResult = new ReviewResult();
boolean showDetails = Boolean.parseBoolean(config.getProperty(GeneralOption.PMD_SHOW_VIOLATION_DETAILS));
for (RuleViolation ruleViolation : report.getViolations()) {
String violationDescription = showDetails ? renderViolationDetails(ruleViolation) :ruleViolation.getDescription();
FileId myFileId = ruleViolation.getFileId();
reviewResult.add(new Violation(myFileId.getOriginalPath(), ruleViolation.getBeginLine(), violationDescription, convert(ruleViolation.getRule().getPriority())));
}
return reviewResult;
}

@NotNull
Expand All @@ -75,74 +90,57 @@ public String getName() {
return SOURCE_NAME;
}

@Nullable
private String getRulesets() {
private List<String> getRulesets() {
String ruleSets = config.getProperty(GeneralOption.PMD_RULESETS);
log.info("Using PMD rulesets {}", ruleSets);
return ruleSets;
if (ruleSets == null) {
return new ArrayList<>();
}
return Arrays.asList(ruleSets.split(PMD_INPUT_PATH_SEPARATOR));
}

/**
* PMD has terrible design of process configuration. You must use report file with it. I paste this method here and
* improve it.
* Run PMD analysis
*
* @throws IllegalArgumentException
* if the configuration is not correct
* @return Report from PMD
* @throws IllegalArgumentException if the configuration is not correct
*/
private void doPMD(@NotNull PMDConfiguration configuration) throws IllegalArgumentException {
// Load the RuleSets
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, new ResourceLoader());

RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), ruleSetFactory);
// this is just double check - we don't get null here
// instead IllegalArgumentException/RuntimeException is thrown if configuration is wrong
if (ruleSets == null) {
return;
@NotNull
private Report doPMD(@NotNull PMDConfiguration configuration) throws IllegalArgumentException {
try (PmdAnalysis analysis = PmdAnalysis.create(configuration)) {
return analysis.performAnalysisAndCollectReport();
}
}

Set<Language> languages = getApplicableLanguages(configuration, ruleSets);
// this throws RuntimeException when modified file does not exist in workspace
List<DataSource> files = PMD.getApplicableFiles(configuration, languages);

long reportStart = System.nanoTime();
try {
renderer = configuration.createRenderer();
List<Renderer> renderers = new LinkedList<>();
renderers.add(renderer);
renderer.start();

Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);

RuleContext ctx = new RuleContext();

PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers);

reportStart = System.nanoTime();
renderer.end();
} catch (IOException e) {
log.error("PMD analysis error", e);
} finally {
Benchmarker.mark(Benchmark.Reporting, System.nanoTime() - reportStart, 0);
@NotNull
private static Severity convert(@NotNull RulePriority rulePriority) {
switch (rulePriority) {
case HIGH:
return Severity.ERROR;
case MEDIUM_HIGH:
return Severity.WARNING;
case MEDIUM:
case MEDIUM_LOW:
return Severity.INFO;
case LOW:
return Severity.IGNORE;
default:
throw new IllegalArgumentException("RulePriority " + rulePriority + " is not supported");
}
}

/**
* Paste from PMD
*/
private static Set<Language> getApplicableLanguages(PMDConfiguration configuration, RuleSets ruleSets) {
Set<Language> languages = new HashSet<>();
LanguageVersionDiscoverer discoverer = configuration.getLanguageVersionDiscoverer();

for (Rule rule : ruleSets.getAllRules()) {
Language language = rule.getLanguage();
if (languages.contains(language))
continue;
LanguageVersion version = discoverer.getDefaultLanguageVersion(language);
if (RuleSet.applies(rule, version)) {
languages.add(language);
log.debug("Using {} version: {}", language.getShortName(), version.getShortName());
}
private static String renderViolationDetails(RuleViolation ruleViolation) {
StringBuilder fullDescription = new StringBuilder(ruleViolation.getDescription());

String reason = ruleViolation.getRule().getDescription();
if (StringUtils.isNotEmpty(reason)) {
fullDescription.append(LINE_SEPARATOR).append(reason);
}
String url = ruleViolation.getRule().getExternalInfoUrl();
if (StringUtils.isNotEmpty(url)) {
fullDescription.append(LINE_SEPARATOR).append(url);
}
return languages;

return fullDescription.toString();
}
}
Loading

0 comments on commit 85c3258

Please sign in to comment.