Index: src/edu/ucsb/nceas/metacat/harvesterClient/Harvester.java
===================================================================
--- src/edu/ucsb/nceas/metacat/harvesterClient/Harvester.java	(revision 2061)
+++ src/edu/ucsb/nceas/metacat/harvesterClient/Harvester.java	(revision 2062)
@@ -36,6 +36,8 @@
   /*
    * Class fields
    */
+  private static final String marker =
+"*****************************************************************************";
    
 
   /* 
@@ -51,20 +53,24 @@
     
 
   /**
-   * Determines whether Harvester is running on a Win32 platform. Used
-   * during development.
-   * 
-   * @return    true if this in Win32, false otherwise
+   * Loads Harvester properties from a configuration file.
    */
-  public static boolean isWin32 () {
-    boolean isWin32;
-    String osName = System.getProperty("os.name");
+  private static Properties loadProperties() {
+    File configfile = new File("../../lib/harvester", "harvester.properties");
+    Properties properties = new Properties();
+
+    try {
+      properties.load(new FileInputStream(configfile));
+    }
+    catch (IOException e) {
+      System.out.println("IOException: " + e.getMessage());
+      System.exit(1);
+    }
     
-    isWin32 = (osName.startsWith("Windows"));
-    return isWin32;
+    return properties;
   }
   
-
+  
   /**
     * Harvester main method.
     * 
@@ -74,12 +80,118 @@
     * @throws ParserConfigurationException
     */
   public static void main(String[] args) {
-    Harvester harvester = new Harvester();
+    Integer delayDefault = new Integer(0); // Default number of hours delay
+    int delay = delayDefault.intValue();  // Delay in hours before first harvest
+    Integer d;                            // Used for determining delay
+    long delta;                           // endTime - startTime
+    long endTime;                         // time that a harvest completes
+    Harvester harvester;                  // object for a single harvest run
+    Integer maxHarvestsDefault = new Integer(30);    // Default max harvests
+    int maxHarvests = maxHarvestsDefault.intValue(); // Max number of harvests
+    Integer mh;                              // used in determining max harvests
+    int nHarvests = 0;                      // counts the number of harvest runs
+    final long oneHour = (60 * 60 * 1000);
+    final long oneSecond = 1000;
+    Integer periodDefault = new Integer(24); // Default hours between harvests
+    int period = periodDefault.intValue();   // Hours between harvests
+    Integer p;                               // Used in determining the period
+    Properties properties;
+    long startTime;                          // time that a harvest starts
+
+    System.out.println(marker);
+    System.out.println("Starting Harvester");
+    properties = Harvester.loadProperties();
+    properties.list(System.out);
+
+    // Parse the delay property. Use default if necessary.    
+    try {
+      d = Integer.valueOf(properties.getProperty(
+                                                 "delay",
+                                                 delayDefault.toString()
+                                                ));
+      delay = d.intValue();
+    }
+    catch (NumberFormatException e) {
+      System.out.println("NumberFormatException: Error parsing delay: " +
+                         e.getMessage());
+      System.out.println("Defaulting to delay=" + delayDefault);
+      delay = delayDefault.intValue();
+    }
+
+    // Parse the maxHarvests property. Use default if necessary.    
+    try {
+      mh = Integer.valueOf(properties.getProperty(
+                                                  "maxHarvests",
+                                                  maxHarvestsDefault.toString()
+                                                 ));
+      maxHarvests = mh.intValue();
+    }
+    catch (NumberFormatException e) {
+      System.out.println("NumberFormatException: Error parsing maxHarvests: " +
+                         e.getMessage());
+      System.out.println("Defaulting to maxHarvests=" + maxHarvestsDefault);
+      maxHarvests = maxHarvestsDefault.intValue();
+    }
+
+    // Parse the period property. Use default if necessary.    
+    try {
+      p = Integer.valueOf(properties.getProperty("period", 
+                                                 periodDefault.toString()
+                                                ));
+      period = p.intValue();
+    }
+    catch (NumberFormatException e) {
+      System.out.println("NumberFormatException: Error parsing period: " +
+                         e.getMessage());
+      System.out.println("Defaulting to period=" + periodDefault);
+      period = periodDefault.intValue();
+    }
     
-    harvester.startup();                  // Start up Harvester
-    harvester.readHarvestSiteSchedule();  // Read the database table
-    harvester.harvest();                  // Harvest the documents
-    harvester.shutdown();                 // Shut down Harvester
+    // Sleep for delay number of hours prior to starting first harvest
+    if (delay > 0) {
+      try {
+        System.out.print("First harvest will begin in " + delay);
+        if (delay == 1) {
+          System.out.println(" hour.");
+        }
+        else {
+          System.out.println(" hours.");
+        }
+        //Thread.sleep(delay * oneHour);
+        Thread.sleep(delay * oneSecond);
+      }
+      catch (InterruptedException e) {
+          System.err.println("InterruptedException: " + e.getMessage());
+          System.exit(1);
+      }
+    }
+
+    // Repeat a new harvest once every period number of hours, until we reach
+    // the maximum number of harvests. Subtract delta from the time period so 
+    // that each harvest will start at a fixed interval.
+    //
+    while (nHarvests < maxHarvests) {
+      nHarvests++;
+      startTime = System.currentTimeMillis();
+      harvester = new Harvester();                // New object for this harvest
+      harvester.startup(nHarvests, maxHarvests);  // Start up Harvester
+      harvester.readHarvestSiteSchedule();        // Read the database table
+      harvester.harvest();                        // Harvest the documents
+      harvester.shutdown();                       // Shut down Harvester
+      endTime = System.currentTimeMillis();
+      delta = endTime - startTime;
+
+      if (nHarvests < maxHarvests) {
+        try {
+//          Thread.sleep((period * oneHour) - delta);
+          Thread.sleep((period * oneSecond) - delta);
+        }
+        catch (InterruptedException e) {
+          System.err.println("InterruptedException: " + e.getMessage());
+          System.exit(1);
+        }
+      }
+    }
   }
 
 
@@ -90,7 +202,10 @@
   /** Database connection */
   Connection conn;
   
-  /** Used during development to determine whether to connect to metacat */
+  /** Used during development to determine whether to connect to metacat 
+   *  Sometimes it's useful to test parts of the code without actually
+   *  connecting to Metacat.
+   */
   private boolean connectToMetacat;
 
   /** Highest DETAIL_LOG_ID primary key in the HARVEST_DETAIL_LOG table */
@@ -120,25 +235,11 @@
   /** Metacat client object */
   Metacat metacat;
   
-  /** Loads harvester properties from configuration file */
-  Properties properties;
-    
 
   /*
    * Object methods
    */
    
-   
-  /*                
-                    Harvester  harvester,
-                    Date       harvestDate,
-                    int        status,
-                    String     message, 
-                    String     harvestOperationCode,
-                    int        siteScheduleID,
-                    HarvestDocument harvestDocument,
-                    String     errorMessage 
-*/
   /**
    * Creates a new HarvestLog object and adds it to the harvestLogList.
    * 
@@ -347,26 +448,12 @@
     harvestLogID = getMaxValue("HARVEST_LOG", "HARVEST_LOG_ID") + 1;
   }
   
-  
+
   /**
-   * Loads Harvester properties from a configuration file.
+   * Prints the site schedule data for a given site.
+   * 
+   * @param siteScheduleID   the primary key in the HARVEST_SITE_SCHEDULE table
    */
-  private void loadProperties() {
-    File configfile = new File("../../lib/harvester", "harvester.properties");
-    
-    properties = new Properties();
-
-    try {
-      properties.load(new FileInputStream(configfile));
-      properties.list(System.out);
-    }
-    catch (IOException e) {
-      System.out.println("IOException: " + e.getMessage());
-      System.exit(1);
-    }
-  }
-  
-  
   void printHarvestSiteSchedule(int siteScheduleID) {
      HarvestSiteSchedule harvestSiteSchedule;
 
@@ -569,16 +656,22 @@
 
   /**
    * Initializes Harvester at startup. Connects to the database and to Metacat.
+   * 
+   * @param nHarvests        the nth harvest
+   * @param maxHarvests      the maximum number of harvests that this process
+   *                         can run
    */
-  private void startup() {
+  private void startup(int nHarvests, int maxHarvests) {
     Boolean ctm;
     String dbDriver;
     Integer lp;
     String metacatURL;
-    String osName = Harvester.isWin32() ? "Windows" : "Unix";
+    Date now;
     String password;
+    Properties properties;
     //String response;
     String sessionId;
+    String timestamp;
 		String url;
     String user;
     String userName = System.getProperty("user.name");
@@ -585,13 +678,17 @@
     SQLWarning warn;
     
     // Log startup operation
-    System.out.println("*****************************************************");
-    System.out.println("Starting Up Harvester");
-    loadProperties();
+    System.out.println(Harvester.marker);
+    now = new Date();
+    timestamp = now.toString() + ": ";
+    System.out.println(timestamp + "Starting Next Harvest (" +
+                       nHarvests + "/" + maxHarvests + ")");
+    properties = loadProperties();
     ctm = Boolean.valueOf(properties.getProperty("connectToMetacat", "true"));
     connectToMetacat = ctm.booleanValue();
     dbDriver = properties.getProperty("dbDriver");
     harvesterAdministrator = properties.getProperty("harvesterAdministrator");
+
     try {
       lp = Integer.valueOf(properties.getProperty("logPeriod", "90"));
       logPeriod = lp.intValue();
@@ -602,6 +699,7 @@
       System.err.println("Defaulting to logPeriod of 90 days");
       logPeriod = 90;
     }
+
     metacatURL = properties.getProperty("metacatURL");
     password = properties.getProperty("password");
     url = properties.getProperty("url");
