Coverage Report - org.apache.maven.plugin.docck.AbstractCheckDocumentationMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractCheckDocumentationMojo
0%
0/185
0%
0/98
4,133
 
 1  
 package org.apache.maven.plugin.docck;
 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.commons.httpclient.Credentials;
 23  
 import org.apache.commons.httpclient.HttpClient;
 24  
 import org.apache.commons.httpclient.HttpException;
 25  
 import org.apache.commons.httpclient.UsernamePasswordCredentials;
 26  
 import org.apache.commons.httpclient.auth.AuthScope;
 27  
 import org.apache.commons.httpclient.methods.HeadMethod;
 28  
 import org.apache.commons.httpclient.params.HttpMethodParams;
 29  
 import org.apache.maven.model.IssueManagement;
 30  
 import org.apache.maven.model.License;
 31  
 import org.apache.maven.model.Organization;
 32  
 import org.apache.maven.model.Prerequisites;
 33  
 import org.apache.maven.model.Scm;
 34  
 import org.apache.maven.plugin.AbstractMojo;
 35  
 import org.apache.maven.plugin.MojoExecutionException;
 36  
 import org.apache.maven.plugin.MojoFailureException;
 37  
 import org.apache.maven.plugin.docck.reports.DocumentationReport;
 38  
 import org.apache.maven.plugin.docck.reports.DocumentationReporter;
 39  
 import org.apache.maven.project.MavenProject;
 40  
 import org.apache.maven.settings.Proxy;
 41  
 import org.apache.maven.settings.Settings;
 42  
 import org.apache.maven.shared.model.fileset.FileSet;
 43  
 import org.apache.maven.shared.model.fileset.util.FileSetManager;
 44  
 import org.codehaus.plexus.util.IOUtil;
 45  
 import org.codehaus.plexus.util.StringUtils;
 46  
 
 47  
 import java.io.File;
 48  
 import java.io.FileWriter;
 49  
 import java.io.IOException;
 50  
 import java.net.MalformedURLException;
 51  
 import java.net.URL;
 52  
 import java.util.ArrayList;
 53  
 import java.util.Iterator;
 54  
 import java.util.LinkedHashMap;
 55  
 import java.util.List;
 56  
 import java.util.Map;
 57  
 
 58  
 /**
 59  
  * Performs the heavy lifting for documentation checks. This is designed to be
 60  
  * reused for other types of projects, too.
 61  
  *
 62  
  * @author jdcasey
 63  
  */
 64  
 public abstract class AbstractCheckDocumentationMojo
 65  
     extends AbstractMojo
 66  
 {
 67  
     private static final int HTTP_STATUS_200 = 200;
 68  
 
 69  
     /**
 70  
      * @parameter default-value="${reactorProjects}"
 71  
      * @readonly
 72  
      * @required
 73  
      */
 74  
     private List reactorProjects;
 75  
 
 76  
     /**
 77  
      * An optional location where the results will be written to. If this is
 78  
      * not specified the results will be written to the console.
 79  
      *
 80  
      * @parameter expression="${output}"
 81  
      */
 82  
     private File output;
 83  
 
 84  
     /**
 85  
      * Directory where the site source for the project is located.
 86  
      *
 87  
      * @parameter expression="${siteDirectory}" default-value="src/site"
 88  
      * @todo should be determined programmatically
 89  
      */
 90  
     protected String siteDirectory;
 91  
 
 92  
     /**
 93  
      * Sets whether this plugin is running in offline or online mode. Also
 94  
      * useful when you don't want to verify http URLs.
 95  
      *
 96  
      * @parameter expression="${settings.offline}"
 97  
      */
 98  
     private boolean offline;
 99  
 
 100  
     /**
 101  
      * The current user system settings for use in Maven.
 102  
      *
 103  
      * @parameter expression="${settings}"
 104  
      * @required
 105  
      * @readonly
 106  
      */
 107  
     private Settings settings;
 108  
 
 109  
     private HttpClient httpClient;
 110  
 
 111  0
     private FileSetManager fileSetManager = new FileSetManager();
 112  
 
 113  0
     private List validUrls = new ArrayList();
 114  
 
 115  
     protected AbstractCheckDocumentationMojo()
 116  0
     {
 117  0
         String httpUserAgent = "maven-docck-plugin/1.x" + " (Java " + System.getProperty( "java.version" ) + "; "
 118  
                 + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
 119  
 
 120  0
         httpClient = new HttpClient();
 121  
 
 122  0
         final int connectionTimeout = 5000;
 123  0
         httpClient.getHttpConnectionManager().getParams().setConnectionTimeout( connectionTimeout );
 124  0
         httpClient.getParams().setParameter( HttpMethodParams.USER_AGENT, httpUserAgent );
 125  0
     }
 126  
 
 127  
     protected List getReactorProjects()
 128  
     {
 129  0
         return reactorProjects;
 130  
     }
 131  
 
 132  
     public void execute()
 133  
         throws MojoExecutionException, MojoFailureException
 134  
     {
 135  0
         setupProxy();
 136  
 
 137  0
         if ( output != null )
 138  
         {
 139  0
             getLog().info( "Writing documentation check results to: " + output );
 140  
         }
 141  
 
 142  0
         Map reporters = new LinkedHashMap();
 143  0
         boolean hasErrors = false;
 144  
 
 145  0
         for ( Iterator it = reactorProjects.iterator(); it.hasNext(); )
 146  
         {
 147  0
             MavenProject project = (MavenProject) it.next();
 148  
 
 149  0
             if ( approveProjectPackaging( project.getPackaging() ) )
 150  
             {
 151  0
                 getLog().info( "Checking project: " + project.getName() );
 152  
 
 153  0
                 DocumentationReporter reporter = new DocumentationReporter();
 154  
 
 155  0
                 checkProject( project, reporter );
 156  
 
 157  0
                 if ( !hasErrors && reporter.hasErrors() )
 158  
                 {
 159  0
                     hasErrors = true;
 160  
                 }
 161  
 
 162  0
                 reporters.put( project, reporter );
 163  
             }
 164  
             else
 165  
             {
 166  0
                 getLog().info( "Skipping unsupported project: " + project.getName() );
 167  
             }
 168  
         }
 169  
 
 170  
         String messages;
 171  
 
 172  0
         messages = buildErrorMessages( reporters );
 173  
 
 174  0
         if ( !hasErrors )
 175  
         {
 176  0
             messages += "No documentation errors were found.";
 177  
         }
 178  
 
 179  
         try
 180  
         {
 181  0
             writeMessages( messages, hasErrors );
 182  
         }
 183  0
         catch ( IOException e )
 184  
         {
 185  0
             throw new MojoExecutionException( "Error writing results to output file: " + output );
 186  0
         }
 187  
 
 188  0
         if ( hasErrors )
 189  
         {
 190  
             String logLocation;
 191  0
             if ( output == null )
 192  
             {
 193  0
                 logLocation = "Please see the console output above for more information.";
 194  
             }
 195  
             else
 196  
             {
 197  0
                 logLocation = "Please see \'" + output + "\' for more information.";
 198  
             }
 199  
 
 200  0
             throw new MojoFailureException( "Documentation problems were found. " + logLocation );
 201  
         }
 202  0
     }
 203  
 
 204  
     /**
 205  
      * Setup proxy access if needed.
 206  
      */
 207  
     private void setupProxy()
 208  
     {
 209  0
         Proxy settingsProxy = settings.getActiveProxy();
 210  
 
 211  0
         if ( settingsProxy != null )
 212  
         {
 213  0
             String proxyUsername = settingsProxy.getUsername();
 214  
 
 215  0
             String proxyPassword = settingsProxy.getPassword();
 216  
 
 217  0
             String proxyHost = settingsProxy.getHost();
 218  
 
 219  0
             int proxyPort = settingsProxy.getPort();
 220  
 
 221  0
             if ( StringUtils.isNotEmpty( proxyHost ) )
 222  
             {
 223  0
                 httpClient.getHostConfiguration().setProxy( proxyHost, proxyPort );
 224  
 
 225  0
                 getLog().info( "Using proxy [" + proxyHost + "] at port [" + proxyPort + "]." );
 226  
 
 227  0
                 if ( StringUtils.isNotEmpty( proxyUsername ) )
 228  
                 {
 229  0
                     getLog().info( "Using proxy user [" + proxyUsername + "]." );
 230  
 
 231  0
                     Credentials creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
 232  
 
 233  0
                     httpClient.getState().setProxyCredentials( new AuthScope( proxyHost, proxyPort ), creds );
 234  0
                     httpClient.getParams().setAuthenticationPreemptive( true );
 235  
                 }
 236  
             }
 237  
         }
 238  0
     }
 239  
 
 240  
     private String buildErrorMessages( Map reporters )
 241  
     {
 242  0
         String messages = "";
 243  0
         StringBuffer buffer = new StringBuffer();
 244  
 
 245  0
         for ( Iterator it = reporters.entrySet().iterator(); it.hasNext(); )
 246  
         {
 247  0
             Map.Entry entry = (Map.Entry) it.next();
 248  
 
 249  0
             MavenProject project = (MavenProject) entry.getKey();
 250  0
             DocumentationReporter reporter = (DocumentationReporter) entry.getValue();
 251  
 
 252  0
             if ( !reporter.getMessages().isEmpty() )
 253  
             {
 254  0
                 buffer.append( "\no " ).append( project.getName() );
 255  0
                 buffer.append( " (" );
 256  0
                 final int numberOfErrors = reporter.getMessagesByType( DocumentationReport.TYPE_ERROR ).size();
 257  0
                 buffer.append( numberOfErrors ).append( " error" ).append( numberOfErrors == 1 ? "" : "s" );
 258  0
                 buffer.append( ", " );
 259  0
                 final int numberOfWarnings = reporter.getMessagesByType( DocumentationReport.TYPE_WARN ).size();
 260  0
                 buffer.append( numberOfWarnings ).append( " warning" ).append( numberOfWarnings == 1 ? "" : "s" );
 261  0
                 buffer.append( ")" );
 262  0
                 buffer.append( "\n" );
 263  
 
 264  0
                 for ( Iterator errorIterator = reporter.getMessages().iterator(); errorIterator.hasNext(); )
 265  
                 {
 266  0
                     String error = (String) errorIterator.next();
 267  
 
 268  0
                     buffer.append( "  " ).append( error ).append( "\n" );
 269  
                 }
 270  
             }
 271  
         }
 272  
 
 273  0
         if ( buffer.length() > 0 )
 274  
         {
 275  0
             messages = "The following documentation problems were found:\n" + buffer.toString();
 276  
         }
 277  
 
 278  0
         return messages;
 279  
     }
 280  
 
 281  
     protected abstract boolean approveProjectPackaging( String packaging );
 282  
 
 283  
     /**
 284  
      * Writes the text in messages either to a file or to the console.
 285  
      *
 286  
      * @param messages The message text
 287  
      * @param hasErrors If there were any documentation errors
 288  
      * @throws IOException
 289  
      */
 290  
     private void writeMessages( String messages, boolean hasErrors )
 291  
         throws IOException
 292  
     {
 293  0
         if ( output != null )
 294  
         {
 295  0
             FileWriter writer = null;
 296  
 
 297  
             try
 298  
             {
 299  0
                 writer = new FileWriter( output );
 300  0
                 writer.write( messages );
 301  0
                 writer.flush();
 302  
             }
 303  
             finally
 304  
             {
 305  0
                 IOUtil.close( writer );
 306  0
             }
 307  
         }
 308  
         else
 309  
         {
 310  0
             if ( hasErrors )
 311  
             {
 312  0
                 getLog().error( messages );
 313  
             }
 314  
             else
 315  
             {
 316  0
                 getLog().info( messages );
 317  
             }
 318  
         }
 319  0
     }
 320  
 
 321  
     private void checkProject( MavenProject project, DocumentationReporter reporter )
 322  
     {
 323  0
         checkPomRequirements( project, reporter );
 324  
 
 325  0
         checkPackagingSpecificDocumentation( project, reporter );
 326  0
     }
 327  
 
 328  
     private void checkPomRequirements( MavenProject project, DocumentationReporter reporter )
 329  
     {
 330  0
         checkProjectLicenses( project, reporter );
 331  
 
 332  0
         if ( StringUtils.isEmpty( project.getName() ) )
 333  
         {
 334  0
             reporter.error( "pom.xml is missing the <name> tag." );
 335  
         }
 336  
 
 337  0
         if ( StringUtils.isEmpty( project.getDescription() ) )
 338  
         {
 339  0
             reporter.error( "pom.xml is missing the <description> tag." );
 340  
         }
 341  
 
 342  0
         if ( StringUtils.isEmpty( project.getUrl() ) )
 343  
         {
 344  0
             reporter.error( "pom.xml is missing the <url> tag." );
 345  
         }
 346  
         else
 347  
         {
 348  0
             checkURL( project.getUrl(), "project site", reporter );
 349  
         }
 350  
 
 351  0
         if ( project.getIssueManagement() == null )
 352  
         {
 353  0
             reporter.error( "pom.xml is missing the <issueManagement> tag." );
 354  
         }
 355  
         else
 356  
         {
 357  0
             IssueManagement issueMngt = project.getIssueManagement();
 358  0
             if ( StringUtils.isEmpty( issueMngt.getUrl() ) )
 359  
             {
 360  0
                 reporter.error( "pom.xml is missing the <url> tag in <issueManagement>." );
 361  
             }
 362  
             else
 363  
             {
 364  0
                 checkURL( issueMngt.getUrl(), "Issue Management", reporter );
 365  
             }
 366  
         }
 367  
 
 368  0
         if ( project.getPrerequisites() == null )
 369  
         {
 370  0
             reporter.error( "pom.xml is missing the <prerequisites> tag." );
 371  
         }
 372  
         else
 373  
         {
 374  0
             Prerequisites prereq = project.getPrerequisites();
 375  0
             if ( StringUtils.isEmpty( prereq.getMaven() ) )
 376  
             {
 377  0
                 reporter.error( "pom.xml is missing the <prerequisites>/<maven> tag." );
 378  
             }
 379  
         }
 380  
 
 381  0
         if ( StringUtils.isEmpty( project.getInceptionYear() ) )
 382  
         {
 383  0
             reporter.error( "pom.xml is missing the <inceptionYear> tag." );
 384  
         }
 385  
 
 386  0
         if ( project.getMailingLists().size() == 0 )
 387  
         {
 388  0
             reporter.warn( "pom.xml has no <mailingLists>/<mailingList> specified." );
 389  
         }
 390  
 
 391  0
         if ( project.getScm() == null )
 392  
         {
 393  0
             reporter.warn( "pom.xml is missing the <scm> tag." );
 394  
         }
 395  
         else
 396  
         {
 397  0
             Scm scm = project.getScm();
 398  0
             if ( StringUtils.isEmpty( scm.getConnection() ) && StringUtils.isEmpty( scm.getDeveloperConnection() )
 399  
                 && StringUtils.isEmpty( scm.getUrl() ) )
 400  
             {
 401  0
                 reporter.warn( "pom.xml is missing the child tags under the <scm> tag." );
 402  
             }
 403  0
             else if ( scm.getUrl() != null )
 404  
             {
 405  0
                 checkURL( scm.getUrl(), "scm", reporter );
 406  
             }
 407  
         }
 408  
 
 409  0
         if ( project.getOrganization() == null )
 410  
         {
 411  0
             reporter.error( "pom.xml is missing the <organization> tag." );
 412  
         }
 413  
         else
 414  
         {
 415  0
             Organization org = project.getOrganization();
 416  0
             if ( StringUtils.isEmpty( org.getName() ) )
 417  
             {
 418  0
                 reporter.error( "pom.xml is missing the <organization>/<name> tag." );
 419  
             }
 420  0
             else if ( org.getUrl() != null )
 421  
             {
 422  0
                 checkURL( org.getUrl(), org.getName() + " site", reporter );
 423  
             }
 424  
         }
 425  0
     }
 426  
 
 427  
     private void checkProjectLicenses( MavenProject project, DocumentationReporter reporter )
 428  
     {
 429  0
         List licenses = project.getLicenses();
 430  
 
 431  0
         if ( licenses == null || licenses.isEmpty() )
 432  
         {
 433  0
             reporter.error( "pom.xml has no <licenses>/<license> specified." );
 434  
         }
 435  
         else
 436  
         {
 437  0
             for ( Iterator it = licenses.iterator(); it.hasNext(); )
 438  
             {
 439  0
                 License license = (License) it.next();
 440  
 
 441  0
                 if ( StringUtils.isEmpty( license.getName() ) )
 442  
                 {
 443  0
                     reporter.error( "pom.xml is missing the <licenses>/<license>/<name> tag." );
 444  
                 }
 445  
                 else
 446  
                 {
 447  0
                     String url = license.getUrl();
 448  0
                     if ( StringUtils.isEmpty( url ) )
 449  
                     {
 450  0
                         reporter.error( "pom.xml is missing the <licenses>/<license>/<url> tag for the license \'"
 451  
                             + license.getName() + "\'." );
 452  
                     }
 453  
                     else
 454  
                     {
 455  0
                         checkURL( url, "license \'" + license.getName() + "\'", reporter );
 456  
                     }
 457  
                 }
 458  
             }
 459  
         }
 460  0
     }
 461  
 
 462  
     private String getURLProtocol( String url )
 463  
         throws MalformedURLException
 464  
     {
 465  
         String protocol;
 466  
 
 467  0
         URL licenseUrl = new URL( url );
 468  0
         protocol = licenseUrl.getProtocol();
 469  
 
 470  0
         if ( protocol != null )
 471  
         {
 472  0
             protocol = protocol.toLowerCase();
 473  
         }
 474  
 
 475  0
         return protocol;
 476  
     }
 477  
 
 478  
     private void checkURL( String url, String description, DocumentationReporter reporter )
 479  
     {
 480  
         try
 481  
         {
 482  0
             String protocol = getURLProtocol( url );
 483  
 
 484  0
             if ( protocol.startsWith( "http" ) )
 485  
             {
 486  0
                 if ( offline )
 487  
                 {
 488  0
                     reporter.warn( "Cannot verify " + description + " in offline mode with URL: \'" + url + "\'." );
 489  
                 }
 490  0
                 else if ( !validUrls.contains( url ) )
 491  
                 {
 492  0
                     HeadMethod headMethod = new HeadMethod( url );
 493  0
                     headMethod.setFollowRedirects( true );
 494  0
                     headMethod.setDoAuthentication( false );
 495  
 
 496  
                     try
 497  
                     {
 498  0
                         getLog().debug( "Verifying http url: " + url );
 499  0
                         if ( httpClient.executeMethod( headMethod ) != HTTP_STATUS_200 )
 500  
                         {
 501  0
                             reporter.error( "Cannot reach " + description + " with URL: \'" + url + "\'." );
 502  
                         }
 503  
                         else
 504  
                         {
 505  0
                             validUrls.add( url );
 506  
                         }
 507  
                     }
 508  0
                     catch ( HttpException e )
 509  
                     {
 510  0
                         reporter.error( "Cannot reach " + description + " with URL: \'" + url + "\'.\nError: "
 511  
                             + e.getMessage() );
 512  
                     }
 513  0
                     catch ( IOException e )
 514  
                     {
 515  0
                         reporter.error( "Cannot reach " + description + " with URL: \'" + url + "\'.\nError: "
 516  
                             + e.getMessage() );
 517  
                     }
 518  
                     finally
 519  
                     {
 520  0
                         headMethod.releaseConnection();
 521  0
                     }
 522  
                 }
 523  
             }
 524  
             else
 525  
             {
 526  0
                 reporter.warn( "Non-HTTP " + description + " URL not verified." );
 527  
             }
 528  
         }
 529  0
         catch ( MalformedURLException e )
 530  
         {
 531  0
             reporter.warn( "The " + description + " appears to have an invalid URL \'" + url + "\'."
 532  
                 + " Message: \'" + e.getMessage() + "\'. Trying to access it as a file instead." );
 533  
 
 534  0
             checkFile( url, description, reporter );
 535  0
         }
 536  0
     }
 537  
 
 538  
     private void checkFile( String url, String description, DocumentationReporter reporter )
 539  
     {
 540  0
         File licenseFile = new File( url );
 541  0
         if ( !licenseFile.exists() )
 542  
         {
 543  0
             reporter.error( "The " + description + " in file \'" + licenseFile.getPath() + "\' does not exist." );
 544  
         }
 545  0
     }
 546  
 
 547  
     protected abstract void checkPackagingSpecificDocumentation( MavenProject project, DocumentationReporter reporter );
 548  
 
 549  
     protected boolean findFiles( File siteDirectory, String pattern )
 550  
     {
 551  0
         FileSet fs = new FileSet();
 552  0
         fs.setDirectory( siteDirectory.getAbsolutePath() );
 553  0
         fs.setFollowSymlinks( false );
 554  
 
 555  0
         fs.addInclude( "apt/" + pattern + ".apt" );
 556  0
         fs.addInclude( "apt/" + pattern + ".apt.vm" );
 557  0
         fs.addInclude( "xdoc/" + pattern + ".xml" );
 558  0
         fs.addInclude( "xdoc/" + pattern + ".xml.vm" );
 559  0
         fs.addInclude( "fml/" + pattern + ".fml" );
 560  0
         fs.addInclude( "fml/" + pattern + ".fml.vm" );
 561  0
         fs.addInclude( "resources/" + pattern + ".html" );
 562  0
         fs.addInclude( "resources/" + pattern + ".html.vm" );
 563  
 
 564  0
         String[] includedFiles = fileSetManager.getIncludedFiles( fs );
 565  
 
 566  0
         return includedFiles != null && includedFiles.length > 0;
 567  
     }
 568  
 }