Project

General

Profile

1 522 berkley
/**
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$'
9
 *     '$Date$'
10
 * '$Revision$'
11 669 jones
 *
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 522 berkley
 */
26 2286 tao
27 5014 daigle
package edu.ucsb.nceas.metacat.replication;
28 522 berkley
29 5014 daigle
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 6001 cjones
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
36 5089 daigle
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
37 5098 daigle
import edu.ucsb.nceas.metacat.accesscontrol.XMLAccessDAO;
38 6001 cjones
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
39 5014 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
40
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
41
import edu.ucsb.nceas.metacat.database.DatabaseService;
42 5030 daigle
import edu.ucsb.nceas.metacat.properties.PropertyService;
43 5014 daigle
import edu.ucsb.nceas.metacat.shared.HandlerException;
44 4698 daigle
import edu.ucsb.nceas.metacat.util.MetacatUtil;
45 5324 berkley
import edu.ucsb.nceas.metacat.IdentifierManager;
46 4080 daigle
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
47
48 522 berkley
import java.sql.*;
49
import java.util.*;
50 2555 tao
import java.util.Date;
51 522 berkley
import java.io.*;
52
import java.net.*;
53 543 berkley
import java.text.*;
54 2663 sgarg
55
import org.apache.log4j.Logger;
56 522 berkley
import org.xml.sax.ContentHandler;
57
import org.xml.sax.ErrorHandler;
58
import org.xml.sax.InputSource;
59 5014 daigle
import org.xml.sax.SAXException;
60 522 berkley
import org.xml.sax.XMLReader;
61
import org.xml.sax.helpers.XMLReaderFactory;
62 561 berkley
import org.xml.sax.helpers.DefaultHandler;
63 522 berkley
64 561 berkley
65
66 522 berkley
/**
67
 * This class handles deltaT replication checking.  Whenever this TimerTask
68
 * is fired it checks each server in xml_replication for updates and updates
69
 * the local db as needed.
70
 */
71
public class ReplicationHandler extends TimerTask
72
{
73 573 berkley
  int serverCheckCode = 1;
74 2286 tao
  ReplicationServerList serverList = null;
75 2573 tao
  //PrintWriter out;
76 4698 daigle
//  private static final AbstractDatabase dbAdapter = MetacatUtil.dbAdapter;
77 5014 daigle
  private static Logger logReplication = Logger.getLogger("ReplicationLogging");
78 2663 sgarg
  private static Logger logMetacat = Logger.getLogger(ReplicationHandler.class);
79 5392 berkley
  private static Logger logD1 = Logger.getLogger("DataOneLogger");
80
81 2725 tao
  private static int DOCINSERTNUMBER = 1;
82
  private static int DOCERRORNUMBER  = 1;
83
  private static int REVINSERTNUMBER = 1;
84
  private static int REVERRORNUMBER  = 1;
85 5175 daigle
86
  private static int _xmlDocQueryCount = 0;
87
  private static int _xmlRevQueryCount = 0;
88
  private static long _xmlDocQueryTime = 0;
89
  private static long _xmlRevQueryTime = 0;
90
91
92 2573 tao
  public ReplicationHandler()
93 522 berkley
  {
94 2573 tao
    //this.out = o;
95 1292 tao
    serverList = new ReplicationServerList();
96 522 berkley
  }
97 2286 tao
98 2573 tao
  public ReplicationHandler(int serverCheckCode)
99 573 berkley
  {
100 2573 tao
    //this.out = o;
101 573 berkley
    this.serverCheckCode = serverCheckCode;
102 1292 tao
    serverList = new ReplicationServerList();
103 573 berkley
  }
104 2286 tao
105 522 berkley
  /**
106 2286 tao
   * Method that implements TimerTask.run().  It runs whenever the timer is
107 522 berkley
   * fired.
108
   */
109
  public void run()
110
  {
111
    //find out the last_checked time of each server in the server list and
112 2286 tao
    //send a query to each server to see if there are any documents in
113 522 berkley
    //xml_documents with an update_date > last_checked
114 5014 daigle
115 1032 tao
      //if serverList is null, metacat don't need to replication
116
      if (serverList==null||serverList.isEmpty())
117
      {
118
        return;
119
      }
120 1292 tao
      updateCatalog();
121
      update();
122 1217 tao
      //conn.close();
123 522 berkley
  }
124 2286 tao
125 522 berkley
  /**
126 5175 daigle
   * Method that uses revision tagging for replication instead of update_date.
127 577 berkley
   */
128 1292 tao
  private void update()
129 577 berkley
  {
130 5175 daigle
131
	  _xmlDocQueryCount = 0;
132
	  _xmlRevQueryCount = 0;
133
	  _xmlDocQueryTime = 0;
134
	  _xmlRevQueryTime = 0;
135 577 berkley
    /*
136
     Pseudo-algorithm
137
     - request a doc list from each server in xml_replication
138 2286 tao
     - check the rev number of each of those documents agains the
139 577 berkley
       documents in the local database
140
     - pull any documents that have a lesser rev number on the local server
141
       from the remote server
142
     - delete any documents that still exist in the local xml_documents but
143
       are in the deletedDocuments tag of the remote host response.
144
     - update last_checked to keep track of the last time it was checked.
145 2286 tao
       (this info is theoretically not needed using this system but probably
146 577 berkley
       should be kept anyway)
147
    */
148 2286 tao
149
    ReplicationServer replServer = null; // Variable to store the
150
                                        // ReplicationServer got from
151 1292 tao
                                        // Server list
152 2298 tao
    String server = null; // Variable to store server name
153 5014 daigle
//    String update;
154
    Vector<String> responses = new Vector<String>();
155 577 berkley
    URL u;
156 5175 daigle
    long replicationStartTime = System.currentTimeMillis();
157
    long timeToGetServerList = 0;
158 3898 tao
159 1585 tao
    //Check for every server in server list to get updated list and put
160
    // them in to response
161 5175 daigle
    long startTimeToGetServers = System.currentTimeMillis();
162 1585 tao
    for (int i=0; i<serverList.size(); i++)
163
    {
164 1292 tao
        // Get ReplicationServer object from server list
165
        replServer = serverList.serverAt(i);
166
        // Get server name from ReplicationServer object
167 2578 tao
        server = replServer.getServerName().trim();
168 1585 tao
        String result = null;
169 5014 daigle
        logReplication.info("ReplicationHandler.update - full update started to: " + server);
170 1292 tao
        // Send command to that server to get updated docid information
171
        try
172
        {
173 1585 tao
          u = new URL("https://" + server + "?server="
174 4698 daigle
          +MetacatUtil.getLocalReplicationServerName()+"&action=update");
175 5014 daigle
          logReplication.info("ReplicationHandler.update - Sending infomation " +u.toString());
176
          result = ReplicationService.getURLContent(u);
177 1585 tao
        }
178 1292 tao
        catch (Exception e)
179
        {
180 5014 daigle
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
181
          logReplication.error( "ReplicationHandler.update - Failed to get updated doc list "+
182 2663 sgarg
                       "for server " + server + " because "+e.getMessage());
183 1292 tao
          continue;
184 1585 tao
        }
185 2286 tao
186 5175 daigle
        //logReplication.info("ReplicationHandler.update - docid: "+server+" "+result);
187 1292 tao
        //check if result have error or not, if has skip it.
188 1609 tao
        if (result.indexOf("<error>")!=-1 && result.indexOf("</error>")!=-1)
189 1102 tao
        {
190 5014 daigle
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
191
          logReplication.error( "ReplicationHandler.update - Failed to get updated doc list "+
192 2663 sgarg
                       "for server " + server + " because "+result);
193 1102 tao
          continue;
194
        }
195 1585 tao
        //Add result to vector
196 577 berkley
        responses.add(result);
197 1585 tao
    }
198 5175 daigle
    timeToGetServerList = System.currentTimeMillis() - startTimeToGetServers;
199 2286 tao
200 1585 tao
    //make sure that there is updated file list
201
    //If response is null, metacat don't need do anything
202
    if (responses==null || responses.isEmpty())
203
    {
204 5014 daigle
    	logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
205
        logReplication.info( "ReplicationHandler.update - No updated doc list for "+
206 1585 tao
                           "every server and failed to replicate");
207 1032 tao
        return;
208 1585 tao
    }
209 2286 tao
210
211 5175 daigle
    //logReplication.info("ReplicationHandler.update - Responses from remote metacat about updated "+
212
    //               "document information: "+ responses.toString());
213
214
    long totalServerListParseTime = 0;
215 1585 tao
    // go through response vector(it contains updated vector and delete vector
216
    for(int i=0; i<responses.size(); i++)
217 2286 tao
    {
218 5175 daigle
    	long startServerListParseTime = System.currentTimeMillis();
219 3898 tao
    	XMLReader parser;
220
    	ReplMessageHandler message = new ReplMessageHandler();
221
    	try
222
        {
223
          parser = initParser(message);
224
        }
225
        catch (Exception e)
226
        {
227 5014 daigle
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
228
          logReplication.error("ReplicationHandler.update - Failed to replicate becaue couldn't " +
229 3898 tao
                                " initParser for message and " +e.getMessage());
230
           // stop replication
231
           return;
232
        }
233
234 1585 tao
        try
235
        {
236
          parser.parse(new InputSource(
237 577 berkley
                     new StringReader(
238
                     (String)(responses.elementAt(i)))));
239 1585 tao
        }
240
        catch(Exception e)
241
        {
242 5014 daigle
          logMetacat.error("ReplicationHandler.update - " + ReplicationService.METACAT_REPL_ERROR_MSG);
243
          logReplication.error("ReplicationHandler.update - Couldn't parse one responses "+
244 2663 sgarg
                                   "because "+ e.getMessage());
245 1585 tao
          continue;
246
        }
247 1037 tao
        //v is the list of updated documents
248 5014 daigle
        Vector<Vector<String>> updateList = new Vector<Vector<String>>(message.getUpdatesVect());
249
        logReplication.info("ReplicationHandler.update - The document list size is "+updateList.size()+ " from "+message.getServerName());
250 577 berkley
        //System.out.println("v: " + v.toString());
251 1037 tao
        //d is the list of deleted documents
252 5014 daigle
        Vector<Vector<String>> deleteList = new Vector<Vector<String>>(message.getDeletesVect());
253 1037 tao
        //System.out.println("d: " + d.toString());
254 5014 daigle
        logReplication.info("ReplicationHandler.update - Update vector size: "+ updateList.size()+" from "+message.getServerName());
255
        logReplication.info("ReplicationHandler.update - Delete vector size: "+ deleteList.size()+" from "+message.getServerName());
256
        logReplication.info("ReplicationHandler.update - The delete document list size is "+deleteList.size()+" from "+message.getServerName());
257 1585 tao
        // go though every element in updated document vector
258 2608 tao
        handleDocList(updateList, DocumentImpl.DOCUMENTTABLE);
259 1037 tao
        //handle deleted docs
260 1585 tao
        for(int k=0; k<deleteList.size(); k++)
261 577 berkley
        { //delete the deleted documents;
262 5014 daigle
          Vector<String> w = new Vector<String>(deleteList.elementAt(k));
263 1585 tao
          String docId = (String)w.elementAt(0);
264
          try
265 579 berkley
          {
266 2298 tao
            handleDeleteSingleDocument(docId, server);
267 579 berkley
          }
268 1585 tao
          catch (Exception ee)
269
          {
270
            continue;
271
          }
272 1037 tao
        }//for delete docs
273 2608 tao
274
        // handle replicate doc in xml_revision
275 5014 daigle
        Vector<Vector<String>> revisionList = new Vector<Vector<String>>(message.getRevisionsVect());
276
        logReplication.info("ReplicationHandler.update - The revision document list size is "+revisionList.size()+ " from "+message.getServerName());
277 2608 tao
        handleDocList(revisionList, DocumentImpl.REVISIONTABLE);
278 2725 tao
        DOCINSERTNUMBER = 1;
279
        DOCERRORNUMBER  = 1;
280
        REVINSERTNUMBER = 1;
281
        REVERRORNUMBER  = 1;
282 5175 daigle
283
        totalServerListParseTime += (System.currentTimeMillis() - startServerListParseTime);
284 1585 tao
    }//for response
285 2286 tao
286 1585 tao
    //updated last_checked
287
    for (int i=0;i<serverList.size(); i++)
288
    {
289
       // Get ReplicationServer object from server list
290
       replServer = serverList.serverAt(i);
291
       try
292
       {
293
         updateLastCheckTimeForSingleServer(replServer);
294
       }
295
       catch(Exception e)
296
       {
297
         continue;
298
       }
299
    }//for
300 5175 daigle
301
    long replicationEndTime = System.currentTimeMillis();
302
    logMetacat.debug("ReplicationHandler.update - Total replication time: " +
303
    		(replicationEndTime - replicationStartTime));
304
    logMetacat.debug("ReplicationHandler.update - time to get server list: " +
305
    		timeToGetServerList);
306
    logMetacat.debug("ReplicationHandler.update - server list parse time: " +
307
    		totalServerListParseTime);
308
    logMetacat.debug("ReplicationHandler.update - 'in xml_documents' total query count: " +
309
    		_xmlDocQueryCount);
310
    logMetacat.debug("ReplicationHandler.update - 'in xml_documents' total query time: " +
311
    		_xmlDocQueryTime + " ms");
312
    logMetacat.debug("ReplicationHandler.update - 'in xml_revisions' total query count: " +
313
    		_xmlRevQueryCount);
314
    logMetacat.debug("ReplicationHandler.update - 'in xml_revisions' total query time: " +
315
    		_xmlRevQueryTime + " ms");;
316 2286 tao
317 1585 tao
  }//update
318 2286 tao
319 1585 tao
  /* Handle replicate single xml document*/
320 2286 tao
  private void handleSingleXMLDocument(String remoteserver, String actions,
321 2641 tao
                                       String accNumber, String tableName)
322 5014 daigle
               throws HandlerException
323 1585 tao
  {
324
    DBConnection dbConn = null;
325
    int serialNumber = -1;
326
    try
327
    {
328
      // Get DBConnection from pool
329
      dbConn=DBConnectionPool.
330
                  getDBConnection("ReplicationHandler.handleSingleXMLDocument");
331
      serialNumber=dbConn.getCheckOutSerialNumber();
332
      //if the document needs to be updated or inserted, this is executed
333 1600 tao
      String readDocURLString = "https://" + remoteserver + "?server="+
334 4698 daigle
              MetacatUtil.getLocalReplicationServerName()+"&action=read&docid="+accNumber;
335
      readDocURLString = MetacatUtil.replaceWhiteSpaceForURL(readDocURLString);
336 1600 tao
      URL u = new URL(readDocURLString);
337 2286 tao
338 1585 tao
      // Get docid content
339 5014 daigle
      String newxmldoc = ReplicationService.getURLContent(u);
340 1585 tao
      // If couldn't get skip it
341 1609 tao
      if ( newxmldoc.indexOf("<error>")!= -1 && newxmldoc.indexOf("</error>")!=-1)
342 1292 tao
      {
343 5014 daigle
         throw new HandlerException("ReplicationHandler.handleSingleXMLDocument - " + newxmldoc);
344 1585 tao
      }
345 5014 daigle
      //logReplication.info("xml documnet:");
346
      //logReplication.info(newxmldoc);
347 2286 tao
348 1585 tao
      // Try get the docid info from remote server
349
      DocInfoHandler dih = new DocInfoHandler();
350
      XMLReader docinfoParser = initParser(dih);
351 2286 tao
      String docInfoURLStr = "https://" + remoteserver +
352 4698 daigle
                       "?server="+MetacatUtil.getLocalReplicationServerName()+
353 2641 tao
                       "&action=getdocumentinfo&docid="+accNumber;
354 4698 daigle
      docInfoURLStr = MetacatUtil.replaceWhiteSpaceForURL(docInfoURLStr);
355 1600 tao
      URL docinfoUrl = new URL(docInfoURLStr);
356 5014 daigle
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - Sending message: " +
357 2663 sgarg
                                                  docinfoUrl.toString());
358 5014 daigle
      String docInfoStr = ReplicationService.getURLContent(docinfoUrl);
359 1585 tao
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
360 5014 daigle
      Hashtable<String, String> docinfoHash = dih.getDocInfo();
361 1585 tao
      // Get home server of the docid
362 5014 daigle
      String docHomeServer = docinfoHash.get("home_server");
363
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - doc home server in repl: "+docHomeServer);
364
      String createdDate = docinfoHash.get("date_created");
365
      String updatedDate = docinfoHash.get("date_updated");
366 1585 tao
      //docid should include rev number too
367 4212 daigle
      /*String accnum=docId+util.getProperty("document.accNumSeparator")+
368 2641 tao
                                              (String)docinfoHash.get("rev");*/
369 5014 daigle
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - docid in repl: "+accNumber);
370
      String docType = docinfoHash.get("doctype");
371
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - doctype in repl: "+docType);
372 2286 tao
373 1585 tao
      String parserBase = null;
374
      // this for eml2 and we need user eml2 parser
375 2169 sgarg
      if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE))
376 1585 tao
      {
377 2163 tao
         parserBase = DocumentImpl.EML200;
378 1585 tao
      }
379 2286 tao
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE))
380
      {
381
        parserBase = DocumentImpl.EML200;
382
      }
383
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE))
384
      {
385
        parserBase = DocumentImpl.EML210;
386
      }
387 5709 leinfelder
      else if (docType != null && (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE))
388
      {
389
        parserBase = DocumentImpl.EML210;
390
      }
391 1585 tao
      // Write the document into local host
392
      DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false);
393 2286 tao
      String newDocid = wrapper.writeReplication(dbConn,
394 5195 daigle
                              newxmldoc,
395 5014 daigle
                              docinfoHash.get("public_access"),
396 1585 tao
                              null,  /* the dtd text */
397 2286 tao
                              actions,
398 2641 tao
                              accNumber,
399 6015 leinfelder
                              null, //docinfoHash.get("user_owner"),
400 1585 tao
                              null, /* null for groups[] */
401 2286 tao
                              docHomeServer,
402 2624 tao
                              remoteserver, tableName, true,// true is for time replication
403
                              createdDate,
404 3230 tao
                              updatedDate);
405 4419 leinfelder
406 6015 leinfelder
      //set the user information
407
      String user = (String) docinfoHash.get("user_owner");
408
      String updated = (String) docinfoHash.get("user_updated");
409
      ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
410
411 5098 daigle
      //process extra access rules
412
      Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
413
      if (xmlAccessDAOList != null) {
414
      	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
415
      	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
416
      		if (!acfsf.accessControlExists(xmlAccessDAO)) {
417
      			acfsf.insertPermissions(xmlAccessDAO);
418
      		}
419 4419 leinfelder
          }
420
      }
421
422 5324 berkley
      //process guid
423
      logReplication.debug("Processing guid information from docinfoHash: " + docinfoHash.toString());
424
      String guid = docinfoHash.get("guid");
425 5944 berkley
      String docName = docinfoHash.get("docname");
426 5440 berkley
      System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%guid passed from docinfo hash: " + guid);
427 5324 berkley
      IdentifierManager idman = IdentifierManager.getInstance();
428 5944 berkley
429
      System.out.println("docname: " + docName);
430
431 5324 berkley
      if(guid != null && !idman.identifierExists(guid))
432
      { //if the guid was passed in, put it in the identifiers table
433
        logReplication.debug("Creating guid/docid mapping for docid " +
434
          docinfoHash.get("docid") + " and guid: " + guid);
435 5944 berkley
436
437
        System.out.println("creating mapping: guid: " + guid + " localId: " + docinfoHash.get("docid"));
438
        idman.createMapping(guid, docinfoHash.get("docid"));
439 5324 berkley
      }
440
      else
441
      {
442
        logReplication.debug("No guid information was included with the replicated document");
443
      }
444
445 5944 berkley
      //handle systemMetadata
446
      if(docName.trim().equals("systemMetadata"))
447
      {
448
          System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!creating mapping for systemMetadata: guid: " + guid + " localId: " + docinfoHash.get("docid"));
449
          idman.createSystemMetadataMapping(guid, docinfoHash.get("docid"));
450
          Long dateUploadedLong = new Long(docinfoHash.get("date_uploaded"));
451
          Long dateModifiedLong = new Long(docinfoHash.get("date_modified"));
452
          idman.insertAdditionalSystemMetadataFields(
453
                  dateUploadedLong.longValue(),
454
                  docinfoHash.get("rights_holder"),
455
                  docinfoHash.get("checksum"),
456
                  docinfoHash.get("checksum_algorithm"),
457
                  docinfoHash.get("origin_member_node"),
458
                  docinfoHash.get("authoritive_member_node"),
459
                  dateModifiedLong.longValue(),
460
                  docinfoHash.get("submitter"),
461
                  docinfoHash.get("guid"),
462
                  docinfoHash.get("objectFormat"),
463
                  new Long(docinfoHash.get("size")).longValue());
464
          System.out.println("4");
465
      }
466
467 5457 berkley
      if(guid != null)
468
      {
469 5459 berkley
          if(!docName.trim().equals("systemMetadata"))
470
          {
471
              logReplication.info("replicate D1GUID:" + guid + ":D1SCIMETADATA:" +
472
                      accNumber + ":");
473
          }
474
          else
475
          {
476
              logReplication.info("replicate D1GUID:" + guid + ":D1SYSMETADATA:" +
477
                      accNumber + ":");
478
          }
479 5457 berkley
      }
480
481 5014 daigle
      logReplication.info("ReplicationHandler.handleSingleXMLDocument - Successfully replicated doc " + accNumber);
482 2725 tao
      if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
483
      {
484 5014 daigle
        logReplication.info("ReplicationHandler.handleSingleXMLDocument - " + DOCINSERTNUMBER + " Wrote xml doc " + accNumber +
485 2725 tao
                                     " into "+tableName + " from " +
486 1585 tao
                                         remoteserver);
487 2725 tao
        DOCINSERTNUMBER++;
488
      }
489
      else
490
      {
491 5014 daigle
          logReplication.info("ReplicationHandler.handleSingleXMLDocument - " +REVINSERTNUMBER + " Wrote xml doc " + accNumber +
492 2725 tao
                  " into "+tableName + " from " +
493
                      remoteserver);
494
          REVINSERTNUMBER++;
495
      }
496 3234 tao
      String ip = getIpFromURL(u);
497 5014 daigle
      EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
498 2725 tao
499 2286 tao
500
    }//try
501 577 berkley
    catch(Exception e)
502
    {
503 2725 tao
504
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
505
        {
506 5014 daigle
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
507
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +DOCERRORNUMBER + " Failed to write xml doc " + accNumber +
508 2725 tao
                                       " into "+tableName + " from " +
509
                                           remoteserver + " because "+e.getMessage());
510
          DOCERRORNUMBER++;
511
        }
512
        else
513
        {
514 5014 daigle
        	logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
515
        	logReplication.error("ReplicationHandler.handleSingleXMLDocument - " +REVERRORNUMBER + " Failed to write xml doc " + accNumber +
516 2725 tao
                    " into "+tableName + " from " +
517
                        remoteserver +" because "+e.getMessage());
518
            REVERRORNUMBER++;
519
        }
520 5014 daigle
        logMetacat.error("ReplicationHandler.handleSingleXMLDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
521
        logReplication.error("ReplicationHandler.handleSingleXMLDocument - Failed to write doc " + accNumber +
522 2663 sgarg
                                      " into db because " +e.getMessage());
523 5014 daigle
      throw new HandlerException("ReplicationHandler.handleSingleXMLDocument - generic exception "
524
    		  + "writing Replication: " +e.getMessage());
525 577 berkley
    }
526 667 berkley
    finally
527
    {
528 1585 tao
       //return DBConnection
529
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
530
    }//finally
531 5392 berkley
    logD1.info("replication.create localId:" + accNumber);
532 1585 tao
  }
533 2286 tao
534
535
536 1585 tao
  /* Handle replicate single xml document*/
537 2286 tao
  private void handleSingleDataFile(String remoteserver, String actions,
538 2641 tao
                                    String accNumber, String tableName)
539 5014 daigle
               throws HandlerException
540 1585 tao
  {
541 5014 daigle
    logReplication.info("ReplicationHandler.handleSingleDataFile - Try to replicate data file: " + accNumber);
542 1585 tao
    DBConnection dbConn = null;
543
    int serialNumber = -1;
544
    try
545
    {
546
      // Get DBConnection from pool
547
      dbConn=DBConnectionPool.
548
                  getDBConnection("ReplicationHandler.handleSinlgeDataFile");
549
      serialNumber=dbConn.getCheckOutSerialNumber();
550
      // Try get docid info from remote server
551
      DocInfoHandler dih = new DocInfoHandler();
552
      XMLReader docinfoParser = initParser(dih);
553 2286 tao
      String docInfoURLString = "https://" + remoteserver +
554 4698 daigle
                  "?server="+MetacatUtil.getLocalReplicationServerName()+
555 2641 tao
                  "&action=getdocumentinfo&docid="+accNumber;
556 4698 daigle
      docInfoURLString = MetacatUtil.replaceWhiteSpaceForURL(docInfoURLString);
557 1600 tao
      URL docinfoUrl = new URL(docInfoURLString);
558 2286 tao
559 5014 daigle
      String docInfoStr = ReplicationService.getURLContent(docinfoUrl);
560 1585 tao
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
561 5014 daigle
      Hashtable<String, String> docinfoHash = dih.getDocInfo();
562 6015 leinfelder
563 1585 tao
      // Get docid name (such as acl or dataset)
564 5014 daigle
      String docName = docinfoHash.get("docname");
565 2286 tao
      // Get doc type (eml public id)
566 5014 daigle
      String docType = docinfoHash.get("doctype");
567 1585 tao
      // Get docid home sever. it might be different to remoteserver
568 5175 daigle
      // because of hub feature
569 5014 daigle
      String docHomeServer = docinfoHash.get("home_server");
570
      String createdDate = docinfoHash.get("date_created");
571
      String updatedDate = docinfoHash.get("date_updated");
572 1585 tao
      //docid should include rev number too
573 4212 daigle
      /*String accnum=docId+util.getProperty("document.accNumSeparator")+
574 2641 tao
                                              (String)docinfoHash.get("rev");*/
575 2286 tao
576
577 4080 daigle
      String datafilePath = PropertyService.getProperty("application.datafilepath");
578 1585 tao
      // Get data file content
579 1600 tao
      String readDataURLString = "https://" + remoteserver + "?server="+
580 4698 daigle
                                        MetacatUtil.getLocalReplicationServerName()+
581 2641 tao
                                            "&action=readdata&docid="+accNumber;
582 4698 daigle
      readDataURLString = MetacatUtil.replaceWhiteSpaceForURL(readDataURLString);
583 1600 tao
      URL u = new URL(readDataURLString);
584 2286 tao
      InputStream input = u.openStream();
585 1585 tao
      //register data file into xml_documents table and wite data file
586
      //into file system
587 1600 tao
      if ( input != null)
588 667 berkley
      {
589 2286 tao
        DocumentImpl.writeDataFileInReplication(input,
590 2608 tao
                                                datafilePath,
591
                                                docName,docType,
592 6015 leinfelder
                                                accNumber,
593
                                                null,
594 2608 tao
                                                docHomeServer,
595
                                                remoteserver,
596
                                                tableName,
597 2624 tao
                                                true, //true means timed replication
598
                                                createdDate,
599 3230 tao
                                                updatedDate);
600 2624 tao
601 6015 leinfelder
        //set the user information
602
        String user = (String) docinfoHash.get("user_owner");
603
		String updated = (String) docinfoHash.get("user_updated");
604
        ReplicationService.updateUserOwner(dbConn, accNumber, user, updated);
605
606 4419 leinfelder
        //process extra access rules
607 5098 daigle
        Vector<XMLAccessDAO> xmlAccessDAOList = dih.getAccessControlList();
608
        if (xmlAccessDAOList != null) {
609
        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(accNumber);
610
        	for (XMLAccessDAO xmlAccessDAO : xmlAccessDAOList) {
611
        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
612
        			acfsf.insertPermissions(xmlAccessDAO);
613
        		}
614 4419 leinfelder
            }
615
        }
616
617 5014 daigle
        logReplication.info("ReplicationHandler.handleSingleDataFile - Successfully to write datafile " + accNumber);
618 2725 tao
        /*MetacatReplication.replLog("wrote datafile " + accNumber + " from " +
619 5175 daigle
                                    remote server);*/
620 2725 tao
        if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
621
        {
622 5014 daigle
          logReplication.info("ReplicationHandler.handleSingleDataFile - " + DOCINSERTNUMBER + " Wrote data file" + accNumber +
623 2725 tao
                                       " into "+tableName + " from " +
624
                                           remoteserver);
625
          DOCINSERTNUMBER++;
626
        }
627
        else
628
        {
629 5014 daigle
            logReplication.info("ReplicationHandler.handleSingleDataFile - " + REVINSERTNUMBER + " Wrote data file" + accNumber +
630 2725 tao
                    " into "+tableName + " from " +
631
                        remoteserver);
632
            REVINSERTNUMBER++;
633
        }
634 3234 tao
        String ip = getIpFromURL(u);
635 5014 daigle
        EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, accNumber, actions);
636 2725 tao
637 1585 tao
      }//if
638
      else
639 1217 tao
      {
640 5014 daigle
         logReplication.info("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
641
         throw new HandlerException("ReplicationHandler.handleSingleDataFile - Couldn't open the data file: " + accNumber);
642 1585 tao
      }//else
643 2286 tao
644
    }//try
645 1585 tao
    catch(Exception e)
646
    {
647 5175 daigle
      /*MetacatReplication.replErrorLog("Failed to try wrote data file " + accNumber +
648 2725 tao
                                      " because " +e.getMessage());*/
649
      if (tableName.equals(DocumentImpl.DOCUMENTTABLE))
650
      {
651 5014 daigle
    	logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG);
652
    	logReplication.error("ReplicationHandler.handleSingleDataFile - " + DOCERRORNUMBER + " Failed to write data file " + accNumber +
653
                                     " into " + tableName + " from " +
654
                                         remoteserver + " because " + e.getMessage());
655 2725 tao
        DOCERRORNUMBER++;
656
      }
657
      else
658
      {
659 5014 daigle
    	  logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG);
660
    	  logReplication.error("ReplicationHandler.handleSingleDataFile - " + REVERRORNUMBER + " Failed to write data file" + accNumber +
661
                  " into " + tableName + " from " +
662
                      remoteserver +" because "+ e.getMessage());
663 2725 tao
          REVERRORNUMBER++;
664
      }
665 5014 daigle
      logMetacat.error("ReplicationHandler.handleSingleDataFile - " + ReplicationService.METACAT_REPL_ERROR_MSG);
666
      logReplication.error("ReplicationHandler.handleSingleDataFile - Failed to try wrote datafile " + accNumber +
667
                                      " because " + e.getMessage());
668
      throw new HandlerException("ReplicationHandler.handleSingleDataFile - generic exception "
669
    		  + "writing Replication: " + e.getMessage());
670 1585 tao
    }
671
    finally
672
    {
673
       //return DBConnection
674
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
675
    }//finally
676 5392 berkley
    logD1.info("replication.create localId:" + accNumber);
677 1585 tao
  }
678 2286 tao
679
680
681 1585 tao
  /* Handle delete single document*/
682 2298 tao
  private void handleDeleteSingleDocument(String docId, String notifyServer)
683 5014 daigle
               throws HandlerException
684 1585 tao
  {
685 5014 daigle
    logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Try delete doc: "+docId);
686 1585 tao
    DBConnection dbConn = null;
687
    int serialNumber = -1;
688
    try
689
    {
690
      // Get DBConnection from pool
691
      dbConn=DBConnectionPool.
692
                  getDBConnection("ReplicationHandler.handleDeleteSingleDoc");
693
      serialNumber=dbConn.getCheckOutSerialNumber();
694
      if(!alreadyDeleted(docId))
695 1217 tao
      {
696 2286 tao
697 1585 tao
         //because delete method docid should have rev number
698
         //so we just add one for it. This rev number is no sence.
699 4212 daigle
         String accnum=docId+PropertyService.getProperty("document.accNumSeparator")+"1";
700 1585 tao
         //System.out.println("accnum: "+accnum);
701 3230 tao
         DocumentImpl.delete(accnum, null, null, notifyServer);
702 5014 daigle
         logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Successfully deleted doc " + docId);
703
         logReplication.info("ReplicationHandler.handleDeleteSingleDocument - Doc " + docId + " deleted");
704 3234 tao
         URL u = new URL("https://"+notifyServer);
705
         String ip = getIpFromURL(u);
706 5014 daigle
         EventLog.getInstance().log(ip, ReplicationService.REPLICATIONUSER, docId, "delete");
707 1585 tao
      }
708 2286 tao
709
    }//try
710 6001 cjones
    catch(McdbDocNotFoundException e)
711
    {
712
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
713
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
714
                                 " in db because because " + e.getMessage());
715
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception "
716
    		  + "when handling document: " + e.getMessage());
717
    }
718
    catch(InsufficientKarmaException e)
719
    {
720
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
721
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
722
                                 " in db because because " + e.getMessage());
723
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception "
724
    		  + "when handling document: " + e.getMessage());
725
    }
726
    catch(SQLException e)
727
    {
728
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
729
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
730
                                 " in db because because " + e.getMessage());
731
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception "
732
    		  + "when handling document: " + e.getMessage());
733
    }
734 1585 tao
    catch(Exception e)
735
    {
736 5014 daigle
      logMetacat.error("ReplicationHandler.handleDeleteSingleDocument - " + ReplicationService.METACAT_REPL_ERROR_MSG);
737
      logReplication.error("ReplicationHandler.handleDeleteSingleDocument - Failed to delete doc " + docId +
738 2663 sgarg
                                 " in db because because " + e.getMessage());
739 5014 daigle
      throw new HandlerException("ReplicationHandler.handleDeleteSingleDocument - generic exception "
740
    		  + "when handling document: " + e.getMessage());
741 1585 tao
    }
742
    finally
743
    {
744
       //return DBConnection
745
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
746 1037 tao
    }//finally
747 5392 berkley
    logD1.info("replication.handleDeleteSingleDocument localId:" + docId);
748 1585 tao
  }
749 2286 tao
750 1585 tao
  /* Handle updateLastCheckTimForSingleServer*/
751 2286 tao
  private void updateLastCheckTimeForSingleServer(ReplicationServer repServer)
752 5014 daigle
                                                  throws HandlerException
753 590 berkley
  {
754 1585 tao
    String server = repServer.getServerName();
755 1217 tao
    DBConnection dbConn = null;
756
    int serialNumber = -1;
757
    PreparedStatement pstmt = null;
758 590 berkley
    try
759
    {
760 1585 tao
      // Get DBConnection from pool
761 1217 tao
      dbConn=DBConnectionPool.
762 1585 tao
             getDBConnection("ReplicationHandler.updateLastCheckTimeForServer");
763 1217 tao
      serialNumber=dbConn.getCheckOutSerialNumber();
764 2286 tao
765 5014 daigle
      logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - Try to update last_check for server: "+server);
766 1585 tao
      // Get time from remote server
767
      URL dateurl = new URL("https://" + server + "?server="+
768 4698 daigle
      MetacatUtil.getLocalReplicationServerName()+"&action=gettime");
769 5014 daigle
      String datexml = ReplicationService.getURLContent(dateurl);
770
      logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - datexml: "+datexml);
771 1585 tao
      if (datexml!=null && !datexml.equals(""))
772
      {
773
         String datestr = datexml.substring(11, datexml.indexOf('<', 11));
774
         StringBuffer sql = new StringBuffer();
775 1751 tao
         /*sql.append("update xml_replication set last_checked = to_date('");
776 1585 tao
         sql.append(datestr).append("', 'YY-MM-DD HH24:MI:SS') where ");
777 1751 tao
         sql.append("server like '").append(server).append("'");*/
778
         sql.append("update xml_replication set last_checked = ");
779 5319 jones
         sql.append(DatabaseService.getInstance().getDBAdapter().toDate(datestr, "MM/DD/YY HH24:MI:SS"));
780 1751 tao
         sql.append(" where server like '").append(server).append("'");
781 1585 tao
         pstmt = dbConn.prepareStatement(sql.toString());
782 2286 tao
783 1585 tao
         pstmt.executeUpdate();
784
         dbConn.commit();
785
         pstmt.close();
786 5014 daigle
         logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - last_checked updated to "+datestr+" on "
787 2663 sgarg
                                      + server);
788 1585 tao
      }//if
789
      else
790
      {
791 2286 tao
792 5014 daigle
         logReplication.info("ReplicationHandler.updateLastCheckTimeForSingleServer - Failed to update last_checked for server "  +
793 2286 tao
                                  server + " in db because couldn't get time "
794 2663 sgarg
                                  );
795 1585 tao
         throw new Exception("Couldn't get time for server "+ server);
796
      }
797 2286 tao
798
    }//try
799 1585 tao
    catch(Exception e)
800
    {
801 5014 daigle
      logMetacat.error("ReplicationHandler.updateLastCheckTimeForSingleServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);
802
      logReplication.error("ReplicationHandler.updateLastCheckTimeForSingleServer - Failed to update last_checked for server " +
803
                                server + " in db because because " + e.getMessage());
804
      throw new HandlerException("ReplicationHandler.updateLastCheckTimeForSingleServer - "
805
    		  + "Error updating last checked time: " + e.getMessage());
806 1585 tao
    }
807
    finally
808
    {
809
       //return DBConnection
810
       DBConnectionPool.returnDBConnection(dbConn, serialNumber);
811
    }//finally
812
  }
813 2286 tao
814
815
816 1585 tao
  /**
817
   * updates xml_catalog with entries from other servers.
818
   */
819
  private void updateCatalog()
820
  {
821 5014 daigle
    logReplication.info("ReplicationHandler.updateCatalog - Start of updateCatalog");
822 1585 tao
    // ReplicationServer object in server list
823
    ReplicationServer replServer = null;
824
    PreparedStatement pstmt = null;
825
    String server = null;
826 2286 tao
827
828 1585 tao
    // Go through each ReplicationServer object in sererlist
829
    for (int j=0; j<serverList.size(); j++)
830 2286 tao
    {
831 5014 daigle
      Vector<Vector<String>> remoteCatalog = new Vector<Vector<String>>();
832
      Vector<String> publicId = new Vector<String>();
833 1585 tao
      try
834
      {
835 1292 tao
        // Get ReplicationServer object from server list
836
        replServer = serverList.serverAt(j);
837
        // Get server name from the ReplicationServer object
838
        server = replServer.getServerName();
839
        // Try to get catalog
840 1011 tao
        URL u = new URL("https://" + server + "?server="+
841 4698 daigle
        MetacatUtil.getLocalReplicationServerName()+"&action=getcatalog");
842 5014 daigle
        logReplication.info("ReplicationHandler.updateCatalog - sending message " + u.toString());
843
        String catxml = ReplicationService.getURLContent(u);
844 2286 tao
845 1292 tao
        // Make sure there are not error, no empty string
846
        if (catxml.indexOf("error")!=-1 || catxml==null||catxml.equals(""))
847
        {
848 1585 tao
          throw new Exception("Couldn't get catalog list form server " +server);
849 1292 tao
        }
850 5175 daigle
        logReplication.debug("ReplicationHandler.updateCatalog - catxml: " + catxml);
851 590 berkley
        CatalogMessageHandler cmh = new CatalogMessageHandler();
852
        XMLReader catparser = initParser(cmh);
853
        catparser.parse(new InputSource(new StringReader(catxml)));
854
        //parse the returned catalog xml and put it into a vector
855 1585 tao
        remoteCatalog = cmh.getCatalogVect();
856 2286 tao
857 5175 daigle
        // Make sure remoteCatalog is not empty
858 1292 tao
        if (remoteCatalog.isEmpty())
859
        {
860 1585 tao
          throw new Exception("Couldn't get catalog list form server " +server);
861 1292 tao
        }
862 2286 tao
863 5014 daigle
        String localcatxml = ReplicationService.getCatalogXML();
864 2286 tao
865 1292 tao
        // Make sure local catalog is no empty
866
        if (localcatxml==null||localcatxml.equals(""))
867
        {
868 1585 tao
          throw new Exception("Couldn't get catalog list form server " +server);
869 1292 tao
        }
870 2286 tao
871 590 berkley
        cmh = new CatalogMessageHandler();
872
        catparser = initParser(cmh);
873
        catparser.parse(new InputSource(new StringReader(localcatxml)));
874 5014 daigle
        Vector<Vector<String>> localCatalog = cmh.getCatalogVect();
875 2286 tao
876 590 berkley
        //now we have the catalog from the remote server and this local server
877
        //we now need to compare the two and merge the differences.
878
        //the comparison is base on the public_id fields which is the 4th
879
        //entry in each row vector.
880 5014 daigle
        publicId = new Vector<String>();
881 590 berkley
        for(int i=0; i<localCatalog.size(); i++)
882
        {
883 5014 daigle
          Vector<String> v = new Vector<String>(localCatalog.elementAt(i));
884
          logReplication.info("ReplicationHandler.updateCatalog - v1: " + v.toString());
885 590 berkley
          publicId.add(new String((String)v.elementAt(3)));
886 595 berkley
          //System.out.println("adding " + (String)v.elementAt(3));
887 590 berkley
        }
888 1585 tao
      }//try
889
      catch (Exception e)
890
      {
891 5014 daigle
        logMetacat.error("ReplicationHandler.updateCatalog - " + ReplicationService.METACAT_REPL_ERROR_MSG);
892
        logReplication.error("ReplicationHandler.updateCatalog - Failed to update catalog for server "+
893 1585 tao
                                    server + " because " +e.getMessage());
894
      }//catch
895 2286 tao
896 1585 tao
      for(int i=0; i<remoteCatalog.size(); i++)
897
      {
898
         // DConnection
899
        DBConnection dbConn = null;
900
        // DBConnection checkout serial number
901
        int serialNumber = -1;
902
        try
903 590 berkley
        {
904 1585 tao
            dbConn=DBConnectionPool.
905
                  getDBConnection("ReplicationHandler.updateCatalog");
906
            serialNumber=dbConn.getCheckOutSerialNumber();
907 5014 daigle
            Vector<String> v = remoteCatalog.elementAt(i);
908 1585 tao
            //System.out.println("v2: " + v.toString());
909
            //System.out.println("i: " + i);
910
            //System.out.println("remoteCatalog.size(): " + remoteCatalog.size());
911
            //System.out.println("publicID: " + publicId.toString());
912 5014 daigle
            logReplication.info
913
                              ("ReplicationHandler.updateCatalog - v.elementAt(3): " + (String)v.elementAt(3));
914 1585 tao
           if(!publicId.contains(v.elementAt(3)))
915
           { //so we don't have this public id in our local table so we need to
916
             //add it.
917
             //System.out.println("in if");
918
             StringBuffer sql = new StringBuffer();
919
             sql.append("insert into xml_catalog (entry_type, source_doctype, ");
920
             sql.append("target_doctype, public_id, system_id) values (?,?,?,");
921
             sql.append("?,?)");
922
             //System.out.println("sql: " + sql.toString());
923
             pstmt = dbConn.prepareStatement(sql.toString());
924
             pstmt.setString(1, (String)v.elementAt(0));
925
             pstmt.setString(2, (String)v.elementAt(1));
926
             pstmt.setString(3, (String)v.elementAt(2));
927
             pstmt.setString(4, (String)v.elementAt(3));
928
             pstmt.setString(5, (String)v.elementAt(4));
929
             pstmt.execute();
930
             pstmt.close();
931 5014 daigle
             logReplication.info("ReplicationHandler.updateCatalog - Success fully to insert new publicid "+
932 1585 tao
                               (String)v.elementAt(3) + " from server"+server);
933
           }
934 590 berkley
        }
935 1585 tao
        catch(Exception e)
936
        {
937 5014 daigle
           logMetacat.error("ReplicationHandler.updateCatalog - " + ReplicationService.METACAT_REPL_ERROR_MSG);
938
           logReplication.error("ReplicationHandler.updateCatalog - Failed to update catalog for server "+
939 1585 tao
                                    server + " because " +e.getMessage());
940
        }//catch
941
        finally
942
        {
943
           DBConnectionPool.returnDBConnection(dbConn, serialNumber);
944 5175 daigle
        }//finally
945 1585 tao
      }//for remote catalog
946
    }//for server list
947 5014 daigle
    logReplication.info("End of updateCatalog");
948 590 berkley
  }
949 2286 tao
950 590 berkley
  /**
951 579 berkley
   * Method that returns true if docid has already been "deleted" from metacat.
952 582 berkley
   * This method really implements a truth table for deleted documents
953 590 berkley
   * The table is (a docid in one of the tables is represented by the X):
954 582 berkley
   * xml_docs      xml_revs      deleted?
955
   * ------------------------------------
956
   *   X             X             FALSE
957
   *   X             _             FALSE
958
   *   _             X             TRUE
959
   *   _             _             TRUE
960 579 berkley
   */
961 5014 daigle
  private static boolean alreadyDeleted(String docid) throws HandlerException
962 579 berkley
  {
963 1217 tao
    DBConnection dbConn = null;
964
    int serialNumber = -1;
965
    PreparedStatement pstmt = null;
966 579 berkley
    try
967
    {
968 1217 tao
      dbConn=DBConnectionPool.
969
                  getDBConnection("ReplicationHandler.alreadyDeleted");
970
      serialNumber=dbConn.getCheckOutSerialNumber();
971 582 berkley
      boolean xml_docs = false;
972
      boolean xml_revs = false;
973 2286 tao
974 579 berkley
      StringBuffer sb = new StringBuffer();
975 582 berkley
      sb.append("select docid from xml_revisions where docid like '");
976 579 berkley
      sb.append(docid).append("'");
977 1217 tao
      pstmt = dbConn.prepareStatement(sb.toString());
978 579 berkley
      pstmt.execute();
979
      ResultSet rs = pstmt.getResultSet();
980
      boolean tablehasrows = rs.next();
981
      if(tablehasrows)
982
      {
983 582 berkley
        xml_revs = true;
984
      }
985 2286 tao
986 582 berkley
      sb = new StringBuffer();
987
      sb.append("select docid from xml_documents where docid like '");
988
      sb.append(docid).append("'");
989 667 berkley
      pstmt.close();
990 1217 tao
      pstmt = dbConn.prepareStatement(sb.toString());
991
      //increase usage count
992
      dbConn.increaseUsageCount(1);
993 582 berkley
      pstmt.execute();
994
      rs = pstmt.getResultSet();
995
      tablehasrows = rs.next();
996 667 berkley
      pstmt.close();
997 582 berkley
      if(tablehasrows)
998
      {
999
        xml_docs = true;
1000
      }
1001 2286 tao
1002 582 berkley
      if(xml_docs && xml_revs)
1003
      {
1004
        return false;
1005
      }
1006
      else if(xml_docs && !xml_revs)
1007
      {
1008
        return false;
1009
      }
1010
      else if(!xml_docs && xml_revs)
1011
      {
1012 579 berkley
        return true;
1013
      }
1014 582 berkley
      else if(!xml_docs && !xml_revs)
1015
      {
1016
        return true;
1017
      }
1018 579 berkley
    }
1019
    catch(Exception e)
1020
    {
1021 5014 daigle
      logMetacat.error("ReplicationHandler.alreadyDeleted - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1022
      logReplication.error("ReplicationHandler.alreadyDeleted - general error in alreadyDeleted: " +
1023 2663 sgarg
                          e.getMessage());
1024 5014 daigle
      throw new HandlerException("ReplicationHandler.alreadyDeleted - general error: "
1025
    		  + e.getMessage());
1026 579 berkley
    }
1027 667 berkley
    finally
1028
    {
1029 1217 tao
      try
1030
      {
1031
        pstmt.close();
1032
      }//try
1033
      catch (SQLException ee)
1034
      {
1035 5014 daigle
    	logMetacat.error("ReplicationHandler.alreadyDeleted - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1036
        logReplication.error("ReplicationHandler.alreadyDeleted - Error in replicationHandler.alreadyDeleted "+
1037 2663 sgarg
                          "to close pstmt: "+ee.getMessage());
1038 5014 daigle
        throw new HandlerException("ReplicationHandler.alreadyDeleted - SQL error when closing prepared statement: "
1039
      		  + ee.getMessage());
1040 1217 tao
      }//catch
1041
      finally
1042
      {
1043
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1044
      }//finally
1045
    }//finally
1046 579 berkley
    return false;
1047
  }
1048 533 berkley
1049 2286 tao
1050 533 berkley
  /**
1051 574 berkley
   * Method to initialize the message parser
1052
   */
1053
  public static XMLReader initParser(DefaultHandler dh)
1054 5014 daigle
          throws HandlerException
1055 574 berkley
  {
1056
    XMLReader parser = null;
1057
1058
    try {
1059
      ContentHandler chandler = dh;
1060
1061
      // Get an instance of the parser
1062 4213 daigle
      String parserName = PropertyService.getProperty("xml.saxparser");
1063 574 berkley
      parser = XMLReaderFactory.createXMLReader(parserName);
1064
1065
      // Turn off validation
1066
      parser.setFeature("http://xml.org/sax/features/validation", false);
1067 2286 tao
1068 574 berkley
      parser.setContentHandler((ContentHandler)chandler);
1069
      parser.setErrorHandler((ErrorHandler)chandler);
1070
1071 5014 daigle
    } catch (SAXException se) {
1072
      throw new HandlerException("ReplicationHandler.initParser - Sax error when "
1073
    		  + " initializing parser: " + se.getMessage());
1074
    } catch (PropertyNotFoundException pnfe) {
1075
        throw new HandlerException("ReplicationHandler.initParser - Property error when "
1076
      		  + " getting parser name: " + pnfe.getMessage());
1077
    }
1078 574 berkley
1079
    return parser;
1080
  }
1081 2286 tao
1082 2555 tao
  /**
1083 5175 daigle
	 * This method will combine given time string(in short format) to current
1084 5014 daigle
	 * date. If the given time (e.g 10:00 AM) passed the current time (e.g 2:00
1085
	 * PM Aug 21, 2005), then the time will set to second day, 10:00 AM Aug 22,
1086
	 * 2005. If the given time (e.g 10:00 AM) haven't passed the current time
1087
	 * (e.g 8:00 AM Aug 21, 2005) The time will set to be 10:00 AM Aug 21, 2005.
1088
	 *
1089
	 * @param givenTime
1090
	 *            the format should be "10:00 AM " or "2:00 PM"
1091
	 * @return
1092
	 * @throws Exception
1093
	 */
1094
	public static Date combinateCurrentDateAndGivenTime(String givenTime) throws HandlerException
1095 2555 tao
  {
1096 5014 daigle
	  try {
1097 2555 tao
     Date givenDate = parseTime(givenTime);
1098
     Date newDate = null;
1099
     Date now = new Date();
1100
     String currentTimeString = getTimeString(now);
1101
     Date currentTime = parseTime(currentTimeString);
1102
     if ( currentTime.getTime() >= givenDate.getTime())
1103
     {
1104 5014 daigle
        logReplication.info("ReplicationHandler.combinateCurrentDateAndGivenTime - Today already pass the given time, we should set it as tomorrow");
1105 2555 tao
        String dateAndTime = getDateString(now) + " " + givenTime;
1106
        Date combinationDate = parseDateTime(dateAndTime);
1107
        // new date should plus 24 hours to make is the second day
1108
        newDate = new Date(combinationDate.getTime()+24*3600*1000);
1109
     }
1110
     else
1111
     {
1112 5014 daigle
         logReplication.info("ReplicationHandler.combinateCurrentDateAndGivenTime - Today haven't pass the given time, we should it as today");
1113 2555 tao
         String dateAndTime = getDateString(now) + " " + givenTime;
1114
         newDate = parseDateTime(dateAndTime);
1115
     }
1116 5014 daigle
     logReplication.warn("ReplicationHandler.combinateCurrentDateAndGivenTime - final setting time is "+ newDate.toString());
1117 2555 tao
     return newDate;
1118 5014 daigle
	  } catch (ParseException pe) {
1119
		  throw new HandlerException("ReplicationHandler.combinateCurrentDateAndGivenTime - "
1120
				  + "parsing error: "  + pe.getMessage());
1121
	  }
1122 2555 tao
  }
1123 2286 tao
1124 2555 tao
  /*
1125 5014 daigle
	 * parse a given string to Time in short format. For example, given time is
1126
	 * 10:00 AM, the date will be return as Jan 1 1970, 10:00 AM
1127
	 */
1128
  private static Date parseTime(String timeString) throws ParseException
1129 2555 tao
  {
1130
    DateFormat format = DateFormat.getTimeInstance(DateFormat.SHORT);
1131
    Date time = format.parse(timeString);
1132 5014 daigle
    logReplication.info("ReplicationHandler.parseTime - Date string is after parse a time string "
1133 2663 sgarg
                              +time.toString());
1134 2555 tao
    return time;
1135
1136
  }
1137
1138
  /*
1139 5175 daigle
   * Parse a given string to date and time. Date format is long and time
1140 2555 tao
   * format is short.
1141
   */
1142 5014 daigle
  private static Date parseDateTime(String timeString) throws ParseException
1143 2555 tao
  {
1144
    DateFormat format = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT);
1145
    Date time = format.parse(timeString);
1146 5014 daigle
    logReplication.info("ReplicationHandler.parseDateTime - Date string is after parse a time string "+
1147 2663 sgarg
                             time.toString());
1148 2555 tao
    return time;
1149
  }
1150
1151
  /*
1152
   * Get a date string from a Date object. The date format will be long
1153
   */
1154
  private static String getDateString(Date now)
1155
  {
1156
     DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);
1157
     String s = df.format(now);
1158 5014 daigle
     logReplication.info("ReplicationHandler.getDateString - Today is " + s);
1159 2555 tao
     return s;
1160
  }
1161
1162
  /*
1163
   * Get a time string from a Date object, the time format will be short
1164
   */
1165
  private static String getTimeString(Date now)
1166
  {
1167
     DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
1168
     String s = df.format(now);
1169 5014 daigle
     logReplication.info("ReplicationHandler.getTimeString - Time is " + s);
1170 2555 tao
     return s;
1171
  }
1172 2608 tao
1173
1174
  /*
1175 4080 daigle
	 * This method will go through the docid list both in xml_Documents table
1176
	 * and in xml_revisions table @author tao
1177
	 */
1178 5014 daigle
	private void handleDocList(Vector<Vector<String>> docList, String tableName) {
1179 4080 daigle
		boolean dataFile = false;
1180
		for (int j = 0; j < docList.size(); j++) {
1181
			// initial dataFile is false
1182
			dataFile = false;
1183
			// w is information for one document, information contain
1184
			// docid, rev, server or datafile.
1185 5014 daigle
			Vector<String> w = new Vector<String>(docList.elementAt(j));
1186 4080 daigle
			// Check if the vector w contain "datafile"
1187
			// If it has, this document is data file
1188
			try {
1189 4173 daigle
				if (w.contains((String) PropertyService.getProperty("replication.datafileflag"))) {
1190 4080 daigle
					dataFile = true;
1191
				}
1192
			} catch (PropertyNotFoundException pnfe) {
1193 5014 daigle
				logMetacat.error("ReplicationHandler.handleDocList - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1194
				logReplication.error("ReplicationHandler.handleDocList - Could not retrieve data file flag property.  "
1195 4080 daigle
						+ "Leaving as false: " + pnfe.getMessage());
1196
			}
1197
			// System.out.println("w: " + w.toString());
1198
			// Get docid
1199
			String docid = (String) w.elementAt(0);
1200 5014 daigle
			logReplication.info("docid: " + docid);
1201 4080 daigle
			// Get revision number
1202
			int rev = Integer.parseInt((String) w.elementAt(1));
1203 5014 daigle
			logReplication.info("rev: " + rev);
1204 5175 daigle
			// Get remote server name (it is may not be doc home server because
1205 4080 daigle
			// the new hub feature
1206
			String remoteServer = (String) w.elementAt(2);
1207
			remoteServer = remoteServer.trim();
1208 2608 tao
1209 4080 daigle
			try {
1210
				if (tableName.equals(DocumentImpl.DOCUMENTTABLE)) {
1211
					handleDocInXMLDocuments(docid, rev, remoteServer, dataFile);
1212
				} else if (tableName.equals(DocumentImpl.REVISIONTABLE)) {
1213
					handleDocInXMLRevisions(docid, rev, remoteServer, dataFile);
1214
				} else {
1215
					continue;
1216
				}
1217 2608 tao
1218 4861 daigle
			} catch (Exception e) {
1219 5014 daigle
				logMetacat.error("ReplicationHandler.handleDocList - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1220
				logReplication.error("ReplicationHandler.handleDocList - error to handle update doc in " + tableName
1221 4861 daigle
						+ " in time replication" + e.getMessage());
1222 4080 daigle
				continue;
1223
			}
1224 5175 daigle
1225
	        if (_xmlDocQueryCount > 0 && (_xmlDocQueryCount % 100) == 0) {
1226
	        	logMetacat.debug("ReplicationHandler.update - xml_doc query count: " + _xmlDocQueryCount +
1227
	        			", xml_doc avg query time: " + (_xmlDocQueryTime / _xmlDocQueryCount));
1228
	        }
1229
1230
	        if (_xmlRevQueryCount > 0 && (_xmlRevQueryCount % 100) == 0) {
1231
	        	logMetacat.debug("ReplicationHandler.update - xml_rev query count: " + _xmlRevQueryCount +
1232
	        			", xml_rev avg query time: " + (_xmlRevQueryTime / _xmlRevQueryCount));
1233
	        }
1234 4080 daigle
1235
		}// for update docs
1236
1237
	}
1238 2608 tao
1239
   /*
1240 4080 daigle
	 * This method will handle doc in xml_documents table.
1241
	 */
1242 2608 tao
   private void handleDocInXMLDocuments(String docid, int rev, String remoteServer, boolean dataFile)
1243 5014 daigle
                                        throws HandlerException
1244 2608 tao
   {
1245
       // compare the update rev and local rev to see what need happen
1246
       int localrev = -1;
1247
       String action = null;
1248
       boolean flag = false;
1249
       try
1250
       {
1251 5175 daigle
    	 long docQueryStartTime = System.currentTimeMillis();
1252 2641 tao
         localrev = DBUtil.getLatestRevisionInDocumentTable(docid);
1253 5175 daigle
         long docQueryEndTime = System.currentTimeMillis();
1254
         _xmlDocQueryTime += (docQueryEndTime - docQueryStartTime);
1255
         _xmlDocQueryCount++;
1256 2608 tao
       }
1257
       catch (SQLException e)
1258
       {
1259 5014 daigle
    	 logMetacat.error("ReplicationHandler.handleDocInXMLDocuments - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1260
         logReplication.error("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " could not "+
1261 2663 sgarg
                                " be found because " + e.getMessage());
1262 5014 daigle
         logReplication.error("ReplicationHandler.handleDocInXMLDocuments - " + DOCERRORNUMBER+"Docid "+ docid + " could not be "+
1263 2608 tao
                 "written because error happend to find it's local revision");
1264 2739 tao
         DOCERRORNUMBER++;
1265 5014 daigle
         throw new HandlerException ("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " could not "+
1266
                 " be found: " + e.getMessage());
1267 2608 tao
       }
1268 5014 daigle
       logReplication.info("ReplicationHandler.handleDocInXMLDocuments - Local rev for docid "+ docid + " is "+
1269 2663 sgarg
                               localrev);
1270 2608 tao
1271
       //check the revs for an update because this document is in the
1272
       //local DB, it might be out of date.
1273
       if (localrev == -1)
1274
       {
1275 2713 tao
          // check if the revision is in the revision table
1276 5014 daigle
    	   Vector<Integer> localRevVector = null;
1277
    	 try {
1278 5175 daigle
        	 long revQueryStartTime = System.currentTimeMillis();
1279 5014 daigle
    		 localRevVector = DBUtil.getRevListFromRevisionTable(docid);
1280 5175 daigle
             long revQueryEndTime = System.currentTimeMillis();
1281
             _xmlRevQueryTime += (revQueryEndTime - revQueryStartTime);
1282
             _xmlRevQueryCount++;
1283 5014 daigle
    	 } catch (SQLException sqle) {
1284
    		 throw new HandlerException("ReplicationHandler.handleDocInXMLDocuments - SQL error "
1285
    				 + " when getting rev list for docid: " + docid + " : " + sqle.getMessage());
1286
    	 }
1287 2713 tao
         if (localRevVector != null && localRevVector.contains(new Integer(rev)))
1288
         {
1289
             // this version was deleted, so don't need replicate
1290
             flag = false;
1291
         }
1292
         else
1293
         {
1294
           //insert this document as new because it is not in the local DB
1295
           action = "INSERT";
1296
           flag = true;
1297
         }
1298 2608 tao
       }
1299
       else
1300
       {
1301
         if(localrev == rev)
1302
         {
1303
           // Local meatacat has the same rev to remote host, don't need
1304
           // update and flag set false
1305
           flag = false;
1306
         }
1307
         else if(localrev < rev)
1308
         {
1309
           //this document needs to be updated so send an read request
1310
           action = "UPDATE";
1311
           flag = true;
1312
         }
1313
       }
1314 2641 tao
1315 5014 daigle
       String accNumber = null;
1316
       try {
1317
    	   accNumber = docid + PropertyService.getProperty("document.accNumSeparator") + rev;
1318
       } catch (PropertyNotFoundException pnfe) {
1319
    	   throw new HandlerException("ReplicationHandler.handleDocInXMLDocuments - error getting "
1320
    			   + "account number separator : " + pnfe.getMessage());
1321
       }
1322 2608 tao
       // this is non-data file
1323
       if(flag && !dataFile)
1324
       {
1325
         try
1326
         {
1327 2641 tao
           handleSingleXMLDocument(remoteServer, action, accNumber, DocumentImpl.DOCUMENTTABLE);
1328 2608 tao
         }
1329 5014 daigle
         catch(HandlerException he)
1330 2608 tao
         {
1331
           // skip this document
1332 5014 daigle
           throw he;
1333 2608 tao
         }
1334
       }//if for non-data file
1335
1336
        // this is for data file
1337
       if(flag && dataFile)
1338
       {
1339
         try
1340
         {
1341 2641 tao
           handleSingleDataFile(remoteServer, action, accNumber, DocumentImpl.DOCUMENTTABLE);
1342 2608 tao
         }
1343 5014 daigle
         catch(HandlerException he)
1344 2608 tao
         {
1345 5175 daigle
           // skip this data file
1346 5014 daigle
           throw he;
1347 2608 tao
         }
1348
1349 5175 daigle
       }//for data file
1350 2608 tao
   }
1351
1352
   /*
1353
    * This method will handle doc in xml_documents table.
1354
    */
1355
   private void handleDocInXMLRevisions(String docid, int rev, String remoteServer, boolean dataFile)
1356 5014 daigle
                                        throws HandlerException
1357 2608 tao
   {
1358
       // compare the update rev and local rev to see what need happen
1359 5014 daigle
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - In handle repliation revsion table");
1360
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - the docid is "+ docid);
1361
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - The rev is "+rev);
1362
       Vector<Integer> localrev = null;
1363 2608 tao
       String action = "INSERT";
1364
       boolean flag = false;
1365
       try
1366
       {
1367 5175 daigle
      	 long revQueryStartTime = System.currentTimeMillis();
1368 2608 tao
         localrev = DBUtil.getRevListFromRevisionTable(docid);
1369 5175 daigle
         long revQueryEndTime = System.currentTimeMillis();
1370
         _xmlRevQueryTime += (revQueryEndTime - revQueryStartTime);
1371
         _xmlRevQueryCount++;
1372 2608 tao
       }
1373 5014 daigle
       catch (SQLException sqle)
1374 2608 tao
       {
1375 5014 daigle
    	 logMetacat.error("ReplicationHandler.handleDocInXMLDocuments - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1376
         logReplication.error("ReplicationHandler.handleDocInXMLRevisions - Local rev for docid "+ docid + " could not "+
1377
                                " be found because " + sqle.getMessage());
1378 2739 tao
         REVERRORNUMBER++;
1379 5014 daigle
         throw new HandlerException ("ReplicationHandler.handleDocInXMLRevisions - SQL exception getting rev list: "
1380
        		 + sqle.getMessage());
1381 2608 tao
       }
1382 5014 daigle
       logReplication.info("ReplicationHandler.handleDocInXMLRevisions - rev list in xml_revision table for docid "+ docid + " is "+
1383 2663 sgarg
                               localrev.toString());
1384 2608 tao
1385
       // if the rev is not in the xml_revision, we need insert it
1386
       if (!localrev.contains(new Integer(rev)))
1387
       {
1388
           flag = true;
1389
       }
1390 2641 tao
1391 5014 daigle
       String accNumber = null;
1392
       try {
1393
    	   accNumber = docid + PropertyService.getProperty("document.accNumSeparator") + rev;
1394
       } catch (PropertyNotFoundException pnfe) {
1395
    	   throw new HandlerException("ReplicationHandler.handleDocInXMLRevisions - error getting "
1396
    			   + "account number separator : " + pnfe.getMessage());
1397
       }
1398 2608 tao
       // this is non-data file
1399
       if(flag && !dataFile)
1400
       {
1401
         try
1402
         {
1403 2641 tao
1404
           handleSingleXMLDocument(remoteServer, action, accNumber, DocumentImpl.REVISIONTABLE);
1405 2608 tao
         }
1406 5014 daigle
         catch(HandlerException he)
1407 2608 tao
         {
1408
           // skip this document
1409 5014 daigle
           throw he;
1410 2608 tao
         }
1411
       }//if for non-data file
1412
1413
        // this is for data file
1414
       if(flag && dataFile)
1415
       {
1416
         try
1417
         {
1418 2641 tao
           handleSingleDataFile(remoteServer, action, accNumber, DocumentImpl.REVISIONTABLE);
1419 2608 tao
         }
1420 5014 daigle
         catch(HandlerException he)
1421 2608 tao
         {
1422 5175 daigle
           // skip this data file
1423 5014 daigle
           throw he;
1424 2608 tao
         }
1425
1426 5175 daigle
       }//for data file
1427 2608 tao
   }
1428 3234 tao
1429
   /*
1430
    * Return a ip address for given url
1431
    */
1432
   private String getIpFromURL(URL url)
1433
   {
1434
	   String ip = null;
1435
	   try
1436
	   {
1437
	      InetAddress address = InetAddress.getByName(url.getHost());
1438
	      ip = address.getHostAddress();
1439
	   }
1440
	   catch(UnknownHostException e)
1441
	   {
1442 5014 daigle
		   logMetacat.error("ReplicationHandler.getIpFromURL - " + ReplicationService.METACAT_REPL_ERROR_MSG);
1443
		   logReplication.error("ReplicationHandler.getIpFromURL - Error in get ip address for host: "
1444 3234 tao
                   +e.getMessage());
1445
	   }
1446
1447
	   return ip;
1448
   }
1449 2608 tao
1450 522 berkley
}