Project

General

Profile

1 522 berkley
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements replication for metacat
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Chad Berkley
7
 *    Release: @release@
8
 *
9
 *   '$Author$'
10
 *     '$Date$'
11
 * '$Revision$'
12 669 jones
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 522 berkley
 */
27
28
package edu.ucsb.nceas.metacat;
29
30
import java.util.*;
31
import java.io.*;
32
import java.sql.*;
33
import java.net.*;
34
import java.lang.*;
35
import java.text.*;
36
import javax.servlet.*;
37
import javax.servlet.http.*;
38 574 berkley
39 522 berkley
import org.xml.sax.*;
40
41 561 berkley
public class MetacatReplication extends HttpServlet implements Runnable
42 522 berkley
{
43
  private String deltaT;
44
  Timer replicationDaemon;
45 561 berkley
  private static MetaCatUtil util = new MetaCatUtil();
46
  private Vector fileLocks = new Vector();
47
  private Thread lockThread = null;
48 522 berkley
49
  /**
50
   * Initialize the servlet by creating appropriate database connections
51
   */
52
  public void init(ServletConfig config) throws ServletException
53
  {
54
    //initialize db connections to handle any update requests
55
    MetaCatUtil util = new MetaCatUtil();
56
    deltaT = util.getOption("deltaT");
57 583 berkley
    //the default deltaT can be set from metacat.properties
58
    //create a thread to do the delta-T check but don't execute it yet
59 522 berkley
    replicationDaemon = new Timer(true);
60
  }
61
62
  public void destroy()
63
  {
64
    replicationDaemon.cancel();
65
    System.out.println("Replication daemon cancelled.");
66
  }
67
68
  public void doGet (HttpServletRequest request, HttpServletResponse response)
69
                     throws ServletException, IOException
70
  {
71
    // Process the data and send back the response
72
    handleGetOrPost(request, response);
73
  }
74
75
  public void doPost(HttpServletRequest request, HttpServletResponse response)
76
                     throws ServletException, IOException
77
  {
78
    // Process the data and send back the response
79
    handleGetOrPost(request, response);
80
  }
81
82
  private void handleGetOrPost(HttpServletRequest request,
83
                               HttpServletResponse response)
84
                               throws ServletException, IOException
85
  {
86
    PrintWriter out = response.getWriter();
87
    Hashtable params = new Hashtable();
88
    Enumeration paramlist = request.getParameterNames();
89
90
    while (paramlist.hasMoreElements())
91
    {
92
      String name = (String)paramlist.nextElement();
93
      String[] value = request.getParameterValues(name);
94
      params.put(name, value);
95
    }
96
97 536 berkley
    if(params.containsKey("action"))
98 522 berkley
    {
99 536 berkley
      if(((String[])params.get("action"))[0].equals("stop"))
100 537 berkley
      { //stop the replication server
101 522 berkley
        replicationDaemon.cancel();
102 631 berkley
        replicationDaemon = new Timer(true);
103 522 berkley
        out.println("Replication Handler Stopped");
104 554 berkley
        System.out.println("Replication Handler Stopped");
105 584 berkley
        MetacatReplication.replLog("deltaT handler stopped");
106 522 berkley
      }
107 536 berkley
      else if(((String[])params.get("action"))[0].equals("start"))
108 537 berkley
      { //start the replication server
109 522 berkley
        int rate;
110
        if(params.containsKey("rate"))
111 549 berkley
        {
112 522 berkley
          rate = new Integer(
113
                 new String(((String[])params.get("rate"))[0])).intValue();
114 549 berkley
          if(rate < 30)
115
          {
116
            out.println("Replication deltaT rate cannot be less than 30!");
117 583 berkley
            //deltaT<30 is a timing mess!
118 549 berkley
            rate = 1000;
119
          }
120
        }
121 522 berkley
        else
122 549 berkley
        {
123 522 berkley
          rate = 1000;
124 549 berkley
        }
125
126 522 berkley
        out.println("New rate is: " + rate + " seconds.");
127
        replicationDaemon.cancel();
128
        replicationDaemon = new Timer(true);
129 523 berkley
        replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(out), 0,
130 522 berkley
                                              rate * 1000);
131 584 berkley
        MetacatReplication.replLog("deltaT handler started with rate=" +
132
                                    rate + " seconds");
133 522 berkley
        out.println("Replication Handler Started");
134 572 berkley
        System.out.println("Replication Handler Started");
135 522 berkley
      }
136 595 berkley
      else if(((String[])params.get("action"))[0].equals("getall"))
137
      { //updates this server exactly once
138
        replicationDaemon.schedule(new ReplicationHandler(out), 0);
139 631 berkley
        response.setContentType("text/html");
140 595 berkley
        out.println("<html><body>\"Get All\" Done</body></html>");
141
      }
142 572 berkley
      else if(((String[])params.get("action"))[0].equals("forcereplicate"))
143
      {
144 574 berkley
        handleForceReplicateRequest(out, params, response);
145 572 berkley
      }
146 536 berkley
      else if(((String[])params.get("action"))[0].equals("update"))
147 537 berkley
      { //request an update list from the server
148 583 berkley
        handleUpdateRequest(out, params, response);
149 536 berkley
      }
150 566 jones
      else if(((String[])params.get("action"))[0].equals("read"))
151 537 berkley
      { //request a specific document from the server
152
        //note that this could be replaced by a call to metacatServlet
153
        //handleGetDocumentAction().
154 536 berkley
        handleGetDocumentRequest(out, params, response);
155
      }
156 561 berkley
      else if(((String[])params.get("action"))[0].equals("getlock"))
157
      {
158
        handleGetLockRequest(out, params, response);
159
      }
160
      else if(((String[])params.get("action"))[0].equals("getdocumentinfo"))
161
      {
162
        handleGetDocumentInfoRequest(out, params, response);
163
      }
164 568 berkley
      else if(((String[])params.get("action"))[0].equals("gettime"))
165
      {
166
        handleGetTimeRequest(out, params, response);
167
      }
168 590 berkley
      else if(((String[])params.get("action"))[0].equals("getcatalog"))
169
      {
170
        handleGetCatalogRequest(out, params, response, true);
171
      }
172 595 berkley
      else if(((String[])params.get("action"))[0].equals("servercontrol"))
173
      {
174
        handleServerControlRequest(out, params, response);
175
      }
176
177 522 berkley
    }
178
  }
179
180 595 berkley
  /**
181
   * This method can add, delete and list the servers currently included in
182
   * xml_replication.
183
   * action           subaction            other needed params
184
   * ---------------------------------------------------------
185
   * servercontrol    add                  server
186
   * servercontrol    delete               server
187
   * servercontrol    list
188
   */
189
  private void handleServerControlRequest(PrintWriter out, Hashtable params,
190
                                          HttpServletResponse response)
191
  {
192
    String subaction = ((String[])params.get("subaction"))[0];
193 683 berkley
    Connection conn = null;
194 595 berkley
    try
195
    {
196 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
197
                                                "handleServerControlRequest");
198 667 berkley
      PreparedStatement pstmt = null;
199 595 berkley
      if(subaction.equals("add"))
200
      {
201 629 berkley
        String replicate = ((String[])params.get("replicate"))[0];
202 595 berkley
        String server = ((String[])params.get("server"))[0];
203
        pstmt = conn.prepareStatement("insert into xml_replication (server, " +
204 629 berkley
                "last_checked, replicate) values ('" + server + "', to_date(" +
205
                "'01/01/00', 'MM/DD/YY'), '" + replicate + "')");
206 595 berkley
        pstmt.execute();
207 631 berkley
        out.println("Server says: server " + server + " added");
208
        response.setContentType("text/html");
209
        out.println("<html><body><table border=\"1\">");
210
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
211
        out.println("<b>replicate</b></td></tr>");
212
        pstmt = conn.prepareStatement("select * from xml_replication");
213
        pstmt.execute();
214
        ResultSet rs = pstmt.getResultSet();
215
        boolean tablehasrows = rs.next();
216
        while(tablehasrows)
217
        {
218
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
219
          out.println(rs.getString(3) + "</td><td>");
220
          out.println(rs.getString(4) + "</td></tr>");
221
          tablehasrows = rs.next();
222
        }
223
        out.println("</table></body></html>");
224 595 berkley
      }
225
      else if(subaction.equals("delete"))
226
      {
227
        String server = ((String[])params.get("server"))[0];
228
        pstmt = conn.prepareStatement("delete from xml_replication where " +
229
                "server like '" + server + "'");
230
        pstmt.execute();
231 631 berkley
        out.println("Server says: server " + server + " deleted");
232
        response.setContentType("text/html");
233
        out.println("<html><body><table border=\"1\">");
234
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
235
        out.println("<b>replicate</b></td></tr>");
236
        pstmt = conn.prepareStatement("select * from xml_replication");
237
        pstmt.execute();
238
        ResultSet rs = pstmt.getResultSet();
239
        boolean tablehasrows = rs.next();
240
        while(tablehasrows)
241
        {
242
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
243
          out.println(rs.getString(3) + "</td><td>");
244
          out.println(rs.getString(4) + "</td></tr>");
245
          tablehasrows = rs.next();
246
        }
247
        out.println("</table></body></html>");
248 595 berkley
      }
249
      else if(subaction.equals("list"))
250
      {
251
        response.setContentType("text/html");
252
        out.println("<html><body><table border=\"1\">");
253 629 berkley
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
254
        out.println("<b>replicate</b></td></tr>");
255 595 berkley
        pstmt = conn.prepareStatement("select * from xml_replication");
256
        pstmt.execute();
257
        ResultSet rs = pstmt.getResultSet();
258
        boolean tablehasrows = rs.next();
259
        while(tablehasrows)
260
        {
261
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
262 629 berkley
          out.println(rs.getString(3) + "</td><td>");
263
          out.println(rs.getString(4) + "</td></tr>");
264 595 berkley
          tablehasrows = rs.next();
265
        }
266
        out.println("</table></body></html>");
267
      }
268 667 berkley
      pstmt.close();
269 595 berkley
      conn.close();
270
    }
271
    catch(Exception e)
272
    {
273 675 berkley
      System.out.println("error in " +
274
                         "MetacatReplication.handleServerControlRequest " +
275 595 berkley
                         e.getMessage());
276
      e.printStackTrace(System.out);
277
    }
278
  }
279
280 583 berkley
  /**
281
   * when a forcereplication request comes in, this method sends a read request
282
   * to the requesting server for the specified docid.
283
   */
284 574 berkley
  private void handleForceReplicateRequest(PrintWriter out, Hashtable params,
285
                                           HttpServletResponse response)
286
  {
287 583 berkley
    //System.out.println("in handleforcereplicaterequest");
288 574 berkley
    String server = ((String[])params.get("server"))[0];
289 629 berkley
    if(!(replToServer(server)))
290
    { //do not get the server's new document if we are not replicating from there
291
      return;
292
    }
293
294 583 berkley
    //the server that the request came from
295 574 berkley
    String docid = ((String[])params.get("docid"))[0];
296 583 berkley
    //the docid of the document to get
297 577 berkley
    String dbaction = "UPDATE";
298 583 berkley
    //default action is update
299 577 berkley
    boolean override = false;
300
    int serverCode = 1;
301
302 574 berkley
    try
303
    {
304 577 berkley
      if(params.containsKey("dbaction"))
305 583 berkley
      { //if the url contains a dbaction then the default action is overridden
306 577 berkley
        dbaction = ((String[])params.get("dbaction"))[0];
307
        serverCode = MetacatReplication.getServerCode(server);
308 583 berkley
        override = true; //we are now overriding the default action
309 577 berkley
      }
310 590 berkley
      MetaCatUtil.debugMessage("action in forcereplicate is: " + dbaction);
311
      MetaCatUtil.debugMessage("serverCode in forcereplicate is: " + serverCode);
312 584 berkley
      MetacatReplication.replLog("force replication request from " + server);
313 577 berkley
314 574 berkley
      int serverCheckCode = MetacatReplication.getServerCode(server);
315
      URL u = new URL("http://" + server + "?action=read&docid=" + docid);
316 590 berkley
      MetaCatUtil.debugMessage("sending message: " + u.toString());
317 574 berkley
      String xmldoc = MetacatReplication.getURLContent(u);
318 590 berkley
      MetaCatUtil.debugMessage("document: " + xmldoc);
319 583 berkley
      //get the document to write
320 574 berkley
      URL docinfourl = new URL("http://" + server +
321
                               "?action=getdocumentinfo&docid=" +
322
                               docid);
323 583 berkley
      //we need to get the document's info so we can set the correct user
324
      //and group once we get the document and write it to our DB
325 590 berkley
      MetaCatUtil.debugMessage("sending message: " + docinfourl.toString());
326 574 berkley
      String docInfoStr = MetacatReplication.getURLContent(docinfourl);
327 590 berkley
      MetaCatUtil.debugMessage("docInfo: " + docInfoStr);
328 574 berkley
      DocInfoHandler dih = new DocInfoHandler();
329 583 berkley
      //dih is the parser for the docinfo xml format
330 574 berkley
      XMLReader docinfoParser = ReplicationHandler.initParser(dih);
331
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
332
      Hashtable docinfoHash = dih.getDocInfo();
333
      String user = (String)docinfoHash.get("user_owner");
334
      String group = new String(user);
335 583 berkley
      //right now the user and group are the same.
336 683 berkley
      Connection conn = null;
337
338
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
339
                                                "handleForceReplicateRequest");
340
341 577 berkley
      DocumentImpl.write(conn, new StringReader(xmldoc), null, dbaction, docid,
342
                         user, group, serverCode, override);
343 584 berkley
      MetacatReplication.replLog("document " + docid + " added to DB with " +
344
                                 "action " + dbaction);
345 574 berkley
      conn.close();
346
    }
347
    catch(Exception e)
348
    {
349
      System.out.println("error in metacatReplication.handleForceReplicate" +
350
                         "Request: " + e.getMessage());
351
    }
352
  }
353
354 561 berkley
  /**
355
   * Grants or denies a lock to a requesting host.
356
   * The servlet parameters of interrest are:
357
   * docid: the docid of the file the lock is being requested for
358
   * currentdate: the timestamp of the document on the remote server
359
   *
360
   */
361
  private void handleGetLockRequest(PrintWriter out, Hashtable params,
362
                                    HttpServletResponse response)
363
  {
364 683 berkley
    Connection conn = null;
365 561 berkley
    try
366
    {
367 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
368
                                                "handleGetLockRequest");
369 561 berkley
      String docid = ((String[])params.get("docid"))[0];
370 580 berkley
      String remoteRev = ((String[])params.get("updaterev"))[0];
371 561 berkley
      DocumentImpl requestDoc = new DocumentImpl(conn, docid);
372 584 berkley
      MetacatReplication.replLog("lock request for " + docid);
373 580 berkley
      int localRevInt = requestDoc.getRev();
374
      int remoteRevInt = Integer.parseInt(remoteRev);
375
376
      if(remoteRevInt >= localRevInt)
377 561 berkley
      {
378
        if(!fileLocks.contains(docid))
379 580 berkley
        { //grant the lock if it is not already locked
380 561 berkley
          fileLocks.add(0, docid); //insert at the beginning of the queue Vector
381
          //send a message back to the the remote host authorizing the insert
382
          out.println("<lockgranted><docid>" +docid+ "</docid></lockgranted>");
383
          lockThread = new Thread(this);
384
          lockThread.setPriority(Thread.MIN_PRIORITY);
385
          lockThread.start();
386 595 berkley
          System.out.println("lock granted for " + docid);
387 584 berkley
          MetacatReplication.replLog("lock granted for " + docid);
388 561 berkley
        }
389
        else
390
        { //deny the lock
391
          out.println("<filelocked><docid>" + docid + "</docid></filelocked>");
392 584 berkley
          MetacatReplication.replLog("lock denied for " + docid +
393
                                     "reason: file already locked");
394 561 berkley
        }
395
      }
396
      else
397
      {//deny the lock.
398
        out.println("<outdatedfile><docid>" + docid + "</docid></filelocked>");
399 584 berkley
        MetacatReplication.replLog("lock denied for " + docid +
400
                                   "reason: client has outdated file");
401 561 berkley
      }
402
      conn.close();
403
    }
404
    catch(Exception e)
405
    {
406 675 berkley
      System.out.println("error requesting file lock from MetacatReplication." +
407
                         "handleGetLockRequest: " + e.getMessage());
408 568 berkley
      e.printStackTrace(System.out);
409 561 berkley
    }
410
  }
411
412
  /**
413
   * Sends all of the xml_documents information encoded in xml to a requestor
414 583 berkley
   * the format is:
415
   * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
416
   *                         user_updated, public_access, rev)
417
   * all of the subelements of document info are #PCDATA
418 561 berkley
   */
419
  private void handleGetDocumentInfoRequest(PrintWriter out, Hashtable params,
420
                                        HttpServletResponse response)
421
  {
422
    String docid = ((String[])(params.get("docid")))[0];
423
    StringBuffer sb = new StringBuffer();
424 683 berkley
    Connection conn = null;
425 561 berkley
    try
426
    {
427 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
428
                                                "handleGetDocumentInfoRequest");
429 561 berkley
      DocumentImpl doc = new DocumentImpl(conn, docid);
430
      sb.append("<documentinfo><docid>").append(docid);
431
      sb.append("</docid><docname>").append(doc.getDocname());
432
      sb.append("</docname><doctype>").append(doc.getDoctype());
433
      sb.append("</doctype><doctitle>").append(doc.getDocTitle());
434
      sb.append("</doctitle><user_owner>").append(doc.getUserowner());
435
      sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
436
      sb.append("</user_updated><public_access>").append(doc.getPublicaccess());
437 583 berkley
      sb.append("</public_access><rev>").append(doc.getRev());
438
      sb.append("</rev></documentinfo>");
439 561 berkley
      response.setContentType("text/xml");
440
      out.println(sb.toString());
441
      conn.close();
442
    }
443
    catch (Exception e)
444
    {
445 675 berkley
      System.out.println("error in " +
446
                         "metacatReplication.handlegetdocumentinforequest: " +
447
                          e.getMessage());
448 561 berkley
    }
449
450
  }
451
452
  /**
453
   * Sends a document to a remote host
454
   */
455 534 berkley
  private void handleGetDocumentRequest(PrintWriter out, Hashtable params,
456 561 berkley
                                        HttpServletResponse response)
457 534 berkley
  {
458 683 berkley
    Connection conn = null;
459 534 berkley
    try
460
    {
461 536 berkley
      String docid = ((String[])(params.get("docid")))[0];
462 683 berkley
      System.out.println("incoming get request for document: " + docid);
463
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
464
                                                "handleGetDocumentRequest");
465 534 berkley
      DocumentImpl di = new DocumentImpl(conn, docid);
466
      response.setContentType("text/xml");
467 537 berkley
      out.print(di.toString());
468 534 berkley
      conn.close();
469 584 berkley
      MetacatReplication.replLog("document " + docid + " sent");
470 595 berkley
      System.out.println("document " + docid + " sent");
471 534 berkley
    }
472
    catch(Exception e)
473
    {
474 675 berkley
      System.out.println("error getting document from MetacatReplication." +
475
                         "handlGetDocumentRequest " + e.getMessage());
476 683 berkley
      e.printStackTrace(System.out);
477 534 berkley
    }
478
479
  }
480 573 berkley
481
  /**
482 583 berkley
   * Sends a list of all of the documents on this sever along with their
483
   * revision numbers.
484
   * The format is:
485
   * <!ELEMENT replication (server, updates)>
486
   * <!ELEMENT server (#PCDATA)>
487
   * <!ELEMENT updates ((updatedDocument | deleteDocument)*)>
488
   * <!ELEMENT updatedDocument (docid, rev)>
489
   * <!ELEMENT deletedDocument (docid, rev)>
490
   * <!ELEMENT docid (#PCDATA)>
491
   * <!ELEMENT rev (#PCDATA)>
492
   * note that the rev in deletedDocument is always empty.  I just left
493
   * it in there to make the parser implementation easier.
494 573 berkley
   */
495
  private void handleUpdateRequest(PrintWriter out, Hashtable params,
496 577 berkley
                                    HttpServletResponse response)
497
  {
498 683 berkley
    Connection conn = null;
499 577 berkley
    try
500
    {
501 595 berkley
      System.out.println("received update request");
502 577 berkley
      StringBuffer docsql = new StringBuffer();
503
      StringBuffer doclist = new StringBuffer();
504 625 berkley
      Vector packageFiles = new Vector();
505 577 berkley
506
      //get all docs that reside on this server
507
      doclist.append("<?xml version=\"1.0\"?><replication>");
508
      doclist.append("<server>").append(util.getOption("server"));
509
      doclist.append(util.getOption("replicationpath"));
510
      doclist.append("</server><updates>");
511
512 625 berkley
      docsql.append("select docid, rev, doctype from xml_documents where ");
513 577 berkley
      docsql.append("server_location = 1");
514
515
      //get any deleted documents
516
      StringBuffer delsql = new StringBuffer();
517
      delsql.append("select distinct docid from ");
518
      delsql.append("xml_revisions where docid not in (select docid from ");
519
      delsql.append("xml_documents) and server_location = 1");
520
521 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
522
                                                "handleUpdateRequest");
523 577 berkley
      PreparedStatement pstmt = conn.prepareStatement(docsql.toString());
524
      pstmt.execute();
525
      ResultSet rs = pstmt.getResultSet();
526
      boolean tablehasrows = rs.next();
527
      while(tablehasrows)
528
      {
529 683 berkley
        String recordDoctype = rs.getString(3);
530
        if(!recordDoctype.equals("BIN"))
531
        { //don't replicate data files
532
          if(!recordDoctype.equals(util.getOption("packagedoctype")))
533
          { //if this is a package file, put it at the end
534
            //because if a package file is read before all of the files it
535
            //refers to are loaded then there is an error
536
            doclist.append("<updatedDocument>");
537
            doclist.append("<docid>").append(rs.getString(1));
538
            doclist.append("</docid><rev>").append(rs.getInt(2));
539
            doclist.append("</rev>");
540
            doclist.append("</updatedDocument>");
541
          }
542
          else
543
          { //the package files are saved to be put into the xml later.
544
            Vector v = new Vector();
545
            v.add(new String(rs.getString(1)));
546
            v.add(new Integer(rs.getInt(2)));
547
            packageFiles.add(new Vector(v));
548
          }
549 625 berkley
        }
550 577 berkley
        tablehasrows = rs.next();
551
      }
552
553
      pstmt = conn.prepareStatement(delsql.toString());
554
      pstmt.execute();
555
      rs = pstmt.getResultSet();
556
      tablehasrows = rs.next();
557
      while(tablehasrows)
558
      { //handle the deleted documents
559
        doclist.append("<deletedDocument><docid>").append(rs.getString(1));
560
        doclist.append("</docid><rev></rev></deletedDocument>");
561 583 berkley
        //note that rev is always empty for deleted docs
562 577 berkley
        tablehasrows = rs.next();
563
      }
564
565 625 berkley
      //now we can put the package files into the xml results
566
      for(int i=0; i<packageFiles.size(); i++)
567
      {
568
        Vector v = (Vector)packageFiles.elementAt(i);
569
        doclist.append("<updatedDocument>");
570
        doclist.append("<docid>").append((String)v.elementAt(0));
571
        doclist.append("</docid><rev>");
572
        doclist.append(((Integer)v.elementAt(1)).intValue());
573
        doclist.append("</rev>");
574
        doclist.append("</updatedDocument>");
575
      }
576
577 577 berkley
      doclist.append("</updates></replication>");
578 590 berkley
      MetaCatUtil.debugMessage("doclist: " + doclist.toString());
579 667 berkley
      pstmt.close();
580 577 berkley
      conn.close();
581
      response.setContentType("text/xml");
582
      out.println(doclist.toString());
583 625 berkley
      System.out.println("doclist: " + doclist.toString());
584 595 berkley
      System.out.println("update request handled");
585 577 berkley
    }
586
    catch(Exception e)
587
    {
588 675 berkley
      System.out.println("error in MetacatReplication.handleupdaterequest: " +
589
                          e.getMessage());
590 577 berkley
      e.printStackTrace(System.out);
591
    }
592
593
  }
594
595
  /**
596 590 berkley
   * Returns the xml_catalog table encoded in xml
597
   */
598
  public static String getCatalogXML()
599
  {
600
    return handleGetCatalogRequest(null, null, null, false);
601
  }
602
603
  /**
604
   * Sends the contents of the xml_catalog table encoded in xml
605
   * The xml format is:
606
   * <!ELEMENT xml_catalog (row*)>
607
   * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
608
   *                system_id)>
609
   * All of the sub elements of row are #PCDATA
610
611
   * If printFlag == false then do not print to out.
612
   */
613
  private static String handleGetCatalogRequest(PrintWriter out,
614
                                                Hashtable params,
615
                                                HttpServletResponse response,
616
                                                boolean printFlag)
617
  {
618 667 berkley
    Connection conn = null;
619
    PreparedStatement pstmt = null;
620 590 berkley
    try
621 683 berkley
    {
622
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
623
                                                "handleGetCatalogRequest");
624 667 berkley
      pstmt = conn.prepareStatement("select entry_type, " +
625 590 berkley
                              "source_doctype, target_doctype, public_id, " +
626
                              "system_id from xml_catalog");
627
      pstmt.execute();
628
      ResultSet rs = pstmt.getResultSet();
629
      boolean tablehasrows = rs.next();
630
      StringBuffer sb = new StringBuffer();
631
      sb.append("<?xml version=\"1.0\"?><xml_catalog>");
632
      while(tablehasrows)
633
      {
634
        sb.append("<row><entry_type>").append(rs.getString(1));
635
        sb.append("</entry_type><source_doctype>").append(rs.getString(2));
636
        sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
637
        sb.append("</target_doctype><public_id>").append(rs.getString(4));
638
        sb.append("</public_id><system_id>").append(rs.getString(5));
639
        sb.append("</system_id></row>");
640
641
        tablehasrows = rs.next();
642
      }
643
      sb.append("</xml_catalog>");
644
      conn.close();
645
      if(printFlag)
646
      {
647
        response.setContentType("text/xml");
648
        out.println(sb.toString());
649
      }
650 667 berkley
      pstmt.close();
651 590 berkley
      return sb.toString();
652
    }
653
    catch(Exception e)
654
    {
655 667 berkley
      try
656
      {
657
        pstmt.close();
658
        conn.close();
659
      }
660
      catch(Exception ee)
661
      {}
662 675 berkley
      System.out.println("error in MetacatReplication.handleGetCatalogRequest:"+
663
                          e.getMessage());
664 590 berkley
      e.printStackTrace(System.out);
665
    }
666
    return null;
667
  }
668
669
  /**
670 568 berkley
   * Sends the current system date to the remote server.  Using this action
671
   * for replication gets rid of any problems with syncronizing clocks
672
   * because a time specific to a document is always kept on its home server.
673
   */
674
  private void handleGetTimeRequest(PrintWriter out, Hashtable params,
675
                                    HttpServletResponse response)
676
  {
677
    SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
678
    java.util.Date localtime = new java.util.Date();
679
    String dateString = formatter.format(localtime);
680
    response.setContentType("text/xml");
681
682
    out.println("<timestamp>" + dateString + "</timestamp>");
683
  }
684
685
  /**
686 583 berkley
   * this method handles the timeout for a file lock.  when a lock is
687
   * granted it is granted for 30 seconds.  When this thread runs out
688
   * it deletes the docid from the queue, thus eliminating the lock.
689 561 berkley
   */
690
  public void run()
691
  {
692
    try
693
    {
694 590 berkley
      MetaCatUtil.debugMessage("thread started for docid: " +
695
                               (String)fileLocks.elementAt(0));
696 595 berkley
      System.out.println("thread started for docid: " +
697
                               (String)fileLocks.elementAt(0));
698 561 berkley
      Thread.sleep(30000); //the lock will expire in 30 seconds
699 590 berkley
      MetaCatUtil.debugMessage("thread for docid: " +
700 595 berkley
                             (String)fileLocks.elementAt(fileLocks.size() - 1) +
701
                              " exiting.");
702
      System.out.println("thread for docid: " +
703
                         (String)fileLocks.elementAt(fileLocks.size() - 1) +
704
                         " exiting.");
705 561 berkley
      fileLocks.remove(fileLocks.size() - 1);
706 568 berkley
      //fileLocks is treated as a FIFO queue.  If there are more than one lock
707 561 berkley
      //in the vector, the first one inserted will be removed.
708
    }
709
    catch(Exception e)
710
    {
711 675 berkley
      System.out.println("error in file lock thread from MetacatReplication." +
712
                         "run: " + e.getMessage());
713 561 berkley
    }
714
  }
715
716
  /**
717
   * Returns the name of a server given a serverCode
718
   * @param serverCode the serverid of the server
719
   * @return the servername or null if the specified serverCode does not
720
   *         exist.
721
   */
722
  public static String getServer(int serverCode)
723
  {
724 569 berkley
    //System.out.println("serverid: " + serverCode);
725 683 berkley
    Connection conn = null;
726 561 berkley
    try
727
    {
728 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
729
                                                "getServer");
730 569 berkley
      String sql = new String("select server from " +
731 561 berkley
                              "xml_replication where serverid = " +
732
                              serverCode);
733 569 berkley
      PreparedStatement pstmt = conn.prepareStatement(sql);
734
      //System.out.println("getserver sql: " + sql);
735 561 berkley
      pstmt.execute();
736
      ResultSet rs = pstmt.getResultSet();
737
      boolean tablehasrows = rs.next();
738
      if(tablehasrows)
739
      {
740 569 berkley
        //System.out.println("server: " + rs.getString(1));
741 561 berkley
        return rs.getString(1);
742
      }
743 667 berkley
      pstmt.close();
744 561 berkley
      conn.close();
745
    }
746
    catch(Exception e)
747
    {
748
      System.out.println("Error in MetacatReplication.getServer: " +
749
                          e.getMessage());
750
    }
751
    return null;
752
      //return null if the server does not exist
753
  }
754 569 berkley
755
  /**
756
   * Returns a server code given a server name
757
   * @param server the name of the server
758
   * @return integer > 0 representing the code of the server, 0 if the server
759
   *  does not exist.
760
   */
761
  public static int getServerCode(String server) throws Exception
762
  {
763 667 berkley
    Connection conn = null;
764
    PreparedStatement pstmt = null;
765 569 berkley
    try
766
    {
767 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication.getServerCode");
768 667 berkley
      pstmt = conn.prepareStatement("select serverid from " +
769 569 berkley
                                         "xml_replication where server " +
770
                                         "like '" + server + "'");
771
      pstmt.execute();
772
      ResultSet rs = pstmt.getResultSet();
773
      boolean tablehasrows = rs.next();
774
      int serverCode = 0;
775
      if(tablehasrows)
776 667 berkley
      {
777
        int ret = rs.getInt(1);
778
        pstmt.close();
779
        conn.close();
780
        return ret;
781 569 berkley
      }
782
      else
783
      {
784 667 berkley
        pstmt.close();
785
        conn.close();
786 569 berkley
        return 0;
787
      }
788
    }
789
    catch(Exception e)
790
    {
791
      throw e;
792
    }
793 667 berkley
    finally
794
    {
795
       try
796
       {
797
         pstmt.close();
798
         conn.close();
799
       }
800
       catch(Exception ee) {}
801
    }
802 569 berkley
  }
803
804
  /**
805
   * This method returns the content of a url
806
   * @param u the url to return the content from
807
   * @return a string representing the content of the url
808
   * @throws java.io.IOException
809
   */
810
  public static String getURLContent(URL u) throws java.io.IOException
811
  {
812
    //System.out.println("url: " + u.toString());
813
    char istreamChar;
814
    int istreamInt;
815
    InputStreamReader istream = new InputStreamReader(u.openStream());
816
    StringBuffer serverResponse = new StringBuffer();
817
    while((istreamInt = istream.read()) != -1)
818
    {
819
      istreamChar = (char)istreamInt;
820
      serverResponse.append(istreamChar);
821
    }
822
823
    return serverResponse.toString();
824
  }
825 584 berkley
826
  /**
827
   * Method for writing replication messages to a log file specified in
828
   * metacat.properties
829
   */
830
  public static void replLog(String message)
831
  {
832
    try
833
    {
834
      FileOutputStream fos = new FileOutputStream(
835
                                 util.getOption("replicationlog"), true);
836
      PrintWriter pw = new PrintWriter(fos);
837
      SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
838
      java.util.Date localtime = new java.util.Date();
839
      String dateString = formatter.format(localtime);
840
      dateString += " :: " + message;
841
      //time stamp each entry
842
      pw.println(dateString);
843
      pw.flush();
844
    }
845
    catch(Exception e)
846
    {
847 675 berkley
      System.out.println("error writing to replication log from " +
848
                         "MetacatReplication.replLog: " + e.getMessage());
849 595 berkley
      //e.printStackTrace(System.out);
850 584 berkley
    }
851
  }
852 629 berkley
853
  /**
854
   * Returns true if the replicate field for server in xml_replication is 1.
855
   * Returns false otherwise
856
   */
857
  public static boolean replToServer(String server)
858
  {
859 667 berkley
    Connection conn = null;
860
    PreparedStatement pstmt = null;
861 629 berkley
    try
862
    {
863 683 berkley
      conn = MetacatReplication.getDBConnection("MetacatReplication.replToServer");
864 667 berkley
      pstmt = conn.prepareStatement("select replicate from " +
865
                                    "xml_replication where server like '" +
866
                                     server + "'");
867 629 berkley
      pstmt.execute();
868
      ResultSet rs = pstmt.getResultSet();
869
      boolean tablehasrows = rs.next();
870
      if(tablehasrows)
871
      {
872
        int i = rs.getInt(1);
873
        if(i == 1)
874
        {
875 667 berkley
          pstmt.close();
876
          conn.close();
877 629 berkley
          return true;
878
        }
879
        else
880
        {
881 667 berkley
          pstmt.close();
882
          conn.close();
883 629 berkley
          return false;
884
        }
885
      }
886
    }
887
    catch(Exception e)
888
    {
889 675 berkley
      System.out.println("error in MetacatReplication.replToServer: " +
890
                         e.getMessage());
891 629 berkley
    }
892 667 berkley
    finally
893
    {
894
      try
895
      {
896
        pstmt.close();
897
        conn.close();
898
      }
899
      catch(Exception ee)
900
      {}
901
    }
902 629 berkley
    return false;
903
    //the default if this server does not exist is to not replicate to it.
904
  }
905 683 berkley
906
  /**
907
   * A method for redundantly trying to connect.  this method will attempt to
908
   * connect to the DB 3 times before failing.
909
   * @param methodname the methodname from which this method is called.
910
   */
911
  public static Connection getDBConnection(String methodname) throws Exception
912
  {
913
    Connection conn = null;
914
    try
915
    {
916
      try
917
      { //this connection is prone to error for some reason so we
918
        //try to connect it three times before quiting.
919
        conn = util.openDBConnection();
920
        return conn;
921
      }
922
      catch(SQLException sqle)
923
      {
924
        try
925
        {
926
          conn = util.openDBConnection();
927
          return conn;
928
        }
929
        catch(SQLException sqlee)
930
        {
931
          try
932
          {
933
            conn = util.openDBConnection();
934
            return conn;
935
          }
936
          catch(SQLException sqleee)
937
          {
938
            System.out.println("error getting db connection in " +
939
                               methodname +  ": " +
940
                               sqleee.getMessage());
941
            return conn;
942
          }
943
        }
944
      }
945
    }
946
    catch(Exception e)
947
    {
948
      throw new java.lang.Exception("error in " + methodname + ": " +
949
                          e.getMessage());
950
    }
951
  }
952 522 berkley
}