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.MojoExecutionException;
23  import org.apache.maven.plugin.MojoFailureException;
24  import org.apache.maven.plugin.pmd.model.PmdErrorDetail;
25  import org.apache.maven.plugin.pmd.model.PmdFile;
26  import org.apache.maven.plugin.pmd.model.Violation;
27  import org.apache.maven.plugin.pmd.model.io.xpp3.PmdXpp3Reader;
28  import org.apache.maven.plugins.annotations.Execute;
29  import org.apache.maven.plugins.annotations.LifecyclePhase;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.codehaus.plexus.util.IOUtil;
33  import org.codehaus.plexus.util.StringUtils;
34  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
35  
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.FileReader;
39  import java.io.IOException;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Map.Entry;
46  import java.util.Properties;
47  import java.util.Set;
48  
49  /**
50   * Fail the build if there were any PMD violations in the source code.
51   *
52   * @version $Id: PmdViolationCheckMojo.html 975451 2015-12-13 21:06:46Z dennisl $
53   * @since 2.0
54   */
55  @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true )
56  @Execute( goal = "pmd" )
57  public class PmdViolationCheckMojo
58      extends AbstractPmdViolationCheckMojo<Violation>
59  {
60      /**
61       * What priority level to fail the build on. Failures at or above this level will stop the build. Anything below
62       * will be warnings and will be displayed in the build output if verbose=true. Note: Minimum Priority = 5 Maximum
63       * Priority = 0
64       */
65      @Parameter( property = "pmd.failurePriority", defaultValue = "5", required = true )
66      private int failurePriority;
67  
68      /**
69       * Skip the PMD checks. Most useful on the command line via "-Dpmd.skip=true".
70       */
71      @Parameter( property = "pmd.skip", defaultValue = "false" )
72      private boolean skip;
73  
74      private final Map<String, Set<String>> excludeFromFailureClasses = new HashMap<String, Set<String>>();
75  
76      /**
77       * {@inheritDoc}
78       */
79      public void execute()
80          throws MojoExecutionException, MojoFailureException
81      {
82          if ( !skip )
83          {
84              executeCheck( "pmd.xml", "violation", "PMD violation", failurePriority );
85          }
86      }
87  
88      @Override
89      protected void loadExcludeFromFailuresData( final String excludeFromFailureFile )
90          throws MojoExecutionException
91      {
92          File file = new File( excludeFromFailureFile );
93          if ( !file.exists() )
94          {
95              return;
96          }
97          final Properties props = new Properties();
98          FileInputStream fileInputStream = null;
99          try
100         {
101             fileInputStream = new FileInputStream( new File( excludeFromFailureFile ) );
102             props.load( fileInputStream );
103         }
104         catch ( final IOException e )
105         {
106             throw new MojoExecutionException( "Cannot load properties file " + excludeFromFailureFile, e );
107         }
108         finally
109         {
110             IOUtil.close( fileInputStream );
111         }
112         for ( final Entry<Object, Object> propEntry : props.entrySet() )
113         {
114             final Set<String> excludedRuleSet = new HashSet<String>();
115             final String className = propEntry.getKey().toString();
116             final String[] excludedRules = propEntry.getValue().toString().split( "," );
117             for ( final String excludedRule : excludedRules )
118             {
119                 excludedRuleSet.add( excludedRule.trim() );
120             }
121             excludeFromFailureClasses.put( className, excludedRuleSet );
122         }
123     }
124 
125     @Override
126     protected boolean isExcludedFromFailure( final Violation errorDetail )
127     {
128         final String className = extractClassName( errorDetail );
129         final Set<String> excludedRuleSet = excludeFromFailureClasses.get( className );
130         return excludedRuleSet != null && excludedRuleSet.contains( errorDetail.getRule() );
131     }
132 
133     private String extractClassName( final Violation errorDetail )
134     {
135         // for some reason, some violations don't contain the package name, so we have to guess the full class name
136         if ( errorDetail.getViolationPackage() != null && errorDetail.getViolationClass() != null )
137         {
138             return errorDetail.getViolationPackage() + "." + errorDetail.getViolationClass();
139         }
140         else
141         {
142             final String fileName = errorDetail.getFileName();
143             final int javaIdx = fileName.indexOf( File.separator + "java" + File.separator );
144             return fileName.substring( javaIdx >= 0 ? javaIdx + 6 : 0, fileName.length() - 5 ).replace(
145                 File.separatorChar, '.' );
146         }
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     protected void printError( Violation item, String severity )
153     {
154 
155         StringBuilder buff = new StringBuilder( 100 );
156         buff.append( "PMD " ).append( severity ).append( ": " );
157         if ( item.getViolationClass() != null )
158         {
159             if ( item.getViolationPackage() != null )
160             {
161                 buff.append( item.getViolationPackage() );
162                 buff.append( "." );
163             }
164             buff.append( item.getViolationClass() );
165         }
166         else
167         {
168             buff.append( item.getFileName() );
169         }
170         buff.append( ":" );
171         buff.append( item.getBeginline() );
172         buff.append( " Rule:" ).append( item.getRule() );
173         buff.append( " Priority:" ).append( item.getPriority() );
174         buff.append( " " ).append( item.getText() ).append( "." );
175 
176         this.getLog().info( buff.toString() );
177     }
178 
179     @Override
180     protected List<Violation> getErrorDetails( File pmdFile )
181         throws XmlPullParserException, IOException
182     {
183         final FileReader reader1 = new FileReader( pmdFile );
184         try
185         {
186             PmdXpp3Reader reader = new PmdXpp3Reader();
187             PmdErrorDetail details = reader.read( reader1, false );
188 
189             List<Violation> violations = new ArrayList<Violation>();
190             for ( PmdFile file : details.getFiles() )
191             {
192                 String fullPath = file.getName();
193 
194                 for ( Violation violation : file.getViolations() )
195                 {
196                     violation.setFileName( getFilename( fullPath, violation.getViolationPackage() ) );
197                     violations.add( violation );
198                 }
199             }
200             return violations;
201         }
202         finally
203         {
204             reader1.close();
205         }
206     }
207 
208     @Override
209     protected int getPriority( Violation errorDetail )
210     {
211         return errorDetail.getPriority();
212     }
213 
214     @Override
215     protected ViolationDetails<Violation> newViolationDetailsInstance()
216     {
217         return new ViolationDetails<Violation>();
218     }
219 
220     private String getFilename( String fullpath, String pkg )
221     {
222         int index = fullpath.lastIndexOf( File.separatorChar );
223 
224         while ( StringUtils.isNotEmpty( pkg ) )
225         {
226             index = fullpath.substring( 0, index ).lastIndexOf( File.separatorChar );
227 
228             int dot = pkg.indexOf( '.' );
229 
230             if ( dot < 0 )
231             {
232                 break;
233             }
234             pkg = pkg.substring( dot + 1 );
235         }
236 
237         return fullpath.substring( index + 1 );
238     }
239 }