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-04-28 10:54:02 -0700 (Wed, 28 Apr 2004) $'
8
 * '$Revision: 2139 $'
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 java.io.File;
29
import java.io.FileInputStream;
30
import java.io.IOException;
31
import java.io.PrintStream;
32
import java.sql.Connection;
33
import java.sql.DriverManager;
34
import java.sql.ResultSet;
35
import java.sql.SQLException;
36
import java.sql.SQLWarning;
37
import java.sql.Statement;
38
import java.util.ArrayList;
39
import java.text.SimpleDateFormat;
40
import java.util.Date;
41
import java.util.Properties;
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 marker =
60
"*****************************************************************************";
61

    
62
  public static Properties properties;
63
   
64

    
65
  /* 
66
   * Class methods
67
   */
68
   
69

    
70
  /**
71
   * Constructor. Creates a new instance of Harvester.
72
   */
73
  public Harvester() {
74
  }
75
    
76

    
77
  /**
78
   * Loads Harvester properties from a configuration file.
79
   */
80
  public static void loadProperties(File configfile) {
81
    properties = new Properties();
82

    
83
    try {
84
      properties.load(new FileInputStream(configfile));
85
    }
86
    catch (IOException e) {
87
      System.out.println("IOException: " + e.getMessage());
88
      System.exit(1);
89
    }
90
  }
91
  
92
  
93
  /**
94
    * Harvester main method.
95
    * 
96
    * @param args        the command line arguments
97
    * @throws SAXException
98
    * @throws IOException
99
    * @throws ParserConfigurationException
100
    */
101
  public static void main(String[] args) {
102
    File configfile = new File("../../lib/harvester", "harvester.properties");
103
    Integer delayDefault = new Integer(0); // Default number of hours delay
104
    int delay = delayDefault.intValue();  // Delay in hours before first harvest
105
    Integer d;                            // Used for determining delay
106
    long delta;                           // endTime - startTime
107
    long endTime;                         // time that a harvest completes
108
    Harvester harvester;                  // object for a single harvest run
109
    Integer maxHarvestsDefault = new Integer(30);    // Default max harvests
110
    int maxHarvests = maxHarvestsDefault.intValue(); // Max number of harvests
111
    Integer mh;                              // used in determining max harvests
112
    int nHarvests = 0;                      // counts the number of harvest runs
113
    final long oneHour = (60 * 60 * 1000);   // milliseconds in one hour
114
    Integer periodDefault = new Integer(24); // Default hours between harvests
115
    int period = periodDefault.intValue();   // Hours between harvests
116
    Integer p;                               // Used in determining the period
117
    long startTime;                          // time that a harvest run starts
118

    
119
    System.out.println(marker);
120
    System.out.println("Starting Harvester");
121
    Harvester.loadProperties(configfile);
122

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

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

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

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

    
200
      if (nHarvests < maxHarvests) {
201
        try {
202
          Thread.sleep((period * oneHour) - delta);
203
        }
204
        catch (InterruptedException e) {
205
          System.err.println("InterruptedException: " + e.getMessage());
206
          System.exit(1);
207
        }
208
      }
209
    }
210
  }
211

    
212

    
213
  /*
214
   * Object fields
215
   */
216

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

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

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

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

    
319

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

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

    
391
      defaultDB = properties.getProperty("defaultDB");
392
      password = properties.getProperty("password");
393
      user = properties.getProperty("user");
394

    
395
      // Load the jdbc driver
396
      try {
397
        Class.forName(dbDriver);
398
      }
399
      catch (ClassNotFoundException e) {
400
        System.out.println("Can't load driver " + e);
401
        System.exit(1);
402
      } 
403

    
404
      // Make the database connection
405
      try {
406
        System.out.println("Getting connection to Harvester tables");
407
        conn = DriverManager.getConnection(defaultDB, user, password);
408

    
409
        // If a SQLWarning object is available, print its warning(s).
410
        // There may be multiple warnings chained.
411
        warn = conn.getWarnings();
412
      
413
        if (warn != null) {
414
          while (warn != null) {
415
            System.out.println("SQLState: " + warn.getSQLState());
416
            System.out.println("Message:  " + warn.getMessage());
417
            System.out.println("Vendor: " + warn.getErrorCode());
418
            System.out.println("");
419
            warn = warn.getNextWarning();
420
          }
421
        }
422
      }
423
      catch (SQLException e) {
424
        System.out.println("Database access failed " + e);
425
        System.exit(1);
426
      }
427
    }
428
    
429
    return conn;
430
  }
431

    
432

    
433
  /**
434
   * Gets the current value of the detailLogID for storage as a primary key in
435
   * the DETAIL_LOG_ID field of the HARVEST_DETAIL_LOG table.
436
   * 
437
   * @return  the current value of the detailLogID
438
   */
439
  public int getDetailLogID() {
440
    int currentValue = detailLogID;
441
    
442
    detailLogID++;
443
    return currentValue;
444
  }
445
  
446
  
447
  /**
448
   * Gets the current value of the harvestLogID for storage as a primary key in
449
   * the HARVEST_LOG_ID field of the HARVEST_LOG table.
450
   * 
451
   * @return  the current value of the detailLogID
452
   */
453
  public int getHarvestLogID() {
454
    int currentValue = harvestLogID;
455
    
456
    harvestLogID++;
457
    return currentValue;
458
  }
459
  
460

    
461
  /** 
462
   * Gets the maximum value of an integer field from a table.
463
   * 
464
   * @param tableName  the database table name
465
   * @param fieldName  the field name of the integer field in the table
466
   * @return  the maximum integer stored in the fieldName field of tableName
467
   */
468
  private int getMaxValue(String tableName, String fieldName) {
469
    int maxValue = 0;
470
    int fieldValue;
471
		String query = "SELECT " + fieldName + " FROM " + tableName;
472
		Statement stmt;
473
    
474
		try {
475
			stmt = conn.createStatement();							
476
			ResultSet rs = stmt.executeQuery(query);
477
	
478
			while (rs.next()) {
479
				fieldValue = rs.getInt(fieldName);
480
        maxValue = Math.max(maxValue, fieldValue);
481
			}
482
	
483
			stmt.close();	
484
		} 
485
    catch(SQLException ex) {
486
			System.out.println("SQLException: " + ex.getMessage());
487
		}
488
    
489
    return maxValue;
490
  }
491
  
492
  
493
  /** 
494
   * Gets the minimum value of an integer field from a table.
495
   * 
496
   * @param tableName  the database table name
497
   * @param fieldName  the field name of the integer field in the table
498
   * @return  the minimum integer stored in the fieldName field of tableName
499
   */
500
  private int getMinValue(String tableName, String fieldName) {
501
    int minValue = 0;
502
    int fieldValue;
503
		String query = "SELECT " + fieldName + " FROM " + tableName;
504
		Statement stmt;
505
    
506
		try {
507
			stmt = conn.createStatement();							
508
			ResultSet rs = stmt.executeQuery(query);
509
	
510
			while (rs.next()) {
511
				fieldValue = rs.getInt(fieldName);
512

    
513
        if (minValue == 0) {
514
          minValue = fieldValue;
515
        }
516
        else {
517
          minValue = Math.min(minValue, fieldValue);
518
        }
519
			}
520
	
521
			stmt.close();	
522
		} 
523
    catch(SQLException ex) {
524
			System.out.println("SQLException: " + ex.getMessage());
525
		}
526
    
527
    return minValue;
528
  }
529
  
530
  
531
  /**
532
   * For every Harvest site schedule in the database, harvest the
533
   * documents for that site if they are due to be harvested.
534
   * 
535
   * @throws SAXException
536
   * @throws IOException
537
   * @throws ParserConfigurationException
538
   */
539
  private void harvest() {
540
    HarvestSiteSchedule harvestSiteSchedule;
541

    
542
    for (int i = 0; i < harvestSiteScheduleList.size(); i++) {
543
      harvestSiteSchedule = (HarvestSiteSchedule)harvestSiteScheduleList.get(i);
544
      harvestSiteSchedule.harvestDocumentList();
545
    }
546
  }
547
  
548
  
549
  /**
550
   * Initializes the detailLogID and harvestLogID values to their current
551
   * maximums + 1.
552
   */
553
  public void initLogIDs() {
554
    detailLogID = getMaxValue("HARVEST_DETAIL_LOG", "DETAIL_LOG_ID") + 1;
555
    harvestLogID = getMaxValue("HARVEST_LOG", "HARVEST_LOG_ID") + 1;
556
  }
557
  
558

    
559
  /**
560
   * Prints harvest log entries for this harvest run. Entries may be filtered
561
   * for a particular site, or all entries may be printed.
562
   * 
563
   * @param out            the PrintStream object to write to
564
   * @param maxCodeLevel   the maximum code level that should be printed,
565
   *                       e.g. "warning". Any log entries higher than this
566
   *                       level will not be printed.
567
   * @param siteScheduleID if greater than 0, indicates that the log
568
   *                       entry should only be printed for a particular site
569
   *                       as identified by its siteScheduleID. if 0, then
570
   *                       print output for all sites.
571
   */
572
  void printHarvestLog(PrintStream out, 
573
                       String maxCodeLevel, 
574
                       int siteScheduleID
575
                      ) {
576
    HarvestLog harvestLog;
577
    int logSiteScheduleID;
578
    int nErrors = 0;
579
    String phrase;
580
    
581
    for (int i = 0; i < harvestLogList.size(); i++) {
582
      harvestLog = (HarvestLog) harvestLogList.get(i);
583
      logSiteScheduleID = harvestLog.getSiteScheduleID();
584
      
585
      if ((siteScheduleID == 0) || (siteScheduleID == logSiteScheduleID)) {
586
        if (harvestLog.isErrorEntry()) {
587
          nErrors++;
588
        }
589
      }      
590
    }
591

    
592
    out.println(marker);
593
    out.println("*");
594
    out.println("* METACAT HARVESTER REPORT: " + timestamp);
595
    out.println("*");
596

    
597
    if (nErrors > 0) {
598
      phrase = (nErrors == 1) ? " ERROR WAS " : " ERRORS WERE ";
599
      out.println("* A TOTAL OF " + nErrors + phrase + "DETECTED.");
600
      out.println("* Please see the log entries below for additonal details.");
601
    }
602
    else {
603
      out.println("* NO ERRORS WERE DETECTED DURING THIS HARVEST.");
604
    }
605
    
606
    out.println("*");
607
    out.println(marker);
608

    
609
    for (int i = 0; i < harvestLogList.size(); i++) {
610
      harvestLog = (HarvestLog) harvestLogList.get(i);
611
      logSiteScheduleID = harvestLog.getSiteScheduleID();
612
      if ((siteScheduleID == 0) || (siteScheduleID == logSiteScheduleID)) {
613
        harvestLog.printOutput(out, maxCodeLevel);
614
      }
615
    }
616
  }
617
    
618

    
619
  /**
620
   * Prints the site schedule data for a given site.
621
   * 
622
   * @param out              the PrintStream to write to
623
   * @param siteScheduleID   the primary key in the HARVEST_SITE_SCHEDULE table
624
   */
625
  void printHarvestSiteSchedule(PrintStream out, int siteScheduleID) {
626
     HarvestSiteSchedule harvestSiteSchedule;
627

    
628
    for (int i = 0; i < harvestSiteScheduleList.size(); i++) {
629
      harvestSiteSchedule = (HarvestSiteSchedule)harvestSiteScheduleList.get(i);
630
      if (harvestSiteSchedule.siteScheduleID == siteScheduleID) {
631
        harvestSiteSchedule.printOutput(out);
632
      }
633
    }
634
  }
635
  
636

    
637
  /**
638
   * Prunes old records from the HARVEST_DETAIL_LOG table. Records are
639
   * removed if the HARVEST_LOG_ID foreign key is less than the lowest
640
   * HARVEST_LOG_ID primary key in the HARVEST_LOG table.
641
   */
642
  private void pruneHarvestDetailLog() {
643
		String deleteString;
644
    int minHarvestLogID;
645
    int recordsDeleted;
646
		Statement stmt;
647
    
648
    minHarvestLogID = getMinValue("HARVEST_LOG", "HARVEST_LOG_ID");
649
    deleteString = "DELETE FROM HARVEST_DETAIL_LOG WHERE HARVEST_LOG_ID < " +
650
                   minHarvestLogID;
651

    
652
		try {
653
			System.out.print("Pruning log entries from HARVEST_DETAIL_LOG: ");
654
			stmt = conn.createStatement();							
655
			recordsDeleted = stmt.executeUpdate(deleteString);
656
			System.out.println(recordsDeleted + " records deleted");
657
			stmt.close();
658
		}
659
    catch(SQLException e) {
660
			System.out.println("SQLException: " + e.getMessage());
661
		}
662
  }
663
    
664

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

    
689
		try {
690
			System.out.print("Pruning log entries from HARVEST_LOG: ");
691
			stmt = conn.createStatement();							
692
			recordsDeleted = stmt.executeUpdate(deleteString);
693
			System.out.println(recordsDeleted + " records deleted");
694
			stmt.close();
695
		}
696
    catch (SQLException e) {
697
			System.out.println("SQLException: " + e.getMessage());
698
		}
699
  }
700
    
701

    
702
  /**
703
   * Reads the HARVEST_SITE_SCHEDULE table in the database, creating
704
   * a HarvestSiteSchedule object for each row in the table.
705
   */
706
  private void readHarvestSiteSchedule() {
707
    HarvestSiteSchedule harvestSiteSchedule;
708
    ResultSet rs;
709
    SQLWarning warn;
710
    Statement stmt;
711

    
712
    String contactEmail;
713
    String dateLastHarvest;
714
    String dateNextHarvest;
715
    String documentListURL;
716
    String ldapDN;
717
    String ldapPwd;
718
    int siteScheduleID;
719
    String unit;
720
    int updateFrequency;
721
        
722
    try {
723
      // Read the HARVEST_SITE_SCHEDULE table
724
      stmt = conn.createStatement();
725
      rs = stmt.executeQuery("SELECT * FROM HARVEST_SITE_SCHEDULE");
726
      warn = rs.getWarnings();
727

    
728
      if (warn != null) {
729
        System.out.println("\n---Warning---\n");
730

    
731
        while (warn != null) {
732
          System.out.println("Message: " + warn.getMessage());
733
          System.out.println("SQLState: " + warn.getSQLState());
734
          System.out.print("Vendor error code: ");
735
          System.out.println(warn.getErrorCode());
736
          System.out.println("");
737
          warn = warn.getNextWarning();
738
        }
739
      }
740
     
741
      while (rs.next()) {
742
        siteScheduleID = rs.getInt("SITE_SCHEDULE_ID");
743
        documentListURL = rs.getString("DOCUMENTLISTURL");
744
        ldapDN = rs.getString("LDAPDN");
745
        ldapPwd = rs.getString("LDAPPWD");
746
        dateNextHarvest = rs.getString("DATENEXTHARVEST");
747
        dateLastHarvest = rs.getString("DATELASTHARVEST");
748
        updateFrequency = rs.getInt("UPDATEFREQUENCY");
749
        unit = rs.getString("UNIT");
750
        contactEmail = rs.getString("CONTACT_EMAIL");
751
        
752
        warn = rs.getWarnings();
753

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

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

    
829
  /**
830
   * Sets the harvest start time for this harvest run.
831
   * 
832
   * @param date
833
   */
834
  public void setHarvestStartTime(Date date) {
835
    harvestStartTime = date;
836
  }
837
    
838

    
839
  /**
840
   * Shuts down Harvester. Performs cleanup operations such as logging out
841
   * of Metacat and disconnecting from the database.
842
   */
843
  private void shutdown() {
844
    String maxCodeLevel = "debug";  // Print all log entries from level 1
845
                                    // ("error") to level 5 ("debug")
846
    int siteScheduleID = 0;
847

    
848
    // Log shutdown operation
849
    System.out.println("Shutting Down Harvester");
850
    addLogEntry(0, "Shutting Down Harvester", "HarvesterShutdown", 0, null, "");
851
    pruneHarvestLog();
852
    pruneHarvestDetailLog();
853
    closeConnection();
854
    // Print log to standard output and then email the Harvester administrator
855
    printHarvestLog(System.out, maxCodeLevel, siteScheduleID);
856
    reportToAdministrator(maxCodeLevel);      // Send a copy to harvester admin
857
  }
858
    
859

    
860
  /**
861
   * Initializes Harvester at startup. Connects to the database and to Metacat.
862
   * 
863
   * @param nHarvests        the nth harvest
864
   * @param maxHarvests      the maximum number of harvests that this process
865
   *                         can run
866
   */
867
  private void startup(int nHarvests, int maxHarvests) {
868
    Boolean ctm;
869
    Integer lp;
870
    String metacatURL;
871
    Date now = new Date();
872
    Properties properties = Harvester.properties;
873
    
874
    timestamp = now.toString();
875
    System.out.println(Harvester.marker);
876
    System.out.println(timestamp + ": Starting Next Harvest (" +
877
                       nHarvests + "/" + maxHarvests + ")");
878
    ctm = Boolean.valueOf(properties.getProperty("connectToMetacat", "true"));
879
    connectToMetacat = ctm.booleanValue();
880
    harvesterAdministrator = properties.getProperty("harvesterAdministrator");
881
    smtpServer = properties.getProperty("smtpServer", "localhost");
882

    
883
    try {
884
      lp = Integer.valueOf(properties.getProperty("logPeriod", "90"));
885
      logPeriod = lp.intValue();
886
    }
887
    catch (NumberFormatException e) {
888
      System.err.println("NumberFormatException: Error parsing logPeriod " +
889
                         logPeriod + e.getMessage());
890
      System.err.println("Defaulting to logPeriod of 90 days");
891
      logPeriod = 90;
892
    }
893

    
894
    conn = getConnection();
895
    initLogIDs();
896
    setHarvestStartTime(now);
897
    // Log startup operation
898
    addLogEntry(0, "Starting Up Harvester", "HarvesterStartup", 0, null, "");
899
      
900
    if (connectToMetacat()) {      
901
      try {
902
        metacatURL = properties.getProperty("metacatURL");
903
        System.out.println("Connecting to Metacat: " + metacatURL);
904
        metacat = MetacatFactory.createMetacatConnection(metacatURL);
905
      } 
906
      catch (MetacatInaccessibleException e) {
907
        System.out.println("Metacat connection failed." + e.getMessage());
908
      } 
909
      catch (Exception e) {
910
        System.out.println("Metacat connection failed." + e.getMessage());
911
      }
912
    }
913
  }
914

    
915
}
(5-5/9)