diff --git a/src/main/java/unimelb/mf/client/instrument/Upload.java b/src/main/java/unimelb/mf/client/instrument/Upload.java index aec92a084c60e0d6e187258ff1217dcb72a1cb4c..d42e2154972e0295c7d797b0a92c3261cf376fab 100644 --- a/src/main/java/unimelb/mf/client/instrument/Upload.java +++ b/src/main/java/unimelb/mf/client/instrument/Upload.java @@ -126,8 +126,8 @@ public class Upload { } public final boolean pathContextMatches(String srcPath) { - if (_pathContext != null && srcPath!=null) { - return _pathContext.replaceFirst("/+$","").equals(srcPath.replaceFirst("/+$", "")); + if (_pathContext != null && srcPath != null) { + return _pathContext.replaceFirst("/+$", "").equals(srcPath.replaceFirst("/+$", "")); } return false; } @@ -136,6 +136,14 @@ public class Upload { return _srcContext; } + public final SourceContext resolveSourceContext() throws Throwable { + _srcContext = null; + if (_pathContext != null) { + _srcContext = SourceContext.resolve(_pathContext, false); + } + return _srcContext; + } + public final void print(PrintStream ps, int indent) { indent = indent < 0 ? 0 : indent; String indentStr = indent > 0 ? StringUtils.stringOf(' ', indent) : ""; diff --git a/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadFind.java b/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadList.java similarity index 96% rename from src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadFind.java rename to src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadList.java index 9689278bfbd886d7df03c856090602c8a0e0f992..976297cf16437e50e9960fd04b0bf5d182ebe681 100644 --- a/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadFind.java +++ b/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadList.java @@ -22,10 +22,10 @@ import unimelb.mf.client.session.MFSession; import unimelb.picocli.converters.DateTimeConverter; import unimelb.picocli.converters.PathConverter; -@Command(name = "instrument-upload-find", abbreviateSynopsis = true, synopsisHeading = "\nUSAGE:\n ", parameterListHeading = "\nPARAMETERS:\n", optionListHeading = "\nOPTIONS:\n", separator = " ", sortOptions = false) -public class InstrumentUploadFind implements MFCommand { +@Command(name = "instrument-upload-list", abbreviateSynopsis = true, synopsisHeading = "\nUSAGE:\n ", parameterListHeading = "\nPARAMETERS:\n", optionListHeading = "\nOPTIONS:\n", separator = " ", sortOptions = false) +public class InstrumentUploadList implements MFCommand { - public static final String APPLICATION_NAME = "instrument-upload-find"; + public static final String APPLICATION_NAME = "instrument-upload-list"; @Option(names = { "--mf.config" }, paramLabel = "<mflux.cfg>", description = "Mediaflux server configuration file. Defaults to $HOME/.Arcitecta/mflux.cfg", required = false, converter = PathConverter.MustBeRegularFile.class) @@ -65,7 +65,7 @@ public class InstrumentUploadFind implements MFCommand { @Override public MFSession authenticate() throws Throwable { MFConfigBuilder mfcb = new MFConfigBuilder(); - mfcb.setApp(InstrumentUploadFind.APPLICATION_NAME); + mfcb.setApp(InstrumentUploadList.APPLICATION_NAME); if (this.mfCFG != null) { mfcb.loadFromConfigFile(mfCFG.toFile()); } else { @@ -215,7 +215,7 @@ public class InstrumentUploadFind implements MFCommand { } public static void main(String[] args) { - CommandLine cl = new CommandLine(new InstrumentUploadFind()); + CommandLine cl = new CommandLine(new InstrumentUploadList()); cl.setExecutionExceptionHandler(new IExecutionExceptionHandler() { @Override public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) diff --git a/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadMissingFind.java b/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadMissingFind.java new file mode 100644 index 0000000000000000000000000000000000000000..9759c1935d33fd03ff5c2a54714606a865bcf1b9 --- /dev/null +++ b/src/main/java/unimelb/mf/client/instrument/cli/InstrumentUploadMissingFind.java @@ -0,0 +1,299 @@ +package unimelb.mf.client.instrument.cli; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.IExecutionExceptionHandler; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; +import picocli.CommandLine.ParseResult; +import unimelb.mf.client.cli.MFCommand; +import unimelb.mf.client.instrument.Upload; +import unimelb.mf.client.instrument.UploadShareable; +import unimelb.mf.client.session.MFConfigBuilder; +import unimelb.mf.client.session.MFSession; +import unimelb.picocli.converters.DateTimeConverter; +import unimelb.picocli.converters.PathConverter; + +@Command(name = "instrument-upload-missing-find", abbreviateSynopsis = true, synopsisHeading = "\nUSAGE:\n ", parameterListHeading = "\nPARAMETERS:\n", optionListHeading = "\nOPTIONS:\n", separator = " ", sortOptions = false) +public class InstrumentUploadMissingFind implements MFCommand { + + public static final String APPLICATION_NAME = "instrument-upload-missing-find"; + + @Option(names = { + "--mf.config" }, paramLabel = "<mflux.cfg>", description = "Mediaflux server configuration file. Defaults to $HOME/.Arcitecta/mflux.cfg", required = false, converter = PathConverter.MustBeRegularFile.class) + private Path mfCFG; + + @Option(names = { "-i", "--id" }, paramLabel = "<id>", description = "The instrument upload shareable id") + private Long id; + + @Option(names = { "-t", "--token" }, paramLabel = "<token>", description = "The instrument upload shareable token.") + private String token; + + @Option(names = { "-l", + "--levels" }, paramLabel = "<n>", description = "Number of levels of sub-directories to search. Defaults to 1.", defaultValue = "1") + private int levels; + + @Option(names = { "-a", + "--after" }, paramLabel = "<dd-MMM-yyyy>", description = "Include only the data directories modified after this date (inclusive).", converter = DateTimeConverter.class) + private Date completedAfter; + + @Option(names = { "-b", + "--before" }, paramLabel = "<dd-MMM-yyyy>", description = "Include only the data directories modified before this date (exclusive).", converter = DateTimeConverter.class) + private Date completedBefore; + + @Option(names = { "-c", "--compare" }, description = "Compare with local source file/directory.") + private boolean compare; + + @Option(names = { "-h", "--help" }, usageHelp = true, description = "display help infomation.") + private boolean printHelp; + + @Parameters(paramLabel = "<parent-directories...>", description = "Parent directories to search.", arity = "1..") + private List<Path> parentDirs; + + @Override + public MFSession authenticate() throws Throwable { + MFConfigBuilder mfcb = new MFConfigBuilder(); + mfcb.setApp(InstrumentUploadList.APPLICATION_NAME); + if (this.mfCFG != null) { + mfcb.loadFromConfigFile(mfCFG.toFile()); + } else { + mfcb.findAndLoadFromConfigFile(); + } + mfcb.setConsoleLogon(true); + return MFSession.create(mfcb); + } + + @Override + public void execute(MFSession session) throws Throwable { + + Settings settings = new Settings(); + if (this.id != null) { + settings.setShareableId(this.id); + } else if (this.token != null) { + settings.setShareableToken(this.token); + } else { + settings.setShareableToken(DataMoverSettings.getToken()); + } + if (!settings.hasShareableIdOrToken()) { + throw new IllegalArgumentException("No shareable id or token is specified!"); + } + if (this.levels > 0) { + settings.setLevels(this.levels); + } + if (this.completedAfter != null) { + settings.setAfter(completedAfter); + } + if (this.completedBefore != null) { + settings.setBefore(completedBefore); + } + if (this.compare) { + settings.setCompare(compare); + } + settings.setParentDirectories(parentDirs); + if (settings.parentDirectories().isEmpty()) { + throw new IllegalArgumentException("No parent directory path is specified!"); + } + + List<Upload> uploads = UploadShareable.getUploads(session, settings.shareableId(), settings.shareableToken(), + false); + + findMissingDirectories(settings.parentDirectories(), settings.levels(), settings.after(), settings.before(), + settings.compare(), uploads); + + } + + private Map<Path, Set<Upload>> findMissingDirectories(Collection<Path> parentDirectories, int levels, Date after, + Date before, boolean compare, List<Upload> uploads) throws Throwable { + Map<Path, Set<Upload>> result = new LinkedHashMap<>(); + for (Path parentDir : parentDirectories) { + findMissingDirectories(parentDir, levels, after, before, compare, uploads, result); + } + return result; + } + + private void findMissingDirectories(Path parentDir, int levels, Date after, Date before, boolean compare, + List<Upload> uploads, Map<Path, Set<Upload>> result) throws IOException { + + int rootDepth = parentDir.getNameCount(); + + Files.walkFileTree(parentDir, new SimpleFileVisitor<Path>() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Objects.requireNonNull(dir); + Objects.requireNonNull(attrs); + int depth = dir.getNameCount(); + if (depth > rootDepth + levels) { + return FileVisitResult.SKIP_SUBTREE; + } + if (depth > rootDepth) { + if (lastModifiedTimeMatches(attrs.lastModifiedTime(), after, before)) { + Set<Upload> matchingUploads = getMatchingUploads(dir, uploads); + if (!matchingUploads.isEmpty()) { + result.put(dir, matchingUploads); + } + } + } + return FileVisitResult.CONTINUE; + } + + }); + + } + + private static boolean lastModifiedTimeMatches(FileTime lastModified, Date after, Date before) { + if (after != null || before != null) { + long lastModifiedMillis = lastModified.toMillis(); + if (after != null) { + long afterMillis = after.getTime(); + if (lastModifiedMillis < afterMillis) { + return false; + } + } + if (before != null) { + long beforeMillis = before.getTime(); + if (lastModifiedMillis >= beforeMillis) { + return false; + } + } + } + return true; + } + + private static Set<Upload> getMatchingUploads(Path dir, Collection<Upload> uploads) { + Set<Upload> matching = new LinkedHashSet<>(); + for (Upload upload : uploads) { + if (upload.pathContextMatches(dir.toAbsolutePath().toString())) { + matching.add(upload); + } + } + return matching; + } + + public static class Settings { + private Long _id; + private String _token; + private int _levels; + private Date _after; + private Date _before; + private boolean _compare = false; + private Set<Path> _parentDirs = new LinkedHashSet<>(); + + Long shareableId() { + return _id; + } + + String shareableToken() { + return _token; + } + + int levels() { + return _levels; + } + + Date before() { + return _before; + } + + Date after() { + return _after; + } + + boolean compare() { + return _compare; + } + + Collection<Path> parentDirectories() { + return Collections.unmodifiableCollection(_parentDirs); + } + + public Settings setShareableId(Long id) { + _id = id; + return this; + } + + public Settings setShareableToken(String token) { + _token = token; + return this; + } + + public Settings setLevels(int levels) { + _levels = levels; + return this; + } + + public boolean hasShareableIdOrToken() { + return _id != null || _token != null; + } + + public Settings setBefore(Date before) { + _before = before; + return this; + } + + public Settings setAfter(Date after) { + _after = after; + return this; + } + + public Settings setCompare(boolean compare) { + _compare = compare; + return this; + } + + public Settings addParentDirectories(Collection<Path> parentDirs) { + if (parentDirs != null) { + for (Path parentDir : parentDirs) { + if (Files.isDirectory(parentDir)) { + _parentDirs.add(parentDir); + } else { + System.err.printf("'%s' does not exist or it is not a directory.", parentDir.toString()); + } + } + } + return this; + } + + public Settings setParentDirectories(Collection<Path> parentDirs) { + _parentDirs.clear(); + addParentDirectories(parentDirs); + return this; + } + + } + + public static void main(String[] args) { + CommandLine cl = new CommandLine(new InstrumentUploadList()); + cl.setExecutionExceptionHandler(new IExecutionExceptionHandler() { + @Override + public int handleExecutionException(Exception ex, CommandLine commandLine, ParseResult parseResult) + throws Exception { + if (ex instanceof IllegalArgumentException) { + System.err.println("Error: " + ex.getMessage()); + cl.usage(System.err); + return 1; + } else { + throw ex; + } + } + }); + System.exit(cl.execute(args)); + } +} diff --git a/src/main/scripts/unix/unimelb-mf-instrument-upload-find b/src/main/scripts/unix/unimelb-mf-instrument-upload-find deleted file mode 100644 index f089c1eecb8d4744d65e657f406375e4ddefd00c..0000000000000000000000000000000000000000 --- a/src/main/scripts/unix/unimelb-mf-instrument-upload-find +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -# ${ROOT}/bin/ -BIN=$(dirname ${BASH_SOURCE[0]}) - -# current directory -CWD=$(pwd) - -# ${ROOT}/ -ROOT=$(cd ${BIN}/../../ && pwd && cd ${CWD}) - -# ${ROOT}/lib/ -LIB=${ROOT}/lib - -# ${ROOT}/lib/unimelb-mf-clients.jar -JAR=${LIB}/unimelb-mf-clients.jar - -# check if unimelb-mf-clients.jar exists -[[ ! -f $JAR ]] && echo "${JAR} is not found." >&2 && exit 2 - -# JRE included? -JRE=${ROOT}/jre -if [[ -d ${JRE} ]]; then - JAVA=${JRE}/bin/java -else - JAVA=$(which java) - [[ -z ${JAVA} ]] && echo "could not find java" && exit 1 -fi - -# execute the command -$JAVA -XX:+UseG1GC -XX:+UseStringDeduplication -Xmx1g -cp "${JAR}" unimelb.mf.client.instrument.cli.InstrumentUploadFind ${1+"$@"} diff --git a/src/main/scripts/windows/unimelb-mf-instrument-upload-find.cmd b/src/main/scripts/windows/unimelb-mf-instrument-upload-find.cmd deleted file mode 100644 index cdc8a446a51ee893c9b9951bbc8f90075efa7e4f..0000000000000000000000000000000000000000 --- a/src/main/scripts/windows/unimelb-mf-instrument-upload-find.cmd +++ /dev/null @@ -1,21 +0,0 @@ -@echo off - -setlocal - -pushd "%~dp0..\..\" -set "ROOT=%cd%" -popd - -set "JRE=%ROOT%\jre" - -if exist "%JRE%\" ( - set "JAVA=%JRE%\bin\java" -) else ( - set JAVA=java -) - -set JAR=%ROOT%\lib\unimelb-mf-clients.jar - -"%JAVA%" -XX:+UseG1GC -XX:+UseStringDeduplication -Xmx1g -cp "%JAR%" unimelb.mf.client.instrument.cli.InstrumentUploadFind %* - -endlocal