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