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 java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileReader;
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Map.Entry;
32  import java.util.Properties;
33  import java.util.Set;
34  
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.MojoFailureException;
37  import org.apache.maven.plugin.pmd.model.PmdErrorDetail;
38  import org.apache.maven.plugin.pmd.model.PmdFile;
39  import org.apache.maven.plugin.pmd.model.Violation;
40  import org.apache.maven.plugin.pmd.model.io.xpp3.PmdXpp3Reader;
41  import org.apache.maven.plugins.annotations.Execute;
42  import org.apache.maven.plugins.annotations.LifecyclePhase;
43  import org.apache.maven.plugins.annotations.Mojo;
44  import org.apache.maven.plugins.annotations.Parameter;
45  import org.codehaus.plexus.util.IOUtil;
46  import org.codehaus.plexus.util.StringUtils;
47  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
48  
49  /**
50   * Fail the build if there were any PMD violations in the source code.
51   *
52   * @version $Id: PmdViolationCheckMojo.html 938498 2015-01-31 17:43:24Z michaelo $
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( "\\java\\" );
144             return fileName.substring( javaIdx >= 0 ? javaIdx + 6 : 0, fileName.length() - 5 ).replace( '\\', '.' );
145         }
146     }
147 
148     /**
149      * {@inheritDoc}
150      */
151     protected void printError( Violation item, String severity )
152     {
153 
154         StringBuilder buff = new StringBuilder( 100 );
155         buff.append( "PMD " ).append( severity ).append( ": " );
156         if ( item.getViolationClass() != null )
157         {
158             if ( item.getViolationPackage() != null )
159             {
160                 buff.append( item.getViolationPackage() );
161                 buff.append( "." );
162             }
163             buff.append( item.getViolationClass() );
164         }
165         else
166         {
167             buff.append( item.getFileName() );
168         }
169         buff.append( ":" );
170         buff.append( item.getBeginline() );
171         buff.append( " Rule:" ).append( item.getRule() );
172         buff.append( " Priority:" ).append( item.getPriority() );
173         buff.append( " " ).append( item.getText() ).append( "." );
174 
175         this.getLog().info( buff.toString() );
176     }
177 
178     @Override
179     protected List<Violation> getErrorDetails( File pmdFile )
180         throws XmlPullParserException, IOException
181     {
182         PmdXpp3Reader reader = new PmdXpp3Reader();
183         PmdErrorDetail details = reader.read( new FileReader( pmdFile ), false );
184 
185         List<Violation> violations = new ArrayList<Violation>();
186         for ( PmdFile file : details.getFiles() )
187         {
188             String fullPath = file.getName();
189 
190             for ( Violation violation : file.getViolations() )
191             {
192                 violation.setFileName( getFilename( fullPath, violation.getViolationPackage() ) );
193                 violations.add( violation );
194             }
195         }
196         return violations;
197     }
198 
199     @Override
200     protected int getPriority( Violation errorDetail )
201     {
202         return errorDetail.getPriority();
203     }
204 
205     @Override
206     protected ViolationDetails<Violation> newViolationDetailsInstance()
207     {
208         return new ViolationDetails<Violation>();
209     }
210 
211     private String getFilename( String fullpath, String pkg )
212     {
213         int index = fullpath.lastIndexOf( File.separatorChar );
214 
215         while ( StringUtils.isNotEmpty( pkg ) )
216         {
217             index = fullpath.substring( 0, index ).lastIndexOf( File.separatorChar );
218 
219             int dot = pkg.indexOf( '.' );
220 
221             if ( dot < 0 )
222             {
223                 break;
224             }
225             pkg = pkg.substring( dot + 1 );
226         }
227 
228         return fullpath.substring( index + 1 );
229     }
230 }