1 package org.apache.maven.doxia.linkcheck.validation;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.maven.doxia.linkcheck.model.LinkcheckFileResult;
25 import org.codehaus.plexus.util.IOUtil;
26 import org.codehaus.plexus.util.SelectorUtils;
27 import org.codehaus.plexus.util.StringUtils;
28
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.InvalidClassException;
34 import java.io.ObjectInputStream;
35 import java.io.ObjectOutputStream;
36 import java.io.Serializable;
37
38 import java.net.URI;
39 import java.net.URISyntaxException;
40
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.LinkedList;
44 import java.util.List;
45 import java.util.Map;
46
47
48
49
50
51
52
53
54
55
56 public class LinkValidatorManager
57 implements Serializable
58 {
59
60 private static final long serialVersionUID = 2467928182206500945L;
61
62
63 private static final Log LOG = LogFactory.getLog( LinkValidatorManager.class );
64
65
66 private List validators = new LinkedList();
67
68
69 private String[] excludedLinks = new String[0];
70
71
72 private Map cache = new HashMap();
73
74
75
76
77
78
79 public List getValidators()
80 {
81 return this.validators;
82 }
83
84
85
86
87
88
89
90
91 public String[] getExcludedLinks()
92 {
93 return this.excludedLinks;
94 }
95
96
97
98
99
100
101
102
103 public void setExcludedLinks( String[] excl )
104 {
105 this.excludedLinks = excl;
106 }
107
108
109
110
111
112
113 public void addLinkValidator( LinkValidator lv )
114 {
115 this.validators.add( lv );
116 }
117
118
119
120
121
122
123
124 public LinkValidationResult validateLink( LinkValidationItem lvi )
125 {
126 LinkValidationResult cachedResult = getCachedResult( lvi );
127
128 if ( cachedResult != null )
129 {
130 return cachedResult;
131 }
132
133 for ( int i = 0; i < this.excludedLinks.length; i++ )
134 {
135 if ( this.excludedLinks[i] != null && matchPattern( lvi.getLink(), this.excludedLinks[i] ) )
136 {
137 if ( LOG.isDebugEnabled() )
138 {
139 LOG.debug( "Excluded " + lvi.getLink() );
140 }
141
142 return new LinkValidationResult( LinkcheckFileResult.VALID_LEVEL, false, "" );
143 }
144 }
145
146 Iterator iter = this.validators.iterator();
147
148 LinkValidator lv;
149
150 Object resourceKey;
151
152 LinkValidationResult lvr;
153
154 while ( iter.hasNext() )
155 {
156 lv = (LinkValidator) iter.next();
157
158 resourceKey = lv.getResourceKey( lvi );
159
160 if ( resourceKey != null )
161 {
162 if ( LOG.isDebugEnabled() )
163 {
164 LOG.debug( lv.getClass().getName() + " - Checking link " + lvi.getLink() );
165 }
166
167 lvr = lv.validateLink( lvi );
168
169 if ( lvr.getStatus() == LinkValidationResult.NOTMINE )
170 {
171 continue;
172 }
173
174 setCachedResult( resourceKey, lvr );
175
176 return lvr;
177 }
178 }
179
180 lv = null;
181
182 resourceKey = null;
183
184 lvr = null;
185
186 if ( LOG.isErrorEnabled() )
187 {
188 LOG.error( "Unable to validate link : " + lvi.getLink() );
189 }
190
191 return new LinkValidationResult( LinkcheckFileResult.UNKNOWN_LEVEL, false,
192 "No validator found for this link" );
193 }
194
195
196
197
198
199
200
201
202 public void loadCache( File cacheFile )
203 throws IOException
204 {
205 if ( cacheFile == null )
206 {
207 LOG.debug( "No cache file specified! Ignoring request to load." );
208 return;
209 }
210
211 if ( !cacheFile.exists() )
212 {
213 LOG.debug( "Specified cache file does not exist! Ignoring request to load." );
214 return;
215 }
216
217 if ( cacheFile.isDirectory() )
218 {
219 LOG.debug( "Cache file is a directory! Ignoring request to load." );
220 return;
221 }
222
223 ObjectInputStream is = null;
224 try
225 {
226 is = new ObjectInputStream( new FileInputStream( cacheFile ) );
227
228 this.cache = (Map) is.readObject();
229
230 if ( LOG.isDebugEnabled() )
231 {
232 LOG.debug( "Cache file loaded: " + cacheFile.getAbsolutePath() );
233 }
234 }
235 catch ( InvalidClassException e )
236 {
237 LOG.warn( "Your cache is incompatible with this version of linkcheck. It will be recreated." );
238 }
239 catch ( ClassNotFoundException e )
240 {
241 if ( LOG.isErrorEnabled() )
242 {
243 LOG.error( "Unable to load the cache: " + cacheFile.getAbsolutePath(), e );
244 }
245 }
246 finally
247 {
248 IOUtil.close( is );
249 }
250 }
251
252
253
254
255
256
257
258
259 public void saveCache( File cacheFile )
260 throws IOException
261 {
262 if ( cacheFile == null )
263 {
264 LOG.warn( "No cache file specified! Ignoring request to store results." );
265 return;
266 }
267
268 if ( cacheFile.isDirectory() )
269 {
270 LOG.debug( "Cache file is a directory! Ignoring request to load." );
271 return;
272 }
273
274
275 Map persistentCache = new HashMap();
276
277 Iterator iter = this.cache.keySet().iterator();
278
279 Object resourceKey;
280
281 while ( iter.hasNext() )
282 {
283 resourceKey = iter.next();
284
285 if ( ( (LinkValidationResult) this.cache.get( resourceKey ) ).isPersistent() )
286 {
287 persistentCache.put( resourceKey, this.cache.get( resourceKey ) );
288
289 if ( LOG.isDebugEnabled() )
290 {
291 LOG.debug( "[" + resourceKey + "] with result [" + this.cache.get( resourceKey )
292 + "] is stored in the cache." );
293 }
294 }
295 }
296
297 File dir = cacheFile.getParentFile();
298 if ( dir != null )
299 {
300 dir.mkdirs();
301 }
302
303 ObjectOutputStream os = null;
304 try
305 {
306 os = new ObjectOutputStream( new FileOutputStream( cacheFile ) );
307
308 os.writeObject( persistentCache );
309 }
310 finally
311 {
312 persistentCache = null;
313
314 iter = null;
315
316 resourceKey = null;
317
318 cacheFile = null;
319
320 dir = null;
321
322 IOUtil.close( os );
323 }
324 }
325
326
327
328
329
330
331
332
333 public LinkValidationResult getCachedResult( LinkValidationItem lvi )
334 {
335 Iterator iter = getValidators().iterator();
336
337 LinkValidator lv;
338
339 Object resourceKey;
340
341 while ( iter.hasNext() )
342 {
343 lv = (LinkValidator) iter.next();
344
345 resourceKey = lv.getResourceKey( lvi );
346
347 if ( resourceKey != null && this.cache.containsKey( resourceKey ) )
348 {
349 if ( LOG.isDebugEnabled() )
350 {
351 LOG.debug( "The cache returns for [" + resourceKey + "] the result ["
352 + this.cache.get( resourceKey ) + "]." );
353 }
354
355 return (LinkValidationResult) this.cache.get( resourceKey );
356 }
357 }
358
359 lv = null;
360
361 resourceKey = null;
362
363 return null;
364 }
365
366
367
368
369
370
371
372 public void setCachedResult( Object resourceKey, LinkValidationResult lvr )
373 {
374 this.cache.put( resourceKey, lvr );
375 }
376
377
378
379
380
381
382 protected static boolean matchPattern( String link, String pattern )
383 {
384 if ( StringUtils.isEmpty( pattern ) )
385 {
386 return StringUtils.isEmpty( link );
387 }
388
389 if ( pattern.indexOf( '*' ) == -1 )
390 {
391 if ( pattern.endsWith( "/" ) )
392 {
393 return link.indexOf( pattern.substring( 0, pattern.lastIndexOf( '/' ) ) ) != -1;
394 }
395
396 return link.indexOf( pattern ) != -1;
397 }
398
399 try
400 {
401 URI uri = new URI( link );
402
403 if ( uri.getScheme() != null && !pattern.startsWith( uri.getScheme() ) )
404 {
405 return false;
406 }
407 }
408 catch ( URISyntaxException ex )
409 {
410 LOG.debug( "Trying to check link to illegal URI: " + link, ex );
411 }
412
413 if ( pattern.matches( "\\*+/?.*" ) && !link.startsWith( "/" ) && !link.startsWith( "./" ) )
414 {
415 link = "./" + link;
416 }
417 String diff = StringUtils.difference( link, pattern );
418 if ( diff.startsWith( "/" ) )
419 {
420 return SelectorUtils.match( pattern, link + "/" );
421 }
422
423 return SelectorUtils.match( pattern, link );
424 }
425 }