Print

Print


Author: [log in to unmask]
Date: Wed Oct 29 18:59:03 2014
New Revision: 1335

Log:
Add first version of command line tool for importing conditions from text files HPSJAVA-280

Added:
    java/trunk/conditions/src/main/java/org/hps/conditions/cli/
    java/trunk/conditions/src/main/java/org/hps/conditions/cli/AbstractCommand.java   (with props)
    java/trunk/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java   (with props)
    java/trunk/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java   (with props)
Modified:
    java/trunk/conditions/src/main/java/org/hps/conditions/QueryBuilder.java

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/QueryBuilder.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/QueryBuilder.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/QueryBuilder.java	Wed Oct 29 18:59:03 2014
@@ -1,16 +1,18 @@
 package org.hps.conditions;
+
+import java.util.List;
 
 /**
  * This is a static utility class for building SQL queries for the conditions system.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
-final class QueryBuilder {
+public final class QueryBuilder {
 
     private QueryBuilder() {
     }
 
-    static String buildSelect(String tableName, int collectionId, String[] fields, String order) {
+    public static String buildSelect(String tableName, int collectionId, String[] fields, String order) {
         StringBuffer buff = new StringBuffer();
         buff.append("SELECT ");
         if (fields == null) {
@@ -32,7 +34,7 @@
         return buff.toString();
     }
 
-    static String buildUpdate(String tableName, int rowId, String[] fields, Object[] values) {
+    public static String buildUpdate(String tableName, int rowId, String[] fields, Object[] values) {
         if (fields.length != values.length)
             throw new IllegalArgumentException("The field and value arrays are different lengths.");
         StringBuffer buff = new StringBuffer();
@@ -45,7 +47,7 @@
         return buff.toString();
     }
 
-    static String buildInsert(String tableName, int collectionId, String[] fields, Object[] values) {
+    public static String buildInsert(String tableName, int collectionId, String[] fields, Object[] values) {
         if (fields.length != values.length)
             throw new IllegalArgumentException("The field and value arrays are different lengths.");
         StringBuffer buff = new StringBuffer();
@@ -60,12 +62,31 @@
         buff.append(") ");
         return buff.toString();
     }
+    
+    public static String buildInsert(String tableName, int collectionID, List<String> columnNames, List<List<String>> rows) {
+        StringBuffer buff = new StringBuffer();
+        buff.append("INSERT INTO " + tableName + " ( collection_id");
+        for (String column : columnNames) {
+            buff.append(", " + column);
+        }
+        buff.append(" ) VALUES ");
+        for (List<String> values : rows) {
+            buff.append("( ");
+            buff.append(collectionID);
+            for (String value : values) {
+                buff.append(", '" + value + "'");
+            }
+            buff.append("), ");
+        }        
+        buff.setLength(buff.length() - 2);
+        return buff.toString();
+    }
+    
 
-    static String buildDelete(String tableName, int rowId) {
+    public static String buildDelete(String tableName, int rowId) {
         if (rowId <= 0)
             throw new IllegalArgumentException("Invalid row ID: " + rowId);
         String query = "DELETE FROM " + tableName + " WHERE id = " + rowId;
         return query;
-    }
-
+    }        
 }

Added: java/trunk/conditions/src/main/java/org/hps/conditions/cli/AbstractCommand.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/cli/AbstractCommand.java	(added)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/cli/AbstractCommand.java	Wed Oct 29 18:59:03 2014
@@ -0,0 +1,78 @@
+package org.hps.conditions.cli;
+
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.Parser;
+import org.apache.commons.cli.PosixParser;
+
+/**
+ * This is the API that sub-commands must implement in the conditions command line interface.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public abstract class AbstractCommand {
+
+    String name;
+    String description;
+    Options options = new Options();
+    Parser parser = new PosixParser();
+    boolean verbose = false;
+
+    /**
+     * Class constructor.     
+     * @param name The string that invokes this command.
+     * @param description The description of this command.
+     */
+    AbstractCommand(String name, String description) {
+        this.name = name;
+        this.description = description;
+    }
+    
+    /**
+     * Get the name of this command.     
+     * @return A String of the name of this command.
+     */
+    String getName() {
+        return this.name;
+    }
+
+    /**
+     * Get the description of this command.    
+     * @return A String of the description of this command.
+     */
+    String getDescription() {
+        return this.description;
+    }
+
+    /**
+     * Options for this command.     
+     * @return Options object for this command.
+     */
+    Options getOptions() {
+        return options;
+    }
+
+    /**
+     * Print the usage of this sub-command.
+     * @param doExit Whether or not to exit after printing usage.
+     */
+    void printUsage() {
+        HelpFormatter help = new HelpFormatter();
+        help.printHelp(getName(), getOptions());
+    }
+    
+    /**
+     * Set whether verbose output is enabled.
+     * @param verbose True to enable verbose output.
+     */
+    void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Execute the command with the arguments.  
+     * This is the only method that sub-classes must implement. 
+     * @param arguments The sub-command's arguments.
+     */
+    abstract void execute(String[] arguments);
+}

Added: java/trunk/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java	(added)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/cli/CommandLineTool.java	Wed Oct 29 18:59:03 2014
@@ -0,0 +1,147 @@
+package org.hps.conditions.cli;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+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;
+import org.apache.commons.cli.PosixParser;
+import org.hps.conditions.DatabaseConditionsManager;
+
+/**
+ * <p>
+ * This class is a command-line tool for performing commands on the conditions database.
+ * It has sub-commands much like the cvs or svn clients.  The only current implemented
+ * command is 'load' to import text files, but more will be added.
+ * <p>
+ * Command line options allow a custom connection properties file or XML configuration
+ * to be supplied by the user which will override the default.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class CommandLineTool {
+    
+    Options options = new Options();
+    Map<String, AbstractCommand> commands = new HashMap<String, AbstractCommand>();
+    PosixParser parser = new PosixParser();
+    static final String CONNECTION_PROPERTIES_RESOURCE = "/org/hps/conditions/config/conditions_dev.properties";
+    static final String XML_CONFIG_RESOURCE = "/org/hps/conditions/config/conditions_dev.xml";
+    DatabaseConditionsManager conditionsManager;
+    boolean verbose = false;
+    
+    public static void main(String[] arguments) {        
+        CommandLineTool.create().run(arguments);
+    }
+    
+    void run(String[] arguments) {        
+        try {
+            if (arguments.length == 0) {
+                printUsage();
+                exit(0);
+            }
+
+            CommandLine commandLine = null;
+            try {
+                commandLine = parser.parse(options, arguments, true);
+            } catch (ParseException e) {
+                e.printStackTrace();
+                printUsage();
+                exit(1);
+            }
+
+            if (commandLine.hasOption("h") || commandLine.getArgs().length == 0) {
+                printUsage();
+                exit(0);
+            }
+            
+            if (commandLine.hasOption("v")) {
+                verbose = true;
+            }
+            
+            setupConditionsManager(commandLine);
+
+            String commandName = commandLine.getArgs()[0];
+
+            AbstractCommand command = commands.get(commandName);
+            if (command == null) {
+                throw new IllegalArgumentException("Unknown command " + commandName);
+            }
+
+            String[] commandArguments = new String[commandLine.getArgs().length - 1];
+            System.arraycopy(commandLine.getArgs(), 1, commandArguments, 0, commandArguments.length);
+
+            command.setVerbose(verbose);
+            command.execute(commandArguments);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            conditionsManager.closeConnection();
+        }
+    }
+
+    private void setupConditionsManager(CommandLine commandLine) {
+        conditionsManager = new DatabaseConditionsManager();
+        if (verbose)
+            conditionsManager.setLogLevel(Level.ALL);
+        else
+            conditionsManager.setLogLevel(Level.WARNING);
+        if (commandLine.hasOption("p")) {
+            File connectionPropertiesFile = new File(commandLine.getOptionValue("p"));
+            if (verbose)
+                System.out.println("using connection properties file " + connectionPropertiesFile.getPath());
+            conditionsManager.setConnectionProperties(connectionPropertiesFile);
+        } else {
+            if (verbose)
+                System.out.println("using connection resource " + CONNECTION_PROPERTIES_RESOURCE);
+            conditionsManager.setConnectionResource(CONNECTION_PROPERTIES_RESOURCE);
+        }            
+        if (commandLine.hasOption("x")) {
+            File xmlConfigFile = new File(commandLine.getOptionValue("x"));
+            conditionsManager.configure(xmlConfigFile);
+            if (verbose)
+                System.out.println("using XML config file " + xmlConfigFile.getPath());
+        } else {
+            if (verbose)
+                System.out.println("using XML config resource " + XML_CONFIG_RESOURCE);
+            conditionsManager.configure(XML_CONFIG_RESOURCE);
+        }            
+        conditionsManager.register();
+        conditionsManager.openConnection();
+    }
+    
+    void printUsage() {
+        HelpFormatter help = new HelpFormatter();
+        StringBuffer s = new StringBuffer();
+        for (String command : commands.keySet()) {
+            s.append(command + '\n');
+        }
+        help.printHelp("CommandLineTool", "Commands:\n" + s.toString(), options, "");        
+    }
+    
+    void exit(int status) {
+        System.exit(status);
+    }
+    
+    void registerCommand(AbstractCommand command) {
+        if (commands.containsKey(command.getName())) {
+            throw new IllegalArgumentException("There is already a command called " + command.getName());
+        }
+        commands.put(command.getName(), command);
+    }
+    
+    static CommandLineTool create() {
+        CommandLineTool cli = new CommandLineTool();
+        cli.options.addOption(new Option("h", false, "Print help and exit"));
+        cli.options.addOption(new Option("v", false, "Enable verbose terminal output"));
+        cli.options.addOption(new Option("p", true, "Set the connection properties file"));
+        cli.options.addOption(new Option("x", true, "Set the conditions database XML configuration file"));
+        cli.registerCommand(new LoadCommand());
+        return cli;
+    }
+
+}

Added: java/trunk/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java	(added)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/cli/LoadCommand.java	Wed Oct 29 18:59:03 2014
@@ -0,0 +1,143 @@
+package org.hps.conditions.cli;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.ParseException;
+import org.hps.conditions.DatabaseConditionsManager;
+import org.hps.conditions.QueryBuilder;
+
+/**
+ * <p>
+ * This is a sub-command to add conditions data using an input text file.
+ * The file should be ASCII text that is tab or space delimited and includes headers with the names of 
+ * the database columns.  (These must match exactly!)  The user must supply a table name as the target 
+ * for the SQL insert.  An optional collection ID can be supplied, which may not exist already in the table.  
+ * Otherwise, the command will fail.  By default, the next collection ID will be found by the conditions manager.
+ * <p>
+ * <pre>
+ * java -cp hps-distribution-bin.jar org.hps.conditions.cli.CommandLineTool -p conditions_dev_local.properties \
+ *     load -t scratch_svt_gains -f ./scratch_svt_gains.txt -c 1
+ * </pre>
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class LoadCommand extends AbstractCommand {
+       
+    LoadCommand() {
+        super("load", "Load a set of conditions into the database from a text file");
+        this.options.addOption(new Option("t", true, "Set the name of the target table in the database"));
+        this.options.addOption(new Option("c", true, "Set the collection ID of this conditions set"));
+        this.options.addOption(new Option("f", true, "Set the input data file"));
+    }    
+    
+    @Override
+    public void execute(String[] arguments) {
+        CommandLine commandLine;        
+        try {
+            commandLine = parser.parse(options,  arguments);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+        
+        String fileName = commandLine.getOptionValue("f");
+        if (fileName == null) {
+            throw new IllegalArgumentException("Missing file argument.");
+        }
+        
+        String tableName = commandLine.getOptionValue("t");
+        if (tableName == null) {
+            throw new IllegalArgumentException("Missing table name.");
+        }
+        
+        DatabaseConditionsManager conditionsManager = DatabaseConditionsManager.getInstance();
+        if (conditionsManager == null) {
+            throw new RuntimeException("The DatabaseConditionsManager was not setup properly.");
+        }
+        
+        int collectionID;
+        if (commandLine.getOptionValue("c") != null) {
+            collectionID = Integer.parseInt(commandLine.getOptionValue("c"));
+            if (collectionExists(conditionsManager, tableName, collectionID)) {
+                throw new IllegalArgumentException("The user supplied collection ID " + collectionID + " already exists in this table.");
+            }            
+        } else {
+            collectionID = conditionsManager.getNextCollectionID(tableName); 
+        }
+                                
+        List<String> columnNames = new ArrayList<String>();
+        List<List<String>> rows = new ArrayList<List<String>>();        
+        parseFile(fileName, columnNames, rows);
+                        
+        String insertSql = QueryBuilder.buildInsert(tableName, collectionID, columnNames, rows);
+        if (verbose)
+            System.out.println(insertSql);
+        List<Integer> IDs = conditionsManager.updateQuery(insertSql);
+        System.out.println("Inserted " + IDs.size() + " new rows into table " + tableName + " with collection_id " + collectionID);
+    }
+    
+    void parseFile(String fileName, List<String> columnNames, List<List<String>> rows) {
+        File inputFile = new File(fileName);
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(inputFile));           
+            String headerLine = reader.readLine();
+            if (headerLine == null) {
+                throw new IllegalArgumentException("The file is empty.");
+            }
+            StringTokenizer tokenizer = new StringTokenizer(headerLine, " \t");
+            while (tokenizer.hasMoreTokens()) {
+                columnNames.add(tokenizer.nextToken().trim());
+            }
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                tokenizer = new StringTokenizer(line, " \t");
+                List<String> row = new ArrayList<String>();
+                while (tokenizer.hasMoreTokens()) {
+                    row.add(tokenizer.nextToken().trim());
+                }
+                rows.add(row);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }        
+    }
+    
+    boolean collectionExists(DatabaseConditionsManager conditionsManager, String tableName, int collectionID) {
+        String sql = "SELECT * FROM " + tableName + " where collection_id = " + collectionID;
+        ResultSet resultSet = conditionsManager.selectQuery(sql);
+        try {
+            resultSet.last();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+        int rowCount = 0;
+        try {
+            rowCount = resultSet.getRow();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+        if (rowCount != 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}