package com.amazonaws.services.dynamodbv2.json.demo.mars; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Properties; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; /** * Processes command line arguments and loads properties file containing configuration. * <p/> * The configuration file can be loaded using one of the following methods: * <ul> * <li>Default location: {@value #DEFAULT_CONFIG_FILE_LOCATION}</li> * <li>Filepath: specify using CLI option {@value #OPTION_FILE_SHORT}</li> * <li>Classpath: specify using CLI option {@value #OPTION_CLASSPATH_SHORT}</li> * </ul> */ public class ImageIngesterCLI { /** * Classpath separator for the OS. */ public static final String CLASSPATH_SEPARATOR = System.getProperty("path.separator"); /** * Classpath. */ public static final String CLASSPATH = System.getProperty("java.class.path"); /** * File encoding. */ public static final String FILE_ENCODING = "UTF-8"; // Constants for CLI /** * Application name for displaying help. */ private static final String APP_NAME = "DynamoDB Mars Image Ingester"; /** * Options for CLI. */ private static final Options OPTIONS; /** * Help option description. */ private static final String OPTION_HELP_DESC = "Shows help"; /** * Help short option. */ private static final String OPTION_HELP_SHORT = "h"; /** * Help long option. */ private static final String OPTION_HELP_LONG = "help"; /** * Help has no argument. */ private static final boolean OPTION_HELP_HASARG = false; /** * Help is not a required flag. */ private static final boolean OPTION_HELP_REQ = false; /** * Configuration filepath description. */ private static final String OPTION_FILE_DESC = "Load configuration file from filepath"; /** * Configuration filepath short option. */ private static final String OPTION_FILE_SHORT = "f"; /** * Configuration filepath long option. */ private static final String OPTION_FILE_LONG = "filepath-configuration"; /** * Configuration filepath has an argument. */ private static final boolean OPTION_FILE_HASARG = true; /** * Configuration file is not a required option. */ private static final boolean OPTION_FILE_REQ = false; /** * Configuration file name in classpath description. */ private static final String OPTION_CLASSPATH_DESC = "Load configuration file by name from classpath"; /** * Configuration file name in classpath short option. */ private static final String OPTION_CLASSPATH_SHORT = "n"; /** * Configuration file name in classpath long option. */ private static final String OPTION_CLASSPATH_LONG = "name-from-classpath"; /** * Configuration file name in classpath has an argument. */ private static final boolean OPTION_CLASSPATH_HASARG = true; /** * Configuration file name in classpath is not a required option. */ private static final boolean OPTION_CLASSPATH_REQ = false; /** * Default configuration file name. */ public static final String DEFAULT_CONFIG_FILE_NAME = "ImageIngester.properties"; static { OPTIONS = new Options(); final Option helpOpt = new Option(OPTION_HELP_SHORT, OPTION_HELP_LONG, OPTION_HELP_HASARG, OPTION_HELP_DESC); helpOpt.setRequired(OPTION_HELP_REQ); OPTIONS.addOption(helpOpt); final Option configOpt = new Option(OPTION_FILE_SHORT, OPTION_FILE_LONG, OPTION_FILE_HASARG, OPTION_FILE_DESC); configOpt.setRequired(OPTION_FILE_REQ); OPTIONS.addOption(configOpt); final Option classpathOpt = new Option(OPTION_CLASSPATH_SHORT, OPTION_CLASSPATH_LONG, OPTION_CLASSPATH_HASARG, OPTION_CLASSPATH_DESC); classpathOpt.setRequired(OPTION_CLASSPATH_REQ); OPTIONS.addOption(classpathOpt); } // State /** * CLI arguments. */ private final String[] args; /** * Loaded configuration. */ private Properties config = null; /** * Constructs a {@link ImageIngesterCLI} for parsing the supplied CLI arguments. * * @param args * CLI arguments */ public ImageIngesterCLI(final String[] args) { this.args = args.clone(); } /** * Called by main program to get the configuration. * * @return Properties * @throws ExitException * Help option specified, syntax error, or error loading configuration file */ public Properties getConfig() throws ExitException { parse(); return config; } /** * Prints help message when help option {@value #OPTION_HELP_SHORT} or {@value #OPTION_HELP_LONG} is specified or * illegal syntax is used. */ private void help() { final HelpFormatter helpFormatter = new HelpFormatter(); helpFormatter.printHelp(APP_NAME, OPTIONS); throw new HelpException(); } /** * Loads the configuration file by name from the classpath. * * @param file * The name of the file * @return Configuration properties * @throws ExitException * Error loading configuration file from classpath */ private Properties loadConfigFromClasspath(final String file) throws ExitException { final Properties p = new Properties(); InputStreamReader reader = null; try { reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(file), Charset.forName(FILE_ENCODING)); p.load(reader); } catch (final IOException | NullPointerException e) { throw new ExitException("Could not load configuration file from classpath. File= " + file + ", Classpath=" + CLASSPATH, e); } finally { if (reader != null) { try { reader.close(); } catch (final IOException e) { throw new ExitException("Could not close configuration classpath"); } } } return p; } /** * Loads the configuration file from a filepath. * * @param filepath * The path to the file * @return Configuration properties * @throws ExitException * Error loading configuration file from filepath */ private Properties loadConfigFromFilepath(final String filepath) throws ExitException { final Properties p = new Properties(); InputStreamReader reader = null; try { reader = new InputStreamReader(new FileInputStream(new File(filepath)), Charset.forName(FILE_ENCODING)); p.load(reader); } catch (final IOException e) { throw new ExitException("Could not load configuration file from filepath", e); } finally { if (reader != null) { try { reader.close(); } catch (final IOException e) { throw new ExitException("Could not close configuration file"); } } } return p; } /** * Parses command line arguments to load the configuration file. * * @throws ExitException * Help option specified, syntax error, or error loading configuration file */ private void parse() throws ExitException { final BasicParser parser = new BasicParser(); CommandLine cl = null; try { cl = parser.parse(OPTIONS, args); } catch (final ParseException e) { help(); } if (cl.hasOption(OPTION_HELP_SHORT)) { help(); } if (cl.hasOption(OPTION_FILE_SHORT) && cl.hasOption(OPTION_CLASSPATH_SHORT)) { throw new ExitException("May not specify both of the command line options " + OPTION_FILE_SHORT + " and " + OPTION_CLASSPATH_SHORT); } if (cl.hasOption(OPTION_FILE_SHORT)) { final String filePath = cl.getOptionValue(OPTION_FILE_SHORT); config = loadConfigFromFilepath(filePath); } else { config = loadConfigFromClasspath(DEFAULT_CONFIG_FILE_NAME); } if (cl.hasOption(OPTION_CLASSPATH_SHORT)) { final String file = cl.getOptionValue(OPTION_CLASSPATH_SHORT); config = loadConfigFromClasspath(file); } } }