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-24 16:18:55 -0700 (Tue, 24 May 2011) $'
10
 * '$Revision: 6097 $'
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
        //System.out.println("v: " + v.toString());
252
        //d is the list of deleted documents
253
        Vector<Vector<String>> deleteList = new Vector<Vector<String>>(message.getDeletesVect());
254
        //System.out.println("d: " + d.toString());
255
        logReplication.info("ReplicationHandler.update - Update vector size: "+ updateList.size()+" from "+message.getServerName());
256
        logReplication.info("ReplicationHandler.update - Delete vector size: "+ deleteList.size()+" from "+message.getServerName());
257
        logReplication.info("ReplicationHandler.update - The delete document list size is "+deleteList.size()+" from "+message.getServerName());
258
        // go though every element in updated document vector
259
        handleDocList(updateList, DocumentImpl.DOCUMENTTABLE);
260
        //handle deleted docs
261
        for(int k=0; k<deleteList.size(); k++)
262
        { //delete the deleted documents;
263
          Vector<String> w = new Vector<String>(deleteList.elementAt(k));
264
          String docId = (String)w.elementAt(0);
265
          try
266
          {
267
            handleDeleteSingleDocument(docId, server);
268
          }
269
          catch (Exception ee)
270
          {
271
            continue;
272
          }
273
        }//for delete docs
274
        
275
        // handle replicate doc in xml_revision
276
        Vector<Vector<String>> revisionList = new Vector<Vector<String>>(message.getRevisionsVect());
277
        logReplication.info("ReplicationHandler.update - The revision document list size is "+revisionList.size()+ " from "+message.getServerName());
278
        handleDocList(revisionList, DocumentImpl.REVISIONTABLE);
279
        DOCINSERTNUMBER = 1;
280
        DOCERRORNUMBER  = 1;
281
        REVINSERTNUMBER = 1;
282
        REVERRORNUMBER  = 1;
283
        
284
        totalServerListParseTime += (System.currentTimeMillis() - startServerListParseTime);
285
    }//for response
286

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

    
318
  }//update
319

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

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

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

    
374
      String parserBase = null;
375
      // this for eml2 and we need user eml2 parser
376
      if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE))
377
      {
378
         parserBase = DocumentImpl.EML200;
379
      }
380
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE))
381
      {
382
        parserBase = DocumentImpl.EML200;
383
      }
384
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE))
385
      {
386
        parserBase = DocumentImpl.EML210;
387
      }
388
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE))
389
      {
390
        parserBase = DocumentImpl.EML210;
391
      }
392
      // Write the document into local host
393
      DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false);
394
      String newDocid = wrapper.writeReplication(dbConn,
395
                              newxmldoc,
396
                              docinfoHash.get("public_access"),
397
                              null,  /* the dtd text */
398
                              actions,
399
                              accNumber,
400
                              null, //docinfoHash.get("user_owner"),                              
401
                              null, /* null for groups[] */
402
                              docHomeServer,
403
                              remoteserver, tableName, true,// true is for time replication 
404
                              createdDate,
405
                              updatedDate);
406
      
407
      //set the user information
408
      String user = (String) docinfoHash.get("user_owner");
409
      String updated = (String) docinfoHash.get("user_updated");
410
      ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
411
      
412
      //process extra access rules 
413
      Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
414
      if (xmlAccessDAOList != null) {
415
      	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
416
      	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
417
      		if (!acfsf.accessControlExists(xmlAccessDAO)) {
418
      			acfsf.insertPermissions(xmlAccessDAO);
419
      		}
420
          }
421
      }
422
      
423
      //process guid
424
      logReplication.debug("Processing guid information from docinfoHash: " + docinfoHash.toString());
425
      String guid = docinfoHash.get("guid");
426
      String docName = docinfoHash.get("docname");
427
      System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%guid passed from docinfo hash: " + guid);
428
      IdentifierManager idman = IdentifierManager.getInstance();
429
      
430
      System.out.println("docname: " + docName);
431
      
432
      if(guid != null && !idman.identifierExists(guid))
433
      { //if the guid was passed in, put it in the identifiers table
434
        logReplication.debug("Creating guid/docid mapping for docid " + 
435
          docinfoHash.get("docid") + " and guid: " + guid);
436
        
437
        
438
        System.out.println("creating mapping: guid: " + guid + " localId: " + docinfoHash.get("docid"));
439
        idman.createMapping(guid, docinfoHash.get("docid"));
440
      }
441
      else
442
      {
443
        logReplication.debug("No guid information was included with the replicated document");
444
      }
445
      
446
      //handle systemMetadata
447
      if(docName.trim().equals("systemMetadata"))
448
      {
449
          if (actions.equalsIgnoreCase("UPDATE")) {
450
        	  System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!update mapping for systemMetadata: guid: " + guid + " localId: " + docinfoHash.get("docid"));
451
              idman.updateSystemMetadataMapping(guid, docinfoHash.get("docid"));
452
          } else { 
453
	    	  System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!creating mapping for systemMetadata: guid: " + guid + " localId: " + docinfoHash.get("docid"));
454
	          idman.createSystemMetadataMapping(guid, docinfoHash.get("docid"));
455
          }
456
          
457
          Date dateUploaded = new Date(new Long(docinfoHash.get("date_uploaded")));
458
          Date dateModified = new Date(new Long(docinfoHash.get("date_modified")));
459
          SystemMetadata sysMeta = IdentifierManager.getInstance().asSystemMetadata(
460
                  dateUploaded, 
461
                  docinfoHash.get("rights_holder"),
462
                  docinfoHash.get("checksum"), 
463
                  docinfoHash.get("checksum_algorithm"), 
464
                  docinfoHash.get("origin_member_node"),
465
                  docinfoHash.get("authoritive_member_node"), 
466
                  dateModified,
467
                  docinfoHash.get("submitter"),
468
                  docinfoHash.get("guid"),
469
                  docinfoHash.get("object_format"),
470
                  new Long(docinfoHash.get("size")).longValue());
471
          IdentifierManager.getInstance().insertAdditionalSystemMetadataFields(sysMeta);
472
          
473
          System.out.println("4");
474
      }
475
      
476
      if(guid != null)
477
      {
478
          if(!docName.trim().equals("systemMetadata"))
479
          {
480
              logReplication.info("replicate D1GUID:" + guid + ":D1SCIMETADATA:" + 
481
                      accNumber + ":");
482
          }
483
          else
484
          {
485
              logReplication.info("replicate D1GUID:" + guid + ":D1SYSMETADATA:" + 
486
                      accNumber + ":");
487
          }
488
      }
489
      
490
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - Successfully replicated doc " + accNumber);
491
      if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
492
      {
493
        logReplication.info("ReplicationHandler.handleSingleXMLDocument - " + DOCINSERTNUMBER + " Wrote xml doc " + accNumber +
494
                                     " into "+tableName + " from " +
495
                                         remoteserver);
496
        DOCINSERTNUMBER++;
497
      }
498
      else
499
      {
500
          logReplication.info("ReplicationHandler.handleSingleXMLDocument - " +REVINSERTNUMBER + " Wrote xml doc " + accNumber +
501
                  " into "+tableName + " from " +
502
                      remoteserver);
503
          REVINSERTNUMBER++;
504
      }
505
      String ip = getIpFromURL(u);
506
      EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
507
      
508

    
509
    }//try
510
    catch(Exception e)
511
    {
512
        
513
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
514
        {
515
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
516
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +DOCERRORNUMBER + " Failed to write xml doc " + accNumber +
517
                                       " into "+tableName + " from " +
518
                                           remoteserver + " because "+e.getMessage());
519
          DOCERRORNUMBER++;
520
        }
521
        else
522
        {
523
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
524
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +REVERRORNUMBER + " Failed to write xml doc " + accNumber +
525
                    " into "+tableName + " from " +
526
                        remoteserver +" because "+e.getMessage());
527
            REVERRORNUMBER++;
528
        }
529
        logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG); 
530
        logReplication.error("ReplicationHandler.handleSingleXMLDocument - Failed to write doc " + accNumber +
531
                                      " into db because " +e.getMessage());
532
      throw new HandlerException("ReplicationHandler.handleSingleXMLDocument - generic exception " 
533
    		  + "writing Replication: " +e.getMessage());
534
    }
535
    finally
536
    {
537
       //return DBConnection
538
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
539
    }//finally
540
    logD1.info("replication.create localId:" + accNumber);
541
  }
542

    
543

    
544

    
545
  /* Handle replicate single xml document*/
546
  private void handleSingleDataFile(String remoteserver, String actions,
547
                                    String accNumber, String tableName)
548
               throws HandlerException
549
  {
550
    logReplication.info("ReplicationHandler.handleSingleDataFile - Try to replicate data file: " + accNumber);
551
    DBConnection dbConn = null;
552
    int serialNumber = -1;
553
    try
554
    {
555
      // Get DBConnection from pool
556
      dbConn=DBConnectionPool.
557
                  getDBConnection("ReplicationHandler.handleSinlgeDataFile");
558
      serialNumber=dbConn.getCheckOutSerialNumber();
559
      // Try get docid info from remote server
560
      DocInfoHandler dih = new DocInfoHandler();
561
      XMLReader docinfoParser = initParser(dih);
562
      String docInfoURLString = "https://" + remoteserver +
563
                  "?server="+MetacatUtil.getLocalReplicationServerName()+
564
                  "&action=getdocumentinfo&docid="+accNumber;
565
      docInfoURLString = MetacatUtil.replaceWhiteSpaceForURL(docInfoURLString);
566
      URL docinfoUrl = new URL(docInfoURLString);
567

    
568
      String docInfoStr = ReplicationService.getURLContent(docinfoUrl);
569
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
570
      Hashtable<String, String> docinfoHash = dih.getDocInfo();
571
      
572
      // Get docid name (such as acl or dataset)
573
      String docName = docinfoHash.get("docname");
574
      // Get doc type (eml public id)
575
      String docType = docinfoHash.get("doctype");
576
      // Get docid home sever. it might be different to remoteserver
577
      // because of hub feature
578
      String docHomeServer = docinfoHash.get("home_server");
579
      String createdDate = docinfoHash.get("date_created");
580
      String updatedDate = docinfoHash.get("date_updated");
581
      //docid should include rev number too
582
      /*String accnum=docId+util.getProperty("document.accNumSeparator")+
583
                                              (String)docinfoHash.get("rev");*/
584

    
585

    
586
      String datafilePath = PropertyService.getProperty("application.datafilepath");
587
      // Get data file content
588
      String readDataURLString = "https://" + remoteserver + "?server="+
589
                                        MetacatUtil.getLocalReplicationServerName()+
590
                                            "&action=readdata&docid="+accNumber;
591
      readDataURLString = MetacatUtil.replaceWhiteSpaceForURL(readDataURLString);
592
      URL u = new URL(readDataURLString);
593
      InputStream input = u.openStream();
594
      //register data file into xml_documents table and wite data file
595
      //into file system
596
      if ( input != null)
597
      {
598
        DocumentImpl.writeDataFileInReplication(input,
599
                                                datafilePath,
600
                                                docName,docType,
601
                                                accNumber,
602
                                                null,
603
                                                docHomeServer,
604
                                                remoteserver,
605
                                                tableName,
606
                                                true, //true means timed replication
607
                                                createdDate,
608
                                                updatedDate);
609
                                         
610
        //set the user information
611
        String user = (String) docinfoHash.get("user_owner");
612
		String updated = (String) docinfoHash.get("user_updated");
613
        ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
614
        
615
        //process extra access rules
616
        Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
617
        if (xmlAccessDAOList != null) {
618
        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
619
        	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
620
        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
621
        			acfsf.insertPermissions(xmlAccessDAO);
622
        		}
623
            }
624
        }
625
        
626
        logReplication.info("ReplicationHandler.handleSingleDataFile - Successfully to write datafile " + accNumber);
627
        /*MetacatReplication.replLog("wrote datafile " + accNumber + " from " +
628
                                    remote server);*/
629
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
630
        {
631
          logReplication.info("ReplicationHandler.handleSingleDataFile - " + DOCINSERTNUMBER + " Wrote data file" + accNumber +
632
                                       " into "+tableName + " from " +
633
                                           remoteserver);
634
          DOCINSERTNUMBER++;
635
        }
636
        else
637
        {
638
            logReplication.info("ReplicationHandler.handleSingleDataFile - " + REVINSERTNUMBER + " Wrote data file" + accNumber +
639
                    " into "+tableName + " from " +
640
                        remoteserver);
641
            REVINSERTNUMBER++;
642
        }
643
        String ip = getIpFromURL(u);
644
        EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
645
        
646
      }//if
647
      else
648
      {
649
         logReplication.info("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
650
         throw new HandlerException("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
651
      }//else
652

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

    
688

    
689

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

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

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

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

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

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

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

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

    
823

    
824

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

    
836

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

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

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

    
872
        String localcatxml = ReplicationService.getCatalogXML();
873

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

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

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

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

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

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

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

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

    
1058

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

    
1067
    try {
1068
      ContentHandler chandler = dh;
1069

    
1070
      // Get an instance of the parser
1071
      String parserName = PropertyService.getProperty("xml.saxparser");
1072
      parser = XMLReaderFactory.createXMLReader(parserName);
1073

    
1074
      // Turn off validation
1075
      parser.setFeature("http://xml.org/sax/features/validation", false);
1076

    
1077
      parser.setContentHandler((ContentHandler)chandler);
1078
      parser.setErrorHandler((ErrorHandler)chandler);
1079

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

    
1088
    return parser;
1089
  }
1090

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

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

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

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

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

    
1244
		}// for update docs
1245

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

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

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

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

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

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

    
1456
	   return ip;
1457
   }
1458
  
1459
}
1460

    
(3-3/7)