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 */