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}