Project

General

Profile

1 393 jones
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents an XML document
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Matt Jones
7
 *    Release: @release@
8
 *
9
 *   '$Author$'
10
 *     '$Date$'
11
 * '$Revision$'
12
 */
13
14
package edu.ucsb.nceas.metacat;
15
16
import java.sql.*;
17 429 jones
import java.io.File;
18
import java.io.FileReader;
19
import java.io.IOException;
20 393 jones
import java.io.PrintWriter;
21 429 jones
import java.io.Reader;
22
import java.io.StringWriter;
23
import java.io.Writer;
24 561 berkley
import java.io.InputStreamReader;
25 393 jones
26 429 jones
import java.util.Iterator;
27 407 jones
import java.util.Stack;
28 429 jones
import java.util.TreeSet;
29 577 berkley
import java.util.Enumeration;
30 407 jones
31
import org.xml.sax.AttributeList;
32
import org.xml.sax.ContentHandler;
33
import org.xml.sax.DTDHandler;
34
import org.xml.sax.EntityResolver;
35
import org.xml.sax.ErrorHandler;
36
import org.xml.sax.InputSource;
37
import org.xml.sax.XMLReader;
38
import org.xml.sax.SAXException;
39
import org.xml.sax.SAXParseException;
40
import org.xml.sax.helpers.XMLReaderFactory;
41
42 561 berkley
import java.net.URL;
43
44 393 jones
/**
45
 * A class that represents an XML document. It can be created with a simple
46 407 jones
 * document identifier from a database connection.  It also will write an
47
 * XML text document to a database connection using SAX.
48 393 jones
 */
49
public class DocumentImpl {
50
51 425 bojilova
  static final int ALL = 1;
52
  static final int WRITE = 2;
53
  static final int READ = 4;
54
55 393 jones
  private Connection conn = null;
56
  private String docid = null;
57
  private String docname = null;
58
  private String doctype = null;
59 465 berkley
  private String doctitle = null;
60
  private String createdate = null;
61
  private String updatedate = null;
62 393 jones
  private String system_id = null;
63 561 berkley
  private String userowner = null;
64
  private String userupdated = null;
65 575 berkley
  private int rev;
66 561 berkley
  private int serverlocation;
67
  private int publicaccess;
68 393 jones
  private long rootnodeid;
69
  private ElementNode rootNode = null;
70 429 jones
  private TreeSet nodeRecordList = null;
71 393 jones
72
  /**
73 396 jones
   * Constructor, creates document from database connection, used
74
   * for reading the document
75 393 jones
   *
76
   * @param conn the database connection from which to read the document
77
   * @param docid the identifier of the document to be created
78
   */
79
  public DocumentImpl(Connection conn, String docid) throws McdbException
80
  {
81 465 berkley
    try {
82 393 jones
      this.conn = conn;
83
      this.docid = docid;
84 622 berkley
85
      DocumentIdentifier id = new DocumentIdentifier(docid);
86
87 393 jones
88
      // Look up the document information
89
      getDocumentInfo(docid);
90
91
      // Download all of the document nodes using a single SQL query
92 429 jones
      // The sort order of the records is determined by the NodeComparator
93
      // class, and needs to represent a depth-first traversal for the
94
      // toXml() method to work properly
95
      nodeRecordList = getNodeRecordList(rootnodeid);
96 393 jones
97
    } catch (McdbException ex) {
98
      throw ex;
99
    } catch (Throwable t) {
100
      throw new McdbException("Error reading document " + docid + ".");
101
    }
102
  }
103
104 396 jones
  /**
105 415 jones
   * Construct a new document instance, writing the contents to the database.
106
   * This method is called from DBSAXHandler because we need to know the
107
   * root element name for documents without a DOCTYPE before creating it.
108 396 jones
   *
109
   * @param conn the JDBC Connection to which all information is written
110
   * @param rootnodeid - sequence id of the root node in the document
111
   * @param docname - the name of DTD, i.e. the name immediately following
112
   *        the DOCTYPE keyword ( should be the root element name ) or
113
   *        the root element name if no DOCTYPE declaration provided
114
   *        (Oracle's and IBM parsers are not aware if it is not the
115
   *        root element name)
116
   * @param doctype - Public ID of the DTD, i.e. the name immediately
117
   *                  following the PUBLIC keyword in DOCTYPE declaration or
118
   *                  the docname if no Public ID provided or
119
   *                  null if no DOCTYPE declaration provided
120
   *
121
   */
122
  public DocumentImpl(Connection conn, long rootnodeid, String docname,
123 549 berkley
                      String doctype, String docid, String action, String user,
124
                      int serverCode)
125
                      throws SQLException, Exception
126
  {
127
    this.conn = conn;
128
    this.rootnodeid = rootnodeid;
129
    this.docname = docname;
130
    this.doctype = doctype;
131
    this.docid = docid;
132
    writeDocumentToDB(action, user, serverCode);
133
  }
134
135
  public DocumentImpl(Connection conn, long rootnodeid, String docname,
136 425 bojilova
                      String doctype, String docid, String action, String user)
137 457 bojilova
                      throws SQLException, Exception
138 396 jones
  {
139
    this.conn = conn;
140
    this.rootnodeid = rootnodeid;
141
    this.docname = docname;
142
    this.doctype = doctype;
143
    this.docid = docid;
144 425 bojilova
    writeDocumentToDB(action, user);
145 396 jones
  }
146 407 jones
147 393 jones
  /**
148
   * get the document name
149
   */
150
  public String getDocname() {
151
    return docname;
152
  }
153
154
  /**
155
   * get the document type (which is the PublicID)
156
   */
157
  public String getDoctype() {
158
    return doctype;
159
  }
160
161
  /**
162
   * get the system identifier
163
   */
164
  public String getSystemID() {
165
    return system_id;
166
  }
167
168
  /**
169
   * get the root node identifier
170
   */
171
  public long getRootNodeID() {
172
    return rootnodeid;
173
  }
174 465 berkley
175
  /**
176
   * get the creation date
177
   */
178
  public String getCreateDate() {
179
    return createdate;
180
  }
181
182
  /**
183
   * get the update date
184
   */
185
  public String getUpdateDate() {
186
    return updatedate;
187
  }
188 393 jones
189 396 jones
  /**
190
   * Get the document identifier (docid)
191
   */
192
  public String getDocID() {
193
    return docid;
194
  }
195 465 berkley
196
  /**
197
   *get the document title
198
   */
199
  public String getDocTitle() {
200
    return doctitle;
201
  }
202 561 berkley
203
  public String getUserowner() {
204
    return userowner;
205
  }
206
207
  public String getUserupdated() {
208
    return userupdated;
209
  }
210
211
  public int getServerlocation() {
212
    return serverlocation;
213
  }
214
215
  public int getPublicaccess() {
216
    return publicaccess;
217
  }
218 575 berkley
219
  public int getRev() {
220
    return rev;
221
  }
222 396 jones
223 393 jones
  /**
224 429 jones
   * Print a string representation of the XML document
225 393 jones
   */
226
  public String toString()
227
  {
228 429 jones
    StringWriter docwriter = new StringWriter();
229
    this.toXml(docwriter);
230
    String document = docwriter.toString();
231
    return document;
232
  }
233
234
  /**
235
   * Get a text representation of the XML document as a string
236
   * This older algorithm uses a recursive tree of Objects to represent the
237
   * nodes of the tree.  Each object is passed the data for the document
238
   * and searches all of the document data to find its children nodes and
239
   * recursively build.  Thus, because each node reads the whole document,
240
   * this algorithm is extremely slow for larger documents, and the time
241
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
242
   * better algorithm.
243
   */
244
  public String readUsingSlowAlgorithm()
245
  {
246 393 jones
    StringBuffer doc = new StringBuffer();
247
248 429 jones
    // Create the elements from the downloaded data in the TreeSet
249
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
250
251 393 jones
    // Append the resulting document to the StringBuffer and return it
252
    doc.append("<?xml version=\"1.0\"?>\n");
253
254
    if (docname != null) {
255
      if ((doctype != null) && (system_id != null)) {
256
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
257
                   "\" \"" + system_id + "\">\n");
258
      } else {
259
        doc.append("<!DOCTYPE " + docname + ">\n");
260
      }
261
    }
262
    doc.append(rootNode.toString());
263
264
    return (doc.toString());
265
  }
266
267
  /**
268 429 jones
   * Print a text representation of the XML document to a Writer
269
   *
270
   * @param pw the Writer to which we print the document
271
   */
272
  public void toXml(Writer pw)
273
  {
274
    PrintWriter out = null;
275
    if (pw instanceof PrintWriter) {
276
      out = (PrintWriter)pw;
277
    } else {
278
      out = new PrintWriter(pw);
279
    }
280
281
    MetaCatUtil util = new MetaCatUtil();
282
283
    Stack openElements = new Stack();
284
    boolean atRootElement = true;
285
    boolean previousNodeWasElement = false;
286
287
    // Step through all of the node records we were given
288
    Iterator it = nodeRecordList.iterator();
289
    while (it.hasNext()) {
290
      NodeRecord currentNode = (NodeRecord)it.next();
291
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
292
                          //" (" + currentNode.parentnodeid +
293
                          //", " + currentNode.nodeindex +
294
                          //", " + currentNode.nodetype +
295
                          //", " + currentNode.nodename +
296
                          //", " + currentNode.nodedata + ")]");
297
298
      // Print the end tag for the previous node if needed
299
      //
300
      // This is determined by inspecting the parent nodeid for the
301
      // currentNode.  If it is the same as the nodeid of the last element
302
      // that was pushed onto the stack, then we are still in that previous
303
      // parent element, and we do nothing.  However, if it differs, then we
304
      // have returned to a level above the previous parent, so we go into
305
      // a loop and pop off nodes and print out their end tags until we get
306
      // the node on the stack to match the currentNode parentnodeid
307
      //
308
      // So, this of course means that we rely on the list of elements
309
      // having been sorted in a depth first traversal of the nodes, which
310
      // is handled by the NodeComparator class used by the TreeSet
311
      if (!atRootElement) {
312
        NodeRecord currentElement = (NodeRecord)openElements.peek();
313
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
314
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
315
            currentElement = (NodeRecord)openElements.pop();
316
            util.debugMessage("\n POPPED: " + currentElement.nodename);
317 451 bojilova
            if (previousNodeWasElement) {
318
              out.print(">");
319
              previousNodeWasElement = false;
320
            }
321 429 jones
            out.print("</" + currentElement.nodename + ">" );
322
            currentElement = (NodeRecord)openElements.peek();
323
          }
324
        }
325
      }
326
327
      // Handle the DOCUMENT node
328
      if (currentNode.nodetype.equals("DOCUMENT")) {
329
        out.println("<?xml version=\"1.0\"?>");
330
331
        if (docname != null) {
332
          if ((doctype != null) && (system_id != null)) {
333
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
334
                       "\" \"" + system_id + "\">");
335
          } else {
336
            out.println("<!DOCTYPE " + docname + ">");
337
          }
338
        }
339
340
      // Handle the ELEMENT nodes
341
      } else if (currentNode.nodetype.equals("ELEMENT")) {
342
        if (atRootElement) {
343
          atRootElement = false;
344
        } else {
345
          if (previousNodeWasElement) {
346
            out.print(">");
347
          }
348
        }
349
        openElements.push(currentNode);
350
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
351
        previousNodeWasElement = true;
352
        out.print("<" + currentNode.nodename);
353
354
      // Handle the ATTRIBUTE nodes
355
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
356
        out.print(" " + currentNode.nodename + "=\""
357
                 + currentNode.nodedata + "\"");
358
      } else if (currentNode.nodetype.equals("TEXT")) {
359
        if (previousNodeWasElement) {
360
          out.print(">");
361
        }
362
        out.print(currentNode.nodedata);
363
        previousNodeWasElement = false;
364
365
      // Handle the COMMENT nodes
366
      } else if (currentNode.nodetype.equals("COMMENT")) {
367
        if (previousNodeWasElement) {
368
          out.print(">");
369
        }
370
        out.print("<!--" + currentNode.nodedata + "-->");
371
        previousNodeWasElement = false;
372
373
      // Handle the PI nodes
374
      } else if (currentNode.nodetype.equals("PI")) {
375
        if (previousNodeWasElement) {
376
          out.print(">");
377
        }
378
        out.print("<?" + currentNode.nodename + " " +
379
                        currentNode.nodedata + "?>");
380
        previousNodeWasElement = false;
381
382
      // Handle any other node type (do nothing)
383
      } else {
384
        // Any other types of nodes are not handled.
385
        // Probably should throw an exception here to indicate this
386
      }
387
      out.flush();
388
    }
389
390
    // Print the final end tag for the root element
391
    NodeRecord currentElement = (NodeRecord)openElements.pop();
392
    util.debugMessage("\n POPPED: " + currentElement.nodename);
393
    out.print("</" + currentElement.nodename + ">" );
394
    out.flush();
395
  }
396 622 berkley
397
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
398
  {
399
    //System.out.println("inRevisionOnly");
400
    PreparedStatement pstmt;
401
    String rev = docid.getRev();
402
    String newid = docid.getIdentifier();
403
    pstmt = conn.prepareStatement("select rev from xml_documents " +
404
                                  "where docid like '" + newid + "'");
405
    pstmt.execute();
406
    ResultSet rs = pstmt.getResultSet();
407
    boolean tablehasrows = rs.next();
408
    if(rev.equals("newest") || rev.equals("all"))
409
    {
410
      return false;
411
    }
412
413
    if(tablehasrows)
414
    {
415
      int r = rs.getInt(1);
416
      if(new Integer(rev).intValue() == r)
417
      { //the current revision in in xml_documents
418
        //System.out.println("returning false");
419
        return false;
420
      }
421
      else if(new Integer(rev).intValue() < r)
422
      { //the current revision is in xml_revisions.
423
        //System.out.println("returning true");
424
        return true;
425
      }
426
      else if(new Integer(rev).intValue() > r)
427
      { //error, rev cannot be greater than r
428
        throw new Exception("requested revision cannot be greater than " +
429
                            "the latest revision number.");
430
      }
431
    }
432
    throw new Exception("the requested docid '" + docid.toString() +
433
                        "' does not exist");
434
  }
435 429 jones
436 622 berkley
  private void getDocumentInfo(String docid) throws McdbException,
437
                                                    AccessionNumberException
438
  {
439
    getDocumentInfo(new DocumentIdentifier(docid));
440
  }
441
442 429 jones
  /**
443 393 jones
   * Look up the document type information from the database
444
   *
445
   * @param docid the id of the document to look up
446
   */
447 622 berkley
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException
448 393 jones
  {
449
    PreparedStatement pstmt;
450 622 berkley
    String table = "xml_documents";
451
452
    try
453
    {
454
      if(isRevisionOnly(docid))
455
      { //pull the document from xml_revisions instead of from xml_documents;
456
        table = "xml_revisions";
457
      }
458
    }
459
    catch(Exception e)
460
    {
461
      System.out.println("error in getDocumentInfo: " + e.getMessage());
462
    }
463
464
    //deal with the key words here.
465
466
    if(docid.getRev().equals("all"))
467
    {
468
469
    }
470
471 393 jones
    try {
472 622 berkley
      StringBuffer sql = new StringBuffer();
473
      sql.append("SELECT docname, doctype, rootnodeid,doctitle, ");
474
      sql.append("date_created, date_updated, ");
475
      sql.append("user_owner, user_updated, server_location, ");
476
      sql.append("rev FROM ").append(table);
477
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
478
      sql.append("' and rev like '").append(docid.getRev()).append("'");
479
      //System.out.println(sql.toString());
480 393 jones
      pstmt =
481 622 berkley
        conn.prepareStatement(sql.toString());
482 393 jones
      // Bind the values to the query
483 622 berkley
      //pstmt.setString(1, docid.getIdentifier());
484
      //pstmt.setString(2, docid.getRev());
485 393 jones
486
      pstmt.execute();
487
      ResultSet rs = pstmt.getResultSet();
488
      boolean tableHasRows = rs.next();
489
      if (tableHasRows) {
490 561 berkley
        this.docname        = rs.getString(1);
491
        this.doctype        = rs.getString(2);
492
        this.rootnodeid     = rs.getLong(3);
493
        this.doctitle       = rs.getString(4);
494
        this.createdate     = rs.getString(5);
495
        this.updatedate     = rs.getString(6);
496
        this.userowner      = rs.getString(7);
497
        this.userupdated    = rs.getString(8);
498
        this.serverlocation = rs.getInt(9);
499 622 berkley
        //this.publicaccess   = rs.getInt(10);
500
        this.rev            = rs.getInt(10);
501 393 jones
      }
502
      pstmt.close();
503
504
      if (this.doctype != null) {
505
        pstmt =
506
          conn.prepareStatement("SELECT system_id " +
507
                                  "FROM xml_catalog " +
508 447 bojilova
                                 "WHERE public_id LIKE ?");
509 393 jones
        // Bind the values to the query
510
        pstmt.setString(1, doctype);
511
512
        pstmt.execute();
513
        rs = pstmt.getResultSet();
514
        tableHasRows = rs.next();
515
        if (tableHasRows) {
516
          this.system_id  = rs.getString(1);
517
        }
518
        pstmt.close();
519
      }
520
    } catch (SQLException e) {
521 622 berkley
      System.out.println("error in getDocumentInfo: " + e.getMessage());
522
      e.printStackTrace(System.out);
523 393 jones
      throw new McdbException("Error accessing database connection.", e);
524
    }
525
526
    if (this.docname == null) {
527
      throw new McdbDocNotFoundException("Document not found: " + docid);
528
    }
529
  }
530
531
  /**
532
   * Look up the node data from the database
533
   *
534 396 jones
   * @param rootnodeid the id of the root node of the node tree to look up
535 393 jones
   */
536 396 jones
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException
537 393 jones
  {
538
    PreparedStatement pstmt;
539
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
540
    long nodeid = 0;
541
    long parentnodeid = 0;
542
    long nodeindex = 0;
543
    String nodetype = null;
544
    String nodename = null;
545
    String nodedata = null;
546
547
    try {
548
      pstmt =
549
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
550
           "nodetype,nodename,"+
551
           "replace(" +
552
           "replace(" +
553
           "replace(nodedata,'&','&amp;') " +
554
           ",'<','&lt;') " +
555
           ",'>','&gt;') " +
556 396 jones
           "FROM xml_nodes WHERE rootnodeid = ?");
557 393 jones
558
      // Bind the values to the query
559 396 jones
      pstmt.setLong(1, rootnodeid);
560 393 jones
561
      pstmt.execute();
562
      ResultSet rs = pstmt.getResultSet();
563
      boolean tableHasRows = rs.next();
564
      while (tableHasRows) {
565
        nodeid = rs.getLong(1);
566
        parentnodeid = rs.getLong(2);
567
        nodeindex = rs.getLong(3);
568
        nodetype = rs.getString(4);
569
        nodename = rs.getString(5);
570
        nodedata = rs.getString(6);
571
572
        // add the data to the node record list hashtable
573
        NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid,
574
                                   nodeindex, nodetype, nodename, nodedata);
575
        nodeRecordList.add(currentRecord);
576
577
        // Advance to the next node
578
        tableHasRows = rs.next();
579
      }
580
      pstmt.close();
581
582
    } catch (SQLException e) {
583
      throw new McdbException("Error accessing database connection.", e);
584
    }
585
586
    if (nodeRecordList != null) {
587
      return nodeRecordList;
588
    } else {
589
      throw new McdbException("Error getting node data: " + docid);
590
    }
591
  }
592 549 berkley
593
  /** creates SQL code and inserts new document into DB connection
594
   default serverCode of 1*/
595
  private void writeDocumentToDB(String action, String user)
596
               throws SQLException, Exception
597
  {
598
    writeDocumentToDB(action, user, 1);
599
  }
600 393 jones
601 459 bojilova
 /** creates SQL code and inserts new document into DB connection */
602 549 berkley
  private void writeDocumentToDB(String action, String user, int serverCode)
603 459 bojilova
               throws SQLException, Exception {
604
    try {
605
      PreparedStatement pstmt = null;
606
607
      if (action.equals("INSERT")) {
608
        //AccessionNumber ac = new AccessionNumber();
609
        //this.docid = ac.generate(docid, "INSERT");
610
        pstmt = conn.prepareStatement(
611
            "INSERT INTO xml_documents " +
612 549 berkley
            "(docid, rootnodeid, docname, doctype, user_owner, " +
613
            "user_updated, date_created, date_updated, server_location) " +
614
            "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate, ?)");
615
        //note that the server_location is set to 1.
616
        //this means that "localhost" in the xml_replication table must
617
        //always be the first entry!!!!!
618
619 459 bojilova
        // Bind the values to the query
620
        pstmt.setString(1, this.docid);
621
        pstmt.setLong(2, rootnodeid);
622
        pstmt.setString(3, docname);
623
        pstmt.setString(4, doctype);
624
        pstmt.setString(5, user);
625
        pstmt.setString(6, user);
626 549 berkley
        pstmt.setInt(7, serverCode);
627 459 bojilova
      } else if (action.equals("UPDATE")) {
628
629
        // Save the old document entry in a backup table
630
        DocumentImpl.archiveDocRevision( conn, docid, user );
631 575 berkley
        DocumentImpl thisdoc = new DocumentImpl(conn, docid);
632
        int thisrev = thisdoc.getRev();
633
        thisrev++;
634 459 bojilova
        // Delete index for the old version of docid
635
        // The new index is inserting on the next calls to DBSAXNode
636
        pstmt = conn.prepareStatement(
637
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
638
        pstmt.execute();
639
        pstmt.close();
640
641
        // Update the new document to reflect the new node tree
642
        pstmt = conn.prepareStatement(
643
            "UPDATE xml_documents " +
644
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
645 549 berkley
            "user_updated = ?, date_updated = sysdate, " +
646 575 berkley
            "server_location = ?, rev = ? WHERE docid LIKE ?");
647 459 bojilova
        // Bind the values to the query
648
        pstmt.setLong(1, rootnodeid);
649
        pstmt.setString(2, docname);
650
        pstmt.setString(3, doctype);
651
        pstmt.setString(4, user);
652 549 berkley
        pstmt.setInt(5, serverCode);
653 575 berkley
        pstmt.setInt(6, thisrev);
654
        pstmt.setString(7, this.docid);
655
656 459 bojilova
      } else {
657
        System.err.println("Action not supported: " + action);
658
      }
659
660
      // Do the insertion
661
      pstmt.execute();
662
      pstmt.close();
663
664
    } catch (SQLException sqle) {
665
      throw sqle;
666
    } catch (Exception e) {
667
      throw e;
668
    }
669
  }
670
671 393 jones
  /**
672 459 bojilova
   * Get the document title
673
   */
674
  public String getTitle() {
675
    return doctitle;
676
  }
677
678
  /**
679
   * Set the document title
680
   *
681
   * @param title the new title for the document
682
   */
683 465 berkley
684 459 bojilova
  public void setTitle( String title ) {
685
    this.doctitle = title;
686
    try {
687
      PreparedStatement pstmt;
688
      pstmt = conn.prepareStatement(
689
            "UPDATE xml_documents " +
690
            " SET doctitle = ? " +
691
            "WHERE docid = ?");
692
693
      // Bind the values to the query
694
      pstmt.setString(1, doctitle);
695
      pstmt.setString(2, docid);
696
697
      // Do the insertion
698
      pstmt.execute();
699
      pstmt.close();
700
    } catch (SQLException e) {
701
      System.out.println(e.getMessage());
702
    }
703
  }
704
705
  /**
706 407 jones
   * Write an XML file to the database, given a filename
707 393 jones
   *
708 408 jones
   * @param conn the JDBC connection to the database
709 407 jones
   * @param filename the filename to be loaded into the database
710
   * @param action the action to be performed (INSERT OR UPDATE)
711
   * @param docid the docid to use for the INSERT OR UPDATE
712 393 jones
   */
713 598 bojilova
  public static String write(Connection conn,String filename,
714
                             String aclfilename,String dtdfilename,
715
                             String action, String docid, String user,
716
                             String group )
717 457 bojilova
                throws Exception {
718 598 bojilova
719
    Reader acl = null;
720
    if ( aclfilename != null ) {
721
      acl = new FileReader(new File(aclfilename).toString());
722
    }
723
    Reader dtd = null;
724
    if ( dtdfilename != null ) {
725
      dtd = new FileReader(new File(dtdfilename).toString());
726
    }
727 555 bojilova
    return write ( conn, new FileReader(new File(filename).toString()),
728 598 bojilova
                   acl, dtd, action, docid, user, group);
729 407 jones
  }
730 598 bojilova
731
  public static String write(Connection conn,Reader xml,Reader acl,Reader dtd,
732
                             String action, String docid, String user,
733
                             String group )
734 549 berkley
                throws Exception {
735 598 bojilova
    return write ( conn, xml, acl, dtd, action, docid, user, group, 1, false);
736
  }
737
738
  public static String write(Connection conn,Reader xml,Reader acl,
739
                             String action, String docid, String user,
740
                             String group )
741
                throws Exception {
742 571 berkley
    if(action.equals("UPDATE"))
743
    {//if the document is being updated then use the servercode from the
744
     //originally inserted document.
745
      DocumentImpl doc = new DocumentImpl(conn, docid);
746
      int servercode = doc.getServerlocation();
747 598 bojilova
      return write(conn,xml,acl,action,docid,user,group,servercode);
748 571 berkley
    }
749
    else
750
    {//if the file is being inserted then the servercode is always 1
751
      return write(conn, xml, acl, action, docid, user, group, 1);
752
    }
753 549 berkley
  }
754
755 559 berkley
  public static String write( Connection conn, Reader xml,
756
                              String action, String docid, String user,
757
                              String group, int serverCode )
758 598 bojilova
                throws Exception
759 559 berkley
  {
760 598 bojilova
    return write(conn,xml,null,action,docid,user,group,serverCode);
761 559 berkley
  }
762
763 598 bojilova
  public static String write( Connection conn,Reader xml,Reader acl,
764 577 berkley
                              String action, String docid, String user,
765 598 bojilova
                              String group, int serverCode)
766
                throws Exception
767 577 berkley
  {
768 598 bojilova
    return write(conn,xml,acl,null,action,docid,user,group,serverCode,false);
769 577 berkley
  }
770
771 600 bojilova
  public static String write( Connection conn,Reader xml,Reader acl,
772
                              String action, String docid, String user,
773
                              String group, int serverCode, boolean override)
774
                throws Exception
775
  {
776
    return write(conn,xml,acl,null,action,docid,user,group,serverCode,override);
777
  }
778
779 407 jones
  /**
780
   * Write an XML file to the database, given a Reader
781
   *
782 408 jones
   * @param conn the JDBC connection to the database
783 407 jones
   * @param xml the xml stream to be loaded into the database
784
   * @param action the action to be performed (INSERT OR UPDATE)
785
   * @param docid the docid to use for the INSERT OR UPDATE
786 580 berkley
   * @param user the user that owns the document
787
   * @param group the group to which user belongs
788
   * @param serverCode the serverid from xml_replication on which this document
789
   *        resides.
790
   * @param override flag to stop insert replication checking.
791
   *        if override = true then a document not belonging to the local server
792
   *        will not be checked upon update for a file lock.
793
   *        if override = false then a document not from this server, upon
794
   *        update will be locked and version checked.
795 407 jones
   */
796 559 berkley
797 598 bojilova
  public static String write( Connection conn,Reader xml,Reader acl,Reader dtd,
798 555 bojilova
                              String action, String docid, String user,
799 577 berkley
                              String group, int serverCode, boolean override)
800 598 bojilova
                throws Exception
801 573 berkley
  {
802 634 berkley
    if(action.equals("UPDATE"))
803
    {
804
      RelationHandler.deleteRelations(docid);
805
    }
806
807 628 berkley
    int updaterev;
808 573 berkley
    MetaCatUtil util = new MetaCatUtil();
809 571 berkley
        // Determine if the docid is OK for INSERT or UPDATE
810
    AccessionNumber ac = new AccessionNumber(conn);
811
    String newdocid = ac.generate(docid, action);
812 569 berkley
813 590 berkley
    MetaCatUtil.debugMessage("action: " + action + " servercode: " +
814
                             serverCode + " override: " + override);
815 580 berkley
816 577 berkley
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
817 559 berkley
    { //if this document being written is not a resident of this server then
818
      //we need to try to get a lock from it's resident server.  If the
819
      //resident server will not give a lock then we send the user a message
820
      //saying that he/she needs to download a new copy of the file and
821
      //merge the differences manually.
822 567 berkley
      int istreamInt;
823 561 berkley
      char istreamChar;
824
      DocumentImpl newdoc = new DocumentImpl(conn, docid);
825 628 berkley
      updaterev = newdoc.getRev();
826 561 berkley
      String server = MetacatReplication.getServer(serverCode);
827 584 berkley
      MetacatReplication.replLog("attempting to lock " + docid);
828 580 berkley
      URL u = new URL("http://" + server + "?action=getlock&updaterev=" +
829
                      updaterev + "&docid=" + docid);
830 574 berkley
      System.out.println("sending message: " + u.toString());
831 569 berkley
      String serverResStr = MetacatReplication.getURLContent(u);
832 567 berkley
      String openingtag = serverResStr.substring(0, serverResStr.indexOf(">")+1);
833 571 berkley
834 561 berkley
      if(openingtag.equals("<lockgranted>"))
835
      {//the lock was granted go ahead with the insert
836 571 berkley
        try
837
        {
838 584 berkley
          MetacatReplication.replLog("lock granted for " + docid + " from " +
839
                                      server);
840 598 bojilova
          XMLReader parser = initializeParser(conn, action, newdocid, user,
841
                                              serverCode, dtd);
842 571 berkley
          conn.setAutoCommit(false);
843
          parser.parse(new InputSource(xml));
844
          conn.commit();
845 638 bojilova
846
          // if acltext is provided for @xml, store acl info into db
847 571 berkley
          if ( acl != null )
848
          {
849 638 bojilova
            AccessControlList aclobj=new AccessControlList(conn,null,newdocid,acl);
850 571 berkley
            conn.commit();
851 572 berkley
          }
852 638 bojilova
853 571 berkley
          conn.setAutoCommit(true);
854
        }
855
        catch (Exception e)
856
        {
857
          conn.rollback();
858
          conn.setAutoCommit(true);
859
          throw e;
860
        }
861
862
        //after inserting the document locally, tell the document's home server
863
        //to come get a copy from here.
864 582 berkley
        ForceReplicationHandler frh = new ForceReplicationHandler(docid);
865 590 berkley
866 571 berkley
        if ( (docid != null) && !(newdocid.equals(docid)) )
867
        {
868
          return new String("New document ID generated:" + newdocid);
869
        }
870
        else
871
        {
872
          return newdocid;
873
        }
874 561 berkley
      }
875
      else if(openingtag.equals("<filelocked>"))
876
      {//the file is currently locked by another user
877
       //notify our user to wait a few minutes, check out a new copy and try
878
       //again.
879 584 berkley
        //System.out.println("file locked");
880
        MetacatReplication.replLog("lock denied for " + docid + " on " +
881
                                   server + " reason: file already locked");
882 571 berkley
        throw new Exception("The file specified is already locked by another " +
883
                            "user.  Please wait 30 seconds, checkout the " +
884
                            "newer document, merge your changes and try " +
885
                            "again.");
886 561 berkley
      }
887
      else if(openingtag.equals("<outdatedfile>"))
888
      {//our file is outdated.  notify our user to check out a new copy of the
889
       //file and merge his version with the new version.
890 584 berkley
        //System.out.println("outdated file");
891
        MetacatReplication.replLog("lock denied for " + docid + " on " +
892
                                    server + " reason: local file outdated");
893 571 berkley
        throw new Exception("The file you are trying to update is an outdated" +
894
                            " version.  Please checkout the newest document, " +
895
                            "merge your changes and try again.");
896 561 berkley
      }
897 559 berkley
    }
898 571 berkley
899 425 bojilova
    if ( action.equals("UPDATE") ) {
900 441 bojilova
      // check for 'write' permission for 'user' to update this document
901 628 berkley
902 570 bojilova
      if ( !hasPermission(conn, user, group, docid) ) {
903 441 bojilova
        throw new Exception("User " + user +
904
              " does not have permission to update XML Document #" + docid);
905 425 bojilova
      }
906
    }
907
908 571 berkley
    try
909 638 bojilova
    {
910 598 bojilova
      XMLReader parser=initializeParser(conn,action,newdocid,user,serverCode,dtd);
911 555 bojilova
      conn.setAutoCommit(false);
912
      parser.parse(new InputSource(xml));
913
      conn.commit();
914 638 bojilova
915
      // if acltext is provided for @xml, store acl info into db
916 571 berkley
      if ( acl != null )
917
      {
918 638 bojilova
        AccessControlList aclobj=new AccessControlList(conn,null,newdocid,acl);
919 407 jones
        conn.commit();
920 638 bojilova
      }
921
922 555 bojilova
      conn.setAutoCommit(true);
923 571 berkley
924
    }
925
    catch (Exception e)
926
    {
927 555 bojilova
      conn.rollback();
928
      conn.setAutoCommit(true);
929
      throw e;
930
    }
931 577 berkley
932
    //force replicate out the new document to each server in our server list.
933
    if(serverCode == 1)
934 582 berkley
    { //start the thread to replicate this new document out to the other servers
935
      ForceReplicationHandler frh = new ForceReplicationHandler(newdocid,
936
                                                                action);
937 577 berkley
    }
938 457 bojilova
939 571 berkley
    if ( (docid != null) && !(newdocid.equals(docid)) )
940
    {
941 555 bojilova
      return new String("New document ID generated:" + newdocid);
942 571 berkley
    }
943
    else
944
    {
945 555 bojilova
      return newdocid;
946
    }
947 393 jones
  }
948 396 jones
949 407 jones
  /**
950
   * Delete an XML file from the database (actually, just make it a revision
951
   * in the xml_revisions table)
952
   *
953
   * @param docid the ID of the document to be deleted from the database
954
   */
955 425 bojilova
  public static void delete( Connection conn, String docid,
956
                                 String user, String group )
957 457 bojilova
                throws Exception {
958 628 berkley
    DocumentIdentifier id = new DocumentIdentifier(docid);
959
    docid = id.getSiteCode() + id.getSeparator() + id.getUniqueId();
960
961 441 bojilova
    // Determine if the docid is OK for DELETE
962 459 bojilova
    AccessionNumber ac = new AccessionNumber(conn);
963 421 bojilova
    String newdocid = ac.generate(docid, "DELETE");
964 396 jones
965 441 bojilova
    // check for 'write' permission for 'user' to delete this document
966 570 bojilova
    if ( !hasPermission(conn, user, group, docid) ) {
967 441 bojilova
      throw new Exception("User " + user +
968
              " does not have permission to delete XML Document #" + docid);
969 425 bojilova
    }
970
971 407 jones
    conn.setAutoCommit(false);
972
    // Copy the record to the xml_revisions table
973 425 bojilova
    DocumentImpl.archiveDocRevision( conn, docid, user );
974 396 jones
975 407 jones
    // Now delete it from the xml_documents table
976 628 berkley
977 407 jones
    Statement stmt = conn.createStatement();
978 425 bojilova
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
979
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
980 407 jones
    stmt.close();
981
    conn.commit();
982 457 bojilova
    conn.setAutoCommit(true);
983 634 berkley
    //IF this is a package document:
984
    //delete all of the relations that this document created.
985
    //if the deleted document is a package document its relations should
986
    //no longer be active if it has been deleted from the system.
987
    RelationHandler.deleteRelations(docid);
988 407 jones
  }
989 638 bojilova
990 570 bojilova
  /**
991
    * Check for "WRITE" permission on @docid for @user and/or @group
992
    * from DB connection
993
    */
994
  private static boolean hasPermission( Connection conn, String user,
995
                                        String group, String docid)
996
                         throws SQLException
997
  {
998 441 bojilova
    // b' of the command line invocation
999
    if ( (user == null) && (group == null) ) {
1000
      return true;
1001
    }
1002
1003 570 bojilova
    // Check for WRITE permission on @docid for @user and/or @group
1004
    AccessControlList aclobj = new AccessControlList(conn);
1005
    boolean hasPermission = aclobj.hasPermission("WRITE",user,docid);
1006
    if ( !hasPermission && group != null ) {
1007
      hasPermission = aclobj.hasPermission("WRITE",group,docid);
1008 425 bojilova
    }
1009 570 bojilova
1010
    return hasPermission;
1011 425 bojilova
  }
1012
1013 396 jones
  /**
1014 407 jones
   * Set up the parser handlers for writing the document to the database
1015
   */
1016 598 bojilova
  private static XMLReader initializeParser(Connection conn, String action,
1017
                                            String docid, String user,
1018
                                            int serverCode, Reader dtd)
1019
                           throws Exception
1020
  {
1021 407 jones
    XMLReader parser = null;
1022
    //
1023
    // Set up the SAX document handlers for parsing
1024
    //
1025
    try {
1026 598 bojilova
      ContentHandler chandler = new DBSAXHandler(conn,action,docid,user,serverCode);
1027
      EntityResolver eresolver= new DBEntityResolver(conn,(DBSAXHandler)chandler,dtd);
1028
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1029 407 jones
1030
      // Get an instance of the parser
1031 408 jones
      MetaCatUtil util = new MetaCatUtil();
1032
      String parserName = util.getOption("saxparser");
1033 407 jones
      parser = XMLReaderFactory.createXMLReader(parserName);
1034
1035
      // Turn off validation
1036
      parser.setFeature("http://xml.org/sax/features/validation", false);
1037
1038
      // Set Handlers in the parser
1039
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
1040
                         chandler);
1041
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
1042
                         chandler);
1043
      parser.setContentHandler((ContentHandler)chandler);
1044 598 bojilova
      parser.setEntityResolver((EntityResolver)eresolver);
1045 407 jones
      parser.setDTDHandler((DTDHandler)dtdhandler);
1046
      parser.setErrorHandler((ErrorHandler)chandler);
1047
1048
    } catch (Exception e) {
1049 457 bojilova
      throw e;
1050 407 jones
    }
1051
1052
    return parser;
1053
  }
1054
1055 459 bojilova
  /** Save a document entry in the xml_revisions table */
1056
  private static void archiveDocRevision(Connection conn, String docid,
1057 552 berkley
                                         String user)
1058
                                         throws SQLException {
1059 459 bojilova
    // create a record in xml_revisions table
1060
    // for that document as selected from xml_documents
1061
    PreparedStatement pstmt = conn.prepareStatement(
1062
      "INSERT INTO xml_revisions " +
1063
        "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
1064 575 berkley
        "user_owner, user_updated, date_created, date_updated, server_location, " +
1065
        "rev)" +
1066 459 bojilova
      "SELECT null, ?, rootnodeid, docname, doctype, doctitle," +
1067 575 berkley
        "user_owner, ?, sysdate, sysdate, server_location, rev "+
1068 459 bojilova
      "FROM xml_documents " +
1069
      "WHERE docid = ?");
1070
    // Bind the values to the query and execute it
1071
    pstmt.setString(1, docid);
1072
    pstmt.setString(2, user);
1073
    pstmt.setString(3, docid);
1074
    pstmt.execute();
1075
    pstmt.close();
1076
1077
  }
1078
1079 407 jones
  /**
1080
   * the main routine used to test the DBWriter utility.
1081
   * <p>
1082
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1083
   *
1084
   * @param filename the filename to be loaded into the database
1085
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1086
   * @param docid the id of the document to process
1087
   */
1088
  static public void main(String[] args) {
1089
1090
    try {
1091 555 bojilova
      String filename    = null;
1092
      String aclfilename = null;
1093 598 bojilova
      String dtdfilename = null;
1094 555 bojilova
      String action      = null;
1095
      String docid       = null;
1096 429 jones
      boolean showRuntime = false;
1097
      boolean useOldReadAlgorithm = false;
1098 407 jones
1099 408 jones
      // Parse the command line arguments
1100 407 jones
      for ( int i=0 ; i < args.length; ++i ) {
1101
        if ( args[i].equals( "-f" ) ) {
1102
          filename =  args[++i];
1103 555 bojilova
        } else if ( args[i].equals( "-c" ) ) {
1104
          aclfilename =  args[++i];
1105 598 bojilova
        } else if ( args[i].equals( "-r" ) ) {
1106
          dtdfilename =  args[++i];
1107 407 jones
        } else if ( args[i].equals( "-a" ) ) {
1108
          action =  args[++i];
1109
        } else if ( args[i].equals( "-d" ) ) {
1110
          docid =  args[++i];
1111 429 jones
        } else if ( args[i].equals( "-t" ) ) {
1112
          showRuntime = true;
1113
        } else if ( args[i].equals( "-old" ) ) {
1114
          useOldReadAlgorithm = true;
1115 407 jones
        } else {
1116
          System.err.println
1117
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
1118
        }
1119
      }
1120
1121 408 jones
      // Check if the required arguments are provided
1122 407 jones
      boolean argsAreValid = false;
1123
      if (action != null) {
1124
        if (action.equals("INSERT")) {
1125
          if (filename != null) {
1126
            argsAreValid = true;
1127
          }
1128
        } else if (action.equals("UPDATE")) {
1129
          if ((filename != null) && (docid != null)) {
1130
            argsAreValid = true;
1131
          }
1132
        } else if (action.equals("DELETE")) {
1133
          if (docid != null) {
1134
            argsAreValid = true;
1135
          }
1136
        } else if (action.equals("READ")) {
1137
          if (docid != null) {
1138
            argsAreValid = true;
1139
          }
1140
        }
1141
      }
1142
1143 408 jones
      // Print usage message if the arguments are not valid
1144 407 jones
      if (!argsAreValid) {
1145
        System.err.println("Wrong number of arguments!!!");
1146
        System.err.println(
1147 598 bojilova
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
1148
          "[-c aclfilename] [-r dtdfilename]");
1149 407 jones
        System.err.println(
1150 598 bojilova
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
1151
          "[-c aclfilename] [-r dtdfilename]");
1152 407 jones
        System.err.println(
1153 429 jones
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
1154 407 jones
        System.err.println(
1155 429 jones
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
1156 407 jones
        return;
1157
      }
1158 429 jones
1159
      // Time the request if asked for
1160
      double startTime = System.currentTimeMillis();
1161
1162 407 jones
      // Open a connection to the database
1163
      MetaCatUtil util = new MetaCatUtil();
1164
      Connection dbconn = util.openDBConnection();
1165
1166 463 berkley
      double connTime = System.currentTimeMillis();
1167 408 jones
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1168 407 jones
      if (action.equals("READ")) {
1169
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1170 429 jones
          if (useOldReadAlgorithm) {
1171
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1172
          } else {
1173
            xmldoc.toXml(new PrintWriter(System.out));
1174
          }
1175 408 jones
      } else if (action.equals("DELETE")) {
1176 425 bojilova
        DocumentImpl.delete(dbconn, docid, null, null);
1177 408 jones
        System.out.println("Document deleted: " + docid);
1178 407 jones
      } else {
1179 555 bojilova
        String newdocid = DocumentImpl.write(dbconn, filename, aclfilename,
1180 598 bojilova
                                             dtdfilename, action, docid,
1181
                                             null, null);
1182 408 jones
        if ((docid != null) && (!docid.equals(newdocid))) {
1183
          if (action.equals("INSERT")) {
1184
            System.out.println("New document ID generated!!! ");
1185
          } else if (action.equals("UPDATE")) {
1186
            System.out.println("ERROR: Couldn't update document!!! ");
1187 407 jones
          }
1188 408 jones
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1189
          System.out.println("ERROR: Couldn't update document!!! ");
1190 407 jones
        }
1191 408 jones
        System.out.println("Document processing finished for: " + filename
1192
              + " (" + newdocid + ")");
1193 407 jones
      }
1194
1195 429 jones
      double stopTime = System.currentTimeMillis();
1196 463 berkley
      double dbOpenTime = (connTime - startTime)/1000;
1197
      double insertTime = (stopTime - connTime)/1000;
1198 429 jones
      double executionTime = (stopTime - startTime)/1000;
1199
      if (showRuntime) {
1200 463 berkley
        System.out.println("\n\nTotal Execution time was: " +
1201
                           executionTime + " seconds.");
1202
        System.out.println("Time to open DB connection was: " + dbOpenTime +
1203
                           " seconds.");
1204
        System.out.println("Time to insert document was: " + insertTime +
1205
                           " seconds.");
1206 429 jones
      }
1207 463 berkley
      dbconn.close();
1208 407 jones
    } catch (McdbException me) {
1209
      me.toXml(new PrintWriter(System.err));
1210
    } catch (AccessionNumberException ane) {
1211
      System.out.println("ERROR: Couldn't delete document!!! ");
1212
      System.out.println(ane.getMessage());
1213
    } catch (Exception e) {
1214
      System.err.println("EXCEPTION HANDLING REQUIRED");
1215
      System.err.println(e.getMessage());
1216
      e.printStackTrace(System.err);
1217
    }
1218
  }
1219 393 jones
}