View Javadoc

1   package org.apache.maven.plugin.pmd;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.AbstractMojo;
23  import org.apache.maven.plugin.MojoExecutionException;
24  import org.apache.maven.plugin.MojoFailureException;
25  import org.apache.maven.plugins.annotations.Component;
26  import org.apache.maven.plugins.annotations.Parameter;
27  import org.apache.maven.project.MavenProject;
28  import org.codehaus.plexus.util.IOUtil;
29  import org.codehaus.plexus.util.StringUtils;
30  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
31  
32  import java.io.File;
33  import java.io.IOException;
34  import java.io.Reader;
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  /**
39   * Base class for mojos that check if there were any PMD violations.
40   *
41   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
42   * @version $Id: AbstractPmdViolationCheckMojo.html 853015 2013-03-04 21:10:54Z olamy $
43   */
44  public abstract class AbstractPmdViolationCheckMojo<D>
45      extends AbstractMojo
46  {
47      /**
48       * The location of the XML report to check, as generated by the PMD report.
49       */
50      @Parameter( property = "project.build.directory", required = true )
51      private File targetDirectory;
52  
53      /**
54       * Whether to fail the build if the validation check fails.
55       */
56      @Parameter( property = "pmd.failOnViolation", defaultValue = "true", required = true )
57      protected boolean failOnViolation;
58  
59      /**
60       * The project language, for determining whether to run the report.
61       */
62      @Parameter( property = "project.artifact.artifactHandler.language", required = true, readonly = true )
63      private String language;
64  
65      /**
66       * Whether to build an aggregated report at the root, or build individual reports.
67       *
68       * @since 2.2
69       */
70      @Parameter( property = "aggregate", defaultValue = "false" )
71      protected boolean aggregate;
72  
73      /**
74       * Print details of check failures to build output.
75       */
76      @Parameter( property = "pmd.verbose", defaultValue = "false" )
77      private boolean verbose;
78  
79      /**
80       * Print details of errors that cause build failure
81       *
82       * @since 3.0
83       */
84      @Parameter(property = "pmd.printFailingErrors", defaultValue = "false")
85      private boolean printFailingErrors;
86  
87      /**
88       * File that lists classes and rules to be excluded from failures
89       * For PMD, this is a properties file
90       * For CPD, this is a text file that contains comma-separated lists of classes that are allowed to duplicate
91       *
92       * @since 3.0
93       */
94      @Parameter(property = "pmd.excludeFromFailureFile", defaultValue = "")
95      private String excludeFromFailureFile;
96  
97      /**
98       * The project to analyze.
99       */
100     @Component
101     protected MavenProject project;
102 
103     protected void executeCheck( final String filename, final String tagName, final String key, final int failurePriority )
104         throws MojoFailureException, MojoExecutionException
105     {
106         if ( aggregate && !project.isExecutionRoot() )
107         {
108             return;
109         }
110 
111         if ( "java".equals( language ) || aggregate )
112         {
113             if ( !StringUtils.isEmpty( excludeFromFailureFile ) )
114             {
115                 loadExcludeFromFailuresData( excludeFromFailureFile );
116             }
117             final File outputFile = new File( targetDirectory, filename );
118 
119             if ( outputFile.exists() )
120             {
121                 final Reader reader = null;
122                 try
123                 {
124                     final ViolationDetails<D> violations = getViolations( outputFile, failurePriority );
125 
126                     final List<D> failures = violations.getFailureDetails();
127                     final List<D> warnings = violations.getWarningDetails();
128 
129                     if ( verbose )
130                     {
131                         printErrors( failures, warnings );
132                     }
133 
134                     final int failureCount = failures.size();
135                     final int warningCount = warnings.size();
136 
137                     final String message = getMessage( failureCount, warningCount, key, outputFile );
138 
139                     getLog().debug( "PMD failureCount: " + failureCount + ", warningCount: " + warningCount );
140 
141                     if ( failureCount > 0 && isFailOnViolation() )
142                     {
143                         throw new MojoFailureException( message );
144                     }
145 
146                     this.getLog().info( message );
147                 }
148                 catch ( final IOException e )
149                 {
150                     throw new MojoExecutionException( "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
151                                                       e );
152                 }
153                 catch ( final XmlPullParserException e )
154                 {
155                     throw new MojoExecutionException( "Unable to read PMD results xml: " + outputFile.getAbsolutePath(),
156                                                       e );
157                 }
158                 finally
159                 {
160                     IOUtil.close( reader );
161                 }
162             }
163             else
164             {
165                 throw new MojoFailureException( "Unable to perform check, " + "unable to find " + outputFile );
166             }
167         }
168     }
169 
170     protected abstract void loadExcludeFromFailuresData( String excludeFromFailureFile )
171         throws MojoExecutionException;
172 
173     /**
174      * Method for collecting the violations found by the PMD tool
175      *
176      * @param analysisFile
177      * @param failurePriority
178      * @return an int that specifies the number of violations found
179      * @throws XmlPullParserException
180      * @throws IOException
181      */
182     private ViolationDetails<D> getViolations( final File analysisFile, final int failurePriority )
183         throws XmlPullParserException, IOException
184     {
185         final List<D> failures = new ArrayList<D>();
186         final List<D> warnings = new ArrayList<D>();
187 
188         final List<D> violations = getErrorDetails( analysisFile );
189 
190         for ( final D violation : violations )
191         {
192             final int priority = getPriority( violation );
193             if ( priority <= failurePriority && !isExcludedFromFailure( violation ) )
194             {
195                 failures.add( violation );
196                 if ( printFailingErrors )
197                 {
198                     printError( violation, "Failure" );
199                 }
200             }
201             else
202             {
203                 warnings.add( violation );
204             }
205         }
206 
207         final ViolationDetails<D> details = newViolationDetailsInstance();
208         details.setFailureDetails( failures );
209         details.setWarningDetails( warnings );
210         return details;
211     }
212 
213     protected abstract int getPriority( D errorDetail );
214 
215     protected abstract boolean isExcludedFromFailure( D errorDetail );
216 
217     protected abstract ViolationDetails<D> newViolationDetailsInstance();
218 
219     /**
220      * Prints the warnings and failures
221      *
222      * @param failures list of failures
223      * @param warnings list of warnings
224      */
225     protected void printErrors( final List<D> failures, final List<D> warnings )
226     {
227         for ( final D warning : warnings )
228         {
229             printError( warning, "Warning" );
230         }
231 
232         for ( final D failure : failures )
233         {
234             printError( failure, "Failure" );
235         }
236     }
237 
238     /**
239      * Gets the output message
240      *
241      * @param failureCount
242      * @param warningCount
243      * @param key
244      * @param outputFile
245      * @return
246      */
247     private String getMessage( final int failureCount, final int warningCount, final String key, final File outputFile )
248     {
249         final StringBuilder message = new StringBuilder( 256 );
250         if ( failureCount > 0 || warningCount > 0 )
251         {
252             if ( failureCount > 0 )
253             {
254                 message.append( "You have " + failureCount + " " + key + ( failureCount > 1 ? "s" : "" ) );
255             }
256 
257             if ( warningCount > 0 )
258             {
259                 if ( failureCount > 0 )
260                 {
261                     message.append( " and " );
262                 }
263                 else
264                 {
265                     message.append( "You have " );
266                 }
267                 message.append( warningCount + " warning" + ( warningCount > 1 ? "s" : "" ) );
268             }
269 
270             message.append( ". For more details see:" ).append( outputFile.getAbsolutePath() );
271         }
272         return message.toString();
273     }
274 
275     /**
276      * Formats the failure details and prints them as an INFO message
277      *
278      * @param item
279      */
280     protected abstract void printError( D item, String severity );
281 
282     /**
283      * Gets the attributes and text for the violation tag and puts them in a
284      * HashMap
285      *
286      * @param analisysFile
287      * @throws XmlPullParserException
288      * @throws IOException
289      */
290     protected abstract List<D> getErrorDetails( File analisysFile )
291         throws XmlPullParserException, IOException;
292 
293     public boolean isFailOnViolation()
294     {
295         return failOnViolation;
296     }
297 }