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: tao $'
10
 *     '$Date: 2002-04-19 18:52:26 -0700 (Fri, 19 Apr 2002) $'
11
 * '$Revision: 1020 $'
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
    //ServletOutputStream outPut = response.getOutputStream();
88
    Hashtable params = new Hashtable();
89
    Enumeration paramlist = request.getParameterNames();
90
    
91
    //First check if administrate conifgure the replcation feature on
92
    //This configuration is set in build.xml file
93
    //If it is not on, reject any request.
94
    if (!(util.getOption("replication")).equals("on"))
95
    {
96
     (response.getWriter()).println("MetaCat is not set to handle replication");
97
      (response.getWriter()).close();
98
      return;
99
    }
100
    
101
// NOT NEEDED - doesn't provide enough security because of possible IP spoofing
102
// REPLACED with running replication comminications over HTTPS
103
//    String requestingServerIP = request.getRemoteAddr();
104
//    InetAddress iaddr = InetAddress.getByName(requestingServerIP);
105
//    String requestingServer = iaddr.getHostName();
106
    
107
    while (paramlist.hasMoreElements()) {
108
      String name = (String)paramlist.nextElement();
109
      String[] value = request.getParameterValues(name);
110
      params.put(name, value);  
111
    }
112
    
113
    String action = ((String[])params.get("action"))[0];
114
    String server = null;
115
    
116
    try {
117
      // check if the server is included in the list of replicated servers
118
      if ( !action.equals("servercontrol") && 
119
           !action.equals("stop") &&
120
           !action.equals("start") &&
121
           !action.equals("getall") ) {
122

    
123
        server = ((String[])params.get("server"))[0];
124
        if ( getServerCode(server) == 0 ) {
125
          System.out.println("Action \"" + action + 
126
                             "\" rejected for server: " + server);
127
          return;
128
        } else {
129
          System.out.println("Action \"" + action + 
130
                             "\" accepted for server: " + server);
131
        }
132
      }
133
    } catch (Exception e) {
134
      System.out.println("Error in MetacatReplication.handleGetOrPost: " +
135
                         e.getMessage() );
136
      return;
137
    }
138
    if ( action.equals("readdata") ) 
139
    {
140
      ServletOutputStream out=response.getOutputStream();
141
      //to get the data file.
142
      handleGetDataFileRequest(out, params, response);
143
      out.close();
144
    }
145
    else
146
    {
147
    PrintWriter out = response.getWriter();
148
    if ( action.equals("stop") ) {
149
      //stop the replication server
150
      replicationDaemon.cancel();
151
      replicationDaemon = new Timer(true);
152
      out.println("Replication Handler Stopped");
153
      MetacatReplication.replLog("deltaT handler stopped");
154
    
155

    
156
    } else if ( action.equals("start") ) {
157
      
158
      //start the replication server
159
      int rate;
160
      if ( params.containsKey("rate") ) {
161
        rate = new Integer(
162
               new String(((String[])params.get("rate"))[0])).intValue();
163
        if(rate < 30) {
164
            out.println("Replication deltaT rate cannot be less than 30!");
165
            //deltaT<30 is a timing mess!
166
            rate = 1000;
167
        }
168
      } else {
169
        rate = 1000;
170
      }
171
      out.println("New rate is: " + rate + " seconds.");
172
      replicationDaemon.cancel();
173
      replicationDaemon = new Timer(true);
174
      replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(out), 0, 
175
                                            rate * 1000);
176
      out.println("Replication Handler Started");
177
      MetacatReplication.replLog("deltaT handler started with rate=" + 
178
                                    rate + " seconds");
179
     
180

    
181
    } else if ( action.equals("getall") ) {
182
      //updates this server exactly once
183
      replicationDaemon.schedule(new ReplicationHandler(out), 0);
184
      response.setContentType("text/html");
185
      out.println("<html><body>\"Get All\" Done</body></html>");
186

    
187
    } else if ( action.equals("forcereplicate") ) {
188
      //read a specific docid from remote host, and store it into local host
189
      handleForceReplicateRequest(out, params, response);
190

    
191
    } else if ( action.equals("update") ) {
192
      //request an update list from the server
193
      handleUpdateRequest(out, params, response);
194

    
195
    } else if ( action.equals("read") ) {
196
      //request a specific document from the server
197
      //note that this could be replaced by a call to metacatServlet
198
      //handleGetDocumentAction().
199
      handleGetDocumentRequest(out, params, response);
200
    } else if ( action.equals("getlock") ) {
201
      handleGetLockRequest(out, params, response);
202

    
203
    } else if ( action.equals("getdocumentinfo") ) {
204
      handleGetDocumentInfoRequest(out, params, response);
205

    
206
    } else if ( action.equals("gettime") ) {
207
      handleGetTimeRequest(out, params, response);
208

    
209
    } else if ( action.equals("getcatalog") ) {
210
      handleGetCatalogRequest(out, params, response, true);
211

    
212
    } else if ( action.equals("servercontrol") ) {
213
      handleServerControlRequest(out, params, response);
214
    }
215
    
216
    out.close();
217
    }//else
218
  }
219
  
220
  /** 
221
   * This method can add, delete and list the servers currently included in
222
   * xml_replication.
223
   * action           subaction            other needed params
224
   * ---------------------------------------------------------
225
   * servercontrol    add                  server
226
   * servercontrol    delete               server
227
   * servercontrol    list                 
228
   */
229
  private void handleServerControlRequest(PrintWriter out, Hashtable params,
230
                                          HttpServletResponse response)
231
  {
232
    String subaction = ((String[])params.get("subaction"))[0];
233
    Connection conn = null;
234

    
235
    try {
236
      conn = util.openDBConnection();
237
      PreparedStatement pstmt = null;
238

    
239
      // add server to server list
240
      if ( subaction.equals("add") ) {
241
        String replicate = ((String[])params.get("replicate"))[0];
242
        String server = ((String[])params.get("server"))[0];
243
        pstmt = conn.prepareStatement("INSERT INTO xml_replication " +
244
                                      "(server, last_checked, replicate) " +
245
                                      "VALUES ('" + server + "', to_date(" +
246
                                      "'01/01/00', 'MM/DD/YY'), '" +
247
                                      replicate + "')");
248
        pstmt.execute();
249
        pstmt.close();
250
        conn.commit();
251
        out.println("Server " + server + " added"); 
252
        response.setContentType("text/html");
253
        out.println("<html><body><table border=\"1\">");
254
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
255
        out.println("<b>replicate</b></td></tr>");
256
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
257
        pstmt.execute();
258
        ResultSet rs = pstmt.getResultSet();
259
        boolean tablehasrows = rs.next();
260
        while(tablehasrows) {
261
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
262
          out.println(rs.getString(3) + "</td><td>");
263
          out.println(rs.getString(4) + "</td></tr>");
264
          tablehasrows = rs.next();
265
        }
266
        out.println("</table></body></html>");
267
        
268
        // download certificate with the public key on this server
269
        // and import it as a trusted certificate
270
        String certURL = ((String[])params.get("certificate"))[0];
271
        downloadCertificate(certURL);
272
        
273
      // delete server from server list
274
      } else if ( subaction.equals("delete") ) {
275
        String server = ((String[])params.get("server"))[0];
276
        pstmt = conn.prepareStatement("DELETE FROM xml_replication " +
277
                                      "WHERE server LIKE '" + server + "'");
278
        pstmt.execute();
279
        pstmt.close();
280
        conn.commit();
281
        out.println("Server " + server + " deleted");
282
        response.setContentType("text/html");
283
        out.println("<html><body><table border=\"1\">");
284
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
285
        out.println("<b>replicate</b></td></tr>");
286
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
287
        pstmt.execute();
288
        ResultSet rs = pstmt.getResultSet();
289
        boolean tablehasrows = rs.next();
290
        while(tablehasrows)
291
        {
292
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
293
          out.println(rs.getString(3) + "</td><td>");
294
          out.println(rs.getString(4) + "</td></tr>");
295
          tablehasrows = rs.next();
296
        }
297
        out.println("</table></body></html>");
298

    
299
      // list servers in server list
300
      } else if ( subaction.equals("list") ) {
301
        response.setContentType("text/html");
302
        out.println("<html><body><table border=\"1\">");
303
        out.println("<tr><td><b>server</b></td><td><b>last_checked</b></td><td>");
304
        out.println("<b>replicate</b></td></tr>");
305
        pstmt = conn.prepareStatement("SELECT * FROM xml_replication");
306
        pstmt.execute();
307
        ResultSet rs = pstmt.getResultSet();
308
        boolean tablehasrows = rs.next();
309
        while(tablehasrows) {
310
          out.println("<tr><td>" + rs.getString(2) + "</td><td>");
311
          out.println(rs.getString(3) + "</td><td>");
312
          out.println(rs.getString(4) + "</td></tr>");
313
          tablehasrows = rs.next();
314
        }
315
        out.println("</table></body></html>");
316
      }
317
      pstmt.close();
318
      conn.close();
319

    
320
    } catch(Exception e) {
321
      System.out.println("Error in " + 
322
                         "MetacatReplication.handleServerControlRequest " + 
323
                         e.getMessage());
324
      e.printStackTrace(System.out);
325
    }
326
  }
327
  
328
  // download certificate with the public key from certURL and 
329
  // upload it onto this server; it then must be imported as a 
330
  // trusted certificate 
331
  private void downloadCertificate (String certURL)
332
                throws FileNotFoundException, IOException, MalformedURLException
333
  {
334
    MetaCatUtil util = new MetaCatUtil();
335
    String certPath = util.getOption("certPath"); //the path to be uploaded to
336
    
337
    // get filename from the URL of the certificate
338
    String filename = certURL;
339
    int slash = Math.max(filename.lastIndexOf('/'), filename.lastIndexOf('\\'));
340
    if ( slash > -1 ) {
341
      filename = filename.substring(slash + 1);
342
    }
343
    
344
    // open file output strem to write the input into it
345
    File f = new File(certPath, filename);
346
    synchronized (f) { 
347
      try {
348
        if ( f.exists() ) {
349
          throw new IOException("File already exist: " + f.getCanonicalFile());
350
          //if ( f.exists() && !f.canWrite() ) {
351
          //  throw new IOException("Not writable: " + f.getCanonicalFile());
352
        }
353
      } catch (SecurityException se) {
354
        // if a security manager exists,
355
        // its checkRead method is called for f.exist()
356
        // or checkWrite method is called for f.canWrite()
357
        throw se;
358
      }
359
      
360
      // create a buffered byte output stream
361
      // that uses a default-sized output buffer
362
      FileOutputStream fos = new FileOutputStream(f);
363
      BufferedOutputStream out = new BufferedOutputStream(fos);
364

    
365
      // this should be http url
366
      URL url = new URL(certURL);
367
      BufferedInputStream bis = null;
368
      try {
369
        bis = new BufferedInputStream(url.openStream());
370
        byte[] buf = new byte[4 * 1024]; // 4K buffer
371
        int b = bis.read(buf);
372
        while (b != -1) {
373
          out.write(buf, 0, b);
374
          b = bis.read(buf);
375
        }
376
      } finally {
377
        if (bis != null) bis.close();
378
      }
379
      // the input and the output streams must be closed
380
      bis.close();
381
	    out.flush();
382
	    out.close();
383
	    fos.close();
384
    } // end of synchronized(f)
385
  }
386
  
387
  /**
388
   * when a forcereplication request comes in, local host sends a read request
389
   * to the requesting server (remote server) for the specified docid.
390
   * Then store it in local database.
391
   */
392
  private void handleForceReplicateRequest(PrintWriter out, Hashtable params,
393
                                           HttpServletResponse response)
394
  {
395
    String server = ((String[])params.get("server"))[0]; // the server that
396
    String docid = ((String[])params.get("docid"))[0]; // sent the document
397
    String dbaction = "UPDATE"; // the default action is UPDATE
398
    boolean override = false;
399
    int serverCode = 1;
400
    
401
    try {
402
      //if the url contains a dbaction then the default action is overridden
403
      if(params.containsKey("dbaction")) {
404
        dbaction = ((String[])params.get("dbaction"))[0];
405
        serverCode = MetacatReplication.getServerCode(server);
406
        override = true; //we are now overriding the default action
407
      }
408
      MetacatReplication.replLog("force replication request from " + server); 
409
      
410
      // sending back read request to server
411
      URL u = new URL("https://" + server + "?server="
412
                +util.getLocalReplicationServerName()
413
                +"&action=read&docid=" + docid);
414
      String xmldoc = MetacatReplication.getURLContent(u);
415

    
416
      // get the document info from server
417
      URL docinfourl = new URL("https://" + server + 
418
                               "?server="+util.getLocalReplicationServerName()
419
                               +"&action=getdocumentinfo&docid=" + docid);
420
      String docInfoStr = MetacatReplication.getURLContent(docinfourl);
421

    
422
      //dih is the parser for the docinfo xml format
423
      DocInfoHandler dih = new DocInfoHandler();
424
      XMLReader docinfoParser = ReplicationHandler.initParser(dih);
425
      docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
426
      Hashtable docinfoHash = dih.getDocInfo();
427
      String user = (String)docinfoHash.get("user_owner");
428

    
429
      // write the document here
430
      Connection conn = util.openDBConnection();
431
      DocumentImpl.write(conn, new StringReader(xmldoc), null, dbaction, docid, 
432
                         user, null, serverCode, override);
433
      conn.close();
434

    
435
      MetacatReplication.replLog("document " + docid + " added to DB with " +
436
                                 "action " + dbaction);
437
    } catch(Exception e) {
438
      System.out.println("ERROR in MetacatReplication.handleForceReplicate" +
439
                         "Request(): " + e.getMessage());
440
    }
441
  }
442
  
443
  /**
444
   * Grants or denies a lock to a requesting host.
445
   * The servlet parameters of interrest are:
446
   * docid: the docid of the file the lock is being requested for
447
   * currentdate: the timestamp of the document on the remote server
448
   * 
449
   */
450
  private void handleGetLockRequest(PrintWriter out, Hashtable params,
451
                                    HttpServletResponse response)
452
  {
453
    Connection conn = null;
454
    try
455
    {
456
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
457
                                                "handleGetLockRequest");
458
      String docid = ((String[])params.get("docid"))[0];
459
      String remoteRev = ((String[])params.get("updaterev"))[0];
460
      DocumentImpl requestDoc = new DocumentImpl(conn, docid);
461
      MetacatReplication.replLog("lock request for " + docid);
462
      int localRevInt = requestDoc.getRev();
463
      int remoteRevInt = Integer.parseInt(remoteRev);
464
      
465
      if(remoteRevInt >= localRevInt)
466
      {
467
        if(!fileLocks.contains(docid))
468
        { //grant the lock if it is not already locked
469
          fileLocks.add(0, docid); //insert at the beginning of the queue Vector
470
          //send a message back to the the remote host authorizing the insert
471
          out.println("<lockgranted><docid>" +docid+ "</docid></lockgranted>");
472
          lockThread = new Thread(this);
473
          lockThread.setPriority(Thread.MIN_PRIORITY);
474
          lockThread.start();
475
          MetacatReplication.replLog("lock granted for " + docid);
476
        }
477
        else
478
        { //deny the lock
479
          out.println("<filelocked><docid>" + docid + "</docid></filelocked>");
480
          MetacatReplication.replLog("lock denied for " + docid + 
481
                                     "reason: file already locked");
482
        }
483
      }
484
      else
485
      {//deny the lock.
486
        out.println("<outdatedfile><docid>" + docid + "</docid></filelocked>");
487
        MetacatReplication.replLog("lock denied for " + docid + 
488
                                   "reason: client has outdated file");
489
      }
490
      conn.close();
491
    }
492
    catch(Exception e)
493
    {
494
      System.out.println("error requesting file lock from MetacatReplication." +
495
                         "handleGetLockRequest: " + e.getMessage());
496
      e.printStackTrace(System.out);
497
    }
498
  }
499
  
500
  /**
501
   * Sends all of the xml_documents information encoded in xml to a requestor
502
   * the format is:
503
   * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
504
   *                         user_updated, public_access, rev)
505
   * all of the subelements of document info are #PCDATA
506
   */
507
  private void handleGetDocumentInfoRequest(PrintWriter out, Hashtable params, 
508
                                        HttpServletResponse response)
509
  {
510
    String docid = ((String[])(params.get("docid")))[0];
511
    StringBuffer sb = new StringBuffer();
512
    Connection conn = null;
513
    try
514
    {
515
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
516
                                                "handleGetDocumentInfoRequest");
517
      DocumentImpl doc = new DocumentImpl(conn, docid);
518
      sb.append("<documentinfo><docid>").append(docid);
519
      sb.append("</docid><docname>").append(doc.getDocname());
520
      sb.append("</docname><doctype>").append(doc.getDoctype());
521
      sb.append("</doctype>");
522
// DOCTITLE attr cleared from the db
523
//      sb.append("</doctype><doctitle>").append(doc.getDocTitle());
524
      sb.append("<user_owner>").append(doc.getUserowner());
525
      sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
526
      sb.append("</user_updated><public_access>").append(doc.getPublicaccess());
527
      sb.append("</public_access><rev>").append(doc.getRev());
528
      sb.append("</rev></documentinfo>");
529
      response.setContentType("text/xml");
530
      out.println(sb.toString());
531
      conn.close();
532
    }
533
    catch (Exception e)
534
    {
535
      System.out.println("error in " +
536
                         "metacatReplication.handlegetdocumentinforequest: " + 
537
                          e.getMessage());
538
    }
539
    
540
  }
541
   
542
  /**
543
   * Sends a datafile to a remote host
544
   */
545
  private void handleGetDataFileRequest(ServletOutputStream outPut, 
546
                            Hashtable params, HttpServletResponse response)
547
                                 
548
  {
549
    String filepath = util.getOption("datafilepath");
550
    String docId = ((String[])(params.get("docid")))[0];
551
  
552
    if(!filepath.endsWith("/")) 
553
    {
554
          filepath += "/";
555
    }
556
    String filename = filepath + docId;      //MIME type
557
    System.out.println("filename: "+filename);
558
    String contentType = null;//getServletContext().getMimeType(filename);
559
    System.out.println("after getMimeType");
560
    if (contentType == null) 
561
    {
562
       if (filename.endsWith(".xml")) 
563
       {
564
          contentType="text/xml";
565
       } 
566
       else if (filename.endsWith(".css")) 
567
       {
568
          contentType="text/css";
569
       } 
570
       else if (filename.endsWith(".dtd")) 
571
       {
572
          contentType="text/plain";
573
       } 
574
       else if (filename.endsWith(".xsd")) 
575
       {
576
          contentType="text/xml";
577
       } 
578
       else if (filename.endsWith("/")) 
579
       {
580
          contentType="text/html";
581
       } 
582
       else 
583
       {
584
          File f = new File(filename);
585
          if ( f.isDirectory() ) 
586
          {
587
             contentType="text/html";
588
          } 
589
          else
590
          {
591
             contentType="application/octet-stream";
592
          }
593
       }
594
   }
595
   System.out.println("contentye: "+contentType);
596
   response.setContentType(contentType);
597
   // if we decide to use "application/octet-stream" for all data returns
598
   // response.setContentType("application/octet-stream");
599
   FileInputStream fin = null;
600
   try 
601
   {
602
      System.out.println("in try");
603
      System.out.println(" after get output stream");
604
      fin = new FileInputStream(filename);
605
      System.out.println("here");
606
      byte[] buf = new byte[4 * 1024]; // 4K buffer
607
      int b = fin.read(buf);
608
      while (b != -1) 
609
      {
610
        System.out.println("in while loop");
611
        outPut.write(buf, 0, b);
612
        b = fin.read(buf);
613
      }
614
      System.out.println(" before close");
615
      fin.close();
616
      System.out.println(" after close");
617
   }  
618
   catch(Exception e)
619
   {
620
      System.out.println("error getting data file from MetacatReplication." +
621
                         "handlGetDataFileRequest " + e.getMessage());
622
      e.printStackTrace(System.out);
623
   }
624
  
625
}
626
  
627
  
628
  /**
629
   * Sends a document to a remote host
630
   */
631
  private void handleGetDocumentRequest(PrintWriter out, Hashtable params, 
632
                                        HttpServletResponse response)
633
  {
634
    Connection conn = null;
635
    try
636
    {
637
      String docid = ((String[])(params.get("docid")))[0];
638
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
639
                                                "handleGetDocumentRequest");
640
      DocumentImpl di = new DocumentImpl(conn, docid);
641
      response.setContentType("text/xml");
642
      out.print(di.toString());
643
      conn.close();
644
      MetacatReplication.replLog("document " + docid + " sent");
645
    
646
    }
647
    catch(Exception e)
648
    {
649
      System.out.println("error getting document from MetacatReplication." +
650
                         "handlGetDocumentRequest " + e.getMessage());
651
      e.printStackTrace(System.out);
652
    }
653
    
654
  }
655
  
656
  /**
657
   * Sends a list of all of the documents on this sever along with their
658
   * revision numbers.  
659
   * The format is:
660
   * <!ELEMENT replication (server, updates)>
661
   * <!ELEMENT server (#PCDATA)>
662
   * <!ELEMENT updates ((updatedDocument | deleteDocument)*)>
663
   * <!ELEMENT updatedDocument (docid, rev, datafile*)>
664
   * <!ELEMENT deletedDocument (docid, rev, datafile*)>
665
   * <!ELEMENT docid (#PCDATA)>
666
   * <!ELEMENT rev (#PCDATA)>
667
   * <!ELEMENT datafile (#PCDATA)>
668
   * note that the rev in deletedDocument is always empty.  I just left
669
   * it in there to make the parser implementation easier.
670
   */
671
  private void handleUpdateRequest(PrintWriter out, Hashtable params, 
672
                                    HttpServletResponse response)
673
  {
674
    Connection conn = null;
675
    try
676
    {
677
      
678
      StringBuffer docsql = new StringBuffer();
679
      StringBuffer doclist = new StringBuffer();
680
      Vector packageFiles = new Vector();
681
      
682
      //get all docs that reside on this server
683
      doclist.append("<?xml version=\"1.0\"?><replication>");
684
      doclist.append("<server>").append(util.getOption("server"));
685
      doclist.append(util.getOption("replicationpath"));
686
      doclist.append("</server><updates>");
687
      
688
      docsql.append("select docid, rev, doctype from xml_documents where "); 
689
      docsql.append("server_location = 1");
690
      
691
      //get any deleted documents
692
      StringBuffer delsql = new StringBuffer();
693
      delsql.append("select distinct docid from ");
694
      delsql.append("xml_revisions where docid not in (select docid from ");
695
      delsql.append("xml_documents) and server_location = 1");
696
      
697
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
698
                                                "handleUpdateRequest");
699
      PreparedStatement pstmt = conn.prepareStatement(docsql.toString());
700
      pstmt.execute();
701
      ResultSet rs = pstmt.getResultSet();
702
      boolean tablehasrows = rs.next();
703
      //If metacat configed not to replicate data file
704
      if (!(util.getOption("replicationdata")).equals("on"))
705
      {
706
        while(tablehasrows)
707
        {
708
          String recordDoctype = rs.getString(3);
709
          if(!recordDoctype.equals("BIN")) 
710
          { //don't replicate data files
711
            Vector packagedoctypes = MetaCatUtil.getOptionList(
712
                                     MetaCatUtil.getOption("packagedoctype"));
713
            if(!packagedoctypes.contains(recordDoctype))
714
            {   //if this is a package file, put it at the end
715
              //because if a package file is read before all of the files it
716
              //refers to are loaded then there is an error
717
              doclist.append("<updatedDocument>");
718
              doclist.append("<docid>").append(rs.getString(1));
719
              doclist.append("</docid><rev>").append(rs.getInt(2));
720
              doclist.append("</rev>");
721
              doclist.append("</updatedDocument>");
722
            }
723
            else
724
            { //the package files are saved to be put into the xml later.
725
              Vector v = new Vector();
726
              v.add(new String(rs.getString(1)));
727
              v.add(new Integer(rs.getInt(2)));
728
              packageFiles.add(new Vector(v));
729
            }
730
         }//if
731
         tablehasrows = rs.next();
732
        }//while
733
      }//if
734
      else //metacat should send data file too
735
      {
736
        
737
      }
738
      
739
      pstmt = conn.prepareStatement(delsql.toString());
740
      pstmt.execute();
741
      rs = pstmt.getResultSet();
742
      tablehasrows = rs.next();
743
      while(tablehasrows)
744
      { //handle the deleted documents
745
        doclist.append("<deletedDocument><docid>").append(rs.getString(1));
746
        doclist.append("</docid><rev></rev></deletedDocument>");
747
        //note that rev is always empty for deleted docs
748
        tablehasrows = rs.next();
749
      }
750
      
751
      //now we can put the package files into the xml results
752
      for(int i=0; i<packageFiles.size(); i++)
753
      {
754
        Vector v = (Vector)packageFiles.elementAt(i);
755
        doclist.append("<updatedDocument>");
756
        doclist.append("<docid>").append((String)v.elementAt(0));
757
        doclist.append("</docid><rev>");
758
        doclist.append(((Integer)v.elementAt(1)).intValue());
759
        doclist.append("</rev>");
760
        doclist.append("</updatedDocument>");
761
      }
762
      
763
      doclist.append("</updates></replication>");
764
      MetaCatUtil.debugMessage("doclist: " + doclist.toString());
765
      pstmt.close();
766
      conn.close();
767
      response.setContentType("text/xml");
768
      out.println(doclist.toString());
769
     
770
    }
771
    catch(Exception e)
772
    {
773
      System.out.println("error in MetacatReplication.handleupdaterequest: " + 
774
                          e.getMessage());
775
      e.printStackTrace(System.out);
776
    }
777
    
778
  }
779
  
780
  /**
781
   * Returns the xml_catalog table encoded in xml
782
   */
783
  public static String getCatalogXML()
784
  {
785
    return handleGetCatalogRequest(null, null, null, false);
786
  }
787
  
788
  /**
789
   * Sends the contents of the xml_catalog table encoded in xml
790
   * The xml format is:
791
   * <!ELEMENT xml_catalog (row*)>
792
   * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
793
   *                system_id)>
794
   * All of the sub elements of row are #PCDATA
795
   
796
   * If printFlag == false then do not print to out.
797
   */
798
  private static String handleGetCatalogRequest(PrintWriter out, 
799
                                                Hashtable params,
800
                                                HttpServletResponse response,
801
                                                boolean printFlag)
802
  {
803
    Connection conn = null;
804
    PreparedStatement pstmt = null;
805
    try
806
    { 
807
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
808
                                                "handleGetCatalogRequest");
809
      pstmt = conn.prepareStatement("select entry_type, " +
810
                              "source_doctype, target_doctype, public_id, " +
811
                              "system_id from xml_catalog");
812
      pstmt.execute();
813
      ResultSet rs = pstmt.getResultSet();
814
      boolean tablehasrows = rs.next();
815
      StringBuffer sb = new StringBuffer();
816
      sb.append("<?xml version=\"1.0\"?><xml_catalog>");
817
      while(tablehasrows)
818
      {
819
        sb.append("<row><entry_type>").append(rs.getString(1));
820
        sb.append("</entry_type><source_doctype>").append(rs.getString(2));
821
        sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
822
        sb.append("</target_doctype><public_id>").append(rs.getString(4));
823
        sb.append("</public_id><system_id>").append(rs.getString(5));
824
        sb.append("</system_id></row>");
825
      
826
        tablehasrows = rs.next();
827
      }
828
      sb.append("</xml_catalog>");
829
      conn.close();
830
      if(printFlag)
831
      {
832
        response.setContentType("text/xml");
833
        out.println(sb.toString());
834
      }
835
      pstmt.close();
836
      return sb.toString();
837
    }
838
    catch(Exception e)
839
    {
840
      try
841
      {
842
        pstmt.close();
843
        conn.close();
844
      }
845
      catch(Exception ee)
846
      {}
847
      System.out.println("error in MetacatReplication.handleGetCatalogRequest:"+ 
848
                          e.getMessage());
849
      e.printStackTrace(System.out);
850
    }
851
    return null;
852
  }
853
  
854
  /**
855
   * Sends the current system date to the remote server.  Using this action
856
   * for replication gets rid of any problems with syncronizing clocks 
857
   * because a time specific to a document is always kept on its home server.
858
   */
859
  private void handleGetTimeRequest(PrintWriter out, Hashtable params, 
860
                                    HttpServletResponse response)
861
  {
862
    SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
863
    java.util.Date localtime = new java.util.Date();
864
    String dateString = formatter.format(localtime);
865
    response.setContentType("text/xml");
866
    
867
    out.println("<timestamp>" + dateString + "</timestamp>");
868
  }
869
  
870
  /**
871
   * this method handles the timeout for a file lock.  when a lock is 
872
   * granted it is granted for 30 seconds.  When this thread runs out
873
   * it deletes the docid from the queue, thus eliminating the lock.
874
   */
875
  public void run()
876
  {
877
    try
878
    {
879
      MetaCatUtil.debugMessage("thread started for docid: " + 
880
                               (String)fileLocks.elementAt(0));
881
      System.out.println("thread started for docid: " + 
882
                               (String)fileLocks.elementAt(0));
883
      Thread.sleep(30000); //the lock will expire in 30 seconds
884
      MetaCatUtil.debugMessage("thread for docid: " + 
885
                             (String)fileLocks.elementAt(fileLocks.size() - 1) + 
886
                              " exiting.");
887
      System.out.println("thread for docid: " + 
888
                         (String)fileLocks.elementAt(fileLocks.size() - 1) + 
889
                         " exiting.");
890
      fileLocks.remove(fileLocks.size() - 1);
891
      //fileLocks is treated as a FIFO queue.  If there are more than one lock
892
      //in the vector, the first one inserted will be removed.
893
    }
894
    catch(Exception e)
895
    {
896
      System.out.println("error in file lock thread from MetacatReplication." + 
897
                         "run: " + e.getMessage());
898
    }
899
  }
900
  
901
  /**
902
   * Returns the name of a server given a serverCode
903
   * @param serverCode the serverid of the server
904
   * @return the servername or null if the specified serverCode does not
905
   *         exist.
906
   */
907
  public static String getServer(int serverCode)
908
  {
909
    //System.out.println("serverid: " + serverCode);
910
    Connection conn = null;
911
    try
912
    {
913
      conn = MetacatReplication.getDBConnection("MetacatReplication." +
914
                                                "getServer");
915
      String sql = new String("select server from " +
916
                              "xml_replication where serverid = " + 
917
                              serverCode);
918
      PreparedStatement pstmt = conn.prepareStatement(sql);
919
      //System.out.println("getserver sql: " + sql);
920
      pstmt.execute();
921
      ResultSet rs = pstmt.getResultSet();
922
      boolean tablehasrows = rs.next();
923
      if(tablehasrows)
924
      {
925
        //System.out.println("server: " + rs.getString(1));
926
        return rs.getString(1);
927
      }
928
      pstmt.close();
929
      conn.close();
930
    }
931
    catch(Exception e)
932
    {
933
      System.out.println("Error in MetacatReplication.getServer: " + 
934
                          e.getMessage());
935
    }
936
    return null;
937
      //return null if the server does not exist
938
  }
939
  
940
  /**
941
   * Returns a server code given a server name
942
   * @param server the name of the server
943
   * @return integer > 0 representing the code of the server, 0 if the server
944
   *  does not exist.
945
   */
946
  public static int getServerCode(String server) throws Exception
947
  {
948
    Connection conn = null;
949
    PreparedStatement pstmt = null;
950
    int serverCode = 0;
951

    
952
    try {
953

    
954
      conn = util.openDBConnection();
955
      pstmt = conn.prepareStatement("SELECT serverid FROM xml_replication " +
956
                                    "WHERE server LIKE '" + server + "'");
957
      pstmt.execute();
958
      ResultSet rs = pstmt.getResultSet();
959
      boolean tablehasrows = rs.next();
960
      if ( tablehasrows ) {  
961
        serverCode = rs.getInt(1);
962
        pstmt.close();
963
        conn.close();
964
        return serverCode;
965
      }
966
      
967
    } catch(Exception e) {
968
      throw e;
969

    
970
    } finally {
971
      try {
972
        pstmt.close();
973
        conn.close();
974
      } catch(Exception ee) {}
975
    }
976
    
977
    return serverCode;
978
  }
979
  
980
  /**
981
   * This method returns the content of a url
982
   * @param u the url to return the content from
983
   * @return a string representing the content of the url
984
   * @throws java.io.IOException
985
   */
986
  public static String getURLContent(URL u) throws java.io.IOException
987
  {
988
    //System.out.println("url: " + u.toString());
989
    char istreamChar;
990
    int istreamInt;
991
    InputStreamReader istream = new InputStreamReader(u.openStream());
992
    StringBuffer serverResponse = new StringBuffer();
993
    while((istreamInt = istream.read()) != -1)
994
    {
995
      istreamChar = (char)istreamInt;
996
      serverResponse.append(istreamChar);
997
    }
998
    
999
    return serverResponse.toString();
1000
  }
1001
  
1002
  /**
1003
   * Method for writing replication messages to a log file specified in 
1004
   * metacat.properties
1005
   */
1006
  public static void replLog(String message)
1007
  {
1008
    try
1009
    {
1010
      FileOutputStream fos = new FileOutputStream(
1011
                                 util.getOption("replicationlog"), true);
1012
      PrintWriter pw = new PrintWriter(fos);
1013
      SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
1014
      java.util.Date localtime = new java.util.Date();
1015
      String dateString = formatter.format(localtime);
1016
      dateString += " :: " + message;
1017
      //time stamp each entry
1018
      pw.println(dateString);
1019
      pw.flush();
1020
    }
1021
    catch(Exception e)
1022
    {
1023
      System.out.println("error writing to replication log from " +
1024
                         "MetacatReplication.replLog: " + e.getMessage());
1025
      //e.printStackTrace(System.out);
1026
    }
1027
  }
1028
  
1029
  /**
1030
   * Returns true if the replicate field for server in xml_replication is 1.
1031
   * Returns false otherwise
1032
   */
1033
  public static boolean replToServer(String server)
1034
  {
1035
    Connection conn = null;
1036
    PreparedStatement pstmt = null;
1037
    try
1038
    {
1039
      conn = MetacatReplication.getDBConnection("MetacatReplication.replToServer");
1040
      pstmt = conn.prepareStatement("select replicate from " + 
1041
                                    "xml_replication where server like '" +
1042
                                     server + "'");
1043
      pstmt.execute();
1044
      ResultSet rs = pstmt.getResultSet();
1045
      boolean tablehasrows = rs.next();
1046
      if(tablehasrows)
1047
      {
1048
        int i = rs.getInt(1);
1049
        if(i == 1)
1050
        {
1051
          pstmt.close();
1052
          conn.close();
1053
          return true;
1054
        }
1055
        else
1056
        {
1057
          pstmt.close();
1058
          conn.close();
1059
          return false;
1060
        }
1061
      }
1062
    }
1063
    catch(Exception e)
1064
    {
1065
      System.out.println("error in MetacatReplication.replToServer: " + 
1066
                         e.getMessage());
1067
    }
1068
    finally
1069
    {
1070
      try
1071
      {
1072
        pstmt.close();
1073
        conn.close();
1074
      }
1075
      catch(Exception ee)
1076
      {}
1077
    }
1078
    return false;
1079
    //the default if this server does not exist is to not replicate to it.
1080
  }
1081
  
1082
  /**
1083
   * A method for redundantly trying to connect.  this method will attempt to 
1084
   * connect to the DB 3 times before failing.
1085
   * @param methodname the methodname from which this method is called.
1086
   */
1087
  public static Connection getDBConnection(String methodname) throws Exception
1088
  {
1089
    Connection conn = null;
1090
    try
1091
    {
1092
      try
1093
      { //this connection is prone to error for some reason so we 
1094
        //try to connect it three times before quiting.
1095
        conn = util.openDBConnection();
1096
        return conn;
1097
      }
1098
      catch(SQLException sqle)
1099
      {
1100
        try
1101
        {
1102
          conn = util.openDBConnection();
1103
          return conn;
1104
        }
1105
        catch(SQLException sqlee)
1106
        {
1107
          try
1108
          {
1109
            conn = util.openDBConnection();
1110
            return conn;
1111
          }
1112
          catch(SQLException sqleee)
1113
          {
1114
            System.out.println("error getting db connection in " + 
1115
                               methodname +  ": " +
1116
                               sqleee.getMessage());
1117
            return conn;
1118
          }
1119
        }
1120
      }
1121
    }
1122
    catch(Exception e)
1123
    {
1124
      throw new java.lang.Exception("error in " + methodname + ": " + 
1125
                          e.getMessage());
1126
    }
1127
  }
1128
}
(32-32/41)