LISTSERV mailing list manager LISTSERV 16.5

Help for LCDET-SVN Archives


LCDET-SVN Archives

LCDET-SVN Archives


LCDET-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

LCDET-SVN Home

LCDET-SVN Home

LCDET-SVN  November 2015

LCDET-SVN November 2015

Subject:

r3686 - in /projects/lcsim/trunk/job-manager: ./ src/main/java/org/lcsim/job/

From:

[log in to unmask]

Reply-To:

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

Date:

Tue, 3 Nov 2015 19:32:29 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (3899 lines)

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

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

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

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

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

Privacy Notice, Security Notice and Terms of Use