package hu.sztaki.ilab.longneck.cli; import hu.sztaki.ilab.longneck.bootstrap.Bootstrap; import hu.sztaki.ilab.longneck.bootstrap.PropertyUtils; import hu.sztaki.ilab.longneck.util.OsType; import hu.sztaki.ilab.longneck.util.OsUtils; import hu.sztaki.ilab.longneck.util.UtilityRunner; import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.cli.*; import org.apache.log4j.*; import org.apache.log4j.xml.DOMConfigurator; import org.springframework.context.ApplicationContext; /** * CLI runner for longneck. * * @author Molnár Péter <molnar.peter@sztaki.mta.hu> */ public class CliRunner { /** The logger. */ private static final Logger LOG = Logger.getLogger(CliRunner.class); public static void main(String[] args) { CliRunner cli = new CliRunner(); cli.execute(args); } public void execute(String[] args) { configureLogging(); Properties runtimeProperties = new Properties(); Options options = getDefaultOptions(); // Load configuration defaults from jars runtimeProperties.putAll(PropertyUtils.readDefaultProperties()); // Config file blacklist Set<String> blacklist = new HashSet( Arrays.asList(new String[] {"log4j.properties" })); // Read configuration files from project config/ runtimeProperties.putAll(PropertyUtils.readPropertyFiles(new File("config/"), blacklist)); // Read configuration files from per-user directory runtimeProperties.putAll(PropertyUtils.readPropertyFiles( new File(OsUtils.getHomeDirectoryPath(OsType.getCurrent())), blacklist)); try { // Parse command line Parser p = new GnuParser(); CommandLine cli = p.parse(options, args); // Check help option if (cli.hasOption('h')) { printHelp(options); System.exit(0); } // Copy command line parameters and set default testing behavior if (cli.hasOption('s')) { String testOptVal = cli.getOptionValue('s'); if (!testOptVal.equals("normal") && !testOptVal.equals("skip") && !testOptVal.equals("tolerant") && !testOptVal.equals("alone")) { System.err.println("Command line parameter failure!"); System.err .println(" --testingBehavior should be either of alone, normal, skip or tolerant!"); System.exit(1); } } for (Option o : cli.getOptions()) { if (cli.hasOption(o.getOpt())) { if (o.hasArg()) { // Add options defined on command line if ("define".equals(o.getLongOpt())) { addCommandLineProperty(runtimeProperties, o.getValue()); } else { runtimeProperties.setProperty(o.getLongOpt(), o.getValue()); } } else { runtimeProperties.setProperty(o.getLongOpt(), "true"); } } } // Create and initialize bootstrap Bootstrap bootstrap = new Bootstrap(runtimeProperties); if (!runtimeProperties.containsKey("executeUtility")) { bootstrap.run(); } else { ApplicationContext context = bootstrap.getApplicationContext(); UtilityRunner ur = (UtilityRunner) context.getBean(runtimeProperties .getProperty("executeUtility")); ur.run(runtimeProperties); } bootstrap.close(); } catch (ParseException ex) { LOG.error("Invalid command line specified.", ex); } catch (RuntimeException ex) { LOG.error("Error during execution.", getRootCause(ex)); } } public static Throwable getRootCause(Throwable e) { for (;;) { Throwable t = e.getCause(); if (t == null) { break; } e = t; } return e; } public static Options getDefaultOptions() { Options options = new Options(); // Add main options options.addOption("h", "help", false, "Prints this help screen."); options.addOption("p", "processFile", true, "Specifes the process file URL."); options.addOption("t", "workerThreadsNum", true, "Number of worker threads on which the process is running. Default: 1"); options.addOption("T", "truncateBeforeWrite", false, "Truncate the target datastore before processing records."); options.addOption("E", "errorTruncateBeforeWrite", false, "Truncate the error datastore before processing records."); options.addOption("m", "measureTimeEnabled", false, "Enables time management on threads."); options.addOption("X", "executeUtility", true, "Execute built-in utility <name> instead of running a process."); options.addOption("D", "define", true, "Define runtime property <name>."); options.addOption("l", "maxErrorEventLevel", true, "The maximum level of errors written by the error writer."); options.addOption("s", "testingBehavior", true, "Define how to handle test cases: normal (default), alone, skip, tolerant"); options.addOption("v", "verbose", false, "Verbose testing"); return options; } public static void configureLogging() { // If available, load log configuration from file File logXml = new File("config/log4j.xml"); if (logXml.exists() && logXml.canRead()) { DOMConfigurator.configure("config/log4j.xml"); return; } File logConf = new File("config/log4j.properties"); if (logConf.exists() && logConf.canRead()) { PropertyConfigurator.configure("config/log4j.properties"); return; } // configure a basic logger by hand Logger rootLogger = Logger.getRootLogger(); rootLogger.removeAllAppenders(); rootLogger.setLevel(Level.INFO); Appender consoleAppender = new ConsoleAppender(new PatternLayout( "%-5p [%C line %L] [%t]: %m%n%throwable")); rootLogger.addAppender(consoleAppender); } public static void printHelp(Options options) { HelpFormatter hf = new HelpFormatter(); hf.printHelp("longneck-app <OPTIONS>", "\nLongneck data transformation.\n\n", options, "\nmore info: http://longneck.sztaki.hu/\n\n"); } public static Map<String, String> parseAdditionalParameters(String[] args) { Pattern pattern = Pattern.compile("^-D([\\w\\.]+)=(.+)$"); Map<String, String> params = new HashMap<String, String>(); for (String arg : args) { Matcher m = pattern.matcher(arg); if (m.matches()) { params.put(m.group(1), m.group(2)); } } return params; } public static void addCommandLineProperty(Properties runtimeProperties, String option) { if (option == null || "".equals(option)) { throw new IllegalArgumentException("Property name must not be null."); } if (!option.contains("=")) { runtimeProperties.setProperty(option, "true"); } else { String[] parts = option.split("=", 2); if (parts[0] == null || "".equals(parts[0])) { throw new IllegalArgumentException( "Property name before = must not be null."); } runtimeProperties.setProperty(parts[0], parts[1]); } } }