diff --git a/.classpath b/.classpath index f0257c5a54142160c682eb3de6435e2e5ec2d03a..cd377e474d1ca053f6d38878c601a5b9f5a669ab 100644 --- a/.classpath +++ b/.classpath @@ -23,21 +23,9 @@ <attribute name="maven.pomderived" value="true"/> </attributes> </classpathentry> - <classpathentry kind="src" path="target/generated-sources/annotations"> + <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> <attributes> - <attribute name="optional" value="true"/> <attribute name="maven.pomderived" value="true"/> - <attribute name="ignore_optional_problems" value="true"/> - <attribute name="m2e-apt" value="true"/> - </attributes> - </classpathentry> - <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> - <attributes> - <attribute name="optional" value="true"/> - <attribute name="maven.pomderived" value="true"/> - <attribute name="ignore_optional_problems" value="true"/> - <attribute name="m2e-apt" value="true"/> - <attribute name="test" value="true"/> </attributes> </classpathentry> <classpathentry kind="output" path="target/classes"/> diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index f9fe34593fcd3624a964478aeb438b0d44fe7237..839d647eef851c560a9854ff81d9caa1df594ced 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,5 @@ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 encoding//src/test/java=UTF-8 encoding/<project>=UTF-8 diff --git a/pom.xml b/pom.xml index 9656bff7670bd6f797feb5664e7dc5ef0f9da1fb..5ad3481c8f2092dfe5803665c14402978c360118 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,7 @@ <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss z</maven.build.timestamp.format> <skipTests>true</skipTests> <mexplorer.version>1.3.27</mexplorer.version> <jre8mac64file>jre-8u201-macosx-x64</jre8mac64file> @@ -51,7 +52,6 @@ </dependencies> <build> <plugins> - <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>wagon-maven-plugin</artifactId> @@ -119,6 +119,28 @@ <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> + <execution> + <id>prepare-resources</id> + <phase>generate-resources</phase> + <configuration> + <target> + <mkdir + dir="${project.build.directory}/classes/unimelb/mf/client" /> + <copy + file="${project.basedir}/src/main/resources/unimelb/mf/client/app.properties" + tofile="${project.build.directory}/classes/unimelb/mf/client/app.properties" /> + <replace + file="${project.build.directory}/classes/unimelb/mf/client/app.properties" + token="@VERSION@" value="${project.version}" /> + <replace + file="${project.build.directory}/classes/unimelb/mf/client/app.properties" + token="@BUILD_TIME@" value="${maven.build.timestamp}" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> <execution> <phase>package</phase> <configuration> @@ -344,5 +366,41 @@ </dependencies> </plugin> </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.apache.maven.plugins + </groupId> + <artifactId> + maven-antrun-plugin + </artifactId> + <versionRange> + [1.8,) + </versionRange> + <goals> + <goal>run</goal> + </goals> + </pluginExecutionFilter> + <action> + <execute /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> </build> </project> diff --git a/src/main/java/unimelb/mf/client/App.java b/src/main/java/unimelb/mf/client/App.java new file mode 100644 index 0000000000000000000000000000000000000000..5aad90b22cd7eb700cd9deb603c29d6c5cf1b64d --- /dev/null +++ b/src/main/java/unimelb/mf/client/App.java @@ -0,0 +1,35 @@ +package unimelb.mf.client; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public final class App { + + private App() { + } + + private static Properties _properties = null; + + static void loadProperties() { + if (_properties == null) { + _properties = new Properties(); + try (InputStream in = App.class.getResourceAsStream("app.properties")) { + _properties.load(in); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static String version() { + loadProperties(); + return _properties.getProperty("version"); + } + + public static String buildTime() { + loadProperties(); + return _properties.getProperty("buildTime"); + } + +} diff --git a/src/main/java/unimelb/mf/client/sync/MFSyncApp.java b/src/main/java/unimelb/mf/client/sync/MFSyncApp.java index ffdd0d43028dd1f0d4fddbc1f3961256947efc65..412ab8e18301775c94a99a5e01324f7528ec2721 100644 --- a/src/main/java/unimelb/mf/client/sync/MFSyncApp.java +++ b/src/main/java/unimelb/mf/client/sync/MFSyncApp.java @@ -9,6 +9,7 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; +import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -16,6 +17,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Date; +import java.util.EnumSet; import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -29,6 +31,7 @@ import java.util.logging.Level; import arc.xml.XmlDoc; import arc.xml.XmlStringWriter; +import unimelb.mf.client.App; import unimelb.mf.client.file.PosixAttributes; import unimelb.mf.client.sync.check.AssetItem; import unimelb.mf.client.sync.check.ChecksumType; @@ -566,48 +569,51 @@ public abstract class MFSyncApp extends AbstractMFApp<unimelb.mf.client.sync.set if (job instanceof DirectoryUploadCheckJob) { DirectoryUploadCheckJob ucj = (DirectoryUploadCheckJob) job; List<Path> files = new ArrayList<Path>(settings().batchSize()); - Files.walkFileTree(ucj.srcDirectory(), new SimpleFileVisitor<Path>() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - try { - if (ucj.accept(file)) { - files.add(file); - if (files.size() >= settings().batchSize()) { - // check files - _queriers.submit(new FileSetCheckTask(session(), logger(), new ArrayList<Path>(files), - ucj, settings().csumCheck(), settings().checkHandler(), _workers)); - files.clear(); + Files.walkFileTree(ucj.srcDirectory(), settings().followLinks() ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) + : EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + try { + if (ucj.accept(file)) { + files.add(file); + if (files.size() >= settings().batchSize()) { + // check files + _queriers.submit(new FileSetCheckTask(session(), logger(), + new ArrayList<Path>(files), ucj, settings().csumCheck(), + settings().checkHandler(), _workers)); + files.clear(); + } + } + } catch (Throwable e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + return FileVisitResult.TERMINATE; + } + logger().log(Level.SEVERE, e.getMessage(), e); } + return FileVisitResult.CONTINUE; } - } catch (Throwable e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - return FileVisitResult.TERMINATE; - } - logger().log(Level.SEVERE, e.getMessage(), e); - } - return FileVisitResult.CONTINUE; - } - @Override - public FileVisitResult visitFileFailed(Path file, IOException ioe) { - logger().log(Level.SEVERE, "Failed to access file: " + file, ioe); - return FileVisitResult.CONTINUE; - } + @Override + public FileVisitResult visitFileFailed(Path file, IOException ioe) { + logger().log(Level.SEVERE, "Failed to access file: " + file, ioe); + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException ioe) { - if (ioe != null) { - logger().log(Level.SEVERE, ioe.getMessage(), ioe); - } - return FileVisitResult.CONTINUE; - } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException ioe) { + if (ioe != null) { + logger().log(Level.SEVERE, ioe.getMessage(), ioe); + } + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - return super.preVisitDirectory(dir, attrs); - } - }); + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + return super.preVisitDirectory(dir, attrs); + } + }); if (!files.isEmpty()) { // check files _queriers.submit(new FileSetCheckTask(session(), logger(), new ArrayList<Path>(files), ucj, @@ -647,47 +653,50 @@ public abstract class MFSyncApp extends AbstractMFApp<unimelb.mf.client.sync.set duj.resolveDstAssetNamespaceStore(session()); List<Path> files = new ArrayList<Path>(settings().batchSize()); - Files.walkFileTree(duj.srcDirectory(), new SimpleFileVisitor<Path>() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - try { - if (duj.accept(file)) { - files.add(file); - if (files.size() >= settings().batchSize()) { - _queriers.submit(new FileSetUploadTask(session(), logger(), new ArrayList<Path>(files), - duj, settings().csumCheck(), settings().retry(), _ul, _workers)); - files.clear(); + Files.walkFileTree(duj.srcDirectory(), settings().followLinks() ? EnumSet.of(FileVisitOption.FOLLOW_LINKS) + : EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + try { + if (duj.accept(file)) { + files.add(file); + if (files.size() >= settings().batchSize()) { + _queriers.submit(new FileSetUploadTask(session(), logger(), + new ArrayList<Path>(files), duj, settings().csumCheck(), + settings().retry(), _ul, _workers)); + files.clear(); + } + } + } catch (Throwable e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + return FileVisitResult.TERMINATE; + } + logger().log(Level.SEVERE, e.getMessage(), e); } + return FileVisitResult.CONTINUE; } - } catch (Throwable e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - return FileVisitResult.TERMINATE; - } - logger().log(Level.SEVERE, e.getMessage(), e); - } - return FileVisitResult.CONTINUE; - } - @Override - public FileVisitResult visitFileFailed(Path file, IOException ioe) { - logger().log(Level.SEVERE, "Failed to access file: " + file, ioe); - return FileVisitResult.CONTINUE; - } + @Override + public FileVisitResult visitFileFailed(Path file, IOException ioe) { + logger().log(Level.SEVERE, "Failed to access file: " + file, ioe); + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException ioe) { - if (ioe != null) { - logger().log(Level.SEVERE, ioe.getMessage(), ioe); - } - return FileVisitResult.CONTINUE; - } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException ioe) { + if (ioe != null) { + logger().log(Level.SEVERE, ioe.getMessage(), ioe); + } + return FileVisitResult.CONTINUE; + } - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - return super.preVisitDirectory(dir, attrs); - } - }); + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + return super.preVisitDirectory(dir, attrs); + } + }); if (!files.isEmpty()) { _queriers.submit(new FileSetUploadTask(session(), logger(), new ArrayList<Path>(files), duj, settings().csumCheck(), settings().retry(), _ul, _workers)); @@ -827,4 +836,8 @@ public abstract class MFSyncApp extends AbstractMFApp<unimelb.mf.client.sync.set } } + public void printVersion() { + System.out.println(String.format("%s %s (build-time: %s)", this.applicationName(), App.version(), App.buildTime())); + } + } diff --git a/src/main/java/unimelb/mf/client/sync/cli/MFCheck.java b/src/main/java/unimelb/mf/client/sync/cli/MFCheck.java index ea2629d8377ecb57e33b414d4cefa0e1830e58cf..b984499a6920cd735c53368f67663a78a08802ec 100644 --- a/src/main/java/unimelb/mf/client/sync/cli/MFCheck.java +++ b/src/main/java/unimelb/mf/client/sync/cli/MFCheck.java @@ -77,6 +77,7 @@ public class MFCheck extends MFSyncApp { System.out.println(" --batch-size <size> Size of the query result. Defaults to " + unimelb.mf.client.sync.settings.Settings.DEFAULT_BATCH_SIZE); System.out.println(" --quiet Do not print progress messages."); System.out.println(" --help Prints usage."); + System.out.println(" --version Prints version."); System.out.println(); System.out.println("POSITIONAL ARGUMENTS:"); System.out.println(" <dir> Local directory path."); @@ -101,6 +102,10 @@ public class MFCheck extends MFSyncApp { printUsage(); System.exit(0); } + if ("--version".equalsIgnoreCase(args[i])) { + printVersion(); + System.exit(0); + } int n = parseMFOptions(args, i); if (n > 0) { i += n; diff --git a/src/main/java/unimelb/mf/client/sync/cli/MFDownload.java b/src/main/java/unimelb/mf/client/sync/cli/MFDownload.java index ea8597d74e001056ba81cb2e0fedf64d4f446699..979804324a4ce922d4ac9148144deabb609223b4 100644 --- a/src/main/java/unimelb/mf/client/sync/cli/MFDownload.java +++ b/src/main/java/unimelb/mf/client/sync/cli/MFDownload.java @@ -76,6 +76,7 @@ public class MFDownload extends MFSyncApp { System.out.println(" --sync-delete-files Delete local files that do not have corresponding assets exist on the server side."); System.out.println(" --quiet Do not print progress messages."); System.out.println(" --help Prints usage."); + System.out.println(" --version Prints version."); System.out.println(); System.out.println("POSITIONAL ARGUMENTS:"); System.out.println(" <namespace> The asset namespace to download."); @@ -102,6 +103,10 @@ public class MFDownload extends MFSyncApp { printUsage(); System.exit(args.length > 1 ? 1 : 0); } + if ("--version".equalsIgnoreCase(arg)) { + printVersion(); + System.exit(args.length > 1 ? 1 : 0); + } } if (args.length == 2 && "--config".equalsIgnoreCase(args[0])) { @@ -324,10 +329,10 @@ public class MFDownload extends MFSyncApp { } else if ("--log-dir".equalsIgnoreCase(args[i])) { Path logDir = Paths.get(args[i + 1]).toAbsolutePath(); if (!Files.exists(logDir)) { - throw new IllegalArgumentException("Directory: " + args[i] + " does not exist."); + throw new IllegalArgumentException("Directory: " + args[i + 1] + " does not exist."); } if (!Files.isDirectory(logDir)) { - throw new IllegalArgumentException(args[i] + " is not a directory."); + throw new IllegalArgumentException(args[i + 1] + " is not a directory."); } settings().setLogDirectory(logDir); return 2; diff --git a/src/main/java/unimelb/mf/client/sync/cli/MFUpload.java b/src/main/java/unimelb/mf/client/sync/cli/MFUpload.java index 70c9b6d7d2589c2611146e932c0fd5e1ccfa99dc..ad7d04351c80cbd912e535408fdcce0c87f6f156 100644 --- a/src/main/java/unimelb/mf/client/sync/cli/MFUpload.java +++ b/src/main/java/unimelb/mf/client/sync/cli/MFUpload.java @@ -73,6 +73,7 @@ public class MFUpload extends MFSyncApp { System.out.println(" --hard-delete-assets Force the asset deletion (see --sync-delete-assets) process to hard delete assets. Otherwise, the behaviour is controlled by server properties (whether a deletion is a soft or hard action)."); System.out.println(" --quiet Do not print progress messages."); System.out.println(" --help Prints usage."); + System.out.println(" --version Prints version."); System.out.println(); System.out.println("POSITIONAL ARGUMENTS:"); System.out.println(" src-dir Source directory to upload."); @@ -101,6 +102,10 @@ public class MFUpload extends MFSyncApp { printUsage(); System.exit(args.length > 1 ? 1 : 0); } + if ("--version".equalsIgnoreCase(arg)) { + printVersion(); + System.exit(args.length > 1 ? 1 : 0); + } } if (args.length == 2 && "--config".equalsIgnoreCase(args[0])) { @@ -336,10 +341,10 @@ public class MFUpload extends MFSyncApp { } else if ("--log-dir".equalsIgnoreCase(args[i])) { Path logDir = Paths.get(args[i + 1]).toAbsolutePath(); if (!Files.exists(logDir)) { - throw new IllegalArgumentException("Directory: " + args[i] + " does not exist."); + throw new IllegalArgumentException("Directory: " + args[i + 1] + " does not exist."); } if (!Files.isDirectory(logDir)) { - throw new IllegalArgumentException(args[i] + " is not a directory."); + throw new IllegalArgumentException(args[i + 1] + " is not a directory."); } settings().setLogDirectory(logDir); return 2; diff --git a/src/main/java/unimelb/mf/client/sync/settings/Settings.java b/src/main/java/unimelb/mf/client/sync/settings/Settings.java index 591b835a0144f5dc15a86a7220313e7447fe1447..daa6a63b7543d0edd7c5d50792f0f2a97aac54fb 100644 --- a/src/main/java/unimelb/mf/client/sync/settings/Settings.java +++ b/src/main/java/unimelb/mf/client/sync/settings/Settings.java @@ -72,6 +72,8 @@ public class Settings implements MFApp.Settings { private boolean _hardDestroyAssets = false; + private boolean _followLinks = false; + public Settings() { _jobs = new ArrayList<Job>(); } @@ -489,4 +491,11 @@ public class Settings implements MFApp.Settings { return (hasUploadJobs()) && deleteAssets(); } + public boolean followLinks() { + return _followLinks; + } + + public void setFollowLinks(boolean followLinks) { + _followLinks = followLinks; + } } diff --git a/src/main/resources/unimelb/mf/client/app.properties b/src/main/resources/unimelb/mf/client/app.properties new file mode 100644 index 0000000000000000000000000000000000000000..d9acd6ffdf3ba971c9ea53f52bcbeae8397a7acd --- /dev/null +++ b/src/main/resources/unimelb/mf/client/app.properties @@ -0,0 +1,3 @@ +version=@VERSION@ +buildTime=@BUILD_TIME@ + diff --git a/src/main/scripts/unix/namespace-download-shell-script-url-create b/src/main/scripts/unix/namespace-download-shell-script-url-create new file mode 100755 index 0000000000000000000000000000000000000000..be1a9f637f41b2bea7274265727598a069b9e341 --- /dev/null +++ b/src/main/scripts/unix/namespace-download-shell-script-url-create @@ -0,0 +1,156 @@ +#!/bin/bash + +# Default values for the script arguments +EXPIRE_DAYS=14 +OVERWRITE=false +VERBOSE=true + + +# aterm.jar download url +ATERM_URL=https://mediaflux.researchsoftware.unimelb.edu.au/mflux/aterm.jar + +# function to print usage +usage() { + echo "" + echo "Usage:" + echo " $(basename $0) [-h|--help] [--expire-days <number-of-days>] [--ncsr <ncsr>] [--overwrite] [--quiet] <namespace>" + echo "" + echo "Options:" + echo " -h | --help prints usage." + echo " --email <addresses> specify the email recipient of the generated url. Can be comma-separated if there are more than one." + echo " --expire-days <number-of-days> expiry of the auth token. Defaults to ${EXPIRE_DAYS} days." + echo " --overwrite overwrite if output file exists." + echo " --quiet do not print output message." + echo "" + echo "Positional arguments:" + echo " <namespace> Mediaflux asset namespace to be downloaded by the scripts. Can be multiple, but must be from the same project." + echo "" + echo "Examples:" + echo " $(basename $0) --email user1@unimelb.edu.au --expire-days 10 proj-abc-1128.4.999/RAW_DATA proj-abc-1128.4.999/PROCESSED_DATA" + echo "" +} + +# check java +[[ -z $(which java) ]] && echo "Error: cannot find java." 1>&2 && exit 1 + +# check mflux.cfg +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && MFLUX_CFG="./mflux.cfg" +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && MFLUX_CFG="${HOME}/.Arcitecta/mflux.cfg" +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && MFLUX_CFG="$(dirname ${BASH_SOURCE[0]})/../../config/mflux.cfg" +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && MFLUX_CFG="$(dirname ${BASH_SOURCE[0]})/../../mflux.cfg" +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && MFLUX_CFG="$(dirname ${BASH_SOURCE[0]})/mflux.cfg" +[[ -z $MFLUX_CFG || ! -f $MFLUX_CFG ]] && echo "Error: cannot find ${HOME}/.Arcitecta/mflux.cfg." 1>&2 && exit 1 + +# check aterm.jar +[[ -z $MFLUX_ATERM || ! -f $MFLUX_ATERM ]] && MFLUX_ATERM="$(dirname ${BASH_SOURCE[0]})/../../lib/aterm.jar" +[[ -z $MFLUX_ATERM || ! -f $MFLUX_ATERM ]] && MFLUX_ATERM="${HOME}/.Arcitecta/aterm.jar" +[[ -z $MFLUX_ATERM || ! -f $MFLUX_ATERM ]] && MFLUX_ATERM=/opt/mediaflux/bin/aterm.jar +[[ -z $MFLUX_ATERM || ! -f $MFLUX_ATERM ]] && MFLUX_ATERM=./aterm.jar + + +# download aterm.jar +if [[ -z $MFLUX_ATERM || ! -f $MFLUX_ATERM ]]; then + MFLUX_ATERM=${HOME}/.Arcitecta/aterm.jar + mkdir -p ${HOME}/.Arcitecta + CURL=$(which curl) + WGET=$(which wget) + [[ -z "${CURL}" && -z "${WGET}" ]] && echo "Error: cannot download aterm.jar. Found no curl or wget." 1>&2 && exit 1 + if [[ ! -z "${CURL}" ]]; then + curl -f --create-dirs -k -o "$(dirname $0)/aterm.jar" "${ATERM_URL}" + else + wget --no-check-certificate -O "$(dirname $0)/aterm.jar" "${ATERM_URL}" + fi + if [[ $? -ne 0 ]]; then + echo "Error: failed to download aterm.jar" + exit 1 + fi +fi + +ATERM="java -jar -Dmf.cfg=$MFLUX_CFG $MFLUX_ATERM nogui" +SERVICE=unimelb.asset.download.shell.script.url.create +COMMAND="${ATERM} ${SERVICE}" + +## +## parse arguments +## +declare -a NAMESPACES=() +declare -a EMAILS=() +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + --expire-days) + EXPIRE_DAYS="$2" + shift + shift + ;; + --email) + IFS=',' read -r -a EMAILS <<< "$2"; unset IFS + shift + shift + ;; + --overwrite) + OVERWRITE=true + shift + ;; + --quiet) + VERBOSE=false + shift + ;; + -h|--help) + shift + usage && exit 0 + ;; + *) + NAMESPACES+=("$1") + shift + ;; +esac +done + +if [[ ${#NAMESPACES[@]} -eq 0 ]]; then + echo "Error: no Mediaflux asset namespace(directory) is specified." 1>&2 + usage + exit 1 +fi + +COMMAND="${COMMAND} :download <" +for ns in "${NAMESPACES[@]}" +do + # resolve project id from namespace path + prj=$(echo "${ns}" | egrep -o "proj-.*-[0-9]+(.[0-9]+)*") + if [[ -z ${prj} ]]; then + echo "Error: cannot resolve project id from namespace path: %{ns}." 1>&2 + exit 1 + fi + + # if multiple namespaces specified, check if they're from same project. + if [[ -z ${PROJECT} ]]; then + PROJECT=${prj} + ROLE="${PROJECT}:participant-a" + ROLE_NAMESPACE="${PROJECT}:" + else + if [[ ${prj} != ${PROJECT} ]]; then + echo "Error: you cannot specify namespaces from multiple projects." 1>&2 + usage + exit 1 + fi + fi + COMMAND="${COMMAND} :namespace ${ns}" +done +COMMAND="${COMMAND} :token < :role -type role ${ROLE} :to now+${EXPIRE_DAYS}day > :verbose ${VERBOSE} :overwrite ${OVERWRITE}" +COMMAND="${COMMAND} >" + +COMMAND="${COMMAND} :token < :perm < :resource -type role:namespace ${ROLE_NAMESPACE} :access ADMINISTER > >" + +if [[ ${#EMAILS[@]} -gt 0 ]]; then + COMMAND="${COMMAND} :email < " + for email in "${EMAILS[@]}" + do + COMMAND="${COMMAND} :to ${email}" + done + COMMAND="${COMMAND} >" +fi + +# execute the command to generate the url +${COMMAND}