001/**
002 * Copyright 2010-2014 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 */
016package org.kuali.common.util.sync;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.List;
022
023import org.kuali.common.util.CollectionUtils;
024import org.kuali.common.util.FileSystemUtils;
025import org.kuali.common.util.SyncResult;
026import org.kuali.common.util.execute.CopyFileRequest;
027import org.kuali.common.util.execute.CopyFileResult;
028import org.kuali.common.util.file.DirDiff;
029import org.kuali.common.util.file.DirRequest;
030import org.kuali.common.util.file.MD5Result;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034public class DefaultSyncService implements SyncService {
035
036        private static final Logger logger = LoggerFactory.getLogger(DefaultSyncService.class);
037
038        @Override
039        public DirDiff getDiff(DirRequest request) {
040                List<DirRequest> requests = Arrays.asList(request);
041                List<DirDiff> results = getDiffs(requests);
042                return results.get(0);
043        }
044
045        @Override
046        public List<DirDiff> getDiffs(List<DirRequest> requests) {
047                List<DirDiff> diffs = new ArrayList<DirDiff>();
048                for (DirRequest request : requests) {
049                        DirDiff diff = FileSystemUtils.getMD5Diff(request);
050                        diffs.add(diff);
051                }
052                return diffs;
053        }
054
055        @Override
056        public SyncResult sync(DirRequest request) {
057                List<DirRequest> requests = Arrays.asList(request);
058                List<SyncResult> results = sync(requests);
059                return results.get(0);
060        }
061
062        @Override
063        public List<SyncResult> sync(List<DirRequest> requests) {
064
065                logger.info("Synchronizing {} directories", requests.size());
066                for (DirRequest request : requests) {
067                        String src = FileSystemUtils.getRelativePathQuietly(request.getRelativeDir(), request.getSourceDir());
068                        String dst = FileSystemUtils.getRelativePathQuietly(request.getRelativeDir(), request.getTargetDir());
069                        logger.info("  [{}] -> [{}]", src, dst);
070                }
071
072                // Scan the file system and record the differences between the directories.
073                // This does a deep, heavy, scan of both directories.
074                // It recursively examines the contents of both directories.
075                // Files that exist in both, are compared for equality using an MD5 checksum
076                List<DirDiff> diffs = getDiffs(requests);
077
078                // Use the diff info to generate a list of files to copy
079                List<CopyFileRequest> copyRequests = getCopyFileRequests(diffs);
080
081                // Show how many files we are copying
082                if (copyRequests.size() > 0) {
083                        logger.info("Copying {} files", copyRequests.size());
084                }
085
086                // Copy the files and record the results
087                List<CopyFileResult> copyResults = FileSystemUtils.copyFiles(copyRequests);
088
089                // Log the number of files copied
090                logger.debug("Copied {} files", copyResults.size());
091
092                // Convert the diff information into sync information (adds, deletes, updates)
093                List<SyncResult> results = getSyncResults(diffs);
094
095                // return the sync info
096                return results;
097        }
098
099        protected List<SyncResult> getSyncResults(List<DirDiff> diffs) {
100                List<SyncResult> results = new ArrayList<SyncResult>();
101                for (DirDiff diff : diffs) {
102                        SyncResult result = getSyncResult(diff);
103                        results.add(result);
104                }
105                return results;
106        }
107
108        protected SyncResult getSyncResult(DirDiff diff) {
109                List<File> adds = FileSystemUtils.getFullPaths(diff.getTargetDir(), diff.getSourceDirOnly());
110                List<File> deletes = FileSystemUtils.getFullPaths(diff.getTargetDir(), diff.getTargetDirOnly());
111                List<File> updates = new ArrayList<File>();
112                for (MD5Result result : diff.getDifferent()) {
113                        updates.add(result.getTarget());
114                }
115
116                SyncResult result = new SyncResult();
117                result.setAdds(adds);
118                result.setDeletes(deletes);
119                result.setUpdates(updates);
120                return result;
121        }
122
123        protected List<CopyFileRequest> getCopyFileRequests(List<DirDiff> diffs) {
124                List<CopyFileRequest> copyRequests = new ArrayList<CopyFileRequest>();
125                for (DirDiff diff : diffs) {
126                        List<CopyFileRequest> list = getCopyFileRequests(diff);
127                        copyRequests.addAll(list);
128                }
129                return copyRequests;
130        }
131
132        /**
133         * Convert the diff into requests for copying files.
134         */
135        protected List<CopyFileRequest> getCopyFileRequests(DirDiff diff) {
136
137                // Copy all the files that were in source dir only
138                List<CopyFileRequest> source = getCopyFileRequests(diff, diff.getSourceDirOnly());
139
140                // Copy files that are in both directories but have different contents
141                List<CopyFileRequest> different = getCopyFileRequestsForFilesThatAreDifferent(diff.getDifferent());
142
143                // Return the combined list
144                return CollectionUtils.combine(different, source);
145        }
146
147        protected List<CopyFileRequest> getCopyFileRequestsForFilesThatAreDifferent(List<MD5Result> different) {
148                List<CopyFileRequest> requests = new ArrayList<CopyFileRequest>();
149                for (MD5Result md5Result : different) {
150                        File source = md5Result.getSource();
151                        File target = md5Result.getTarget();
152                        CopyFileRequest request = new CopyFileRequest(source, target);
153                        requests.add(request);
154                }
155                return requests;
156        }
157
158        protected List<CopyFileRequest> getCopyFileRequests(DirDiff diff, List<String> relativePaths) {
159                List<CopyFileRequest> requests = new ArrayList<CopyFileRequest>();
160                for (String relativePath : relativePaths) {
161                        File source = new File(diff.getSourceDir(), relativePath);
162                        File target = new File(diff.getTargetDir(), relativePath);
163                        CopyFileRequest request = new CopyFileRequest(source, target);
164                        requests.add(request);
165                }
166                return requests;
167        }
168
169}