|
|
|
@ -1,7 +1,6 @@
|
|
|
|
|
package com.olexyn.ensync.artifacts;
|
|
|
|
|
|
|
|
|
|
import com.olexyn.ensync.Execute;
|
|
|
|
|
import com.olexyn.ensync.Flow;
|
|
|
|
|
import com.olexyn.ensync.LogUtil;
|
|
|
|
|
import com.olexyn.ensync.Tools;
|
|
|
|
|
|
|
|
|
@ -11,10 +10,13 @@ import java.nio.file.Files;
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A SyncDirectory is a singular occurrence of a directory in the filesystems.
|
|
|
|
@ -23,16 +25,12 @@ public class SyncDirectory {
|
|
|
|
|
|
|
|
|
|
private static final Logger LOGGER = LogUtil.get(SyncDirectory.class);
|
|
|
|
|
|
|
|
|
|
private String flowState;
|
|
|
|
|
private SyncDirectory thisSD = this;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final SyncBundle syncMap;
|
|
|
|
|
public String path = null;
|
|
|
|
|
public String directoryPath;
|
|
|
|
|
|
|
|
|
|
public Map<String, SyncFile> listCreated = new HashMap<>();
|
|
|
|
|
public Map<String, SyncFile> listDeleted = new HashMap<>();
|
|
|
|
|
public Map<String, SyncFile> listModified = new HashMap<>();
|
|
|
|
|
public final Map<String, SyncFile> listCreated = new HashMap<>();
|
|
|
|
|
public final Map<String, SyncFile> listDeleted = new HashMap<>();
|
|
|
|
|
public final Map<String, SyncFile> listModified = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tools tools = new Tools();
|
|
|
|
@ -43,9 +41,9 @@ public class SyncDirectory {
|
|
|
|
|
*
|
|
|
|
|
* @see SyncBundle
|
|
|
|
|
*/
|
|
|
|
|
public SyncDirectory(String path, SyncBundle syncMap) {
|
|
|
|
|
public SyncDirectory(String directoryPath, SyncBundle syncMap) {
|
|
|
|
|
|
|
|
|
|
this.path = path;
|
|
|
|
|
this.directoryPath = directoryPath;
|
|
|
|
|
this.syncMap = syncMap;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
@ -55,21 +53,12 @@ public class SyncDirectory {
|
|
|
|
|
* Get the current state by using the `find` command.
|
|
|
|
|
*/
|
|
|
|
|
public Map<String, SyncFile> readStateFromFS() {
|
|
|
|
|
//NOTE that the SFile().lastModifiedOld is not set here, so it is 0 by default.
|
|
|
|
|
//NOTE that the SyncFile().lastModifiedOld is not set here, so it is 0 by default.
|
|
|
|
|
Map<String, SyncFile> filemap = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
Execute.TwoBr find = x.execute(new String[]{
|
|
|
|
|
"find",
|
|
|
|
|
path
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
List<String> pathList = tools.brToListString(find.output);
|
|
|
|
|
|
|
|
|
|
for (String filePath : pathList) {
|
|
|
|
|
SyncFile file = new SyncFile(this, filePath);
|
|
|
|
|
|
|
|
|
|
filemap.put(filePath, file);
|
|
|
|
|
}
|
|
|
|
|
getFiles()
|
|
|
|
|
.map(file -> new SyncFile(this, file.getAbsolutePath()))
|
|
|
|
|
.forEach(file -> filemap.put(file.getAbsolutePath(), file)
|
|
|
|
|
);
|
|
|
|
|
return filemap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -79,7 +68,7 @@ public class SyncDirectory {
|
|
|
|
|
*/
|
|
|
|
|
public Map<String, SyncFile> readStateFile() {
|
|
|
|
|
Map<String, SyncFile> filemap = new HashMap<>();
|
|
|
|
|
var stateFile = new StateFile(path);
|
|
|
|
|
var stateFile = new StateFile(directoryPath);
|
|
|
|
|
List<String> lines = tools.fileToLines(new File(stateFile.getPath()));
|
|
|
|
|
|
|
|
|
|
for (String line : lines) {
|
|
|
|
@ -117,10 +106,10 @@ public class SyncDirectory {
|
|
|
|
|
*/
|
|
|
|
|
public Map<String, SyncFile> makeListOfLocallyDeletedFiles() {
|
|
|
|
|
|
|
|
|
|
Map<String, SyncFile> fromA = readStateFile();
|
|
|
|
|
Map<String, SyncFile> substractB = readStateFromFS();
|
|
|
|
|
var fromA = readStateFile();
|
|
|
|
|
var substractB = readStateFromFS();
|
|
|
|
|
|
|
|
|
|
Map<String, SyncFile> listDeleted = tools.mapMinus(fromA, substractB);
|
|
|
|
|
var listDeleted = tools.mapMinus(fromA, substractB);
|
|
|
|
|
|
|
|
|
|
Map<String, SyncFile> swap = new HashMap<>();
|
|
|
|
|
|
|
|
|
@ -143,7 +132,8 @@ public class SyncDirectory {
|
|
|
|
|
* Compare the OLD and NEW pools.
|
|
|
|
|
* List is cleared and created each time.
|
|
|
|
|
*/
|
|
|
|
|
public Map<String, SyncFile> makeListOfLocallyModifiedFiles() {
|
|
|
|
|
public void makeListOfLocallyModifiedFiles() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, SyncFile> listModified = new HashMap<>();
|
|
|
|
|
|
|
|
|
@ -164,7 +154,6 @@ public class SyncDirectory {
|
|
|
|
|
listModified.put(freshFileKey, freshFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return listModified;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -174,198 +163,88 @@ public class SyncDirectory {
|
|
|
|
|
*/
|
|
|
|
|
public void writeStateFile(StateFile stateFile) {
|
|
|
|
|
List<String> outputList = new ArrayList<>();
|
|
|
|
|
getFiles().forEach(
|
|
|
|
|
file -> {
|
|
|
|
|
String relativePath = file.getAbsolutePath()
|
|
|
|
|
.replace(stateFile.getTargetPath(), "");
|
|
|
|
|
outputList.add("" + file.lastModified() + " " + relativePath);
|
|
|
|
|
});
|
|
|
|
|
tools.writeStringListToFile(stateFile.getPath(), outputList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Stream<File> getFiles() {
|
|
|
|
|
try {
|
|
|
|
|
Files.walk(Paths.get(path))
|
|
|
|
|
return Files.walk(Paths.get(directoryPath))
|
|
|
|
|
.filter(Files::isRegularFile)
|
|
|
|
|
.map(Path::toFile)
|
|
|
|
|
.filter(file -> !file.getName().equals(Constants.STATE_FILE_NAME))
|
|
|
|
|
.forEach(file -> {
|
|
|
|
|
String relativePath = file.getAbsolutePath()
|
|
|
|
|
.replace(stateFile.getTargetPath(), "");
|
|
|
|
|
outputList.add("" + file.lastModified() + " " + relativePath);
|
|
|
|
|
});
|
|
|
|
|
.filter(file -> !file.getName().equals(Constants.STATE_FILE_NAME));
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
LOGGER.severe("Could walk the file tree : StateFile will be empty.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tools.writeStringListToFile(stateFile.getPath(), outputList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private class Info {
|
|
|
|
|
|
|
|
|
|
private String thisFilePath;
|
|
|
|
|
private String otherFilePath;
|
|
|
|
|
private String otherParentPath;
|
|
|
|
|
private File otherParentFile;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Info(SyncDirectory thisSD, SyncFile sFile, SyncDirectory otherSD) {
|
|
|
|
|
// Example:
|
|
|
|
|
// syncDirectory /foo
|
|
|
|
|
// otherSyncDirectory /bar
|
|
|
|
|
// createdFile /foo/hello/created-file.gif
|
|
|
|
|
// relativePath /hello/created-file.gif
|
|
|
|
|
String relativePath = sFile.getPath().replace(thisSD.path, "");
|
|
|
|
|
this.thisFilePath = sFile.getPath();
|
|
|
|
|
this.otherFilePath = otherSD.path + relativePath;
|
|
|
|
|
File otherFile = new File(otherFilePath);
|
|
|
|
|
|
|
|
|
|
this.otherParentPath = otherFile.getParent();
|
|
|
|
|
this.otherParentFile = new File(otherParentPath);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Stream.empty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void doCreateOpsOnOtherSDs() {
|
|
|
|
|
|
|
|
|
|
for (var entry : listCreated.entrySet()) {
|
|
|
|
|
SyncFile createdFile = entry.getValue();
|
|
|
|
|
|
|
|
|
|
for (var otherEntry : syncMap.syncDirectories.entrySet()) {
|
|
|
|
|
SyncDirectory otherSD = otherEntry.getValue();
|
|
|
|
|
|
|
|
|
|
if (this.equals(otherSD)) { continue; }
|
|
|
|
|
|
|
|
|
|
Info info = new Info(this, createdFile, otherSD);
|
|
|
|
|
|
|
|
|
|
writeFileIfNewer(info, createdFile, otherSD);
|
|
|
|
|
for (var createdFile : listCreated.values()) {
|
|
|
|
|
for (var otherFile : otherFiles(createdFile)) {
|
|
|
|
|
writeFileIfNewer(createdFile, otherFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
public void doDeleteOpsOnOtherSDs() {
|
|
|
|
|
|
|
|
|
|
for (var entry : listDeleted.entrySet()) {
|
|
|
|
|
SyncFile deletedFile = entry.getValue();
|
|
|
|
|
|
|
|
|
|
for (var otherEntry : syncMap.syncDirectories.entrySet()) {
|
|
|
|
|
SyncDirectory otherSD = otherEntry.getValue();
|
|
|
|
|
|
|
|
|
|
if (this.equals(otherSD)) { continue; }
|
|
|
|
|
|
|
|
|
|
Info info = new Info(thisSD, deletedFile, otherSD);
|
|
|
|
|
deleteFile(info, thisSD, deletedFile, otherSD);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (var deletedFile : listDeleted.values()) {
|
|
|
|
|
for (var otherFile : otherFiles(deletedFile)) {
|
|
|
|
|
deleteFileIfNewer(deletedFile, otherFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void deleteFile(Info info, SyncDirectory thisSD, SyncFile thisFile, SyncDirectory otherSD) {
|
|
|
|
|
|
|
|
|
|
SyncFile otherFile = new SyncFile(otherSD, otherSD.path + thisFile.relativePath);
|
|
|
|
|
|
|
|
|
|
if (!otherFile.exists()) { return;}
|
|
|
|
|
|
|
|
|
|
// if the otherFile was created with ensync it will have the == TimeModified.
|
|
|
|
|
long thisFileTimeModified = thisFile.getTimeModified();
|
|
|
|
|
long otherFileTimeModified = otherFile.getTimeModified();
|
|
|
|
|
|
|
|
|
|
if (thisFile.getTimeModified() >= otherFile.getTimeModified()) {
|
|
|
|
|
List<String> cmd = List.of("rm", "-r", info.otherFilePath);
|
|
|
|
|
x.execute(cmd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void doModifyOpsOnOtherSDs() {
|
|
|
|
|
|
|
|
|
|
for (var entry : listModified.entrySet()) {
|
|
|
|
|
SyncFile modifiedFile = entry.getValue();
|
|
|
|
|
|
|
|
|
|
for (var otherEntry : syncMap.syncDirectories.entrySet()) {
|
|
|
|
|
SyncDirectory otherSD = otherEntry.getValue();
|
|
|
|
|
|
|
|
|
|
if (this.equals(otherSD)) { continue; }
|
|
|
|
|
|
|
|
|
|
Info info = new Info(this, modifiedFile, otherSD);
|
|
|
|
|
|
|
|
|
|
writeFileIfNewer(info, modifiedFile, otherSD);
|
|
|
|
|
for (var modifiedFile : listModified.values()) {
|
|
|
|
|
for (var otherFile : otherFiles(modifiedFile)) {
|
|
|
|
|
writeFileIfNewer(modifiedFile, otherFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***
|
|
|
|
|
*
|
|
|
|
|
* @param info
|
|
|
|
|
* @param thisFile
|
|
|
|
|
* @param otherSD
|
|
|
|
|
*/
|
|
|
|
|
private void writeFileIfNewer(Info info, SyncFile thisFile, SyncDirectory otherSD) {
|
|
|
|
|
|
|
|
|
|
SyncFile otherFile = new SyncFile(otherSD, otherSD.path + thisFile.relativePath);
|
|
|
|
|
|
|
|
|
|
if (otherFile.exists() && thisFile.getTimeModified() < otherFile.getTimeModified()) { return;}
|
|
|
|
|
|
|
|
|
|
if (thisFile.isDirectory() && !otherFile.exists()) {
|
|
|
|
|
List<String> cmd = List.of("mkdir", "-p", info.otherFilePath);
|
|
|
|
|
x.execute(cmd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thisFile.isFile()) {
|
|
|
|
|
private Collection<SyncFile> otherFiles(SyncFile thisFile) {
|
|
|
|
|
return syncMap.getSyncDirectories().stream()
|
|
|
|
|
.filter(sd -> !this.equals(sd))
|
|
|
|
|
.map(thisFile::otherFile)
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!info.otherParentFile.exists()) {
|
|
|
|
|
makeParentChain(otherFile, thisFile);
|
|
|
|
|
/**
|
|
|
|
|
* Delete other file if this file is newer.
|
|
|
|
|
*/
|
|
|
|
|
private void deleteFileIfNewer(SyncFile thisFile, SyncFile otherFile) {
|
|
|
|
|
if (!otherFile.exists()) { return; }
|
|
|
|
|
// if the otherFile was created with ensync it will have the == TimeModified.
|
|
|
|
|
if (thisFile.getTimeModified() >= otherFile.getTimeModified()) {
|
|
|
|
|
try {
|
|
|
|
|
Files.delete(Path.of(otherFile.getPath()));
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
LOGGER.severe("Could not delete file.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<String> cmd = List.of("cp", "-p", info.thisFilePath, info.otherFilePath);
|
|
|
|
|
x.execute(cmd);
|
|
|
|
|
copyModifDate(thisFile.getParentFile(), otherFile.getParentFile());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param otherFile
|
|
|
|
|
* @param thisFile
|
|
|
|
|
* Overwrite other file if this file is newer.
|
|
|
|
|
*/
|
|
|
|
|
private void makeParentChain(File otherFile, File thisFile) {
|
|
|
|
|
try {
|
|
|
|
|
File otherParent = new File(otherFile.getParent());
|
|
|
|
|
File thisParent = new File(thisFile.getParent());
|
|
|
|
|
|
|
|
|
|
if (!otherParent.exists()) {
|
|
|
|
|
makeParentChain(otherParent, thisParent);
|
|
|
|
|
makeParentChain(otherFile, thisFile);
|
|
|
|
|
|
|
|
|
|
} else if (thisFile.isDirectory()) {
|
|
|
|
|
|
|
|
|
|
List<String> cmd = List.of("mkdir", otherFile.getPath());
|
|
|
|
|
x.execute(cmd);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cmd = List.of("stat", "--format", "%y", thisFile.getPath());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String mDate = x.execute(cmd).output.readLine();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cmd = List.of("touch", "-m", "--date=" + mDate, otherFile.getPath());
|
|
|
|
|
String error = x.execute(cmd).error.readLine();
|
|
|
|
|
int br = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void writeFileIfNewer(SyncFile thisFile, SyncFile otherFile) {
|
|
|
|
|
if (otherFile.exists() && thisFile.isOlder(otherFile)) { return; }
|
|
|
|
|
if (thisFile.isFile()) {
|
|
|
|
|
try {
|
|
|
|
|
Files.copy(
|
|
|
|
|
Path.of(thisFile.getPath()),
|
|
|
|
|
Path.of(otherFile.getPath())
|
|
|
|
|
);
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
LOGGER.severe("Could not copy file.");
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception ignored) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void copyModifDate(File fromFile, File toFile) {
|
|
|
|
|
try {
|
|
|
|
|
List<String> cmd = List.of("stat", "--format", "%y", fromFile.getPath());
|
|
|
|
|
String mDate = x.execute(cmd).output.readLine();
|
|
|
|
|
|
|
|
|
|
cmd = List.of("touch", "-m", "--date=" + mDate, toFile.getPath());
|
|
|
|
|
x.execute(cmd);
|
|
|
|
|
} catch (Exception ignored) {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|