001    /**
002     * Copyright 2010-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.common.util;
017    
018    import java.io.File;
019    import java.io.IOException;
020    import java.util.ArrayList;
021    import java.util.Arrays;
022    import java.util.List;
023    
024    import org.apache.commons.io.FileUtils;
025    import org.apache.commons.lang3.StringUtils;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    public class FileSystemUtils {
030    
031            private static final Logger logger = LoggerFactory.getLogger(FileSystemUtils.class);
032    
033            /**
034             * Examine the contents of a text file, stopping as soon as it contains <code>token</code>, or <code>timeout</code> is exceeded, whichever comes first.
035             */
036            public static MonitorTextFileResult monitorTextFile(File file, String token, int intervalMillis, int timeoutMillis, String encoding) {
037    
038                    // Make sure we are configured correctly
039                    Assert.notNull(file, "file is null");
040                    Assert.hasText(token, "token has no text");
041                    Assert.hasText(encoding, "encoding has no text");
042                    Assert.isTrue(intervalMillis > 0, "interval must be a positive integer");
043                    Assert.isTrue(timeoutMillis > 0, "timeout must be a positive integer");
044    
045                    // Setup some member variables to record what happens
046                    long start = System.currentTimeMillis();
047                    long stop = start + timeoutMillis;
048                    boolean exists = false;
049                    boolean contains = false;
050                    boolean timeoutExceeded = false;
051                    long now = -1;
052                    String content = null;
053    
054                    // loop until timeout is exceeded or we find the token inside the file
055                    for (;;) {
056    
057                            // Always pause (unless this is the first iteration)
058                            if (now != -1) {
059                                    ThreadUtils.sleep(intervalMillis);
060                            }
061    
062                            // Check to make sure we haven't exceeded our timeout limit
063                            now = System.currentTimeMillis();
064                            if (now > stop) {
065                                    timeoutExceeded = true;
066                                    break;
067                            }
068    
069                            // If the file does not exist, no point in going any further
070                            exists = LocationUtils.exists(file);
071                            if (!exists) {
072                                    continue;
073                            }
074    
075                            // The file exists, check to see if the token we are looking for is present in the file
076                            content = LocationUtils.toString(file, encoding);
077                            contains = StringUtils.contains(content, token);
078                            if (contains) {
079                                    // We found what we are looking for, we are done
080                                    break;
081                            }
082                    }
083    
084                    // Record how long the overall process took
085                    long elapsed = now - start;
086    
087                    // Fill in a pojo detailing what happened
088                    MonitorTextFileResult mtfr = new MonitorTextFileResult(exists, contains, timeoutExceeded, elapsed);
089                    mtfr.setAbsolutePath(LocationUtils.getCanonicalPath(file));
090                    mtfr.setContent(content);
091                    return mtfr;
092            }
093    
094            public static List<SyncResult> syncFiles(List<SyncRequest> requests) throws IOException {
095                    List<SyncResult> results = new ArrayList<SyncResult>();
096                    for (SyncRequest request : requests) {
097                            SyncResult result = syncFiles(request);
098                            results.add(result);
099                    }
100                    return results;
101            }
102    
103            public static SyncResult syncFiles(SyncRequest request) throws IOException {
104                    logger.info("Sync [{}] -> [{}]", request.getSrcDir(), request.getDstDir());
105                    List<File> dstFiles = getAllFiles(request.getDstDir());
106                    List<File> srcFiles = request.getSrcFiles();
107    
108                    List<String> dstPaths = getRelativePaths(request.getDstDir(), dstFiles);
109                    List<String> srcPaths = getRelativePaths(request.getSrcDir(), srcFiles);
110    
111                    List<String> adds = new ArrayList<String>();
112                    List<String> updates = new ArrayList<String>();
113                    List<String> deletes = new ArrayList<String>();
114    
115                    for (String srcPath : srcPaths) {
116                            boolean existing = dstPaths.contains(srcPath);
117                            if (existing) {
118                                    updates.add(srcPath);
119                            } else {
120                                    adds.add(srcPath);
121                            }
122                    }
123                    for (String dstPath : dstPaths) {
124                            boolean extra = !srcPaths.contains(dstPath);
125                            if (extra) {
126                                    deletes.add(dstPath);
127                            }
128                    }
129    
130                    copyFiles(request.getSrcDir(), request.getSrcFiles(), request.getDstDir());
131    
132                    SyncResult result = new SyncResult();
133                    result.setAdds(getFullPaths(request.getDstDir(), adds));
134                    result.setUpdates(getFullPaths(request.getDstDir(), updates));
135                    result.setDeletes(getFullPaths(request.getDstDir(), deletes));
136                    return result;
137            }
138    
139            protected static void copyFiles(File srcDir, List<File> files, File dstDir) throws IOException {
140                    for (File file : files) {
141                            String relativePath = getRelativePath(srcDir, file);
142                            File dstFile = new File(dstDir, relativePath);
143                            FileUtils.copyFile(file, dstFile);
144                    }
145            }
146    
147            protected static List<File> getFullPaths(File dir, List<String> relativePaths) {
148                    List<File> files = new ArrayList<File>();
149                    for (String relativePath : relativePaths) {
150                            File file = new File(dir, relativePath);
151                            files.add(file);
152                    }
153                    return files;
154            }
155    
156            protected static List<String> getRelativePaths(File dir, List<File> files) {
157                    List<String> relativePaths = new ArrayList<String>();
158                    for (File file : files) {
159                            String relativePath = getRelativePath(dir, file);
160                            relativePaths.add(relativePath);
161                    }
162                    return relativePaths;
163            }
164    
165            protected static String getRelativePath(File dir, File file) {
166                    String dirPath = LocationUtils.getCanonicalPath(dir);
167                    String filePath = LocationUtils.getCanonicalPath(file);
168                    if (!StringUtils.contains(filePath, dirPath)) {
169                            throw new IllegalArgumentException(file + " does not reside under " + dir);
170                    }
171                    return StringUtils.remove(filePath, dirPath);
172            }
173    
174            protected static List<File> getAllFiles(File dir) {
175                    SimpleScanner scanner = new SimpleScanner(dir, Arrays.asList("**/*"), Arrays.asList("**/.svn/**", "**/.git/**"));
176                    return scanner.getFiles();
177            }
178    
179    }