1 package org.apache.maven.shared.model.fileset.util;
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.plugin.logging.Log;
23 import org.apache.maven.shared.io.logging.DefaultMessageHolder;
24 import org.apache.maven.shared.io.logging.MessageHolder;
25 import org.apache.maven.shared.io.logging.MessageLevels;
26 import org.apache.maven.shared.io.logging.MojoLogSink;
27 import org.apache.maven.shared.io.logging.PlexusLoggerSink;
28 import org.apache.maven.shared.model.fileset.FileSet;
29 import org.apache.maven.shared.model.fileset.mappers.FileNameMapper;
30 import org.apache.maven.shared.model.fileset.mappers.MapperException;
31 import org.apache.maven.shared.model.fileset.mappers.MapperUtil;
32 import org.codehaus.plexus.logging.Logger;
33 import org.codehaus.plexus.util.DirectoryScanner;
34 import org.codehaus.plexus.util.FileUtils;
35
36 import java.io.File;
37 import java.io.IOException;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.LinkedHashMap;
45 import java.util.LinkedList;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49
50
51
52
53
54
55
56
57 public class FileSetManager
58 {
59 private static final String[] EMPTY_STRING_ARRAY = new String[0];
60
61 private final boolean verbose;
62
63 private MessageHolder messages;
64
65
66
67
68
69
70
71
72
73
74
75 public FileSetManager( Log log, boolean verbose )
76 {
77 if ( verbose )
78 {
79 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_DEBUG, MessageLevels.LEVEL_INFO,
80 new MojoLogSink( log ) );
81 }
82 else
83 {
84 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO,
85 new MojoLogSink( log ) );
86 }
87
88 this.verbose = verbose;
89 }
90
91
92
93
94
95
96 public FileSetManager( Log log )
97 {
98 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO,
99 new MojoLogSink( log ) );
100 this.verbose = false;
101 }
102
103
104
105
106
107
108
109 public FileSetManager( Logger log, boolean verbose )
110 {
111 if ( verbose )
112 {
113 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_DEBUG, MessageLevels.LEVEL_INFO,
114 new PlexusLoggerSink( log ) );
115 }
116 else
117 {
118 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO,
119 new PlexusLoggerSink( log ) );
120 }
121
122 this.verbose = verbose;
123 }
124
125
126
127
128
129
130 public FileSetManager( Logger log )
131 {
132 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO,
133 new PlexusLoggerSink( log ) );
134 this.verbose = false;
135 }
136
137
138
139
140 public FileSetManager()
141 {
142 this.verbose = false;
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156 public Map mapIncludedFiles( FileSet fileSet )
157 throws MapperException
158 {
159 String[] sourcePaths = getIncludedFiles( fileSet );
160 Map mappedPaths = new LinkedHashMap();
161
162 FileNameMapper fileMapper = MapperUtil.getFileNameMapper( fileSet.getMapper() );
163
164 for ( int i = 0; i < sourcePaths.length; i++ )
165 {
166 String sourcePath = sourcePaths[i];
167
168 String destPath;
169 if ( fileMapper != null )
170 {
171 destPath = fileMapper.mapFileName( sourcePath );
172 }
173 else
174 {
175 destPath = sourcePath;
176 }
177
178 mappedPaths.put( sourcePath, destPath );
179 }
180
181 return mappedPaths;
182 }
183
184
185
186
187
188
189
190
191 public String[] getIncludedFiles( FileSet fileSet )
192 {
193 DirectoryScanner scanner = scan( fileSet );
194
195 if ( scanner != null )
196 {
197 return scanner.getIncludedFiles();
198 }
199
200 return EMPTY_STRING_ARRAY;
201 }
202
203
204
205
206
207
208
209
210 public String[] getIncludedDirectories( FileSet fileSet )
211 {
212 DirectoryScanner scanner = scan( fileSet );
213
214 if ( scanner != null )
215 {
216 return scanner.getIncludedDirectories();
217 }
218
219 return EMPTY_STRING_ARRAY;
220 }
221
222
223
224
225
226
227
228
229 public String[] getExcludedFiles( FileSet fileSet )
230 {
231 DirectoryScanner scanner = scan( fileSet );
232
233 if ( scanner != null )
234 {
235 return scanner.getExcludedFiles();
236 }
237
238 return EMPTY_STRING_ARRAY;
239 }
240
241
242
243
244
245
246
247
248 public String[] getExcludedDirectories( FileSet fileSet )
249 {
250 DirectoryScanner scanner = scan( fileSet );
251
252 if ( scanner != null )
253 {
254 return scanner.getExcludedDirectories();
255 }
256
257 return EMPTY_STRING_ARRAY;
258 }
259
260
261
262
263
264
265
266 public void delete( FileSet fileSet )
267 throws IOException
268 {
269 delete( fileSet, true );
270 }
271
272
273
274
275
276
277
278
279
280 public void delete( FileSet fileSet, boolean throwsError )
281 throws IOException
282 {
283 Set deletablePaths = findDeletablePaths( fileSet );
284
285 if ( messages != null && messages.isDebugEnabled() )
286 {
287 messages
288 .addDebugMessage( "Found deletable paths: " + String.valueOf( deletablePaths ).replace( ',', '\n' ) ).flush();
289 }
290
291 List warnMessages = new LinkedList();
292
293 for ( Iterator it = deletablePaths.iterator(); it.hasNext(); )
294 {
295 String path = (String) it.next();
296
297 File file = new File( fileSet.getDirectory(), path );
298
299 if ( file.exists() )
300 {
301 if ( file.isDirectory() )
302 {
303 if ( fileSet.isFollowSymlinks() || !isSymlink( file ) )
304 {
305 if ( verbose && messages != null )
306 {
307 messages.addInfoMessage( "Deleting directory: " + file ).flush();
308 }
309
310 removeDir( file, fileSet.isFollowSymlinks(), throwsError, warnMessages );
311 }
312 else
313 {
314 if ( verbose && messages != null )
315 {
316 messages.addInfoMessage( "Deleting symlink to directory: " + file ).flush();
317 }
318
319 if ( !file.delete() )
320 {
321 String message = "Unable to delete symlink " + file.getAbsolutePath();
322 if ( throwsError )
323 {
324 throw new IOException( message );
325 }
326
327 if ( !warnMessages.contains( message ) )
328 {
329 warnMessages.add( message );
330 }
331 }
332 }
333 }
334 else
335 {
336 if ( verbose && messages != null )
337 {
338 messages.addInfoMessage( "Deleting file: " + file ).flush();
339 }
340
341 if ( !delete( file ) )
342 {
343 String message = "Failed to delete file " + file.getAbsolutePath() + ". Reason is unknown.";
344 if ( throwsError )
345 {
346 throw new IOException( message );
347 }
348
349 warnMessages.add( message );
350 }
351 }
352 }
353 }
354
355 if ( messages != null && messages.isWarningEnabled() && !throwsError && ( warnMessages.size() > 0 ) )
356 {
357 for ( Iterator it = warnMessages.iterator(); it.hasNext(); )
358 {
359 String msg = (String) it.next();
360
361 messages.addWarningMessage( msg ).flush();
362 }
363 }
364 }
365
366
367
368
369
370 private boolean isSymlink( File file )
371 throws IOException
372 {
373 File fileInCanonicalParent = null;
374 File parentDir = file.getParentFile();
375 if ( parentDir == null )
376 {
377 fileInCanonicalParent = file;
378 }
379 else
380 {
381 fileInCanonicalParent = new File( parentDir.getCanonicalPath(), file.getName() );
382 }
383 if ( messages != null && messages.isDebugEnabled() )
384 {
385 messages.addDebugMessage(
386 "Checking for symlink:\nFile's canonical path: "
387 + fileInCanonicalParent.getCanonicalPath()
388 + "\nFile's absolute path with canonical parent: "
389 + fileInCanonicalParent.getPath() ).flush();
390 }
391 return !fileInCanonicalParent.getCanonicalFile().equals( fileInCanonicalParent.getAbsoluteFile() );
392 }
393
394 private Set findDeletablePaths( FileSet fileSet )
395 {
396 Set includes = findDeletableDirectories( fileSet );
397 includes.addAll( findDeletableFiles( fileSet, includes ) );
398
399 return includes;
400 }
401
402 private Set findDeletableDirectories( FileSet fileSet )
403 {
404 if ( verbose && messages != null )
405 {
406 messages.addInfoMessage( "Scanning for deletable directories." ).flush();
407 }
408
409 DirectoryScanner scanner = scan( fileSet );
410
411 if ( scanner == null )
412 {
413 return Collections.EMPTY_SET;
414 }
415
416 Set includes = new HashSet( Arrays.asList( scanner.getIncludedDirectories() ) );
417 Collection excludes = new ArrayList( Arrays.asList( scanner.getExcludedDirectories() ) );
418 Collection linksForDeletion = new ArrayList();
419
420 if ( !fileSet.isFollowSymlinks() )
421 {
422 if ( verbose && messages != null )
423 {
424 messages
425 .addInfoMessage( "Adding symbolic link dirs which were previously excluded to the list being deleted." ).flush();
426 }
427
428
429 scanner.setFollowSymlinks( true );
430 scanner.scan();
431
432 if ( messages != null && messages.isDebugEnabled() )
433 {
434 messages.addDebugMessage( "Originally marked for delete: " + includes ).flush();
435 messages.addDebugMessage( "Marked for preserve (with followSymlinks == false): " + excludes ).flush();
436 }
437
438 List includedDirsAndSymlinks = Arrays.asList( scanner.getIncludedDirectories() );
439
440 linksForDeletion.addAll( excludes );
441 linksForDeletion.retainAll( includedDirsAndSymlinks );
442
443 if ( messages != null && messages.isDebugEnabled() )
444 {
445 messages.addDebugMessage( "Symlinks marked for deletion (originally mismarked): " + linksForDeletion ).flush();
446 }
447
448 excludes.removeAll( includedDirsAndSymlinks );
449 }
450
451 excludeParentDirectoriesOfExcludedPaths( excludes, includes );
452
453 includes.addAll( linksForDeletion );
454
455 return includes;
456 }
457
458 private Set findDeletableFiles( FileSet fileSet, Set deletableDirectories )
459 {
460 if ( verbose && messages != null )
461 {
462 messages.addInfoMessage( "Re-scanning for deletable files." ).flush();
463 }
464
465 DirectoryScanner scanner = scan( fileSet );
466
467 if ( scanner == null )
468 {
469 return deletableDirectories;
470 }
471
472 Set includes = deletableDirectories;
473 includes.addAll( Arrays.asList( scanner.getIncludedFiles() ) );
474 Collection excludes = new ArrayList( Arrays.asList( scanner.getExcludedFiles() ) );
475 Collection linksForDeletion = new ArrayList();
476
477 if ( !fileSet.isFollowSymlinks() )
478 {
479 if ( verbose && messages != null )
480 {
481 messages
482 .addInfoMessage( "Adding symbolic link files which were previously excluded to the list being deleted." ).flush();
483 }
484
485
486 scanner.setFollowSymlinks( true );
487 scanner.scan();
488
489 if ( messages != null && messages.isDebugEnabled() )
490 {
491 messages.addDebugMessage( "Originally marked for delete: " + includes ).flush();
492 messages.addDebugMessage( "Marked for preserve (with followSymlinks == false): " + excludes ).flush();
493 }
494
495 List includedFilesAndSymlinks = Arrays.asList( scanner.getIncludedFiles() );
496
497 linksForDeletion.addAll( excludes );
498 linksForDeletion.retainAll( includedFilesAndSymlinks );
499
500 if ( messages != null && messages.isDebugEnabled() )
501 {
502 messages.addDebugMessage( "Symlinks marked for deletion (originally mismarked): " + linksForDeletion ).flush();
503 }
504
505 excludes.removeAll( includedFilesAndSymlinks );
506 }
507
508 excludeParentDirectoriesOfExcludedPaths( excludes, includes );
509
510 includes.addAll( linksForDeletion );
511
512 return includes;
513 }
514
515
516
517
518
519
520
521
522
523
524 private void excludeParentDirectoriesOfExcludedPaths( Collection excludedPaths, Set deletablePaths )
525 {
526 for ( Iterator it = excludedPaths.iterator(); it.hasNext(); )
527 {
528 String path = (String) it.next();
529
530 String parentPath = new File( path ).getParent();
531
532 while ( parentPath != null )
533 {
534 if ( messages != null && messages.isDebugEnabled() )
535 {
536 messages.addDebugMessage( "Verifying path " + parentPath
537 + " is not present; contains file which is excluded." ).flush();
538 }
539
540 boolean removed = deletablePaths.remove( parentPath );
541
542 if ( removed && messages != null && messages.isDebugEnabled() )
543 {
544 messages.addDebugMessage( "Path " + parentPath + " was removed from delete list." ).flush();
545 }
546
547 parentPath = new File( parentPath ).getParent();
548 }
549 }
550
551 if ( !excludedPaths.isEmpty() )
552 {
553 if ( messages != null && messages.isDebugEnabled() )
554 {
555 messages.addDebugMessage( "Verifying path " + "."
556 + " is not present; contains file which is excluded." ).flush();
557 }
558
559 boolean removed = deletablePaths.remove( "" );
560
561 if ( removed && messages != null && messages.isDebugEnabled() )
562 {
563 messages.addDebugMessage( "Path " + "." + " was removed from delete list." ).flush();
564 }
565 }
566 }
567
568
569
570
571
572
573
574
575
576
577 private void removeDir( File dir, boolean followSymlinks, boolean throwsError, List warnMessages )
578 throws IOException
579 {
580 String[] list = dir.list();
581 if ( list == null )
582 {
583 list = new String[0];
584 }
585
586 for ( int i = 0; i < list.length; i++ )
587 {
588 String s = list[i];
589 File f = new File( dir, s );
590 if ( f.isDirectory() && ( followSymlinks || !isSymlink( f ) ) )
591 {
592 removeDir( f, followSymlinks, throwsError, warnMessages );
593 }
594 else
595 {
596 if ( !delete( f ) )
597 {
598 String message = "Unable to delete file " + f.getAbsolutePath();
599 if ( throwsError )
600 {
601 throw new IOException( message );
602 }
603
604 if ( !warnMessages.contains( message ) )
605 {
606 warnMessages.add( message );
607 }
608 }
609 }
610 }
611
612 if ( !delete( dir ) )
613 {
614 String message = "Unable to delete directory " + dir.getAbsolutePath();
615 if ( throwsError )
616 {
617 throw new IOException( message );
618 }
619
620 if ( !warnMessages.contains( message ) )
621 {
622 warnMessages.add( message );
623 }
624 }
625 }
626
627
628
629
630
631
632 private boolean delete( File f )
633 {
634 try
635 {
636 FileUtils.forceDelete( f );
637 }
638 catch ( IOException e )
639 {
640 return false;
641 }
642
643 return true;
644 }
645
646 private DirectoryScanner scan( FileSet fileSet )
647 {
648 File basedir = new File( fileSet.getDirectory() );
649 if ( !basedir.exists() || !basedir.isDirectory() )
650 {
651 return null;
652 }
653
654 DirectoryScanner scanner = new DirectoryScanner();
655
656 String[] includesArray = fileSet.getIncludesArray();
657 String[] excludesArray = fileSet.getExcludesArray();
658
659 if ( includesArray.length > 0 )
660 {
661 scanner.setIncludes( includesArray );
662 }
663
664 if ( excludesArray.length > 0 )
665 {
666 scanner.setExcludes( excludesArray );
667 }
668
669 if ( fileSet.isUseDefaultExcludes() )
670 {
671 scanner.addDefaultExcludes();
672 }
673
674 scanner.setBasedir( basedir );
675 scanner.setFollowSymlinks( fileSet.isFollowSymlinks() );
676
677 scanner.scan();
678
679 return scanner;
680 }
681
682 }