Coverage Report - org.apache.maven.plugins.linkcheck.LinkcheckReport
 
Classes in this File Line Coverage Branch Coverage Complexity
LinkcheckReport
0%
0/109
0%
0/36
2,5
 
 1  
 package org.apache.maven.plugins.linkcheck;
 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.IOException;
 24  
 import java.net.URL;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Arrays;
 27  
 import java.util.List;
 28  
 import java.util.Locale;
 29  
 import java.util.Properties;
 30  
 
 31  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 32  
 import org.apache.maven.doxia.linkcheck.HttpBean;
 33  
 import org.apache.maven.doxia.linkcheck.LinkCheck;
 34  
 import org.apache.maven.doxia.linkcheck.LinkCheckException;
 35  
 import org.apache.maven.doxia.linkcheck.model.LinkcheckModel;
 36  
 import org.apache.maven.doxia.siterenderer.Renderer;
 37  
 import org.apache.maven.plugin.MojoExecutionException;
 38  
 import org.apache.maven.project.MavenProject;
 39  
 import org.apache.maven.reporting.AbstractMavenReport;
 40  
 import org.apache.maven.reporting.MavenReportException;
 41  
 import org.apache.maven.settings.Proxy;
 42  
 import org.apache.maven.settings.Settings;
 43  
 import org.codehaus.plexus.i18n.I18N;
 44  
 import org.codehaus.plexus.util.FileUtils;
 45  
 import org.codehaus.plexus.util.ReaderFactory;
 46  
 import org.codehaus.plexus.util.StringUtils;
 47  
 
 48  
 /**
 49  
  * Generates a <code>Linkcheck</code> report.
 50  
  *
 51  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 52  
  * @version $Id: LinkcheckReport.java 1021298 2010-10-11 10:18:19Z ltheussl $
 53  
  * @since 1.0
 54  
  * @goal linkcheck
 55  
  */
 56  0
 public class LinkcheckReport
 57  
     extends AbstractMavenReport
 58  
 {
 59  
     // ----------------------------------------------------------------------
 60  
     // Report Components
 61  
     // ----------------------------------------------------------------------
 62  
 
 63  
     /**
 64  
      * Internationalization.
 65  
      *
 66  
      * @component
 67  
      */
 68  
     private I18N i18n;
 69  
 
 70  
     /**
 71  
      * Doxia Site Renderer.
 72  
      *
 73  
      * @component
 74  
      */
 75  
     private Renderer siteRenderer;
 76  
 
 77  
     /**
 78  
      * LinkCheck component.
 79  
      *
 80  
      * @component
 81  
      */
 82  
     private LinkCheck linkCheck;
 83  
 
 84  
     // ----------------------------------------------------------------------
 85  
     // Report Parameters
 86  
     // ----------------------------------------------------------------------
 87  
 
 88  
     /**
 89  
      * The Maven Project.
 90  
      *
 91  
      * @parameter expression="${project}"
 92  
      * @required
 93  
      * @readonly
 94  
      */
 95  
     private MavenProject project;
 96  
 
 97  
     /**
 98  
      * Local Repository.
 99  
      *
 100  
      * @parameter expression="${localRepository}"
 101  
      * @required
 102  
      * @readonly
 103  
      */
 104  
     private ArtifactRepository localRepository;
 105  
 
 106  
     /**
 107  
      * Report output directory.
 108  
      *
 109  
      * @parameter expression="${project.reporting.outputDirectory}"
 110  
      * @required
 111  
      */
 112  
     private File outputDirectory;
 113  
 
 114  
     /**
 115  
      * The Maven Settings.
 116  
      *
 117  
      * @parameter default-value="${settings}"
 118  
      * @required
 119  
      * @readonly
 120  
      */
 121  
     private Settings settings;
 122  
 
 123  
     // ----------------------------------------------------------------------
 124  
     // Linkcheck parameters
 125  
     // ----------------------------------------------------------------------
 126  
 
 127  
     /**
 128  
      * Whether we are offline or not.
 129  
      *
 130  
      * @parameter default-value="${settings.offline}" expression="${linkcheck.offline}"
 131  
      * @required
 132  
      */
 133  
     private boolean offline;
 134  
 
 135  
     /**
 136  
      * If online, the HTTP method should automatically follow HTTP redirects,
 137  
      * <tt>false</tt> otherwise.
 138  
      *
 139  
      * @parameter default-value="true"
 140  
      */
 141  
     private boolean httpFollowRedirect;
 142  
 
 143  
     /**
 144  
      * The location of the Linkcheck cache file.
 145  
      *
 146  
      * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.cache"
 147  
      * @required
 148  
      */
 149  
     protected File linkcheckCache;
 150  
 
 151  
     /**
 152  
      * The location of the Linkcheck report file.
 153  
      *
 154  
      * @parameter default-value="${project.build.directory}/linkcheck/linkcheck.xml"
 155  
      * @required
 156  
      */
 157  
     protected File linkcheckOutput;
 158  
 
 159  
     /**
 160  
      * The HTTP method to use. Currently supported are "GET" and "HEAD".
 161  
      * <dl>
 162  
      * <dt>HTTP GET</dt>
 163  
      * <dd>
 164  
      * The HTTP GET method is defined in section 9.3 of
 165  
      * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
 166  
      * The GET method means retrieve whatever information (in the form of an
 167  
      * entity) is identified by the Request-URI.
 168  
      * </dd>
 169  
      * <dt>HTTP HEAD</dt>
 170  
      * <dd>
 171  
      * The HTTP HEAD method is defined in section 9.4 of
 172  
      * <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC2616</a>:
 173  
      * The HEAD method is identical to GET except that the server MUST NOT
 174  
      * return a message-body in the response.
 175  
      * </dd>
 176  
      * </dl>
 177  
      *
 178  
      * @parameter default-value="head"
 179  
      * @required
 180  
      */
 181  
     private String httpMethod;
 182  
 
 183  
     /**
 184  
      * The list of HTTP errors to ignored, like <code>404</code>.
 185  
      *
 186  
      * @parameter
 187  
      * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values.
 188  
      */
 189  
     private Integer[] excludedHttpStatusErrors;
 190  
 
 191  
     /**
 192  
      * The list of HTTP warnings to ignored, like <code>301</code>.
 193  
      *
 194  
      * @parameter
 195  
      * @see {@link org.apache.commons.httpclient.HttpStatus} for all defined values.
 196  
      */
 197  
     private Integer[] excludedHttpStatusWarnings;
 198  
 
 199  
     /**
 200  
      * A list of pages to exclude.
 201  
      * <br/>
 202  
      * <b>Note</b>:
 203  
      * <br/>
 204  
      * <ul>
 205  
      * <li>This report, i.e. <code>linkcheck.html</code>, is always excluded.</li>
 206  
      * <li>May contain Ant-style wildcards and double wildcards, e.g. <code>apidocs/**</code>, etc.</li>
 207  
      * </ul>
 208  
      *
 209  
      * @parameter
 210  
      */
 211  
     private String[] excludedPages;
 212  
 
 213  
     /**
 214  
      * The list of links to exclude.
 215  
      * <br/>
 216  
      * <b>Note</b>: Patterns like <code>&#42;&#42;/dummy/&#42;</code> are allowed for excludedLink.
 217  
      *
 218  
      * @parameter
 219  
      */
 220  
     private String[] excludedLinks;
 221  
 
 222  
     /**
 223  
      * The file encoding to use when Linkcheck reads the source files. If the property
 224  
      * <code>project.build.sourceEncoding</code> is not set, the platform default encoding is used.
 225  
      *
 226  
      * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
 227  
      */
 228  
     private String encoding;
 229  
 
 230  
     /**
 231  
      * The extra HttpClient parameters to be used when fetching links. For instance:
 232  
      * <pre>
 233  
      * &lt;httpClientParameters&gt;
 234  
      * &nbsp;&lt;property&gt;
 235  
      * &nbsp;&nbsp;&lt;name&gt;http.protocol.max-redirects&lt;/name&gt;
 236  
      * &nbsp;&nbsp;&lt;value&gt;10&lt;/value&gt;
 237  
      * &nbsp;&lt;/property&gt;
 238  
      * &lt;/httpClientParameters&gt;
 239  
      * </pre>
 240  
      * See <a href="http://hc.apache.org/httpclient-3.x/preference-api.html">HttpClient preference page</a>
 241  
      *
 242  
      * @parameter expression="${httpClientParameters}"
 243  
      */
 244  
     private Properties httpClientParameters;
 245  
 
 246  
     /**
 247  
      * Set the timeout to be used when fetching links. A value of zero means the timeout is not used.
 248  
      *
 249  
      * @parameter expression="${timeout}" default-value="2000"
 250  
      */
 251  
     private int timeout;
 252  
 
 253  
     /**
 254  
      * <code>true</code> to skip the report execution, <code>false</code> otherwise.
 255  
      * The purpose is to prevent infinite call when {@link #forceSite} is enable.
 256  
      *
 257  
      * @parameter expression="${linkcheck.skip}" default-value="false"
 258  
      */
 259  
     private boolean skip;
 260  
 
 261  
     /**
 262  
      * <code>true</code> to force the site generation, <code>false</code> otherwise.
 263  
      * Using this parameter ensures that all documents have been correctly generated.
 264  
      *
 265  
      * @parameter expression="${linkcheck.forceSite}" default-value="true"
 266  
      */
 267  
     private boolean forceSite;
 268  
 
 269  
     /**
 270  
      * The base URL to use for absolute links (eg <code>/index.html</code>) in the site.
 271  
      *
 272  
      * @parameter expression="${linkcheck.baseURL}" default-value="${project.url}"
 273  
      */
 274  
     private String baseURL;
 275  
 
 276  
     // ----------------------------------------------------------------------
 277  
     // Instance fields
 278  
     // ----------------------------------------------------------------------
 279  
 
 280  
     /** Result of the linkcheck in {@link #execute()} */
 281  
     private LinkcheckModel result;
 282  
 
 283  
     protected static final String ICON_SUCCESS = "images/icon_success_sml.gif";
 284  
     protected static final String ICON_WARNING = "images/icon_warning_sml.gif";
 285  
     protected static final String ICON_INFO = "images/icon_info_sml.gif";
 286  
     protected static final String ICON_ERROR = "images/icon_error_sml.gif";
 287  
     private static final String pluginResourcesBase = "org/apache/maven/plugin/linkcheck";
 288  0
     private static final String resourceNames[] = { ICON_SUCCESS, ICON_WARNING, ICON_INFO, ICON_ERROR };
 289  
 
 290  
     // ----------------------------------------------------------------------
 291  
     // Public methods
 292  
     // ----------------------------------------------------------------------
 293  
 
 294  
     /** {@inheritDoc} */
 295  
     public String getDescription( Locale locale )
 296  
     {
 297  0
         return i18n.getString( "linkcheck-report", locale, "report.linkcheck.description" );
 298  
     }
 299  
 
 300  
     /** {@inheritDoc} */
 301  
     public String getName( Locale locale )
 302  
     {
 303  0
         return i18n.getString( "linkcheck-report", locale, "report.linkcheck.name" );
 304  
     }
 305  
 
 306  
     /** {@inheritDoc} */
 307  
     public String getOutputName()
 308  
     {
 309  0
         return "linkcheck";
 310  
     }
 311  
 
 312  
     /** {@inheritDoc} */
 313  
     public boolean canGenerateReport()
 314  
     {
 315  0
         if ( skip )
 316  
         {
 317  0
             return false;
 318  
         }
 319  
 
 320  0
         return true;
 321  
     }
 322  
 
 323  
     /** {@inheritDoc} */
 324  
     public void execute()
 325  
         throws MojoExecutionException
 326  
     {
 327  0
         if ( !canGenerateReport() )
 328  
         {
 329  0
             return;
 330  
         }
 331  
 
 332  0
         checkEncoding();
 333  
 
 334  
         try
 335  
         {
 336  0
             result = executeLinkCheck( getBasedir() );
 337  
         }
 338  0
         catch ( LinkCheckException e )
 339  
         {
 340  0
             throw new MojoExecutionException( "LinkCheckException: " + e.getMessage(), e );
 341  0
         }
 342  0
     }
 343  
 
 344  
     // ----------------------------------------------------------------------
 345  
     // Protected methods
 346  
     // ----------------------------------------------------------------------
 347  
 
 348  
     /** {@inheritDoc} */
 349  
     protected String getOutputDirectory()
 350  
     {
 351  0
         return outputDirectory.getAbsolutePath();
 352  
     }
 353  
 
 354  
     /** {@inheritDoc} */
 355  
     protected MavenProject getProject()
 356  
     {
 357  0
         return project;
 358  
     }
 359  
 
 360  
     /** {@inheritDoc} */
 361  
     protected Renderer getSiteRenderer()
 362  
     {
 363  0
         return siteRenderer;
 364  
     }
 365  
 
 366  
     /** {@inheritDoc} */
 367  
     protected void executeReport( Locale locale )
 368  
         throws MavenReportException
 369  
     {
 370  0
         if ( result == null )
 371  
         {
 372  0
             getLog().debug( "Calling execute()" );
 373  
 
 374  
             try
 375  
             {
 376  0
                 this.execute();
 377  
             }
 378  0
             catch ( MojoExecutionException e )
 379  
             {
 380  0
                 throw new MavenReportException( "MojoExecutionException: " + e.getMessage(), e );
 381  0
             }
 382  
         }
 383  
 
 384  0
         if ( result != null )
 385  
         {
 386  0
             generateReport( locale, result );
 387  
             // free memory
 388  0
             result = null;
 389  
         }
 390  0
     }
 391  
 
 392  
     // ----------------------------------------------------------------------
 393  
     // Private methods
 394  
     // ----------------------------------------------------------------------
 395  
 
 396  
     private void checkEncoding()
 397  
     {
 398  0
         if ( StringUtils.isEmpty( encoding ) )
 399  
         {
 400  0
             if ( getLog().isWarnEnabled() )
 401  
             {
 402  0
                 getLog().warn( "File encoding has not been set, using platform encoding "
 403  
                     + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
 404  
             }
 405  
 
 406  0
             encoding = ReaderFactory.FILE_ENCODING;
 407  
         }
 408  0
     }
 409  
 
 410  
     private File getBasedir()
 411  
         throws MojoExecutionException
 412  
     {
 413  
         File basedir;
 414  
 
 415  0
         if ( forceSite )
 416  
         {
 417  0
             basedir = new File( linkcheckOutput.getParentFile(), "tmpsite" );
 418  0
             basedir.mkdirs();
 419  
 
 420  0
             List documents = null;
 421  
             try
 422  
             {
 423  0
                 documents = FileUtils.getFiles( basedir, "**/*.html", null );
 424  
             }
 425  0
             catch ( IOException e )
 426  
             {
 427  0
                 getLog().error( "IOException: " + e.getMessage() );
 428  0
                 getLog().debug( e );
 429  0
             }
 430  
 
 431  
             // if the site was not already generated, invoke it
 432  0
             if ( documents == null || ( documents != null && documents.size() == 0 ) )
 433  
             {
 434  0
                 getLog().info( "Invoking the maven-site-plugin to ensure that all files are generated..." );
 435  
 
 436  
                 try
 437  
                 {
 438  0
                     SiteInvoker invoker = new SiteInvoker( localRepository, getLog() );
 439  0
                     invoker.invokeSite( project, basedir );
 440  
                 }
 441  0
                 catch ( IOException e )
 442  
                 {
 443  0
                     throw new MojoExecutionException( "IOException: " + e.getMessage(), e );
 444  0
                 }
 445  
             }
 446  0
         }
 447  
         else
 448  
         {
 449  0
             getLog().warn( "The number of documents analyzed by Linkcheck could differ from the actual "
 450  
                                    + "number of documents!" );
 451  
 
 452  0
             basedir = outputDirectory;
 453  0
             basedir.mkdirs();
 454  
         }
 455  
 
 456  0
         return basedir;
 457  
     }
 458  
 
 459  
     /**
 460  
      * Execute the <code>Linkcheck</code> tool.
 461  
      *
 462  
      * @param basedir not null
 463  
      * @throws LinkCheckException if any
 464  
      */
 465  
     private LinkcheckModel executeLinkCheck( File basedir )
 466  
         throws LinkCheckException
 467  
     {
 468  
         // Wrap linkcheck
 469  0
         linkCheck.setOnline( !offline );
 470  0
         linkCheck.setBasedir( basedir );
 471  0
         linkCheck.setBaseURL( baseURL );
 472  0
         linkCheck.setReportOutput( linkcheckOutput );
 473  0
         linkCheck.setLinkCheckCache( linkcheckCache );
 474  0
         linkCheck.setExcludedLinks( excludedLinks );
 475  0
         linkCheck.setExcludedPages( getExcludedPages() );
 476  0
         linkCheck.setExcludedHttpStatusErrors( asIntArray( excludedHttpStatusErrors ) );
 477  0
         linkCheck.setExcludedHttpStatusWarnings( asIntArray( excludedHttpStatusWarnings ) );
 478  0
         linkCheck.setEncoding( ( StringUtils.isNotEmpty( encoding ) ? encoding : ReaderFactory.UTF_8 ) );
 479  
 
 480  0
         HttpBean bean = new HttpBean();
 481  0
         bean.setMethod( httpMethod );
 482  0
         bean.setFollowRedirects( httpFollowRedirect );
 483  0
         bean.setTimeout( timeout );
 484  0
         if ( httpClientParameters != null )
 485  
         {
 486  0
             bean.setHttpClientParameters( httpClientParameters );
 487  
         }
 488  
 
 489  0
         Proxy proxy = settings.getActiveProxy();
 490  0
         if ( proxy != null )
 491  
         {
 492  0
             bean.setProxyHost( proxy.getHost() );
 493  0
             bean.setProxyPort( proxy.getPort() );
 494  0
             bean.setProxyUser( proxy.getUsername() );
 495  0
             bean.setProxyPassword( proxy.getPassword() );
 496  
         }
 497  0
         linkCheck.setHttp( bean );
 498  
 
 499  0
         return linkCheck.execute();
 500  
     }
 501  
 
 502  
     /**
 503  
      * @return the excludedPages defined by the user and also this report.
 504  
      */
 505  
     private String[] getExcludedPages()
 506  
     {
 507  0
         List pagesToExclude =
 508  
             ( excludedPages != null ? new ArrayList( Arrays.asList( excludedPages ) ) : new ArrayList() );
 509  
 
 510  
         // Exclude this report
 511  0
         pagesToExclude.add( getOutputName() + ".html" );
 512  
 
 513  0
         return (String[]) pagesToExclude.toArray( new String[0] );
 514  
     }
 515  
 
 516  
     // ----------------------------------------------------------------------
 517  
     // Linkcheck report
 518  
     // ----------------------------------------------------------------------
 519  
 
 520  
     private void generateReport( Locale locale, LinkcheckModel linkcheckModel )
 521  
     {
 522  0
         LinkcheckReportGenerator reportGenerator = new LinkcheckReportGenerator( i18n );
 523  
 
 524  0
         reportGenerator.setExcludedHttpStatusErrors( excludedHttpStatusErrors );
 525  0
         reportGenerator.setExcludedHttpStatusWarnings( excludedHttpStatusWarnings );
 526  0
         reportGenerator.setExcludedLinks( excludedLinks );
 527  0
         reportGenerator.setExcludedPages( excludedPages );
 528  0
         reportGenerator.setHttpFollowRedirect( httpFollowRedirect );
 529  0
         reportGenerator.setHttpMethod( httpMethod );
 530  0
         reportGenerator.setOffline( offline );
 531  
 
 532  0
         reportGenerator.generateReport( locale, linkcheckModel, getSink() );
 533  0
         closeReport();
 534  
 
 535  
         // Copy the images
 536  0
         copyStaticResources();
 537  0
     }
 538  
 
 539  
     private void copyStaticResources()
 540  
     {
 541  
         try
 542  
         {
 543  0
             getLog().debug( "Copying static linkcheck resources." );
 544  0
             for ( int i = 0; i < resourceNames.length; i++ )
 545  
             {
 546  0
                 URL url = this.getClass().getClassLoader().getResource( pluginResourcesBase + "/" + resourceNames[i] );
 547  0
                 FileUtils.copyURLToFile( url, new File( getReportOutputDirectory(), resourceNames[i] ) );
 548  
             }
 549  
         }
 550  0
         catch ( IOException e )
 551  
         {
 552  0
             getLog().error( "Unable to copy icons for linkcheck report." );
 553  0
             getLog().debug( e );
 554  0
         }
 555  0
     }
 556  
 
 557  
     private static int[] asIntArray( Integer[] array )
 558  
     {
 559  0
         if ( array == null )
 560  
         {
 561  0
             return null;
 562  
         }
 563  
 
 564  0
         int[] newArray = new int[array.length];
 565  
 
 566  0
         for ( int i = 0; i < array.length; i++ )
 567  
         {
 568  0
             newArray[i] = array[i].intValue();
 569  
         }
 570  
 
 571  0
         return newArray;
 572  
     }
 573  
 }