Project

General

Profile

1
/**
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: bojilova $'
10
 *     '$Date: 2001-09-24 10:55:00 -0700 (Mon, 24 Sep 2001) $'
11
 * '$Revision: 840 $'
12
 *
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
 */
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

    
39
import org.xml.sax.*;
40

    
41
public class MetacatReplication extends HttpServlet implements Runnable
42
{  
43
  private String deltaT;
44
  Timer replicationDaemon;
45
  private static MetaCatUtil util = new MetaCatUtil();
46
  private Vector fileLocks = new Vector();
47
  private Thread lockThread = null;
48
  
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
    //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
    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
// NOT NEEDED - doesn't provide enough security because of possible IP spoofing
91
// REPLACED with running replication comminications over HTTPS
92
//    String requestingServerIP = request.getRemoteAddr();
93
//    InetAddress iaddr = InetAddress.getByName(requestingServerIP);
94
//    String requestingServer = iaddr.getHostName();
95
    
96
    while (paramlist.hasMoreElements()) {
97
      String name = (String)paramlist.nextElement();
98
      String[] value = request.getParameterValues(name);
99
      params.put(name, value);  
100
    }
101
    
102
    String action = ((String[])params.get("action"))[0];
103
    String server = null;
104
    
105
    try {
106
      // check if the server is included in the list of replicated servers
107
      if ( !action.equals("servercontrol") && 
108
           !action.equals("stop") &&
109
           !action.equals("start") &&
110
           !action.equals("getall") ) {
111

    
112
        server = ((String[])params.get("server"))[0];
113
        if ( getServerCode(server) == 0 ) {
114
          System.out.println("Action \"" + action + 
115
                             "\" rejected for server: " + server);
116
          return;
117
        } else {
118
          System.out.println("Action \"" + action + 
119
                             "\" accepted for server: " + server);
120
        }
121
      }
122
    } catch (Exception e) {
123
      System.out.println("Error in MetacatReplication.handleGetOrPost: " +
124
                         e.getMessage() );
125
      return;
126
    }
127
    
128
    if ( action.equals("stop") ) {
129
      //stop the replication server
130
      replicationDaemon.cancel();
131
      replicationDaemon = new Timer(true);
132
      out.println("Replication Handler Stopped");
133
      MetacatReplication.replLog("deltaT handler stopped");
134

    
135
    } else if ( action.equals("start") ) {
136
      //start the replication server
137
      int rate;
138
      if ( params.containsKey("rate") ) {
139
        rate = new Integer(
140
               new String(((String[])params.get("rate"))[0])).intValue();
141
        if(rate < 30) {
142
            out.println("Replication deltaT rate cannot be less than 30!");
143
            //deltaT<30 is a timing mess!
144
            rate = 1000;
145
        }
146
      } else {
147
        rate = 1000;
148
      }
149
      out.println("New rate is: " + rate + " seconds.");
150
      replicationDaemon.cancel();
151
      replicationDaemon = new Timer(true);
152
      replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(out), 0, 
153
                                            rate * 1000);
154
      out.println("Replication Handler Started");
155
      MetacatReplication.replLog("deltaT handler started with rate=" + 
156
                                    rate + " seconds");
157

    
158
    } else if ( action.equals("getall") ) {
159
      //updates this server exactly once
160
      replicationDaemon.schedule(new ReplicationHandler(out), 0);
161
      response.setContentType("text/html");
162
      out.println("<html><body>\"Get All\" Done</body></html>");
163

    
164
    } else if ( action.equals("forcereplicate") ) {
165
      handleForceReplicateRequest(out, params, response);
166

    
167
    } else if ( action.equals("update") ) {
168
      //request an update list from the server
169
      handleUpdateRequest(out, params, response);
170

    
171
    } else if ( action.equals("read") ) {
172
      //request a specific document from the server
173
      //note that this could be replaced by a call to metacatServlet
174
      //handleGetDocumentAction().
175
      handleGetDocumentRequest(out, params, response);
176

    
177
    } else if ( action.equals("getlock") ) {
178
      handleGetLockRequest(out, params, response);
179

    
180
    } else if ( action.equals("getdocumentinfo") ) {
181
      handleGetDocumentInfoRequest(out, params, response);
182

    
183
    } else if ( action.equals("gettime") ) {
184
      handleGetTimeRequest(out, params, response);
185

    
186
    } else if ( action.equals("getcatalog") ) {
187
      handleGetCatalogRequest(out, params, response, true);
188

    
189
    } else if ( action.equals("servercontrol") ) {
190
      handleServerControlRequest(out, params, response);
191
    }
192
    
193
    out.close();
194
  }
195
  
196
  /** 
197
   * This method can add, delete and list the servers currently included in
198
   * xml_replication.
199
   * action           subaction            other needed params
200
   * ---------------------------------------------------------
201
   * servercontrol    add                  server
202
   * servercontrol    delete               server
203
   * servercontrol    list                 
204
   */
205
  private void handleServerControlRequest(PrintWriter out, Hashtable params,
206
                                          HttpServletResponse response)
207
  {
208
    String subaction = ((String[])params.get("subaction"))[0];
209
    Connection conn = null;
210

    
211
    try {
212
      conn = util.openDBConnection();
213
      PreparedStatement pstmt = null;
214

    
215
      // add server to server list
216
      if ( subaction.equals("add") ) {
217
        String replicate = ((String[])params.get("replicate"))[0];
218
        String server = ((String[])params.get("server"))[0];
219
        pstmt = conn.prepareStatement("INSERT INTO xml_replication " +
220
                                      "(server, last_checked, replicate) " +
221
                                      "VALUES ('" + server + "', to_date(" +
222
                                      "'01/01/00', 'MM/DD/YY'), '" +
223
                                      replicate + "')");
224
        pstmt.execute();
225
        pstmt.close();
226
        conn.commit();
227
        out.println("Server " + server + " added"); 
228
        response.setContentType("text/html");
229
        out.println("<html><body><table border=\"1\">");
230
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
231
        out.println("<b>replicate</b></td></tr>");
232
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
233
        pstmt.execute();
234
        ResultSet rs = pstmt.getResultSet();
235
        boolean tablehasrows = rs.next();
236
        while(tablehasrows) {
237
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
238
          out.println(rs.getString(3) + "</td><td>");
239
          out.println(rs.getString(4) + "</td></tr>");
240
          tablehasrows = rs.next();
241
        }
242
        out.println("</table></body></html>");
243
        
244
        // download certificate with the public key on this server
245
        // and import it as a trusted certificate
246
        String certURL = ((String[])params.get("certificate"))[0];
247
        downloadCertificate(certURL);
248
        
249
      // delete server from server list
250
      } else if ( subaction.equals("delete") ) {
251
        String server = ((String[])params.get("server"))[0];
252
        pstmt = conn.prepareStatement("DELETE FROM xml_replication " +
253
                                      "WHERE server LIKE '" + server + "'");
254
        pstmt.execute();
255
        pstmt.close();
256
        conn.commit();
257
        out.println("Server " + server + " deleted");
258
        response.setContentType("text/html");
259
        out.println("<html><body><table border=\"1\">");
260
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
261
        out.println("<b>replicate</b></td></tr>");
262
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
263
        pstmt.execute();
264
        ResultSet rs = pstmt.getResultSet();
265
        boolean tablehasrows = rs.next();
266
        while(tablehasrows)
267
        {
268
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
269
          out.println(rs.getString(3) + "</td><td>");
270
          out.println(rs.getString(4) + "</td></tr>");
271
          tablehasrows = rs.next();
272
        }
273
        out.println("</table></body></html>");
274

    
275
      // list servers in server list
276
      } else if ( subaction.equals("list") ) {
277
        response.setContentType("text/html");
278
        out.println("<html><body><table border=\"1\">");
279
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
280
        out.println("<b>replicate</b></td></tr>");
281
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
282
        pstmt.execute();
283
        ResultSet rs = pstmt.getResultSet();
284
        boolean tablehasrows = rs.next();
285
        while(tablehasrows) {
286
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
287
          out.println(rs.getString(3) + "</td><td>");
288
          out.println(rs.getString(4) + "</td></tr>");
289
          tablehasrows = rs.next();
290
        }
291
        out.println("</table></body></html>");
292
      }
293
      pstmt.close();
294
      conn.close();
295

    
296
    } catch(Exception e) {
297
      System.out.println("Error in " + 
298
                         "MetacatReplication.handleServerControlRequest " + 
299
                         e.getMessage());
300
      e.printStackTrace(System.out);
301
    }
302
  }
303
  
304
  // download certificate with the public key from certURL and 
305
  // upload it onto this server; it then must be imported as a 
306
  // trusted certificate 
307
  private void downloadCertificate (String certURL)
308
                throws FileNotFoundException, IOException, MalformedURLException
309
  {
310
    MetaCatUtil util = new MetaCatUtil();
311
    String certPath = util.getOption("certPath"); //the path to be uploaded to
312
    
313
    // get filename from the URL of the certificate
314
    String filename = certURL;
315
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
316
    if ( slash > -1 ) {
317
      filename = filename.substring(slash + 1);
318
    }
319
    
320
    // open file output strem to write the input into it
321
    File f = new File(certPath, filename);
322
    synchronized (f) { 
323
      try {
324
        if ( f.exists() ) {
325
          throw new IOException("File already exist: " + f.getCanonicalFile());
326
          //if ( f.exists() && !f.canWrite() ) {
327
          //  throw new IOException("Not writable: " + f.getCanonicalFile());
328
        }
329
      } catch (SecurityException se) {
330
        // if a security manager exists,
331
        // its checkRead method is called for f.exist()
332
        // or checkWrite method is called for f.canWrite()
333
        throw se;
334
      }
335
      
336
      // create a buffered byte output stream
337
      // that uses a default-sized output buffer
338
      FileOutputStream fos = new FileOutputStream(f);
339
      BufferedOutputStream out = new BufferedOutputStream(fos);
340

    
341
      // this should be http url
342
      URL url = new URL(certURL);
343
      BufferedInputStream bis = null;
344
      try {
345
        bis = new BufferedInputStream(url.openStream());
346
        byte[] buf = new byte[4 * 1024]; // 4K buffer
347
        int b = bis.read(buf);
348
        while (b != -1) {
349
          out.write(buf, 0, b);
350
          b = bis.read(buf);
351
        }
352
      } finally {
353
        if (bis != null) bis.close();
354
      }
355
      // the input and the output streams must be closed
356
      bis.close();
357
	    out.flush();
358
	    out.close();
359
	    fos.close();
360
    } // end of synchronized(f)
361
  }
362
  
363
  /**
364
   * when a forcereplication request comes in, this method sends a read request
365
   * to the requesting server for the specified docid.
366
   */
367
  private void handleForceReplicateRequest(PrintWriter out, Hashtable params,
368
                                           HttpServletResponse response)
369
  {
370
    String server = ((String[])params.get("server"))[0]; // the server that
371
    String docid = ((String[])params.get("docid"))[0]; // sent the document
372
    String dbaction = "UPDATE"; // the default action is UPDATE
373
    boolean override = false;
374
    int serverCode = 1;
375
    
376
    try {
377
      //if the url contains a dbaction then the default action is overridden
378
      if(params.containsKey("dbaction")) {
379
        dbaction = ((String[])params.get("dbaction"))[0];
380
        serverCode = MetacatReplication.getServerCode(server);
381
        override = true; //we are now overriding the default action
382
      }
383
      MetacatReplication.replLog("force replication request from " + server); 
384
      
385
      // sending back read request to server
386
      URL u = new URL("https://" + server + "?action=read&docid=" + docid);
387
      String xmldoc = MetacatReplication.getURLContent(u);
388

    
389
      // get the document info from server
390
      URL docinfourl = new URL("https://" + server + 
391
                               "?action=getdocumentinfo&docid=" + docid);
392
      String docInfoStr = MetacatReplication.getURLContent(docinfourl);
393

    
394
      //dih is the parser for the docinfo xml format
395
      DocInfoHandler dih = new DocInfoHandler();
396
      XMLReader docinfoParser = ReplicationHandler.initParser(dih);
397
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
398
      Hashtable docinfoHash = dih.getDocInfo();
399
      String user = (String)docinfoHash.get("user_owner");
400

    
401
      // write the document here
402
System.out.println("Open db connection");
403
      Connection conn = util.openDBConnection();
404
System.out.println("DB connection opened");
405
      DocumentImpl.write(conn, new StringReader(xmldoc), null, dbaction, docid, 
406
                         user, null, serverCode, override);
407
      conn.close();
408

    
409
      MetacatReplication.replLog("document " + docid + " added to DB with " +
410
                                 "action " + dbaction);
411
    } catch(Exception e) {
412
      System.out.println("ERROR in MetacatReplication.handleForceReplicate" +
413
                         "Request(): " + e.getMessage());
414
    }
415
  }
416
  
417
  /**
418
   * Grants or denies a lock to a requesting host.
419
   * The servlet parameters of interrest are:
420
   * docid: the docid of the file the lock is being requested for
421
   * currentdate: the timestamp of the document on the remote server
422
   * 
423
   */
424
  private void handleGetLockRequest(PrintWriter out, Hashtable params,
425
                                    HttpServletResponse response)
426
  {
427
    Connection conn = null;
428
    try
429
    {
430
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
431
                                                "handleGetLockRequest");
432
      String docid = ((String[])params.get("docid"))[0];
433
      String remoteRev = ((String[])params.get("updaterev"))[0];
434
      DocumentImpl requestDoc = new DocumentImpl(conn, docid);
435
      MetacatReplication.replLog("lock request for " + docid);
436
      int localRevInt = requestDoc.getRev();
437
      int remoteRevInt = Integer.parseInt(remoteRev);
438
      
439
      if(remoteRevInt >= localRevInt)
440
      {
441
        if(!fileLocks.contains(docid))
442
        { //grant the lock if it is not already locked
443
          fileLocks.add(0, docid); //insert at the beginning of the queue Vector
444
          //send a message back to the the remote host authorizing the insert
445
          out.println("<lockgranted><docid>" +docid+ "</docid></lockgranted>");
446
          lockThread = new Thread(this);
447
          lockThread.setPriority(Thread.MIN_PRIORITY);
448
          lockThread.start();
449
          System.out.println("lock granted for " + docid);
450
          MetacatReplication.replLog("lock granted for " + docid);
451
        }
452
        else
453
        { //deny the lock
454
          out.println("<filelocked><docid>" + docid + "</docid></filelocked>");
455
          MetacatReplication.replLog("lock denied for " + docid + 
456
                                     "reason: file already locked");
457
        }
458
      }
459
      else
460
      {//deny the lock.
461
        out.println("<outdatedfile><docid>" + docid + "</docid></filelocked>");
462
        MetacatReplication.replLog("lock denied for " + docid + 
463
                                   "reason: client has outdated file");
464
      }
465
      conn.close();
466
    }
467
    catch(Exception e)
468
    {
469
      System.out.println("error requesting file lock from MetacatReplication." +
470
                         "handleGetLockRequest: " + e.getMessage());
471
      e.printStackTrace(System.out);
472
    }
473
  }
474
  
475
  /**
476
   * Sends all of the xml_documents information encoded in xml to a requestor
477
   * the format is:
478
   * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
479
   *                         user_updated, public_access, rev)
480
   * all of the subelements of document info are #PCDATA
481
   */
482
  private void handleGetDocumentInfoRequest(PrintWriter out, Hashtable params, 
483
                                        HttpServletResponse response)
484
  {
485
    String docid = ((String[])(params.get("docid")))[0];
486
    StringBuffer sb = new StringBuffer();
487
    Connection conn = null;
488
    try
489
    {
490
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
491
                                                "handleGetDocumentInfoRequest");
492
      DocumentImpl doc = new DocumentImpl(conn, docid);
493
      sb.append("<documentinfo><docid>").append(docid);
494
      sb.append("</docid><docname>").append(doc.getDocname());
495
      sb.append("</docname><doctype>").append(doc.getDoctype());
496
      sb.append("</doctype>");
497
// DOCTITLE attr cleared from the db
498
//      sb.append("</doctype><doctitle>").append(doc.getDocTitle());
499
      sb.append("<user_owner>").append(doc.getUserowner());
500
      sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
501
      sb.append("</user_updated><public_access>").append(doc.getPublicaccess());
502
      sb.append("</public_access><rev>").append(doc.getRev());
503
      sb.append("</rev></documentinfo>");
504
      response.setContentType("text/xml");
505
      out.println(sb.toString());
506
      conn.close();
507
    }
508
    catch (Exception e)
509
    {
510
      System.out.println("error in " +
511
                         "metacatReplication.handlegetdocumentinforequest: " + 
512
                          e.getMessage());
513
    }
514
    
515
  }
516
  
517
  /**
518
   * Sends a document to a remote host
519
   */
520
  private void handleGetDocumentRequest(PrintWriter out, Hashtable params, 
521
                                        HttpServletResponse response)
522
  {
523
    Connection conn = null;
524
    try
525
    {
526
      String docid = ((String[])(params.get("docid")))[0];
527
      System.out.println("incoming get request for document: " + docid);
528
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
529
                                                "handleGetDocumentRequest");
530
      DocumentImpl di = new DocumentImpl(conn, docid);
531
      response.setContentType("text/xml");
532
      out.print(di.toString());
533
      conn.close();
534
      MetacatReplication.replLog("document " + docid + " sent");
535
      System.out.println("document " + docid + " sent");
536
    }
537
    catch(Exception e)
538
    {
539
      System.out.println("error getting document from MetacatReplication." +
540
                         "handlGetDocumentRequest " + e.getMessage());
541
      e.printStackTrace(System.out);
542
    }
543
    
544
  }
545
  
546
  /**
547
   * Sends a list of all of the documents on this sever along with their
548
   * revision numbers.  
549
   * The format is:
550
   * <!ELEMENT replication (server, updates)>
551
   * <!ELEMENT server (#PCDATA)>
552
   * <!ELEMENT updates ((updatedDocument | deleteDocument)*)>
553
   * <!ELEMENT updatedDocument (docid, rev)>
554
   * <!ELEMENT deletedDocument (docid, rev)>
555
   * <!ELEMENT docid (#PCDATA)>
556
   * <!ELEMENT rev (#PCDATA)>
557
   * note that the rev in deletedDocument is always empty.  I just left
558
   * it in there to make the parser implementation easier.
559
   */
560
  private void handleUpdateRequest(PrintWriter out, Hashtable params, 
561
                                    HttpServletResponse response)
562
  {
563
    Connection conn = null;
564
    try
565
    {
566
      System.out.println("received update request");
567
      StringBuffer docsql = new StringBuffer();
568
      StringBuffer doclist = new StringBuffer();
569
      Vector packageFiles = new Vector();
570
      
571
      //get all docs that reside on this server
572
      doclist.append("<?xml version=\"1.0\"?><replication>");
573
      doclist.append("<server>").append(util.getOption("server"));
574
      doclist.append(util.getOption("replicationpath"));
575
      doclist.append("</server><updates>");
576
      
577
      docsql.append("select docid, rev, doctype from xml_documents where "); 
578
      docsql.append("server_location = 1");
579
      
580
      //get any deleted documents
581
      StringBuffer delsql = new StringBuffer();
582
      delsql.append("select distinct docid from ");
583
      delsql.append("xml_revisions where docid not in (select docid from ");
584
      delsql.append("xml_documents) and server_location = 1");
585
      
586
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
587
                                                "handleUpdateRequest");
588
      PreparedStatement pstmt = conn.prepareStatement(docsql.toString());
589
      pstmt.execute();
590
      ResultSet rs = pstmt.getResultSet();
591
      boolean tablehasrows = rs.next();
592
      while(tablehasrows)
593
      {
594
        String recordDoctype = rs.getString(3);
595
        if(!recordDoctype.equals("BIN"))
596
        { //don't replicate data files
597
          if(!recordDoctype.equals(util.getOption("packagedoctype")))
598
          { //if this is a package file, put it at the end
599
            //because if a package file is read before all of the files it
600
            //refers to are loaded then there is an error
601
            doclist.append("<updatedDocument>");
602
            doclist.append("<docid>").append(rs.getString(1));
603
            doclist.append("</docid><rev>").append(rs.getInt(2));
604
            doclist.append("</rev>");
605
            doclist.append("</updatedDocument>");
606
          }
607
          else
608
          { //the package files are saved to be put into the xml later.
609
            Vector v = new Vector();
610
            v.add(new String(rs.getString(1)));
611
            v.add(new Integer(rs.getInt(2)));
612
            packageFiles.add(new Vector(v));
613
          }
614
        }
615
        tablehasrows = rs.next();
616
      }
617
      
618
      pstmt = conn.prepareStatement(delsql.toString());
619
      pstmt.execute();
620
      rs = pstmt.getResultSet();
621
      tablehasrows = rs.next();
622
      while(tablehasrows)
623
      { //handle the deleted documents
624
        doclist.append("<deletedDocument><docid>").append(rs.getString(1));
625
        doclist.append("</docid><rev></rev></deletedDocument>");
626
        //note that rev is always empty for deleted docs
627
        tablehasrows = rs.next();
628
      }
629
      
630
      //now we can put the package files into the xml results
631
      for(int i=0; i<packageFiles.size(); i++)
632
      {
633
        Vector v = (Vector)packageFiles.elementAt(i);
634
        doclist.append("<updatedDocument>");
635
        doclist.append("<docid>").append((String)v.elementAt(0));
636
        doclist.append("</docid><rev>");
637
        doclist.append(((Integer)v.elementAt(1)).intValue());
638
        doclist.append("</rev>");
639
        doclist.append("</updatedDocument>");
640
      }
641
      
642
      doclist.append("</updates></replication>");
643
      MetaCatUtil.debugMessage("doclist: " + doclist.toString());
644
      pstmt.close();
645
      conn.close();
646
      response.setContentType("text/xml");
647
      out.println(doclist.toString());
648
      System.out.println("doclist: " + doclist.toString());
649
      System.out.println("update request handled");
650
    }
651
    catch(Exception e)
652
    {
653
      System.out.println("error in MetacatReplication.handleupdaterequest: " + 
654
                          e.getMessage());
655
      e.printStackTrace(System.out);
656
    }
657
    
658
  }
659
  
660
  /**
661
   * Returns the xml_catalog table encoded in xml
662
   */
663
  public static String getCatalogXML()
664
  {
665
    return handleGetCatalogRequest(null, null, null, false);
666
  }
667
  
668
  /**
669
   * Sends the contents of the xml_catalog table encoded in xml
670
   * The xml format is:
671
   * <!ELEMENT xml_catalog (row*)>
672
   * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
673
   *                system_id)>
674
   * All of the sub elements of row are #PCDATA
675
   
676
   * If printFlag == false then do not print to out.
677
   */
678
  private static String handleGetCatalogRequest(PrintWriter out, 
679
                                                Hashtable params,
680
                                                HttpServletResponse response,
681
                                                boolean printFlag)
682
  {
683
    Connection conn = null;
684
    PreparedStatement pstmt = null;
685
    try
686
    { 
687
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
688
                                                "handleGetCatalogRequest");
689
      pstmt = conn.prepareStatement("select entry_type, " +
690
                              "source_doctype, target_doctype, public_id, " +
691
                              "system_id from xml_catalog");
692
      pstmt.execute();
693
      ResultSet rs = pstmt.getResultSet();
694
      boolean tablehasrows = rs.next();
695
      StringBuffer sb = new StringBuffer();
696
      sb.append("<?xml version=\"1.0\"?><xml_catalog>");
697
      while(tablehasrows)
698
      {
699
        sb.append("<row><entry_type>").append(rs.getString(1));
700
        sb.append("</entry_type><source_doctype>").append(rs.getString(2));
701
        sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
702
        sb.append("</target_doctype><public_id>").append(rs.getString(4));
703
        sb.append("</public_id><system_id>").append(rs.getString(5));
704
        sb.append("</system_id></row>");
705
      
706
        tablehasrows = rs.next();
707
      }
708
      sb.append("</xml_catalog>");
709
      conn.close();
710
      if(printFlag)
711
      {
712
        response.setContentType("text/xml");
713
        out.println(sb.toString());
714
      }
715
      pstmt.close();
716
      return sb.toString();
717
    }
718
    catch(Exception e)
719
    {
720
      try
721
      {
722
        pstmt.close();
723
        conn.close();
724
      }
725
      catch(Exception ee)
726
      {}
727
      System.out.println("error in MetacatReplication.handleGetCatalogRequest:"+ 
728
                          e.getMessage());
729
      e.printStackTrace(System.out);
730
    }
731
    return null;
732
  }
733
  
734
  /**
735
   * Sends the current system date to the remote server.  Using this action
736
   * for replication gets rid of any problems with syncronizing clocks 
737
   * because a time specific to a document is always kept on its home server.
738
   */
739
  private void handleGetTimeRequest(PrintWriter out, Hashtable params, 
740
                                    HttpServletResponse response)
741
  {
742
    SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
743
    java.util.Date localtime = new java.util.Date();
744
    String dateString = formatter.format(localtime);
745
    response.setContentType("text/xml");
746
    
747
    out.println("<timestamp>" + dateString + "</timestamp>");
748
  }
749
  
750
  /**
751
   * this method handles the timeout for a file lock.  when a lock is 
752
   * granted it is granted for 30 seconds.  When this thread runs out
753
   * it deletes the docid from the queue, thus eliminating the lock.
754
   */
755
  public void run()
756
  {
757
    try
758
    {
759
      MetaCatUtil.debugMessage("thread started for docid: " + 
760
                               (String)fileLocks.elementAt(0));
761
      System.out.println("thread started for docid: " + 
762
                               (String)fileLocks.elementAt(0));
763
      Thread.sleep(30000); //the lock will expire in 30 seconds
764
      MetaCatUtil.debugMessage("thread for docid: " + 
765
                             (String)fileLocks.elementAt(fileLocks.size() - 1) + 
766
                              " exiting.");
767
      System.out.println("thread for docid: " + 
768
                         (String)fileLocks.elementAt(fileLocks.size() - 1) + 
769
                         " exiting.");
770
      fileLocks.remove(fileLocks.size() - 1);
771
      //fileLocks is treated as a FIFO queue.  If there are more than one lock
772
      //in the vector, the first one inserted will be removed.
773
    }
774
    catch(Exception e)
775
    {
776
      System.out.println("error in file lock thread from MetacatReplication." + 
777
                         "run: " + e.getMessage());
778
    }
779
  }
780
  
781
  /**
782
   * Returns the name of a server given a serverCode
783
   * @param serverCode the serverid of the server
784
   * @return the servername or null if the specified serverCode does not
785
   *         exist.
786
   */
787
  public static String getServer(int serverCode)
788
  {
789
    //System.out.println("serverid: " + serverCode);
790
    Connection conn = null;
791
    try
792
    {
793
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
794
                                                "getServer");
795
      String sql = new String("select server from " +
796
                              "xml_replication where serverid = " + 
797
                              serverCode);
798
      PreparedStatement pstmt = conn.prepareStatement(sql);
799
      //System.out.println("getserver sql: " + sql);
800
      pstmt.execute();
801
      ResultSet rs = pstmt.getResultSet();
802
      boolean tablehasrows = rs.next();
803
      if(tablehasrows)
804
      {
805
        //System.out.println("server: " + rs.getString(1));
806
        return rs.getString(1);
807
      }
808
      pstmt.close();
809
      conn.close();
810
    }
811
    catch(Exception e)
812
    {
813
      System.out.println("Error in MetacatReplication.getServer: " + 
814
                          e.getMessage());
815
    }
816
    return null;
817
      //return null if the server does not exist
818
  }
819
  
820
  /**
821
   * Returns a server code given a server name
822
   * @param server the name of the server
823
   * @return integer > 0 representing the code of the server, 0 if the server
824
   *  does not exist.
825
   */
826
  public static int getServerCode(String server) throws Exception
827
  {
828
    Connection conn = null;
829
    PreparedStatement pstmt = null;
830
    int serverCode = 0;
831

    
832
    try {
833

    
834
      conn = util.openDBConnection();
835
      pstmt = conn.prepareStatement("SELECT serverid FROM xml_replication " +
836
                                    "WHERE server LIKE '" + server + "'");
837
      pstmt.execute();
838
      ResultSet rs = pstmt.getResultSet();
839
      boolean tablehasrows = rs.next();
840
      if ( tablehasrows ) {  
841
        serverCode = rs.getInt(1);
842
        pstmt.close();
843
        conn.close();
844
        return serverCode;
845
      }
846
      
847
    } catch(Exception e) {
848
      throw e;
849

    
850
    } finally {
851
      try {
852
        pstmt.close();
853
        conn.close();
854
      } catch(Exception ee) {}
855
    }
856
    
857
    return serverCode;
858
  }
859
  
860
  /**
861
   * This method returns the content of a url
862
   * @param u the url to return the content from
863
   * @return a string representing the content of the url
864
   * @throws java.io.IOException
865
   */
866
  public static String getURLContent(URL u) throws java.io.IOException
867
  {
868
    //System.out.println("url: " + u.toString());
869
    char istreamChar;
870
    int istreamInt;
871
    InputStreamReader istream = new InputStreamReader(u.openStream());
872
    StringBuffer serverResponse = new StringBuffer();
873
    while((istreamInt = istream.read()) != -1)
874
    {
875
      istreamChar = (char)istreamInt;
876
      serverResponse.append(istreamChar);
877
    }
878
    
879
    return serverResponse.toString();
880
  }
881
  
882
  /**
883
   * Method for writing replication messages to a log file specified in 
884
   * metacat.properties
885
   */
886
  public static void replLog(String message)
887
  {
888
    try
889
    {
890
      FileOutputStream fos = new FileOutputStream(
891
                                 util.getOption("replicationlog"), true);
892
      PrintWriter pw = new PrintWriter(fos);
893
      SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
894
      java.util.Date localtime = new java.util.Date();
895
      String dateString = formatter.format(localtime);
896
      dateString += " :: " + message;
897
      //time stamp each entry
898
      pw.println(dateString);
899
      pw.flush();
900
    }
901
    catch(Exception e)
902
    {
903
      System.out.println("error writing to replication log from " +
904
                         "MetacatReplication.replLog: " + e.getMessage());
905
      //e.printStackTrace(System.out);
906
    }
907
  }
908
  
909
  /**
910
   * Returns true if the replicate field for server in xml_replication is 1.
911
   * Returns false otherwise
912
   */
913
  public static boolean replToServer(String server)
914
  {
915
    Connection conn = null;
916
    PreparedStatement pstmt = null;
917
    try
918
    {
919
      conn = MetacatReplication.getDBConnection("MetacatReplication.replToServer");
920
      pstmt = conn.prepareStatement("select replicate from " + 
921
                                    "xml_replication where server like '" +
922
                                     server + "'");
923
      pstmt.execute();
924
      ResultSet rs = pstmt.getResultSet();
925
      boolean tablehasrows = rs.next();
926
      if(tablehasrows)
927
      {
928
        int i = rs.getInt(1);
929
        if(i == 1)
930
        {
931
          pstmt.close();
932
          conn.close();
933
          return true;
934
        }
935
        else
936
        {
937
          pstmt.close();
938
          conn.close();
939
          return false;
940
        }
941
      }
942
    }
943
    catch(Exception e)
944
    {
945
      System.out.println("error in MetacatReplication.replToServer: " + 
946
                         e.getMessage());
947
    }
948
    finally
949
    {
950
      try
951
      {
952
        pstmt.close();
953
        conn.close();
954
      }
955
      catch(Exception ee)
956
      {}
957
    }
958
    return false;
959
    //the default if this server does not exist is to not replicate to it.
960
  }
961
  
962
  /**
963
   * A method for redundantly trying to connect.  this method will attempt to 
964
   * connect to the DB 3 times before failing.
965
   * @param methodname the methodname from which this method is called.
966
   */
967
  public static Connection getDBConnection(String methodname) throws Exception
968
  {
969
    Connection conn = null;
970
    try
971
    {
972
      try
973
      { //this connection is prone to error for some reason so we 
974
        //try to connect it three times before quiting.
975
        conn = util.openDBConnection();
976
        return conn;
977
      }
978
      catch(SQLException sqle)
979
      {
980
        try
981
        {
982
          conn = util.openDBConnection();
983
          return conn;
984
        }
985
        catch(SQLException sqlee)
986
        {
987
          try
988
          {
989
            conn = util.openDBConnection();
990
            return conn;
991
          }
992
          catch(SQLException sqleee)
993
          {
994
            System.out.println("error getting db connection in " + 
995
                               methodname +  ": " +
996
                               sqleee.getMessage());
997
            return conn;
998
          }
999
        }
1000
      }
1001
    }
1002
    catch(Exception e)
1003
    {
1004
      throw new java.lang.Exception("error in " + methodname + ": " + 
1005
                          e.getMessage());
1006
    }
1007
  }
1008
}
(31-31/40)