1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.model.fileset.util;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.Files;
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 private static final String[] EMPTY_STRING_ARRAY = new String[0];
53
54 private final boolean verbose;
55
56 private final Logger logger;
57
58
59
60
61
62
63
64
65
66
67
68 public FileSetManager(Logger logger, boolean verbose) {
69 this.logger = requireNonNull(logger);
70 this.verbose = verbose;
71 }
72
73
74
75
76
77
78 public FileSetManager(Logger logger) {
79 this(logger, false);
80 }
81
82
83
84
85 public FileSetManager() {
86 this(LoggerFactory.getLogger(FileSetManager.class), false);
87 }
88
89
90
91
92
93
94
95
96
97
98
99 public Map<String, String> mapIncludedFiles(FileSet fileSet) throws MapperException {
100 String[] sourcePaths = getIncludedFiles(fileSet);
101 Map<String, String> mappedPaths = new LinkedHashMap<>();
102
103 FileNameMapper fileMapper = MapperUtil.getFileNameMapper(fileSet.getMapper());
104
105 for (String sourcePath : sourcePaths) {
106 String destPath;
107 if (fileMapper != null) {
108 destPath = fileMapper.mapFileName(sourcePath);
109 } else {
110 destPath = sourcePath;
111 }
112
113 mappedPaths.put(sourcePath, destPath);
114 }
115
116 return mappedPaths;
117 }
118
119
120
121
122
123
124
125 public String[] getIncludedFiles(FileSet fileSet) {
126 DirectoryScanner scanner = scan(fileSet);
127
128 if (scanner != null) {
129 return scanner.getIncludedFiles();
130 }
131
132 return EMPTY_STRING_ARRAY;
133 }
134
135
136
137
138
139
140
141 public String[] getIncludedDirectories(FileSet fileSet) {
142 DirectoryScanner scanner = scan(fileSet);
143
144 if (scanner != null) {
145 return scanner.getIncludedDirectories();
146 }
147
148 return EMPTY_STRING_ARRAY;
149 }
150
151
152
153
154
155
156
157 public String[] getExcludedFiles(FileSet fileSet) {
158 DirectoryScanner scanner = scan(fileSet);
159
160 if (scanner != null) {
161 return scanner.getExcludedFiles();
162 }
163
164 return EMPTY_STRING_ARRAY;
165 }
166
167
168
169
170
171
172
173 public String[] getExcludedDirectories(FileSet fileSet) {
174 DirectoryScanner scanner = scan(fileSet);
175
176 if (scanner != null) {
177 return scanner.getExcludedDirectories();
178 }
179
180 return EMPTY_STRING_ARRAY;
181 }
182
183
184
185
186
187
188
189 public void delete(FileSet fileSet) throws IOException {
190 delete(fileSet, true);
191 }
192
193
194
195
196
197
198
199
200
201 public void delete(FileSet fileSet, boolean throwsError) throws IOException {
202 Set<String> deletablePaths = findDeletablePaths(fileSet);
203
204 if (logger.isDebugEnabled()) {
205 String paths = String.valueOf(deletablePaths).replace(',', '\n');
206 logger.debug("Found deletable paths: " + paths);
207 }
208
209 List<String> warnMessages = new LinkedList<>();
210
211 for (String path : deletablePaths) {
212 File file = new File(fileSet.getDirectory(), path);
213
214 if (file.exists()) {
215 if (file.isDirectory()) {
216 if (fileSet.isFollowSymlinks() || !Files.isSymbolicLink(file.toPath())) {
217 if (verbose) {
218 logger.info("Deleting directory: " + file);
219 }
220
221 removeDir(file, fileSet.isFollowSymlinks(), throwsError, warnMessages);
222 } else {
223 if (verbose) {
224 logger.info("Deleting symlink to directory: " + file);
225 }
226
227 if (!file.delete()) {
228 String message = "Unable to delete symlink " + file.getAbsolutePath();
229 if (throwsError) {
230 throw new IOException(message);
231 }
232
233 if (!warnMessages.contains(message)) {
234 warnMessages.add(message);
235 }
236 }
237 }
238 } else {
239 if (verbose) {
240 logger.info("Deleting file: " + file);
241 }
242
243 if (!FileUtils.deleteQuietly(file)) {
244 String message = "Failed to delete file " + file.getAbsolutePath() + ". Reason is unknown.";
245 if (throwsError) {
246 throw new IOException(message);
247 }
248
249 warnMessages.add(message);
250 }
251 }
252 }
253 }
254
255 if (logger.isWarnEnabled() && !throwsError && (warnMessages.size() > 0)) {
256 for (String warnMessage : warnMessages) {
257 logger.warn(warnMessage);
258 }
259 }
260 }
261
262
263
264
265
266 private Set<String> findDeletablePaths(FileSet fileSet) {
267 Set<String> includes = findDeletableDirectories(fileSet);
268 includes.addAll(findDeletableFiles(fileSet, includes));
269
270 return includes;
271 }
272
273 private Set<String> findDeletableDirectories(FileSet fileSet) {
274 if (verbose) {
275 logger.info("Scanning for deletable directories.");
276 }
277
278 DirectoryScanner scanner = scan(fileSet);
279
280 if (scanner == null) {
281 return Collections.emptySet();
282 }
283
284 Set<String> includes = new HashSet<>(Arrays.asList(scanner.getIncludedDirectories()));
285 List<String> excludes = new ArrayList<>(Arrays.asList(scanner.getExcludedDirectories()));
286 List<String> linksForDeletion = new ArrayList<>();
287
288 if (!fileSet.isFollowSymlinks()) {
289 if (verbose) {
290 logger.info("Adding symbolic link dirs which were previously excluded" + " to the list being deleted.");
291 }
292
293
294 scanner.setFollowSymlinks(true);
295 scanner.scan();
296
297 if (logger.isDebugEnabled()) {
298 logger.debug("Originally marked for delete: " + includes);
299 logger.debug("Marked for preserve (with followSymlinks == false): " + excludes);
300 }
301
302 List<String> includedDirsAndSymlinks = Arrays.asList(scanner.getIncludedDirectories());
303
304 linksForDeletion.addAll(excludes);
305 linksForDeletion.retainAll(includedDirsAndSymlinks);
306
307 if (logger.isDebugEnabled()) {
308 logger.debug("Symlinks marked for deletion (originally mismarked): " + linksForDeletion);
309 }
310
311 excludes.removeAll(includedDirsAndSymlinks);
312 }
313
314 excludeParentDirectoriesOfExcludedPaths(excludes, includes);
315
316 includes.addAll(linksForDeletion);
317
318 return includes;
319 }
320
321 private Set<String> findDeletableFiles(FileSet fileSet, Set<String> deletableDirectories) {
322 if (verbose) {
323 logger.info("Re-scanning for deletable files.");
324 }
325
326 DirectoryScanner scanner = scan(fileSet);
327
328 if (scanner == null) {
329 return deletableDirectories;
330 }
331
332 deletableDirectories.addAll(Arrays.asList(scanner.getIncludedFiles()));
333 List<String> excludes = new ArrayList<>(Arrays.asList(scanner.getExcludedFiles()));
334 List<String> linksForDeletion = new ArrayList<>();
335
336 if (!fileSet.isFollowSymlinks()) {
337 if (verbose) {
338 logger.info(
339 "Adding symbolic link files which were previously excluded " + "to the list being deleted.");
340 }
341
342
343 scanner.setFollowSymlinks(true);
344 scanner.scan();
345
346 if (logger.isDebugEnabled()) {
347 logger.debug("Originally marked for delete: " + deletableDirectories);
348 logger.debug("Marked for preserve (with followSymlinks == false): " + excludes);
349 }
350
351 List<String> includedFilesAndSymlinks = Arrays.asList(scanner.getIncludedFiles());
352
353 linksForDeletion.addAll(excludes);
354 linksForDeletion.retainAll(includedFilesAndSymlinks);
355
356 if (logger.isDebugEnabled()) {
357 logger.debug("Symlinks marked for deletion (originally mismarked): " + linksForDeletion);
358 }
359
360 excludes.removeAll(includedFilesAndSymlinks);
361 }
362
363 excludeParentDirectoriesOfExcludedPaths(excludes, deletableDirectories);
364
365 deletableDirectories.addAll(linksForDeletion);
366
367 return deletableDirectories;
368 }
369
370
371
372
373
374
375
376
377
378
379 private void excludeParentDirectoriesOfExcludedPaths(List<String> excludedPaths, Set<String> deletablePaths) {
380 for (String path : excludedPaths) {
381 String parentPath = new File(path).getParent();
382
383 while (parentPath != null) {
384 if (logger.isDebugEnabled()) {
385 logger.debug("Verifying path " + parentPath + " is not present; contains file which is excluded.");
386 }
387
388 boolean removed = deletablePaths.remove(parentPath);
389
390 if (removed && logger.isDebugEnabled()) {
391 logger.debug("Path " + parentPath + " was removed from delete list.");
392 }
393
394 parentPath = new File(parentPath).getParent();
395 }
396 }
397
398 if (!excludedPaths.isEmpty()) {
399 if (logger.isDebugEnabled()) {
400 logger.debug("Verifying path " + "." + " is not present; contains file which is excluded.");
401 }
402
403 boolean removed = deletablePaths.remove("");
404
405 if (removed && logger.isDebugEnabled()) {
406 logger.debug("Path " + "." + " was removed from delete list.");
407 }
408 }
409 }
410
411
412
413
414
415
416
417
418
419
420 private void removeDir(File dir, boolean followSymlinks, boolean throwsError, List<String> warnMessages)
421 throws IOException {
422 String[] list = dir.list();
423 if (list == null) {
424 list = new String[0];
425 }
426
427 for (String s : list) {
428 File f = new File(dir, s);
429 if (f.isDirectory() && (followSymlinks || !Files.isSymbolicLink(f.toPath()))) {
430 removeDir(f, followSymlinks, throwsError, warnMessages);
431 } else {
432 if (!FileUtils.deleteQuietly(f)) {
433 String message = "Unable to delete file " + f.getAbsolutePath();
434 if (throwsError) {
435 throw new IOException(message);
436 }
437
438 if (!warnMessages.contains(message)) {
439 warnMessages.add(message);
440 }
441 }
442 }
443 }
444
445 if (!FileUtils.deleteQuietly(dir)) {
446 String message = "Unable to delete directory " + dir.getAbsolutePath();
447 if (throwsError) {
448 throw new IOException(message);
449 }
450
451 if (!warnMessages.contains(message)) {
452 warnMessages.add(message);
453 }
454 }
455 }
456
457 private DirectoryScanner scan(FileSet fileSet) {
458 File basedir = new File(fileSet.getDirectory());
459 if (!basedir.exists() || !basedir.isDirectory()) {
460 return null;
461 }
462
463 DirectoryScanner scanner = new DirectoryScanner();
464
465 String[] includesArray = fileSet.getIncludesArray();
466 String[] excludesArray = fileSet.getExcludesArray();
467
468 if (includesArray.length > 0) {
469 scanner.setIncludes(includesArray);
470 }
471
472 if (excludesArray.length > 0) {
473 scanner.setExcludes(excludesArray);
474 }
475
476 if (fileSet.isUseDefaultExcludes()) {
477 scanner.addDefaultExcludes();
478 }
479
480 scanner.setBasedir(basedir);
481 scanner.setFollowSymlinks(fileSet.isFollowSymlinks());
482
483 scanner.scan();
484
485 return scanner;
486 }
487 }