1 package org.apache.maven.plugins.linkcheck;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.repository.ArtifactRepository;
23 import org.apache.maven.doxia.linkcheck.HttpBean;
24 import org.apache.maven.doxia.linkcheck.LinkCheck;
25 import org.apache.maven.doxia.linkcheck.LinkCheckException;
26 import org.apache.maven.doxia.linkcheck.model.LinkcheckModel;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugins.annotations.Component;
29 import org.apache.maven.plugins.annotations.Mojo;
30 import org.apache.maven.plugins.annotations.Parameter;
31 import org.apache.maven.reporting.AbstractMavenReport;
32 import org.apache.maven.reporting.MavenReportException;
33 import org.apache.maven.settings.Proxy;
34 import org.apache.maven.settings.Settings;
35 import org.codehaus.plexus.i18n.I18N;
36 import org.codehaus.plexus.util.FileUtils;
37 import org.codehaus.plexus.util.ReaderFactory;
38 import org.codehaus.plexus.util.StringUtils;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.net.URL;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Locale;
47 import java.util.Properties;
48
49
50
51
52
53
54
55
56 @Mojo( name = "linkcheck" )
57 public class LinkcheckReport
58 extends AbstractMavenReport
59 {
60
61
62
63
64
65
66
67 @Component
68 private I18N i18n;
69
70
71
72
73 @Component
74 private LinkCheck linkCheck;
75
76
77
78
79
80
81
82
83 @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
84 private ArtifactRepository localRepository;
85
86
87
88
89 @Parameter( defaultValue = "${settings}", readonly = true, required = true )
90 private Settings settings;
91
92
93
94
95
96
97
98
99 @Parameter( property = "linkcheck.offline", defaultValue = "${settings.offline}", required = true )
100 private boolean offline;
101
102
103
104
105
106
107
108 @Parameter( defaultValue = "true" )
109 private boolean httpFollowRedirect;
110
111
112
113
114 @Parameter( defaultValue = "${project.build.directory}/linkcheck/linkcheck.cache", required = true )
115 protected File linkcheckCache;
116
117
118
119
120 @Parameter( defaultValue = "${project.build.directory}/linkcheck/linkcheck.xml", required = true )
121 protected File linkcheckOutput;
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 @Parameter( defaultValue = "head", required = true )
143 private String httpMethod;
144
145
146
147
148
149
150 @Parameter
151 private Integer[] excludedHttpStatusErrors;
152
153
154
155
156
157
158 @Parameter
159 private Integer[] excludedHttpStatusWarnings;
160
161
162
163
164
165
166
167
168
169
170
171 @Parameter
172 private String[] excludedPages;
173
174
175
176
177
178
179 @Parameter
180 private String[] excludedLinks;
181
182
183
184
185
186 @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
187 private String encoding;
188
189
190
191
192
193
194
195
196
197
198
199
200
201 @Parameter( property = "httpClientParameters" )
202 private Properties httpClientParameters;
203
204
205
206
207 @Parameter( property = "timeout", defaultValue = "2000" )
208 private int timeout;
209
210
211
212
213
214 @Parameter( property = "linkcheck.skip", defaultValue = "false" )
215 private boolean skip;
216
217
218
219
220
221 @Parameter( property = "linkcheck.forceSite", defaultValue = "true" )
222 private boolean forceSite;
223
224
225
226
227 @Parameter( property = "linkcheck.baseURL", defaultValue = "${project.url}" )
228 private String baseURL;
229
230
231
232
233
234
235 private LinkcheckModel result;
236
237 protected static final String ICON_SUCCESS = "images/icon_success_sml.gif";
238 protected static final String ICON_WARNING = "images/icon_warning_sml.gif";
239 protected static final String ICON_INFO = "images/icon_info_sml.gif";
240 protected static final String ICON_ERROR = "images/icon_error_sml.gif";
241 private static final String pluginResourcesBase = "org/apache/maven/plugin/linkcheck";
242 private static final String resourceNames[] = { ICON_SUCCESS, ICON_WARNING, ICON_INFO, ICON_ERROR };
243
244
245
246
247
248
249 public String getDescription( Locale locale )
250 {
251 return i18n.getString( "linkcheck-report", locale, "report.linkcheck.description" );
252 }
253
254
255 public String getName( Locale locale )
256 {
257 return i18n.getString( "linkcheck-report", locale, "report.linkcheck.name" );
258 }
259
260
261 public String getOutputName()
262 {
263 return "linkcheck";
264 }
265
266
267 public boolean canGenerateReport()
268 {
269 return !skip;
270 }
271
272
273 public void execute()
274 throws MojoExecutionException
275 {
276 if ( !canGenerateReport() )
277 {
278 return;
279 }
280
281 checkEncoding();
282
283 try
284 {
285 result = executeLinkCheck( getBasedir() );
286 }
287 catch ( LinkCheckException e )
288 {
289 throw new MojoExecutionException( "LinkCheckException: " + e.getMessage(), e );
290 }
291 }
292
293
294
295
296
297
298 protected void executeReport( Locale locale )
299 throws MavenReportException
300 {
301 if ( result == null )
302 {
303 getLog().debug( "Calling execute()" );
304
305 try
306 {
307 this.execute();
308 }
309 catch ( MojoExecutionException e )
310 {
311 throw new MavenReportException( "MojoExecutionException: " + e.getMessage(), e );
312 }
313 }
314
315 if ( result != null )
316 {
317 generateReport( locale, result );
318
319 result = null;
320 }
321 }
322
323
324
325
326
327 private void checkEncoding()
328 {
329 if ( StringUtils.isEmpty( encoding ) )
330 {
331 if ( getLog().isWarnEnabled() )
332 {
333 getLog().warn( "File encoding has not been set, using platform encoding "
334 + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
335 }
336
337 encoding = ReaderFactory.FILE_ENCODING;
338 }
339 }
340
341 private File getBasedir()
342 throws MojoExecutionException
343 {
344 File basedir;
345
346 if ( forceSite )
347 {
348 basedir = new File( linkcheckOutput.getParentFile(), "tmpsite" );
349 basedir.mkdirs();
350
351 List documents = null;
352 try
353 {
354 documents = FileUtils.getFiles( basedir, "**/*.html", null );
355 }
356 catch ( IOException e )
357 {
358 getLog().error( "IOException: " + e.getMessage() );
359 getLog().debug( e );
360 }
361
362
363 if ( documents == null || ( documents != null && documents.size() == 0 ) )
364 {
365 getLog().info( "Invoking the maven-site-plugin to ensure that all files are generated..." );
366
367 try
368 {
369 SiteInvoker invoker = new SiteInvoker( localRepository, getLog() );
370 invoker.invokeSite( project, basedir );
371 }
372 catch ( IOException e )
373 {
374 throw new MojoExecutionException( "IOException: " + e.getMessage(), e );
375 }
376 }
377 }
378 else
379 {
380 getLog().warn( "The number of documents analyzed by Linkcheck could differ from the actual "
381 + "number of documents!" );
382
383 basedir = outputDirectory;
384 basedir.mkdirs();
385 }
386
387 return basedir;
388 }
389
390
391
392
393
394
395
396 private LinkcheckModel executeLinkCheck( File basedir )
397 throws LinkCheckException
398 {
399
400 linkCheck.setOnline( !offline );
401 linkCheck.setBasedir( basedir );
402 linkCheck.setBaseURL( baseURL );
403 linkCheck.setReportOutput( linkcheckOutput );
404 linkCheck.setLinkCheckCache( linkcheckCache );
405 linkCheck.setExcludedLinks( excludedLinks );
406 linkCheck.setExcludedPages( getExcludedPages() );
407 linkCheck.setExcludedHttpStatusErrors( asIntArray( excludedHttpStatusErrors ) );
408 linkCheck.setExcludedHttpStatusWarnings( asIntArray( excludedHttpStatusWarnings ) );
409 linkCheck.setEncoding( ( StringUtils.isNotEmpty( encoding ) ? encoding : ReaderFactory.UTF_8 ) );
410
411 HttpBean bean = new HttpBean();
412 bean.setMethod( httpMethod );
413 bean.setFollowRedirects( httpFollowRedirect );
414 bean.setTimeout( timeout );
415 if ( httpClientParameters != null )
416 {
417 bean.setHttpClientParameters( httpClientParameters );
418 }
419
420 Proxy proxy = settings.getActiveProxy();
421 if ( proxy != null )
422 {
423 bean.setProxyHost( proxy.getHost() );
424 bean.setProxyPort( proxy.getPort() );
425 bean.setProxyUser( proxy.getUsername() );
426 bean.setProxyPassword( proxy.getPassword() );
427 }
428 linkCheck.setHttp( bean );
429
430 return linkCheck.execute();
431 }
432
433
434
435
436 private String[] getExcludedPages()
437 {
438 List pagesToExclude =
439 ( excludedPages != null ? new ArrayList( Arrays.asList( excludedPages ) ) : new ArrayList() );
440
441
442 pagesToExclude.add( getOutputName() + ".html" );
443
444 return (String[]) pagesToExclude.toArray(new String[pagesToExclude.size()]);
445 }
446
447
448
449
450
451 private void generateReport( Locale locale, LinkcheckModel linkcheckModel )
452 {
453 LinkcheckReportGenerator reportGenerator = new LinkcheckReportGenerator( i18n );
454
455 reportGenerator.setExcludedHttpStatusErrors( excludedHttpStatusErrors );
456 reportGenerator.setExcludedHttpStatusWarnings( excludedHttpStatusWarnings );
457 reportGenerator.setExcludedLinks( excludedLinks );
458 reportGenerator.setExcludedPages( excludedPages );
459 reportGenerator.setHttpFollowRedirect( httpFollowRedirect );
460 reportGenerator.setHttpMethod( httpMethod );
461 reportGenerator.setOffline( offline );
462
463 reportGenerator.generateReport( locale, linkcheckModel, getSink() );
464 closeReport();
465
466
467 copyStaticResources();
468 }
469
470 private void copyStaticResources()
471 {
472 try
473 {
474 getLog().debug( "Copying static linkcheck resources." );
475 for (String resourceName : resourceNames) {
476 URL url = this.getClass().getClassLoader().getResource(pluginResourcesBase + "/" + resourceName);
477 FileUtils.copyURLToFile(url, new File(getReportOutputDirectory(), resourceName));
478 }
479 }
480 catch ( IOException e )
481 {
482 getLog().error( "Unable to copy icons for linkcheck report." );
483 getLog().debug( e );
484 }
485 }
486
487 private static int[] asIntArray( Integer[] array )
488 {
489 if ( array == null )
490 {
491 return null;
492 }
493
494 int[] newArray = new int[array.length];
495
496 for ( int i = 0; i < array.length; i++ )
497 {
498 newArray[i] = array[i];
499 }
500
501 return newArray;
502 }
503 }