Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A class to asyncronously do delta-T replication checking
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Chad Berkley
7
 *
8
 *   '$Author: leinfelder $'
9
 *     '$Date: 2011-05-25 11:59:00 -0700 (Wed, 25 May 2011) $'
10
 * '$Revision: 6099 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.replication;
28

    
29
import edu.ucsb.nceas.metacat.CatalogMessageHandler;
30
import edu.ucsb.nceas.metacat.DBUtil;
31
import edu.ucsb.nceas.metacat.DocInfoHandler;
32
import edu.ucsb.nceas.metacat.DocumentImpl;
33
import edu.ucsb.nceas.metacat.DocumentImplWrapper;
34
import edu.ucsb.nceas.metacat.EventLog;
35
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
36
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
37
import edu.ucsb.nceas.metacat.accesscontrol.XMLAccessDAO;
38
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
39
import edu.ucsb.nceas.metacat.database.DBConnection;
40
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
41
import edu.ucsb.nceas.metacat.database.DatabaseService;
42
import edu.ucsb.nceas.metacat.properties.PropertyService;
43
import edu.ucsb.nceas.metacat.shared.HandlerException;
44
import edu.ucsb.nceas.metacat.util.MetacatUtil;
45
import edu.ucsb.nceas.metacat.IdentifierManager;
46
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
47

    
48
import java.sql.*;
49
import java.util.*;
50
import java.util.Date;
51
import java.io.*;
52
import java.net.*;
53
import java.text.*;
54

    
55
import org.apache.log4j.Logger;
56
import org.dataone.service.types.SystemMetadata;
57
import org.xml.sax.ContentHandler;
58
import org.xml.sax.ErrorHandler;
59
import org.xml.sax.InputSource;
60
import org.xml.sax.SAXException;
61
import org.xml.sax.XMLReader;
62
import org.xml.sax.helpers.XMLReaderFactory;
63
import org.xml.sax.helpers.DefaultHandler;
64

    
65

    
66

    
67
/**
68
 * This class handles deltaT replication checking.  Whenever this TimerTask
69
 * is fired it checks each server in xml_replication for updates and updates
70
 * the local db as needed.
71
 */
72
public class ReplicationHandler extends TimerTask
73
{
74
  int serverCheckCode = 1;
75
  ReplicationServerList serverList = null;
76
  //PrintWriter out;
77
//  private static final AbstractDatabase dbAdapter = MetacatUtil.dbAdapter;
78
  private static Logger logReplication = Logger.getLogger("ReplicationLogging");
79
  private static Logger logMetacat = Logger.getLogger(ReplicationHandler.class);
80
  private static Logger logD1 = Logger.getLogger("DataOneLogger");
81
  
82
  private static int DOCINSERTNUMBER = 1;
83
  private static int DOCERRORNUMBER  = 1;
84
  private static int REVINSERTNUMBER = 1;
85
  private static int REVERRORNUMBER  = 1;
86
  
87
  private static int _xmlDocQueryCount = 0;
88
  private static int _xmlRevQueryCount = 0;
89
  private static long _xmlDocQueryTime = 0;
90
  private static long _xmlRevQueryTime = 0;
91
  
92
  
93
  public ReplicationHandler()
94
  {
95
    //this.out = o;
96
    serverList = new ReplicationServerList();
97
  }
98

    
99
  public ReplicationHandler(int serverCheckCode)
100
  {
101
    //this.out = o;
102
    this.serverCheckCode = serverCheckCode;
103
    serverList = new ReplicationServerList();
104
  }
105

    
106
  /**
107
   * Method that implements TimerTask.run().  It runs whenever the timer is
108
   * fired.
109
   */
110
  public void run()
111
  {
112
    //find out the last_checked time of each server in the server list and
113
    //send a query to each server to see if there are any documents in
114
    //xml_documents with an update_date > last_checked
115
	  
116
      //if serverList is null, metacat don't need to replication
117
      if (serverList==null||serverList.isEmpty())
118
      {
119
        return;
120
      }
121
      updateCatalog();
122
      update();
123
      //conn.close();
124
  }
125

    
126
  /**
127
   * Method that uses revision tagging for replication instead of update_date.
128
   */
129
  private void update()
130
  {
131
	  
132
	  _xmlDocQueryCount = 0;
133
	  _xmlRevQueryCount = 0;
134
	  _xmlDocQueryTime = 0;
135
	  _xmlRevQueryTime = 0;
136
    /*
137
     Pseudo-algorithm
138
     - request a doc list from each server in xml_replication
139
     - check the rev number of each of those documents agains the
140
       documents in the local database
141
     - pull any documents that have a lesser rev number on the local server
142
       from the remote server
143
     - delete any documents that still exist in the local xml_documents but
144
       are in the deletedDocuments tag of the remote host response.
145
     - update last_checked to keep track of the last time it was checked.
146
       (this info is theoretically not needed using this system but probably
147
       should be kept anyway)
148
    */
149

    
150
    ReplicationServer replServer = null; // Variable to store the
151
                                        // ReplicationServer got from
152
                                        // Server list
153
    String server = null; // Variable to store server name
154
//    String update;
155
    Vector<String> responses = new Vector<String>();
156
    URL u;
157
    long replicationStartTime = System.currentTimeMillis();
158
    long timeToGetServerList = 0;
159
    
160
    //Check for every server in server list to get updated list and put
161
    // them in to response
162
    long startTimeToGetServers = System.currentTimeMillis();
163
    for (int i=0; i<serverList.size(); i++)
164
    {
165
        // Get ReplicationServer object from server list
166
        replServer = serverList.serverAt(i);
167
        // Get server name from ReplicationServer object
168
        server = replServer.getServerName().trim();
169
        String result = null;
170
        logReplication.info("ReplicationHandler.update - full update started to: " + server);
171
        // Send command to that server to get updated docid information
172
        try
173
        {
174
          u = new URL("https://" + server + "?server="
175
          +MetacatUtil.getLocalReplicationServerName()+"&action=update");
176
          logReplication.info("ReplicationHandler.update - Sending infomation " +u.toString());
177
          result = ReplicationService.getURLContent(u);
178
        }
179
        catch (Exception e)
180
        {
181
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
182
          logReplication.error( "ReplicationHandler.update - Failed to get updated doc list "+
183
                       "for server " + server + " because "+e.getMessage());
184
          continue;
185
        }
186

    
187
        //logReplication.info("ReplicationHandler.update - docid: "+server+" "+result);
188
        //check if result have error or not, if has skip it.
189
        if (result.indexOf("<error>")!=-1 && result.indexOf("</error>")!=-1)
190
        {
191
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
192
          logReplication.error( "ReplicationHandler.update - Failed to get updated doc list "+
193
                       "for server " + server + " because "+result);
194
          continue;
195
        }
196
        //Add result to vector
197
        responses.add(result);
198
    }
199
    timeToGetServerList = System.currentTimeMillis() - startTimeToGetServers;
200

    
201
    //make sure that there is updated file list
202
    //If response is null, metacat don't need do anything
203
    if (responses==null || responses.isEmpty())
204
    {
205
    	logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
206
        logReplication.info( "ReplicationHandler.update - No updated doc list for "+
207
                           "every server and failed to replicate");
208
        return;
209
    }
210

    
211

    
212
    //logReplication.info("ReplicationHandler.update - Responses from remote metacat about updated "+
213
    //               "document information: "+ responses.toString());
214
    
215
    long totalServerListParseTime = 0;
216
    // go through response vector(it contains updated vector and delete vector
217
    for(int i=0; i<responses.size(); i++)
218
    {
219
    	long startServerListParseTime = System.currentTimeMillis();
220
    	XMLReader parser;
221
    	ReplMessageHandler message = new ReplMessageHandler();
222
    	try
223
        {
224
          parser = initParser(message);
225
        }
226
        catch (Exception e)
227
        {
228
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
229
          logReplication.error("ReplicationHandler.update - Failed to replicate becaue couldn't " +
230
                                " initParser for message and " +e.getMessage());
231
           // stop replication
232
           return;
233
        }
234
    	
235
        try
236
        {
237
          parser.parse(new InputSource(
238
                     new StringReader(
239
                     (String)(responses.elementAt(i)))));
240
        }
241
        catch(Exception e)
242
        {
243
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
244
          logReplication.error("ReplicationHandler.update - Couldn't parse one responses "+
245
                                   "because "+ e.getMessage());
246
          continue;
247
        }
248
        //v is the list of updated documents
249
        Vector<Vector<String>> updateList = new Vector<Vector<String>>(message.getUpdatesVect());
250
        logReplication.info("ReplicationHandler.update - The document list size is "+updateList.size()+ " from "+message.getServerName());
251
        //d is the list of deleted documents
252
        Vector<Vector<String>> deleteList = new Vector<Vector<String>>(message.getDeletesVect());
253
        logReplication.info("ReplicationHandler.update - Update vector size: "+ updateList.size()+" from "+message.getServerName());
254
        logReplication.info("ReplicationHandler.update - Delete vector size: "+ deleteList.size()+" from "+message.getServerName());
255
        logReplication.info("ReplicationHandler.update - The delete document list size is "+deleteList.size()+" from "+message.getServerName());
256
        // go though every element in updated document vector
257
        handleDocList(updateList, DocumentImpl.DOCUMENTTABLE);
258
        //handle deleted docs
259
        for(int k=0; k<deleteList.size(); k++)
260
        { //delete the deleted documents;
261
          Vector<String> w = new Vector<String>(deleteList.elementAt(k));
262
          String docId = (String)w.elementAt(0);
263
          try
264
          {
265
            handleDeleteSingleDocument(docId, server);
266
          }
267
          catch (Exception ee)
268
          {
269
            continue;
270
          }
271
        }//for delete docs
272
        
273
        // handle replicate doc in xml_revision
274
        Vector<Vector<String>> revisionList = new Vector<Vector<String>>(message.getRevisionsVect());
275
        logReplication.info("ReplicationHandler.update - The revision document list size is "+revisionList.size()+ " from "+message.getServerName());
276
        handleDocList(revisionList, DocumentImpl.REVISIONTABLE);
277
        DOCINSERTNUMBER = 1;
278
        DOCERRORNUMBER  = 1;
279
        REVINSERTNUMBER = 1;
280
        REVERRORNUMBER  = 1;
281
        
282
        totalServerListParseTime += (System.currentTimeMillis() - startServerListParseTime);
283
    }//for response
284

    
285
    //updated last_checked
286
    for (int i=0;i<serverList.size(); i++)
287
    {
288
       // Get ReplicationServer object from server list
289
       replServer = serverList.serverAt(i);
290
       try
291
       {
292
         updateLastCheckTimeForSingleServer(replServer);
293
       }
294
       catch(Exception e)
295
       {
296
         continue;
297
       }
298
    }//for
299
    
300
    long replicationEndTime = System.currentTimeMillis();
301
    logMetacat.debug("ReplicationHandler.update - Total replication time: " + 
302
    		(replicationEndTime - replicationStartTime));
303
    logMetacat.debug("ReplicationHandler.update - time to get server list: " + 
304
    		timeToGetServerList);
305
    logMetacat.debug("ReplicationHandler.update - server list parse time: " + 
306
    		totalServerListParseTime);
307
    logMetacat.debug("ReplicationHandler.update - 'in xml_documents' total query count: " + 
308
    		_xmlDocQueryCount);
309
    logMetacat.debug("ReplicationHandler.update - 'in xml_documents' total query time: " + 
310
    		_xmlDocQueryTime + " ms");
311
    logMetacat.debug("ReplicationHandler.update - 'in xml_revisions' total query count: " + 
312
    		_xmlRevQueryCount);
313
    logMetacat.debug("ReplicationHandler.update - 'in xml_revisions' total query time: " + 
314
    		_xmlRevQueryTime + " ms");;
315

    
316
  }//update
317

    
318
  /* Handle replicate single xml document*/
319
  private void handleSingleXMLDocument(String remoteserver, String actions,
320
                                       String accNumber, String tableName)
321
               throws HandlerException
322
  {
323
    DBConnection dbConn = null;
324
    int serialNumber = -1;
325
    try
326
    {
327
      // Get DBConnection from pool
328
      dbConn=DBConnectionPool.
329
                  getDBConnection("ReplicationHandler.handleSingleXMLDocument");
330
      serialNumber=dbConn.getCheckOutSerialNumber();
331
      //if the document needs to be updated or inserted, this is executed
332
      String readDocURLString = "https://" + remoteserver + "?server="+
333
              MetacatUtil.getLocalReplicationServerName()+"&action=read&docid="+accNumber;
334
      readDocURLString = MetacatUtil.replaceWhiteSpaceForURL(readDocURLString);
335
      URL u = new URL(readDocURLString);
336

    
337
      // Get docid content
338
      String newxmldoc = ReplicationService.getURLContent(u);
339
      // If couldn't get skip it
340
      if ( newxmldoc.indexOf("<error>")!= -1 && newxmldoc.indexOf("</error>")!=-1)
341
      {
342
         throw new HandlerException("ReplicationHandler.handleSingleXMLDocument - " + newxmldoc);
343
      }
344
      //logReplication.info("xml documnet:");
345
      //logReplication.info(newxmldoc);
346

    
347
      // Try get the docid info from remote server
348
      DocInfoHandler dih = new DocInfoHandler();
349
      XMLReader docinfoParser = initParser(dih);
350
      String docInfoURLStr = "https://" + remoteserver +
351
                       "?server="+MetacatUtil.getLocalReplicationServerName()+
352
                       "&action=getdocumentinfo&docid="+accNumber;
353
      docInfoURLStr = MetacatUtil.replaceWhiteSpaceForURL(docInfoURLStr);
354
      URL docinfoUrl = new URL(docInfoURLStr);
355
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - Sending message: " +
356
                                                  docinfoUrl.toString());
357
      String docInfoStr = ReplicationService.getURLContent(docinfoUrl);
358
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
359
      Hashtable<String, String> docinfoHash = dih.getDocInfo();
360
      // Get home server of the docid
361
      String docHomeServer = docinfoHash.get("home_server");
362
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - doc home server in repl: "+docHomeServer);
363
      String createdDate = docinfoHash.get("date_created");
364
      String updatedDate = docinfoHash.get("date_updated");
365
      //docid should include rev number too
366
      /*String accnum=docId+util.getProperty("document.accNumSeparator")+
367
                                              (String)docinfoHash.get("rev");*/
368
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - docid in repl: "+accNumber);
369
      String docType = docinfoHash.get("doctype");
370
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - doctype in repl: "+docType);
371

    
372
      String parserBase = null;
373
      // this for eml2 and we need user eml2 parser
374
      if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE))
375
      {
376
         parserBase = DocumentImpl.EML200;
377
      }
378
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE))
379
      {
380
        parserBase = DocumentImpl.EML200;
381
      }
382
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE))
383
      {
384
        parserBase = DocumentImpl.EML210;
385
      }
386
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE))
387
      {
388
        parserBase = DocumentImpl.EML210;
389
      }
390
      // Write the document into local host
391
      DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false);
392
      String newDocid = wrapper.writeReplication(dbConn,
393
                              newxmldoc,
394
                              docinfoHash.get("public_access"),
395
                              null,  /* the dtd text */
396
                              actions,
397
                              accNumber,
398
                              null, //docinfoHash.get("user_owner"),                              
399
                              null, /* null for groups[] */
400
                              docHomeServer,
401
                              remoteserver, tableName, true,// true is for time replication 
402
                              createdDate,
403
                              updatedDate);
404
      
405
      //set the user information
406
      String user = (String) docinfoHash.get("user_owner");
407
      String updated = (String) docinfoHash.get("user_updated");
408
      ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
409
      
410
      //process extra access rules 
411
      Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
412
      if (xmlAccessDAOList != null) {
413
      	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
414
      	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
415
      		if (!acfsf.accessControlExists(xmlAccessDAO)) {
416
      			acfsf.insertPermissions(xmlAccessDAO);
417
      		}
418
          }
419
      }
420
      
421
      //process guid
422
      String guid = docinfoHash.get("guid");
423
      logMetacat.debug("Guid passed from docinfo hash: " + guid);
424
      if (guid != null) {
425
          // if the guid was passed in, put it in the identifiers table
426
    	  if (!IdentifierManager.getInstance().identifierExists(guid)) {
427
    		  logReplication.debug("Creating guid/docid mapping for docid " + docinfoHash.get("docid") + " and guid: " + guid);
428
    		  IdentifierManager.getInstance().createMapping(guid, docinfoHash.get("docid"));
429
    	  } else {
430
    		  logReplication.debug("Updating guid/docid mapping for docid " + docinfoHash.get("docid") + " and guid: " + guid);
431
    		  IdentifierManager.getInstance().updateMapping(guid, docinfoHash.get("docid"));
432
    	  }
433
    	  // process system metadata
434
    	  Date dateUploaded = new Date(new Long(docinfoHash.get("date_uploaded")));
435
          Date dateModified = new Date(new Long(docinfoHash.get("date_modified")));
436
          SystemMetadata sysMeta = IdentifierManager.getInstance().asSystemMetadata(
437
                  dateUploaded, 
438
                  docinfoHash.get("rights_holder"),
439
                  docinfoHash.get("checksum"), 
440
                  docinfoHash.get("checksum_algorithm"), 
441
                  docinfoHash.get("origin_member_node"),
442
                  docinfoHash.get("authoritive_member_node"), 
443
                  dateModified,
444
                  docinfoHash.get("submitter"),
445
                  docinfoHash.get("guid"),
446
                  docinfoHash.get("object_format"),
447
                  new Long(docinfoHash.get("size")).longValue());
448
          if (actions.equalsIgnoreCase("INSERT")) {
449
	    	  logMetacat.debug("Creating systemMetadata for guid: " + guid);
450
	    	  IdentifierManager.getInstance().createSystemMetadata(sysMeta);
451
          }
452
          IdentifierManager.getInstance().updateSystemMetadata(sysMeta);
453
      }
454
      
455
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - Successfully replicated doc " + accNumber);
456
      if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
457
      {
458
        logReplication.info("ReplicationHandler.handleSingleXMLDocument - " + DOCINSERTNUMBER + " Wrote xml doc " + accNumber +
459
                                     " into "+tableName + " from " +
460
                                         remoteserver);
461
        DOCINSERTNUMBER++;
462
      }
463
      else
464
      {
465
          logReplication.info("ReplicationHandler.handleSingleXMLDocument - " +REVINSERTNUMBER + " Wrote xml doc " + accNumber +
466
                  " into "+tableName + " from " +
467
                      remoteserver);
468
          REVINSERTNUMBER++;
469
      }
470
      String ip = getIpFromURL(u);
471
      EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
472
      
473

    
474
    }//try
475
    catch(Exception e)
476
    {
477
        
478
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
479
        {
480
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
481
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +DOCERRORNUMBER + " Failed to write xml doc " + accNumber +
482
                                       " into "+tableName + " from " +
483
                                           remoteserver + " because "+e.getMessage());
484
          DOCERRORNUMBER++;
485
        }
486
        else
487
        {
488
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
489
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +REVERRORNUMBER + " Failed to write xml doc " + accNumber +
490
                    " into "+tableName + " from " +
491
                        remoteserver +" because "+e.getMessage());
492
            REVERRORNUMBER++;
493
        }
494
        logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
495
        logReplication.error("ReplicationHandler.handleSingleXMLDocument - Failed to write doc " + accNumber +
496
                                      " into db because " +e.getMessage());
497
      throw new HandlerException("ReplicationHandler.handleSingleXMLDocument - generic exception " 
498
    		  + "writing Replication: " +e.getMessage());
499
    }
500
    finally
501
    {
502
       //return DBConnection
503
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
504
    }//finally
505
    logD1.info("replication.create localId:" + accNumber);
506
  }
507

    
508

    
509

    
510
  /* Handle replicate single xml document*/
511
  private void handleSingleDataFile(String remoteserver, String actions,
512
                                    String accNumber, String tableName)
513
               throws HandlerException
514
  {
515
    logReplication.info("ReplicationHandler.handleSingleDataFile - Try to replicate data file: " + accNumber);
516
    DBConnection dbConn = null;
517
    int serialNumber = -1;
518
    try
519
    {
520
      // Get DBConnection from pool
521
      dbConn=DBConnectionPool.
522
                  getDBConnection("ReplicationHandler.handleSinlgeDataFile");
523
      serialNumber=dbConn.getCheckOutSerialNumber();
524
      // Try get docid info from remote server
525
      DocInfoHandler dih = new DocInfoHandler();
526
      XMLReader docinfoParser = initParser(dih);
527
      String docInfoURLString = "https://" + remoteserver +
528
                  "?server="+MetacatUtil.getLocalReplicationServerName()+
529
                  "&action=getdocumentinfo&docid="+accNumber;
530
      docInfoURLString = MetacatUtil.replaceWhiteSpaceForURL(docInfoURLString);
531
      URL docinfoUrl = new URL(docInfoURLString);
532

    
533
      String docInfoStr = ReplicationService.getURLContent(docinfoUrl);
534
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
535
      Hashtable<String, String> docinfoHash = dih.getDocInfo();
536
      
537
      // Get docid name (such as acl or dataset)
538
      String docName = docinfoHash.get("docname");
539
      // Get doc type (eml public id)
540
      String docType = docinfoHash.get("doctype");
541
      // Get docid home sever. it might be different to remoteserver
542
      // because of hub feature
543
      String docHomeServer = docinfoHash.get("home_server");
544
      String createdDate = docinfoHash.get("date_created");
545
      String updatedDate = docinfoHash.get("date_updated");
546
      //docid should include rev number too
547
      /*String accnum=docId+util.getProperty("document.accNumSeparator")+
548
                                              (String)docinfoHash.get("rev");*/
549

    
550

    
551
      String datafilePath = PropertyService.getProperty("application.datafilepath");
552
      // Get data file content
553
      String readDataURLString = "https://" + remoteserver + "?server="+
554
                                        MetacatUtil.getLocalReplicationServerName()+
555
                                            "&action=readdata&docid="+accNumber;
556
      readDataURLString = MetacatUtil.replaceWhiteSpaceForURL(readDataURLString);
557
      URL u = new URL(readDataURLString);
558
      InputStream input = u.openStream();
559
      //register data file into xml_documents table and wite data file
560
      //into file system
561
      if ( input != null)
562
      {
563
        DocumentImpl.writeDataFileInReplication(input,
564
                                                datafilePath,
565
                                                docName,docType,
566
                                                accNumber,
567
                                                null,
568
                                                docHomeServer,
569
                                                remoteserver,
570
                                                tableName,
571
                                                true, //true means timed replication
572
                                                createdDate,
573
                                                updatedDate);
574
                                         
575
        //set the user information
576
        String user = (String) docinfoHash.get("user_owner");
577
		String updated = (String) docinfoHash.get("user_updated");
578
        ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
579
        
580
        //process extra access rules
581
        Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
582
        if (xmlAccessDAOList != null) {
583
        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
584
        	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
585
        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
586
        			acfsf.insertPermissions(xmlAccessDAO);
587
        		}
588
            }
589
        }
590
        
591
        //process guid
592
        String guid = docinfoHash.get("guid");
593
        logMetacat.debug("Guid passed from docinfo hash: " + guid);
594
        if (guid != null) {
595
            // if the guid was passed in, put it in the identifiers table
596
      	  if (!IdentifierManager.getInstance().identifierExists(guid)) {
597
      		  logReplication.debug("Creating guid/docid mapping for docid " + docinfoHash.get("docid") + " and guid: " + guid);
598
      		  IdentifierManager.getInstance().createMapping(guid, docinfoHash.get("docid"));
599
      	  } else {
600
      		  logReplication.debug("Updating guid/docid mapping for docid " + docinfoHash.get("docid") + " and guid: " + guid);
601
      		  IdentifierManager.getInstance().updateMapping(guid, docinfoHash.get("docid"));
602
      	  }
603
      	  // process system metadata
604
      	  Date dateUploaded = new Date(new Long(docinfoHash.get("date_uploaded")));
605
            Date dateModified = new Date(new Long(docinfoHash.get("date_modified")));
606
            SystemMetadata sysMeta = IdentifierManager.getInstance().asSystemMetadata(
607
                    dateUploaded, 
608
                    docinfoHash.get("rights_holder"),
609
                    docinfoHash.get("checksum"), 
610
                    docinfoHash.get("checksum_algorithm"), 
611
                    docinfoHash.get("origin_member_node"),
612
                    docinfoHash.get("authoritive_member_node"), 
613
                    dateModified,
614
                    docinfoHash.get("submitter"),
615
                    docinfoHash.get("guid"),
616
                    docinfoHash.get("object_format"),
617
                    new Long(docinfoHash.get("size")).longValue());
618
            if (actions.equalsIgnoreCase("INSERT")) {
619
  	    	  logMetacat.debug("Creating systemMetadata for guid: " + guid);
620
  	    	  IdentifierManager.getInstance().createSystemMetadata(sysMeta);
621
            }
622
            IdentifierManager.getInstance().updateSystemMetadata(sysMeta);
623
        }
624
        
625
        logReplication.info("ReplicationHandler.handleSingleDataFile - Successfully to write datafile " + accNumber);
626
        /*MetacatReplication.replLog("wrote datafile " + accNumber + " from " +
627
                                    remote server);*/
628
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
629
        {
630
          logReplication.info("ReplicationHandler.handleSingleDataFile - " + DOCINSERTNUMBER + " Wrote data file" + accNumber +
631
                                       " into "+tableName + " from " +
632
                                           remoteserver);
633
          DOCINSERTNUMBER++;
634
        }
635
        else
636
        {
637
            logReplication.info("ReplicationHandler.handleSingleDataFile - " + REVINSERTNUMBER + " Wrote data file" + accNumber +
638
                    " into "+tableName + " from " +
639
                        remoteserver);
640
            REVINSERTNUMBER++;
641
        }
642
        String ip = getIpFromURL(u);
643
        EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
644
        
645
      }//if
646
      else
647
      {
648
         logReplication.info("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
649
         throw new HandlerException("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
650
      }//else
651

    
652
    }//try
653
    catch(Exception e)
654
    {
655
      /*MetacatReplication.replErrorLog("Failed to try wrote data file " + accNumber +
656
                                      " because " +e.getMessage());*/
657
      if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
658
      {
659
    	logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
660
    	logReplication.error("ReplicationHandler.handleSingleDataFile - " + DOCERRORNUMBER + " Failed to write data file " + accNumber +
661
                                     " into " + tableName + " from " +
662
                                         remoteserver + " because " + e.getMessage());
663
        DOCERRORNUMBER++;
664
      }
665
      else
666
      {
667
    	  logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
668
    	  logReplication.error("ReplicationHandler.handleSingleDataFile - " + REVERRORNUMBER + " Failed to write data file" + accNumber +
669
                  " into " + tableName + " from " +
670
                      remoteserver +" because "+ e.getMessage());
671
          REVERRORNUMBER++;
672
      }
673
      logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
674
      logReplication.error("ReplicationHandler.handleSingleDataFile - Failed to try wrote datafile " + accNumber +
675
                                      " because " + e.getMessage());
676
      throw new HandlerException("ReplicationHandler.handleSingleDataFile - generic exception " 
677
    		  + "writing Replication: " + e.getMessage());
678
    }
679
    finally
680
    {
681
       //return DBConnection
682
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
683
    }//finally
684
    logD1.info("replication.create localId:" + accNumber);
685
  }
686

    
687

    
688

    
689
  /* Handle delete single document*/
690
  private void handleDeleteSingleDocument(String docId, String notifyServer)
691
               throws HandlerException
692
  {
693
    logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Try delete doc: "+docId);
694
    DBConnection dbConn = null;
695
    int serialNumber = -1;
696
    try
697
    {
698
      // Get DBConnection from pool
699
      dbConn=DBConnectionPool.
700
                  getDBConnection("ReplicationHandler.handleDeleteSingleDoc");
701
      serialNumber=dbConn.getCheckOutSerialNumber();
702
      if(!alreadyDeleted(docId))
703
      {
704

    
705
         //because delete method docid should have rev number
706
         //so we just add one for it. This rev number is no sence.
707
         String accnum=docId+PropertyService.getProperty("document.accNumSeparator")+"1";
708
         DocumentImpl.delete(accnum, null, null, notifyServer);
709
         logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Successfully deleted doc " + docId);
710
         logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Doc " + docId + " deleted");
711
         URL u = new URL("https://"+notifyServer);
712
         String ip = getIpFromURL(u);
713
         EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, docId, "delete");
714
      }
715

    
716
    }//try
717
    catch(McdbDocNotFoundException e)
718
    {
719
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
720
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
721
                                 " in db because because " + e.getMessage());
722
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception " 
723
    		  + "when handling document: " + e.getMessage());
724
    }
725
    catch(InsufficientKarmaException e)
726
    {
727
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
728
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
729
                                 " in db because because " + e.getMessage());
730
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception " 
731
    		  + "when handling document: " + e.getMessage());
732
    }
733
    catch(SQLException e)
734
    {
735
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
736
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
737
                                 " in db because because " + e.getMessage());
738
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception " 
739
    		  + "when handling document: " + e.getMessage());
740
    }
741
    catch(Exception e)
742
    {
743
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
744
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
745
                                 " in db because because " + e.getMessage());
746
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception " 
747
    		  + "when handling document: " + e.getMessage());
748
    }
749
    finally
750
    {
751
       //return DBConnection
752
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
753
    }//finally
754
    logD1.info("replication.handleDeleteSingleDocument localId:" + docId);
755
  }
756

    
757
  /* Handle updateLastCheckTimForSingleServer*/
758
  private void updateLastCheckTimeForSingleServer(ReplicationServer repServer)
759
                                                  throws HandlerException
760
  {
761
    String server = repServer.getServerName();
762
    DBConnection dbConn = null;
763
    int serialNumber = -1;
764
    PreparedStatement pstmt = null;
765
    try
766
    {
767
      // Get DBConnection from pool
768
      dbConn=DBConnectionPool.
769
             getDBConnection("ReplicationHandler.updateLastCheckTimeForServer");
770
      serialNumber=dbConn.getCheckOutSerialNumber();
771

    
772
      logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - Try to update last_check for server: "+server);
773
      // Get time from remote server
774
      URL dateurl = new URL("https://" + server + "?server="+
775
      MetacatUtil.getLocalReplicationServerName()+"&action=gettime");
776
      String datexml = ReplicationService.getURLContent(dateurl);
777
      logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - datexml: "+datexml);
778
      if (datexml!=null && !datexml.equals(""))
779
      {
780
         String datestr = datexml.substring(11, datexml.indexOf('<', 11));
781
         StringBuffer sql = new StringBuffer();
782
         /*sql.append("update xml_replication set last_checked = to_date('");
783
         sql.append(datestr).append("', 'YY-MM-DD HH24:MI:SS') where ");
784
         sql.append("server like '").append(server).append("'");*/
785
         sql.append("update xml_replication set last_checked = ");
786
         sql.append(DatabaseService.getInstance().getDBAdapter().toDate(datestr, "MM/DD/YY HH24:MI:SS"));
787
         sql.append(" where server like '").append(server).append("'");
788
         pstmt = dbConn.prepareStatement(sql.toString());
789

    
790
         pstmt.executeUpdate();
791
         dbConn.commit();
792
         pstmt.close();
793
         logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - last_checked updated to "+datestr+" on "
794
                                      + server);
795
      }//if
796
      else
797
      {
798

    
799
         logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - Failed to update last_checked for server "  +
800
                                  server + " in db because couldn't get time "
801
                                  );
802
         throw new Exception("Couldn't get time for server "+ server);
803
      }
804

    
805
    }//try
806
    catch(Exception e)
807
    {
808
      logMetacat.error("ReplicationHandler.updateLastCheckTimeForSingleServer - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
809
      logReplication.error("ReplicationHandler.updateLastCheckTimeForSingleServer - Failed to update last_checked for server " +
810
                                server + " in db because because " + e.getMessage());
811
      throw new HandlerException("ReplicationHandler.updateLastCheckTimeForSingleServer - " 
812
    		  + "Error updating last checked time: " + e.getMessage());
813
    }
814
    finally
815
    {
816
       //return DBConnection
817
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
818
    }//finally
819
  }
820

    
821

    
822

    
823
  /**
824
   * updates xml_catalog with entries from other servers.
825
   */
826
  private void updateCatalog()
827
  {
828
    logReplication.info("ReplicationHandler.updateCatalog - Start of updateCatalog");
829
    // ReplicationServer object in server list
830
    ReplicationServer replServer = null;
831
    PreparedStatement pstmt = null;
832
    String server = null;
833

    
834

    
835
    // Go through each ReplicationServer object in sererlist
836
    for (int j=0; j<serverList.size(); j++)
837
    {
838
      Vector<Vector<String>> remoteCatalog = new Vector<Vector<String>>();
839
      Vector<String> publicId = new Vector<String>();
840
      try
841
      {
842
        // Get ReplicationServer object from server list
843
        replServer = serverList.serverAt(j);
844
        // Get server name from the ReplicationServer object
845
        server = replServer.getServerName();
846
        // Try to get catalog
847
        URL u = new URL("https://" + server + "?server="+
848
        MetacatUtil.getLocalReplicationServerName()+"&action=getcatalog");
849
        logReplication.info("ReplicationHandler.updateCatalog - sending message " + u.toString());
850
        String catxml = ReplicationService.getURLContent(u);
851

    
852
        // Make sure there are not error, no empty string
853
        if (catxml.indexOf("error")!=-1 || catxml==null||catxml.equals(""))
854
        {
855
          throw new Exception("Couldn't get catalog list form server " +server);
856
        }
857
        logReplication.debug("ReplicationHandler.updateCatalog - catxml: " + catxml);
858
        CatalogMessageHandler cmh = new CatalogMessageHandler();
859
        XMLReader catparser = initParser(cmh);
860
        catparser.parse(new InputSource(new StringReader(catxml)));
861
        //parse the returned catalog xml and put it into a vector
862
        remoteCatalog = cmh.getCatalogVect();
863

    
864
        // Make sure remoteCatalog is not empty
865
        if (remoteCatalog.isEmpty())
866
        {
867
          throw new Exception("Couldn't get catalog list form server " +server);
868
        }
869

    
870
        String localcatxml = ReplicationService.getCatalogXML();
871

    
872
        // Make sure local catalog is no empty
873
        if (localcatxml==null||localcatxml.equals(""))
874
        {
875
          throw new Exception("Couldn't get catalog list form server " +server);
876
        }
877

    
878
        cmh = new CatalogMessageHandler();
879
        catparser = initParser(cmh);
880
        catparser.parse(new InputSource(new StringReader(localcatxml)));
881
        Vector<Vector<String>> localCatalog = cmh.getCatalogVect();
882

    
883
        //now we have the catalog from the remote server and this local server
884
        //we now need to compare the two and merge the differences.
885
        //the comparison is base on the public_id fields which is the 4th
886
        //entry in each row vector.
887
        publicId = new Vector<String>();
888
        for(int i=0; i<localCatalog.size(); i++)
889
        {
890
          Vector<String> v = new Vector<String>(localCatalog.elementAt(i));
891
          logReplication.info("ReplicationHandler.updateCatalog - v1: " + v.toString());
892
          publicId.add(new String((String)v.elementAt(3)));
893
        }
894
      }//try
895
      catch (Exception e)
896
      {
897
        logMetacat.error("ReplicationHandler.updateCatalog - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
898
        logReplication.error("ReplicationHandler.updateCatalog - Failed to update catalog for server "+
899
                                    server + " because " +e.getMessage());
900
      }//catch
901

    
902
      for(int i=0; i<remoteCatalog.size(); i++)
903
      {
904
         // DConnection
905
        DBConnection dbConn = null;
906
        // DBConnection checkout serial number
907
        int serialNumber = -1;
908
        try
909
        {
910
            dbConn=DBConnectionPool.
911
                  getDBConnection("ReplicationHandler.updateCatalog");
912
            serialNumber=dbConn.getCheckOutSerialNumber();
913
            Vector<String> v = remoteCatalog.elementAt(i);
914
            //logMetacat.debug("v2: " + v.toString());
915
            //logMetacat.debug("i: " + i);
916
            //logMetacat.debug("remoteCatalog.size(): " + remoteCatalog.size());
917
            //logMetacat.debug("publicID: " + publicId.toString());
918
            logReplication.info
919
                              ("ReplicationHandler.updateCatalog - v.elementAt(3): " + (String)v.elementAt(3));
920
           if(!publicId.contains(v.elementAt(3)))
921
           { //so we don't have this public id in our local table so we need to
922
             //add it.
923
             //logMetacat.debug("in if");
924
             StringBuffer sql = new StringBuffer();
925
             sql.append("insert into xml_catalog (entry_type, source_doctype, ");
926
             sql.append("target_doctype, public_id, system_id) values (?,?,?,");
927
             sql.append("?,?)");
928
             //logMetacat.debug("sql: " + sql.toString());
929
             pstmt = dbConn.prepareStatement(sql.toString());
930
             pstmt.setString(1, (String)v.elementAt(0));
931
             pstmt.setString(2, (String)v.elementAt(1));
932
             pstmt.setString(3, (String)v.elementAt(2));
933
             pstmt.setString(4, (String)v.elementAt(3));
934
             pstmt.setString(5, (String)v.elementAt(4));
935
             pstmt.execute();
936
             pstmt.close();
937
             logReplication.info("ReplicationHandler.updateCatalog - Success fully to insert new publicid "+
938
                               (String)v.elementAt(3) + " from server"+server);
939
           }
940
        }
941
        catch(Exception e)
942
        {
943
           logMetacat.error("ReplicationHandler.updateCatalog - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
944
           logReplication.error("ReplicationHandler.updateCatalog - Failed to update catalog for server "+
945
                                    server + " because " +e.getMessage());
946
        }//catch
947
        finally
948
        {
949
           DBConnectionPool.returnDBConnection(dbConn, serialNumber);
950
        }//finally
951
      }//for remote catalog
952
    }//for server list
953
    logReplication.info("End of updateCatalog");
954
  }
955

    
956
  /**
957
   * Method that returns true if docid has already been "deleted" from metacat.
958
   * This method really implements a truth table for deleted documents
959
   * The table is (a docid in one of the tables is represented by the X):
960
   * xml_docs      xml_revs      deleted?
961
   * ------------------------------------
962
   *   X             X             FALSE
963
   *   X             _             FALSE
964
   *   _             X             TRUE
965
   *   _             _             TRUE
966
   */
967
  private static boolean alreadyDeleted(String docid) throws HandlerException
968
  {
969
    DBConnection dbConn = null;
970
    int serialNumber = -1;
971
    PreparedStatement pstmt = null;
972
    try
973
    {
974
      dbConn=DBConnectionPool.
975
                  getDBConnection("ReplicationHandler.alreadyDeleted");
976
      serialNumber=dbConn.getCheckOutSerialNumber();
977
      boolean xml_docs = false;
978
      boolean xml_revs = false;
979

    
980
      StringBuffer sb = new StringBuffer();
981
      sb.append("select docid from xml_revisions where docid like '");
982
      sb.append(docid).append("'");
983
      pstmt = dbConn.prepareStatement(sb.toString());
984
      pstmt.execute();
985
      ResultSet rs = pstmt.getResultSet();
986
      boolean tablehasrows = rs.next();
987
      if(tablehasrows)
988
      {
989
        xml_revs = true;
990
      }
991

    
992
      sb = new StringBuffer();
993
      sb.append("select docid from xml_documents where docid like '");
994
      sb.append(docid).append("'");
995
      pstmt.close();
996
      pstmt = dbConn.prepareStatement(sb.toString());
997
      //increase usage count
998
      dbConn.increaseUsageCount(1);
999
      pstmt.execute();
1000
      rs = pstmt.getResultSet();
1001
      tablehasrows = rs.next();
1002
      pstmt.close();
1003
      if(tablehasrows)
1004
      {
1005
        xml_docs = true;
1006
      }
1007

    
1008
      if(xml_docs && xml_revs)
1009
      {
1010
        return false;
1011
      }
1012
      else if(xml_docs && !xml_revs)
1013
      {
1014
        return false;
1015
      }
1016
      else if(!xml_docs && xml_revs)
1017
      {
1018
        return true;
1019
      }
1020
      else if(!xml_docs && !xml_revs)
1021
      {
1022
        return true;
1023
      }
1024
    }
1025
    catch(Exception e)
1026
    {
1027
      logMetacat.error("ReplicationHandler.alreadyDeleted - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1028
      logReplication.error("ReplicationHandler.alreadyDeleted - general error in alreadyDeleted: " +
1029
                          e.getMessage());
1030
      throw new HandlerException("ReplicationHandler.alreadyDeleted - general error: " 
1031
    		  + e.getMessage());
1032
    }
1033
    finally
1034
    {
1035
      try
1036
      {
1037
        pstmt.close();
1038
      }//try
1039
      catch (SQLException ee)
1040
      {
1041
    	logMetacat.error("ReplicationHandler.alreadyDeleted - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1042
        logReplication.error("ReplicationHandler.alreadyDeleted - Error in replicationHandler.alreadyDeleted "+
1043
                          "to close pstmt: "+ee.getMessage());
1044
        throw new HandlerException("ReplicationHandler.alreadyDeleted - SQL error when closing prepared statement: " 
1045
      		  + ee.getMessage());
1046
      }//catch
1047
      finally
1048
      {
1049
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1050
      }//finally
1051
    }//finally
1052
    return false;
1053
  }
1054

    
1055

    
1056
  /**
1057
   * Method to initialize the message parser
1058
   */
1059
  public static XMLReader initParser(DefaultHandler dh)
1060
          throws HandlerException
1061
  {
1062
    XMLReader parser = null;
1063

    
1064
    try {
1065
      ContentHandler chandler = dh;
1066

    
1067
      // Get an instance of the parser
1068
      String parserName = PropertyService.getProperty("xml.saxparser");
1069
      parser = XMLReaderFactory.createXMLReader(parserName);
1070

    
1071
      // Turn off validation
1072
      parser.setFeature("http://xml.org/sax/features/validation", false);
1073

    
1074
      parser.setContentHandler((ContentHandler)chandler);
1075
      parser.setErrorHandler((ErrorHandler)chandler);
1076

    
1077
    } catch (SAXException se) {
1078
      throw new HandlerException("ReplicationHandler.initParser - Sax error when " 
1079
    		  + " initializing parser: " + se.getMessage());
1080
    } catch (PropertyNotFoundException pnfe) {
1081
        throw new HandlerException("ReplicationHandler.initParser - Property error when " 
1082
      		  + " getting parser name: " + pnfe.getMessage());
1083
    } 
1084

    
1085
    return parser;
1086
  }
1087

    
1088
  /**
1089
	 * This method will combine given time string(in short format) to current
1090
	 * date. If the given time (e.g 10:00 AM) passed the current time (e.g 2:00
1091
	 * PM Aug 21, 2005), then the time will set to second day, 10:00 AM Aug 22,
1092
	 * 2005. If the given time (e.g 10:00 AM) haven't passed the current time
1093
	 * (e.g 8:00 AM Aug 21, 2005) The time will set to be 10:00 AM Aug 21, 2005.
1094
	 * 
1095
	 * @param givenTime
1096
	 *            the format should be "10:00 AM " or "2:00 PM"
1097
	 * @return
1098
	 * @throws Exception
1099
	 */
1100
	public static Date combinateCurrentDateAndGivenTime(String givenTime) throws HandlerException
1101
  {
1102
	  try {
1103
     Date givenDate = parseTime(givenTime);
1104
     Date newDate = null;
1105
     Date now = new Date();
1106
     String currentTimeString = getTimeString(now);
1107
     Date currentTime = parseTime(currentTimeString); 
1108
     if ( currentTime.getTime() >= givenDate.getTime())
1109
     {
1110
        logReplication.info("ReplicationHandler.combinateCurrentDateAndGivenTime - Today already pass the given time, we should set it as tomorrow");
1111
        String dateAndTime = getDateString(now) + " " + givenTime;
1112
        Date combinationDate = parseDateTime(dateAndTime);
1113
        // new date should plus 24 hours to make is the second day
1114
        newDate = new Date(combinationDate.getTime()+24*3600*1000);
1115
     }
1116
     else
1117
     {
1118
         logReplication.info("ReplicationHandler.combinateCurrentDateAndGivenTime - Today haven't pass the given time, we should it as today");
1119
         String dateAndTime = getDateString(now) + " " + givenTime;
1120
         newDate = parseDateTime(dateAndTime);
1121
     }
1122
     logReplication.warn("ReplicationHandler.combinateCurrentDateAndGivenTime - final setting time is "+ newDate.toString());
1123
     return newDate;
1124
	  } catch (ParseException pe) {
1125
		  throw new HandlerException("ReplicationHandler.combinateCurrentDateAndGivenTime - "
1126
				  + "parsing error: "  + pe.getMessage());
1127
	  }
1128
  }
1129

    
1130
  /*
1131
	 * parse a given string to Time in short format. For example, given time is
1132
	 * 10:00 AM, the date will be return as Jan 1 1970, 10:00 AM
1133
	 */
1134
  private static Date parseTime(String timeString) throws ParseException
1135
  {
1136
    DateFormat format = DateFormat.getTimeInstance(DateFormat.SHORT);
1137
    Date time = format.parse(timeString); 
1138
    logReplication.info("ReplicationHandler.parseTime - Date string is after parse a time string "
1139
                              +time.toString());
1140
    return time;
1141

    
1142
  }
1143
  
1144
  /*
1145
   * Parse a given string to date and time. Date format is long and time
1146
   * format is short.
1147
   */
1148
  private static Date parseDateTime(String timeString) throws ParseException
1149
  {
1150
    DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT);
1151
    Date time = format.parse(timeString);
1152
    logReplication.info("ReplicationHandler.parseDateTime - Date string is after parse a time string "+
1153
                             time.toString());
1154
    return time;
1155
  }
1156
  
1157
  /*
1158
   * Get a date string from a Date object. The date format will be long
1159
   */
1160
  private static String getDateString(Date now)
1161
  {
1162
     DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
1163
     String s = df.format(now);
1164
     logReplication.info("ReplicationHandler.getDateString - Today is " + s);
1165
     return s;
1166
  }
1167
  
1168
  /*
1169
   * Get a time string from a Date object, the time format will be short
1170
   */
1171
  private static String getTimeString(Date now)
1172
  {
1173
     DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
1174
     String s = df.format(now);
1175
     logReplication.info("ReplicationHandler.getTimeString - Time is " + s);
1176
     return s;
1177
  }
1178
  
1179
  
1180
  /*
1181
	 * This method will go through the docid list both in xml_Documents table
1182
	 * and in xml_revisions table @author tao
1183
	 */
1184
	private void handleDocList(Vector<Vector<String>> docList, String tableName) {
1185
		boolean dataFile = false;
1186
		for (int j = 0; j < docList.size(); j++) {
1187
			// initial dataFile is false
1188
			dataFile = false;
1189
			// w is information for one document, information contain
1190
			// docid, rev, server or datafile.
1191
			Vector<String> w = new Vector<String>(docList.elementAt(j));
1192
			// Check if the vector w contain "datafile"
1193
			// If it has, this document is data file
1194
			try {
1195
				if (w.contains((String) PropertyService.getProperty("replication.datafileflag"))) {
1196
					dataFile = true;
1197
				}
1198
			} catch (PropertyNotFoundException pnfe) {
1199
				logMetacat.error("ReplicationHandler.handleDocList - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1200
				logReplication.error("ReplicationHandler.handleDocList - Could not retrieve data file flag property.  "
1201
						+ "Leaving as false: " + pnfe.getMessage());
1202
			}
1203
			// logMetacat.debug("w: " + w.toString());
1204
			// Get docid
1205
			String docid = (String) w.elementAt(0);
1206
			logReplication.info("docid: " + docid);
1207
			// Get revision number
1208
			int rev = Integer.parseInt((String) w.elementAt(1));
1209
			logReplication.info("rev: " + rev);
1210
			// Get remote server name (it is may not be doc home server because
1211
			// the new hub feature
1212
			String remoteServer = (String) w.elementAt(2);
1213
			remoteServer = remoteServer.trim();
1214

    
1215
			try {
1216
				if (tableName.equals(DocumentImpl.DOCUMENTTABLE)) {
1217
					handleDocInXMLDocuments(docid, rev, remoteServer, dataFile);
1218
				} else if (tableName.equals(DocumentImpl.REVISIONTABLE)) {
1219
					handleDocInXMLRevisions(docid, rev, remoteServer, dataFile);
1220
				} else {
1221
					continue;
1222
				}
1223

    
1224
			} catch (Exception e) {
1225
				logMetacat.error("ReplicationHandler.handleDocList - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1226
				logReplication.error("ReplicationHandler.handleDocList - error to handle update doc in " + tableName
1227
						+ " in time replication" + e.getMessage());
1228
				continue;
1229
			}
1230
			
1231
	        if (_xmlDocQueryCount > 0 && (_xmlDocQueryCount % 100) == 0) {
1232
	        	logMetacat.debug("ReplicationHandler.update - xml_doc query count: " + _xmlDocQueryCount + 
1233
	        			", xml_doc avg query time: " + (_xmlDocQueryTime / _xmlDocQueryCount));
1234
	        }
1235
	        
1236
	        if (_xmlRevQueryCount > 0 && (_xmlRevQueryCount % 100) == 0) {
1237
	        	logMetacat.debug("ReplicationHandler.update - xml_rev query count: " + _xmlRevQueryCount + 
1238
	        			", xml_rev avg query time: " + (_xmlRevQueryTime / _xmlRevQueryCount));
1239
	        }
1240

    
1241
		}// for update docs
1242

    
1243
	}
1244
   
1245
   /*
1246
	 * This method will handle doc in xml_documents table.
1247
	 */
1248
   private void handleDocInXMLDocuments(String docid, int rev, String remoteServer, boolean dataFile) 
1249
                                        throws HandlerException
1250
   {
1251
       // compare the update rev and local rev to see what need happen
1252
       int localrev = -1;
1253
       String action = null;
1254
       boolean flag = false;
1255
       try
1256
       {
1257
    	 long docQueryStartTime = System.currentTimeMillis();
1258
         localrev = DBUtil.getLatestRevisionInDocumentTable(docid);
1259
         long docQueryEndTime = System.currentTimeMillis();
1260
         _xmlDocQueryTime += (docQueryEndTime - docQueryStartTime);
1261
         _xmlDocQueryCount++;
1262
       }
1263
       catch (SQLException e)
1264
       {
1265
    	 logMetacat.error("ReplicationHandler.handleDocInXMLDocuments - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1266
         logReplication.error("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " could not "+
1267
                                " be found because " + e.getMessage());
1268
         logReplication.error("ReplicationHandler.handleDocInXMLDocuments - " + DOCERRORNUMBER+"Docid "+ docid + " could not be "+
1269
                 "written because error happend to find it's local revision");
1270
         DOCERRORNUMBER++;
1271
         throw new HandlerException ("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " could not "+
1272
                 " be found: " + e.getMessage());
1273
       }
1274
       logReplication.info("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " is "+
1275
                               localrev);
1276

    
1277
       //check the revs for an update because this document is in the
1278
       //local DB, it might be out of date.
1279
       if (localrev == -1)
1280
       {
1281
          // check if the revision is in the revision table
1282
    	   Vector<Integer> localRevVector = null;
1283
    	 try {
1284
        	 long revQueryStartTime = System.currentTimeMillis();
1285
    		 localRevVector = DBUtil.getRevListFromRevisionTable(docid);
1286
             long revQueryEndTime = System.currentTimeMillis();
1287
             _xmlRevQueryTime += (revQueryEndTime - revQueryStartTime);
1288
             _xmlRevQueryCount++;
1289
    	 } catch (SQLException sqle) {
1290
    		 throw new HandlerException("ReplicationHandler.handleDocInXMLDocuments - SQL error " 
1291
    				 + " when getting rev list for docid: " + docid + " : " + sqle.getMessage());
1292
    	 }
1293
         if (localRevVector != null && localRevVector.contains(new Integer(rev)))
1294
         {
1295
             // this version was deleted, so don't need replicate
1296
             flag = false;
1297
         }
1298
         else
1299
         {
1300
           //insert this document as new because it is not in the local DB
1301
           action = "INSERT";
1302
           flag = true;
1303
         }
1304
       }
1305
       else
1306
       {
1307
         if(localrev == rev)
1308
         {
1309
           // Local meatacat has the same rev to remote host, don't need
1310
           // update and flag set false
1311
           flag = false;
1312
         }
1313
         else if(localrev < rev)
1314
         {
1315
           //this document needs to be updated so send an read request
1316
           action = "UPDATE";
1317
           flag = true;
1318
         }
1319
       }
1320
       
1321
       String accNumber = null;
1322
       try {
1323
    	   accNumber = docid + PropertyService.getProperty("document.accNumSeparator") + rev;
1324
       } catch (PropertyNotFoundException pnfe) {
1325
    	   throw new HandlerException("ReplicationHandler.handleDocInXMLDocuments - error getting " 
1326
    			   + "account number separator : " + pnfe.getMessage());
1327
       }
1328
       // this is non-data file
1329
       if(flag && !dataFile)
1330
       {
1331
         try
1332
         {
1333
           handleSingleXMLDocument(remoteServer, action, accNumber, DocumentImpl.DOCUMENTTABLE);
1334
         }
1335
         catch(HandlerException he)
1336
         {
1337
           // skip this document
1338
           throw he;
1339
         }
1340
       }//if for non-data file
1341

    
1342
        // this is for data file
1343
       if(flag && dataFile)
1344
       {
1345
         try
1346
         {
1347
           handleSingleDataFile(remoteServer, action, accNumber, DocumentImpl.DOCUMENTTABLE);
1348
         }
1349
         catch(HandlerException he)
1350
         {
1351
           // skip this data file
1352
           throw he;
1353
         }
1354

    
1355
       }//for data file
1356
   }
1357
   
1358
   /*
1359
    * This method will handle doc in xml_documents table.
1360
    */
1361
   private void handleDocInXMLRevisions(String docid, int rev, String remoteServer, boolean dataFile) 
1362
                                        throws HandlerException
1363
   {
1364
       // compare the update rev and local rev to see what need happen
1365
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - In handle repliation revsion table");
1366
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - the docid is "+ docid);
1367
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - The rev is "+rev);
1368
       Vector<Integer> localrev = null;
1369
       String action = "INSERT";
1370
       boolean flag = false;
1371
       try
1372
       {
1373
      	 long revQueryStartTime = System.currentTimeMillis();
1374
         localrev = DBUtil.getRevListFromRevisionTable(docid);
1375
         long revQueryEndTime = System.currentTimeMillis();
1376
         _xmlRevQueryTime += (revQueryEndTime - revQueryStartTime);
1377
         _xmlRevQueryCount++;
1378
       }
1379
       catch (SQLException sqle)
1380
       {
1381
    	 logMetacat.error("ReplicationHandler.handleDocInXMLDocuments - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1382
         logReplication.error("ReplicationHandler.handleDocInXMLRevisions - Local rev for docid "+ docid + " could not "+
1383
                                " be found because " + sqle.getMessage());
1384
         REVERRORNUMBER++;
1385
         throw new HandlerException ("ReplicationHandler.handleDocInXMLRevisions - SQL exception getting rev list: " 
1386
        		 + sqle.getMessage());
1387
       }
1388
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - rev list in xml_revision table for docid "+ docid + " is "+
1389
                               localrev.toString());
1390
       
1391
       // if the rev is not in the xml_revision, we need insert it
1392
       if (!localrev.contains(new Integer(rev)))
1393
       {
1394
           flag = true;    
1395
       }
1396
     
1397
       String accNumber = null;
1398
       try {
1399
    	   accNumber = docid + PropertyService.getProperty("document.accNumSeparator") + rev;
1400
       } catch (PropertyNotFoundException pnfe) {
1401
    	   throw new HandlerException("ReplicationHandler.handleDocInXMLRevisions - error getting " 
1402
    			   + "account number separator : " + pnfe.getMessage());
1403
       }
1404
       // this is non-data file
1405
       if(flag && !dataFile)
1406
       {
1407
         try
1408
         {
1409
           
1410
           handleSingleXMLDocument(remoteServer, action, accNumber, DocumentImpl.REVISIONTABLE);
1411
         }
1412
         catch(HandlerException he)
1413
         {
1414
           // skip this document
1415
           throw he;
1416
         }
1417
       }//if for non-data file
1418

    
1419
        // this is for data file
1420
       if(flag && dataFile)
1421
       {
1422
         try
1423
         {
1424
           handleSingleDataFile(remoteServer, action, accNumber, DocumentImpl.REVISIONTABLE);
1425
         }
1426
         catch(HandlerException he)
1427
         {
1428
           // skip this data file
1429
           throw he;
1430
         }
1431

    
1432
       }//for data file
1433
   }
1434
   
1435
   /*
1436
    * Return a ip address for given url
1437
    */
1438
   private String getIpFromURL(URL url)
1439
   {
1440
	   String ip = null;
1441
	   try
1442
	   {
1443
	      InetAddress address = InetAddress.getByName(url.getHost());
1444
	      ip = address.getHostAddress();
1445
	   }
1446
	   catch(UnknownHostException e)
1447
	   {
1448
		   logMetacat.error("ReplicationHandler.getIpFromURL - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1449
		   logReplication.error("ReplicationHandler.getIpFromURL - Error in get ip address for host: "
1450
                   +e.getMessage());
1451
	   }
1452

    
1453
	   return ip;
1454
   }
1455
  
1456
}
1457

    
(3-3/7)