1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.common.util;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26
27 import org.apache.commons.io.FileUtils;
28 import org.apache.commons.lang3.StringUtils;
29 import org.kuali.common.util.execute.CopyFileRequest;
30 import org.kuali.common.util.execute.CopyFileResult;
31 import org.kuali.common.util.file.DirDiff;
32 import org.kuali.common.util.file.DirRequest;
33 import org.kuali.common.util.file.MD5Result;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 public class FileSystemUtils {
38
39 private static final Logger logger = LoggerFactory.getLogger(FileSystemUtils.class);
40
41 public static final String RECURSIVE_FILE_INCLUDE_PATTERN = "**/**";
42 public static final List<String> DEFAULT_RECURSIVE_INCLUDES = Arrays.asList(RECURSIVE_FILE_INCLUDE_PATTERN);
43
44 private static final String SVN_PATTERN = "**/.svn/**";
45 private static final String GIT_PATTERN = "**/.git/**";
46 public static final List<String> DEFAULT_SCM_IGNORE_PATTERNS = Arrays.asList(SVN_PATTERN, GIT_PATTERN);
47
48
49
50
51 public static List<File> getAllNonScmFiles(File dir) {
52 return getAllNonScmFiles(dir, DEFAULT_SCM_IGNORE_PATTERNS);
53 }
54
55
56
57
58 public static List<File> getAllNonScmFiles(File dir, List<String> scmIgnorePatterns) {
59 SimpleScanner scanner = new SimpleScanner(dir, DEFAULT_RECURSIVE_INCLUDES, scmIgnorePatterns);
60 return scanner.getFiles();
61 }
62
63
64
65
66
67
68
69
70
71
72
73
74
75 @Deprecated
76 public static DirectoryDiff prepareScmDir(PrepareScmDirRequest request) {
77 return prepareScmDir(request, null, false);
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 @Deprecated
95 public static DirectoryDiff prepareScmDir(PrepareScmDirRequest request, File relativeDir, boolean diffOnly) {
96
97
98 Assert.notNull(request, "request is null");
99 Assert.notNull(request.getSrcDir(), "srcDir is null");
100 Assert.notNull(request.getScmDir(), "scmDir is null");
101
102
103 Assert.isExistingDir(request.getSrcDir(), "srcDir is not an existing directory");
104 Assert.isExistingDir(request.getScmDir(), "scmDir is not an existing directory");
105
106
107 DirectoryDiffRequest diffRequest = new DirectoryDiffRequest();
108 diffRequest.setDir1(request.getSrcDir());
109 diffRequest.setDir2(request.getScmDir());
110 diffRequest.setExcludes(request.getScmIgnorePatterns());
111
112
113 DirectoryDiff diff = getDiff(diffRequest);
114
115
116 if (!diffOnly) {
117 org.kuali.common.util.execute.CopyFilePatternsExecutable exec = new org.kuali.common.util.execute.CopyFilePatternsExecutable();
118 exec.setSrcDir(request.getSrcDir());
119 exec.setDstDir(request.getScmDir());
120 exec.setExcludes(request.getScmIgnorePatterns());
121 exec.setRelativeDir(relativeDir);
122 exec.execute();
123 }
124
125
126 return diff;
127 }
128
129 public static List<File> getFiles(File dir, List<String> includes, List<String> excludes) {
130 SimpleScanner scanner = new SimpleScanner(dir, includes, excludes);
131 return scanner.getFiles();
132 }
133
134 @Deprecated
135 public static DirectoryDiff getDiff(File dir1, File dir2, List<String> includes, List<String> excludes) {
136 DirectoryDiffRequest request = new DirectoryDiffRequest();
137 request.setDir1(dir1);
138 request.setDir2(dir2);
139 request.setIncludes(includes);
140 request.setExcludes(excludes);
141 return getDiff(request);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public static DirDiff getMD5Diff(DirRequest request) {
159
160
161 DirDiff diff = getQuickDiff(request);
162
163
164
165 fillInMD5Results(diff);
166
167
168 return diff;
169 }
170
171 public static List<MD5Result> getMD5Results(List<File> sources, List<File> targets) {
172 Assert.isTrue(sources.size() == targets.size(), "lists are not the same size");
173 List<MD5Result> results = new ArrayList<MD5Result>();
174 for (int i = 0; i < sources.size(); i++) {
175 File source = sources.get(i);
176 File target = targets.get(i);
177 MD5Result md5Result = getMD5Result(source, target);
178 results.add(md5Result);
179 }
180 return results;
181 }
182
183 protected static void fillInMD5Results(DirDiff diff) {
184 List<File> sources = getFullPaths(diff.getSourceDir(), diff.getBoth());
185 List<File> targets = getFullPaths(diff.getTargetDir(), diff.getBoth());
186
187 List<MD5Result> results = getMD5Results(sources, targets);
188
189 List<MD5Result> different = new ArrayList<MD5Result>();
190 List<MD5Result> identical = new ArrayList<MD5Result>();
191 for (MD5Result md5Result : results) {
192 String sourceChecksum = md5Result.getSourceChecksum();
193 String targetChecksum = md5Result.getTargetChecksum();
194 Assert.notNull(sourceChecksum, "sourceChecksum is null");
195 Assert.notNull(targetChecksum, "targetChecksum is null");
196 if (StringUtils.equals(sourceChecksum, targetChecksum)) {
197 identical.add(md5Result);
198 } else {
199 different.add(md5Result);
200 }
201 }
202
203
204 diff.setDifferent(different);
205 diff.setIdentical(identical);
206 }
207
208 public static MD5Result getMD5Result(File source, File target) {
209
210 String sourceChecksum = LocationUtils.getMD5Checksum(source);
211 String targetChecksum = LocationUtils.getMD5Checksum(target);
212
213 return new MD5Result(source, sourceChecksum, target, targetChecksum);
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227
228 @Deprecated
229 public static DirectoryDiff getDiff(DirectoryDiffRequest request) {
230 DirRequest newRequest = new DirRequest();
231 newRequest.setExcludes(request.getExcludes());
232 newRequest.setIncludes(request.getIncludes());
233 newRequest.setSourceDir(request.getDir1());
234 newRequest.setTargetDir(request.getDir2());
235 DirDiff diff = getQuickDiff(newRequest);
236
237 DirectoryDiff dd = new DirectoryDiff(diff.getSourceDir(), diff.getTargetDir());
238 dd.setBoth(diff.getBoth());
239 dd.setDir1Only(diff.getSourceDirOnly());
240 dd.setDir2Only(diff.getTargetDirOnly());
241 return dd;
242 }
243
244 public static DirDiff getQuickDiff(DirRequest request) {
245
246
247 List<File> sourceFiles = getFiles(request.getSourceDir(), request.getIncludes(), request.getExcludes());
248 List<File> targetFiles = getFiles(request.getTargetDir(), request.getIncludes(), request.getExcludes());
249
250
251 Set<String> sourcePaths = new HashSet<String>(getRelativePaths(request.getSourceDir(), sourceFiles));
252 Set<String> targetPaths = new HashSet<String>(getRelativePaths(request.getTargetDir(), targetFiles));
253
254
255 Set<String> both = SetUtils.intersection(sourcePaths, targetPaths);
256
257
258 Set<String> sourceOnly = SetUtils.difference(sourcePaths, targetPaths);
259
260
261 Set<String> targetOnly = SetUtils.difference(targetPaths, sourcePaths);
262
263 logger.debug("source={}, sourceOnly.size()={}", request.getSourceDir(), sourceOnly.size());
264 logger.debug("target={}, targetOnly.size()={}", request.getTargetDir(), targetOnly.size());
265
266
267 DirDiff result = new DirDiff(request.getSourceDir(), request.getTargetDir());
268
269
270 result.setBoth(new ArrayList<String>(both));
271 result.setSourceDirOnly(new ArrayList<String>(sourceOnly));
272 result.setTargetDirOnly(new ArrayList<String>(targetOnly));
273
274
275 Collections.sort(result.getBoth());
276 Collections.sort(result.getSourceDirOnly());
277 Collections.sort(result.getTargetDirOnly());
278
279
280 return result;
281 }
282
283
284
285
286 public static MonitorTextFileResult monitorTextFile(File file, String token, int intervalMillis, int timeoutMillis, String encoding) {
287
288
289 Assert.notNull(file, "file is null");
290 Assert.hasText(token, "token has no text");
291 Assert.hasText(encoding, "encoding has no text");
292 Assert.isTrue(intervalMillis > 0, "interval must be a positive integer");
293 Assert.isTrue(timeoutMillis > 0, "timeout must be a positive integer");
294
295
296 long start = System.currentTimeMillis();
297 long stop = start + timeoutMillis;
298 boolean exists = false;
299 boolean contains = false;
300 boolean timeoutExceeded = false;
301 long now = -1;
302 String content = null;
303
304
305 for (;;) {
306
307
308 if (now != -1) {
309 ThreadUtils.sleep(intervalMillis);
310 }
311
312
313 now = System.currentTimeMillis();
314 if (now > stop) {
315 timeoutExceeded = true;
316 break;
317 }
318
319
320 exists = LocationUtils.exists(file);
321 if (!exists) {
322 continue;
323 }
324
325
326 content = LocationUtils.toString(file, encoding);
327 contains = StringUtils.contains(content, token);
328 if (contains) {
329
330 break;
331 }
332 }
333
334
335 long elapsed = now - start;
336
337
338 MonitorTextFileResult mtfr = new MonitorTextFileResult(exists, contains, timeoutExceeded, elapsed);
339 mtfr.setAbsolutePath(LocationUtils.getCanonicalPath(file));
340 mtfr.setContent(content);
341 return mtfr;
342 }
343
344 public static List<SyncResult> syncFiles(List<SyncRequest> requests) throws IOException {
345 List<SyncResult> results = new ArrayList<SyncResult>();
346 for (SyncRequest request : requests) {
347 SyncResult result = syncFiles(request);
348 results.add(result);
349 }
350 return results;
351 }
352
353 public static SyncResult syncFilesQuietly(SyncRequest request) {
354 try {
355 return syncFiles(request);
356 } catch (IOException e) {
357 throw new IllegalStateException("Unexpected IO error");
358 }
359 }
360
361 public static SyncResult syncFiles(SyncRequest request) throws IOException {
362 logger.info("Sync [{}] -> [{}]", request.getSrcDir(), request.getDstDir());
363 List<File> dstFiles = getAllNonScmFiles(request.getDstDir());
364 List<File> srcFiles = request.getSrcFiles();
365
366 List<String> dstPaths = getRelativePaths(request.getDstDir(), dstFiles);
367 List<String> srcPaths = getRelativePaths(request.getSrcDir(), srcFiles);
368
369 List<String> adds = new ArrayList<String>();
370 List<String> updates = new ArrayList<String>();
371 List<String> deletes = new ArrayList<String>();
372
373 for (String srcPath : srcPaths) {
374 boolean existing = dstPaths.contains(srcPath);
375 if (existing) {
376 updates.add(srcPath);
377 } else {
378 adds.add(srcPath);
379 }
380 }
381 for (String dstPath : dstPaths) {
382 boolean extra = !srcPaths.contains(dstPath);
383 if (extra) {
384 deletes.add(dstPath);
385 }
386 }
387
388 copyFiles(request.getSrcDir(), request.getSrcFiles(), request.getDstDir());
389
390 SyncResult result = new SyncResult();
391 result.setAdds(getFullPaths(request.getDstDir(), adds));
392 result.setUpdates(getFullPaths(request.getDstDir(), updates));
393 result.setDeletes(getFullPaths(request.getDstDir(), deletes));
394 return result;
395 }
396
397 protected static void copyFiles(File srcDir, List<File> files, File dstDir) throws IOException {
398 for (File file : files) {
399 String relativePath = getRelativePath(srcDir, file);
400 File dstFile = new File(dstDir, relativePath);
401 FileUtils.copyFile(file, dstFile);
402 }
403 }
404
405 public static List<File> getFullPaths(File dir, Set<String> relativePaths) {
406 return getFullPaths(dir, new ArrayList<String>(relativePaths));
407 }
408
409 public static List<File> getSortedFullPaths(File dir, List<String> relativePaths) {
410 List<File> files = getFullPaths(dir, relativePaths);
411 Collections.sort(files);
412 return files;
413 }
414
415 public static List<File> getFullPaths(File dir, List<String> relativePaths) {
416 List<File> files = new ArrayList<File>();
417 for (String relativePath : relativePaths) {
418 File file = new File(dir, relativePath);
419 File canonical = new File(LocationUtils.getCanonicalPath(file));
420 files.add(canonical);
421 }
422 return files;
423 }
424
425 protected static List<String> getRelativePaths(File dir, List<File> files) {
426 List<String> relativePaths = new ArrayList<String>();
427 for (File file : files) {
428 String relativePath = getRelativePath(dir, file);
429 relativePaths.add(relativePath);
430 }
431 return relativePaths;
432 }
433
434
435
436
437 public static boolean isParent(File parent, File child) {
438 if (parent == null || child == null) {
439 return false;
440 }
441
442 String parentPath = LocationUtils.getCanonicalPath(parent);
443 String childPath = LocationUtils.getCanonicalPath(child);
444
445 if (StringUtils.equals(parentPath, childPath)) {
446 return false;
447 } else {
448 return StringUtils.contains(childPath, parentPath);
449 }
450 }
451
452
453
454
455
456 public static String getRelativePathQuietly(File parentDir, File file) {
457 Assert.notNull(file, "file is null");
458 if (isParent(parentDir, file)) {
459 return getRelativePath(parentDir, file);
460 } else {
461 return LocationUtils.getCanonicalPath(file);
462 }
463 }
464
465 public static String getRelativePath(File dir, File file) {
466 String dirPath = LocationUtils.getCanonicalPath(dir);
467 String filePath = LocationUtils.getCanonicalPath(file);
468 if (!StringUtils.contains(filePath, dirPath)) {
469 throw new IllegalArgumentException(file + " does not reside under " + dir);
470 }
471 return StringUtils.remove(filePath, dirPath);
472 }
473
474 public static List<CopyFileRequest> getCopyFileRequests(File srcDir, List<String> includes, List<String> excludes, File dstDir) {
475 SimpleScanner scanner = new SimpleScanner(srcDir, includes, excludes);
476 List<File> srcFiles = scanner.getFiles();
477
478 List<CopyFileRequest> requests = new ArrayList<CopyFileRequest>();
479 for (File srcFile : srcFiles) {
480 String relativePath = FileSystemUtils.getRelativePath(srcDir, srcFile);
481 File dstFile = new File(dstDir, relativePath);
482 CopyFileRequest request = new CopyFileRequest(srcFile, dstFile);
483 requests.add(request);
484 }
485 return requests;
486 }
487
488 public static CopyFileResult copyFile(File src, File dst) {
489 try {
490 long start = System.currentTimeMillis();
491 boolean overwritten = dst.exists();
492 FileUtils.copyFile(src, dst);
493 return new CopyFileResult(src, dst, overwritten, System.currentTimeMillis() - start);
494 } catch (IOException e) {
495 throw new IllegalStateException("Unexpected IO error", e);
496 }
497 }
498
499 public static List<CopyFileResult> copyFiles(List<CopyFileRequest> requests) {
500 List<CopyFileResult> results = new ArrayList<CopyFileResult>();
501 for (CopyFileRequest request : requests) {
502 CopyFileResult result = copyFile(request.getSource(), request.getDestination());
503 results.add(result);
504 }
505 return results;
506 }
507
508 }