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 393 jones
25 429 jones
import java.util.Iterator;
26 407 jones
import java.util.Stack;
27 429 jones
import java.util.TreeSet;
28 407 jones
29
import org.xml.sax.AttributeList;
30
import org.xml.sax.ContentHandler;
31
import org.xml.sax.DTDHandler;
32
import org.xml.sax.EntityResolver;
33
import org.xml.sax.ErrorHandler;
34
import org.xml.sax.InputSource;
35
import org.xml.sax.XMLReader;
36
import org.xml.sax.SAXException;
37
import org.xml.sax.SAXParseException;
38
import org.xml.sax.helpers.XMLReaderFactory;
39
40 393 jones
/**
41
 * A class that represents an XML document. It can be created with a simple
42 407 jones
 * document identifier from a database connection.  It also will write an
43
 * XML text document to a database connection using SAX.
44 393 jones
 */
45
public class DocumentImpl {
46
47 425 bojilova
  static final int ALL = 1;
48
  static final int WRITE = 2;
49
  static final int READ = 4;
50
51 393 jones
  private Connection conn = null;
52
  private String docid = null;
53
  private String docname = null;
54
  private String doctype = null;
55 465 berkley
  private String doctitle = null;
56
  private String createdate = null;
57
  private String updatedate = null;
58 393 jones
  private String system_id = null;
59
  private long rootnodeid;
60
  private ElementNode rootNode = null;
61 429 jones
  private TreeSet nodeRecordList = null;
62 393 jones
63
  /**
64 396 jones
   * Constructor, creates document from database connection, used
65
   * for reading the document
66 393 jones
   *
67
   * @param conn the database connection from which to read the document
68
   * @param docid the identifier of the document to be created
69
   */
70
  public DocumentImpl(Connection conn, String docid) throws McdbException
71
  {
72 465 berkley
    try {
73 393 jones
      this.conn = conn;
74
      this.docid = docid;
75
76
      // Look up the document information
77
      getDocumentInfo(docid);
78
79
      // Download all of the document nodes using a single SQL query
80 429 jones
      // The sort order of the records is determined by the NodeComparator
81
      // class, and needs to represent a depth-first traversal for the
82
      // toXml() method to work properly
83
      nodeRecordList = getNodeRecordList(rootnodeid);
84 393 jones
85
    } catch (McdbException ex) {
86
      throw ex;
87
    } catch (Throwable t) {
88
      throw new McdbException("Error reading document " + docid + ".");
89
    }
90
  }
91
92 396 jones
  /**
93 415 jones
   * Construct a new document instance, writing the contents to the database.
94
   * This method is called from DBSAXHandler because we need to know the
95
   * root element name for documents without a DOCTYPE before creating it.
96 396 jones
   *
97
   * @param conn the JDBC Connection to which all information is written
98
   * @param rootnodeid - sequence id of the root node in the document
99
   * @param docname - the name of DTD, i.e. the name immediately following
100
   *        the DOCTYPE keyword ( should be the root element name ) or
101
   *        the root element name if no DOCTYPE declaration provided
102
   *        (Oracle's and IBM parsers are not aware if it is not the
103
   *        root element name)
104
   * @param doctype - Public ID of the DTD, i.e. the name immediately
105
   *                  following the PUBLIC keyword in DOCTYPE declaration or
106
   *                  the docname if no Public ID provided or
107
   *                  null if no DOCTYPE declaration provided
108
   *
109
   */
110
  public DocumentImpl(Connection conn, long rootnodeid, String docname,
111 425 bojilova
                      String doctype, String docid, String action, String user)
112 457 bojilova
                      throws SQLException, Exception
113 396 jones
  {
114
    this.conn = conn;
115
    this.rootnodeid = rootnodeid;
116
    this.docname = docname;
117
    this.doctype = doctype;
118
    this.docid = docid;
119 425 bojilova
    writeDocumentToDB(action, user);
120 396 jones
  }
121 407 jones
122 393 jones
  /**
123
   * get the document name
124
   */
125
  public String getDocname() {
126
    return docname;
127
  }
128
129
  /**
130
   * get the document type (which is the PublicID)
131
   */
132
  public String getDoctype() {
133
    return doctype;
134
  }
135
136
  /**
137
   * get the system identifier
138
   */
139
  public String getSystemID() {
140
    return system_id;
141
  }
142
143
  /**
144
   * get the root node identifier
145
   */
146
  public long getRootNodeID() {
147
    return rootnodeid;
148
  }
149 465 berkley
150
  /**
151
   * get the creation date
152
   */
153
  public String getCreateDate() {
154
    return createdate;
155
  }
156
157
  /**
158
   * get the update date
159
   */
160
  public String getUpdateDate() {
161
    return updatedate;
162
  }
163 393 jones
164 396 jones
  /**
165
   * Get the document identifier (docid)
166
   */
167
  public String getDocID() {
168
    return docid;
169
  }
170 465 berkley
171
  /**
172
   *get the document title
173
   */
174
  public String getDocTitle() {
175
    return doctitle;
176
  }
177 396 jones
178 429 jones
179 393 jones
  /**
180 429 jones
   * Print a string representation of the XML document
181 393 jones
   */
182
  public String toString()
183
  {
184 429 jones
    StringWriter docwriter = new StringWriter();
185
    this.toXml(docwriter);
186
    String document = docwriter.toString();
187
    return document;
188
  }
189
190
  /**
191
   * Get a text representation of the XML document as a string
192
   * This older algorithm uses a recursive tree of Objects to represent the
193
   * nodes of the tree.  Each object is passed the data for the document
194
   * and searches all of the document data to find its children nodes and
195
   * recursively build.  Thus, because each node reads the whole document,
196
   * this algorithm is extremely slow for larger documents, and the time
197
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
198
   * better algorithm.
199
   */
200
  public String readUsingSlowAlgorithm()
201
  {
202 393 jones
    StringBuffer doc = new StringBuffer();
203
204 429 jones
    // Create the elements from the downloaded data in the TreeSet
205
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
206
207 393 jones
    // Append the resulting document to the StringBuffer and return it
208
    doc.append("<?xml version=\"1.0\"?>\n");
209
210
    if (docname != null) {
211
      if ((doctype != null) && (system_id != null)) {
212
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
213
                   "\" \"" + system_id + "\">\n");
214
      } else {
215
        doc.append("<!DOCTYPE " + docname + ">\n");
216
      }
217
    }
218
    doc.append(rootNode.toString());
219
220
    return (doc.toString());
221
  }
222
223
  /**
224 429 jones
   * Print a text representation of the XML document to a Writer
225
   *
226
   * @param pw the Writer to which we print the document
227
   */
228
  public void toXml(Writer pw)
229
  {
230
    PrintWriter out = null;
231
    if (pw instanceof PrintWriter) {
232
      out = (PrintWriter)pw;
233
    } else {
234
      out = new PrintWriter(pw);
235
    }
236
237
    MetaCatUtil util = new MetaCatUtil();
238
239
    Stack openElements = new Stack();
240
    boolean atRootElement = true;
241
    boolean previousNodeWasElement = false;
242
243
    // Step through all of the node records we were given
244
    Iterator it = nodeRecordList.iterator();
245
    while (it.hasNext()) {
246
      NodeRecord currentNode = (NodeRecord)it.next();
247
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
248
                          //" (" + currentNode.parentnodeid +
249
                          //", " + currentNode.nodeindex +
250
                          //", " + currentNode.nodetype +
251
                          //", " + currentNode.nodename +
252
                          //", " + currentNode.nodedata + ")]");
253
254
      // Print the end tag for the previous node if needed
255
      //
256
      // This is determined by inspecting the parent nodeid for the
257
      // currentNode.  If it is the same as the nodeid of the last element
258
      // that was pushed onto the stack, then we are still in that previous
259
      // parent element, and we do nothing.  However, if it differs, then we
260
      // have returned to a level above the previous parent, so we go into
261
      // a loop and pop off nodes and print out their end tags until we get
262
      // the node on the stack to match the currentNode parentnodeid
263
      //
264
      // So, this of course means that we rely on the list of elements
265
      // having been sorted in a depth first traversal of the nodes, which
266
      // is handled by the NodeComparator class used by the TreeSet
267
      if (!atRootElement) {
268
        NodeRecord currentElement = (NodeRecord)openElements.peek();
269
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
270
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
271
            currentElement = (NodeRecord)openElements.pop();
272
            util.debugMessage("\n POPPED: " + currentElement.nodename);
273 451 bojilova
            if (previousNodeWasElement) {
274
              out.print(">");
275
              previousNodeWasElement = false;
276
            }
277 429 jones
            out.print("</" + currentElement.nodename + ">" );
278
            currentElement = (NodeRecord)openElements.peek();
279
          }
280
        }
281
      }
282
283
      // Handle the DOCUMENT node
284
      if (currentNode.nodetype.equals("DOCUMENT")) {
285
        out.println("<?xml version=\"1.0\"?>");
286
287
        if (docname != null) {
288
          if ((doctype != null) && (system_id != null)) {
289
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
290
                       "\" \"" + system_id + "\">");
291
          } else {
292
            out.println("<!DOCTYPE " + docname + ">");
293
          }
294
        }
295
296
      // Handle the ELEMENT nodes
297
      } else if (currentNode.nodetype.equals("ELEMENT")) {
298
        if (atRootElement) {
299
          atRootElement = false;
300
        } else {
301
          if (previousNodeWasElement) {
302
            out.print(">");
303
          }
304
        }
305
        openElements.push(currentNode);
306
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
307
        previousNodeWasElement = true;
308
        out.print("<" + currentNode.nodename);
309
310
      // Handle the ATTRIBUTE nodes
311
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
312
        out.print(" " + currentNode.nodename + "=\""
313
                 + currentNode.nodedata + "\"");
314
      } else if (currentNode.nodetype.equals("TEXT")) {
315
        if (previousNodeWasElement) {
316
          out.print(">");
317
        }
318
        out.print(currentNode.nodedata);
319
        previousNodeWasElement = false;
320
321
      // Handle the COMMENT nodes
322
      } else if (currentNode.nodetype.equals("COMMENT")) {
323
        if (previousNodeWasElement) {
324
          out.print(">");
325
        }
326
        out.print("<!--" + currentNode.nodedata + "-->");
327
        previousNodeWasElement = false;
328
329
      // Handle the PI nodes
330
      } else if (currentNode.nodetype.equals("PI")) {
331
        if (previousNodeWasElement) {
332
          out.print(">");
333
        }
334
        out.print("<?" + currentNode.nodename + " " +
335
                        currentNode.nodedata + "?>");
336
        previousNodeWasElement = false;
337
338
      // Handle any other node type (do nothing)
339
      } else {
340
        // Any other types of nodes are not handled.
341
        // Probably should throw an exception here to indicate this
342
      }
343
      out.flush();
344
    }
345
346
    // Print the final end tag for the root element
347
    NodeRecord currentElement = (NodeRecord)openElements.pop();
348
    util.debugMessage("\n POPPED: " + currentElement.nodename);
349
    out.print("</" + currentElement.nodename + ">" );
350
    out.flush();
351
  }
352
353
  /**
354 393 jones
   * Look up the document type information from the database
355
   *
356
   * @param docid the id of the document to look up
357
   */
358
  private void getDocumentInfo(String docid) throws McdbException
359
  {
360
    PreparedStatement pstmt;
361
362
    try {
363
      pstmt =
364 465 berkley
        conn.prepareStatement("SELECT docname, doctype, rootnodeid,doctitle, " +
365
                              "date_created, date_updated " +
366
                               "FROM xml_documents " +
367 447 bojilova
                               "WHERE docid LIKE ?");
368 393 jones
      // Bind the values to the query
369
      pstmt.setString(1, docid);
370
371
      pstmt.execute();
372
      ResultSet rs = pstmt.getResultSet();
373
      boolean tableHasRows = rs.next();
374
      if (tableHasRows) {
375
        this.docname    = rs.getString(1);
376
        this.doctype    = rs.getString(2);
377
        this.rootnodeid = rs.getLong(3);
378 465 berkley
        this.doctitle   = rs.getString(4);
379
        this.createdate = rs.getString(5);
380
        this.updatedate = rs.getString(6);
381 393 jones
      }
382
      pstmt.close();
383
384
      if (this.doctype != null) {
385
        pstmt =
386
          conn.prepareStatement("SELECT system_id " +
387
                                  "FROM xml_catalog " +
388 447 bojilova
                                 "WHERE public_id LIKE ?");
389 393 jones
        // Bind the values to the query
390
        pstmt.setString(1, doctype);
391
392
        pstmt.execute();
393
        rs = pstmt.getResultSet();
394
        tableHasRows = rs.next();
395
        if (tableHasRows) {
396
          this.system_id  = rs.getString(1);
397
        }
398
        pstmt.close();
399
      }
400
    } catch (SQLException e) {
401
      throw new McdbException("Error accessing database connection.", e);
402
    }
403
404
    if (this.docname == null) {
405
      throw new McdbDocNotFoundException("Document not found: " + docid);
406
    }
407
  }
408
409
  /**
410
   * Look up the node data from the database
411
   *
412 396 jones
   * @param rootnodeid the id of the root node of the node tree to look up
413 393 jones
   */
414 396 jones
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException
415 393 jones
  {
416
    PreparedStatement pstmt;
417
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
418
    long nodeid = 0;
419
    long parentnodeid = 0;
420
    long nodeindex = 0;
421
    String nodetype = null;
422
    String nodename = null;
423
    String nodedata = null;
424
425
    try {
426
      pstmt =
427
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
428
           "nodetype,nodename,"+
429
           "replace(" +
430
           "replace(" +
431
           "replace(nodedata,'&','&amp;') " +
432
           ",'<','&lt;') " +
433
           ",'>','&gt;') " +
434 396 jones
           "FROM xml_nodes WHERE rootnodeid = ?");
435 393 jones
436
      // Bind the values to the query
437 396 jones
      pstmt.setLong(1, rootnodeid);
438 393 jones
439
      pstmt.execute();
440
      ResultSet rs = pstmt.getResultSet();
441
      boolean tableHasRows = rs.next();
442
      while (tableHasRows) {
443
        nodeid = rs.getLong(1);
444
        parentnodeid = rs.getLong(2);
445
        nodeindex = rs.getLong(3);
446
        nodetype = rs.getString(4);
447
        nodename = rs.getString(5);
448
        nodedata = rs.getString(6);
449
450
        // add the data to the node record list hashtable
451
        NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid,
452
                                   nodeindex, nodetype, nodename, nodedata);
453
        nodeRecordList.add(currentRecord);
454
455
        // Advance to the next node
456
        tableHasRows = rs.next();
457
      }
458
      pstmt.close();
459
460
    } catch (SQLException e) {
461
      throw new McdbException("Error accessing database connection.", e);
462
    }
463
464
    if (nodeRecordList != null) {
465
      return nodeRecordList;
466
    } else {
467
      throw new McdbException("Error getting node data: " + docid);
468
    }
469
  }
470
471 459 bojilova
 /** creates SQL code and inserts new document into DB connection */
472
  private void writeDocumentToDB(String action, String user)
473
               throws SQLException, Exception {
474
    try {
475
      PreparedStatement pstmt = null;
476
477
      if (action.equals("INSERT")) {
478
        //AccessionNumber ac = new AccessionNumber();
479
        //this.docid = ac.generate(docid, "INSERT");
480
        pstmt = conn.prepareStatement(
481
            "INSERT INTO xml_documents " +
482
            "(docid, rootnodeid, docname, doctype, " +
483
            "user_owner, user_updated, date_created, date_updated) " +
484
            "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate)");
485
        // Bind the values to the query
486
        pstmt.setString(1, this.docid);
487
        pstmt.setLong(2, rootnodeid);
488
        pstmt.setString(3, docname);
489
        pstmt.setString(4, doctype);
490
        pstmt.setString(5, user);
491
        pstmt.setString(6, user);
492
      } else if (action.equals("UPDATE")) {
493
494
        // Save the old document entry in a backup table
495
        DocumentImpl.archiveDocRevision( conn, docid, user );
496
497
        // Delete index for the old version of docid
498
        // The new index is inserting on the next calls to DBSAXNode
499
        pstmt = conn.prepareStatement(
500
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
501
        pstmt.execute();
502
        pstmt.close();
503
504
        // Update the new document to reflect the new node tree
505
        pstmt = conn.prepareStatement(
506
            "UPDATE xml_documents " +
507
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
508
            "user_updated = ?, date_updated = sysdate WHERE docid LIKE ?");
509
        // Bind the values to the query
510
        pstmt.setLong(1, rootnodeid);
511
        pstmt.setString(2, docname);
512
        pstmt.setString(3, doctype);
513
        pstmt.setString(4, user);
514
        pstmt.setString(5, this.docid);
515
      } else {
516
        System.err.println("Action not supported: " + action);
517
      }
518
519
      // Do the insertion
520
      pstmt.execute();
521
      pstmt.close();
522
523
    } catch (SQLException sqle) {
524
      throw sqle;
525
//    } catch (AccessionNumberException ane) {
526
//      MetaCatUtil.debugMessage("Invalid accession number.");
527
//      MetaCatUtil.debugMessage(ane.getMessage());
528
//      throw ane;
529
    } catch (Exception e) {
530
      throw e;
531
    }
532
  }
533
534 393 jones
  /**
535 459 bojilova
   * Get the document title
536
   */
537
  public String getTitle() {
538
    return doctitle;
539
  }
540
541
  /**
542
   * Set the document title
543
   *
544
   * @param title the new title for the document
545
   */
546 465 berkley
547 459 bojilova
  public void setTitle( String title ) {
548
    this.doctitle = title;
549
    try {
550
      PreparedStatement pstmt;
551
      pstmt = conn.prepareStatement(
552
            "UPDATE xml_documents " +
553
            " SET doctitle = ? " +
554
            "WHERE docid = ?");
555
556
      // Bind the values to the query
557
      pstmt.setString(1, doctitle);
558
      pstmt.setString(2, docid);
559
560
      // Do the insertion
561
      pstmt.execute();
562
      pstmt.close();
563
    } catch (SQLException e) {
564
      System.out.println(e.getMessage());
565
    }
566
  }
567
568
  /**
569
   * Look up the title of the first child element named "title"
570
   * and record it as the document title
571
   */
572
/*   NOT NEEDED ANY MORE
573
  public void setTitleFromChildElement() {
574
    String title = null;
575
    long assigned_id=0;
576
    PreparedStatement pstmt;
577
    try {
578
      pstmt = conn.prepareStatement(
579
              "SELECT nodedata FROM xml_nodes " +
580
              "WHERE nodetype = 'TEXT' " +
581
              "AND rootnodeid = ? " +
582
              "AND parentnodeid IN " +
583
              "  (SELECT nodeid FROM xml_nodes " +
584
              "  WHERE nodename = 'title' " +
585
              "  AND nodetype =  'ELEMENT' " +
586
              "  AND rootnodeid = ? ) " +
587
              "ORDER BY nodeid");
588
*/
589
      // The above query might be slow, and probably will be because
590
      // it gets ALL of the title elements while searching for one
591
      // title in a small subtree but it avoids the problem of using
592
      // Oracle's Hierarchical Query syntax which is not portable --
593
      // the commented out SQL that follows shows an equivalent query
594
      // using Oracle-specific hierarchical query
595
      /*
596
      pstmt = conn.prepareStatement(
597
              "SELECT nodedata FROM xml_nodes " +
598
              "WHERE nodetype = 'TEXT' " +
599
              "AND parentnodeid IN " +
600
              "(SELECT nodeid FROM xml_nodes " +
601
              "WHERE nodename = 'title' " +
602
              "START WITH nodeid = ? " +
603
              "CONNECT BY PRIOR nodeid = parentnodeid)");
604
      */
605
/*
606
      // Bind the values to the query
607
      pstmt.setLong(1, rootnodeid);
608
      pstmt.setLong(2, rootnodeid);
609
610
      pstmt.execute();
611
      ResultSet rs = pstmt.getResultSet();
612
      boolean tableHasRows = rs.next();
613
      if (tableHasRows) {
614
        title = rs.getString(1);
615
      }
616
      pstmt.close();
617
    } catch (SQLException e) {
618
      System.out.println("Error getting title: " + e.getMessage());
619
    }
620
621
    // assign the new title
622
    this.setTitle(title);
623
  }
624
*/
625
626
  /**
627 407 jones
   * Write an XML file to the database, given a filename
628 393 jones
   *
629 408 jones
   * @param conn the JDBC connection to the database
630 407 jones
   * @param filename the filename to be loaded into the database
631
   * @param action the action to be performed (INSERT OR UPDATE)
632
   * @param docid the docid to use for the INSERT OR UPDATE
633 393 jones
   */
634 408 jones
  public static String write( Connection conn, String filename, String action,
635 425 bojilova
                              String docid, String user, String group )
636 457 bojilova
                throws Exception {
637 425 bojilova
638
    return write(conn, new FileReader(new File(filename).toString()),
639
                  action, docid, user, group);
640 407 jones
  }
641
642
  /**
643
   * Write an XML file to the database, given a Reader
644
   *
645 408 jones
   * @param conn the JDBC connection to the database
646 407 jones
   * @param xml the xml stream to be loaded into the database
647
   * @param action the action to be performed (INSERT OR UPDATE)
648
   * @param docid the docid to use for the INSERT OR UPDATE
649
   */
650 408 jones
  public static String write( Connection conn, Reader xml, String action,
651 425 bojilova
                              String docid, String user, String group )
652 457 bojilova
                throws Exception {
653 425 bojilova
654 457 bojilova
    // Determine if the docid is OK for INSERT or UPDATE
655 459 bojilova
    AccessionNumber ac = new AccessionNumber(conn);
656 457 bojilova
    String newdocid = ac.generate(docid, action);
657
658 425 bojilova
    if ( action.equals("UPDATE") ) {
659 441 bojilova
      // check for 'write' permission for 'user' to update this document
660
      if ( !hasWritePermission(conn, docid, user, group) ) {
661
        throw new Exception("User " + user +
662
              " does not have permission to update XML Document #" + docid);
663 425 bojilova
      }
664
    }
665
666 407 jones
    try {
667 457 bojilova
        XMLReader parser = initializeParser(conn, action, newdocid, user);
668 407 jones
        conn.setAutoCommit(false);
669
        parser.parse(new InputSource(xml));
670
        conn.commit();
671
        conn.setAutoCommit(true);
672 457 bojilova
        //return newdocid;
673 407 jones
      } catch (SAXParseException e) {
674
        conn.rollback();
675 457 bojilova
        conn.setAutoCommit(true);
676 407 jones
        throw e;
677
      } catch (SAXException e) {
678 457 bojilova
        conn.rollback();
679
        conn.setAutoCommit(true);
680
        throw e;
681
/*
682 407 jones
        // If its a problem with the accession number its ok, just the
683
        // accession number was regenerated
684
        AccessionNumberGeneratedException ang = null;
685 393 jones
        try {
686 407 jones
          Exception embedded = e.getException();
687
          if ((embedded != null) &&
688
              (embedded instanceof AccessionNumberGeneratedException)) {
689
            ang = (AccessionNumberGeneratedException)e.getException();
690
          }
691
        } catch (ClassCastException cce) {
692
          // Do nothing and just fall through to the ang != null test
693 393 jones
        }
694 407 jones
        if (ang != null) {
695
          conn.commit();
696
          conn.setAutoCommit(true);
697
          return (ang.getMessage());
698
        } else {
699
          conn.rollback();
700
          throw e;
701
        }
702 457 bojilova
*/
703 407 jones
      } catch (Exception e) {
704
        conn.rollback();
705 457 bojilova
        conn.setAutoCommit(true);
706 407 jones
        throw e;
707
      }
708 457 bojilova
709
      if ( (docid != null) && !(newdocid.equals(docid)) ) {
710
        return new String("New document ID generated:" + newdocid);
711
      } else {
712
        return newdocid;
713
      }
714 393 jones
  }
715 396 jones
716 407 jones
  /**
717
   * Delete an XML file from the database (actually, just make it a revision
718
   * in the xml_revisions table)
719
   *
720
   * @param docid the ID of the document to be deleted from the database
721
   */
722 425 bojilova
  public static void delete( Connection conn, String docid,
723
                                 String user, String group )
724 457 bojilova
                throws Exception {
725 396 jones
726 441 bojilova
    // Determine if the docid is OK for DELETE
727 459 bojilova
    AccessionNumber ac = new AccessionNumber(conn);
728 421 bojilova
    String newdocid = ac.generate(docid, "DELETE");
729 396 jones
730 441 bojilova
    // check for 'write' permission for 'user' to delete this document
731
    if ( !hasWritePermission(conn, docid, user, group) ) {
732
      throw new Exception("User " + user +
733
              " does not have permission to delete XML Document #" + docid);
734 425 bojilova
    }
735
736 407 jones
    conn.setAutoCommit(false);
737
    // Copy the record to the xml_revisions table
738 425 bojilova
    DocumentImpl.archiveDocRevision( conn, docid, user );
739 396 jones
740 407 jones
    // Now delete it from the xml_documents table
741
    Statement stmt = conn.createStatement();
742 425 bojilova
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
743
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
744 407 jones
    stmt.close();
745
    conn.commit();
746 457 bojilova
    conn.setAutoCommit(true);
747 407 jones
  }
748 425 bojilova
749
  /** Check for "write" permissions from DB connection */
750
  private static boolean hasWritePermission(Connection conn, String docid,
751
                                     String user, String group)
752
                                     throws SQLException {
753 441 bojilova
    // b' of the command line invocation
754
    if ( (user == null) && (group == null) ) {
755
      return true;
756
    }
757
758 425 bojilova
    PreparedStatement pstmt;
759
    // checking if user is owner of docid
760
    try {
761
      pstmt = conn.prepareStatement(
762 441 bojilova
                   "SELECT 'x' FROM xml_documents " +
763 425 bojilova
                   "WHERE docid LIKE ? AND user_owner LIKE ?");
764
      // Bind the values to the query
765
      pstmt.setString(1, docid);
766
      pstmt.setString(2, user);
767 396 jones
768 425 bojilova
      pstmt.execute();
769
      ResultSet rs = pstmt.getResultSet();
770
      boolean hasRow = rs.next();
771
      pstmt.close();
772
      if (hasRow) {
773
        return true;
774
      }
775
776
    } catch (SQLException e) {
777
      throw new
778 441 bojilova
        SQLException("Error checking document's owner: " + e.getMessage());
779 425 bojilova
    }
780
781
    // checking access type from xml_access table
782
    int accesstype = 0;
783
    try {
784
      pstmt = conn.prepareStatement(
785
                   "SELECT access_type FROM xml_access " +
786
                   "WHERE docid LIKE ? " +
787
                   "AND principal_name LIKE ? " +
788
                   "AND principal_type = 'user' " +
789 441 bojilova
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
790
                                   "AND nvl(end_time,sysdate) " +
791 425 bojilova
                   "UNION " +
792
                   "SELECT access_type FROM xml_access " +
793
                   "WHERE docid LIKE ? " +
794
                   "AND principal_name LIKE ? " +
795
                   "AND principal_type = 'group' " +
796 441 bojilova
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
797
                                   "AND nvl(end_time,sysdate)");
798 425 bojilova
      // Bind the values to the query
799
      pstmt.setString(1, docid);
800
      pstmt.setString(2, user);
801
      pstmt.setString(3, docid);
802
      pstmt.setString(2, group);
803
804
      pstmt.execute();
805
      ResultSet rs = pstmt.getResultSet();
806
      boolean hasRows = rs.next();
807 427 bojilova
      while ( hasRows ) {
808
        accesstype = rs.getInt(1);
809
        if ( (accesstype & WRITE) == WRITE ) {
810
          pstmt.close();
811
          return true;
812
        }
813
        hasRows = rs.next();
814 425 bojilova
      }
815
816
      pstmt.close();
817
      return false;
818
819
    } catch (SQLException e) {
820
      throw new
821
      SQLException("Error getting document's permissions: " + e.getMessage());
822
    }
823
  }
824
825 396 jones
  /**
826 407 jones
   * Set up the parser handlers for writing the document to the database
827
   */
828 408 jones
  private static XMLReader initializeParser(Connection conn,
829 457 bojilova
                           String action, String docid, String user)
830
                           throws Exception {
831 407 jones
    XMLReader parser = null;
832
    //
833
    // Set up the SAX document handlers for parsing
834
    //
835
    try {
836 425 bojilova
      ContentHandler chandler   = new DBSAXHandler(conn, action, docid, user);
837 407 jones
      EntityResolver dbresolver = new DBEntityResolver(conn,
838
                                      (DBSAXHandler)chandler);
839
      DTDHandler dtdhandler     = new DBDTDHandler(conn);
840
841
      // Get an instance of the parser
842 408 jones
      MetaCatUtil util = new MetaCatUtil();
843
      String parserName = util.getOption("saxparser");
844 407 jones
      parser = XMLReaderFactory.createXMLReader(parserName);
845
846
      // Turn off validation
847
      parser.setFeature("http://xml.org/sax/features/validation", false);
848
849
      // Set Handlers in the parser
850
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
851
                         chandler);
852
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
853
                         chandler);
854
      parser.setContentHandler((ContentHandler)chandler);
855
      parser.setEntityResolver((EntityResolver)dbresolver);
856
      parser.setDTDHandler((DTDHandler)dtdhandler);
857
      parser.setErrorHandler((ErrorHandler)chandler);
858
859
    } catch (Exception e) {
860 457 bojilova
      throw e;
861 407 jones
    }
862
863
    return parser;
864
  }
865
866 459 bojilova
  /** Save a document entry in the xml_revisions table */
867
  private static void archiveDocRevision(Connection conn, String docid,
868
                                         String user) throws SQLException {
869
    // create a record in xml_revisions table
870
    // for that document as selected from xml_documents
871
    PreparedStatement pstmt = conn.prepareStatement(
872
      "INSERT INTO xml_revisions " +
873
        "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
874
        "user_owner, user_updated, date_created, date_updated) " +
875
      "SELECT null, ?, rootnodeid, docname, doctype, doctitle," +
876
        "user_owner, ?, sysdate, sysdate "+
877
      "FROM xml_documents " +
878
      "WHERE docid = ?");
879
    // Bind the values to the query and execute it
880
    pstmt.setString(1, docid);
881
    pstmt.setString(2, user);
882
    pstmt.setString(3, docid);
883
    pstmt.execute();
884
    pstmt.close();
885
886
  }
887
888 407 jones
  /**
889
   * the main routine used to test the DBWriter utility.
890
   * <p>
891
   * Usage: java DocumentImpl <-f filename -a action -d docid>
892
   *
893
   * @param filename the filename to be loaded into the database
894
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
895
   * @param docid the id of the document to process
896
   */
897
  static public void main(String[] args) {
898
899
    try {
900
      String filename = null;
901
      String action   = null;
902
      String docid    = null;
903 429 jones
      boolean showRuntime = false;
904
      boolean useOldReadAlgorithm = false;
905 407 jones
906 408 jones
      // Parse the command line arguments
907 407 jones
      for ( int i=0 ; i < args.length; ++i ) {
908
        if ( args[i].equals( "-f" ) ) {
909
          filename =  args[++i];
910
        } else if ( args[i].equals( "-a" ) ) {
911
          action =  args[++i];
912
        } else if ( args[i].equals( "-d" ) ) {
913
          docid =  args[++i];
914 429 jones
        } else if ( args[i].equals( "-t" ) ) {
915
          showRuntime = true;
916
        } else if ( args[i].equals( "-old" ) ) {
917
          useOldReadAlgorithm = true;
918 407 jones
        } else {
919
          System.err.println
920
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
921
        }
922
      }
923
924 408 jones
      // Check if the required arguments are provided
925 407 jones
      boolean argsAreValid = false;
926
      if (action != null) {
927
        if (action.equals("INSERT")) {
928
          if (filename != null) {
929
            argsAreValid = true;
930
          }
931
        } else if (action.equals("UPDATE")) {
932
          if ((filename != null) && (docid != null)) {
933
            argsAreValid = true;
934
          }
935
        } else if (action.equals("DELETE")) {
936
          if (docid != null) {
937
            argsAreValid = true;
938
          }
939
        } else if (action.equals("READ")) {
940
          if (docid != null) {
941
            argsAreValid = true;
942
          }
943
        }
944
      }
945
946 408 jones
      // Print usage message if the arguments are not valid
947 407 jones
      if (!argsAreValid) {
948
        System.err.println("Wrong number of arguments!!!");
949
        System.err.println(
950 429 jones
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename>");
951 407 jones
        System.err.println(
952 429 jones
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename>");
953 407 jones
        System.err.println(
954 429 jones
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
955 407 jones
        System.err.println(
956 429 jones
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
957 407 jones
        return;
958
      }
959 429 jones
960
      // Time the request if asked for
961
      double startTime = System.currentTimeMillis();
962
963 407 jones
      // Open a connection to the database
964
      MetaCatUtil util = new MetaCatUtil();
965
      Connection dbconn = util.openDBConnection();
966
967 463 berkley
      double connTime = System.currentTimeMillis();
968 408 jones
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
969 407 jones
      if (action.equals("READ")) {
970
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
971 429 jones
          if (useOldReadAlgorithm) {
972
            System.out.println(xmldoc.readUsingSlowAlgorithm());
973
          } else {
974
            xmldoc.toXml(new PrintWriter(System.out));
975
          }
976 408 jones
      } else if (action.equals("DELETE")) {
977 425 bojilova
        DocumentImpl.delete(dbconn, docid, null, null);
978 408 jones
        System.out.println("Document deleted: " + docid);
979 407 jones
      } else {
980 425 bojilova
        String newdocid = DocumentImpl.write(dbconn, filename, action, docid,
981
                                                                  null, null);
982 408 jones
        if ((docid != null) && (!docid.equals(newdocid))) {
983
          if (action.equals("INSERT")) {
984
            System.out.println("New document ID generated!!! ");
985
          } else if (action.equals("UPDATE")) {
986
            System.out.println("ERROR: Couldn't update document!!! ");
987 407 jones
          }
988 408 jones
        } else if ((docid == null) && (action.equals("UPDATE"))) {
989
          System.out.println("ERROR: Couldn't update document!!! ");
990 407 jones
        }
991 408 jones
        System.out.println("Document processing finished for: " + filename
992
              + " (" + newdocid + ")");
993 407 jones
      }
994
995 429 jones
      double stopTime = System.currentTimeMillis();
996 463 berkley
      double dbOpenTime = (connTime - startTime)/1000;
997
      double insertTime = (stopTime - connTime)/1000;
998 429 jones
      double executionTime = (stopTime - startTime)/1000;
999
      if (showRuntime) {
1000 463 berkley
        System.out.println("\n\nTotal Execution time was: " +
1001
                           executionTime + " seconds.");
1002
        System.out.println("Time to open DB connection was: " + dbOpenTime +
1003
                           " seconds.");
1004
        System.out.println("Time to insert document was: " + insertTime +
1005
                           " seconds.");
1006 429 jones
      }
1007 463 berkley
      dbconn.close();
1008 407 jones
    } catch (McdbException me) {
1009
      me.toXml(new PrintWriter(System.err));
1010
    } catch (AccessionNumberException ane) {
1011
      System.out.println("ERROR: Couldn't delete document!!! ");
1012
      System.out.println(ane.getMessage());
1013
    } catch (Exception e) {
1014
      System.err.println("EXCEPTION HANDLING REQUIRED");
1015
      System.err.println(e.getMessage());
1016
      e.printStackTrace(System.err);
1017
    }
1018
  }
1019 393 jones
}