All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.duracloud.sync.config.SyncToolConfigParser Maven / Gradle / Ivy

There is a newer version: 8.1.0
Show newest version
/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 *     http://duracloud.org/license/
 */
package org.duracloud.sync.config;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.duracloud.common.util.CommandLineToolUtil;
import org.duracloud.common.util.ConsolePrompt;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Handles reading the configuration parameters for the Sync Tool
 *
 * @author: Bill Branan
 * Date: Mar 15, 2010
 */
public class SyncToolConfigParser {

    protected static final long GIGABYTE = 1000*1000*1000;

    protected static final String BACKUP_FILE_NAME = "synctool.config";
    protected static final String PREV_BACKUP_FILE_NAME = "synctool.config.bak";

    protected static final int DEFAULT_PORT = 443;
    protected static final long DEFAULT_POLL_FREQUENCY = 10000;
    protected static final int DEFAULT_NUM_THREADS = 3;
    protected static final int DEFAULT_MAX_FILE_SIZE = 1; // 1 GB
    protected static final String context = "durastore";
    
    private Options cmdOptions;
    private Options configFileOptions;

    private CommandLineToolUtil cmdLineUtil;

    /**
     * Creates a parser for command line configuration options.
     */
    public SyncToolConfigParser() {
        cmdLineUtil = new CommandLineToolUtil();

       // Command Line Options
       cmdOptions = new Options();

       Option hostOption =
           new Option("h", "host", true,
                      "the host address of the DuraCloud " +
                      "DuraStore application");
       hostOption.setRequired(true);
       cmdOptions.addOption(hostOption);

       Option portOption =
           new Option("r", "port", true,
                      "the port of the DuraCloud DuraStore application " +
                      "(optional, default value is " + DEFAULT_PORT + ")");
       portOption.setRequired(false);
       cmdOptions.addOption(portOption);

       Option usernameOption =
           new Option("u", "username", true,
                      "the username necessary to perform writes to DuraStore");
       usernameOption.setRequired(true);
       cmdOptions.addOption(usernameOption);

        Option passwordOption =
            new Option("p",
                       "password",
                       true,
                       "the password necessary to perform writes to DuraStore; NOTICE: "
                           + "if no password is specified in the command line the sync tool will "
                           + "look for an environment variable named "
                           + CommandLineToolUtil.PASSWORD_ENV_VARIABLE_NAME
                           + " containing the password.  Finally, if this environment variable "
                           + "does not exist the user will be prompted for the password.");
       passwordOption.setRequired(false);
       cmdOptions.addOption(passwordOption);

        Option storeIdOption =
            new Option("i", "store-id", true,
                       "the Store ID for the DuraCloud storage provider");
        storeIdOption.setRequired(false);
        cmdOptions.addOption(storeIdOption);        

       Option spaceId =
           new Option("s", "space-id", true,
                      "the ID of the DuraCloud space where content " +
                      "will be stored");
       spaceId.setRequired(true);
       cmdOptions.addOption(spaceId);

       Option workDirOption =
           new Option("w", "work-dir", true,
                      "the state of the sync tool is persisted to " +
                      "this directory (optional, default value is " +
                      "duracloud-sync-work directory in user home)");
       workDirOption.setRequired(false);
       cmdOptions.addOption(workDirOption);

       Option contentDirs =
           new Option("c", "content-dirs", true,
                      "the directory paths to monitor and sync with DuraCloud");
       contentDirs.setRequired(true);
       contentDirs.setArgs(Option.UNLIMITED_VALUES);
       cmdOptions.addOption(contentDirs);

       Option pollFrequency =
           new Option("f", "poll-frequency", true,
                      "the time (in ms) to wait between each poll of the " +
                      "sync-dirs (optional, default value is " +
                      DEFAULT_POLL_FREQUENCY + ")");
        pollFrequency.setRequired(false);
        cmdOptions.addOption(pollFrequency);

       Option numThreads =
           new Option("t", "threads", true,
                      "the number of threads in the pool used to manage " +
                      "file transfers (optional, default value is " +
                      DEFAULT_NUM_THREADS + ")");
        numThreads.setRequired(false);
        cmdOptions.addOption(numThreads);

       Option maxFileSize =
           new Option("m", "max-file-size", true,
                      "the maximum size of a stored file in GB (value must " +
                      "be between 1 and 5), larger files will be split into " +
                      "pieces (optional, default value is " +
                      DEFAULT_MAX_FILE_SIZE + ")");
        maxFileSize.setRequired(false);
        cmdOptions.addOption(maxFileSize);


         Option renameUpdates =
             new Option("n", "rename-updates", true,
                        "indicates that updates should be synced to the cloud and renamed. " +
                        "Specify an optional suffix to override default " +
                        "( \"" + SyncToolConfig.DEFAULT_UPDATE_SUFFIX + "\"); " + 
                        "To prevent updates altogether, see option -o. " +
                        "(optional, not set by default)");
         renameUpdates.setRequired(false);
         renameUpdates.setArgName("suffix");
         renameUpdates.setOptionalArg(true);
         cmdOptions.addOption(renameUpdates);

         Option syncUpdates =
             new Option("o", "no-update", false,
                        "indicates that changed files should not be updated; " +
                        "to perform updates without overwriting, see option -n. " +
                        "(optional, not set by default)");
         syncUpdates.setRequired(false);
         cmdOptions.addOption(syncUpdates);

       Option syncDeletes =
           new Option("d", "sync-deletes", false,
                      "indicates that deletes performed on files within the " +
                      "sync directories should also be performed on those " +
                      "files in DuraCloud; if this option is not included " +
                      "all deletes are ignored (optional, not set by default)");
        syncDeletes.setRequired(false);
        cmdOptions.addOption(syncDeletes);

       Option cleanStart =
           new Option("l", "clean-start", false,
                      "indicates that the sync tool should perform a clean " +
                      "start, ensuring that all files in all content " +
                      "directories are checked against DuraCloud, even if " +
                      "those files have not changed locally since the last " +
                      "run of the sync tool. (optional, not set by default)");
        cleanStart.setRequired(false);
        cmdOptions.addOption(cleanStart);

       Option jumpStart =
           new Option("j", "jump-start", false,
                      "indicates that the sync tool should not attempt to " +
                      "check if content to be synchronized is already in " +
                      "DuraCloud, but should instead transfer all content. " +
                      "This option is best used for new data sets. " +
                      "(optional, not set by default)");
        jumpStart.setRequired(false);
        cmdOptions.addOption(jumpStart);

       Option exitOnCompletion =
           new Option("x", "exit-on-completion", false,
                      "indicates that the sync tool should exit once it has " +
                      "completed a scan of the content directories and synced " +
                      "all files; if this option is included, the sync tool " +
                      "will not continue to monitor the sync dirs " +
                      "(optional, not set by default)");
        exitOnCompletion.setRequired(false);
        cmdOptions.addOption(exitOnCompletion);

       Option excludeOption =
           new Option("e", "exclude", true,
                      "file which provides a list of files and/or " +
                      "directories to exclude from the sync (one file or " +
                      "directory name rule per line)");
       excludeOption.setRequired(false);
       cmdOptions.addOption(excludeOption);

       Option prefixOption =
           new Option("a", "prefix", true,
                      "a prefix that is added to the beginning of the ID of " +
                      "each content item that is stored in DuraCloud. For " +
                      "example, a prefix value of 'a/b/c/' with a content " +
                      "item whose path is 'dir1/file.txt' would result in " +
                      "the file stored in DuraCloud as 'a/b/c/dir1/file.txt " +
                      "(optional)");
       prefixOption.setRequired(false);
       cmdOptions.addOption(prefixOption);

       // Options to use Backup Config
       configFileOptions = new Options();

       Option configFileOption =
           new Option("g", "config-file", true,
                      "read configuration from this file (a file containing " +
                      "the most recently used configuration can be found in " +
                      "the work-dir, named " + BACKUP_FILE_NAME + ")");
       configFileOption.setRequired(true);
       configFileOptions.addOption(configFileOption);
    }

    /**
     * Parses command line configuration into an object structure, validates
     * correct values along the way.
     *
     * Prints a help message and exits the JVM on parse failure.
     *
     * @param args command line configuration values
     * @return populated SyncToolConfig
     */
    public SyncToolConfig processCommandLine(String[] args) {
        SyncToolConfig config = null;
        try {
            config = processConfigFileOptions(args);
        } catch (ParseException e) {
            printHelp(e.getMessage());
        }
        return config;
    }

    protected SyncToolConfig processConfigFileOptions(String[] args)
        throws ParseException {
        try {
            CommandLineParser parser = new PosixParser();
            CommandLine cmd = parser.parse(configFileOptions, args);

            String configFilePath = cmd.getOptionValue("g");
            File configFile = new File(configFilePath);
            if(!configFile.exists()) {
                throw new ParseException("No configuration file exists at " +
                                         "the indicated path: " +
                                         configFilePath);
            }

            String[] configFileArgs = retrieveConfig(configFile);
            return processAndBackup(configFileArgs);
        } catch(ParseException e) {           
            return processAndBackup(args);
        }
    }

    private SyncToolConfig processAndBackup(String[] args)
        throws ParseException {
        SyncToolConfig config = processStandardOptions(args);

        // Make sure work dir is set
        SyncConfig.setWorkDir(config.getWorkDir());
        config.setWorkDir(SyncConfig.getWorkDir());

        backupConfig(config.getWorkDir(), args);
        return config;
    }

    protected SyncToolConfig processStandardOptions(String[] args)
        throws ParseException {
        return processStandardOptions(args, true);
    }

    protected SyncToolConfig processStandardOptions(String[] args,
                                                    boolean requirePassword)
        throws ParseException {
        CommandLineParser parser = new PosixParser();
        CommandLine cmd = parser.parse(cmdOptions, args);
        SyncToolConfig config = new SyncToolConfig();

        config.setContext(context);
        config.setHost(cmd.getOptionValue("h"));
        config.setUsername(cmd.getOptionValue("u"));
        
        if (null != cmd.getOptionValue("p")) {
            config.setPassword(cmd.getOptionValue("p"));
        } else if (null != getPasswordEnvVariable()) {
            config.setPassword(getPasswordEnvVariable());
        } else if (requirePassword) {
            ConsolePrompt console = getConsole();
            if (null == console) {
                printHelp("You must either specify a password in the command "+ 
                          "line or specify the " +
                          CommandLineToolUtil.PASSWORD_ENV_VARIABLE_NAME + 
                          " environmental variable.");
            } else {
                char[] password = console.readPassword("DuraCloud password: ");
                config.setPassword(new String(password));
            }
        }

        config.setSpaceId(cmd.getOptionValue("s"));

        if(cmd.hasOption("i")) {
            config.setStoreId(cmd.getOptionValue("i"));
        }

        if(cmd.hasOption("r")) {
            try {
                config.setPort(Integer.valueOf(cmd.getOptionValue("r")));
            } catch(NumberFormatException e) {
                throw new ParseException("The value for port (-r) must be " +
                                         "a number.");
            }
        } else {
            config.setPort(DEFAULT_PORT);
        }

        if(cmd.hasOption("w")) {
            File workDir = new File(cmd.getOptionValue("w"));
            if(workDir.exists()) {
                if(!workDir.isDirectory()) {
                    throw new ParseException("Work Dir parameter must provide " +
                                             "the path to a directory. " +
                                             "(optional, set to duracloud-" +
                                             "sync-work directory in user's " +
                                             "home directory by default)");
                }
            } else {
                workDir.mkdirs();
            }
            workDir.setWritable(true);
            config.setWorkDir(workDir);
        } else {
            config.setWorkDir(null);
        }

        String[] contentDirPaths = cmd.getOptionValues("c");
        List contentDirs = new ArrayList();
        for(String path : contentDirPaths) {
            File contentDir = new File(path);
            if(!contentDir.exists() || !contentDir.isDirectory()) {
                throw new ParseException("Each content dir value must provide " +
                                         "the path to a directory.");
            }
            contentDirs.add(contentDir);
        }
        config.setContentDirs(contentDirs);

        if(cmd.hasOption("f")) {
            try {
                config.setPollFrequency(Long.valueOf(cmd.getOptionValue("f")));
            } catch(NumberFormatException e) {
                throw new ParseException("The value for poll frequency (-f) " +
                                         "must be a number.");
            }
        } else {
            config.setPollFrequency(DEFAULT_POLL_FREQUENCY);
        }

        if(cmd.hasOption("t")) {
            try {
                config.setNumThreads(Integer.valueOf(cmd.getOptionValue("t")));
            } catch(NumberFormatException e) {
                throw new ParseException("The value for threads (-t) must " +
                                         "be a number.");
            }
        } else {
            config.setNumThreads(DEFAULT_NUM_THREADS);
        }

        if(cmd.hasOption("m")) {
            String error = "The value for max-file-size (-m) must be a " +
                           "number between 1 and 5.";
            try {
                long maxFileSize = Integer.valueOf(cmd.getOptionValue("m"));
                if(maxFileSize >= 1 && maxFileSize <= 5) {
                    config.setMaxFileSize(maxFileSize * GIGABYTE);
                } else {
                    throw new ParseException(error);
                }
            } catch(NumberFormatException e) {
                throw new ParseException(error);
            }
        } else {
            config.setMaxFileSize(DEFAULT_MAX_FILE_SIZE * GIGABYTE);
        }

        if(cmd.hasOption("o") && cmd.hasOption("n")){
            throw new ParseException("Options -o (no updates) and -n " +
                                     "(rename updates) cannot be used together.");
        }
            
        if(cmd.hasOption("o")){
            config.setSyncUpdates(false);
        }

        if(cmd.hasOption("n") && cmd.hasOption("d")){
            throw new ParseException("Options -n (rename updates) and -d " +
                                     "(sync deletes) cannot be used together.");
        }

        if(cmd.hasOption("n")){
            config.setRenameUpdates(true);
            String suffix = cmd.getOptionValue("n");
            if(StringUtils.isNotBlank(suffix)){
                config.setUpdateSuffix(suffix);
            }
        }
        
        if(cmd.hasOption("d")) {
            config.setSyncDeletes(true);
        } else {
            config.setSyncDeletes(false);
        }

        if(cmd.hasOption("l")) {
            config.setCleanStart(true);
        } else {
            config.setCleanStart(false);
        }

        if(cmd.hasOption("j")) {
            config.setJumpStart(true);

            if(cmd.hasOption("n") || cmd.hasOption("o")){
                throw new ParseException(
                    "The Jump Start option (-j) requires that updates be " +
                    "handled as overwrites, thus options -n (rename updates) " +
                    "and -o (no-updates) cannot be used at the same time.");
            }
        } else {
            config.setJumpStart(false);
        }

        if(cmd.hasOption("x")) {
            config.setExitOnCompletion(true);
        } else {
            config.setExitOnCompletion(false);
        }

        if(cmd.hasOption("e")) {
            File excludeFile = new File(cmd.getOptionValue("e"));
            if(!excludeFile.exists()) {
                throw new ParseException("Exclude parameter must provide the " +
                                         "path to a valid file.");
            }
            config.setExcludeList(excludeFile);
        }

        if(cmd.hasOption("a")) {
            config.setPrefix(cmd.getOptionValue("a"));
        }

        return config;
    }

    private void printHelp(String message) {
        System.out.println("\n-----------------------\n" +
                           message +
                           "\n-----------------------\n");

        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp("Running SyncTool",
                            cmdOptions);
        formatter.printHelp("ReRunning SyncTool",
                            configFileOptions); 
        System.exit(1);
    }

    protected void backupConfig(File backupDir, String[] args) {
        File configBackupFile = new File(backupDir, BACKUP_FILE_NAME);
        try {
            if(configBackupFile.exists()) {
                File prevConfigBackupFile =
                    new File(backupDir, PREV_BACKUP_FILE_NAME);
                FileUtils.copyFile(configBackupFile, prevConfigBackupFile);
            }

            BufferedWriter backupWriter =
                new BufferedWriter(new FileWriter(configBackupFile));
            for(String arg : args) {
                backupWriter.write(arg);
                backupWriter.newLine();
                backupWriter.flush();
            }
            backupWriter.close();
        } catch(IOException e) {
            throw new RuntimeException("Unable to write configuration file " +
                                       "due to: " + e.getMessage(), e);
        }
    }

    protected String[] retrieveConfig(File configBackupFile) {
        String[] config = null;
        if(configBackupFile.exists()) {
            ArrayList args = new ArrayList();
            try {
                BufferedReader backupReader =
                    new BufferedReader(new FileReader(configBackupFile));
                String line = backupReader.readLine();
                while(line != null) {
                    args.add(line);
                    line = backupReader.readLine();
                }
                config = args.toArray(new String[args.size()]);
            } catch(IOException e) {
                throw new RuntimeException("Unable to read configuration file " +
                                           "due to: " + e.getMessage(), e);
            }
        }
        return config;
    }

    /**
     * Retrieves the configuration of the previous run of the Sync Tool.
     * If there was no previous run, the backup file cannot be found, or
     * the backup file cannot be read, returns null, otherwise returns the
     * parsed configuration
     * @param backupDir the current backup directory
     * @return config for previous sync tool run, or null
     */
    public SyncToolConfig retrievePrevConfig(File backupDir) {
        File prevConfigBackupFile =
                    new File(backupDir, PREV_BACKUP_FILE_NAME);
        if(prevConfigBackupFile.exists()) {
            String[] prevConfigArgs = retrieveConfig(prevConfigBackupFile);
            try {
                return processStandardOptions(prevConfigArgs, false);
            } catch (ParseException e) {
                return null;
            }          
        } else {
            return null;
        }
    }

    protected String getPasswordEnvVariable() {
        return cmdLineUtil.getPasswordEnvVariable();
    }

    protected ConsolePrompt getConsole() {
        return cmdLineUtil.getConsole();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy