Project

General

Profile

1
/**
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: berkley $'
10
 *     '$Date: 2000-11-16 15:26:00 -0800 (Thu, 16 Nov 2000) $'
11
 * '$Revision: 549 $'
12
 */
13

    
14
package edu.ucsb.nceas.metacat;
15

    
16
import java.sql.*;
17
import java.io.File;
18
import java.io.FileReader;
19
import java.io.IOException;
20
import java.io.PrintWriter;
21
import java.io.Reader;
22
import java.io.StringWriter;
23
import java.io.Writer;
24

    
25
import java.util.Iterator;
26
import java.util.Stack;
27
import java.util.TreeSet;
28

    
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
/**
41
 * A class that represents an XML document. It can be created with a simple
42
 * document identifier from a database connection.  It also will write an
43
 * XML text document to a database connection using SAX.
44
 */
45
public class DocumentImpl {
46

    
47
  static final int ALL = 1;
48
  static final int WRITE = 2;
49
  static final int READ = 4;
50

    
51
  private Connection conn = null;
52
  private String docid = null;
53
  private String docname = null;
54
  private String doctype = null;
55
  private String doctitle = null;
56
  private String createdate = null;
57
  private String updatedate = null;
58
  private String system_id = null;
59
  private long rootnodeid;
60
  private ElementNode rootNode = null;
61
  private TreeSet nodeRecordList = null;
62

    
63
  /**
64
   * Constructor, creates document from database connection, used 
65
   * for reading the document
66
   *
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
    try { 
73
      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
      // 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
  
85
    } catch (McdbException ex) {
86
      throw ex;
87
    } catch (Throwable t) {
88
      throw new McdbException("Error reading document " + docid + ".");
89
    }
90
  }
91

    
92
  /** 
93
   * 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
   *
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
                      String doctype, String docid, String action, String user,
112
                      int serverCode)
113
                      throws SQLException, Exception
114
  {
115
    this.conn = conn;
116
    this.rootnodeid = rootnodeid;
117
    this.docname = docname;
118
    this.doctype = doctype;
119
    this.docid = docid;
120
    writeDocumentToDB(action, user, serverCode);
121
  }
122
  
123
  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
124
                      String doctype, String docid, String action, String user)
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);
133
  }
134

    
135
  /**
136
   * get the document name
137
   */
138
  public String getDocname() {
139
    return docname;
140
  }
141

    
142
  /**
143
   * get the document type (which is the PublicID)
144
   */
145
  public String getDoctype() {
146
    return doctype;
147
  }
148

    
149
  /**
150
   * get the system identifier
151
   */
152
  public String getSystemID() {
153
    return system_id;
154
  }
155

    
156
  /**
157
   * get the root node identifier
158
   */
159
  public long getRootNodeID() {
160
    return rootnodeid;
161
  }
162
  
163
  /**
164
   * get the creation date
165
   */
166
  public String getCreateDate() {
167
    return createdate;
168
  }
169
  
170
  /**
171
   * get the update date
172
   */
173
  public String getUpdateDate() {
174
    return updatedate;
175
  }
176

    
177
  /** 
178
   * Get the document identifier (docid)
179
   */
180
  public String getDocID() {
181
    return docid;
182
  }
183
  
184
  /**
185
   *get the document title
186
   */
187
  public String getDocTitle() {
188
    return doctitle;
189
  }
190

    
191

    
192
  /**
193
   * Print a string representation of the XML document
194
   */
195
  public String toString()
196
  {
197
    StringWriter docwriter = new StringWriter();
198
    this.toXml(docwriter);
199
    String document = docwriter.toString();
200
    return document;
201
  }
202

    
203
  /**
204
   * Get a text representation of the XML document as a string
205
   * This older algorithm uses a recursive tree of Objects to represent the
206
   * nodes of the tree.  Each object is passed the data for the document 
207
   * and searches all of the document data to find its children nodes and
208
   * recursively build.  Thus, because each node reads the whole document,
209
   * this algorithm is extremely slow for larger documents, and the time
210
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
211
   * better algorithm.
212
   */
213
  public String readUsingSlowAlgorithm()
214
  {
215
    StringBuffer doc = new StringBuffer();
216

    
217
    // Create the elements from the downloaded data in the TreeSet
218
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
219

    
220
    // Append the resulting document to the StringBuffer and return it
221
    doc.append("<?xml version=\"1.0\"?>\n");
222
      
223
    if (docname != null) {
224
      if ((doctype != null) && (system_id != null)) {
225
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
226
                   "\" \"" + system_id + "\">\n");
227
      } else {
228
        doc.append("<!DOCTYPE " + docname + ">\n");
229
      }
230
    }
231
    doc.append(rootNode.toString());
232
  
233
    return (doc.toString());
234
  }
235

    
236
  /**
237
   * Print a text representation of the XML document to a Writer
238
   *
239
   * @param pw the Writer to which we print the document
240
   */
241
  public void toXml(Writer pw)
242
  {
243
    PrintWriter out = null;
244
    if (pw instanceof PrintWriter) {
245
      out = (PrintWriter)pw;
246
    } else {
247
      out = new PrintWriter(pw);
248
    }
249

    
250
    MetaCatUtil util = new MetaCatUtil();
251
    
252
    Stack openElements = new Stack();
253
    boolean atRootElement = true;
254
    boolean previousNodeWasElement = false;
255

    
256
    // Step through all of the node records we were given
257
    Iterator it = nodeRecordList.iterator();
258
    while (it.hasNext()) {
259
      NodeRecord currentNode = (NodeRecord)it.next();
260
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
261
                          //" (" + currentNode.parentnodeid +
262
                          //", " + currentNode.nodeindex + 
263
                          //", " + currentNode.nodetype + 
264
                          //", " + currentNode.nodename + 
265
                          //", " + currentNode.nodedata + ")]");
266

    
267
      // Print the end tag for the previous node if needed
268
      //
269
      // This is determined by inspecting the parent nodeid for the
270
      // currentNode.  If it is the same as the nodeid of the last element
271
      // that was pushed onto the stack, then we are still in that previous
272
      // parent element, and we do nothing.  However, if it differs, then we
273
      // have returned to a level above the previous parent, so we go into
274
      // a loop and pop off nodes and print out their end tags until we get
275
      // the node on the stack to match the currentNode parentnodeid
276
      //
277
      // So, this of course means that we rely on the list of elements
278
      // having been sorted in a depth first traversal of the nodes, which
279
      // is handled by the NodeComparator class used by the TreeSet
280
      if (!atRootElement) {
281
        NodeRecord currentElement = (NodeRecord)openElements.peek();
282
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
283
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
284
            currentElement = (NodeRecord)openElements.pop();
285
            util.debugMessage("\n POPPED: " + currentElement.nodename);
286
            if (previousNodeWasElement) {
287
              out.print(">");
288
              previousNodeWasElement = false;
289
            }  
290
            out.print("</" + currentElement.nodename + ">" );
291
            currentElement = (NodeRecord)openElements.peek();
292
          }
293
        }
294
      }
295

    
296
      // Handle the DOCUMENT node
297
      if (currentNode.nodetype.equals("DOCUMENT")) {
298
        out.println("<?xml version=\"1.0\"?>");
299
      
300
        if (docname != null) {
301
          if ((doctype != null) && (system_id != null)) {
302
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
303
                       "\" \"" + system_id + "\">");
304
          } else {
305
            out.println("<!DOCTYPE " + docname + ">");
306
          }
307
        }
308

    
309
      // Handle the ELEMENT nodes
310
      } else if (currentNode.nodetype.equals("ELEMENT")) {
311
        if (atRootElement) {
312
          atRootElement = false;
313
        } else {
314
          if (previousNodeWasElement) {
315
            out.print(">");
316
          }
317
        }
318
        openElements.push(currentNode);
319
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
320
        previousNodeWasElement = true;
321
        out.print("<" + currentNode.nodename);
322

    
323
      // Handle the ATTRIBUTE nodes
324
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
325
        out.print(" " + currentNode.nodename + "=\""
326
                 + currentNode.nodedata + "\"");
327
      } else if (currentNode.nodetype.equals("TEXT")) {
328
        if (previousNodeWasElement) {
329
          out.print(">");
330
        }
331
        out.print(currentNode.nodedata);
332
        previousNodeWasElement = false;
333

    
334
      // Handle the COMMENT nodes
335
      } else if (currentNode.nodetype.equals("COMMENT")) {
336
        if (previousNodeWasElement) {
337
          out.print(">");
338
        }
339
        out.print("<!--" + currentNode.nodedata + "-->");
340
        previousNodeWasElement = false;
341

    
342
      // Handle the PI nodes
343
      } else if (currentNode.nodetype.equals("PI")) {
344
        if (previousNodeWasElement) {
345
          out.print(">");
346
        }
347
        out.print("<?" + currentNode.nodename + " " +
348
                        currentNode.nodedata + "?>");
349
        previousNodeWasElement = false;
350

    
351
      // Handle any other node type (do nothing)
352
      } else {
353
        // Any other types of nodes are not handled.
354
        // Probably should throw an exception here to indicate this
355
      }
356
      out.flush();
357
    }
358

    
359
    // Print the final end tag for the root element
360
    NodeRecord currentElement = (NodeRecord)openElements.pop();
361
    util.debugMessage("\n POPPED: " + currentElement.nodename);
362
    out.print("</" + currentElement.nodename + ">" );
363
    out.flush();
364
  }
365

    
366
  /**
367
   * Look up the document type information from the database
368
   *
369
   * @param docid the id of the document to look up
370
   */
371
  private void getDocumentInfo(String docid) throws McdbException 
372
  {
373
    PreparedStatement pstmt;
374

    
375
    try {
376
      pstmt =
377
        conn.prepareStatement("SELECT docname, doctype, rootnodeid,doctitle, " +
378
                              "date_created, date_updated " + 
379
                               "FROM xml_documents " +
380
                               "WHERE docid LIKE ?");
381
      // Bind the values to the query
382
      pstmt.setString(1, docid);
383

    
384
      pstmt.execute();
385
      ResultSet rs = pstmt.getResultSet();
386
      boolean tableHasRows = rs.next();
387
      if (tableHasRows) {
388
        this.docname    = rs.getString(1);
389
        this.doctype    = rs.getString(2);
390
        this.rootnodeid = rs.getLong(3);
391
        this.doctitle   = rs.getString(4);
392
        this.createdate = rs.getString(5);
393
        this.updatedate = rs.getString(6);
394
      } 
395
      pstmt.close();
396

    
397
      if (this.doctype != null) {
398
        pstmt =
399
          conn.prepareStatement("SELECT system_id " +
400
                                  "FROM xml_catalog " +
401
                                 "WHERE public_id LIKE ?");
402
        // Bind the values to the query
403
        pstmt.setString(1, doctype);
404
  
405
        pstmt.execute();
406
        rs = pstmt.getResultSet();
407
        tableHasRows = rs.next();
408
        if (tableHasRows) {
409
          this.system_id  = rs.getString(1);
410
        } 
411
        pstmt.close();
412
      }
413
    } catch (SQLException e) {
414
      throw new McdbException("Error accessing database connection.", e);
415
    }
416

    
417
    if (this.docname == null) {
418
      throw new McdbDocNotFoundException("Document not found: " + docid);
419
    }
420
  }
421

    
422
  /**
423
   * Look up the node data from the database
424
   *
425
   * @param rootnodeid the id of the root node of the node tree to look up
426
   */
427
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
428
  {
429
    PreparedStatement pstmt;
430
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
431
    long nodeid = 0;
432
    long parentnodeid = 0;
433
    long nodeindex = 0;
434
    String nodetype = null;
435
    String nodename = null;
436
    String nodedata = null;
437

    
438
    try {
439
      pstmt =
440
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
441
           "nodetype,nodename,"+               
442
           "replace(" +
443
           "replace(" +
444
           "replace(nodedata,'&','&amp;') " +
445
           ",'<','&lt;') " +
446
           ",'>','&gt;') " +
447
           "FROM xml_nodes WHERE rootnodeid = ?");
448

    
449
      // Bind the values to the query
450
      pstmt.setLong(1, rootnodeid);
451

    
452
      pstmt.execute();
453
      ResultSet rs = pstmt.getResultSet();
454
      boolean tableHasRows = rs.next();
455
      while (tableHasRows) {
456
        nodeid = rs.getLong(1);
457
        parentnodeid = rs.getLong(2);
458
        nodeindex = rs.getLong(3);
459
        nodetype = rs.getString(4);
460
        nodename = rs.getString(5);
461
        nodedata = rs.getString(6);
462

    
463
        // add the data to the node record list hashtable
464
        NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid, 
465
                                   nodeindex, nodetype, nodename, nodedata);
466
        nodeRecordList.add(currentRecord);
467

    
468
        // Advance to the next node
469
        tableHasRows = rs.next();
470
      } 
471
      pstmt.close();
472

    
473
    } catch (SQLException e) {
474
      throw new McdbException("Error accessing database connection.", e);
475
    }
476

    
477
    if (nodeRecordList != null) {
478
      return nodeRecordList;
479
    } else {
480
      throw new McdbException("Error getting node data: " + docid);
481
    }
482
  }
483
  
484
  /** creates SQL code and inserts new document into DB connection 
485
   default serverCode of 1*/
486
  private void writeDocumentToDB(String action, String user)
487
               throws SQLException, Exception
488
  {
489
    writeDocumentToDB(action, user, 1);
490
  }
491

    
492
 /** creates SQL code and inserts new document into DB connection */
493
  private void writeDocumentToDB(String action, String user, int serverCode) 
494
               throws SQLException, Exception {
495
    try {
496
      PreparedStatement pstmt = null;
497

    
498
      if (action.equals("INSERT")) {
499
        //AccessionNumber ac = new AccessionNumber();
500
        //this.docid = ac.generate(docid, "INSERT");
501
        pstmt = conn.prepareStatement(
502
            "INSERT INTO xml_documents " +
503
            "(docid, rootnodeid, docname, doctype, user_owner, " +
504
            "user_updated, date_created, date_updated, server_location) " +
505
            "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate, ?)");
506
        //note that the server_location is set to 1. 
507
        //this means that "localhost" in the xml_replication table must
508
        //always be the first entry!!!!!
509
        
510
        // Bind the values to the query
511
        pstmt.setString(1, this.docid);
512
        pstmt.setLong(2, rootnodeid);
513
        pstmt.setString(3, docname);
514
        pstmt.setString(4, doctype);
515
        pstmt.setString(5, user);
516
        pstmt.setString(6, user);
517
        pstmt.setInt(7, serverCode);
518
      } else if (action.equals("UPDATE")) {
519

    
520
        // Save the old document entry in a backup table
521
        DocumentImpl.archiveDocRevision( conn, docid, user );
522

    
523
        // Delete index for the old version of docid
524
        // The new index is inserting on the next calls to DBSAXNode
525
        pstmt = conn.prepareStatement(
526
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
527
        pstmt.execute();
528
        pstmt.close();
529

    
530
        // Update the new document to reflect the new node tree
531
        pstmt = conn.prepareStatement(
532
            "UPDATE xml_documents " +
533
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
534
            "user_updated = ?, date_updated = sysdate, " +
535
            "server_location = ? WHERE docid LIKE ?");
536
        // Bind the values to the query
537
        pstmt.setLong(1, rootnodeid);
538
        pstmt.setString(2, docname);
539
        pstmt.setString(3, doctype);
540
        pstmt.setString(4, user);
541
        pstmt.setInt(5, serverCode);
542
        pstmt.setString(6, this.docid);
543
      } else {
544
        System.err.println("Action not supported: " + action);
545
      }
546

    
547
      // Do the insertion
548
      pstmt.execute();
549
      pstmt.close();
550

    
551
    } catch (SQLException sqle) {
552
      throw sqle;
553
//    } catch (AccessionNumberException ane) {
554
//      MetaCatUtil.debugMessage("Invalid accession number.");
555
//      MetaCatUtil.debugMessage(ane.getMessage());
556
//      throw ane;
557
    } catch (Exception e) {
558
      throw e;
559
    }
560
  }
561

    
562
  /**
563
   * Get the document title
564
   */
565
  public String getTitle() {
566
    return doctitle;
567
  }
568

    
569
  /**
570
   * Set the document title
571
   *
572
   * @param title the new title for the document
573
   */
574

    
575
  public void setTitle( String title ) {
576
    this.doctitle = title;
577
    try {
578
      PreparedStatement pstmt;
579
      pstmt = conn.prepareStatement(
580
            "UPDATE xml_documents " +
581
            " SET doctitle = ? " +
582
            "WHERE docid = ?");
583

    
584
      // Bind the values to the query
585
      pstmt.setString(1, doctitle);
586
      pstmt.setString(2, docid);
587

    
588
      // Do the insertion
589
      pstmt.execute();
590
      pstmt.close();
591
    } catch (SQLException e) {
592
      System.out.println(e.getMessage());
593
    }
594
  }
595

    
596
  /**
597
   * Look up the title of the first child element named "title"
598
   * and record it as the document title
599
   */
600
/*   NOT NEEDED ANY MORE
601
  public void setTitleFromChildElement() {
602
    String title = null;
603
    long assigned_id=0;
604
    PreparedStatement pstmt;
605
    try {
606
      pstmt = conn.prepareStatement(
607
              "SELECT nodedata FROM xml_nodes " +
608
              "WHERE nodetype = 'TEXT' " +
609
              "AND rootnodeid = ? " +
610
              "AND parentnodeid IN " +
611
              "  (SELECT nodeid FROM xml_nodes " +
612
              "  WHERE nodename = 'title' " +
613
              "  AND nodetype =  'ELEMENT' " +
614
              "  AND rootnodeid = ? ) " +
615
              "ORDER BY nodeid");
616
*/
617
      // The above query might be slow, and probably will be because
618
      // it gets ALL of the title elements while searching for one
619
      // title in a small subtree but it avoids the problem of using
620
      // Oracle's Hierarchical Query syntax which is not portable --
621
      // the commented out SQL that follows shows an equivalent query
622
      // using Oracle-specific hierarchical query
623
      /*
624
      pstmt = conn.prepareStatement(
625
              "SELECT nodedata FROM xml_nodes " +
626
              "WHERE nodetype = 'TEXT' " +
627
              "AND parentnodeid IN " +
628
              "(SELECT nodeid FROM xml_nodes " +
629
              "WHERE nodename = 'title' " +
630
              "START WITH nodeid = ? " +
631
              "CONNECT BY PRIOR nodeid = parentnodeid)");
632
      */
633
/*
634
      // Bind the values to the query
635
      pstmt.setLong(1, rootnodeid);
636
      pstmt.setLong(2, rootnodeid);
637

    
638
      pstmt.execute();
639
      ResultSet rs = pstmt.getResultSet();
640
      boolean tableHasRows = rs.next();
641
      if (tableHasRows) {
642
        title = rs.getString(1);
643
      }
644
      pstmt.close();
645
    } catch (SQLException e) {
646
      System.out.println("Error getting title: " + e.getMessage());
647
    }
648

    
649
    // assign the new title
650
    this.setTitle(title);
651
  }
652
*/
653

    
654
  /**
655
   * Write an XML file to the database, given a filename
656
   *
657
   * @param conn the JDBC connection to the database
658
   * @param filename the filename to be loaded into the database
659
   * @param action the action to be performed (INSERT OR UPDATE)
660
   * @param docid the docid to use for the INSERT OR UPDATE
661
   */
662
  public static String write( Connection conn, String filename, String action, 
663
                              String docid, String user, String group )
664
                throws Exception {
665

    
666
    return write(conn, new FileReader(new File(filename).toString()), 
667
                  action, docid, user, group);
668
  }
669
  
670
  public static String write(Connection conn, Reader xml, String action, 
671
                              String docid, String user, String group)
672
                throws Exception {
673
    return write(conn, xml, action, docid, user, group, 1);
674
  }
675
  
676
  /**
677
   * Write an XML file to the database, given a Reader
678
   *
679
   * @param conn the JDBC connection to the database
680
   * @param xml the xml stream to be loaded into the database
681
   * @param action the action to be performed (INSERT OR UPDATE)
682
   * @param docid the docid to use for the INSERT OR UPDATE
683
   */
684
  public static String write( Connection conn, Reader xml, String action, 
685
                              String docid, String user, String group, int serverCode )
686
                throws Exception {
687

    
688
    // Determine if the docid is OK for INSERT or UPDATE
689
    AccessionNumber ac = new AccessionNumber(conn);
690
    String newdocid = ac.generate(docid, action);
691

    
692
    if ( action.equals("UPDATE") ) {
693
      // check for 'write' permission for 'user' to update this document
694
      if ( !hasWritePermission(conn, docid, user, group) ) {
695
        throw new Exception("User " + user + 
696
              " does not have permission to update XML Document #" + docid);
697
      }          
698
    }
699

    
700
    try {
701
        XMLReader parser = initializeParser(conn, action, newdocid, user, serverCode);
702
        conn.setAutoCommit(false);
703
        parser.parse(new InputSource(xml));
704
        conn.commit();
705
        conn.setAutoCommit(true);
706
        //return newdocid;
707
      } catch (SAXParseException e) {
708
        conn.rollback();
709
        conn.setAutoCommit(true);
710
        throw e;
711
      } catch (SAXException e) {
712
        conn.rollback();
713
        conn.setAutoCommit(true);
714
        throw e;
715
/*
716
        // If its a problem with the accession number its ok, just the 
717
        // accession number was regenerated
718
        AccessionNumberGeneratedException ang = null;
719
        try {
720
          Exception embedded = e.getException();
721
          if ((embedded != null) && 
722
              (embedded instanceof AccessionNumberGeneratedException)) {
723
            ang = (AccessionNumberGeneratedException)e.getException();
724
          }
725
        } catch (ClassCastException cce) {
726
          // Do nothing and just fall through to the ang != null test
727
        }
728
        if (ang != null) {
729
          conn.commit();
730
          conn.setAutoCommit(true);
731
          return (ang.getMessage());
732
        } else {
733
          conn.rollback();
734
          throw e;
735
        }
736
*/        
737
      } catch (Exception e) {
738
        conn.rollback();
739
        conn.setAutoCommit(true);
740
        throw e;
741
      }
742
      
743
      if ( (docid != null) && !(newdocid.equals(docid)) ) {
744
        return new String("New document ID generated:" + newdocid);
745
      } else {
746
        return newdocid;
747
      }
748
  }
749

    
750
  /**
751
   * Delete an XML file from the database (actually, just make it a revision
752
   * in the xml_revisions table)
753
   *
754
   * @param docid the ID of the document to be deleted from the database
755
   */
756
  public static void delete( Connection conn, String docid,
757
                                 String user, String group )
758
                throws Exception {
759

    
760
    // Determine if the docid is OK for DELETE
761
    AccessionNumber ac = new AccessionNumber(conn);
762
    String newdocid = ac.generate(docid, "DELETE");
763

    
764
    // check for 'write' permission for 'user' to delete this document
765
    if ( !hasWritePermission(conn, docid, user, group) ) {
766
      throw new Exception("User " + user + 
767
              " does not have permission to delete XML Document #" + docid);
768
    }
769

    
770
    conn.setAutoCommit(false);
771
    // Copy the record to the xml_revisions table
772
    DocumentImpl.archiveDocRevision( conn, docid, user );
773

    
774
    // Now delete it from the xml_documents table
775
    Statement stmt = conn.createStatement();
776
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
777
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
778
    stmt.close();
779
    conn.commit();
780
    conn.setAutoCommit(true);
781
  }
782
  
783
  /** Check for "write" permissions from DB connection */
784
  private static boolean hasWritePermission(Connection conn, String docid, 
785
                                     String user, String group) 
786
                                     throws SQLException {
787
    // b' of the command line invocation
788
    if ( (user == null) && (group == null) ) {
789
      return true;
790
    }
791

    
792
    PreparedStatement pstmt;
793
    // checking if user is owner of docid
794
    try {
795
      pstmt = conn.prepareStatement(
796
                   "SELECT 'x' FROM xml_documents " +
797
                   "WHERE docid LIKE ? AND user_owner LIKE ?");
798
      // Bind the values to the query
799
      pstmt.setString(1, docid);
800
      pstmt.setString(2, user);
801

    
802
      pstmt.execute();
803
      ResultSet rs = pstmt.getResultSet();
804
      boolean hasRow = rs.next();
805
      pstmt.close();
806
      if (hasRow) {
807
        return true;
808
      }
809
      
810
    } catch (SQLException e) {
811
      throw new 
812
        SQLException("Error checking document's owner: " + e.getMessage());
813
    }
814

    
815
    // checking access type from xml_access table
816
    int accesstype = 0;
817
    try {
818
      pstmt = conn.prepareStatement(
819
                   "SELECT access_type FROM xml_access " +
820
                   "WHERE docid LIKE ? " + 
821
                   "AND principal_name LIKE ? " +
822
                   "AND principal_type = 'user' " +
823
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
824
                                   "AND nvl(end_time,sysdate) " +
825
                   "UNION " +
826
                   "SELECT access_type FROM xml_access " +
827
                   "WHERE docid LIKE ? " + 
828
                   "AND principal_name LIKE ? " +
829
                   "AND principal_type = 'group' " +
830
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
831
                                   "AND nvl(end_time,sysdate)");
832
      // Bind the values to the query
833
      pstmt.setString(1, docid);
834
      pstmt.setString(2, user);
835
      pstmt.setString(3, docid);
836
      pstmt.setString(4, group);
837

    
838
      pstmt.execute();
839
      ResultSet rs = pstmt.getResultSet();
840
      boolean hasRows = rs.next();
841
      while ( hasRows ) {
842
        accesstype = rs.getInt(1);
843
        if ( (accesstype & WRITE) == WRITE ) {
844
          pstmt.close();
845
          return true;
846
        }
847
        hasRows = rs.next();
848
      }
849

    
850
      pstmt.close();
851
      return false;
852
      
853
    } catch (SQLException e) {
854
      throw new 
855
      SQLException("Error getting document's permissions: " + e.getMessage());
856
    }
857
  }
858

    
859
  /**
860
   * Set up the parser handlers for writing the document to the database
861
   */
862
  private static XMLReader initializeParser(Connection conn,
863
                           String action, String docid, String user, int serverCode) 
864
                           throws Exception {
865
    XMLReader parser = null;
866
    //
867
    // Set up the SAX document handlers for parsing
868
    //
869
    try {
870
      ContentHandler chandler   = new DBSAXHandler(conn, action, docid, user, serverCode);
871
      EntityResolver dbresolver = new DBEntityResolver(conn, 
872
                                      (DBSAXHandler)chandler);
873
      DTDHandler dtdhandler     = new DBDTDHandler(conn);
874

    
875
      // Get an instance of the parser
876
      MetaCatUtil util = new MetaCatUtil();
877
      String parserName = util.getOption("saxparser");
878
      parser = XMLReaderFactory.createXMLReader(parserName);
879

    
880
      // Turn off validation
881
      parser.setFeature("http://xml.org/sax/features/validation", false);
882
      
883
      // Set Handlers in the parser
884
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
885
                         chandler);
886
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
887
                         chandler);
888
      parser.setContentHandler((ContentHandler)chandler);
889
      parser.setEntityResolver((EntityResolver)dbresolver);
890
      parser.setDTDHandler((DTDHandler)dtdhandler);
891
      parser.setErrorHandler((ErrorHandler)chandler);
892

    
893
    } catch (Exception e) {
894
      throw e;
895
    }
896

    
897
    return parser;
898
  }
899

    
900
  /** Save a document entry in the xml_revisions table */
901
  private static void archiveDocRevision(Connection conn, String docid,
902
                                         String user) throws SQLException {
903
    // create a record in xml_revisions table 
904
    // for that document as selected from xml_documents
905
    PreparedStatement pstmt = conn.prepareStatement(
906
      "INSERT INTO xml_revisions " +
907
        "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
908
        "user_owner, user_updated, date_created, date_updated) " +
909
      "SELECT null, ?, rootnodeid, docname, doctype, doctitle," + 
910
        "user_owner, ?, sysdate, sysdate "+
911
      "FROM xml_documents " +
912
      "WHERE docid = ?");
913
    // Bind the values to the query and execute it
914
    pstmt.setString(1, docid);
915
    pstmt.setString(2, user);
916
    pstmt.setString(3, docid);
917
    pstmt.execute();
918
    pstmt.close();
919

    
920
  }
921

    
922
  /**
923
   * the main routine used to test the DBWriter utility.
924
   * <p>
925
   * Usage: java DocumentImpl <-f filename -a action -d docid>
926
   *
927
   * @param filename the filename to be loaded into the database
928
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
929
   * @param docid the id of the document to process
930
   */
931
  static public void main(String[] args) {
932
     
933
    try {
934
      String filename = null;
935
      String action   = null;
936
      String docid    = null;
937
      boolean showRuntime = false;
938
      boolean useOldReadAlgorithm = false;
939

    
940
      // Parse the command line arguments
941
      for ( int i=0 ; i < args.length; ++i ) {
942
        if ( args[i].equals( "-f" ) ) {
943
          filename =  args[++i];
944
        } else if ( args[i].equals( "-a" ) ) {
945
          action =  args[++i];
946
        } else if ( args[i].equals( "-d" ) ) {
947
          docid =  args[++i];
948
        } else if ( args[i].equals( "-t" ) ) {
949
          showRuntime = true;
950
        } else if ( args[i].equals( "-old" ) ) {
951
          useOldReadAlgorithm = true;
952
        } else {
953
          System.err.println
954
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
955
        }
956
      }
957
      
958
      // Check if the required arguments are provided
959
      boolean argsAreValid = false;
960
      if (action != null) {
961
        if (action.equals("INSERT")) {
962
          if (filename != null) {
963
            argsAreValid = true;
964
          } 
965
        } else if (action.equals("UPDATE")) {
966
          if ((filename != null) && (docid != null)) {
967
            argsAreValid = true;
968
          } 
969
        } else if (action.equals("DELETE")) {
970
          if (docid != null) {
971
            argsAreValid = true;
972
          } 
973
        } else if (action.equals("READ")) {
974
          if (docid != null) {
975
            argsAreValid = true;
976
          } 
977
        } 
978
      } 
979

    
980
      // Print usage message if the arguments are not valid
981
      if (!argsAreValid) {
982
        System.err.println("Wrong number of arguments!!!");
983
        System.err.println(
984
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename>");
985
        System.err.println(
986
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename>");
987
        System.err.println(
988
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
989
        System.err.println(
990
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
991
        return;
992
      }
993
      
994
      // Time the request if asked for
995
      double startTime = System.currentTimeMillis();
996
      
997
      // Open a connection to the database
998
      MetaCatUtil util = new MetaCatUtil();
999
      Connection dbconn = util.openDBConnection();
1000

    
1001
      double connTime = System.currentTimeMillis();
1002
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1003
      if (action.equals("READ")) {
1004
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1005
          if (useOldReadAlgorithm) {
1006
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1007
          } else {
1008
            xmldoc.toXml(new PrintWriter(System.out));
1009
          }
1010
      } else if (action.equals("DELETE")) {
1011
        DocumentImpl.delete(dbconn, docid, null, null);
1012
        System.out.println("Document deleted: " + docid);
1013
      } else {
1014
        String newdocid = DocumentImpl.write(dbconn, filename, action, docid,
1015
                                                                  null, null);
1016
        if ((docid != null) && (!docid.equals(newdocid))) {
1017
          if (action.equals("INSERT")) {
1018
            System.out.println("New document ID generated!!! ");
1019
          } else if (action.equals("UPDATE")) {
1020
            System.out.println("ERROR: Couldn't update document!!! ");
1021
          }
1022
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1023
          System.out.println("ERROR: Couldn't update document!!! ");
1024
        }
1025
        System.out.println("Document processing finished for: " + filename
1026
              + " (" + newdocid + ")");
1027
      }
1028

    
1029
      double stopTime = System.currentTimeMillis();
1030
      double dbOpenTime = (connTime - startTime)/1000;
1031
      double insertTime = (stopTime - connTime)/1000;
1032
      double executionTime = (stopTime - startTime)/1000;
1033
      if (showRuntime) {
1034
        System.out.println("\n\nTotal Execution time was: " + 
1035
                           executionTime + " seconds.");
1036
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
1037
                           " seconds.");
1038
        System.out.println("Time to insert document was: " + insertTime +
1039
                           " seconds.");
1040
      }
1041
      dbconn.close();
1042
    } catch (McdbException me) {
1043
      me.toXml(new PrintWriter(System.err));
1044
    } catch (AccessionNumberException ane) {
1045
      System.out.println("ERROR: Couldn't delete document!!! ");
1046
      System.out.println(ane.getMessage());
1047
    } catch (Exception e) {
1048
      System.err.println("EXCEPTION HANDLING REQUIRED");
1049
      System.err.println(e.getMessage());
1050
      e.printStackTrace(System.err);
1051
    }
1052
  }
1053
}
(19-19/34)