Author: [log in to unmask] Date: Tue Nov 3 11:32:25 2015 New Revision: 3686 Log: [HPSJAVA-623] Add event printing and other updates to job manager; source code formatting. Added: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventPrintLoopAdapter.java Modified: projects/lcsim/trunk/job-manager/pom.xml projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/AidaSaveDriver.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventMarkerDriver.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/IParameterConverter.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlDriver.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlManager.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/LCSimClassLoader.java projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/ParameterConverters.java Modified: projects/lcsim/trunk/job-manager/pom.xml ============================================================================= --- projects/lcsim/trunk/job-manager/pom.xml (original) +++ projects/lcsim/trunk/job-manager/pom.xml Tue Nov 3 11:32:25 2015 @@ -1,23 +1,19 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> <artifactId>lcsim-job-manager</artifactId> <name>job-manager</name> <description>job manager allowing XML steering using a command line utility</description> - <parent> <groupId>org.lcsim</groupId> <artifactId>lcsim-parent</artifactId> <version>3.1.6-SNAPSHOT</version> <relativePath>../parent/pom.xml</relativePath> </parent> - <scm> <url>http://java.freehep.org/svn/repos/lcdet/list/projects/lcsim/trunk/job-manager/</url> <connection>scm:svn:svn://svn.freehep.org/lcdet/projects/lcsim/trunk/job-manager/</connection> <developerConnection>scm:svn:svn://svn.freehep.org/lcdet/projects/lcsim/trunk/job-manager/</developerConnection> </scm> - <dependencies> <dependency> <groupId>org.lcsim</groupId> @@ -30,8 +26,7 @@ <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> - <version>1.0</version> + <version>1.3</version> </dependency> </dependencies> - </project> Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/AidaSaveDriver.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/AidaSaveDriver.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/AidaSaveDriver.java Tue Nov 3 11:32:25 2015 @@ -5,34 +5,28 @@ import org.lcsim.util.Driver; import org.lcsim.util.aida.AIDA; -public class AidaSaveDriver extends Driver -{ +public class AidaSaveDriver extends Driver { + String outputFileName = "plots.aida"; boolean verbose = false; - public AidaSaveDriver() - {} + public AidaSaveDriver() { + } - public void setOutputFileName(String outputFileName) - { + public void setOutputFileName(String outputFileName) { this.outputFileName = outputFileName; } - - public void setVerbose(boolean verbose) - { + + public void setVerbose(boolean verbose) { this.verbose = verbose; } - public void endOfData() - { - try - { + public void endOfData() { + try { if (verbose) System.out.println("Saving AIDA file to " + outputFileName + " ..."); AIDA.defaultInstance().saveAs(outputFileName); - } - catch (IOException x) - { + } catch (IOException x) { throw new RuntimeException("Problem saving AIDA file to " + outputFileName + ".", x); } } Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventMarkerDriver.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventMarkerDriver.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventMarkerDriver.java Tue Nov 3 11:32:25 2015 @@ -7,7 +7,9 @@ * Driver to print markers during event processing. * * @author Jeremy McCormick <[log in to unmask]> + * @deprecated Use built-in command line option of {@link JobManager} instead. */ +@Deprecated public class EventMarkerDriver extends Driver { private int interval = 1; @@ -32,7 +34,7 @@ protected void process(EventHeader event) { if (nEvents % interval == 0) { getLogger().info(marker + "Event " + event.getEventNumber() + " with sequence " + nEvents); - } + } nEvents++; } Added: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventPrintLoopAdapter.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventPrintLoopAdapter.java (added) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/EventPrintLoopAdapter.java Tue Nov 3 11:32:25 2015 @@ -0,0 +1,64 @@ +package org.lcsim.job; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.freehep.record.loop.RecordEvent; +import org.freehep.record.loop.RecordListener; +import org.lcsim.event.EventHeader; + +/** + * Prints out information from event processing such as the event number at a specified interval. + * + * @author Jeremy McCormick, SLAC + */ +class EventPrintLoopAdapter implements RecordListener { + + /** + * Setup the logger. + */ + private static Logger LOGGER = Logger.getLogger(EventPrintLoopAdapter.class.getName()); + static { + if (LOGGER.getLevel() == null) { + System.out.println("EventPrintLoopAdapter: set level to ALL"); + LOGGER.setLevel(Level.ALL); + } else { + System.out.println("EventPrintLoopAdapter: level " + LOGGER.getLevel() + " from config"); + } + } + + /** + * Sequence number of events processed. + */ + private long eventSequence = 0; + + /** + * Event print interval which means every Nth event will be printed. + */ + private long printInterval = 1; + + /** + * Class constructor. + * @param printInterval the event print interval + */ + EventPrintLoopAdapter(long printInterval) { + this.printInterval = printInterval; + } + + /** + * Process an event and print the event information. + */ + @Override + public void recordSupplied(RecordEvent recordEvent) { + Object record = recordEvent.getRecord(); + if (record instanceof EventHeader) { + EventHeader event = (EventHeader) recordEvent.getRecord(); + //System.out.println("EventPrintLoopAdapter.recordSupplied - " + event.getEventNumber()); + if (eventSequence % printInterval == 0) { + LOGGER.info("event: " + event.getEventNumber() + "; time: " + event.getTimeStamp() + "; seq: " + + eventSequence); + } + ++eventSequence; + } + } +} Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/IParameterConverter.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/IParameterConverter.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/IParameterConverter.java Tue Nov 3 11:32:25 2015 @@ -5,22 +5,25 @@ /** * Interface for converting from XML to typed objects for input to LCSim Drivers. + * * @author jeremym */ -public interface IParameterConverter -{ - /** - * This method returns true if the converter can handle the given type. - * @param propertyType The class of the parameter. - * @return True if converter handles the given type; False if no. - */ - public boolean handles(Class propertyType); - - /** - * Convert an XML element parameter to a specific type and return as an Object. - * @param factory The expression factory to be used for variable evaluation. - * @param parameterElement The XML parameter data. - * @return Parameter converted to specific type. Returned as generic object. - */ - public Object convert(JDOMExpressionFactory factory, Element parameterElement); +public interface IParameterConverter { + + /** + * This method returns true if the converter can handle the given type. + * + * @param propertyType The class of the parameter. + * @return True if converter handles the given type; False if no. + */ + public boolean handles(Class propertyType); + + /** + * Convert an XML element parameter to a specific type and return as an Object. + * + * @param factory The expression factory to be used for variable evaluation. + * @param parameterElement The XML parameter data. + * @return Parameter converted to specific type. Returned as generic object. + */ + public Object convert(JDOMExpressionFactory factory, Element parameterElement); } Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlDriver.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlDriver.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlDriver.java Tue Nov 3 11:32:25 2015 @@ -6,51 +6,42 @@ import org.lcsim.util.Driver; /** - * Extend this driver to run an xml recon job in JAS3. - * Your driver needs to call <code>super.process()</code> - * in its <code>process()</code> method for this to work. + * Extend this driver to run an xml recon job in JAS3. Your driver needs to call <code>super.process()</code> in its + * <code>process()</code> method for this to work. The only sections that will be used are <drivers>, <execute>, + * <control>, and <define>. The <code>numberOfEvents</code> parameter will be ignored. Non-standard jar files need to be + * added to the ~/.JAS3/extensions directory. * - * The only sections that will be used are <drivers>, - * <execute>, <control>, and <define>. The <code>numberOfEvents</code> - * parameter will be ignored. Non-standard jar files need - * to be added to the ~/.JAS3/extensions directory. - * * @author jeremym */ -public class JobControlDriver extends Driver -{ - JobControlManager mgr = new JobControlManager(); - - private boolean wasSetup = false; - - public JobControlDriver() - {} - - public JobControlDriver(File steering) - { - setup(steering); - } - - public void setup(File steering) - { - if (wasSetup) - return; - else - wasSetup = true; - mgr.setup(steering); - for (Driver driver : mgr.getDriverExecList()) - { - this.add(driver); - } - } - - public JobControlManager getManager() - { - return this.mgr; - } +public class JobControlDriver extends Driver { - public void process(EventHeader event) - { - super.process(event); - } + JobControlManager mgr = new JobControlManager(); + + private boolean wasSetup = false; + + public JobControlDriver() { + } + + public JobControlDriver(File steering) { + setup(steering); + } + + public void setup(File steering) { + if (wasSetup) + return; + else + wasSetup = true; + mgr.setup(steering); + for (Driver driver : mgr.getDriverExecList()) { + this.add(driver); + } + } + + public JobControlManager getManager() { + return this.mgr; + } + + public void process(EventHeader event) { + super.process(event); + } } Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlManager.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlManager.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/JobControlManager.java Tue Nov 3 11:32:25 2015 @@ -10,7 +10,6 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; -import java.io.PrintStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; @@ -38,7 +37,6 @@ import org.apache.commons.cli.PosixParser; import org.freehep.record.loop.RecordEvent; import org.jdom.Attribute; -import org.jdom.DataConversionException; import org.jdom.Document; import org.jdom.Element; import org.jdom.Text; @@ -46,13 +44,12 @@ import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import org.lcsim.conditions.ConditionsManager; -import org.lcsim.conditions.ConditionsManager.ConditionsSetNotFoundException; +import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException; import org.lcsim.event.EventHeader; import org.lcsim.units.Constants; import org.lcsim.util.Driver; import org.lcsim.util.DriverAdapter; import org.lcsim.util.cache.FileCache; -import org.lcsim.util.log.LogUtil; import org.lcsim.util.loop.LCIOEventSource; import org.lcsim.util.loop.LCSimConditionsManagerImplementation; import org.lcsim.util.loop.LCSimLoop; @@ -61,443 +58,1021 @@ /** * <p> - * This class provides a frontend for running and managing LCSim jobs using an XML steering format. - * </p> + * This class provides a front end for running and managing LCSim event processing jobs using XML steering files. * <p> - * The command line syntax is: - * </p> + * More details about this XML format can be found at the<br/> + * <a href="https://confluence.slac.stanford.edu/display/ilc/lcsim+xml">LCSim XML Confluence Page</a>. * <p> - * <code>java -jar ./lib/lcsim-bin.jar steeringFile.xml [options]</code> - * </p> + * The command line syntax is:<br/> + * <code>java org.lcsim.job.JobManager steeringFile.xml [options]</code> * <p> * To see the available command line options with descriptions, run with "-h" as the only option. - * </p> * <p> - * In the XML file, variables have the format <code>${variableName}</code>. No nested variable - * definitions are allowed (e.g. no variables within variables). - * </p> - * <p> - * Command-line parameters that can be defined using switches are overridden by the corresponding - * settings in the job XML file, if they are present. This means that if these parameters are to be - * taken from the CL, the matching settings should be left out of the XML job file. This is not the - * case, however, for input files specified by the "-i" option, which are appended to the ones - * listed in the steering file. - * </p> - * + * Command-line parameters that can be defined using switches are overridden by the corresponding settings in the job + * XML file, if they are present. This means that if these parameters are to be taken from the CL, the matching settings + * should be left out of the XML job file. This is not the case, however, for input files specified by the "-i" option, + * which are appended to the ones listed in the steering file. + * * @version $Id: JobControlManager.java,v 1.65 2013/02/14 22:30:07 jeremy Exp $ * @author Jeremy McCormick */ -// FIXME: Command line arguments should probably override the steering file when applicable. @SuppressWarnings({"unchecked", "rawtypes"}) public class JobControlManager { - protected static Logger logger = LogUtil.create(JobControlManager.class); - static { - logger.setLevel(Level.ALL); - } - - // The LCIO record loop. - protected LCSimLoop loop; - - // Driver management. - protected Map<String, Driver> driverMap = new LinkedHashMap<String, Driver>(); - protected List<Driver> driverExec = new ArrayList<Driver>(); - protected Map<String, String> availableDrivers = new HashMap<String, String>(); - - // Run parameters. - protected List<File> inputFiles = new ArrayList<File>(); - protected int numberOfEvents = -1; - protected int skipEvents = -1; - protected File rewriteFile; - - // Variables and constants. - protected Map<String, String> variableMap = new HashMap<String, String>(); - protected Map<String, Double> constantsMap = new HashMap<String, Double>(); - - // Boolean job options. - protected boolean performDryRun; - protected boolean rewrite; - protected boolean useSteeringResource; - protected boolean dummyDetector; - - // Settings effecting logging verbosity. - protected boolean printInputFiles; - protected boolean printDriverStatistics; - protected boolean printSystemProperties; - protected boolean printUserClassPath; - protected boolean printDriversDetailed; - // private boolean printVersion; - protected boolean verbose; - - // File caching. - protected File cacheDirectory; - protected FileCache fileCache; // Start with default dir. - - // True once job parameters are setup from CL and XML. - protected boolean wasSetup; - - // The manager's class loader. - protected ClassLoader loader; - - // The log stream for messages. - protected PrintStream logStream = System.out; - - // Root node of XML job file. - protected Element root; - - // XML utils. - protected JDOMExpressionFactory factory = new JDOMExpressionFactory(); - protected ParameterConverters paramConverter = new ParameterConverters(factory); - - // Command line options. - protected static final Options options = createCommandLineOptions(); - - // Reg exp to extract variables from a string with the form "${varName}". - protected static final Pattern varPattern = Pattern.compile("[$][{][a-zA-Z_-]*[}]"); - - protected DriverAdapter driverAdapter = null; + /** + * Initialize the logger which uses the package name. + */ + protected static final Logger LOGGER = Logger.getLogger(JobControlManager.class.getPackage().getName()); + + /** + * The command line options. + */ + private static final Options OPTIONS = createCommandLineOptions(); + + /** + * The regular expression for extracting the variables from an XML file. + */ + private static final Pattern VARIABLE_PATTERN = Pattern.compile("[$][{][a-zA-Z_-]*[}]"); + + /** + * Create the command line options. + * + * @return The command line options for the manager. + */ + private static Options createCommandLineOptions() { + final Options options = new Options(); + options.addOption(new Option("p", "properties", true, "Load a properties file containing variable definitions")); + options.addOption(new Option("D", "define", true, "Define a variable with form [name]=[value]")); + options.addOption(new Option("w", "rewrite", true, "Rewrite the XML file with variables resolved")); + options.addOption(new Option("s", "skip", true, "Set the number of events to skip")); + options.addOption(new Option("n", "nevents", true, "Set the max number of events to process")); + options.addOption(new Option("x", "dry-run", false, "Perform a dry run which does not process events")); + options.addOption(new Option("i", "input-file", true, "Add an LCIO input file to process")); + options.addOption(new Option("r", "resource", false, "Use a steering resource rather than a file")); + options.addOption(new Option("b", "batch", false, "Run in batch mode in which plots will not be shown.")); + options.addOption(new Option("e", "event-print", true, "Event print interval")); + options.addOption(new Option("d", "detector", true, "user supplied detector name (careful!)")); + options.addOption(new Option("R", "run", true, "user supplied run number (careful!)")); + return options; + } + + /** + * Get the Java primitive type class from a type name. + * + * @param name The name of the type. + * @return The primitive type class. + */ + private static Class getPrimitiveType(final String name) { + if (name.equals("byte")) { + return byte.class; + } + if (name.equals("short")) { + return short.class; + } + if (name.equals("int")) { + return int.class; + } + if (name.equals("long")) { + return long.class; + } + if (name.equals("char")) { + return char.class; + } + if (name.equals("float")) { + return float.class; + } + if (name.equals("double")) { + return double.class; + } + if (name.equals("boolean")) { + return boolean.class; + } + if (name.equals("String")) { + return String.class; + } + return null; + } + + /** + * Run from the command line. + * <p> + * Takes command-line options (use -h option to see them). + * + * @param args the command line arguments + */ + public static void main(final String args[]) { + final JobControlManager mgr = new JobControlManager(); + mgr.parse(args); + mgr.run(); + } + + /** + * Print help and exit. + */ + private static void printHelp() { + LOGGER.info("java " + JobControlManager.class.getCanonicalName() + " [options] steeringFile.xml"); + final HelpFormatter help = new HelpFormatter(); + help.printHelp(" ", OPTIONS); + System.exit(1); + } + + /** + * Root directory for file caching. + */ + private File cacheDirectory; + + /** + * The class loader that will be used for the job. + */ + private ClassLoader classLoader; + + /** + * Command line that is setup from <code>main</code> arguments. + */ + CommandLine commandLine = null; + + /** + * Map of constants definitions. + */ + private final Map<String, Double> constantsMap = new HashMap<String, Double>(); + + /** + * User supplied detector name. + */ + private String detectorName = null; + + /** + * A driver adapter created on the fly in case it is needed by an external program. + */ + private DriverAdapter driverAdapter = null; + + /** + * List of drivers to execute in the job. + */ + private final List<Driver> driverExec = new ArrayList<Driver>(); + + /** + * Map of driver names to objects. + */ + private final Map<String, Driver> driverMap = new LinkedHashMap<String, Driver>(); + + /** + * Enable dry run so no events are processed. + */ + private boolean dryRun; + + /** + * Setup a "dummy" detector in the conditions system. + */ + private boolean dummyDetector; + + /** + * Event printing interval (null means no event printing). + */ + private Long eventPrintInterval = null; + + /** + * JDOM expression factory for variables. + */ + private final JDOMExpressionFactory factory = new JDOMExpressionFactory(); + + /** + * File cache. + */ + private FileCache fileCache; // Start with default dir. + + /** + * List of input LCIO files. + */ + private final List<File> inputFiles = new ArrayList<File>(); + + /** + * Flag set to <code>true</code> after setup is performed. + */ + private boolean isSetup; + + /** + * The job end timestamp in ms. + */ + private long jobEnd = 0; + + /** + * The job start timestamp in ms. + */ + private long jobStart = 0; + + /** + * The LCIO record loop. + */ + private LCSimLoop loop; + + /** + * Number of events to run before stopping job. + */ + private int numberOfEvents = -1; + + /** + * Helper for converting Driver parameters. + */ + private final ParameterConverters paramConverter = new ParameterConverters(factory); + + /** + * Set to <code>true</code> to print out driver statistics at the end of the job. + */ + private boolean printDriverStatistics; + + /** + * Path for rewriting steering file with variables resolved. + */ + private File rewriteFile; + + /** + * Enable rewriting of the steering file to a new path with variables resolved. + */ + private boolean rewriteSteering; + + /** + * The root node of the XML document providing config to the manager. + */ + private Element root; + + /** + * User supplied run number. + */ + private Integer runNumber = null; + + /** + * Number of events to skip at start of job. + */ + private int skipEvents = -1; + + /** + * Interpret steering file argument as a resource rather than file path. + */ + private boolean useSteeringResource; + + /** + * Map of variable names to their values. + */ + private final Map<String, String> variableMap = new HashMap<String, String>(); /** * The default constructor. */ public JobControlManager() { + try { fileCache = new FileCache(); - } catch (IOException x) { + } catch (final IOException x) { throw new RuntimeException(x); } + + // FIXME: Should this instead be done when the job is started? LCSimConditionsManagerImplementation.register(); } /** - * Run the manager using a main. Takes command-line options (use -h option to see them). - * @param args The command line arguments. - */ - public static void main(String args[]) { - JobControlManager mgr = new JobControlManager(); - mgr.run(args); - } - - public void run(String args[]) { - if (args.length == 0) { - logStream.println("java -jar lcsim-bin.jar [options] steeringFile.xml"); - HelpFormatter help = new HelpFormatter(); - help.printHelp(" ", options); - System.exit(1); - } - parseCommandLineOptions(args); - run(); - } - - /** - * Create the command line options. - * - * @return The command line options for the manager. - */ - private static Options createCommandLineOptions() { - Options options = new Options(); - options.addOption(new Option("p", true, "Load a properties file containing variable definitions")); - options.addOption(new Option("D", true, "Define a variable with form [name]=[value]")); - options.addOption(new Option("w", true, "Rewrite the XML file with variables resolved")); - options.addOption(new Option("v", false, "Turn on verbose mode")); - options.addOption(new Option("s", true, "Set the number of events to skip")); - options.addOption(new Option("n", true, "Set the max number of events to process")); - options.addOption(new Option("x", false, "Perform a dry run which does not process events")); - options.addOption(new Option("q", false, "Turn on quiet mode")); - options.addOption(new Option("i", true, "Add an LCIO input file to process")); - options.addOption(new Option("r", false, "Use a steering resource rather than a file")); - options.addOption(new Option("b", false, "Run in headless mode in which plots will not be shown.")); - return options; - } - - /** - * Parse command-line options and setup job state from them. This method calls - * {@link #setup(File)} to load the steering paramters from an XML file, after processing other - * command line options. This method is private so that callers must all use the - * {@link #main(String[])} routine as the primary entry point. - * - * @param args The command line arguments. - */ - private void parseCommandLineOptions(String args[]) { - // Setup parser. - CommandLineParser parser = new PosixParser(); - - // CommandLine to be loaded. - CommandLine cl = null; - - // Parse CL arguments. - try { - cl = parser.parse(options, args); - } catch (ParseException x) { - throw new RuntimeException("Problem parsing command line options.", x); - } - - // Turn on verbose mode. This setting may be overridden by control section from XML. - if (cl.hasOption("v")) { - setVerbose(true); - } - // Turn on quiet mode. If "-v" is also specified, this will have no effect. - else if (cl.hasOption("q")) { - setVerbose(false); - } - - // Load properties file containing variable definitions. - if (cl.hasOption("p")) { - String[] propValues = cl.getOptionValues("p"); - for (String propFileName : propValues) { - InputStream in = null; - try { - in = new FileInputStream(propFileName); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - Properties props = new Properties(); - try { - props.load(in); - } catch (IOException e) { - throw new RuntimeException(e); - } - for (Entry<Object, Object> entry : props.entrySet()) { - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - addVariableDefinition(key, value); - } - } - } - - // Process user variable definitions. - if (cl.hasOption("D")) { - String[] defValues = cl.getOptionValues("D"); - for (String def : defValues) { - String[] s = def.split("="); - if (s.length != 2) { - throw new RuntimeException("Bad variable format: " + def); - } - String key = s[0]; - String value = s[1]; - addVariableDefinition(key, value); - } - } - - // Rewrite XML file with variables resolved. - if (cl.hasOption("w")) { - this.rewrite = true; - String rewritePath = cl.getOptionValue("w"); - this.rewriteFile = new File(rewritePath); - if (this.rewriteFile.exists()) { - throw new RuntimeException("Rewrite file already exists: " + rewritePath); - } - } - - // Set max number of events to run. - if (cl.hasOption("n")) { - this.numberOfEvents = Integer.valueOf(cl.getOptionValue("n")); - } - - // Set number of events to skip. - if (cl.hasOption("s")) { - this.skipEvents = Integer.valueOf(cl.getOptionValue("s")); - } - - // Perform a dry run, not processing any events but doing job setup. - if (cl.hasOption("x")) { - this.performDryRun = true; - } - - if (cl.hasOption("r")) { - this.useSteeringResource = true; - } - - // Check that there is exactly one extra argument for the XML steering file. - if (cl.getArgList().size() == 0) { - throw new RuntimeException("Missing LCSim XML file argument."); - } else if (cl.getArgList().size() > 1) { - throw new RuntimeException("Too many extra arguments."); - } - - // Local LCIO files to process. - if (cl.hasOption("i")) { - String[] files = cl.getOptionValues("i"); - for (String fileName : files) { - File file = new File(fileName); - if (!file.exists()) { - throw new RuntimeException("File given as command line option does not exist: " + fileName); - } - inputFiles.add(new File(fileName)); - } - } - - // Run in headless mode in which plots will not show. - if (cl.hasOption("b")) { - enableHeadlessMode(); - } - - // Steering argument points to either a file or embedded resource. - String steering = (String) cl.getArgList().get(0); - - // Using an embedded resource in the jar for steering. - if (this.useSteeringResource) { - setup(steering); - // Steering from a local file. - } else { - File xmlRunControlFile = new File(steering); - if (!xmlRunControlFile.exists()) { - throw new RuntimeException("The steering file " + args[0] + " does not exist!"); - } - setup(xmlRunControlFile); - } - } - - public void enableHeadlessMode() { - System.setProperty("hep.aida.IAnalysisFactory", BatchAnalysisFactory.class.getName()); - } - - /** - * Add a variable definition to be substituted into the job's XML file. This method is public - * so that caller's not using the CL can still define necessary variables for the steering - * file. - * + * Add a Driver to the internal Driver map. + * + * @param name the unique name of the Driver + * @param driver the instance of the Driver + */ + private void addDriver(final String name, final Driver driver) { + if (driverMap.containsKey(name)) { + throw new RuntimeException("Duplicate driver name: " + name); + } + driverMap.put(name, driver); + } + + /** + * Add an input LCIO file to be proceesed. + * + * @param inputFile The input LCIO file. + */ + public void addInputFile(final File inputFile) { + if (isSetup) { + throw new RuntimeException("Input files cannot be added when manager has already been setup."); + } + inputFiles.add(inputFile); + } + + /** + * Add a variable definition to be substituted into the job's XML file. This method is public so that caller's not + * using the CL can still define necessary variables for the steering file. + * * @param key The variable name. * @param value The variable's value. */ - public void addVariableDefinition(String key, String value) { - if (verbose) { - logStream.println(key + " = " + value); - } + public void addVariableDefinition(final String key, final String value) { + LOGGER.config(key + " = " + value); if (!this.variableMap.containsKey(key)) { variableMap.put(key, value); } else { throw new RuntimeException("Duplicate variable definition: " + key); } } - - /** - * Add an input LCIO file to be proceesed. - * @param inputFile The input LCIO file. - */ - public void addInputFile(File inputFile) { - if (wasSetup) { - throw new RuntimeException("Input files cannot be added when manager has already been setup."); - } - inputFiles.add(inputFile); + + /** + * Configure start of job (usually done automatically). + */ + public void configure() { + this.getDriverAdapter().start(null); + } + + /** + * Create a driver adapter. + */ + private void createDriverAdapter() { + if (this.isSetup == false) { + throw new IllegalStateException("The job manager was never setup."); + } + final Driver topDriver = new Driver(); + for (final Driver driver : this.getDriverExecList()) { + topDriver.add(driver); + } + driverAdapter = new DriverAdapter(topDriver); + } + + /** + * Create the <code>Driver</code> execution list. + */ + private void createDriverExecList() { + // Make a list of Drivers to be executed. + final List<Element> exec = root.getChild("execute").getChildren("driver"); + for (final Element execDriver : exec) { + final String driverName = execDriver.getAttributeValue("name"); + final Driver driverFind = driverMap.get(driverName); + if (driverFind != null) { + driverExec.add(driverFind); + } else { + throw new RuntimeException("A Driver called " + driverName + " was not found."); + } + } + + // Add the drivers to the LCSimLoop. + for (final Driver driver : driverExec) { + loop.add(driver); + } + } + + /** + * Turn on the batch analysis factory so plots are not shown on the screen even when <code>Plotter.show()</code> is + * called. + */ + public void enableHeadlessMode() { + System.setProperty("hep.aida.IAnalysisFactory", BatchAnalysisFactory.class.getName()); + } + + /** + * Activate end of job hooks (usually done automatically). + */ + public void finish() { + this.getDriverAdapter().finish(null); + } + + /** + * Get a <code>DriverAdapter</code> from the currently configured Driver list. + * + * @return the driver adapter + */ + public DriverAdapter getDriverAdapter() { + if (driverAdapter == null) { + // Driver adapter created on demand. + this.createDriverAdapter(); + } + return driverAdapter; + } + + /** + * Return a list of Drivers to be executed. This can be used from an external framework like JAS3. The list will be + * empty unless the <code>setup()</code> method has been called. + * + * @return A <code>List</code> of <code>Drivers</code>. + */ + public List<Driver> getDriverExecList() { + return this.driverExec; + } + + /** + * Get the <code>LCSimLoop</code> of this JobManager. + * + * @return The LCSimLoop. + */ + public LCSimLoop getLCSimLoop() { + return loop; + } + + /** + * Get a list of a class's setter methods. + * + * @param klass The class. + * @return A list of setter methods. + */ + private List<Method> getSetterMethods(final Class klass) { + final List<Method> methods = new ArrayList<Method>(); + Class currentClass = klass; + while (currentClass != null) { + for (final Method method : currentClass.getMethods()) { + if (method.getName().startsWith("set") && !methods.contains(method)) { + methods.add(method); + } + } + currentClass = currentClass.getSuperclass(); + } + return methods; + } + + /** + * Initialize the <code>LCSimLoop</code>. + */ + private void initializeLoop() { + LOGGER.config("initializing LCSim loop"); + loop = new LCSimLoop(); + if (this.eventPrintInterval != null) { + loop.addRecordListener(new EventPrintLoopAdapter(this.eventPrintInterval)); + LOGGER.config("enabled event marker printing with interval " + eventPrintInterval); + } else { + LOGGER.config("no event printing enabled"); + } + } + + /** + * Parse command-line options and setup job state from them. This method calls {@link #setup(File)} to load the + * steering paramters from an XML file, after processing other command line options. This method is private so that + * callers must all use the {@link #main(String[])} routine as the primary entry point. + * + * @param args The command line arguments. + */ + public void parse(final String args[]) { + + LOGGER.config("parsing command line arguments"); + + // Print help and exit. + if (args.length == 0) { + printHelp(); + } + + // Setup parser. + final CommandLineParser parser = new PosixParser(); + + // Parse the command line arguments. + try { + commandLine = parser.parse(OPTIONS, args); + } catch (final ParseException x) { + throw new RuntimeException("Problem parsing command line options.", x); + } + + // Load a properties file containing variable definitions. + if (commandLine.hasOption("p")) { + final String[] propValues = commandLine.getOptionValues("p"); + for (final String propFileName : propValues) { + InputStream in = null; + try { + in = new FileInputStream(propFileName); + } catch (final FileNotFoundException e) { + throw new RuntimeException(e); + } + final Properties props = new Properties(); + try { + props.load(in); + } catch (final IOException e) { + throw new RuntimeException(e); + } + for (final Entry<Object, Object> entry : props.entrySet()) { + final String key = (String) entry.getKey(); + final String value = (String) entry.getValue(); + this.addVariableDefinition(key, value); + } + LOGGER.config("loaded variable definitions from " + propFileName); + } + } + + // Process the user variable definitions. + if (commandLine.hasOption("D")) { + final String[] defValues = commandLine.getOptionValues("D"); + for (final String def : defValues) { + final String[] s = def.split("="); + if (s.length != 2) { + throw new RuntimeException("Bad variable format: " + def); + } + final String key = s[0]; + final String value = s[1]; + this.addVariableDefinition(key, value); + LOGGER.config("defined " + key + " = " + value); + } + } + + // Rewrite XML file with variables resolved. + if (commandLine.hasOption("w")) { + this.rewriteSteering = true; + final String rewritePath = commandLine.getOptionValue("w"); + this.rewriteFile = new File(rewritePath); + if (this.rewriteFile.exists()) { + throw new RuntimeException("Rewrite file already exists: " + rewritePath); + } + LOGGER.config("XML will be rewritten to " + this.rewriteFile.getPath()); + } + + // Set max number of events to run. + if (commandLine.hasOption("n")) { + this.numberOfEvents = Integer.valueOf(commandLine.getOptionValue("n")); + LOGGER.config("max number of events set to " + this.numberOfEvents); + } + + // Set number of events to skip. + if (commandLine.hasOption("s")) { + this.skipEvents = Integer.valueOf(commandLine.getOptionValue("s")); + LOGGER.config("skip events set to " + this.skipEvents); + } + + // Perform a dry run, not processing any events but doing job setup. + if (commandLine.hasOption("x")) { + this.dryRun = true; + LOGGER.config("dry run is enabled"); + } + + // Interpret steering argument as a resource rather than file path. + if (commandLine.hasOption("r")) { + this.useSteeringResource = true; + LOGGER.config("steering resource enabled"); + } + + // Check that there is exactly one extra argument for the XML steering file. + if (commandLine.getArgList().size() == 0) { + throw new RuntimeException("Missing LCSim XML file argument."); + } else if (commandLine.getArgList().size() > 1) { + throw new RuntimeException("Too many extra arguments."); + } + + // Local LCIO files to process. + if (commandLine.hasOption("i")) { + final String[] files = commandLine.getOptionValues("i"); + for (final String fileName : files) { + final File file = new File(fileName); + if (!file.exists()) { + throw new RuntimeException("File given as command line option does not exist: " + fileName); + } + inputFiles.add(new File(fileName)); + LOGGER.config("added input file " + fileName); + } + } + + // Run in headless mode in which plots will not show. + if (commandLine.hasOption("b")) { + this.enableHeadlessMode(); + LOGGER.config("headless mode enabled"); + } + + // Steering argument points to either a file or embedded resource. + final String steering = (String) commandLine.getArgList().get(0); + + if (commandLine.hasOption("e")) { + this.eventPrintInterval = Long.parseLong(commandLine.getOptionValue("e")); + LOGGER.config("eventPrintInterval: " + this.eventPrintInterval); + if (this.eventPrintInterval <= 0) { + throw new IllegalArgumentException("The event print interval must be > 0."); + } + } + + if (commandLine.hasOption("d")) { + this.detectorName = commandLine.getOptionValue("d"); + LOGGER.config("detector: " + this.detectorName); + } + + if (commandLine.hasOption("R")) { + this.runNumber = Integer.parseInt(commandLine.getOptionValue("R")); + LOGGER.config("runNumber: " + this.runNumber); + } + + if (this.detectorName != null && this.runNumber == null || + this.runNumber != null && this.detectorName == null) { + throw new IllegalArgumentException("The detector name and run number must be given together."); + } + + // This will actually initialize everything from the steering file so it must come last. + if (this.useSteeringResource) { + // Using an embedded resource in the jar for steering. + LOGGER.config("initializing from steering resource " + steering); + this.setup(steering); + } else { + // Steering from a local file. + final File xmlRunControlFile = new File(steering); + if (!xmlRunControlFile.exists()) { + throw new RuntimeException("The steering file " + args[0] + " does not exist!"); + } + LOGGER.config("initializing from steering file " + xmlRunControlFile.getPath()); + this.setup(xmlRunControlFile); + } + } + + /** + * Print the list of input files. + */ + private void printInputFileList() { + final StringBuffer sb = new StringBuffer(); + sb.append('\n'); + sb.append("--- Input Files ---"); + for (final File file : inputFiles) { + sb.append(file.getAbsolutePath()); + sb.append('\n'); + } + LOGGER.config(sb.toString()); + } + + /** + * Print out extra URLs added to the classpath from the XML. + */ + private void printUserClasspath() { + final StringBuffer sb = new StringBuffer(); + final URL[] urls = ((URLClassLoader) classLoader).getURLs(); + if (urls.length > 0) { + for (final URL url : ((URLClassLoader) classLoader).getURLs()) { + sb.append(url + " "); + } + sb.append('\n'); + LOGGER.config("Extra classpath URLs:" + sb.toString()); + } + } + + /** + * Create the constants from the XML file. + */ + private void processConstants() { + final Element define = root.getChild("define"); + if (define != null) { + for (final Object o : define.getChildren()) { + final Element e = (Element) o; + final Text txt = (Text) e.getContent().get(0); + final double dval = factory.computeDouble(txt.getValue()); + this.constantsMap.put(e.getName(), dval); + factory.addConstant(e.getName(), dval); + } + } + } + + /** + * A fairly ugly method to process the provided XML parameters on a <code>Driver</code>. + * + * @param driverClass the Java class of the driver + * @param newDriver the instantiated Driver + * @param parameters the list of XML parameters + */ + private void processDriverParameters(final Class driverClass, final Driver newDriver, final List<Element> parameters) { + // Process the parameter elements. + for (final Element parameterElement : parameters) { + + // The parameter's setter method that we will try to find. + Method setter = null; + + // The parameter's type that will be inferred from the method or provided by an XML attribute. + Class propertyType = null; + + // Get the parameter's name. + final String pname = parameterElement.getName(); + + // Find setter methods that look like good matches for this parameter. + final List<Method> methods = this.getSetterMethods(driverClass); + final List<Method> methodCandidates = new ArrayList<Method>(); + for (final Method method : methods) { + String propHack = method.getName().replaceFirst("set", ""); + propHack = propHack.substring(0, 1).toLowerCase() + propHack.substring(1); + if (propHack.equals(pname)) { + methodCandidates.add(method); + } + } + if (methodCandidates.size() == 1) { + // Found the single setter method so try to use it. + setter = methodCandidates.get(0); + if (setter.getParameterTypes().length > 1) { + throw new RuntimeException("The set method has too many arguments for parameter: " + pname); + } + propertyType = setter.getParameterTypes()[0]; + } else if (methodCandidates.size() > 1) { + // Found several, overloaded methods. Try to disambiguate them if possible. + if (parameterElement.getAttribute("type") == null) { + throw new RuntimeException("Parameter " + pname + " in Driver " + driverClass.getCanonicalName() + + " is overloaded, but a type field is missing from the parameter's XML element."); + } + try { + // Try a primitive type first. + propertyType = getPrimitiveType(parameterElement.getAttribute("type").getValue()); + + // If type is null, then parameter is an Object and not a primitive, or it + // is not a valid type. + if (propertyType == null) { + propertyType = Class.forName(parameterElement.getAttribute("type").getValue()); + } + } catch (final ClassNotFoundException x) { + throw new RuntimeException("Bad type " + parameterElement.getAttribute("type").getValue() + + " given for parameter " + pname + "."); + } + // Find a method that matches the user type. + for (final Method candidateMethod : methodCandidates) { + if (candidateMethod.getParameterTypes().length == 1 + && candidateMethod.getParameterTypes()[0].equals(propertyType)) { + setter = candidateMethod; + break; + } + } + } else if (methodCandidates.size() == 0) { + // No method found. The parameter name is probably invalid. + throw new RuntimeException("Set method for Driver parameter " + pname + " was not found."); + } + + // No setter method found. + if (setter == null) { + throw new RuntimeException("Unable to find set method for parameter " + pname + "."); + } + + // Convert the parameter to the appropriate type. + final IParameterConverter converter = paramConverter.getConverterForType(propertyType); + if (converter == null) { + throw new RuntimeException("No converter found for parameter " + parameterElement.getName() + + " with type " + propertyType.getName() + "."); + } + final Object nextParameter = converter.convert(factory, parameterElement); + + // Call the setter with the parameter as argument. + final Object pargs[] = new Object[1]; + pargs[0] = nextParameter; + try { + // This invokes the setter method of the driver. + setter.invoke(newDriver, pargs); + + // Print parameters and values as they are set. + LOGGER.fine(" " + pname + " = " + parameterElement.getText().trim()); + + } catch (final Exception x) { + throw new RuntimeException("Problem processing parameter " + parameterElement.getName() + ".", x); + } + } // parameter loop + } + + /** + * Process a single event. + * + * @param event + */ + public void processEvent(final EventHeader event) { + this.getDriverAdapter().recordSupplied(new RecordEvent(loop, event)); + } + + /** + * Create a <code>File</code> object from the text in an XML element. + * + * @param fileElement The element containing a file path or URL. + * @param fileList List to append new <code>File</code>. + * @return The <code>File</code> object. + */ + private File processFileElement(final Element fileElement, final List<File> fileList) { + + final String fileLoc = this.processPath(fileElement.getText().trim()); + File file = null; + + // Try to process the file text as a URL. + try { + final URL fileURL = new URL(fileLoc); + + // Local file URL. + if (fileLoc.startsWith("file:")) { + file = new File(fileURL.getPath()); + } else { + // Remote file URL. + try { + file = this.fileCache.getCachedFile(fileURL); + } catch (final IOException x) { + throw new RuntimeException("Unable to fetch file " + fileLoc + " to the cache directory.", x); + } + } + } catch (final MalformedURLException x) { + // Interpret as local file. + file = new File(fileLoc); + } + + // Add to the list. + if (fileList != null) { + fileList.add(file); + } + + return file; + } + + /** + * Cleanup file text and add to the file list. + * + * @param fileText the text containing the file's path from the XML + * @param fileList the list of files to which the new file will be appended + * @return the file that was created from the text + */ + private File processFileText(final String fileText, final List<File> fileList) { + final Element fileElement = new Element("file"); + fileElement.setText(fileText.trim()); + return this.processFileElement(fileElement, fileList); + } + + /** + * Process the file path string to substitute in the user's home directory for the "~" character. This is needed if + * running on Windows. + * + * @param path The original path. + * @return The path with home dir substitution. + */ + private String processPath(final String path) { + if (path.startsWith("~")) { + return path.replaceFirst("~", System.getProperty("user.home")); + } else { + return path; + } + } + + /** + * Rewrite the XML steering file after resolving variables (done externally). The output path is set by command line + * argument to "-w". + * + * @param doc The XML steering doc with variables substituted. + */ + private void rewriteXMLSteering(final Document doc) { + LOGGER.info("Rewriting XML to " + this.rewriteFile); + final XMLOutputter outputter = new XMLOutputter(); + outputter.setFormat(Format.getPrettyFormat()); + try { + final FileOutputStream out = new FileOutputStream(this.rewriteFile); + outputter.output(doc, out); + out.close(); + } catch (final Exception e) { + throw new RuntimeException(e); + } } /** * Execute a job using the current parameters. */ public boolean run() { - + // If setup was not called first, then abort the job. - if (!wasSetup) { - logStream.println("Aborting job! Setup was never called."); + if (!isSetup) { + LOGGER.info("Aborting job! Setup was never called."); return false; } - // Dry run selected. No events will be processed here. - if (performDryRun) { - logStream.println("Executed dry run. No events processed!"); + // Dry run selected. No events will be processed. + if (dryRun) { + LOGGER.info("Executed dry run. No events processed!"); return false; } - + + boolean okay = true; + try { // Add the LCIO files to the loop. loop.setLCIORecordSource(new LCIOEventSource(this.getClass().getSimpleName(), inputFiles)); + + // Set up user supplied conditions information (already checked that these were both given if one was used). + initializeConditions(); // Setup dummy detector if selected. if (dummyDetector) { - logStream.println("Using dummy detector for conditions system!"); + LOGGER.info("Using dummy detector for conditions system!"); loop.setDummyDetector("dummy"); } - // Driver statistics printout. - PrintStream statsStream = null; - if (printDriverStatistics) { - statsStream = logStream; - } - - if (verbose) { - logStream.println("Start time: " + (new Date())); - logStream.println(); - } - - if (skipEvents > 0) { - if (verbose) { - logStream.println("Skipping " + skipEvents + " events."); - } + this.jobStart = System.currentTimeMillis(); + + LOGGER.info("Job started: " + new Date(jobStart)); + + if (this.skipEvents > 0) { + LOGGER.info("Skipping " + skipEvents + " events."); loop.skip(skipEvents); } // Execute the loop. - long processedEvents = loop.loop(numberOfEvents, statsStream); - if (processedEvents != numberOfEvents) { - logStream.println("End of file reached"); - } else if (verbose) { - logStream.println(); - logStream.println("End time: " + (new Date())); - } - } - // Catch other fatal exceptions thrown from the loop. - catch (Exception e) { - throw new RuntimeException(e); - } - - // Cleanup the loop. - loop.dispose(); - //loop = null; - - // Job was successful. - return true; - } - - /** - * Get the <code>LCSimLoop</code> of this JobManager. - * - * @return The LCSimLoop. - */ - public LCSimLoop getLCSimLoop() { - return loop; - } - - /** - * Return a list of Drivers to be executed. This can be used from an external framework like - * JAS3. The list will be empty unless the <code>setup()</code> method has been called. - * - * @return A <code>List</code> of <code>Drivers</code>. - */ - public List<Driver> getDriverExecList() { - return this.driverExec; + final long processedEvents = loop.loop(numberOfEvents, this.printDriverStatistics ? System.out : null); + if (numberOfEvents != -1 && processedEvents != numberOfEvents) { + LOGGER.info("End of file was reached."); + } + LOGGER.info("Job processed " + processedEvents + " events."); + this.jobEnd = System.currentTimeMillis(); + + LOGGER.info("Job ended: " + new Date(this.jobEnd)); + final long elapsed = this.jobStart - this.jobEnd; + LOGGER.info("Job took " + elapsed + " which is " + elapsed / processedEvents + " ms/event."); + + } catch (final Exception e) { + LOGGER.log(Level.SEVERE, "A fatal error occurred during the job.", e); + okay = false; + } finally { + try { + // Dispose of the loop. + loop.dispose(); + } catch (final Exception e) { + LOGGER.log(Level.WARNING, "An error occurred during job cleanup.", e); + okay = false; + } + } + + // Return true or false depending on whether job was successfully executed. + return okay; + } + + /** + * Set whether a dry run should be performed which will only perform setup and not process any events. + * + * @param dryRun <code>true</code> to enable a dry run + */ + public void setDryRun(final boolean dryRun) { + this.dryRun = dryRun; + } + + /** + * Set the number of events to run on the loop before ending the job. This should be called after the + * {@link #setup(File)} method is called or it will be overridden. + */ + public void setNumberOfEvents(final int numberOfEvents) { + this.numberOfEvents = numberOfEvents; + } + + /** + * Setup the job parameters from an XML Document. + * <p> + * This method contains the primary logic for setting up job parameters from an XML file. The other setup methods + * such as {@link #setup(InputStream)}, {@link #setup(String)} and {@link #setup(File)} all call this method. + * + * @param xmlDocument The lcsim recon XML document describing the job. + */ + private void setup(final Document xmlDocument) { + + // This method should not be called more than once. + if (isSetup) { + throw new IllegalStateException("The job manager was already setup."); + } + + // Set the root element from the XML document. + root = xmlDocument.getRootElement(); + + // Do variable substitutions into the document first. + this.substituteVariables(xmlDocument); + + // Rewrite XML after variable substitution. + if (this.rewriteSteering) { + this.rewriteXMLSteering(xmlDocument); + } + + // Setup the job control parameters. + this.setupJobControlParameters(); + + // Setup the class loader. + this.setupClassLoader(); + + // Setup system of units. + this.setupUnits(); + + // Process the constant definitions. + this.processConstants(); + + // Initialize the LCSimLoop. + this.initializeLoop(); + + // Setup drivers with parameters and execution order. + this.setupDrivers(); + + // Setup the file cache. + this.setupFileCache(); + + // Setup the input files. + this.setupInputFiles(); + + // Throw an error if there were no files provided and dry run is not enabled. + if (inputFiles.size() == 0 && !this.dryRun) { + LOGGER.severe("No input files provided and dry run is not enabled."); + throw new IllegalStateException("No input files to process."); + } + + // Flag JobManager as setup. + isSetup = true; } /** * Setup job parameters from a <code>File</code>. - * - * @param file - */ - public void setup(File file) { + * + * @param file the path to the XML file + */ + public void setup(final File file) { try { - setup((new FileInputStream(file))); - } catch (FileNotFoundException x) { + this.setup(new FileInputStream(file)); + } catch (final FileNotFoundException x) { throw new RuntimeException(x); } } /** - * Setup job parameters from an embedded resource. This method calls - * {@link #setup(InputStream)}. - */ - public void setup(String resourceURL) { - setup(this.getClass().getResourceAsStream(resourceURL)); - } - - /** - * Setup job parameters from an <code>InputStream</code> with XML text. - * - * @param in The XML input stream. - */ - public void setup(InputStream in) { + * Setup job parameters from an <code>InputStream</code> that should be valid XML text. + * + * @param in the XML input stream + */ + public void setup(final InputStream in) { // Make the Document builder. - SAXBuilder builder = new SAXBuilder(); + final SAXBuilder builder = new SAXBuilder(); // Setup XML schema validation. builder.setEntityResolver(new ClasspathEntityResolver()); @@ -511,429 +1086,163 @@ Document doc = null; try { doc = builder.build(in); - } catch (Exception x) { + } catch (final Exception x) { throw new RuntimeException(x); } // Setup the JobControlManager from the XML file. - setup(doc); - } - - /** - * Setup the job parameters from an XML Document. Though it isn't publically accessible, this - * method contains the primary logic for setting up job parameters from an XML file. The other - * setup methods, namely, {@link #setup(InputStream)}, {@link #setup(String)} and - * {@link #setup(File)}, all eventually call this method. - * - * @param doc The lcsim recon XML document describing the job. - */ - private void setup(Document doc) { - if (wasSetup) { - logStream.println("Ignoring call to setup as it was already called."); + this.setup(doc); + } + + /** + * Setup job parameters from an embedded resource. This method calls {@link #setup(InputStream)}. + */ + public void setup(final String resourceURL) { + this.setup(this.getClass().getResourceAsStream(resourceURL)); + } + + /** + * Setup the manager's class loader. + */ + private void setupClassLoader() { + + if (classLoader != null) { + LOGGER.info("The ClassLoader was already set externally, so custom classpaths will be ignored!"); return; } - - // Set the root element. - root = doc.getRootElement(); - - // Do variable substitutions. - substituteVariables(doc); - - // Rewrite XML after variable substitution. Correctness of values, - // such as existence of input file paths, is NOT checked. - if (this.rewrite) { - rewriteXMLSteering(doc); - } - - // Setup the job control parameters. - setupJobControlParameters(); - - // Print system properties. - if (printSystemProperties) { - printSystemProperties(logStream); - } - - // Setup the class loader. - setupClassLoader(); - - // Setup units. - setupUnits(); - - // Process the variable definitions. - processConstants(); - - // Check for required conditions. - checkConditions(); - - // Setup drivers with parameters and execution order. - setupDrivers(); - - // Setup the file cache. - setupFileCache(); - - // Setup the input files. - setupInputFiles(); - - // Check if a dry run should be performed when no input files are present. - if (inputFiles.size() == 0) { - performDryRun = true; - logStream.println("No input files provided by XML or command line. Dry run will be enabled."); - } - - // Flag JobManager as setup. - wasSetup = true; - } - - /** - * Perform variable substitution within all text data in a entire document. - * - * @param doc The XML document. - */ - private void substituteVariables(Document doc) { - substituteVariables(doc.getRootElement()); - } - - /** - * Substitute values from the <code>variableMap</code> into an XML element and all its - * children, recursively. - * - * @param element The XML element. - * @throw RuntimeException If a variable does not exist in the <code>variableMap</code>. - */ - private void substituteVariables(Element element) { - String text = element.getTextNormalize(); - if (text.length() != 0) { - // Create a new matcher. - Matcher match = varPattern.matcher(text); - - // No variables were used. - if (!match.find()) - return; - - // Text data on which to perform substitutions. - String newText = new String(text); - - // Reset the matcher. - match.reset(); - - // Loop over the matches. - while (match.find()) { - - // Variable string with ${...} enclosure included. - String var = match.group(); - - // The name of the variable for lookup. - String varName = var.substring(2, var.length() - 1); - - // The value of the variable. - String varValue = variableMap.get(varName); - - // If a variable was not defined, then the application will immediately exit here. - if (varValue == null) { - throw new RuntimeException("Variable not defined: " + varName); - } - - // Substitute this variable's value into the text. - newText = newText.replace(var, varValue); - } - - // Set this element's new text value. - element.setText(newText); - } - - // Recursively process all child elements of this one. - for (Iterator it = element.getChildren().iterator(); it.hasNext();) { - substituteVariables((Element) it.next()); - } - } - - /** - * Rewrite the XML steering file after resolving variables (done externally). The output path - * is set by command line argument to "-w". - * - * @param doc The XML steering doc with variables substituted. - */ - private void rewriteXMLSteering(Document doc) { - if (verbose) { - logStream.println("Rewriting XML to " + this.rewriteFile + " ..."); - } - XMLOutputter outputter = new XMLOutputter(); - outputter.setFormat(Format.getPrettyFormat()); - try { - FileOutputStream out = new FileOutputStream(this.rewriteFile); - outputter.output(doc, out); - out.close(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Setup the drivers from the XML job file, adding them to the JobManager's - * <code>driverMap</code> . + + final Element classpath = root.getChild("classpath"); + final List<URL> urlList = new ArrayList<URL>(); + if (classpath != null) { + for (final Object jarObject : classpath.getChildren("jar")) { + final Element jarElement = (Element) jarObject; + try { + urlList.add(new File(this.processPath(jarElement.getText())).toURI().toURL()); + } catch (final Exception x) { + throw new RuntimeException("Bad jar location: " + jarElement.getText(), x); + } + } + for (final Object jarUrlObject : classpath.getChildren("jarUrl")) { + final Element jarUrlElement = (Element) jarUrlObject; + try { + urlList.add(new URL(jarUrlElement.getText())); + } catch (final Exception x) { + throw new RuntimeException("Bad jar URL: " + jarUrlElement.getText(), x); + } + } + for (final Object cpDirObject : classpath.getChildren("directory")) { + final Element cpDirElement = (Element) cpDirObject; + try { + final File cpFile = new File(this.processPath(cpDirElement.getText())); + if (!cpFile.isDirectory()) { + throw new RuntimeException("The classpath component " + cpFile.getPath() + + " is not a valid directory!"); + } + urlList.add(cpFile.toURI().toURL()); + } catch (final Exception x) { + throw new RuntimeException("Bad classpath directory: " + cpDirElement.getText(), x); + } + } + } + final URL[] urls = urlList.toArray(new URL[] {}); + + classLoader = new LCSimClassLoader(urls); + + // Print extra user classpath entries. + this.printUserClasspath(); + } + + /** + * Create the drivers from the XML. */ private void setupDrivers() { - if (printDriversDetailed) { - logStream.println("--- Drivers ---"); - } - - // Instantiate the LCSimLoop here in case subsequently created Drivers want to override - // its default conditions implementation. - loop = new LCSimLoop(); - + // Loop over the list of driver elements. - List<Element> drivers = root.getChild("drivers").getChildren("driver"); - for (Element driver : drivers) { + final List<Element> drivers = root.getChild("drivers").getChildren("driver"); + for (final Element driver : drivers) { // Get the name of the Driver. - String name = driver.getAttributeValue("name"); + final String name = driver.getAttributeValue("name"); // Get the fully qualified type of the Driver. ([packageName].[className]) - String type = driver.getAttributeValue("type"); - - // Translate from a short name (optional). - if (availableDrivers.get(type) != null) { - type = availableDrivers.get(type); - } - - // - // Get Class, BeanInfo, and Constructor, and setup the new Driver instance. - // - - // Class of the Driver. + final String type = driver.getAttributeValue("type"); + + // Get the Java class of the Driver. Class driverClass; try { - driverClass = loader.loadClass(type); - } catch (ClassNotFoundException x) { + driverClass = classLoader.loadClass(type); + } catch (final ClassNotFoundException x) { throw new RuntimeException("The Driver class " + type + " was not found.", x); } - if (printDriversDetailed) - logStream.println(driverClass.getCanonicalName()); - - // The Driver instance. + LOGGER.fine("adding driver " + driverClass.getCanonicalName()); + + // Create an instance of the driver. Driver newDriver; try { newDriver = (Driver) driverClass.newInstance(); - } catch (InstantiationException x) { - throw new RuntimeException("Failed to create a Driver of class " + type + ". " + "This class might be missing a public constructor with no arguments.", x); - } catch (IllegalAccessException x) { - throw new RuntimeException("Cannot access Driver type " + type + ". " + "You may need to make this class or its constructor public.", x); - } - - // Get a list of Driver parameters. - List<Element> parameters = driver.getChildren(); - - // Process the parameters. - for (Element parameterElement : parameters) { - // The parameter's setter method that we will find. - Method setter = null; - - // The parameter's type that will be inferred from the method or is given by a type - // string in the case of an overloaded method. - Class propertyType = null; - - // Get the parameter name. - String pname = parameterElement.getName(); - - // Find setter methods that look like good matches for this parameter. - List<Method> methods = getSetterMethods(driverClass); - List<Method> methodCandidates = new ArrayList<Method>(); - for (Method method : methods) { - String propHack = method.getName().replaceFirst("set", ""); - propHack = propHack.substring(0, 1).toLowerCase() + propHack.substring(1); - if (propHack.equals(pname)) { - methodCandidates.add(method); - } - } if (methodCandidates.size() == 1) { - // Found the single setter method. - setter = methodCandidates.get(0); - if (setter.getParameterTypes().length > 1) { - throw new RuntimeException("The set method has too many arguments for parameter: " + pname); - } - propertyType = setter.getParameterTypes()[0]; - } else if (methodCandidates.size() > 1) { - // Found several, overloaded methods. Try to disambiguate them. - if (parameterElement.getAttribute("type") == null) - throw new RuntimeException("Parameter " + pname + " in Driver " + driverClass.getCanonicalName() + " is overloaded, but a type field is missing from the parameter's XML element."); - try { - // Try a primitive type first. - propertyType = getPrimitiveType(parameterElement.getAttribute("type").getValue()); - - // If type is null, then parameter is an Object and not a primitive, or it - // is not a - // valid type. - if (propertyType == null) - propertyType = Class.forName(parameterElement.getAttribute("type").getValue()); - } catch (ClassNotFoundException x) { - throw new RuntimeException("Bad user type: " + parameterElement.getAttribute("type").getValue() + " for parameter " + pname + "."); - } - // Find a method that matches the user type. - for (Method candidateMethod : methodCandidates) { - if (candidateMethod.getParameterTypes().length == 1 && candidateMethod.getParameterTypes()[0].equals(propertyType)) { - setter = candidateMethod; - break; - } - } - } else if (methodCandidates.size() == 0) { - // No method found. The parameter name is probably invalid. - throw new RuntimeException("Set method was not found: " + driverClass.getSimpleName() + "." + pname); - } - - // No setter method found. - if (setter == null) { - throw new RuntimeException("Unable to find set method for parameter: " + pname); - } - - // Convert the parameter to the appropriate type. - IParameterConverter converter = paramConverter.getConverterForType(propertyType); - if (converter == null) { - throw new RuntimeException("No converter found for parameter " + parameterElement.getName() + " with type " + propertyType.getName() + "."); - } - Object nextParameter = converter.convert(factory, parameterElement); - - // Call the setter with the parameter as argument. - Object pargs[] = new Object[1]; - pargs[0] = nextParameter; - try { - // This invokes the setter method of the driver. - setter.invoke(newDriver, pargs); - - // Print parameters and values as they are set. - if (printDriversDetailed) - logStream.println(" " + pname + " = " + parameterElement.getText().trim()); - } catch (Exception x) { - throw new RuntimeException("Problem processing parameter " + parameterElement.getName() + ".", x); - } - } // parameter loop - - // Add a driver to the manager. - addDriver(name, newDriver); + } catch (final InstantiationException x) { + throw new RuntimeException("Failed to create a Driver of class " + type + ".", x); + } catch (final IllegalAccessException x) { + throw new RuntimeException("Cannot access Driver type " + type + ".", x); + } + + // Get the list of Driver parameters from the XML. + final List<Element> parameters = driver.getChildren(); + + // Process the parameters provided for the driver. + this.processDriverParameters(driverClass, newDriver, parameters); + + // Add the driver to the manager. + this.addDriver(name, newDriver); + } // driver loop - if (printDriversDetailed) { - logStream.println("--- End Drivers ---"); - } - - // Make a list of Drivers to be executed. - List<Element> exec = root.getChild("execute").getChildren("driver"); - for (Element execDriver : exec) { - String driverName = execDriver.getAttributeValue("name"); - Driver driverFind = driverMap.get(driverName); - if (driverFind != null) { - driverExec.add(driverFind); - } else { - throw new RuntimeException("A Driver called " + driverName + " was not found."); - } - } - - // Add the drivers to the LCSimLoop. - for (Driver driver : driverExec) { - loop.add(driver); - } - } - - /** - * Add a Driver to the Driver map. - * - * @param name The unique name of the Driver. - * @param driver The instance of the Driver. - */ - private void addDriver(String name, Driver driver) { - if (driverMap.containsKey(name)) { - throw new RuntimeException("Duplicate driver name: " + name); - } - driverMap.put(name, driver); - } - - private void printSystemProperties(PrintStream ps) { - logStream.println("--- System Properties ---"); - for (Entry<Object, Object> entry : System.getProperties().entrySet()) { - logStream.println(entry.getKey() + " = " + entry.getValue()); - } - logStream.println("-- End System Properties"); - logStream.println(); - } - - /** - * Cleaup file text and add to file list. - * - * @param fileText The file's raw text path from XML file. - * @param fileList The list files to which new file will be appended. - * @return The file that was created from the text. - */ - private File processFileText(String fileText, List<File> fileList) { - Element fileElement = new Element("file"); - fileElement.setText(fileText.trim()); - return processFileElement(fileElement, fileList); - } - - /** - * Create a <code>File</code> object from the text in an XML element. - * - * @param fileElement The element containing a file path or URL. - * @param fileList List to append new <code>File</code>. - * @return The <code>File</code> object. - */ - private File processFileElement(Element fileElement, List<File> fileList) { - String fileLoc = processPath(fileElement.getText().trim()); - File file = null; - - // Try to process the file text as a URL. - try { - URL fileURL = new URL(fileLoc); - - // Local file URL. - if (fileLoc.startsWith("file:")) { - file = new File(fileURL.getPath()); - } - // Remote file URL. - else { - try { - file = this.fileCache.getCachedFile(fileURL); - } catch (IOException x) { - throw new RuntimeException("Unable to fetch file " + fileLoc + " to the cache directory.", x); - } - } - } - // Text is not a URL. Attempt to process as local file. - catch (MalformedURLException x) { - file = new File(fileLoc); - } - - // Add to list. - if (fileList != null) { - fileList.add(file); - } - - return file; + // Make the list of drivers to execute. + this.createDriverExecList(); + } + + /** + * Setup the file cache. + */ + private void setupFileCache() { + if (cacheDirectory != null) { + try { + fileCache = new FileCache(); + fileCache.setCacheDirectory(cacheDirectory); + fileCache.setPrintStream(null); + LOGGER.config("File cache created at " + cacheDirectory); + } catch (final IOException x) { + throw new RuntimeException(x); + } + } } /** * Setup the list of input files to be processed from the XML job file. */ - @SuppressWarnings("unchecked") - protected void setupInputFiles() { + private void setupInputFiles() { + if (root.getChild("inputFiles") == null) { - logStream.println("No input files in XML file."); - // this.performDryRun = true; + // This is not a warning because input files can be provided via the command line. + LOGGER.config("No input files in XML file."); return; } // Process the <file> elements. - List<Element> files = root.getChild("inputFiles").getChildren("file"); - for (Element fileElem : files) { - processFileElement(fileElem, this.inputFiles); + final List<Element> files = root.getChild("inputFiles").getChildren("file"); + for (final Element fileElem : files) { + this.processFileElement(fileElem, this.inputFiles); } // Read lists of file locations given by <fileList> elements. - List<Element> fileLists = root.getChild("inputFiles").getChildren("fileList"); - for (Element fileList : fileLists) { - String filePath = fileList.getText(); + final List<Element> fileLists = root.getChild("inputFiles").getChildren("fileList"); + for (final Element fileList : fileLists) { + final String filePath = fileList.getText(); BufferedReader input; try { input = new BufferedReader(new FileReader(new File(filePath))); - } catch (FileNotFoundException x) { + } catch (final FileNotFoundException x) { throw new RuntimeException("File not found: " + filePath, x); } String line = null; @@ -941,15 +1250,15 @@ // Read the next file, turn the text into an XML element, and process it using // common method. while ((line = input.readLine()) != null) { - processFileText(line.trim(), inputFiles); - } - } catch (IOException x) { + this.processFileText(line.trim(), inputFiles); + } + } catch (final IOException x) { throw new RuntimeException(x); } finally { if (input != null) { try { input.close(); - } catch (IOException e) { + } catch (final IOException e) { e.printStackTrace(); } } @@ -957,91 +1266,61 @@ } // Process <fileSet> elements. - List<Element> fileSets = (List<Element>) root.getChild("inputFiles").getChildren("fileSet"); - for (Element fileSet : fileSets) { - Attribute basedirAttrib = fileSet.getAttribute("baseDir"); + final List<Element> fileSets = root.getChild("inputFiles").getChildren("fileSet"); + for (final Element fileSet : fileSets) { + final Attribute basedirAttrib = fileSet.getAttribute("baseDir"); String basedir = ""; if (basedirAttrib != null) { basedir = basedirAttrib.getValue(); } - List<Element> fsFiles = fileSet.getChildren("file"); - for (Element file : fsFiles) { - String filePath = basedir + File.separator + file.getText().trim(); - processFileText(filePath, inputFiles); + final List<Element> fsFiles = fileSet.getChildren("file"); + for (final Element file : fsFiles) { + final String filePath = basedir + File.separator + file.getText().trim(); + this.processFileText(filePath, inputFiles); } } // Read <fileRegExp> elements, which may only reference local files, not URLs. - List<Element> fileRegExps = (List<Element>) root.getChild("inputFiles").getChildren("fileRegExp"); - for (Element fileRegExp : fileRegExps) { - Pattern pattern = Pattern.compile(fileRegExp.getText()); - String basedir = fileRegExp.getAttributeValue("baseDir"); - File dir = new File(basedir); - if (!dir.isDirectory()) + final List<Element> fileRegExps = root.getChild("inputFiles").getChildren("fileRegExp"); + for (final Element fileRegExp : fileRegExps) { + final Pattern pattern = Pattern.compile(fileRegExp.getText()); + final String basedir = fileRegExp.getAttributeValue("baseDir"); + final File dir = new File(basedir); + if (!dir.isDirectory()) { throw new RuntimeException(basedir + " is not a valid directory!"); - String dirlist[] = dir.list(); - if (verbose) { - logStream.println(); - } - for (String file : dirlist) { + } + final String dirlist[] = dir.list(); + for (final String file : dirlist) { if (file.endsWith(".slcio")) { - Matcher matcher = pattern.matcher(file); + final Matcher matcher = pattern.matcher(file); if (matcher.matches()) { - processFileText(basedir + File.separator + file, inputFiles); - if (verbose) { - logStream.println("Matched file <" + file.toString() + "> to pattern <" + pattern.toString() + ">"); - } + this.processFileText(basedir + File.separator + file, inputFiles); + LOGGER.fine("Matched file <" + file.toString() + "> to pattern <" + pattern.toString() + ">"); } else { - if (verbose) { - logStream.println("Did NOT match file <" + file.toString() + "> to pattern <" + pattern.toString() + ">"); - } + LOGGER.fine("Did NOT match file <" + file.toString() + "> to pattern <" + pattern.toString() + + ">"); } } } } // Check that all the files exist if job is not a dry run. - if (!performDryRun) { - for (File file : inputFiles) { + if (!dryRun) { + for (final File file : inputFiles) { if (!file.exists()) { - logStream.println("The input file " + file.getAbsolutePath() + " does not exist."); + LOGGER.info("The input file " + file.getAbsolutePath() + " does not exist."); throw new RuntimeException("The input file " + file.getAbsolutePath() + " does not exist!"); } } } // Print out the input file list. - if (printInputFiles) { - logStream.println(); - logStream.println("--- Input Files ---"); - int index = 1; - for (File file : inputFiles) { - logStream.println("[" + index + "] " + file.getAbsolutePath()); - ++index; - } - logStream.println("--- End Input Files ---"); - logStream.println(); - } + this.printInputFileList(); if (inputFiles.size() == 0) { - logStream.println("No input files were given in the steering file or command line options. Dry run will be enabled."); - this.performDryRun = true; - } - } - - /** - * Set logging verbosity. - * - * @param verbose True to turn on verbose mode; false to turn on quiet mode. - */ - private void setVerbose(boolean verbose) { - this.verbose = verbose; - this.printInputFiles = verbose; - this.printDriversDetailed = verbose; - this.printDriverStatistics = verbose; - this.printSystemProperties = verbose; - this.printUserClassPath = verbose; - // this.printVersion = verbose; + LOGGER.info("No input files were provided by the steering file or command line options. Dry run will be enabled."); + this.dryRun = true; + } } /** @@ -1049,228 +1328,59 @@ */ private void setupJobControlParameters() { - Element control = root.getChild("control"); - if (control == null) + final Element control = root.getChild("control"); + + if (control == null) { + // The control element is optional. return; - - // Verbose mode. - Element verboseElement = control.getChild("verbose"); - if (verboseElement != null) { - verbose = Boolean.valueOf(verboseElement.getText()); - setVerbose(verbose); - } - - // Log file setup needs to come first. - Element logFileElement = control.getChild("logFile"); - String logFilePath = null; - if (logFileElement != null) { - logFilePath = logFileElement.getText(); - File logFile = new File(logFilePath); - try { - logFile.createNewFile(); - logStream = new PrintStream(new FileOutputStream(logFile)); - - // Redirect standard out and err to log file. - System.setOut(logStream); - System.setErr(logStream); - } catch (IOException x) { - throw new RuntimeException("Error creating log file: " + logFile.toString(), x); - } } // Print hello world message to appear at top of log. - if (verbose) { - logStream.println(this.getClass().getCanonicalName() + " is initialized."); - logStream.println(); - logStream.println("--- Job Control Parameters ---"); - } - - // Print log file path now that PrintStream is set. - if (verbose) - logStream.println("logFile = " + logFilePath); + LOGGER.config(this.getClass().getCanonicalName() + " is initialized."); // Number of events to run. - Element controlElement = control.getChild("numberOfEvents"); + final Element controlElement = control.getChild("numberOfEvents"); if (controlElement != null) { numberOfEvents = Integer.valueOf(controlElement.getText()); - if (verbose) - logStream.println("numberOfEvents = " + numberOfEvents); - } - - Element skipElement = control.getChild("skipEvents"); + LOGGER.config("numberOfEvents: " + numberOfEvents); + } + + final Element skipElement = control.getChild("skipEvents"); if (skipElement != null) { skipEvents = Integer.valueOf(skipElement.getText()); - if (verbose) - logStream.println("skipEvents = " + skipEvents); - } - - Element dryRunElement = control.getChild("dryRun"); + LOGGER.config("skipEvents: " + skipEvents); + } + + final Element dryRunElement = control.getChild("dryRun"); if (dryRunElement != null) { - performDryRun = Boolean.valueOf(dryRunElement.getText()); - if (verbose) - logStream.println("dryRun = " + performDryRun); + dryRun = Boolean.valueOf(dryRunElement.getText()); + LOGGER.config("dryRun: " + dryRun); } // The cache directory. Defaults to the current directory. - Element cacheDirElement = control.getChild("cacheDirectory"); + final Element cacheDirElement = control.getChild("cacheDirectory"); if (cacheDirElement != null) { cacheDirectory = new File(cacheDirElement.getText()); - if (!cacheDirectory.exists()) + if (!cacheDirectory.exists()) { throw new RuntimeException("cacheDirectory does not exist at location: " + cacheDirElement.getText()); + } } else { - // Default to the user dir if cacheDirectory was not set explicitly. - // FIXME: Better default might be current directory. + // Cache directory defaults to user home dir. cacheDirectory = new File(System.getProperties().get("user.dir").toString()); } - if (verbose) - logStream.println("cacheDirectory = " + cacheDirectory); - - Element printInputFilesElement = control.getChild("printInputFiles"); - if (printInputFilesElement != null) { - printInputFiles = Boolean.valueOf(printInputFilesElement.getText()); - } - - if (verbose) - logStream.println("printInputFiles = " + printInputFiles); - - Element printStatisticsElement = control.getChild("printDriverStatistics"); + LOGGER.config("cacheDirectory: " + cacheDirectory); + + final Element printStatisticsElement = control.getChild("printDriverStatistics"); if (printStatisticsElement != null) { printDriverStatistics = Boolean.valueOf(printStatisticsElement.getText()); - } - - if (verbose) - logStream.println("printDriverStatistics = " + printDriverStatistics); - - Element printSystemPropertiesElement = control.getChild("printSystemProperties"); - if (printSystemPropertiesElement != null) { - printSystemProperties = Boolean.valueOf(printSystemPropertiesElement.getText()); - } - - if (verbose) - logStream.println("printSystemProperties = " + printSystemProperties); - - Element printUserClassPathElement = control.getChild("printUserClassPath"); - if (printUserClassPathElement != null) - printUserClassPath = Boolean.valueOf(printUserClassPathElement.getText()); - - if (verbose) - logStream.println("printUserClassPath = " + printUserClassPath); - - // Element printVersionElement = control.getChild("printVersion"); - // if (printVersionElement != null) { - // printVersion = Boolean.valueOf(printVersionElement.getText()); - // } - - Element printDriversDetailedElement = control.getChild("printDriversDetailed"); - if (printDriversDetailedElement != null) - printDriversDetailed = Boolean.valueOf(printDriversDetailedElement.getText()); - - if (verbose) { - logStream.println("printDriversDetailed = " + printDriversDetailed); - logStream.println("--- End Job Control Parameters ---"); - logStream.println(); - } - - Element dummyDetectorElement = control.getChild("dummyDetector"); - if (dummyDetectorElement != null) + LOGGER.config("printDriverStatistics: " + printDriverStatistics); + } + + final Element dummyDetectorElement = control.getChild("dummyDetector"); + if (dummyDetectorElement != null) { dummyDetector = Boolean.valueOf(dummyDetectorElement.getText()); - } - - /** - * Setup the manager's class loader. - */ - private void setupClassLoader() { - - if (loader != null) { - logStream.println("The ClassLoader was already set externally, so custom classpaths will be ignored!"); - return; - } - - Element classpath = root.getChild("classpath"); - List<URL> urlList = new ArrayList<URL>(); - if (classpath != null) { - for (Object jarObject : classpath.getChildren("jar")) { - Element jarElement = (Element) jarObject; - try { - urlList.add((new File(processPath(jarElement.getText())).toURL())); - } catch (Exception x) { - throw new RuntimeException("Bad jar location: " + jarElement.getText(), x); - } - } - for (Object jarUrlObject : classpath.getChildren("jarUrl")) { - Element jarUrlElement = (Element) jarUrlObject; - try { - urlList.add(new URL(jarUrlElement.getText())); - } catch (Exception x) { - throw new RuntimeException("Bad jar URL: " + jarUrlElement.getText(), x); - } - } - for (Object cpDirObject : classpath.getChildren("directory")) { - Element cpDirElement = (Element) cpDirObject; - try { - File cpFile = new File(processPath(cpDirElement.getText())); - if (!cpFile.isDirectory()) - throw new RuntimeException("The classpath component " + cpFile.getPath() + " is not a valid directory!"); - urlList.add(cpFile.toURL()); - } catch (Exception x) { - throw new RuntimeException("Bad classpath directory: " + cpDirElement.getText(), x); - } - } - } - URL[] urls = urlList.toArray(new URL[] {}); - - // Running in JAS. Jars need to already be on classpath. - // FIXME This creates a dep on freehep/JAS3 jars not used elsewhere in this package. - // if (Studio.getApplication() != null) { - // FreeHEPLookup fhl = ((Studio) Studio.getApplication()).getLookup(); - // loader = ((DynamicClassLoader)fhl.lookup(DynamicClassLoader.class)).getClassLoader(); - // } - // Running in batch. - // else { - - loader = new LCSimClassLoader(urls); - // } - - // Print user classpath entries. - if (printUserClassPath) { - logStream.println("-- Extra Classpath URLs --"); - for (URL url : ((URLClassLoader) loader).getURLs()) { - logStream.println(url); - } - logStream.println("-- End Extra Classpath URLs --"); - logStream.println(); - } - } - - /** - * Setup the file cache. - */ - private void setupFileCache() { - if (cacheDirectory == null) - return; - try { - fileCache = new FileCache(); - fileCache.setCacheDirectory(cacheDirectory); - fileCache.setPrintStream(null); - } catch (IOException x) { - throw new RuntimeException(x); - } - } - - /** - * Create the constants from the XML file. - */ - private void processConstants() { - Element define = root.getChild("define"); - if (define != null) { - for (Object o : define.getChildren()) { - Element e = (Element) o; - Text txt = (Text) e.getContent().get(0); - double dval = factory.computeDouble(txt.getValue()); - this.constantsMap.put(e.getName(), dval); - factory.addConstant(e.getName(), dval); - } + LOGGER.config("dummyDetector: " + dummyDetector); } } @@ -1278,261 +1388,107 @@ * Setup the system of units. */ private void setupUnits() { - Constants constants = Constants.getInstance(); - for (Entry<String, Double> unit : constants.entrySet()) { + final Constants constants = Constants.getInstance(); + for (final Entry<String, Double> unit : constants.entrySet()) { factory.addConstant(unit.getKey(), unit.getValue()); - } - } - - /** - * Get the Java primitive type class from a type name. + LOGGER.finest(unit.getKey() + " = " + unit.getValue()); + } + } + + /** + * Perform variable substitution within all text data in a entire document. + * + * @param doc The XML document. + */ + private void substituteVariables(final Document doc) { + this.substituteVariables(doc.getRootElement()); + } + + /** + * Substitute values from the <code>variableMap</code> into an XML element and all its children, recursively. + * + * @param element The XML element. + * @throw RuntimeException If a variable does not exist in the <code>variableMap</code>. + */ + private void substituteVariables(final Element element) { + + final String text = element.getTextNormalize(); + + if (text.length() != 0) { + + // Create a new matcher. + final Matcher match = VARIABLE_PATTERN.matcher(text); + + // No variables were used. + if (!match.find()) { + return; + } + + // Text data on which to perform substitutions. + String newText = new String(text); + + // Reset the matcher. + match.reset(); + + // Loop over the matches. + while (match.find()) { + + // Variable string with ${...} enclosure included. + final String var = match.group(); + + // The name of the variable for lookup. + final String varName = var.substring(2, var.length() - 1); + + // The value of the variable. + final String varValue = variableMap.get(varName); + + // If a variable was not defined, then the application will immediately exit here. + if (varValue == null) { + throw new RuntimeException("Required variable was not defined: " + varName); + } + + // Substitute this variable's value into the text. + newText = newText.replace(var, varValue); + } + + // Set this element's new text value. + element.setText(newText); + } + + // Recursively process all child elements of this one. + for (final Iterator it = element.getChildren().iterator(); it.hasNext();) { + this.substituteVariables((Element) it.next()); + } + } + + /** + * Initialize the conditions system before the job starts. + * <p> + * Sub-classes should override this if custom conditions system initialization is required. * - * @param name The name of the type. - * @return The primitive type class. - */ - private static Class getPrimitiveType(String name) { - if (name.equals("byte")) - return byte.class; - if (name.equals("short")) - return short.class; - if (name.equals("int")) - return int.class; - if (name.equals("long")) - return long.class; - if (name.equals("char")) - return char.class; - if (name.equals("float")) - return float.class; - if (name.equals("double")) - return double.class; - if (name.equals("boolean")) - return boolean.class; - if (name.equals("String")) - return String.class; - return null; - } - - /** - * Get a list of a class's setter methods. - * - * @param klass The class. - * @return A list of setter methods. - */ - private List<Method> getSetterMethods(Class klass) { - List<Method> methods = new ArrayList<Method>(); - Class currentClass = klass; - while (currentClass != null) { - for (Method method : currentClass.getMethods()) { - if (method.getName().startsWith("set") && !methods.contains(method)) { - methods.add(method); - } - } - currentClass = currentClass.getSuperclass(); - } - return methods; - } - - /** - * Process the file path string to substitute in the user's home directory for the "~" - * character. This is needed if running on Windows. - * - * @param path The original path. - * @return The path with home dir substitution. - */ - private String processPath(String path) { - if (path.startsWith("~")) { - return path.replaceFirst("~", System.getProperty("user.home")); - } else { - return path; - } - } - - // Wrap Drivers with a DriverAdapter. Only one will be created per job. - public DriverAdapter getDriverAdapter() { - if (driverAdapter == null) { - Driver topDriver = new Driver(); - for (Driver driver : getDriverExecList()) { - topDriver.add(driver); - } - driverAdapter = new DriverAdapter(topDriver); - } - return driverAdapter; - } - - // Process a single LCSim event using the DriverAdapter. - public void processEvent(EventHeader event) { - getDriverAdapter().recordSupplied(new RecordEvent(loop, event)); - } - - /** - * Reset the class's state so another job can be run. - */ - public synchronized void reset() { - - driverAdapter = null; - loop = null; - - driverMap = new LinkedHashMap<String, Driver>(); - driverExec = new ArrayList<Driver>(); - availableDrivers = new HashMap<String, String>(); - - // Run parameters. - inputFiles = new ArrayList<File>(); - numberOfEvents = -1; - skipEvents = -1; - - rewriteFile = null; - - // Variables and constants. - variableMap = new HashMap<String, String>(); - constantsMap = new HashMap<String, Double>(); - - // Boolean job options. - performDryRun = false; - rewrite = false; - - // Settings effecting logging verbosity. - printInputFiles = false; - printDriverStatistics = false; - printSystemProperties = false; - printUserClassPath = false; - printDriversDetailed = false; - // printVersion = false; - verbose = false; - - // File caching. - cacheDirectory = null; - - // The manager's class loader. - loader = null; - - // The log stream for messages. - logStream = System.out; - - // Root node of XML job file. - root = null; - - // XML utils. - factory = new JDOMExpressionFactory(); - paramConverter = new ParameterConverters(factory); - - // True once job parameters are setup from CL and XML. - wasSetup = false; - } - - public void configure() { - getDriverAdapter().start(null); - } - - public void reconfigure() { - getDriverAdapter().start(null); - } - - public void suspend() { - getDriverAdapter().suspend(null); - } - - public void finish() { - getDriverAdapter().finish(null); - } - - public void setPerformDryRun(boolean d) { - this.performDryRun = d; + * @throws ConditionsNotFoundException if some conditions were not found + */ + protected void initializeConditions() throws ConditionsNotFoundException { + // Set up user supplied conditions information (we already checked that these were both given if one was provided). + if (this.detectorName != null) { + LOGGER.config("initializing conditions system with detector " + this.detectorName + " and run " + this.runNumber); + ConditionsManager.defaultInstance().setDetector(this.detectorName, this.runNumber); + } } - public void setClassLoader(ClassLoader loader) { - if (loader == null) - throw new IllegalArgumentException("The ClassLoader argument points to null."); - this.loader = loader; - logStream.println("Set ClassLoader to " + loader.getClass().getCanonicalName()); - } - - private void checkConditions() { - - // Get conditions element if it is there; otherwise return. - Element conditionsElement = root.getChild("conditions"); - if (conditionsElement == null) - return; - - // Get the list of valid detectors for this steering file. - Element detectorsElement = conditionsElement.getChild("detectors"); - - if (detectorsElement != null) { - String detectors = detectorsElement.getTextNormalize(); - - String[] detectorNames = detectors.split(" "); - - // Loop over detector names and check for required conditions in each. - for (String detector : detectorNames) { - logStream.println("Looking up required conditions for " + detector + " ..."); - ConditionsManager mgr = ConditionsManager.defaultInstance(); - try { - mgr.setDetector(detector, 0); - } catch (ConditionsManager.ConditionsNotFoundException e) { - throw new RuntimeException(e); - } - for (Object condition : conditionsElement.getChildren()) { - Element conditionElement = (Element) condition; - boolean required = true; - try { - required = conditionElement.getAttribute("required").getBooleanValue(); - } catch (DataConversionException e) { - } catch (NullPointerException e) { - } - try { - mgr.getRawConditions(conditionElement.getTextTrim()); - logStream.println(conditionElement.getTextTrim() + " - OKAY"); - } catch (ConditionsSetNotFoundException e) { - logStream.println(conditionElement.getTextTrim() + " - NOT FOUND"); - if (required) - throw new RuntimeException("Required conditions " + conditionElement.getTextTrim() + " are missing from detector " + detector + "."); - } - } - } - } - - logStream.println("Adding ConditionsCheckDriver to front of Driver exec list."); - ConditionsCheckDriver d = createConditionsCheckDriver(); - if (d != null) { - if (detectorsElement == null) { - // No detector names provided, so individual detectors will have all conditions - // checked. - d.setCheckDetector(false); - d.setCheckConditions(true); - } else { - // Detectors already checked by name so just check that detector names in the job - // are okay. - d.setCheckConditions(false); - d.setCheckDetector(true); - } - driverExec.add(d); - } - } - - private ConditionsCheckDriver createConditionsCheckDriver() { - ConditionsCheckDriver driver = new ConditionsCheckDriver(); - Element conditionsElement = root.getChild("conditions"); - if (conditionsElement == null) - return null; - for (Object condition : conditionsElement.getChildren()) { - Element conditionElement = (Element) condition; - driver.setCondition(conditionElement.getTextTrim()); - } - if (conditionsElement.getChild("detectors") != null) { - String[] detectorNames = conditionsElement.getChild("detectors").getText().split(" +"); - for (String detector : detectorNames) { - driver.setDetector(detector); - } - } - return driver; + /** + * Get the run number set by the command line options. + * @return the run number or <code>null</code> if not set + */ + protected Integer getRunNumber() { + return this.runNumber; } /** - * Set the number of events to run on the loop before ending the job. - * This should be called after the {@link #setup(File)} method - * is called or it will be overridden. - */ - public void setNumberOfEvents(int numberOfEvents) { - this.numberOfEvents = numberOfEvents; + * Get the detector name set by the command line options. + * @return the detector name or <code>null</code> if not set + */ + protected String getDetectorName() { + return this.detectorName; } } Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/LCSimClassLoader.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/LCSimClassLoader.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/LCSimClassLoader.java Tue Nov 3 11:32:25 2015 @@ -5,18 +5,17 @@ /** * A simple class loader that accepts URLs and extends the system class loader. + * * @author jeremym * @version $id: $ */ -class LCSimClassLoader extends URLClassLoader -{ - LCSimClassLoader(URL[] urls) - { - super(urls, ClassLoader.getSystemClassLoader()); - } +class LCSimClassLoader extends URLClassLoader { - public void addURL(URL url) - { - this.addURL(url); - } -} + LCSimClassLoader(URL[] urls) { + super(urls, ClassLoader.getSystemClassLoader()); + } + + public void addURL(URL url) { + this.addURL(url); + } +} Modified: projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/ParameterConverters.java ============================================================================= --- projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/ParameterConverters.java (original) +++ projects/lcsim/trunk/job-manager/src/main/java/org/lcsim/job/ParameterConverters.java Tue Nov 3 11:32:25 2015 @@ -14,431 +14,360 @@ import org.lcsim.util.xml.JDOMExpressionFactory; /** - * Converter utilities for making Java Beans arguments from LCSim XML Driver parameters, - * using a JDOM factory to do expression evaluation. + * Converter utilities for making Java Beans arguments from LCSim XML Driver parameters, using a JDOM factory to do + * expression evaluation. + * * @author jeremym */ -public class ParameterConverters -{ - List<IParameterConverter> converters = new ArrayList<IParameterConverter>(); - JDOMExpressionFactory factory; - - public ParameterConverters(JDOMExpressionFactory factory) - { - this.factory = factory; - - add(new IntegerConverter()); - add(new StringConverter()); - add(new DoubleConverter()); - add(new FloatConverter()); - add(new BooleanConverter()); - add(new Hep3VectorConverter()); - add(new DoubleArray1DConverter()); - add(new IntegerArray1DConverter()); - add(new FloatArray1DConverter()); - add(new StringArray1DConverter()); - add(new BooleanArray1DConverter()); - add(new FileConverter()); - add(new URLConverter()); - add(new IntegerArray2DConverter()); - add(new DoubleArray2DConverter()); - } - - public Object convert(Class propertyType, Element parameterElement) - { - IParameterConverter p = getConverterForType(propertyType); - if (p != null) - { - return p.convert(factory, parameterElement); - } - else - { - return null; - } - } - - protected void add(IParameterConverter converter) - { - converters.add(converter); - } - - public List<IParameterConverter> getConverters() - { - return converters; - } - - public IParameterConverter getConverterForType(Class propertyType) - { - for (IParameterConverter p : converters) - { - if (p.handles(propertyType)) - return p; - } - return null; - } - - public class IntegerConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(int.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return Integer.valueOf((int)factory.computeDouble(parameterElement.getValue())); - } - } - - public class StringConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(String.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return parameterElement.getValue(); - } - } - - public class DoubleConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(double.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return Double.valueOf(factory.computeDouble(parameterElement.getValue())); - } - } - - public class FloatConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(float.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return Float.valueOf(factory.computeFloat(parameterElement.getValue())); - } - } - - public class BooleanConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(boolean.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return Boolean.valueOf(parameterElement.getValue()); - } - } - - public class Hep3VectorConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(Hep3Vector.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - double x = Double.valueOf(factory.computeDouble(tokenize.nextToken())); - double y = Double.valueOf(factory.computeDouble(tokenize.nextToken())); - double z = Double.valueOf(factory.computeDouble(tokenize.nextToken())); - return new BasicHep3Vector(x, y, z); - } - } - - public class DoubleArray1DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[D"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - int size = tokenize.countTokens(); - double da[] = new double[size]; - int i = 0; - while (tokenize.hasMoreTokens()) - { - da[i] = Double.valueOf(factory.computeDouble(tokenize.nextToken())); - ++i; - } - return da; - } - } - - public class IntegerArray1DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[I"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - int size = tokenize.countTokens(); - int ia[] = new int[size]; - int i = 0; - while (tokenize.hasMoreTokens()) - { - ia[i] = Integer.valueOf((int)factory.computeDouble(tokenize.nextToken())); - ++i; - } - return ia; - } - } - - public class FloatArray1DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[F"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - int size = tokenize.countTokens(); - float fa[] = new float[size]; - int i = 0; - while (tokenize.hasMoreTokens()) - { - fa[i] = Float.valueOf(factory.computeFloat(tokenize.nextToken())); - ++i; - } - return fa; - } - } - - public class StringArray1DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[Ljava.lang.String;"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - int size = tokenize.countTokens(); - String sa[] = new String[size]; - int i = 0; - while (tokenize.hasMoreTokens()) - { - sa[i] = tokenize.nextToken(); - ++i; - } - return sa; - } - } - - public class BooleanArray1DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[Z"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); - int size = tokenize.countTokens(); - boolean ba[] = new boolean[size]; - int i = 0; - while (tokenize.hasMoreTokens()) - { - ba[i] = Boolean.valueOf(tokenize.nextToken()); - ++i; - } - return ba; - } - } - - public class FileConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(File.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return new File(parameterElement.getValue()); - } - } - - public class URLConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(URL.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - try - { - return new URL(parameterElement.getValue()); - } - catch (MalformedURLException x) - { - throw new RuntimeException("Bad URL " + parameterElement.getValue() + " in XML job description.",x); - } - } - } - public class DoubleArray2DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[[D"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - // Parse into a list of list of doubles. - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue(), ";"); - int length = tokenize.countTokens(); - int ir = 0; - List<List<Double>> rows = new ArrayList<List<Double>>(length); - while (tokenize.hasMoreTokens()) - { - String rowStr = tokenize.nextToken(); - StringTokenizer tokenize2 = new StringTokenizer(rowStr); - rows.add(new ArrayList<Double>()); - List<Double> row = rows.get(ir); - while (tokenize2.hasMoreTokens()) - { - String entry = tokenize2.nextToken(); - Double d = factory.computeDouble(entry); - row.add(d); - } - ++ir; - } - - // Convert list of doubles into 2D array, checking for wrong sized rows. - double arr[][] = new double[rows.size()][rows.get(0).size()]; - int jcheck = rows.get(0).size() - 1; - int i=0; - int j=0; - for (List<Double> aa : rows) - { - for (Double a : aa) - { - if (j > jcheck) - { - throw new RuntimeException( - "Row " + j + " of array " + - parameterElement.getName() + - "with length " + aa.size() + - " is too long."); - } - arr[i][j] = a; - ++j; - } - if (j < jcheck) - { - throw new RuntimeException( - "Row " + j + " of array " + - parameterElement.getName() + - "with length " + aa.size() + - "is too short."); - } - j=0; - ++i; - } - return arr; - } - } - - public class IntegerArray2DConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.getName().equals("[[I"); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - // Parse into a list of list of doubles. - StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue(), ";"); - int length = tokenize.countTokens(); - int ir = 0; - List<List<Integer>> rows = new ArrayList<List<Integer>>(length); - while (tokenize.hasMoreTokens()) - { - String rowStr = tokenize.nextToken(); - StringTokenizer tokenize2 = new StringTokenizer(rowStr); - rows.add(new ArrayList<Integer>()); - List<Integer> row = rows.get(ir); - while (tokenize2.hasMoreTokens()) - { - String entry = tokenize2.nextToken(); - Integer d = Integer.valueOf(entry); - row.add(d); - } - ++ir; - } - - // Convert list of doubles into 2D array, checking for wrong sized rows. - int arr[][] = new int[rows.size()][rows.get(0).size()]; - int jcheck = rows.get(0).size() - 1; - int i=0; - int j=0; - for (List<Integer> aa : rows) - { - for (Integer a : aa) - { - if (j > jcheck) - { - throw new RuntimeException( - "Row " + j + " of array " + - parameterElement.getName() + - "with length " + aa.size() + - " is too long."); - } - arr[i][j] = a; - ++j; - } - if (j < jcheck) - { - throw new RuntimeException( - "Row " + j + " of array " + - parameterElement.getName() + - "with length " + aa.size() + - "is too short."); - } - j=0; - ++i; - } - return arr; - } - } - - public class ElementConverter implements IParameterConverter - { - public boolean handles(Class propertyType) - { - return propertyType.equals(Element.class); - } - - public Object convert(JDOMExpressionFactory factory, Element parameterElement) - { - return parameterElement; - } - } +public class ParameterConverters { + + List<IParameterConverter> converters = new ArrayList<IParameterConverter>(); + JDOMExpressionFactory factory; + + public ParameterConverters(JDOMExpressionFactory factory) { + this.factory = factory; + + add(new IntegerConverter()); + add(new StringConverter()); + add(new DoubleConverter()); + add(new FloatConverter()); + add(new BooleanConverter()); + add(new Hep3VectorConverter()); + add(new DoubleArray1DConverter()); + add(new IntegerArray1DConverter()); + add(new FloatArray1DConverter()); + add(new StringArray1DConverter()); + add(new BooleanArray1DConverter()); + add(new FileConverter()); + add(new URLConverter()); + add(new IntegerArray2DConverter()); + add(new DoubleArray2DConverter()); + } + + public Object convert(Class propertyType, Element parameterElement) { + IParameterConverter p = getConverterForType(propertyType); + if (p != null) { + return p.convert(factory, parameterElement); + } else { + return null; + } + } + + protected void add(IParameterConverter converter) { + converters.add(converter); + } + + public List<IParameterConverter> getConverters() { + return converters; + } + + public IParameterConverter getConverterForType(Class propertyType) { + for (IParameterConverter p : converters) { + if (p.handles(propertyType)) + return p; + } + return null; + } + + public class IntegerConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(int.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return Integer.valueOf((int) factory.computeDouble(parameterElement.getValue())); + } + } + + public class StringConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(String.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return parameterElement.getValue(); + } + } + + public class DoubleConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(double.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return Double.valueOf(factory.computeDouble(parameterElement.getValue())); + } + } + + public class FloatConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(float.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return Float.valueOf(factory.computeFloat(parameterElement.getValue())); + } + } + + public class BooleanConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(boolean.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return Boolean.valueOf(parameterElement.getValue()); + } + } + + public class Hep3VectorConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(Hep3Vector.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + double x = Double.valueOf(factory.computeDouble(tokenize.nextToken())); + double y = Double.valueOf(factory.computeDouble(tokenize.nextToken())); + double z = Double.valueOf(factory.computeDouble(tokenize.nextToken())); + return new BasicHep3Vector(x, y, z); + } + } + + public class DoubleArray1DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[D"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + int size = tokenize.countTokens(); + double da[] = new double[size]; + int i = 0; + while (tokenize.hasMoreTokens()) { + da[i] = Double.valueOf(factory.computeDouble(tokenize.nextToken())); + ++i; + } + return da; + } + } + + public class IntegerArray1DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[I"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + int size = tokenize.countTokens(); + int ia[] = new int[size]; + int i = 0; + while (tokenize.hasMoreTokens()) { + ia[i] = Integer.valueOf((int) factory.computeDouble(tokenize.nextToken())); + ++i; + } + return ia; + } + } + + public class FloatArray1DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[F"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + int size = tokenize.countTokens(); + float fa[] = new float[size]; + int i = 0; + while (tokenize.hasMoreTokens()) { + fa[i] = Float.valueOf(factory.computeFloat(tokenize.nextToken())); + ++i; + } + return fa; + } + } + + public class StringArray1DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[Ljava.lang.String;"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + int size = tokenize.countTokens(); + String sa[] = new String[size]; + int i = 0; + while (tokenize.hasMoreTokens()) { + sa[i] = tokenize.nextToken(); + ++i; + } + return sa; + } + } + + public class BooleanArray1DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[Z"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue()); + int size = tokenize.countTokens(); + boolean ba[] = new boolean[size]; + int i = 0; + while (tokenize.hasMoreTokens()) { + ba[i] = Boolean.valueOf(tokenize.nextToken()); + ++i; + } + return ba; + } + } + + public class FileConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(File.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return new File(parameterElement.getValue()); + } + } + + public class URLConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(URL.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + try { + return new URL(parameterElement.getValue()); + } catch (MalformedURLException x) { + throw new RuntimeException("Bad URL " + parameterElement.getValue() + " in XML job description.", x); + } + } + } + + public class DoubleArray2DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[[D"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + // Parse into a list of list of doubles. + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue(), ";"); + int length = tokenize.countTokens(); + int ir = 0; + List<List<Double>> rows = new ArrayList<List<Double>>(length); + while (tokenize.hasMoreTokens()) { + String rowStr = tokenize.nextToken(); + StringTokenizer tokenize2 = new StringTokenizer(rowStr); + rows.add(new ArrayList<Double>()); + List<Double> row = rows.get(ir); + while (tokenize2.hasMoreTokens()) { + String entry = tokenize2.nextToken(); + Double d = factory.computeDouble(entry); + row.add(d); + } + ++ir; + } + + // Convert list of doubles into 2D array, checking for wrong sized rows. + double arr[][] = new double[rows.size()][rows.get(0).size()]; + int jcheck = rows.get(0).size() - 1; + int i = 0; + int j = 0; + for (List<Double> aa : rows) { + for (Double a : aa) { + if (j > jcheck) { + throw new RuntimeException("Row " + j + " of array " + parameterElement.getName() + + "with length " + aa.size() + " is too long."); + } + arr[i][j] = a; + ++j; + } + if (j < jcheck) { + throw new RuntimeException("Row " + j + " of array " + parameterElement.getName() + "with length " + + aa.size() + "is too short."); + } + j = 0; + ++i; + } + return arr; + } + } + + public class IntegerArray2DConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.getName().equals("[[I"); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + // Parse into a list of list of doubles. + StringTokenizer tokenize = new StringTokenizer(parameterElement.getValue(), ";"); + int length = tokenize.countTokens(); + int ir = 0; + List<List<Integer>> rows = new ArrayList<List<Integer>>(length); + while (tokenize.hasMoreTokens()) { + String rowStr = tokenize.nextToken(); + StringTokenizer tokenize2 = new StringTokenizer(rowStr); + rows.add(new ArrayList<Integer>()); + List<Integer> row = rows.get(ir); + while (tokenize2.hasMoreTokens()) { + String entry = tokenize2.nextToken(); + Integer d = Integer.valueOf(entry); + row.add(d); + } + ++ir; + } + + // Convert list of doubles into 2D array, checking for wrong sized rows. + int arr[][] = new int[rows.size()][rows.get(0).size()]; + int jcheck = rows.get(0).size() - 1; + int i = 0; + int j = 0; + for (List<Integer> aa : rows) { + for (Integer a : aa) { + if (j > jcheck) { + throw new RuntimeException("Row " + j + " of array " + parameterElement.getName() + + "with length " + aa.size() + " is too long."); + } + arr[i][j] = a; + ++j; + } + if (j < jcheck) { + throw new RuntimeException("Row " + j + " of array " + parameterElement.getName() + "with length " + + aa.size() + "is too short."); + } + j = 0; + ++i; + } + return arr; + } + } + + public class ElementConverter implements IParameterConverter { + + public boolean handles(Class propertyType) { + return propertyType.equals(Element.class); + } + + public Object convert(JDOMExpressionFactory factory, Element parameterElement) { + return parameterElement; + } + } } ######################################################################## Use REPLY-ALL to reply to list To unsubscribe from the LCDET-SVN list, click the following link: https://listserv.slac.stanford.edu/cgi-bin/wa?SUBED1=LCDET-SVN&A=1