Print

Print


Author: [log in to unmask]
Date: Wed Jun 10 13:10:16 2015
New Revision: 3119

Log:
Cleanup database update interface; add full support for EPICS variables; add command line options; other misc changes.

Modified:
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileCrawler.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLogUpdater.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummary.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryUpdater.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/package-info.java

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EpicsLog.java	Wed Jun 10 13:10:16 2015
@@ -99,5 +99,5 @@
                 //System.out.println(name + " => added " + this.currentData.getValue(name) + "; total = " + value + "; mean = " + value / count);
             }
         }
-    }
+    }       
 }

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileCrawler.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileCrawler.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileCrawler.java	Wed Jun 10 13:10:16 2015
@@ -20,6 +20,7 @@
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.cli.PosixParser;
+import org.hps.conditions.database.DatabaseConditionsManager;
 import org.hps.record.evio.EvioEventProcessor;
 import org.lcsim.util.log.DefaultLogFormatter;
 import org.lcsim.util.log.LogUtil;
@@ -30,13 +31,6 @@
  *
  * @author Jeremy McCormick, SLAC
  */
-// TODO: need options for...
-// -database connections prop file
-// -writing Auger XML for crawl job (and don't actually execute job)
-// -writing out a summary EVIO file containing control events only (PRESTART, EPICS, scalars?, END)
-// -get supplementary information from run spreadsheet (including whether run was "JUNK" or not)
-// -allow running arbitrary EvioEventProcessor classes by giving fully qualified class names as args on command line
-//  e.g. -E org.hps.derp.MyEvioEventProcessor
 public final class EvioFileCrawler {
 
     /**
@@ -63,15 +57,16 @@
         OPTIONS.addOption("a", "accept-runs", true, "list of run numbers to accept (others will be excluded)");
         OPTIONS.addOption("b", "begin-date", true, "min date for files (example 2015-03-26 11:28:59)");
         OPTIONS.addOption("c", "cache-files", false, "automatically cache files from MSS (JLAB only)");
+        OPTIONS.addOption("C", "db-config", true, "database connection properties file (required)");
         OPTIONS.addOption("d", "directory", true, "root directory to start crawling (default is current dir)");
-        OPTIONS.addOption("e", "epics", false, "process EPICS data found in EVIO files");
-        OPTIONS.addOption("E", "evio-processor", true, "class name of an additional EVIO processor to execute");
+        OPTIONS.addOption("e", "epics", false, "process EPICS data found in EVIO files (run log is also updated if -r is used)");
+        OPTIONS.addOption("E", "evio-processor", true, "class name of an additional EVIO processor to execute (can be used multiple times)");
         OPTIONS.addOption("h", "help", false, "print help and exit");
         OPTIONS.addOption("m", "max-files", true, "max number of files to process per run (only for debugging)");
         OPTIONS.addOption("p", "print", true, "set event print interval during EVIO processing");
         OPTIONS.addOption("r", "insert-run-log", false, "update the run database (not done by default)");
         OPTIONS.addOption("t", "timestamp-file", true, "existing or new timestamp file name for date cut off");
-        OPTIONS.addOption("s", "print-summary", false, "print run summary at the end of the job");
+        OPTIONS.addOption("s", "print-summary", false, "print the run summaries at the end of the job");
         OPTIONS.addOption("w", "max-cache-wait", true, "total seconds to allow for file caching");
         OPTIONS.addOption("L", "log-level", true, "set the log level (INFO, FINE, etc.)");
         OPTIONS.addOption("u", "update-run-log", false, "allow overriding existing data in the run db (not allowed by default)");
@@ -108,7 +103,7 @@
     /**
      * Flag indicating whether EPICS data banks should be processed.
      */
-    private boolean epics = false;
+    private boolean processEpicsData = false;
 
     /**
      * Interval for printing out event number while running EVIO processors.
@@ -116,7 +111,7 @@
     private int eventPrintInterval = DEFAULT_EVENT_PRINT_INTERVAL;
 
     /**
-     * The maximum number of files to
+     * The maximum number of files to accept (just used for debugging purposes).
      */
     private int maxFiles = -1;
 
@@ -172,7 +167,7 @@
      */
     private RunProcessor createRunProcessor(final RunSummary runSummary) {
         final RunProcessor processor = new RunProcessor(runSummary, this.cacheManager);
-        if (this.epics) {
+        if (this.processEpicsData) {
             processor.addProcessor(new EpicsLog(runSummary));
         }
         if (this.printSummary) {
@@ -204,6 +199,18 @@
                 final Level level = Level.parse(cl.getOptionValue("L"));
                 LOGGER.info("setting log level to " + level);
                 LOGGER.setLevel(level);
+            }
+            
+            if (cl.hasOption("C")) {
+                String dbPropPath = cl.getOptionValue("C");
+                File dbPropFile = new File(dbPropPath);
+                if (!dbPropFile.exists()) {
+                    throw new IllegalArgumentException("Connection properties file " + dbPropFile.getPath() + " does not exist.");
+                }
+                LOGGER.config("using " + dbPropPath + " for db connection properties");
+                DatabaseConditionsManager.getInstance().setConnectionProperties(dbPropFile);
+            } else {
+                throw new RuntimeException("The -C switch providing the database connection properties file is a required argument.");
             }
 
             if (cl.hasOption("d")) {
@@ -256,7 +263,7 @@
             }
 
             if (cl.hasOption("e")) {                
-                this.epics = true;
+                this.processEpicsData = true;
                 LOGGER.config("EPICS processing enabled");
             }
 
@@ -412,9 +419,21 @@
 
         // Insert run information into the database.
         if (this.updateRunLog) {
-            // Update run log.
-            new RunLogUpdater(runs, allowUpdates).insert();
-        }
+
+            // Create and configure RunLogUpdater which updates the run log for all runs found in crawl.
+            RunLogUpdater runUpdater = new RunLogUpdater(runs, allowUpdates);
+            
+            if (!this.processEpicsData) {
+                // Disable inserting EPICS data if it was not processed.
+                runUpdater.setInsertEpicsData(false);
+            }
+            
+            // Update the db.
+            runUpdater.insert();
+            
+            // Close the db connection.
+            runUpdater.close();
+        }               
 
         // Update the timestamp file which can be used to tell which files have been processed.
         if (this.timestampFile == null) {
@@ -430,7 +449,14 @@
         LOGGER.info("set modified on timestamp file: " + new Date(this.timestampFile.lastModified()));
     }
     
-    EvioEventProcessor createEvioEventProcessor(String className) throws Exception {
+    /**
+     * Create an {@link org.hps.record.evio.EvioEventProcessor} by its class name.
+     * 
+     * @param className the fully qualified name of the class
+     * @return the new object
+     * @throws Exception if there is a problem instantiating the class (does not exist; access exception etc.)
+     */
+    private EvioEventProcessor createEvioEventProcessor(String className) throws Exception {
         return EvioEventProcessor.class.cast(Class.forName(className).newInstance());
     }
 }

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileSequenceComparator.java	Wed Jun 10 13:10:16 2015
@@ -13,7 +13,7 @@
     /**
      * Compare two EVIO files by their sequence numbers.
      *
-     * @return -1 if the first file's sequence number is less than the second's; 0 if equal; 1 if greater than
+     * @return -1, 0, or 1 if the first file's sequence number is less than, equal to, or greater than the second's
      */
     @Override
     public int compare(final File o1, final File o2) {

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLog.java	Wed Jun 10 13:10:16 2015
@@ -12,9 +12,7 @@
 
 /**
  * This class contains summary information about a series of runs that are themselves modeled with the {@link RunSummary} class. These can be looked
- * up by their run number.
- * <p>
- * This class is able to update the run database using the <code>insert</code> methods.
+ * up by their run number {@link #getRunSummary(int)}.
  *
  * @author Jeremy McCormick, SLAC
  */

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLogUpdater.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLogUpdater.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunLogUpdater.java	Wed Jun 10 13:10:16 2015
@@ -9,7 +9,9 @@
 import org.lcsim.util.log.LogUtil;
 
 /**
- * Updates the run database with run log information from crawler job.
+ * Updates the run database with information from the crawler job.
+ * <p>
+ * The {@link RunSummaryUpdater} is used to insert rows for each run.
  * 
  * @author Jeremy McCormick, SLAC
  */
@@ -20,12 +22,32 @@
      */
     private static final Logger LOGGER = LogUtil.create(RunLogUpdater.class);
     
+    /**
+     * The run log with information for all runs.
+     */
     private RunLog runLog;
     
+    /**
+     * The database connection.
+     */
     private final Connection connection;
     
+    /**
+     * <code>true</code> if updates should be allowed in database or only inserts.
+     */
     private boolean allowUpdates = false;
-        
+    
+    /**
+     * <code>true</code> if EPICS data should be put into the database (skipped if not).
+     */
+    private boolean insertEpicsData = true;
+
+    /**
+     * Create a new updater.
+     * 
+     * @param runLog the run information
+     * @param allowUpdates <code>true</code> if updates should be allowed
+     */
     RunLogUpdater(RunLog runLog, boolean allowUpdates) {
         this.runLog = runLog;
         
@@ -34,6 +56,18 @@
         connection = cp.createConnection();
     }
     
+    /**
+     * Set to <code>true</code> if EPICS data should be inserted.
+     * 
+     * @param insertEpicsData <code>true</code> 
+     */
+    void setInsertEpicsData(boolean insertEpicsData) {
+        this.insertEpicsData = insertEpicsData;
+    }
+    
+    /**
+     * Close the database connection.
+     */
     void close() {
         try {
             connection.close();
@@ -43,62 +77,87 @@
     }
     
     /**
-     * Insert the run summary information into the database.
+     * Insert the run summary information into the database, including updating the run_log_files
+     * and run_log_epics tables.
      *
      * @param connection the database connection
      * @throws SQLException if there is an error querying the database
      */
     void insert() throws SQLException {
         
-        LOGGER.info("inserting runs into run_log ...");
+        LOGGER.info("inserting run data into database ...");
         try {
             connection.setAutoCommit(false);
-
-            // Update or insert a row for every run found.
+            
+            // Loop over all runs found while crawling.
             for (final Integer run : runLog.getSortedRunNumbers()) {
                 
+                LOGGER.info("beginning transaction for run " + run);
+                
+                // Get the RunSummary data for the run.
                 RunSummary runSummary = runLog.getRunSummary(run);
                 
                 LOGGER.info("updating " + runSummary);
                                 
-                RunSummaryUpdater updater = new RunSummaryUpdater(connection, runSummary);      
+                // Create the db updater for the RunSummary.
+                RunSummaryUpdater runUpdater = new RunSummaryUpdater(connection, runSummary);      
                                 
-                // Does a row already exist for run?
-                if (updater.runLogExists()) {
+                // Does a row already exist for the run?
+                if (runUpdater.runExists()) {
                     LOGGER.info("record for " + run + " exists already");
                     // Are updates allowed?
                     if (allowUpdates) {
-                        LOGGER.info("updating existing row in run_log for " + run);
+                        LOGGER.info("existing row for " + run + " will be updated");
                         // Update existing row.
-                        updater.updateRunLog();
+                        runUpdater.updateRun();
                     } else {
                         // Row exists and updates not allowed which is an error.
-                        throw new RuntimeException("Row already exists for run " + run + " and allowUpdates is false");
+                        throw new RuntimeException("Row already exists for run " + run + " and allowUpdates is false.");
                     }
                 } else {                
-                    
-                    LOGGER.info("inserting new row in run_log for " + run);
+                                        
+                    LOGGER.info("inserting new row in runs for run " + run + " ...");
                     
                     // Insert new record into run_log.
-                    updater.insertRunLog();
-                }
-
-                boolean fileLogExists = updater.fileLogExists();
-                
-                // Are updates disallowed and file log exists?
-                if (!allowUpdates && fileLogExists) {
-                    // File records exist but updates not allowed so this is an error.
-                    throw new RuntimeException("Cannot delete existing file records because allowUpdates is false");                    
+                    runUpdater.insertRun();
                 }
                 
-                // Delete existing file log.
-                if (fileLogExists) {
-                    // Delete the file log.
-                    updater.deleteFileLog();
+                // Do records exist in the run_log_files table?
+                if (runUpdater.filesExist()) {
+                    // Are updates disallowed?
+                    if (!allowUpdates) { 
+                        // File records exist for the run but updating is allowed so throw an exception.
+                        throw new RuntimeException("Cannot delete existing records in run_log_files because allowUpdates is false.");
+                    } else {
+                        // Delete the file log.
+                        runUpdater.deleteFiles();
+                    }
+                }
+                                
+                // Insert records into run_log_files now that existing records were deleted, if necessary.
+                runUpdater.insertFiles();
+                
+                // Is EPICS data processing enabled?
+                if (insertEpicsData) {
+                    // Does the EPICS data already exist?
+                    if (runUpdater.epicsExists()) {
+                        // Is replacing data disallowed?
+                        if (!allowUpdates) {
+                            // EPICS data exists but updating is not allowed so throw exception.
+                            throw new RuntimeException("EPICS run log already exists and allowUpdates is false.");
+                        } else {
+                            // Delete existing EPICS data.
+                            runUpdater.deleteEpics();
+                        }
+                    }
+                    
+                    // Insert EPICS data processed in the job for this run.
+                    runUpdater.insertEpics();
                 }
                 
-                // Insert the file log.
-                updater.insertFileLog();
+                // Commit the transactions for this run.
+                LOGGER.info("committing transaction for run " + run);
+                connection.commit();
             }
             
         } catch (final SQLException e) {
@@ -106,15 +165,17 @@
             try {
                 connection.rollback();
             } catch (final SQLException e2) {
+                LOGGER.log(Level.SEVERE, "error rolling back transaction", e2);
                 throw new RuntimeException(e);
             }
         } finally {
             try {
-                connection.setAutoCommit(true);
                 connection.close();
             } catch (final SQLException e) {
                 throw new RuntimeException(e);
             }
         }
+        
+        LOGGER.info("done inserting run data");
     }             
 }

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunProcessor.java	Wed Jun 10 13:10:16 2015
@@ -17,12 +17,11 @@
 import org.lcsim.util.log.LogUtil;
 
 /**
- * Processes all the EVIO files from a run.
+ * Processes all the EVIO files from a run in order to extract various information including start and end dates.
  * <p>
- * This class is a wrapper for activating different sub-tasks, including optionally caching all files from the JLAB MSS to the cache disk using
- * jcache.
+ * This class is a wrapper for activating different sub-tasks, including optionally caching all files from the JLAB MSS to the cache disk.
  * <p>
- * There is also a list of processors which is run on all events from the run, if the processor list is not empty.
+ * There is also a list of processors which is run on all events from the run.
  *
  * @author Jeremy McCormick, SLAC
  */
@@ -106,6 +105,14 @@
         LOGGER.info("done caching files from run " + this.runSummary.getRun());
     }
 
+    /**
+     * Get the event count from the current <code>EvioReader</code>.
+     * 
+     * @param reader the current <code>EvioReader</code>
+     * @return the event count
+     * @throws IOException if there is a generic IO error
+     * @throws EvioException if there is an EVIO related error
+     */
     Integer computeEventCount(final EvioReader reader) throws IOException, EvioException {
         return reader.getEventCount();
     }
@@ -136,7 +143,8 @@
     }
 
     /**
-     * Return <code>true</code> if valid END event can be located. 
+     * Return <code>true</code> if a valid CODA <i>END</i> event can be located in the
+     * <code>EvioReader</code>'s current file. 
      * 
      * @param reader the EVIO reader
      * @return <code>true</code> if valid END event is located
@@ -144,8 +152,13 @@
      */
     boolean isEndOkay(final EvioReader reader) throws Exception {
         LOGGER.info("checking is END okay ...");
+        
         boolean endOkay = false;
+        
+        // Go to second to last event for searching.
         reader.gotoEventNumber(reader.getEventCount() - 2);
+        
+        // Look for END event.
         EvioEvent event = null;
         while ((event = reader.parseNextEvent()) != null) {
             if (event.getHeader().getTag() == EvioEventConstants.END_EVENT_TAG) {
@@ -157,7 +170,10 @@
     }
 
     /**
-     * Process the run.
+     * Process the run by executing the registered {@link org.hps.record.evio.EvioEventProcessor}s and 
+     * performing special tasks such as the extraction of start and end dates.
+     * <p>
+     * This method will also activate file caching, if enabled by the {@link #useFileCache} option.
      *
      * @throws Exception if there is an error processing a file
      */

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummary.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummary.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummary.java	Wed Jun 10 13:10:16 2015
@@ -190,9 +190,9 @@
         for (final Object key : this.eventTypeCounts.keySet()) {
             ps.println("  " + key + ": " + this.eventTypeCounts.get(key));
         }
-        ps.println("files" + this.files.size());
+        ps.println("files " + this.files.size());
         for (final File file : this.files) {
-            ps.println(file.getPath());
+            ps.println("  " + file.getPath());
         }
     }
 

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryUpdater.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryUpdater.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/RunSummaryUpdater.java	Wed Jun 10 13:10:16 2015
@@ -5,13 +5,14 @@
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.List;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.hps.record.epics.EpicsData;
 import org.lcsim.util.log.LogUtil;
 
 /**
- * Utilities for updating or insert a run summary row into the run_log table.
+ * Updates the run database tables with information from a single run.
  * 
  * @author Jeremy McCormick, SLAC
  */
@@ -36,7 +37,7 @@
      * The run number (read from the summary in the constructor for convenience).
      */
     private int run = -1;
-    
+        
     /**
      * Create a <code>RunSummaryUpdater</code> for the given <code>RunSummary</code>.
      * 
@@ -57,18 +58,18 @@
         
         this.run = this.runSummary.getRun();
     }
-    
+        
     /**
      * Execute a SQL update to modify an existing row in the database.
      * 
      * @throws SQLException if there is an error executing the SQL statement
      */
-    void updateRunLog() throws SQLException {
+    void updateRun() throws SQLException {
         
         PreparedStatement runLogStatement = null;
         runLogStatement = 
-                connection.prepareStatement("UPDATE run_log SET start_date = ?, end_date = ?, nevents = ?, nfiles = ?, end_ok = ? where run = ?");        
-        LOGGER.info("preparing to update run " + run + " in run_log ..");
+                connection.prepareStatement("UPDATE runs SET start_date = ?, end_date = ?, nevents = ?, nfiles = ?, end_ok = ? where run = ?");        
+        LOGGER.info("preparing to update run " + run + " in runs table ..");
         runLogStatement.setTimestamp(1, new java.sql.Timestamp(runSummary.getStartDate().getTime()));
         runLogStatement.setTimestamp(2, new java.sql.Timestamp(runSummary.getEndDate().getTime()));
         runLogStatement.setInt(3, runSummary.getTotalEvents());
@@ -76,20 +77,19 @@
         runLogStatement.setBoolean(5, runSummary.isEndOkay());
         runLogStatement.setInt(6, run);
         runLogStatement.executeUpdate();
-        connection.commit();
-        LOGGER.info("run " + run + " was updated in run_log");
-    }
-    
-    /**
-     * Insert a new row in the run_log table.
+        LOGGER.info("run " + run + " was updated");
+    }
+    
+    /**
+     * Insert a new row in the <i>runs</i> table.
      *
      * @param connection the database connection
      * @throws SQLException if there is an error querying the database
      */
-    void insertRunLog() throws SQLException {
+    void insertRun() throws SQLException {
         PreparedStatement statement = 
-                connection.prepareStatement("INSERT INTO run_log (run, start_date, end_date, nevents, nfiles, end_ok) VALUES(?, ?, ?, ?, ?, ?)");
-        LOGGER.info("preparing to insert run " + run + " into run_log ..");
+                connection.prepareStatement("INSERT INTO runs (run, start_date, end_date, nevents, nfiles, end_ok) VALUES(?, ?, ?, ?, ?, ?)");
+        LOGGER.info("preparing to insert run " + run + " into runs table ..");
         statement.setInt(1, run);
         statement.setTimestamp(2, new java.sql.Timestamp(runSummary.getStartDate().getTime()));
         statement.setTimestamp(3, new java.sql.Timestamp(runSummary.getEndDate().getTime()));
@@ -97,8 +97,7 @@
         statement.setInt(5, runSummary.getEvioFileList().size());
         statement.setBoolean(6, runSummary.isEndOkay());
         statement.executeUpdate();
-        connection.commit();
-        LOGGER.info("insert run " + run + " to run_log");
+        LOGGER.info("inserted run " + run + " to runs table");
     }
     
     /**
@@ -107,8 +106,8 @@
      * @return <code>true</code> if there is an existing row for this run summary.
      * @throws SQLException if there is an error executing the SQL query
      */
-    boolean runLogExists() throws SQLException {
-        PreparedStatement s = connection.prepareStatement("SELECT run from run_log where run = ?");
+    boolean runExists() throws SQLException {
+        PreparedStatement s = connection.prepareStatement("SELECT run FROM runs where run = ?");
         s.setInt(1, run);        
         ResultSet rs = s.executeQuery();
         return rs.first();
@@ -121,11 +120,11 @@
      * @param run the run number
      * @throws SQLException if there is a problem executing one of the database queries
      */
-    void insertFileLog() throws SQLException {
+    void insertFiles() throws SQLException {
         LOGGER.info("updating file list ...");
         PreparedStatement filesStatement = null;
-        filesStatement = connection.prepareStatement("INSERT INTO run_log_files (run, directory, name) VALUES(?, ?, ?)");
-        LOGGER.info("inserting files from run " + run + " into database");
+        filesStatement = connection.prepareStatement("INSERT INTO run_files (run, directory, name) VALUES(?, ?, ?)");
+        LOGGER.info("inserting files from run " + run + " into run_files ...");
         for (final File file : runSummary.getEvioFileList()) {
             LOGGER.info("creating update statement for " + file.getPath());
             filesStatement.setInt(1, run);
@@ -134,8 +133,7 @@
             LOGGER.info("executing statement: " + filesStatement);
             filesStatement.executeUpdate();
         }
-        connection.commit();
-        LOGGER.info("run_log_files was updated!");
+        LOGGER.info("run_files was updated");
     }    
     
     /**
@@ -144,27 +142,27 @@
      * @param files the list of files
      * @throws SQLException if there is an error executing the SQL query
      */
-    void deleteFileLog() throws SQLException {
-        LOGGER.info("deleting run_log_files for " + run + " ...");
-        PreparedStatement s = connection.prepareStatement("DELETE FROM run_log_files where run = ?");
+    void deleteFiles() throws SQLException {        
+        LOGGER.info("deleting rows from run_files for " + run + " ...");
+        PreparedStatement s = connection.prepareStatement("DELETE FROM run_files where run = ?");
         s.setInt(1, run);
         s.executeUpdate();
-        connection.commit();
-        LOGGER.info("done deleting run_log_files for " + run);
-    }
-    
-    /**
-     * Delete the row for this run from the run_log table.
-     * 
-     * @throws SQLException if there is an error executing the SQL query
-     */
-    void deleteRunLog() throws SQLException {
-        LOGGER.info("deleting run_log for " + run + " ...");
-        PreparedStatement s = connection.prepareStatement("DELETE FROM run_log where run = ?");
+        LOGGER.info("done deleting rows from run_files for " + run);
+    }
+    
+    /**
+     * Delete the row for this run from the <i>runs</i> table.
+     * <p>
+     * This doesn't delete the rows from <i>run_epics</i> or <i>run_files</i>.
+     * 
+     * @throws SQLException if there is an error executing the SQL query
+     */
+    void deleteRun() throws SQLException {
+        LOGGER.info("deleting record from runs for " + run + " ...");
+        PreparedStatement s = connection.prepareStatement("DELETE FROM runs where run = ?");
         s.setInt(1, run);
         s.executeUpdate();
-        connection.commit();
-        LOGGER.info("done deleting run_log_files for " + run);
+        LOGGER.info("deleted rows from runs for " + run);
     }
     
     /**
@@ -172,10 +170,54 @@
      * @return <code>true</code> if there are file rows for this run
      * @throws SQLException if there is an error executing the SQL query
      */
-    boolean fileLogExists() throws SQLException {
-        PreparedStatement s = connection.prepareStatement("SELECT run FROM run_log_files where run = ?");
+    boolean filesExist() throws SQLException {
+        PreparedStatement s = connection.prepareStatement("SELECT run FROM run_files where run = ?");
         s.setInt(1, run);        
         ResultSet rs = s.executeQuery();
         return rs.first();
     }
-}
+    
+    /**
+     * Insert EPICS data into the run_log_epics table.
+     * 
+     * @throws SQLException if there is an error performing the db query
+     */
+    void insertEpics() throws SQLException {
+        PreparedStatement statement = connection.prepareStatement("INSERT INTO run_epics (run, variable_name, value) values (?, ?, ?)");
+        EpicsData data = runSummary.getEpicsData();
+        if (data != null) {
+            for (String variableName : data.getUsedNames()) {
+                statement.setInt(1, this.run);
+                statement.setString(2, variableName);
+                statement.setDouble(3, data.getValue(variableName));
+                statement.executeUpdate();
+            }
+        } else {
+            LOGGER.warning("skipped inserting EPICS data (none found in RunSummary)");
+        }
+    }
+    
+    /**
+     * Delete existing EPICS data from the run_log_epics table.
+     * 
+     * @throws SQLException if there is an error performing the db query
+     */
+    void deleteEpics() throws SQLException {
+        PreparedStatement statement = connection.prepareStatement("DELETE FROM run_epics WHERE run = ?");
+        statement.setInt(1, this.run);
+        statement.executeUpdate();
+    }
+    
+    /**
+     * Return <code>true</code> if there is an existing row for this run summary.
+     * 
+     * @return <code>true</code> if there is an existing row for this run summary.
+     * @throws SQLException if there is an error executing the SQL query
+     */
+    boolean epicsExists() throws SQLException {
+        PreparedStatement s = connection.prepareStatement("SELECT run from run_epics where run = ?");
+        s.setInt(1, run);
+        ResultSet rs = s.executeQuery();
+        return rs.first();
+    }
+}

Modified: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/package-info.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/package-info.java	(original)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/package-info.java	Wed Jun 10 13:10:16 2015
@@ -1,5 +1,5 @@
 /**
- * Implements an EVIO file crawler for extracting run and configuration information, including run start and end dates, event counts, etc.
+ * Implements an EVIO file crawler to extract run and configuration information, including run start and end dates, event counts, etc.
  *
  * @author Jeremy McCormick, SLAC
  */