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: berkley $'
10
 *     '$Date: 2001-12-20 12:13:11 -0800 (Thu, 20 Dec 2001) $'
11
 * '$Revision: 887 $'
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
          Vector packagedoctypes = MetaCatUtil.getOptionList(
598
                                     MetaCatUtil.getOption("packagedoctype"));
599
          if(!packagedoctypes.contains(recordDoctype))
600
          { //if this is a package file, put it at the end
601
            //because if a package file is read before all of the files it
602
            //refers to are loaded then there is an error
603
            doclist.append("<updatedDocument>");
604
            doclist.append("<docid>").append(rs.getString(1));
605
            doclist.append("</docid><rev>").append(rs.getInt(2));
606
            doclist.append("</rev>");
607
            doclist.append("</updatedDocument>");
608
          }
609
          else
610
          { //the package files are saved to be put into the xml later.
611
            Vector v = new Vector();
612
            v.add(new String(rs.getString(1)));
613
            v.add(new Integer(rs.getInt(2)));
614
            packageFiles.add(new Vector(v));
615
          }
616
        }
617
        tablehasrows = rs.next();
618
      }
619
      
620
      pstmt = conn.prepareStatement(delsql.toString());
621
      pstmt.execute();
622
      rs = pstmt.getResultSet();
623
      tablehasrows = rs.next();
624
      while(tablehasrows)
625
      { //handle the deleted documents
626
        doclist.append("<deletedDocument><docid>").append(rs.getString(1));
627
        doclist.append("</docid><rev></rev></deletedDocument>");
628
        //note that rev is always empty for deleted docs
629
        tablehasrows = rs.next();
630
      }
631
      
632
      //now we can put the package files into the xml results
633
      for(int i=0; i<packageFiles.size(); i++)
634
      {
635
        Vector v = (Vector)packageFiles.elementAt(i);
636
        doclist.append("<updatedDocument>");
637
        doclist.append("<docid>").append((String)v.elementAt(0));
638
        doclist.append("</docid><rev>");
639
        doclist.append(((Integer)v.elementAt(1)).intValue());
640
        doclist.append("</rev>");
641
        doclist.append("</updatedDocument>");
642
      }
643
      
644
      doclist.append("</updates></replication>");
645
      MetaCatUtil.debugMessage("doclist: " + doclist.toString());
646
      pstmt.close();
647
      conn.close();
648
      response.setContentType("text/xml");
649
      out.println(doclist.toString());
650
      System.out.println("doclist: " + doclist.toString());
651
      System.out.println("update request handled");
652
    }
653
    catch(Exception e)
654
    {
655
      System.out.println("error in MetacatReplication.handleupdaterequest: " + 
656
                          e.getMessage());
657
      e.printStackTrace(System.out);
658
    }
659
    
660
  }
661
  
662
  /**
663
   * Returns the xml_catalog table encoded in xml
664
   */
665
  public static String getCatalogXML()
666
  {
667
    return handleGetCatalogRequest(null, null, null, false);
668
  }
669
  
670
  /**
671
   * Sends the contents of the xml_catalog table encoded in xml
672
   * The xml format is:
673
   * <!ELEMENT xml_catalog (row*)>
674
   * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
675
   *                system_id)>
676
   * All of the sub elements of row are #PCDATA
677
   
678
   * If printFlag == false then do not print to out.
679
   */
680
  private static String handleGetCatalogRequest(PrintWriter out, 
681
                                                Hashtable params,
682
                                                HttpServletResponse response,
683
                                                boolean printFlag)
684
  {
685
    Connection conn = null;
686
    PreparedStatement pstmt = null;
687
    try
688
    { 
689
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
690
                                                "handleGetCatalogRequest");
691
      pstmt = conn.prepareStatement("select entry_type, " +
692
                              "source_doctype, target_doctype, public_id, " +
693
                              "system_id from xml_catalog");
694
      pstmt.execute();
695
      ResultSet rs = pstmt.getResultSet();
696
      boolean tablehasrows = rs.next();
697
      StringBuffer sb = new StringBuffer();
698
      sb.append("<?xml version=\"1.0\"?><xml_catalog>");
699
      while(tablehasrows)
700
      {
701
        sb.append("<row><entry_type>").append(rs.getString(1));
702
        sb.append("</entry_type><source_doctype>").append(rs.getString(2));
703
        sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
704
        sb.append("</target_doctype><public_id>").append(rs.getString(4));
705
        sb.append("</public_id><system_id>").append(rs.getString(5));
706
        sb.append("</system_id></row>");
707
      
708
        tablehasrows = rs.next();
709
      }
710
      sb.append("</xml_catalog>");
711
      conn.close();
712
      if(printFlag)
713
      {
714
        response.setContentType("text/xml");
715
        out.println(sb.toString());
716
      }
717
      pstmt.close();
718
      return sb.toString();
719
    }
720
    catch(Exception e)
721
    {
722
      try
723
      {
724
        pstmt.close();
725
        conn.close();
726
      }
727
      catch(Exception ee)
728
      {}
729
      System.out.println("error in MetacatReplication.handleGetCatalogRequest:"+ 
730
                          e.getMessage());
731
      e.printStackTrace(System.out);
732
    }
733
    return null;
734
  }
735
  
736
  /**
737
   * Sends the current system date to the remote server.  Using this action
738
   * for replication gets rid of any problems with syncronizing clocks 
739
   * because a time specific to a document is always kept on its home server.
740
   */
741
  private void handleGetTimeRequest(PrintWriter out, Hashtable params, 
742
                                    HttpServletResponse response)
743
  {
744
    SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
745
    java.util.Date localtime = new java.util.Date();
746
    String dateString = formatter.format(localtime);
747
    response.setContentType("text/xml");
748
    
749
    out.println("<timestamp>" + dateString + "</timestamp>");
750
  }
751
  
752
  /**
753
   * this method handles the timeout for a file lock.  when a lock is 
754
   * granted it is granted for 30 seconds.  When this thread runs out
755
   * it deletes the docid from the queue, thus eliminating the lock.
756
   */
757
  public void run()
758
  {
759
    try
760
    {
761
      MetaCatUtil.debugMessage("thread started for docid: " + 
762
                               (String)fileLocks.elementAt(0));
763
      System.out.println("thread started for docid: " + 
764
                               (String)fileLocks.elementAt(0));
765
      Thread.sleep(30000); //the lock will expire in 30 seconds
766
      MetaCatUtil.debugMessage("thread for docid: " + 
767
                             (String)fileLocks.elementAt(fileLocks.size() - 1) + 
768
                              " exiting.");
769
      System.out.println("thread for docid: " + 
770
                         (String)fileLocks.elementAt(fileLocks.size() - 1) + 
771
                         " exiting.");
772
      fileLocks.remove(fileLocks.size() - 1);
773
      //fileLocks is treated as a FIFO queue.  If there are more than one lock
774
      //in the vector, the first one inserted will be removed.
775
    }
776
    catch(Exception e)
777
    {
778
      System.out.println("error in file lock thread from MetacatReplication." + 
779
                         "run: " + e.getMessage());
780
    }
781
  }
782
  
783
  /**
784
   * Returns the name of a server given a serverCode
785
   * @param serverCode the serverid of the server
786
   * @return the servername or null if the specified serverCode does not
787
   *         exist.
788
   */
789
  public static String getServer(int serverCode)
790
  {
791
    //System.out.println("serverid: " + serverCode);
792
    Connection conn = null;
793
    try
794
    {
795
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
796
                                                "getServer");
797
      String sql = new String("select server from " +
798
                              "xml_replication where serverid = " + 
799
                              serverCode);
800
      PreparedStatement pstmt = conn.prepareStatement(sql);
801
      //System.out.println("getserver sql: " + sql);
802
      pstmt.execute();
803
      ResultSet rs = pstmt.getResultSet();
804
      boolean tablehasrows = rs.next();
805
      if(tablehasrows)
806
      {
807
        //System.out.println("server: " + rs.getString(1));
808
        return rs.getString(1);
809
      }
810
      pstmt.close();
811
      conn.close();
812
    }
813
    catch(Exception e)
814
    {
815
      System.out.println("Error in MetacatReplication.getServer: " + 
816
                          e.getMessage());
817
    }
818
    return null;
819
      //return null if the server does not exist
820
  }
821
  
822
  /**
823
   * Returns a server code given a server name
824
   * @param server the name of the server
825
   * @return integer > 0 representing the code of the server, 0 if the server
826
   *  does not exist.
827
   */
828
  public static int getServerCode(String server) throws Exception
829
  {
830
    Connection conn = null;
831
    PreparedStatement pstmt = null;
832
    int serverCode = 0;
833

    
834
    try {
835

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

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