Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2004 University of New Mexico and the 
4
 *                  Regents of the University of California
5
 *
6
 *   '$Author: costa $'
7
 *     '$Date: 2004-07-08 10:45:40 -0700 (Thu, 08 Jul 2004) $'
8
 * '$Revision: 2203 $'
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
 */
24

    
25
package edu.ucsb.nceas.metacat.harvesterClient;
26

    
27
import com.oreilly.servlet.MailMessage;
28
import edu.ucsb.nceas.utilities.Options;
29
import java.io.File;
30
import java.io.FileInputStream;
31
import java.io.IOException;
32
import java.io.PrintStream;
33
import java.sql.Connection;
34
import java.sql.DriverManager;
35
import java.sql.ResultSet;
36
import java.sql.SQLException;
37
import java.sql.SQLWarning;
38
import java.sql.Statement;
39
import java.util.ArrayList;
40
import java.text.SimpleDateFormat;
41
import java.util.Date;
42

    
43
import edu.ucsb.nceas.metacat.client.Metacat;
44
import edu.ucsb.nceas.metacat.client.MetacatFactory;
45
import edu.ucsb.nceas.metacat.client.MetacatInaccessibleException;
46

    
47
/**
48
 * Harvester is the main class for the Harvester application. The main
49
 * method creates a single Harvester object which drives the application.
50
 * 
51
 * @author    costa
52
 * 
53
 */
54
public class Harvester {
55

    
56
  /*
57
   * Class fields
58
   */
59
  private static final String CONFIG_DIR = "../../build/war/WEB-INF";
60
  private static final String CONFIG_DIR_TEST = "./build/war/WEB-INF";
61
  private static final String CONFIG_NAME = "metacat.properties";
62
  public static final String filler = "*";
63
  public static final String marker =
64
"*****************************************************************************";
65
  public static Options options = null;
66
   
67

    
68
  /* 
69
   * Class methods
70
   */
71
   
72

    
73
  /**
74
   * Constructor. Creates a new instance of Harvester.
75
   */
76
  public Harvester() {
77
  }
78
    
79

    
80
  /**
81
   * Loads Harvester options from a configuration file.
82
   */
83
  public static void loadOptions(boolean test) {
84
    String configDir = test ? CONFIG_DIR_TEST : CONFIG_DIR;    
85
    File propertyFile = new File(configDir, CONFIG_NAME);
86

    
87
    try {
88
      options = Options.initialize(propertyFile);
89
    } 
90
    catch (IOException e) {
91
      System.out.println("Error in loading options: " + e.getMessage());
92
    }
93

    
94
  }
95
  
96
  
97
  /**
98
    * Harvester main method.
99
    * 
100
    * @param args        the command line arguments
101
    * @throws SAXException
102
    * @throws IOException
103
    * @throws ParserConfigurationException
104
    */
105
  public static void main(String[] args) {
106
    Integer delayDefault = new Integer(0); // Default number of hours delay
107
    int delay = delayDefault.intValue();  // Delay in hours before first harvest
108
    Integer d;                            // Used for determining delay
109
    long delta;                           // endTime - startTime
110
    long endTime;                         // time that a harvest completes
111
    Harvester harvester;                  // object for a single harvest run
112
    Integer maxHarvestsDefault = new Integer(30);    // Default max harvests
113
    int maxHarvests = maxHarvestsDefault.intValue(); // Max number of harvests
114
    Integer mh;                              // used in determining max harvests
115
    int nHarvests = 0;                      // counts the number of harvest runs
116
    final long oneHour = (60 * 60 * 1000);   // milliseconds in one hour
117
    Integer periodDefault = new Integer(24); // Default hours between harvests
118
    int period = periodDefault.intValue();   // Hours between harvests
119
    Integer p;                               // Used in determining the period
120
    long startTime;                          // time that a harvest run starts
121
    boolean test = false;                    // Passed to loadOption()
122

    
123
    System.out.println(marker);
124
    System.out.println("Starting Harvester");
125
    Harvester.loadOptions(test);
126

    
127
    // Parse the delay property. Use default if necessary.    
128
    try {
129
      d = Integer.valueOf(options.getOption("delay"));
130
      delay = d.intValue();
131
    }
132
    catch (NumberFormatException e) {
133
      System.out.println("NumberFormatException: Error parsing delay: " +
134
                         e.getMessage());
135
      System.out.println("Defaulting to delay=" + delayDefault);
136
      delay = delayDefault.intValue();
137
    }
138

    
139
    // Parse the maxHarvests property. Use default if necessary.    
140
    try {
141
      mh = Integer.valueOf(options.getOption("maxHarvests"));
142
      maxHarvests = mh.intValue();
143
    }
144
    catch (NumberFormatException e) {
145
      System.out.println("NumberFormatException: Error parsing maxHarvests: " +
146
                         e.getMessage());
147
      System.out.println("Defaulting to maxHarvests=" + maxHarvestsDefault);
148
      maxHarvests = maxHarvestsDefault.intValue();
149
    }
150

    
151
    // Parse the period property. Use default if necessary.    
152
    try {
153
      p = Integer.valueOf(options.getOption("period"));
154
      period = p.intValue();
155
    }
156
    catch (NumberFormatException e) {
157
      System.out.println("NumberFormatException: Error parsing period: " +
158
                         e.getMessage());
159
      System.out.println("Defaulting to period=" + periodDefault);
160
      period = periodDefault.intValue();
161
    }
162
    
163
    // Sleep for delay number of hours prior to starting first harvest
164
    if (delay > 0) {
165
      try {
166
        System.out.print("First harvest will begin in " + delay);
167
        if (delay == 1) {
168
          System.out.println(" hour.");
169
        }
170
        else {
171
          System.out.println(" hours.");
172
        }
173
        Thread.sleep(delay * oneHour);
174
      }
175
      catch (InterruptedException e) {
176
          System.err.println("InterruptedException: " + e.getMessage());
177
          System.exit(1);
178
      }
179
    }
180

    
181
    // Repeat a new harvest once every period number of hours, until we reach
182
    // the maximum number of harvests. Subtract delta from the time period so 
183
    // that each harvest will start at a fixed interval.
184
    //
185
    while (nHarvests < maxHarvests) {
186
      nHarvests++;
187
      startTime = System.currentTimeMillis();
188
      harvester = new Harvester();                // New object for this harvest
189
      harvester.startup(nHarvests, maxHarvests);  // Start up Harvester
190
      harvester.readHarvestSiteSchedule();        // Read the database table
191
      harvester.harvest();                        // Harvest the documents
192
      harvester.shutdown();                       // Shut down Harvester
193
      endTime = System.currentTimeMillis();
194
      delta = endTime - startTime;
195

    
196
      if (nHarvests < maxHarvests) {
197
        try {
198
          System.out.println("Next harvest will begin in " + 
199
                             period + " hours.");
200
          Thread.sleep((period * oneHour) - delta);
201
        }
202
        catch (InterruptedException e) {
203
          System.err.println("InterruptedException: " + e.getMessage());
204
          System.exit(1);
205
        }
206
      }
207
    }
208
  }
209

    
210

    
211
  /*
212
   * Object fields
213
   */
214

    
215
  /** Database connection */
216
  private Connection conn = null;
217
  
218
  /** Used during development to determine whether to connect to metacat 
219
   *  Sometimes it's useful to test parts of the code without actually
220
   *  connecting to Metacat.
221
   */
222
  private boolean connectToMetacat;
223

    
224
  /** Highest DETAIL_LOG_ID primary key in the HARVEST_DETAIL_LOG table */
225
  private int detailLogID;
226
  
227
  /** Email address of the Harvester Administrator */
228
  String harvesterAdministrator;
229
  
230
  /** Highest HARVEST_LOG_ID primary key in the HARVEST_LOG table */
231
  private int harvestLogID;
232
  
233
  /** End time of this harvest session */
234
  private Date harvestEndTime;
235
  
236
  /** List of HarvestLog objects. Stores log entries for report generation. */
237
  private ArrayList harvestLogList = new ArrayList();
238
  
239
  /** List of HarvestSiteSchedule objects */
240
  private ArrayList harvestSiteScheduleList = new ArrayList();
241
  
242
  /** Start time of this harvest session */
243
  private Date harvestStartTime;
244
  
245
  /** Number of days to save log records. Any that are older are purged. */
246
  int logPeriod;
247
  
248
  /** Metacat client object */
249
  Metacat metacat;
250
  
251
  /** SMTP server for sending mail messages */
252
  String smtpServer;
253
  
254
  /** The timestamp for this harvest run. Used for output only. */
255
  String timestamp;
256
  
257

    
258
  /*
259
   * Object methods
260
   */
261
   
262
  /**
263
   * Creates a new HarvestLog object and adds it to the harvestLogList.
264
   * 
265
   * @param  status          the status of the harvest operation
266
   * @param  message         the message text of the harvest operation
267
   * @param  harvestOperationCode  the harvest operation code
268
   * @param  siteScheduleID  the siteScheduleID for which this operation was
269
   *                         performed. 0 indicates that the operation did not
270
   *                         involve a particular harvest site.
271
   * @param  harvestDocument the associated HarvestDocument object. May be null.
272
   * @param  errorMessage    additional error message pertaining to document
273
   *                         error.
274
   */
275
  void addLogEntry(int    status,
276
                   String message,
277
                   String harvestOperationCode,
278
                   int    siteScheduleID,
279
                   HarvestDocument harvestDocument,
280
                   String errorMessage
281
                  ) {
282
    HarvestLog harvestLog;
283
    int harvestLogID = getHarvestLogID();
284
    int detailLogID;
285

    
286
    /* If there is no associated harvest document, call the basic constructor;
287
     * else call the extended constructor.
288
     */
289
    if (harvestDocument == null) {    
290
      harvestLog = new HarvestLog(this, conn, harvestLogID, harvestStartTime, 
291
                                  status, message, harvestOperationCode, 
292
                                  siteScheduleID);
293
    }
294
    else {
295
      detailLogID = getDetailLogID();
296
      harvestLog = new HarvestLog(this, conn, harvestLogID, detailLogID, 
297
                                  harvestStartTime, status, message,
298
                                  harvestOperationCode, siteScheduleID,
299
                                  harvestDocument, errorMessage);
300
    }
301
    
302
    harvestLogList.add(harvestLog);
303
  }
304
  
305
  
306
  public void closeConnection() {
307
    try {
308
      // Close the database connection
309
      System.out.println("Closing the database connection.");
310
      conn.close();
311
    }
312
    catch (SQLException e) {
313
      System.out.println("Database access failed " + e);
314
    }    
315
  }
316

    
317

    
318
  /**
319
   * Determines whether Harvester should attempt to connect to Metacat.
320
   * Used during development and testing.
321
   * 
322
   * @return     true if Harvester should connect, otherwise false
323
   */
324
  boolean connectToMetacat () {
325
    return connectToMetacat;
326
  }
327
  
328

    
329
  /**
330
   * Normalizes text prior to insertion into the HARVEST_LOG or
331
   * HARVEST_DETAIL_LOG tables. In particular, replaces the single quote
332
   * character with the double quote character. This prevents SQL errors
333
   * involving words that contain single quotes. Also removes \n and \r
334
   * characters from the text.
335
   * 
336
   * @param text  the original string
337
   * @return      a string containing the normalized text
338
   */
339
  public String dequoteText(String text) {
340
    char c;
341
    StringBuffer stringBuffer = new StringBuffer();
342
    
343
    for (int i = 0; i < text.length(); i++) {
344
      c = text.charAt(i);
345
      switch (c) {
346
        case '\'':
347
          stringBuffer.append('\"');
348
          break;
349
        case '\r':
350
        case '\n':
351
          break;
352
        default:
353
          stringBuffer.append(c);
354
          break;
355
      }
356
    }
357
    
358
    return stringBuffer.toString();
359
  }
360
  
361
  /**
362
   * Returns a connection to the database. Opens the connection if a connection
363
   * has not already been made previously.
364
   * 
365
   * @return  conn  the database Connection object
366
   */
367
  public Connection getConnection() {
368
    String dbDriver = "";
369
		String defaultDB;
370
    String password;
371
    String user;
372
    SQLWarning warn;
373
    
374
    if (conn == null) {
375
      dbDriver = options.getOption("dbDriver");
376
      defaultDB = options.getOption("defaultDB");
377
      password = options.getOption("password");
378
      user = options.getOption("user");
379

    
380
      // Load the jdbc driver
381
      try {
382
        Class.forName(dbDriver);
383
      }
384
      catch (ClassNotFoundException e) {
385
        System.out.println("Can't load driver " + e);
386
        System.exit(1);
387
      } 
388

    
389
      // Make the database connection
390
      try {
391
        System.out.println("Getting connection to Harvester tables");
392
        conn = DriverManager.getConnection(defaultDB, user, password);
393

    
394
        // If a SQLWarning object is available, print its warning(s).
395
        // There may be multiple warnings chained.
396
        warn = conn.getWarnings();
397
      
398
        if (warn != null) {
399
          while (warn != null) {
400
            System.out.println("SQLState: " + warn.getSQLState());
401
            System.out.println("Message:  " + warn.getMessage());
402
            System.out.println("Vendor: " + warn.getErrorCode());
403
            System.out.println("");
404
            warn = warn.getNextWarning();
405
          }
406
        }
407
      }
408
      catch (SQLException e) {
409
        System.out.println("Database access failed " + e);
410
        System.exit(1);
411
      }
412
    }
413
    
414
    return conn;
415
  }
416

    
417

    
418
  /**
419
   * Gets the current value of the detailLogID for storage as a primary key in
420
   * the DETAIL_LOG_ID field of the HARVEST_DETAIL_LOG table.
421
   * 
422
   * @return  the current value of the detailLogID
423
   */
424
  public int getDetailLogID() {
425
    int currentValue = detailLogID;
426
    
427
    detailLogID++;
428
    return currentValue;
429
  }
430
  
431
  
432
  /**
433
   * Gets the current value of the harvestLogID for storage as a primary key in
434
   * the HARVEST_LOG_ID field of the HARVEST_LOG table.
435
   * 
436
   * @return  the current value of the detailLogID
437
   */
438
  public int getHarvestLogID() {
439
    int currentValue = harvestLogID;
440
    
441
    harvestLogID++;
442
    return currentValue;
443
  }
444
  
445

    
446
  /** 
447
   * Gets the maximum value of an integer field from a table.
448
   * 
449
   * @param tableName  the database table name
450
   * @param fieldName  the field name of the integer field in the table
451
   * @return  the maximum integer stored in the fieldName field of tableName
452
   */
453
  private int getMaxValue(String tableName, String fieldName) {
454
    int maxValue = 0;
455
    int fieldValue;
456
		String query = "SELECT " + fieldName + " FROM " + tableName;
457
		Statement stmt;
458
    
459
		try {
460
			stmt = conn.createStatement();							
461
			ResultSet rs = stmt.executeQuery(query);
462
	
463
			while (rs.next()) {
464
				fieldValue = rs.getInt(fieldName);
465
        maxValue = Math.max(maxValue, fieldValue);
466
			}
467
	
468
			stmt.close();	
469
		} 
470
    catch(SQLException ex) {
471
			System.out.println("SQLException: " + ex.getMessage());
472
		}
473
    
474
    return maxValue;
475
  }
476
  
477
  
478
  /** 
479
   * Gets the minimum value of an integer field from a table.
480
   * 
481
   * @param tableName  the database table name
482
   * @param fieldName  the field name of the integer field in the table
483
   * @return  the minimum integer stored in the fieldName field of tableName
484
   */
485
  private int getMinValue(String tableName, String fieldName) {
486
    int minValue = 0;
487
    int fieldValue;
488
		String query = "SELECT " + fieldName + " FROM " + tableName;
489
		Statement stmt;
490
    
491
		try {
492
			stmt = conn.createStatement();							
493
			ResultSet rs = stmt.executeQuery(query);
494
	
495
			while (rs.next()) {
496
				fieldValue = rs.getInt(fieldName);
497

    
498
        if (minValue == 0) {
499
          minValue = fieldValue;
500
        }
501
        else {
502
          minValue = Math.min(minValue, fieldValue);
503
        }
504
			}
505
	
506
			stmt.close();	
507
		} 
508
    catch(SQLException ex) {
509
			System.out.println("SQLException: " + ex.getMessage());
510
		}
511
    
512
    return minValue;
513
  }
514
  
515
  
516
  /**
517
   * For every Harvest site schedule in the database, harvest the
518
   * documents for that site if they are due to be harvested.
519
   * 
520
   * @throws SAXException
521
   * @throws IOException
522
   * @throws ParserConfigurationException
523
   */
524
  private void harvest() {
525
    HarvestSiteSchedule harvestSiteSchedule;
526

    
527
    for (int i = 0; i < harvestSiteScheduleList.size(); i++) {
528
      harvestSiteSchedule = (HarvestSiteSchedule)harvestSiteScheduleList.get(i);
529
      harvestSiteSchedule.harvestDocumentList();
530
    }
531
  }
532
  
533
  
534
  /**
535
   * Initializes the detailLogID and harvestLogID values to their current
536
   * maximums + 1.
537
   */
538
  public void initLogIDs() {
539
    detailLogID = getMaxValue("HARVEST_DETAIL_LOG", "DETAIL_LOG_ID") + 1;
540
    harvestLogID = getMaxValue("HARVEST_LOG", "HARVEST_LOG_ID") + 1;
541
  }
542
  
543

    
544
  /**
545
   * Prints the header of the harvest report.
546
   * 
547
   * @param out            the PrintStream object to print to
548
   * @param siteScheduleID the siteScheduleId of the HarvestSiteSchedule. Will
549
   *                       have a value of 0 if no particular site is involved,
550
   *                       which indicates that the report is being prepared
551
   *                       for the Harvester Administrator rather than for a
552
   *                       particular Site Contact.
553
   */
554
  void printHarvestHeader(PrintStream out, int siteScheduleID) {
555
    HarvestLog harvestLog;
556
    int logSiteScheduleID;
557
    int nErrors = 0;
558
    String phrase;
559
    
560
    for (int i = 0; i < harvestLogList.size(); i++) {
561
      harvestLog = (HarvestLog) harvestLogList.get(i);
562
      logSiteScheduleID = harvestLog.getSiteScheduleID();
563
      
564
      if ((siteScheduleID == 0) || (siteScheduleID == logSiteScheduleID)) {
565
        if (harvestLog.isErrorEntry()) {
566
          nErrors++;
567
        }
568
      }      
569
    }
570

    
571
    out.println(marker);
572
    out.println(filler);
573
    out.println("* METACAT HARVESTER REPORT: " + timestamp);
574
    out.println(filler);
575

    
576
    if (nErrors > 0) {
577
      phrase = (nErrors == 1) ? " ERROR WAS " : " ERRORS WERE ";
578
      out.println("* A TOTAL OF " + nErrors + phrase + "DETECTED.");
579
      out.println("* Please see the log entries below for additonal details.");
580
    }
581
    else {
582
      out.println("* NO ERRORS WERE DETECTED DURING THIS HARVEST.");
583
    }
584
    
585
    out.println(filler);
586
    out.println(marker);
587
  }
588
    
589

    
590
  /**
591
   * Prints harvest log entries for this harvest run. Entries may be filtered
592
   * for a particular site, or all entries may be printed.
593
   * 
594
   * @param out            the PrintStream object to write to
595
   * @param maxCodeLevel   the maximum code level that should be printed,
596
   *                       e.g. "warning". Any log entries higher than this
597
   *                       level will not be printed.
598
   * @param siteScheduleID if greater than 0, indicates that the log
599
   *                       entry should only be printed for a particular site
600
   *                       as identified by its siteScheduleID. if 0, then
601
   *                       print output for all sites.
602
   */
603
  void printHarvestLog(PrintStream out, String maxCodeLevel, int siteScheduleID
604
                      ) {
605
    HarvestLog harvestLog;
606
    int logSiteScheduleID;
607
    int nErrors = 0;
608
    String phrase;
609
    
610
    out.println("");
611
    out.println(marker);
612
    out.println(filler);
613
    out.println("*                       LOG ENTRIES");
614
    out.println(filler);
615
    out.println(marker);
616

    
617
    for (int i = 0; i < harvestLogList.size(); i++) {
618
      harvestLog = (HarvestLog) harvestLogList.get(i);
619
      logSiteScheduleID = harvestLog.getSiteScheduleID();
620
      if ((siteScheduleID == 0) || (siteScheduleID == logSiteScheduleID)) {
621
        harvestLog.printOutput(out, maxCodeLevel);
622
      }
623
    }
624
  }
625
    
626

    
627
  /**
628
   * Prints the site schedule data for a given site.
629
   * 
630
   * @param out              the PrintStream to write to
631
   * @param siteScheduleID   the primary key in the HARVEST_SITE_SCHEDULE table
632
   */
633
  void printHarvestSiteSchedule(PrintStream out, int siteScheduleID) {
634
     HarvestSiteSchedule harvestSiteSchedule;
635

    
636
    for (int i = 0; i < harvestSiteScheduleList.size(); i++) {
637
      harvestSiteSchedule = (HarvestSiteSchedule)harvestSiteScheduleList.get(i);
638
      if (harvestSiteSchedule.siteScheduleID == siteScheduleID) {
639
        harvestSiteSchedule.printOutput(out);
640
      }
641
    }
642
  }
643
  
644

    
645
  /**
646
   * Prunes old records from the HARVEST_DETAIL_LOG table. Records are
647
   * removed if the HARVEST_LOG_ID foreign key is less than the lowest
648
   * HARVEST_LOG_ID primary key in the HARVEST_LOG table.
649
   */
650
  private void pruneHarvestDetailLog() {
651
		String deleteString;
652
    int minHarvestLogID;
653
    int recordsDeleted;
654
		Statement stmt;
655
    
656
    minHarvestLogID = getMinValue("HARVEST_LOG", "HARVEST_LOG_ID");
657
    deleteString = "DELETE FROM HARVEST_DETAIL_LOG WHERE HARVEST_LOG_ID < " +
658
                   minHarvestLogID;
659

    
660
		try {
661
			System.out.print("Pruning log entries from HARVEST_DETAIL_LOG: ");
662
      System.out.println(deleteString);
663
			stmt = conn.createStatement();							
664
			recordsDeleted = stmt.executeUpdate(deleteString);
665
			System.out.println(recordsDeleted + " records deleted");
666
			stmt.close();
667
		}
668
    catch(SQLException e) {
669
			System.out.println("SQLException: " + e.getMessage());
670
		}
671
  }
672
    
673

    
674
  /**
675
   * Prunes old records from the HARVEST_LOG table. Records are removed if
676
   * their HARVEST_DATE is older than a given number of days, as stored in the
677
   * logPeriod object field.
678
   */
679
  private void pruneHarvestLog() {
680
    long currentTime = harvestStartTime.getTime(); // time in milliseconds
681
    Date dateLastLog;                    // Prune everything prior to this date
682
		String deleteString;
683
    long delta;
684
    final long millisecondsPerDay = (1000 * 60 * 60 * 24);
685
    int recordsDeleted;
686
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
687
    String dateString;
688
		Statement stmt;
689
    long timeLastLog = 0;
690
    
691
    delta = logPeriod * millisecondsPerDay;
692
    deleteString = "DELETE FROM HARVEST_LOG WHERE HARVEST_DATE < ";
693
    timeLastLog = currentTime - delta;
694
    dateLastLog = new Date(timeLastLog);
695
    dateString = "'" + simpleDateFormat.format(dateLastLog) + "'";
696
    deleteString += dateString;
697

    
698
		try {
699
			System.out.print("Pruning log entries from HARVEST_LOG: ");
700
      System.out.println(deleteString);
701
			stmt = conn.createStatement();							
702
			recordsDeleted = stmt.executeUpdate(deleteString);
703
			System.out.println(recordsDeleted + " records deleted");
704
			stmt.close();
705
		}
706
    catch (SQLException e) {
707
			System.out.println("SQLException: " + e.getMessage());
708
		}
709
  }
710
    
711

    
712
  /**
713
   * Reads the HARVEST_SITE_SCHEDULE table in the database, creating
714
   * a HarvestSiteSchedule object for each row in the table.
715
   */
716
  private void readHarvestSiteSchedule() {
717
    HarvestSiteSchedule harvestSiteSchedule;
718
    ResultSet rs;
719
    SQLWarning warn;
720
    Statement stmt;
721

    
722
    String contactEmail;
723
    String dateLastHarvest;
724
    String dateNextHarvest;
725
    String documentListURL;
726
    String ldapDN;
727
    String ldapPwd;
728
    int siteScheduleID;
729
    String unit;
730
    int updateFrequency;
731
        
732
    try {
733
      // Read the HARVEST_SITE_SCHEDULE table
734
      stmt = conn.createStatement();
735
      rs = stmt.executeQuery("SELECT * FROM HARVEST_SITE_SCHEDULE");
736
      warn = rs.getWarnings();
737

    
738
      if (warn != null) {
739
        System.out.println("\n---Warning---\n");
740

    
741
        while (warn != null) {
742
          System.out.println("Message: " + warn.getMessage());
743
          System.out.println("SQLState: " + warn.getSQLState());
744
          System.out.print("Vendor error code: ");
745
          System.out.println(warn.getErrorCode());
746
          System.out.println("");
747
          warn = warn.getNextWarning();
748
        }
749
      }
750
     
751
      while (rs.next()) {
752
        siteScheduleID = rs.getInt("SITE_SCHEDULE_ID");
753
        documentListURL = rs.getString("DOCUMENTLISTURL");
754
        ldapDN = rs.getString("LDAPDN");
755
        ldapPwd = rs.getString("LDAPPWD");
756
        dateNextHarvest = rs.getString("DATENEXTHARVEST");
757
        dateLastHarvest = rs.getString("DATELASTHARVEST");
758
        updateFrequency = rs.getInt("UPDATEFREQUENCY");
759
        unit = rs.getString("UNIT");
760
        contactEmail = rs.getString("CONTACT_EMAIL");
761
        
762
        warn = rs.getWarnings();
763

    
764
        if (warn != null) {
765
          System.out.println("\n---Warning---\n");
766
      
767
          while (warn != null) {
768
            System.out.println("Message: " + warn.getMessage());
769
            System.out.println("SQLState: " + warn.getSQLState());
770
            System.out.print("Vendor error code: ");
771
            System.out.println(warn.getErrorCode());
772
            System.out.println("");
773
            warn = warn.getNextWarning();
774
          }
775
        }
776
      
777
        harvestSiteSchedule = new HarvestSiteSchedule(this,
778
                                                      siteScheduleID,
779
                                                      documentListURL,
780
                                                      ldapDN,
781
                                                      ldapPwd,
782
                                                      dateNextHarvest,
783
                                                      dateLastHarvest,
784
                                                      updateFrequency,
785
                                                      unit,
786
                                                      contactEmail
787
                                                     );
788
        harvestSiteScheduleList.add(harvestSiteSchedule);
789
      }
790
      
791
      rs.close();
792
      stmt.close();
793
    }
794
    catch (SQLException e) {
795
      System.out.println("Database access failed " + e);
796
      System.exit(1);
797
    }
798
    
799
  }
800
    
801

    
802
  /**
803
   * Sends a report to the Harvester Administrator. The report prints each log
804
   * entry pertaining to this harvest run.
805
   *
806
   * @param maxCodeLevel  the maximum code level that should be printed,
807
   *                      e.g. "warning". Any log entries higher than this
808
   *                      level will not be printed.
809
   */
810
  void reportToAdministrator(String maxCodeLevel) {
811
    PrintStream body;
812
    String from = harvesterAdministrator;
813
    MailMessage msg;
814
    int siteScheduleID = 0;
815
    String subject = "Report from Metacat Harvester: " + timestamp;
816
    String to = harvesterAdministrator;
817
    
818
    if (!to.equals("")) {
819
      System.out.println("Sending report to Harvester Administrator at address "
820
                         + harvesterAdministrator);
821
      
822
      try {
823
        msg = new MailMessage(smtpServer);
824
        msg.from(from);
825
        msg.to(to);
826
        msg.setSubject(subject);
827
        body = msg.getPrintStream();
828
        printHarvestHeader(body, siteScheduleID);
829
        printHarvestLog(body, maxCodeLevel, siteScheduleID);
830
        msg.sendAndClose();
831
      }
832
      catch (IOException e) {
833
        System.out.println("There was a problem sending email to " + to);
834
        System.out.println("IOException: " + e.getMessage());
835
      }
836
    }
837
  }
838
  
839

    
840
  /**
841
   * Sets the harvest start time for this harvest run.
842
   * 
843
   * @param date
844
   */
845
  public void setHarvestStartTime(Date date) {
846
    harvestStartTime = date;
847
  }
848
    
849

    
850
  /**
851
   * Shuts down Harvester. Performs cleanup operations such as logging out
852
   * of Metacat and disconnecting from the database.
853
   */
854
  private void shutdown() {
855
    String maxCodeLevel = "debug";  // Print all log entries from level 1
856
                                    // ("error") to level 5 ("debug")
857
    int siteScheduleID = 0;
858

    
859
    // Log shutdown operation
860
    System.out.println("Shutting Down Harvester");
861
    addLogEntry(0, "Shutting Down Harvester", "HarvesterShutdown", 0, null, "");
862
    pruneHarvestLog();
863
    pruneHarvestDetailLog();
864
    closeConnection();
865
    // Print log to standard output and then email the Harvester administrator
866
    printHarvestLog(System.out, maxCodeLevel, siteScheduleID);
867
    reportToAdministrator(maxCodeLevel);      // Send a copy to harvester admin
868
  }
869
    
870

    
871
  /**
872
   * Initializes Harvester at startup. Connects to the database and to Metacat.
873
   * 
874
   * @param nHarvests        the nth harvest
875
   * @param maxHarvests      the maximum number of harvests that this process
876
   *                         can run
877
   */
878
  private void startup(int nHarvests, int maxHarvests) {
879
    String context;
880
    Boolean ctm;
881
    String httpserver;
882
    Integer lp;
883
    String metacatURL;
884
    Date now = new Date();
885
    
886
    timestamp = now.toString();
887
    System.out.println(Harvester.marker);
888
    System.out.println(timestamp + ": Starting Next Harvest (" +
889
                       nHarvests + "/" + maxHarvests + ")");
890
    ctm = Boolean.valueOf(options.getOption("connectToMetacat"));
891
    connectToMetacat = ctm.booleanValue();
892
    harvesterAdministrator = options.getOption("harvesterAdministrator");
893
    smtpServer = options.getOption("smtpServer");
894

    
895
    try {
896
      lp = Integer.valueOf(options.getOption("logPeriod"));
897
      logPeriod = lp.intValue();
898
    }
899
    catch (NumberFormatException e) {
900
      System.err.println("NumberFormatException: Error parsing logPeriod " +
901
                         logPeriod + e.getMessage());
902
      System.err.println("Defaulting to logPeriod of 90 days");
903
      logPeriod = 90;
904
    }
905

    
906
    conn = getConnection();
907
    initLogIDs();
908
    setHarvestStartTime(now);
909
    // Log startup operation
910
    addLogEntry(0, "Starting Up Harvester", "HarvesterStartup", 0, null, "");
911
      
912
    if (connectToMetacat()) {      
913
      try {
914
        httpserver = options.getOption("httpserver");
915
        context = options.getOption("context");
916
        metacatURL = httpserver + "/" + context + "/servlet/metacat";
917
        System.out.println("Connecting to Metacat: " + metacatURL);
918
        metacat = MetacatFactory.createMetacatConnection(metacatURL);
919
      } 
920
      catch (MetacatInaccessibleException e) {
921
        System.out.println("Metacat connection failed." + e.getMessage());
922
      } 
923
      catch (Exception e) {
924
        System.out.println("Metacat connection failed." + e.getMessage());
925
      }
926
    }
927
  }
928

    
929
}
(6-6/10)