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