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