All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugin.pmd.PmdReport Maven / Gradle / Ivy

Go to download

A Maven plugin for the PMD toolkit, that produces a report on both code rule violations and detected copy and paste fragments, as well as being able to fail the build based on these metrics.

There is a newer version: 3.9.0
Show newest version
package org.apache.maven.plugin.pmd;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;

import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.RuleSetFactory;
import net.sourceforge.pmd.RuleSetReferenceId;
import net.sourceforge.pmd.benchmark.Benchmarker;
import net.sourceforge.pmd.benchmark.TextReport;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.renderers.CSVRenderer;
import net.sourceforge.pmd.renderers.HTMLRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.TextRenderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.datasource.FileDataSource;

import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.resource.ResourceManager;
import org.codehaus.plexus.resource.loader.FileResourceCreationException;
import org.codehaus.plexus.resource.loader.FileResourceLoader;
import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;

/**
 * Creates a PMD report.
 *
 * @author Brett Porter
 * @version $Id: PmdReport.java 1682450 2015-05-29 12:55:17Z dennisl $
 * @since 2.0
 */
@Mojo( name = "pmd", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
public class PmdReport
    extends AbstractPmdReport
{
    /**
     * The target JDK to analyze based on. Should match the target used in the compiler plugin. Valid values are
     * currently 1.3, 1.4, 1.5, 1.6, 1.7 and
     * 1.8.
     * 

* Note: this parameter is only used if the language parameter is set to java. *

*/ @Parameter( property = "targetJdk", defaultValue = "${maven.compiler.target}" ) private String targetJdk; /** * The programming language to be analyzed by PMD. Valid values are currently java, * javascript and jsp. * * @since 3.0 */ @Parameter( defaultValue = "java" ) private String language; /** * The rule priority threshold; rules with lower priority than this will not be evaluated. * * @since 2.1 */ @Parameter( property = "minimumPriority", defaultValue = "5" ) private int minimumPriority = 5; /** * Skip the PMD report generation. Most useful on the command line via "-Dpmd.skip=true". * * @since 2.1 */ @Parameter( property = "pmd.skip", defaultValue = "false" ) private boolean skip; /** * The PMD rulesets to use. See the Stock Rulesets for a * list of some included. Defaults to the java-basic, java-imports and java-unusedcode rulesets. */ @Parameter private String[] rulesets = new String[] { "java-basic", "java-unusedcode", "java-imports" }; /** * Controls whether the project's compile/test classpath should be passed to PMD to enable its type resolution * feature. * * @since 3.0 */ @Parameter( property = "pmd.typeResolution", defaultValue = "false" ) private boolean typeResolution; /** * Controls whether PMD will track benchmark information. * * @since 3.1 */ @Parameter( property = "pmd.benchmark", defaultValue = "false" ) private boolean benchmark; /** * Benchmark output filename. * * @since 3.1 */ @Parameter( property = "pmd.benchmarkOutputFilename", defaultValue = "${project.build.directory}/pmd-benchmark.txt" ) private String benchmarkOutputFilename; /** * Source level marker used to indicate whether a RuleViolation should be suppressed. If it is not set, PMD's * default will be used, which is NOPMD. See also PMD – Suppressing warnings. * * @since 3.4 */ @Parameter( property = "pmd.suppressMarker" ) private String suppressMarker; /** */ @Component private ResourceManager locator; /** The PMD report listener for collecting violations. */ private PmdReportListener reportListener; /** * per default pmd executions error are ignored to not break the whole * * @since 3.1 */ @Parameter( property = "pmd.skipPmdError", defaultValue = "true" ) private boolean skipPmdError; /** * {@inheritDoc} */ public String getName( Locale locale ) { return getBundle( locale ).getString( "report.pmd.name" ); } /** * {@inheritDoc} */ public String getDescription( Locale locale ) { return getBundle( locale ).getString( "report.pmd.description" ); } public void setRulesets( String[] rules ) { rulesets = rules; } /** * {@inheritDoc} */ @Override public void executeReport( Locale locale ) throws MavenReportException { try { execute( locale ); } finally { if ( getSink() != null ) { getSink().close(); } } } private void execute( Locale locale ) throws MavenReportException { if ( !skip && canGenerateReport() ) { ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() ); Report report = generateReport( locale ); if ( !isHtml() && !isXml() ) { writeNonHtml( report ); } } finally { Thread.currentThread().setContextClassLoader( origLoader ); } } } @Override public boolean canGenerateReport() { if ( skip ) { return false; } boolean result = super.canGenerateReport(); if ( result ) { try { executePmdWithClassloader(); if ( skipEmptyReport ) { result = reportListener.hasViolations(); if ( result ) { getLog().debug( "Skipping report since skipEmptyReport is true and" + "there are no PMD violations." ); } } } catch ( MavenReportException e ) { throw new RuntimeException( e ); } } return result; } private void executePmdWithClassloader() throws MavenReportException { ClassLoader origLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() ); executePmd(); } finally { Thread.currentThread().setContextClassLoader( origLoader ); } } private void executePmd() throws MavenReportException { if ( reportListener != null ) { // PMD has already been run getLog().debug( "PMD has already been run - skipping redundant execution." ); return; } // configure ResourceManager locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() ); locator.addSearchPath( "url", "" ); locator.setOutputDirectory( targetDirectory ); reportListener = new PmdReportListener(); PMDConfiguration pmdConfiguration = getPMDConfiguration(); RuleContext ruleContext = new RuleContext(); ruleContext.getReport().addListener( reportListener ); RuleSetFactory ruleSetFactory = new RuleSetFactory(); ruleSetFactory.setMinimumPriority( RulePriority.valueOf( this.minimumPriority ) ); // Workaround for https://sourceforge.net/p/pmd/bugs/1155/: add a dummy ruleset. String[] presentRulesets = rulesets.length > 0 ? rulesets : new String[] { "/rulesets/dummy.xml" }; String[] sets = new String[presentRulesets.length]; try { for ( int idx = 0; idx < presentRulesets.length; idx++ ) { String set = presentRulesets[idx]; getLog().debug( "Preparing ruleset: " + set ); RuleSetReferenceId id = new RuleSetReferenceId( set ); File ruleset = locator.getResourceAsFile( id.getRuleSetFileName(), getLocationTemp( set ) ); if ( null == ruleset ) { throw new MavenReportException( "Could not resolve " + set ); } sets[idx] = ruleset.getAbsolutePath(); } } catch ( ResourceNotFoundException e ) { throw new MavenReportException( e.getMessage(), e ); } catch ( FileResourceCreationException e ) { throw new MavenReportException( e.getMessage(), e ); } pmdConfiguration.setRuleSets( StringUtils.join( sets, "," ) ); try { if ( filesToProcess == null ) { filesToProcess = getFilesToProcess(); } if ( filesToProcess.isEmpty() && !"java".equals( language ) ) { getLog().warn( "No files found to process. Did you add your additional source folders like javascript?" + " (see also build-helper-maven-plugin)" ); } } catch ( IOException e ) { throw new MavenReportException( "Can't get file list", e ); } String encoding = getSourceEncoding(); if ( StringUtils.isEmpty( encoding ) && !filesToProcess.isEmpty() ) { getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" ); encoding = ReaderFactory.FILE_ENCODING; } pmdConfiguration.setSourceEncoding( encoding ); List dataSources = new ArrayList( filesToProcess.size() ); for ( File f : filesToProcess.keySet() ) { dataSources.add( new FileDataSource( f ) ); } try { getLog().debug( "Executing PMD..." ); PMD.processFiles( pmdConfiguration, ruleSetFactory, dataSources, ruleContext, Collections.emptyList() ); if ( getLog().isDebugEnabled() ) { getLog().debug( "PMD finished. Found " + reportListener.getViolations().size() + " violations." ); } } catch ( Exception e ) { String message = "Failure executing PMD: " + e.getLocalizedMessage(); if ( !skipPmdError ) { throw new MavenReportException( message, e ); } getLog().warn( message, e ); } // if format is XML, we need to output it even if the file list is empty or we have no violations // so the "check" goals can check for violations if ( isXml() && reportListener != null ) { writeNonHtml( reportListener.asReport() ); } if ( benchmark ) { PrintStream benchmarkFileStream = null; try { benchmarkFileStream = new PrintStream( benchmarkOutputFilename ); ( new TextReport() ).generate( Benchmarker.values(), benchmarkFileStream ); } catch ( FileNotFoundException fnfe ) { getLog().error( "Unable to generate benchmark file: " + benchmarkOutputFilename, fnfe ); } finally { if ( null != benchmarkFileStream ) { benchmarkFileStream.close(); } } } } private Report generateReport( Locale locale ) throws MavenReportException { Sink sink = getSink(); PmdReportGenerator renderer = new PmdReportGenerator( getLog(), sink, getBundle( locale ), aggregate ); renderer.setFiles( filesToProcess ); renderer.setViolations( reportListener.getViolations() ); try { renderer.beginDocument(); renderer.render(); renderer.endDocument(); } catch ( IOException e ) { getLog().warn( "Failure creating the report: " + e.getLocalizedMessage(), e ); } return reportListener.asReport(); } /** * Convenience method to get the location of the specified file name. * * @param name the name of the file whose location is to be resolved * @return a String that contains the absolute file name of the file */ protected String getLocationTemp( String name ) { String loc = name; if ( loc.indexOf( '/' ) != -1 ) { loc = loc.substring( loc.lastIndexOf( '/' ) + 1 ); } if ( loc.indexOf( '\\' ) != -1 ) { loc = loc.substring( loc.lastIndexOf( '\\' ) + 1 ); } // MPMD-127 in the case that the rules are defined externally on a url // we need to replace some special url characters that cannot be // used in filenames on disk or produce ackward filenames. // replace all occurrences of the following characters: ? : & = % loc = loc.replaceAll( "[\\?\\:\\&\\=\\%]", "_" ); if ( !loc.endsWith( ".xml" ) ) { loc = loc + ".xml"; } getLog().debug( "Before: " + name + " After: " + loc ); return loc; } /** * Use the PMD renderers to render in any format aside from HTML. * * @param report * @throws MavenReportException */ private void writeNonHtml( Report report ) throws MavenReportException { Renderer r = createRenderer(); if ( r == null ) { return; } Writer writer = null; FileOutputStream tStream = null; try { targetDirectory.mkdirs(); File targetFile = new File( targetDirectory, "pmd." + format ); tStream = new FileOutputStream( targetFile ); writer = new OutputStreamWriter( tStream, getOutputEncoding() ); r.setWriter( writer ); r.start(); r.renderFileReport( report ); r.end(); writer.close(); if ( includeXmlInSite ) { File siteDir = getReportOutputDirectory(); siteDir.mkdirs(); FileUtils.copyFile( targetFile, new File( siteDir, "pmd." + format ) ); } } catch ( IOException ioe ) { throw new MavenReportException( ioe.getMessage(), ioe ); } finally { IOUtil.close( writer ); IOUtil.close( tStream ); } } /** * Constructs the PMD configuration class, passing it an argument that configures the target JDK. * * @return the resulting PMD * @throws org.apache.maven.reporting.MavenReportException if targetJdk is not supported */ public PMDConfiguration getPMDConfiguration() throws MavenReportException { PMDConfiguration configuration = new PMDConfiguration(); LanguageVersion languageVersion = null; if ( ( "java".equals( language ) || null == language ) && null != targetJdk ) { languageVersion = LanguageRegistry.findLanguageVersionByTerseName( "java " + targetJdk ); if ( languageVersion == null ) { throw new MavenReportException( "Unsupported targetJdk value '" + targetJdk + "'." ); } } else if ( "javascript".equals( language ) || "ecmascript".equals( language ) ) { languageVersion = LanguageRegistry.findLanguageVersionByTerseName( "ecmascript" ); } else if ( "jsp".equals( language ) ) { languageVersion = LanguageRegistry.findLanguageVersionByTerseName( "jsp" ); } if ( languageVersion != null ) { getLog().debug( "Using language " + languageVersion ); configuration.setDefaultLanguageVersion( languageVersion ); } if ( typeResolution ) { try { @SuppressWarnings( "unchecked" ) List classpath = includeTests ? project.getTestClasspathElements() : project.getCompileClasspathElements(); getLog().debug( "Using aux classpath: " + classpath ); configuration.prependClasspath( StringUtils.join( classpath.iterator(), File.pathSeparator ) ); } catch ( Exception e ) { throw new MavenReportException( e.getMessage(), e ); } } if ( null != suppressMarker ) { configuration.setSuppressMarker( suppressMarker ); } configuration.setBenchmark( benchmark ); return configuration; } /** * {@inheritDoc} */ public String getOutputName() { return "pmd"; } private static ResourceBundle getBundle( Locale locale ) { return ResourceBundle.getBundle( "pmd-report", locale, PmdReport.class.getClassLoader() ); } /** * Create and return the correct renderer for the output type. * * @return the renderer based on the configured output * @throws org.apache.maven.reporting.MavenReportException if no renderer found for the output type */ public final Renderer createRenderer() throws MavenReportException { Renderer renderer = null; if ( "xml".equals( format ) ) { renderer = new XMLRenderer( getOutputEncoding() ); } else if ( "txt".equals( format ) ) { renderer = new TextRenderer(); } else if ( "csv".equals( format ) ) { renderer = new CSVRenderer(); } else if ( "html".equals( format ) ) { renderer = new HTMLRenderer(); } else if ( !"".equals( format ) && !"none".equals( format ) ) { try { renderer = (Renderer) Class.forName( format ).getConstructor( Properties.class ). newInstance( new Properties() ); } catch ( Exception e ) { throw new MavenReportException( "Can't find PMD custom format " + format + ": " + e.getClass().getName(), e ); } } return renderer; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy