LISTSERV mailing list manager LISTSERV 16.5

Help for HPS-SVN Archives


HPS-SVN Archives

HPS-SVN Archives


HPS-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

HPS-SVN Home

HPS-SVN Home

HPS-SVN  June 2015

HPS-SVN June 2015

Subject:

r3171 - /java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/

From:

[log in to unmask]

Reply-To:

Notification of commits to the hps svn repository <[log in to unmask]>

Date:

Sat, 20 Jun 2015 02:03:48 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (1813 lines)

Author: [log in to unmask]
Date: Fri Jun 19 19:03:30 2015
New Revision: 3171

Log:
Updates to crawler including overhaul of configuration (now in a separate class); add support for inserting scaler data into db.

Added:
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java
      - copied, changed from r3169, 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/CrawlerConfig.java
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/ScalerDataUpdater.java
Removed:
    java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/EvioFileCrawler.java
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/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

Copied: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/Crawler.java (from r3169, 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/Crawler.java	Fri Jun 19 19:03:30 2015
@@ -5,12 +5,11 @@
 import java.nio.file.FileVisitOption;
 import java.nio.file.Files;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
+import java.sql.Connection;
+import java.sql.SQLException;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -20,8 +19,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.hps.conditions.database.ConnectionParameters;
 import org.lcsim.util.log.DefaultLogFormatter;
 import org.lcsim.util.log.LogUtil;
 
@@ -31,12 +29,12 @@
  *
  * @author Jeremy McCormick, SLAC
  */
-public final class EvioFileCrawler {
-
-    /**
-     * Setup logger.
-     */
-    private static final Logger LOGGER = LogUtil.create(EvioFileCrawler.class, new DefaultLogFormatter(), Level.ALL);
+public final class Crawler {
+
+    /**
+     * Setup the logger.
+     */
+    private static final Logger LOGGER = LogUtil.create(Crawler.class, new DefaultLogFormatter(), Level.ALL);
 
     /**
      * Constant for milliseconds conversion.
@@ -47,8 +45,6 @@
      * Command line options for the crawler.
      */
     private static final Options OPTIONS = new Options();
-    
-    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
     /**
      * Statically define the command options.
@@ -59,15 +55,13 @@
         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 (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("E", "evio-processor", true, "full class name of an EvioEventProcessor 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("m", "max-files", true, "max number of files to process per run (mostly for debugging)");
+        OPTIONS.addOption("p", "print", true, "set event printing 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 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("t", "timestamp-file", true, "existing or new timestamp file name");
+        OPTIONS.addOption("w", "max-cache-wait", true, "total time to allow for file caching (seconds)");
         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)");
     }
@@ -79,384 +73,298 @@
      */
     public static void main(final String[] args) {
         try {
-            new EvioFileCrawler().parse(args).run();
+            new Crawler().parse(args).run();
         } catch (final Exception e) {
             throw new RuntimeException(e);
         }
     }
 
     /**
-     * A list of run numbers to accept in the job.
-     */
-    private final Set<Integer> acceptRuns = new HashSet<Integer>();
-
-    /**
      * The class for managing the file caching using the 'jcache' command.
      */
     private final JCacheManager cacheManager = new JCacheManager();
 
     /**
-     * Default event print interval.
-     */
-    private final int DEFAULT_EVENT_PRINT_INTERVAL = 1000;
-
-    /**
-     * Flag indicating whether EPICS data banks should be processed.
-     */
-    private boolean processEpicsData = false;
-
-    /**
-     * Interval for printing out event number while running EVIO processors.
-     */
-    private int eventPrintInterval = DEFAULT_EVENT_PRINT_INTERVAL;
-
-    /**
-     * The maximum number of files to accept (just used for debugging purposes).
-     */
-    private int maxFiles = -1;
-
-    /**
      * The options parser.
      */
     private final PosixParser parser = new PosixParser();
-
-    /**
-     * Flag indicating whether the run summaries should be printed (may result in some extra file processing).
-     */
-    private boolean printSummary = false;
-
-    /**
-     * The root directory to crawl which defaults to the current directory.
-     */
-    private File rootDir = new File(System.getProperty("user.dir"));
-
-    /**
-     * A timestamp to use for filtering input files on their creation date.
-     */
-    private Date timestamp = null;
-
-    /**
-     * A file to use for the timestamp date.
-     */
-    private File timestampFile = null;
-
-    /**
-     * Flag indicating if the run database should be updated from results of the job.
-     */
-    private boolean updateRunLog = false;
-
-    /**
-     * Flag indicating if the file cache should be used (e.g. jcache automatically executed to move files to the cache disk from tape).
-     */
-    private boolean useFileCache = false;
-
-    /**
-     * The maximum wait time in milliseconds to allow for file caching operations.
-     */
-    private Long waitTime;
     
-    private boolean allowUpdates = false;
+    /**
+     * Configuration options from the command line.
+     */
+    private CrawlerConfig config;
     
-    private List<EvioEventProcessor> processors = new ArrayList<EvioEventProcessor>();
-
-    /**
-     * Create the processor for a single run.
-     *
-     * @param runSummary the run summary for the run
-     * @return the run processor
-     */
-    private RunProcessor createRunProcessor(final RunSummary runSummary) {
-        final RunProcessor processor = new RunProcessor(runSummary, this.cacheManager);
-        if (this.processEpicsData) {
-            processor.addProcessor(new EpicsLog(runSummary));
-        }
-        if (this.printSummary) {
-            processor.addProcessor(new EventTypeLog(runSummary));
-        }
-        if (this.maxFiles != -1) {
-            processor.setMaxFiles(this.maxFiles);
-        }
-        processor.useFileCache(this.useFileCache);
-        processor.setEventPrintInterval(this.eventPrintInterval);
-        return processor;
-    }
-
-    /**
-     * Parse command line options, but do not start the job.
+    /**
+     * Parse command line options into internal configuration object.
      *
      * @param args the command line arguments
      * @return the configured crawler object
      */
-    private EvioFileCrawler parse(final String args[]) {
+    private Crawler parse(final String args[]) {
+        
+        LOGGER.info("parsing command line options");
+        
+        config = new CrawlerConfig();
+        
         try {
             final CommandLine cl = this.parser.parse(OPTIONS, args);
 
+            // Print help.
             if (cl.hasOption("h")) {
                 this.printUsage();
             }
 
+            // Log level.
             if (cl.hasOption("L")) {
                 final Level level = Level.parse(cl.getOptionValue("L"));
                 LOGGER.info("setting log level to " + level);
                 LOGGER.setLevel(level);
             }
-            
+
+            // Database connection properties file (this is not optional).
             if (cl.hasOption("C")) {
-                String dbPropPath = cl.getOptionValue("C");
-                File dbPropFile = new File(dbPropPath);
+                final String dbPropPath = cl.getOptionValue("C");
+                final File dbPropFile = new File(dbPropPath);
                 if (!dbPropFile.exists()) {
                     throw new IllegalArgumentException("Connection properties file " + dbPropFile.getPath() + " does not exist.");
-                }
+                }                
+                config.setConnection(ConnectionParameters.fromProperties(dbPropFile));
                 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.");
             }
 
+            // Root directory for file crawling.
             if (cl.hasOption("d")) {
-                this.rootDir = new File(cl.getOptionValue("d"));
-                if (!this.rootDir.exists()) {
+                File rootDir = new File(cl.getOptionValue("d"));
+                if (!rootDir.exists()) {
                     throw new IllegalArgumentException("The directory does not exist.");
                 }
-                if (!this.rootDir.isDirectory()) {
+                if (!rootDir.isDirectory()) {
                     throw new IllegalArgumentException("The specified path is not a directory.");
                 }
-            }
-
+                config.setRootDir(rootDir);
+                LOGGER.config("root dir for crawling set to " + config.rootDir());
+            }
+
+            // Timestamp file for date filtering.
             if (cl.hasOption("t")) {
-                this.timestampFile = new File(cl.getOptionValue("t"));
-                if (!this.timestampFile.exists()) {
+                File timestampFile = new File(cl.getOptionValue("t"));
+                config.setTimestampFile(timestampFile);
+                if (!timestampFile.exists()) {
                     try {
-                        // Create new time stamp file.
-                        LOGGER.info("creating new timestamp file " + this.timestampFile.getPath());
-                        this.timestampFile.createNewFile();
-                    } catch (IOException e) {
-                        throw new IllegalArgumentException("Error creating timestamp file " + this.timestampFile.getPath());
+                        // Create new time stamp file which will have its date updated at the end of the job.
+                        LOGGER.config("creating new timestamp file " + timestampFile.getPath());
+                        timestampFile.createNewFile();                        
+                    } catch (final IOException e) {
+                        throw new IllegalArgumentException("Error creating timestamp file: " + timestampFile.getPath());
                     }
-                } else { 
+                } else {
                     try {
-                        // Get cut-off date for files from existing time stamp file. 
-                        this.timestamp = new Date(Files.readAttributes(this.timestampFile.toPath(), BasicFileAttributes.class).lastModifiedTime().toMillis());
-                        LOGGER.info("got timestamp " + this.timestamp + " from existing file " + this.timestampFile.getPath());
+                        // Get the date filter for files from an existing time stamp file provided by the user.
+                        Date timestamp = new Date(Files.readAttributes(config.timestampFile().toPath(), BasicFileAttributes.class).lastModifiedTime()
+                                .toMillis());
+                        config.setTimestamp(timestamp);
+                        LOGGER.config("got timestamp " + timestamp + " from existing file " + config.timestampFile().getPath());
                     } catch (final IOException e) {
                         throw new RuntimeException("Error getting attributes of timestamp file.", e);
                     }
                 }
             }
 
+            // List of one or more runs to accept in the job.
             if (cl.hasOption("a")) {
+                Set<Integer> acceptRuns = new HashSet<Integer>();
                 for (final String runString : cl.getOptionValues("a")) {
                     final Integer acceptRun = Integer.parseInt(runString);
-                    this.acceptRuns.add(acceptRun);
-                    LOGGER.config("added run number filter " + acceptRun);
-                }
-            }
-
-            if (cl.hasOption("s")) {
-                LOGGER.config("print summary enabled");
-                this.printSummary = true;
-            }
-
-            if (cl.hasOption("r")) {                
-                this.updateRunLog = true;
-                LOGGER.config("run db will be updated");
-            }
-
-            if (cl.hasOption("e")) {                
-                this.processEpicsData = true;
-                LOGGER.config("EPICS processing enabled");
-            }
-
-            if (cl.hasOption("c")) {                
-                this.useFileCache = true;
-                LOGGER.config("using file cache");
-            }
-
+                    acceptRuns.add(acceptRun);
+                    LOGGER.config("added run filter " + acceptRun);
+                }
+                config.setAcceptRuns(acceptRuns);
+            }
+
+            // Enable run log updating (off by default).
+            if (cl.hasOption("r")) {
+                config.setUpdateRunLog(true);
+                LOGGER.config("updating run database is enabled");
+            }
+
+            // Enable file cache usage for running at JLAB.
+            if (cl.hasOption("c")) {
+                config.setUseFileCache(true);
+                LOGGER.config("file cache is enabled");
+            }
+
+            // Max wait time for file caching.
             if (cl.hasOption("w")) {
-                this.waitTime = Long.parseLong(cl.getOptionValue("w")) * MILLISECONDS;
-                if (this.waitTime > 0L) {
-                    this.cacheManager.setWaitTime(this.waitTime);
-                    LOGGER.config("max wait time for caching set to " + this.waitTime);
-                }
-            }
-
+                Long waitTime = Long.parseLong(cl.getOptionValue("w")) * MILLISECONDS;
+                config.setWaitTime(waitTime);
+                LOGGER.config("max time for file caching set to " + config.waitTime());
+            }
+
+            // Max files to process per run; mostly just here for debugging purposes.
             if (cl.hasOption("m")) {
-                this.maxFiles = Integer.parseInt(cl.getOptionValue("m"));
-                LOGGER.config("max files set to " + this.maxFiles);
-            }
-
+                int maxFiles = Integer.parseInt(cl.getOptionValue("m"));
+                config.setMaxFiles(maxFiles);
+                LOGGER.config("max files set to " + maxFiles);
+            }
+
+            // Event printing interval when doing EVIO event processing.
             if (cl.hasOption("p")) {
-                this.eventPrintInterval = Integer.parseInt(cl.getOptionValue("p"));
-                LOGGER.config("event print interval set to " + this.eventPrintInterval);
-            }
-            
+                int eventPrintInterval = Integer.parseInt(cl.getOptionValue("p"));
+                config.setEventPrintInterval(eventPrintInterval);
+                LOGGER.config("event print interval set to " + eventPrintInterval);
+            }
+
+            // Flag to allow replacement of existing records in the database; not allowed by default.
             if (cl.hasOption("u")) {
-                this.allowUpdates = true;
-                if (!this.updateRunLog) {
-                    LOGGER.info("the -u option is ignored because run_log is not being updated");
-                }
-            }
-            
+                config.setAllowUpdates(true);
+                LOGGER.config("replacement of existing run log information in database is enabled");
+            }
+
+            // User supplied timestamp string that is converted to a date for file filtering.
             if (cl.hasOption("b")) {
                 try {
-                    if (this.timestamp != null) {
-                        LOGGER.warning("existing timestamp from file " + this.timestamp + " will be overridden by date from -b argument");
+                    if (config.timestamp() != null) {
+                        LOGGER.warning("existing timestamp from file " + config.timestamp() + " will be overridden by date from -b argument");
                     }
-                    this.timestamp = DATE_FORMAT.parse(cl.getOptionValue("b"));
-                    LOGGER.info("set timestamp to " + DATE_FORMAT.format(this.timestamp));
-                } catch (java.text.ParseException e) {
+                    config.setTimestamp(cl.getOptionValue("b"));
+                    LOGGER.config("set timestamp to " + config.timestamp() + " from -b argument");
+                } catch (final java.text.ParseException e) {
                     throw new RuntimeException(e);
                 }
             }
-            
-            
+
+            // User supplied EvioEventProcessor classes to run in the run processing step.
             if (cl.hasOption("E")) {
-                String[] classNames = cl.getOptionValues("E");
-                for (String className : classNames) {
+                final String[] classNames = cl.getOptionValues("E");
+                for (final String className : classNames) {
                     try {
-                        processors.add(createEvioEventProcessor(className));
-                    } catch (Exception e) {
+                        config.addProcessor(className);
+                    } catch (final Exception e) {
                         throw new RuntimeException(e);
                     }
                 }
             }
-            
+
         } catch (final ParseException e) {
             throw new RuntimeException("Error parsing options.", e);
         }
+        
+        LOGGER.info("done parsing command line options");
 
         return this;
     }
 
     /**
-     * Print the usage statement for this tool to the console.
+     * Print the usage statement for this tool to the console and exit.
      */
     private void printUsage() {
         final HelpFormatter help = new HelpFormatter();
         help.printHelp("EvioFileCrawler", "", OPTIONS, "");
         System.exit(0);
     }
-
-    /**
-     * Process a single run.
-     *
-     * @param runSummary the run summary information
-     * @throws Exception if there is some error while running the file processing
-     */
-    private void processRun(final RunSummary runSummary) throws Exception {
-
-        // Clear the cache manager.
-        this.cacheManager.clear();
-
-        // Create a processor to process all the EVIO events in the run.
-        final RunProcessor runProcessor = this.createRunProcessor(runSummary);
-        
-        for (EvioEventProcessor processor : processors) {
-            runProcessor.addProcessor(processor);
-            LOGGER.config("added extra EVIO processor " + processor.getClass().getName());
-        }
-
-        // Process all of the runs files.
-        runProcessor.process();
-    }
-
-    /**
-     * Process all runs that were found.
-     *
-     * @param runs the run log containing the list of run summaries
-     * @throws Exception if there is an error processing one of the runs
-     */
-    private void processRuns(final RunLog runs) throws Exception {
-        // Process all of the runs that were found.
-        for (final int run : runs.getSortedRunNumbers()) {
-            this.processRun(runs.getRunSummary(run));
-        }
-    }
-
-    /**
-     * Run the crawler job (generally will take a long time!).
+   
+    /**
+     * Run the full crawler job, including all configured file-processing steps, which may take a very long time!
      *
      * @throws Exception if there is some error during the job
      */
     public void run() throws Exception {
-        final EnumSet<FileVisitOption> options = EnumSet.noneOf(FileVisitOption.class);
-        final EvioFileVisitor visitor = new EvioFileVisitor(this.timestamp);
-        if (this.timestamp != null) {
-            visitor.addFilter(new DateFileFilter(this.timestamp));
-            LOGGER.config("added date filter with timestamp " + this.timestamp);
-        }
-        if (!this.acceptRuns.isEmpty()) {
-            visitor.addFilter(new RunFilter(this.acceptRuns));
-            LOGGER.config("added run filter");
-        }
-        try {
-            // Walk the file tree from the root directory.
-            Files.walkFileTree(this.rootDir.toPath(), options, Integer.MAX_VALUE, visitor);
-        } catch (final IOException e) {
-            throw new RuntimeException(e);
-        }
-
+
+        LOGGER.info("running Crawler job");
+        
+        // Create the file visitor for crawling the root directory with the given date filter.
+        final EvioFileVisitor visitor = new EvioFileVisitor(config.timestamp());
+
+        // Walk the file tree using the visitor.
+        walk(visitor);
+
+        // Get the list of run data created by the visitor.
         final RunLog runs = visitor.getRunLog();
 
-        // Print run numbers that were found.
+        // Print the run numbers that were found.
+        printRunNumbers(runs);
+
+        // Sort the files on their sequence numbers.
+        runs.sortAllFiles();
+
+        // Process all the files, performing caching from MSS if necessary. 
+        RunProcessor.processRuns(this.cacheManager, runs, config);
+
+        // Print the summary information after the run processing is done.
+        runs.printRunSummaries();
+
+        // Execute the database update.
+        executeRunLogUpdate(runs);
+
+        // Update the timestamp output file.
+        updateTimestamp();
+
+        LOGGER.info("Crawler job is done!");
+    }
+
+    private void executeRunLogUpdate(final RunLog runs) throws SQLException {
+        // Insert the run information into the database.
+        if (config.updateRunLog()) {
+
+            // Open a DB connection.
+            final Connection connection = config.connectionParameters().createConnection();
+
+            // Create and configure RunLogUpdater which updates the run log for all runs found in the crawl job.
+            final RunLogUpdater runUpdater = new RunLogUpdater(connection, runs, config.allowUpdates());
+
+            // Update the DB.
+            runUpdater.insert();
+
+            // Close the DB connection.
+            connection.close();
+        }
+    }
+
+    private void printRunNumbers(final RunLog runs) {
+        // Print the list of runs that were found.
         final StringBuffer sb = new StringBuffer();
         for (final Integer run : runs.getSortedRunNumbers()) {
             sb.append(run + " ");
         }
-        LOGGER.info("found files from runs: " + sb.toString());
-
-        // Sort files on their sequence numbers.
-        LOGGER.fine("sorting files by sequence ...");
-        runs.sortAllFiles();
-
-        // Process all the files in all of runs. This will perform caching from MSS if necessary.
-        this.processRuns(runs);
-
-        // Print the run summary information.
-        if (this.printSummary) {
-            runs.printRunSummaries();
-        }
-
-        // Insert run information into the database.
-        if (this.updateRunLog) {
-
-            // 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) {
-            this.timestampFile = new File("timestamp");
+        LOGGER.info("found EVIO files from runs: " + sb.toString());
+    }
+
+    private void walk(final EvioFileVisitor visitor) {
+        if (config.timestamp() != null) {
+            // Date filter from timestamp.
+            visitor.addFilter(new DateFileFilter(config.timestamp()));
+            LOGGER.config("added date filter " + config.timestamp());
+        }
+
+        if (!config.acceptRuns().isEmpty()) {
+            // List of run numbers to accept.
+            visitor.addFilter(new RunFilter(config.acceptRuns()));
+            LOGGER.config("added run number filter");
+        }
+
+        try {
+            // Walk the file tree from the root directory.
+            final EnumSet<FileVisitOption> options = EnumSet.noneOf(FileVisitOption.class);
+            Files.walkFileTree(config.rootDir().toPath(), options, Integer.MAX_VALUE, visitor);
+        } catch (final IOException e) {
+            throw new RuntimeException("Error while walking the directory tree.", e);
+        }
+    }
+
+    private void updateTimestamp() {
+        // Update the timestamp file which can be used to tell which files have been processed by their creation date.
+        if (config.timestampFile() == null) {
+            config.setTimestampFile(new File("timestamp"));
             try {
-                this.timestampFile.createNewFile();
+                config.timestampFile().createNewFile();
             } catch (final IOException e) {
                 throw new RuntimeException(e);
             }
-            LOGGER.info("created new timestamp file: " + this.timestampFile.getPath());
-        }
-        this.timestampFile.setLastModified(System.currentTimeMillis());
-        LOGGER.info("set modified on timestamp file: " + new Date(this.timestampFile.lastModified()));
-    }
-    
-    /**
-     * 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());
+            LOGGER.config("created new timestamp file: " + config.timestampFile().getPath());
+        }
+        config.timestampFile().setLastModified(System.currentTimeMillis());
+        LOGGER.config("set modified on timestamp file: " + new Date(config.timestampFile().lastModified()));
     }
 }

Added: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java	(added)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/CrawlerConfig.java	Fri Jun 19 19:03:30 2015
@@ -0,0 +1,201 @@
+package org.hps.record.evio.crawler;
+
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.hps.conditions.database.ConnectionParameters;
+import org.hps.record.evio.EvioEventProcessor;
+
+/**
+ * Crawls EVIO files in a directory tree, groups the files that are found by run, and optionally performs various tasks based on the run summary
+ * information that is accumulated, including printing a summary, caching the files from JLAB MSS, and updating a run database.
+ *
+ * @author Jeremy McCormick, SLAC
+ */
+final class CrawlerConfig {
+
+    private static final SimpleDateFormat TIMESTAMP_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    
+    /**
+     * Default event print interval.
+     */
+    private final int DEFAULT_EVENT_PRINT_INTERVAL = 1000;
+
+    /**
+     * Interval for printing out event number while running EVIO processors.
+     */
+    private int eventPrintInterval = DEFAULT_EVENT_PRINT_INTERVAL;
+   
+    /**
+     * A list of run numbers to accept in the job.
+     */
+    private Set<Integer> acceptRuns;
+
+    private boolean allowUpdates = false;
+
+    private ConnectionParameters connectionParameters;
+
+    /**
+     * The maximum number of files to accept (just used for debugging purposes).
+     */
+    private int maxFiles = -1;
+
+    private final List<EvioEventProcessor> processors = new ArrayList<EvioEventProcessor>();
+
+    /**
+     * The root directory to crawl which defaults to the current directory.
+     */
+    private File rootDir = new File(System.getProperty("user.dir"));
+
+    /**
+     * A timestamp to use for filtering input files on their creation date.
+     */
+    private Date timestamp = null;
+
+    /**
+     * A file to use for the timestamp date.
+     */
+    private File timestampFile = null;
+
+    /**
+     * Flag indicating if the run database should be updated from results of the job.
+     */
+    private boolean updateRunLog = false;
+
+    /**
+     * Flag indicating if the file cache should be used (e.g. jcache automatically executed to move files to the cache disk from tape).
+     */
+    private boolean useFileCache = false;
+
+    /**
+     * The maximum wait time in milliseconds to allow for file caching operations.
+     */
+    private Long waitTime;
+
+    CrawlerConfig addProcessor(final EvioEventProcessor processor) {
+        this.processors.add(processor);
+        return this;
+    }
+    
+    CrawlerConfig addProcessor(final String className) {
+        try {
+            this.processors.add(EvioEventProcessor.class.cast(Class.forName(className).newInstance()));
+        } catch (Exception e) {
+            throw new RuntimeException("Error creating EvioEventProcessor with type: " + className, e);
+        }
+        return this;
+    }
+
+    CrawlerConfig setAcceptRuns(final Set<Integer> acceptRuns) {
+        this.acceptRuns = acceptRuns;
+        return this;
+    }
+
+    CrawlerConfig setAllowUpdates(final boolean allowUpdates) {
+        this.allowUpdates = allowUpdates;
+        return this;
+    }
+
+    CrawlerConfig setConnection(final ConnectionParameters connectionParameters) {
+        this.connectionParameters = connectionParameters;
+        return this;
+    }
+
+    CrawlerConfig setMaxFiles(final int maxFiles) {
+        this.maxFiles = maxFiles;
+        return this;
+    }
+    
+    CrawlerConfig setRootDir(File rootDir) {
+        this.rootDir = rootDir;
+        return this;
+    }
+    
+    CrawlerConfig setTimestamp(Date timestamp) {
+        this.timestamp = timestamp;
+        return this;
+    }
+    
+    CrawlerConfig setTimestamp(String timestampString) throws ParseException {
+        TIMESTAMP_DATE_FORMAT.parse(timestampString);
+        return this;
+    }
+    
+    CrawlerConfig setTimestampFile(File timestampFile) {
+        this.timestampFile = timestampFile;
+        return this;
+    }
+    
+    CrawlerConfig setUpdateRunLog(boolean updateRunLog) {
+        this.updateRunLog = updateRunLog;
+        return this;
+    }
+    
+    CrawlerConfig setUseFileCache(boolean useFileCache) {
+        this.useFileCache = useFileCache;
+        return this;
+    }
+    
+    CrawlerConfig setWaitTime(long waitTime) {
+        this.waitTime = waitTime;
+        return this;
+    }
+    
+    CrawlerConfig setEventPrintInterval(int eventPrintInterval) {
+        this.eventPrintInterval = eventPrintInterval;
+        return this;
+    }
+    
+    Set<Integer> acceptRuns() {
+        return acceptRuns;
+    }
+    
+    boolean allowUpdates() {
+        return allowUpdates;
+    }
+
+    ConnectionParameters connectionParameters() {
+        return connectionParameters;
+    }
+
+    int maxFiles() {
+        return maxFiles;
+    }
+
+    List<EvioEventProcessor> processors() {
+        return processors;
+    }
+
+    File rootDir() {
+        return rootDir;
+    }
+
+    Date timestamp() {
+        return timestamp;
+    }
+    
+    File timestampFile() {
+        return timestampFile;
+    }
+
+    boolean updateRunLog() {
+        return updateRunLog;
+    }
+
+    boolean useFileCache() {
+        return useFileCache;
+    }
+
+    Long waitTime() {
+        return waitTime;
+    }    
+    
+    int eventPrintInterval() {
+        return this.eventPrintInterval;
+    }          
+}

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	Fri Jun 19 19:03:30 2015
@@ -11,8 +11,9 @@
 import org.lcsim.util.log.LogUtil;
 
 /**
- * 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 {@link #getRunSummary(int)}.
+ * This class contains summary information about a series of runs which are modeled with the {@link RunSummary} class.
+ * <p> 
+ * These can be looked 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	Fri Jun 19 19:03:30 2015
@@ -5,13 +5,12 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import org.hps.conditions.database.ConnectionParameters;
 import org.lcsim.util.log.LogUtil;
 
 /**
  * Updates the run database with information from the crawler job.
  * <p>
- * The {@link RunSummaryUpdater} is used to insert rows for each run.
+ * The {@link RunSummaryUpdater} is used to insert the data for each run.
  * 
  * @author Jeremy McCormick, SLAC
  */
@@ -38,44 +37,20 @@
     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) {
+    RunLogUpdater(Connection connection, RunLog runLog, boolean allowUpdates) {
+        
+        // Set the DB connection.
+        this.connection = connection;
+        
+        // Set the run log with the run info to update.
         this.runLog = runLog;
-        
-        // Create database connection to use in this session.
-        final ConnectionParameters cp = new ConnectionParameters("root", "derp", "hps_run_db", "localhost");
-        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();
-        } catch (SQLException e) {
-            e.printStackTrace();
-        }
-    }
-    
+            
     /**
      * Insert the run summary information into the database, including updating the run_log_files
      * and run_log_epics tables.
@@ -96,68 +71,19 @@
                 
                 // Get the RunSummary data for the run.
                 RunSummary runSummary = runLog.getRunSummary(run);
-                
+                                               
                 LOGGER.info("updating " + runSummary);
                                 
                 // Create the db updater for the RunSummary.
-                RunSummaryUpdater runUpdater = new RunSummaryUpdater(connection, runSummary);      
-                                
-                // 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("existing row for " + run + " will be updated");
-                        // Update existing row.
-                        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.");
-                    }
-                } else {                
-                                        
-                    LOGGER.info("inserting new row in runs for run " + run + " ...");
-                    
-                    // Insert new record into run_log.
-                    runUpdater.insertRun();
-                }
+                RunSummaryUpdater runUpdater = new RunSummaryUpdater(connection, runSummary);
                 
-                // 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();
+                // Set whether existing records can be replaced.
+                runUpdater.setAllowDeleteExisting(allowUpdates);
                 
-                // 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 run records.
+                runUpdater.insert();
                 
-                // Commit the transactions for this run.
-                LOGGER.info("committing transaction for run " + run);
-                connection.commit();
+                LOGGER.info("run " + runSummary.getRun() + " summary inserted successfully");
             }
             
         } catch (final SQLException e) {
@@ -168,13 +94,7 @@
                 LOGGER.log(Level.SEVERE, "error rolling back transaction", e2);
                 throw new RuntimeException(e);
             }
-        } finally {
-            try {
-                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	Fri Jun 19 19:03:30 2015
@@ -10,6 +10,7 @@
 
 import org.hps.record.evio.EvioEventConstants;
 import org.hps.record.evio.EvioEventProcessor;
+import org.hps.record.scalers.ScalersEvioProcessor;
 import org.jlab.coda.jevio.EvioEvent;
 import org.jlab.coda.jevio.EvioException;
 import org.jlab.coda.jevio.EvioReader;
@@ -17,7 +18,7 @@
 import org.lcsim.util.log.LogUtil;
 
 /**
- * Processes all the EVIO files from a run in order to extract various information including start and end dates.
+ * Processes EVIO files from a run in order to extract various meta data 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.
  * <p>
@@ -33,6 +34,42 @@
     private static final Logger LOGGER = LogUtil.create(RunProcessor.class, new DefaultLogFormatter(), Level.FINE);
 
     /**
+     * Create the processor for a single run.
+     *
+     * @param runSummary the run summary for the run
+     * @return the run processor
+     */
+    static RunProcessor createRunProcessor(final JCacheManager cacheManager, final RunSummary runSummary, CrawlerConfig config) {
+
+        // Create new run processor.
+        final RunProcessor processor = new RunProcessor(runSummary, cacheManager);
+
+        // EPICS processor.
+        processor.addProcessor(new EpicsLog(runSummary));
+
+        // Scaler data processor.
+        final ScalersEvioProcessor scalersProcessor = new ScalersEvioProcessor();
+        scalersProcessor.setResetEveryEvent(false);
+        processor.addProcessor(scalersProcessor);
+
+        // Event log processor.
+        processor.addProcessor(new EventTypeLog(runSummary));
+
+        // Max files.
+        if (config.maxFiles() != -1) {
+            processor.setMaxFiles(config.maxFiles());
+        }
+
+        // Enable file caching.
+        processor.useFileCache(config.useFileCache());
+
+        // Set event printing interval.
+        processor.setEventPrintInterval(config.eventPrintInterval());
+
+        return processor;
+    }
+
+    /**
      * The cache manager.
      */
     private final JCacheManager cacheManager;
@@ -107,7 +144,7 @@
 
     /**
      * 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
@@ -142,22 +179,30 @@
         return this.processors;
     }
 
-    /**
-     * Return <code>true</code> if a valid CODA <i>END</i> event can be located in the
-     * <code>EvioReader</code>'s current file. 
-     * 
+    ScalersEvioProcessor getScalersProcessor() {
+        for (final EvioEventProcessor processor : this.processors) {
+            if (processor instanceof ScalersEvioProcessor) {
+                return ScalersEvioProcessor.class.cast(processor);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 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
      * @throws Exception if there are IO problems using the reader
      */
     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) {
@@ -170,8 +215,8 @@
     }
 
     /**
-     * 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.
+     * 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.
      *
@@ -230,13 +275,13 @@
                 this.runSummary.setStartDate(runStart);
             }
 
-            // Compute event count for the file and store the value in the run summary's file list.
+            // Compute the event count for the file and store the value in the run summary's file list.
             LOGGER.info("getting event count for " + file.getPath() + "...");
             final int eventCount = this.computeEventCount(reader);
             this.runSummary.getEvioFileList().setEventCount(file, eventCount);
             LOGGER.info("set event count " + eventCount + " for " + file.getPath());
 
-            // Process the events using the list of EVIO processors.
+            // Process the events using the EVIO processors.
             LOGGER.info("running EVIO processors ...");
             reader.gotoEventNumber(0);
             int nProcessed = 0;
@@ -266,6 +311,12 @@
                 LOGGER.info("found end date " + endDate);
             }
 
+            // Pull scaler data from EVIO processor into run summary.
+            final ScalersEvioProcessor scalersProcessor = this.getScalersProcessor();
+            if (scalersProcessor != null) {
+                runSummary.setScalerData(scalersProcessor.getScalerData());
+            }
+
         } finally {
             if (reader != null) {
                 reader.close();
@@ -306,5 +357,40 @@
         this.useFileCache = cacheFiles;
         LOGGER.config("file caching enabled");
     }
-
+    
+    /**
+     * Process all the runs that were found.
+     *
+     * @param runs the run log containing the list of run summaries
+     * @throws Exception if there is an error processing one of the runs
+     */
+    static void processRuns(JCacheManager cacheManager, final RunLog runs, CrawlerConfig config) throws Exception {
+        
+        // Configure max wait time of jcache manager.
+        if (config.waitTime() != null && config.waitTime() > 0L) {
+            cacheManager.setWaitTime(config.waitTime());
+            LOGGER.config("JCacheManager max wait time set to " + config.waitTime());
+        }                                
+        
+        // Process all of the runs that were found.
+        for (final int run : runs.getSortedRunNumbers()) {
+            
+            // Get the run summary.
+            RunSummary runSummary = runs.getRunSummary(run);
+            
+            // Clear the cache manager.
+            cacheManager.clear();
+
+            // Create a processor to process all the EVIO events in the run.
+            final RunProcessor runProcessor = RunProcessor.createRunProcessor(cacheManager, runSummary, config);
+
+            for (final EvioEventProcessor processor : config.processors()) {
+                runProcessor.addProcessor(processor);
+                LOGGER.config("added extra EVIO processor " + processor.getClass().getName());
+            }
+
+            // Process all of the runs files.
+            runProcessor.process();
+        }
+    }
 }

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	Fri Jun 19 19:03:30 2015
@@ -7,11 +7,11 @@
 import java.util.logging.Logger;
 
 import org.hps.record.epics.EpicsData;
+import org.hps.record.scalers.ScalerData;
 import org.lcsim.util.log.LogUtil;
 
 /**
- * This class models the run summary information which is persisted as a row in the <i>run_log</i> table
- * of the run database.
+ * This class models the run summary information which is persisted as a row in the <i>run_log</i> table of the run database.
  * <p>
  * This information includes:
  * <ul>
@@ -63,6 +63,8 @@
      */
     private final int run;
 
+    private ScalerData scalerData;
+
     /**
      * The start date of the run.
      */
@@ -141,6 +143,10 @@
      */
     int getRun() {
         return this.run;
+    }
+
+    ScalerData getScalerData() {
+        return this.scalerData;
     }
 
     /**
@@ -232,6 +238,10 @@
         this.eventTypeCounts = eventTypeCounts;
     }
 
+    void setScalerData(final ScalerData scalerData) {
+        this.scalerData = scalerData;
+    }
+
     /**
      * Set the start date of the run.
      *
@@ -247,9 +257,10 @@
     void sortFiles() {
         this.files.sort();
     }
-    
+
+    @Override
     public String toString() {
-        return "RunSummary { run: " + this.run + ", started: " + this.getStartDate() + ", ended: " + this.getEndDate() + ", events: " 
+        return "RunSummary { run: " + this.run + ", started: " + this.getStartDate() + ", ended: " + this.getEndDate() + ", events: "
                 + this.getTotalEvents() + ", endOkay: " + endOkay + " }";
     }
 }

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	Fri Jun 19 19:03:30 2015
@@ -13,7 +13,7 @@
 
 /**
  * Updates the run database tables with information from a single run.
- * 
+ *
  * @author Jeremy McCormick, SLAC
  */
 public class RunSummaryUpdater {
@@ -22,105 +22,176 @@
      * Setup logging.
      */
     private static final Logger LOGGER = LogUtil.create(RunSummaryUpdater.class);
-    
+
+    /**
+     * Flag to allow deletion/replacement of existing records; disallowed by default.
+     */
+    private boolean allowDeleteExisting = false;
+
+    /**
+     * The database connection.
+     */
+    private final Connection connection;
+
+    /**
+     * The run number (read from the summary in the constructor for convenience).
+     */
+    private int run = -1;
+
     /**
      * The run summary to update or insert.
      */
-    private RunSummary runSummary;   
-    
-    /**
-     * The database connection.
-     */
-    private Connection connection;
-    
-    /**
-     * The run number (read from the summary in the constructor for convenience).
-     */
-    private int run = -1;
-        
+    private final RunSummary runSummary;
+
     /**
      * Create a <code>RunSummaryUpdater</code> for the given <code>RunSummary</code>.
-     * 
+     *
      * @param connection the database connection
      * @param runSummary the run summary to update or insert
      */
-    RunSummaryUpdater(Connection connection, RunSummary runSummary) {
+    RunSummaryUpdater(final Connection connection, final RunSummary runSummary) {
 
         if (connection == null) {
             throw new IllegalArgumentException("connection is null");
         }
         this.connection = connection;
-        
+
         if (runSummary == null) {
             throw new IllegalArgumentException("runSummary is null");
         }
         this.runSummary = runSummary;
+
+        // Cache run number.
+        this.run = this.runSummary.getRun();
+    }
+
+    private void delete() throws SQLException {
+
+        LOGGER.info("deleting existing information for run " + runSummary.getRun());
+
+        // Delete EPICS log.
+        this.deleteEpics();
+
+        // Delete file list.
+        this.deleteFiles();
+
+        // Delete run log.
+        this.deleteRun();
+
+        LOGGER.info("deleted run " + runSummary.getRun() + " info successfully");
+    }
+
+    /**
+     * Delete existing EPICS data from the run_log_epics table.
+     *
+     * @throws SQLException if there is an error performing the db query
+     */
+    private void deleteEpics() throws SQLException {
+        final PreparedStatement statement = connection.prepareStatement("DELETE FROM run_epics WHERE run = ?");
+        statement.setInt(1, this.run);
+        statement.executeUpdate();
+    }
+
+    /**
+     * Delete the records of the files associated to this run.
+     *
+     * @param files the list of files
+     * @throws SQLException if there is an error executing the SQL query
+     */
+    private void deleteFiles() throws SQLException {
+        LOGGER.info("deleting rows from run_files for " + run + " ...");
+        final PreparedStatement s = connection.prepareStatement("DELETE FROM run_files where run = ?");
+        s.setInt(1, run);
+        s.executeUpdate();
+        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
+     */
+    private void deleteRun() throws SQLException {
+        LOGGER.info("deleting record from runs for " + run + " ...");
+        final PreparedStatement s = connection.prepareStatement("DELETE FROM runs where run = ?");
+        s.setInt(1, run);
+        s.executeUpdate();
+        LOGGER.info("deleted rows from runs for " + run);
+    }
+
+    void insert() throws SQLException {
         
-        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 updateRun() throws SQLException {
-        
-        PreparedStatement runLogStatement = null;
-        runLogStatement = 
-                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());
-        runLogStatement.setInt(4, runSummary.getEvioFileList().size());
-        runLogStatement.setBoolean(5, runSummary.isEndOkay());
-        runLogStatement.setInt(6, run);
-        runLogStatement.executeUpdate();
-        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 insertRun() throws SQLException {
-        PreparedStatement statement = 
-                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()));
-        statement.setInt(4, runSummary.getTotalEvents());
-        statement.setInt(5, runSummary.getEvioFileList().size());
-        statement.setBoolean(6, runSummary.isEndOkay());
-        statement.executeUpdate();
-        LOGGER.info("inserted run " + run + " to runs table");
-    }
-    
-    /**
-     * 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 runExists() throws SQLException {
-        PreparedStatement s = connection.prepareStatement("SELECT run FROM runs where run = ?");
-        s.setInt(1, run);        
-        ResultSet rs = s.executeQuery();
-        return rs.first();
-    }
-    
-    /**
-     * Insert the file names into the run database.    
+        LOGGER.info("performing db insert for " + runSummary);
+
+        // Turn auto-commit off as this whole method is a single transaction.
+        connection.setAutoCommit(false);
+
+        // Does the run exist in the database already?
+        if (this.runExists()) {
+            // Is deleting existing rows allowed?
+            if (allowDeleteExisting) {
+                // Delete the existing rows.
+                this.delete();
+            } else {
+                // Rows exist but updating is disallowed which is a fatal error.
+                final RuntimeException x = new RuntimeException("Run " + runSummary.getRun() + " already exists and deleting is not allowed.");
+                LOGGER.log(Level.SEVERE, x.getMessage(), x);
+                throw x;
+            }
+        }
+
+        // Insert basic run log info.
+        this.insertRun();
+
+        // Insert list of files.
+        this.insertFiles();
+
+        // Insert EPICS data.
+        this.insertEpics();
+
+        // Insert scaler data.
+        if (runSummary.getScalerData() != null) {
+            new ScalerDataUpdater(connection, runSummary.getScalerData(), run).insert();
+        }
+
+        // Commit the transactions for this run.
+        LOGGER.info("committing transaction for run " + run);
+        connection.commit();
+
+        // Turn auto-commit back on.
+        connection.setAutoCommit(true);
+    }
+
+    /**
+     * Insert EPICS data into the run_log_epics table.
+     *
+     * @throws SQLException if there is an error performing the db query
+     */
+    private void insertEpics() throws SQLException {
+        final PreparedStatement statement = connection.prepareStatement("INSERT INTO run_epics (run, variable_name, value) values (?, ?, ?)");
+        final EpicsData data = runSummary.getEpicsData();
+        if (data != null) {
+            for (final 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)");
+        }
+    }
+
+    /**
+     * Insert the file names into the run database.
      *
      * @param connection the database connection
      * @param run the run number
      * @throws SQLException if there is a problem executing one of the database queries
      */
-    void insertFiles() throws SQLException {
+    private void insertFiles() throws SQLException {
         LOGGER.info("updating file list ...");
         PreparedStatement filesStatement = null;
         filesStatement = connection.prepareStatement("INSERT INTO run_files (run, directory, name) VALUES(?, ?, ?)");
@@ -134,90 +205,42 @@
             filesStatement.executeUpdate();
         }
         LOGGER.info("run_files was updated");
-    }    
-    
-    /**
-     * Delete the records of the files associated to this run.
-     * 
-     * @param files the list of files
-     * @throws SQLException if there is an error executing the SQL query
-     */
-    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();
-        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();
-        LOGGER.info("deleted rows from runs for " + run);
-    }
-    
-    /**
-     * Return <code>true</code> if there is a row for at least one file for the run.
-     * @return <code>true</code> if there are file rows for this run
-     * @throws SQLException if there is an error executing the SQL query
-     */
-    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);
+    }
+
+    /**
+     * 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
+     */
+    private void insertRun() throws SQLException {
+        final PreparedStatement statement = 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()));
+        statement.setInt(4, runSummary.getTotalEvents());
+        statement.setInt(5, runSummary.getEvioFileList().size());
+        statement.setBoolean(6, runSummary.isEndOkay());
         statement.executeUpdate();
-    }
-    
+        LOGGER.info("inserted run " + run + " to runs table");
+    }
+
     /**
      * 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 = ?");
+    private boolean runExists() throws SQLException {
+        final PreparedStatement s = connection.prepareStatement("SELECT run FROM runs where run = ?");
         s.setInt(1, run);
-        ResultSet rs = s.executeQuery();
+        final ResultSet rs = s.executeQuery();
         return rs.first();
     }
-}
+
+    void setAllowDeleteExisting(final boolean allowDeleteExisting) {
+        this.allowDeleteExisting = allowDeleteExisting;
+    }
+}

Added: java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/ScalerDataUpdater.java
 =============================================================================
--- java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/ScalerDataUpdater.java	(added)
+++ java/trunk/record-util/src/main/java/org/hps/record/evio/crawler/ScalerDataUpdater.java	Fri Jun 19 19:03:30 2015
@@ -0,0 +1,83 @@
+package org.hps.record.evio.crawler;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.hps.record.scalers.ScalerData;
+
+/**
+ * Database interface for inserting scaler data into the run log.
+ * 
+ * @author Jeremy McCormick, SLAC
+ */
+public class ScalerDataUpdater {
+
+    private boolean allowDeleteExisting = false;
+
+    private final Connection connection;
+
+    private final ScalerData data;
+
+    private final int run;
+
+    private final String SQL_DELETE = "DELETE FROM run_scalers WHERE run = ?";
+
+    private final String SQL_INSERT = "INSERT INTO run_scalers (run, idx, value) VALUES (?, ?, ?)";
+
+    private final String SQL_SELECT = "SELECT run FROM run_scalers WHERE run = ?";
+
+    ScalerDataUpdater(final Connection connection, final ScalerData data, final int run) {
+        this.connection = connection;
+        this.data = data;
+        this.run = run;
+    }
+
+    private void delete() throws SQLException {
+        final PreparedStatement s = connection.prepareStatement(SQL_DELETE);
+        s.setInt(1, run);
+        s.executeUpdate();
+    }
+
+    private boolean exists() throws SQLException {
+        final PreparedStatement s = connection.prepareStatement(SQL_SELECT);
+        s.setInt(1, run);
+        final ResultSet rs = s.executeQuery();
+        return rs.first();
+    }
+
+    void insert() throws SQLException {
+        if (this.exists()) {
+            if (allowDeleteExisting) {
+                this.delete();
+            } else {
+                throw new RuntimeException("Scaler data already exists and updates are not allowed.");
+            }
+        }
+        try {
+            this.insertScalerData();
+        } catch (final SQLException e) {
+            connection.rollback();
+        }
+    }
+
+    private void insertScalerData() throws SQLException {
+        final PreparedStatement statement;
+        try {
+            statement = connection.prepareStatement(SQL_INSERT);
+            for (int idx = 0; idx < data.size(); idx++) {
+                statement.setInt(1, run);
+                statement.setInt(2, idx);
+                statement.setInt(3, data.getValue(idx));
+                statement.executeUpdate();
+            }
+        } finally {
+            connection.commit();
+        }
+    }
+
+    void setAllowDeleteExisting(final boolean allowDeleteExisting) {
+        this.allowDeleteExisting = allowDeleteExisting;
+    }
+}

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	Fri Jun 19 19:03:30 2015
@@ -1,5 +1,8 @@
 /**
- * Implements an EVIO file crawler to extract run and configuration information, including run start and end dates, event counts, etc.
+ * Implements an EVIO file crawler to extract run and configuration information.
+ * <p>
+ * This information includes run start and end dates, event counts, EPICS data, scaler data, 
+ * and the list of associated EVIO files.
  *
  * @author Jeremy McCormick, SLAC
  */

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

November 2017
August 2017
July 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager

Privacy Notice, Security Notice and Terms of Use