org.fcrepo.importexport.ArgParser.java Source code

Java tutorial

Introduction

Here is the source code for org.fcrepo.importexport.ArgParser.java

Source

/*
 * Licensed to DuraSpace under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * DuraSpace licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fcrepo.importexport;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
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.jena.riot.Lang;

import org.fcrepo.client.FcrepoClient;
import org.fcrepo.importexport.common.Config;
import org.fcrepo.importexport.common.TransferProcess;
import org.fcrepo.importexport.exporter.Exporter;
import org.fcrepo.importexport.importer.Importer;
import org.slf4j.Logger;

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.io.PrintWriter;
import java.util.ArrayList;

import static org.apache.jena.riot.RDFLanguages.contentTypeToLang;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Command-line arguments parser.
 *
 * @author awoods
 * @author escowles
 * @since 2016-08-29
 */
public class ArgParser {

    private static final Logger logger = getLogger(ArgParser.class);

    public static final String DEFAULT_RDF_LANG = "text/turtle";
    public static final String DEFAULT_RDF_EXT = getRDFExtension(DEFAULT_RDF_LANG);
    public static final String CONFIG_FILE_NAME = "importexport.config";

    private final Options configOptions;
    private final Options configFileOptions;

    /**
     * Constructor that creates the command line options
     */
    public ArgParser() {
        // Command Line Options
        configOptions = new Options();
        configFileOptions = new Options();

        // Help option
        configOptions.addOption(Option.builder("h").longOpt("help").hasArg(false).desc("Print these options")
                .required(false).build());

        // Mode option
        configOptions.addOption(Option.builder("m").longOpt("mode").hasArg(true).numberOfArgs(1).argName("mode")
                .desc("Mode: [import|export]").required(true).build());

        // Resource option
        configOptions.addOption(Option.builder("r").longOpt("resource").hasArg(true).numberOfArgs(1)
                .argName("resource").desc("Resource (URI) to import/export").required(true).build());

        // Source Resource option
        configOptions.addOption(Option.builder("s").longOpt("source").hasArg(true).numberOfArgs(1).argName("source")
                .desc("Source (URI) data was exported from, when importing to a different Fedora URI")
                .required(false).build());

        // Binary Directory option
        configOptions.addOption(Option.builder("b").longOpt("binDir").hasArg(true).numberOfArgs(1).argName("binDir")
                .desc("Directory where binaries (files) are stored").required(false).build());

        // Description Directory option
        configOptions.addOption(Option.builder("d").longOpt("descDir").hasArg(true).numberOfArgs(1)
                .argName("descDir").desc("Directory where the RDF descriptions are stored").required(true).build());

        // RDF language option
        configOptions
                .addOption(Option.builder("l").longOpt("rdfLang").hasArg(true).numberOfArgs(1).argName("rdfLang")
                        .desc("RDF language (default: " + DEFAULT_RDF_LANG + ")").required(false).build());

        // username option
        final Option userOption = Option.builder("u").longOpt("user").hasArg(true).numberOfArgs(1).argName("user")
                .desc("username:password for fedora basic authentication").build();
        configOptions.addOption(userOption);
        configFileOptions.addOption(userOption);

        // Config file option
        configFileOptions.addOption(Option.builder("c").longOpt("config").hasArg(true).numberOfArgs(1)
                .argName("config").desc("Path to config file").required(true).build());
    }

    protected Config parseConfiguration(final String[] args) {
        // first see if they've specified a config file
        CommandLine c = null;
        Config config = null;
        try {
            c = parseConfigFileCommandLineArgs(args);
            config = parseConfigFileOptions(c);
            addSharedOptions(c, config);
        } catch (ParseException ignore) {
            logger.debug("Command line argments weren't valid for specifying a config file.");
        }
        if (config == null) {
            // check for presence of the help flag
            if (helpFlagged(args)) {
                printHelpWithoutHeaderMessage();
            }

            try {
                c = parseConfigArgs(args);
                config = this.parseConfigurationArgs(c);
                addSharedOptions(c, config);
            } catch (ParseException e) {
                printHelp("Error parsing args: " + e.getMessage());
            }
        }

        // Write command line options to disk
        saveConfig(c);

        return config;
    }

    /**
     * @param args
     * @return
     */
    private boolean helpFlagged(final String[] args) {
        for (String arg : args) {
            if (arg.equals("-h") || arg.equals("--help")) {
                return true;
            }
        }

        return false;
    }

    private CommandLine parseConfigFileCommandLineArgs(final String[] args) throws ParseException {
        return new DefaultParser().parse(configFileOptions, args);
    }

    private CommandLine parseConfigArgs(final String[] args) throws ParseException {
        return new DefaultParser().parse(configOptions, args);
    }

    /**
     * This method tries to parse the configuration file, if that option was provided
     *
     * @param args from command line
     * @return Config or null if no config file option was provided
     */
    private Config parseConfigFileOptions(final CommandLine cmd) {
        final String[] fileArgs = retrieveConfig(new File(cmd.getOptionValue('c')));
        try {
            final Config config = parseConfigurationArgs(parseConfigArgs(fileArgs));
            addSharedOptions(cmd, config);
            return config;
        } catch (ParseException e) {
            printHelp("Unable to parse config file: " + e.getMessage());
            return null;
        }
    }

    /**
     * This method parses the provided configFile into its equivalent command-line args
     *
     * @param configFile containing config args
     * @return Array of args
     */
    private String[] retrieveConfig(final File configFile) {
        if (!configFile.exists()) {
            printHelp("Configuration file does not exist: " + configFile);
        }

        final ArrayList<String> args = new ArrayList<>();
        try {
            try (final BufferedReader configReader = new BufferedReader(new FileReader(configFile))) {
                String line = configReader.readLine();
                while (line != null) {
                    args.add(line);
                    line = configReader.readLine();
                }
            }
            return args.toArray(new String[args.size()]);

        } catch (IOException e) {
            throw new RuntimeException("Unable to read configuration file due to: " + e.getMessage(), e);
        }
    }

    /**
     * This method parses the command-line args
     *
     * @param cmd command line options
     * @return Config
     */
    private Config parseConfigurationArgs(final CommandLine cmd) {
        final Config config = new Config();

        // Inspect Mode option
        final String mode = cmd.getOptionValue('m');
        if (!mode.equalsIgnoreCase("import") && !mode.equalsIgnoreCase("export")) {
            printHelp("Invalid 'mode' option: " + mode);
        }

        config.setMode(mode);
        config.setResource(cmd.getOptionValue('r'));
        config.setBinaryDirectory(cmd.getOptionValue('b'));
        config.setDescriptionDirectory(cmd.getOptionValue('d'));

        final String rdfLanguage = cmd.getOptionValue('l', DEFAULT_RDF_LANG);
        config.setRdfLanguage(rdfLanguage);
        config.setRdfExtension(getRDFExtension(rdfLanguage));
        config.setSource(cmd.getOptionValue('s'));

        return config;
    }

    private static String getRDFExtension(final String language) {
        final Lang lang = contentTypeToLang(language);
        if (lang == null) {
            throw new RuntimeException(language + " is not a recognized RDF language");
        }

        return "." + lang.getFileExtensions().get(0);
    }

    /**
     * This method add/updates the values of any options that may be
     * valid in either scenario (config file or fully command line)
     *
     * @param cmd a parsed command line
     * @return Config the config which may be updated
     */
    private void addSharedOptions(final CommandLine cmd, final Config config) {
        final String user = cmd.getOptionValue("user");
        if (user != null) {
            if (user.indexOf(':') == -1) {
                printHelp("user option must be in the format username:password");
            } else {
                config.setUsername(user.substring(0, user.indexOf(':')));
                config.setPassword(user.substring(user.indexOf(':') + 1));
            }
        }
    }

    /**
     * This method writes the configuration file to disk.  The current
     * implementation omits the user/password information.
     * @param args to be persisted
     */
    private void saveConfig(final CommandLine cmd) {
        final File configFile = new File(System.getProperty("java.io.tmpdir"), CONFIG_FILE_NAME);

        // Leave existing config file alone
        if (configFile.exists()) {
            logger.info("Configuration file exists, new file will NOT be created: {}", configFile.getPath());
            return;
        }

        // Write config to file
        try (final BufferedWriter configWriter = new BufferedWriter(new FileWriter(configFile));) {
            for (Option option : cmd.getOptions()) {
                // write out all but the username/password
                if (!option.getOpt().equals("u")) {
                    configWriter.write("-" + option.getOpt());
                    configWriter.newLine();
                    if (option.getValue() != null) {
                        configWriter.write(option.getValue());
                        configWriter.newLine();
                    }
                }
            }

            logger.info("Saved configuration to: {}", configFile.getPath());

        } catch (IOException e) {
            throw new RuntimeException("Unable to write configuration file due to: " + e.getMessage(), e);
        }
    }

    /**
     * Parse command-line arguments.
     * @param args Command-line arguments
     * @return A configured Importer or Exporter instance.
    **/
    public TransferProcess parse(final String[] args) {
        final Config config = parseConfiguration(args);
        if (config.isImport()) {
            return new Importer(config, clientBuilder());
        } else if (config.isExport()) {
            return new Exporter(config, clientBuilder());
        }
        throw new IllegalArgumentException("Invalid mode parameter");
    }

    private FcrepoClient.FcrepoClientBuilder clientBuilder() {
        return FcrepoClient.client();
    }

    private void printHelpWithoutHeaderMessage() {
        printHelp(null);
    }

    private void printHelp(final String message) {
        final HelpFormatter formatter = new HelpFormatter();
        final PrintWriter writer = new PrintWriter(System.out);
        if (message != null) {
            writer.println("\n-----------------------\n" + message + "\n-----------------------\n");
        }

        writer.println("Running Import/Export Utility from command line arguments");
        formatter.printHelp(writer, 80, "java -jar import-export-driver.jar", "", configOptions, 4, 4, "", true);

        writer.println("\n--- or ---\n");

        writer.println("Running Import/Export Utility from configuration file");
        formatter.printHelp(writer, 80, "java -jar import-export-driver.jar", "", configFileOptions, 4, 4, "",
                true);

        writer.println("\n");
        writer.flush();

        throw new RuntimeException(message);
    }

}