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: bojilova $'
10
 *     '$Date: 2000-09-20 13:15:58 -0700 (Wed, 20 Sep 2000) $'
11
 * '$Revision: 459 $'
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 system_id = null;
56
  private long rootnodeid;
57
  private ElementNode rootNode = null;
58
  private String doctitle = null;
59
  private TreeSet nodeRecordList = null;
60

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

    
90
  /** 
91
   * 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
   *
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
                      String doctype, String docid, String action, String user)
110
                      throws SQLException, Exception
111
  {
112
    this.conn = conn;
113
    this.rootnodeid = rootnodeid;
114
    this.docname = docname;
115
    this.doctype = doctype;
116
    this.docid = docid;
117
    writeDocumentToDB(action, user);
118
  }
119

    
120
  /**
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
  /** 
149
   * Get the document identifier (docid)
150
   */
151
  public String getDocID() {
152
    return docid;
153
  }
154

    
155

    
156
  /**
157
   * Print a string representation of the XML document
158
   */
159
  public String toString()
160
  {
161
    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
    StringBuffer doc = new StringBuffer();
180

    
181
    // Create the elements from the downloaded data in the TreeSet
182
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
183

    
184
    // 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
   * 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
            if (previousNodeWasElement) {
251
              out.print(">");
252
              previousNodeWasElement = false;
253
            }  
254
            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
   * 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
                               "WHERE docid LIKE ?");
344
      // 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
                                 "WHERE public_id LIKE ?");
362
        // 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
   * @param rootnodeid the id of the root node of the node tree to look up
386
   */
387
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
388
  {
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
           "FROM xml_nodes WHERE rootnodeid = ?");
408

    
409
      // Bind the values to the query
410
      pstmt.setLong(1, rootnodeid);
411

    
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
 /** creates SQL code and inserts new document into DB connection */
445
  private void writeDocumentToDB(String action, String user) 
446
               throws SQLException, Exception {
447
    try {
448
      PreparedStatement pstmt = null;
449

    
450
      if (action.equals("INSERT")) {
451
        //AccessionNumber ac = new AccessionNumber();
452
        //this.docid = ac.generate(docid, "INSERT");
453
        pstmt = conn.prepareStatement(
454
            "INSERT INTO xml_documents " +
455
            "(docid, rootnodeid, docname, doctype, " +
456
            "user_owner, user_updated, date_created, date_updated) " +
457
            "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate)");
458
        // Bind the values to the query
459
        pstmt.setString(1, this.docid);
460
        pstmt.setLong(2, rootnodeid);
461
        pstmt.setString(3, docname);
462
        pstmt.setString(4, doctype);
463
        pstmt.setString(5, user);
464
        pstmt.setString(6, user);
465
      } else if (action.equals("UPDATE")) {
466

    
467
        // Save the old document entry in a backup table
468
        DocumentImpl.archiveDocRevision( conn, docid, user );
469

    
470
        // Delete index for the old version of docid
471
        // The new index is inserting on the next calls to DBSAXNode
472
        pstmt = conn.prepareStatement(
473
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
474
        pstmt.execute();
475
        pstmt.close();
476

    
477
        // Update the new document to reflect the new node tree
478
        pstmt = conn.prepareStatement(
479
            "UPDATE xml_documents " +
480
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
481
            "user_updated = ?, date_updated = sysdate WHERE docid LIKE ?");
482
        // Bind the values to the query
483
        pstmt.setLong(1, rootnodeid);
484
        pstmt.setString(2, docname);
485
        pstmt.setString(3, doctype);
486
        pstmt.setString(4, user);
487
        pstmt.setString(5, this.docid);
488
      } else {
489
        System.err.println("Action not supported: " + action);
490
      }
491

    
492
      // Do the insertion
493
      pstmt.execute();
494
      pstmt.close();
495

    
496
    } catch (SQLException sqle) {
497
      throw sqle;
498
//    } catch (AccessionNumberException ane) {
499
//      MetaCatUtil.debugMessage("Invalid accession number.");
500
//      MetaCatUtil.debugMessage(ane.getMessage());
501
//      throw ane;
502
    } catch (Exception e) {
503
      throw e;
504
    }
505
  }
506

    
507
  /**
508
   * Get the document title
509
   */
510
  public String getTitle() {
511
    return doctitle;
512
  }
513

    
514
  /**
515
   * Set the document title
516
   *
517
   * @param title the new title for the document
518
   */
519
  public void setTitle( String title ) {
520
    this.doctitle = title;
521
    try {
522
      PreparedStatement pstmt;
523
      pstmt = conn.prepareStatement(
524
            "UPDATE xml_documents " +
525
            " SET doctitle = ? " +
526
            "WHERE docid = ?");
527

    
528
      // Bind the values to the query
529
      pstmt.setString(1, doctitle);
530
      pstmt.setString(2, docid);
531

    
532
      // Do the insertion
533
      pstmt.execute();
534
      pstmt.close();
535
    } catch (SQLException e) {
536
      System.out.println(e.getMessage());
537
    }
538
  }
539

    
540
  /**
541
   * Look up the title of the first child element named "title"
542
   * and record it as the document title
543
   */
544
/*   NOT NEEDED ANY MORE
545
  public void setTitleFromChildElement() {
546
    String title = null;
547
    long assigned_id=0;
548
    PreparedStatement pstmt;
549
    try {
550
      pstmt = conn.prepareStatement(
551
              "SELECT nodedata FROM xml_nodes " +
552
              "WHERE nodetype = 'TEXT' " +
553
              "AND rootnodeid = ? " +
554
              "AND parentnodeid IN " +
555
              "  (SELECT nodeid FROM xml_nodes " +
556
              "  WHERE nodename = 'title' " +
557
              "  AND nodetype =  'ELEMENT' " +
558
              "  AND rootnodeid = ? ) " +
559
              "ORDER BY nodeid");
560
*/
561
      // The above query might be slow, and probably will be because
562
      // it gets ALL of the title elements while searching for one
563
      // title in a small subtree but it avoids the problem of using
564
      // Oracle's Hierarchical Query syntax which is not portable --
565
      // the commented out SQL that follows shows an equivalent query
566
      // using Oracle-specific hierarchical query
567
      /*
568
      pstmt = conn.prepareStatement(
569
              "SELECT nodedata FROM xml_nodes " +
570
              "WHERE nodetype = 'TEXT' " +
571
              "AND parentnodeid IN " +
572
              "(SELECT nodeid FROM xml_nodes " +
573
              "WHERE nodename = 'title' " +
574
              "START WITH nodeid = ? " +
575
              "CONNECT BY PRIOR nodeid = parentnodeid)");
576
      */
577
/*
578
      // Bind the values to the query
579
      pstmt.setLong(1, rootnodeid);
580
      pstmt.setLong(2, rootnodeid);
581

    
582
      pstmt.execute();
583
      ResultSet rs = pstmt.getResultSet();
584
      boolean tableHasRows = rs.next();
585
      if (tableHasRows) {
586
        title = rs.getString(1);
587
      }
588
      pstmt.close();
589
    } catch (SQLException e) {
590
      System.out.println("Error getting title: " + e.getMessage());
591
    }
592

    
593
    // assign the new title
594
    this.setTitle(title);
595
  }
596
*/
597

    
598
  /**
599
   * Write an XML file to the database, given a filename
600
   *
601
   * @param conn the JDBC connection to the database
602
   * @param filename the filename to be loaded into the database
603
   * @param action the action to be performed (INSERT OR UPDATE)
604
   * @param docid the docid to use for the INSERT OR UPDATE
605
   */
606
  public static String write( Connection conn, String filename, String action, 
607
                              String docid, String user, String group )
608
                throws Exception {
609

    
610
    return write(conn, new FileReader(new File(filename).toString()), 
611
                  action, docid, user, group);
612
  }
613
  
614
  /**
615
   * Write an XML file to the database, given a Reader
616
   *
617
   * @param conn the JDBC connection to the database
618
   * @param xml the xml stream to be loaded into the database
619
   * @param action the action to be performed (INSERT OR UPDATE)
620
   * @param docid the docid to use for the INSERT OR UPDATE
621
   */
622
  public static String write( Connection conn, Reader xml, String action, 
623
                              String docid, String user, String group )
624
                throws Exception {
625

    
626
    // Determine if the docid is OK for INSERT or UPDATE
627
    AccessionNumber ac = new AccessionNumber(conn);
628
    String newdocid = ac.generate(docid, action);
629

    
630
    if ( action.equals("UPDATE") ) {
631
      // check for 'write' permission for 'user' to update this document
632
      if ( !hasWritePermission(conn, docid, user, group) ) {
633
        throw new Exception("User " + user + 
634
              " does not have permission to update XML Document #" + docid);
635
      }          
636
    }
637

    
638
    try {
639
        XMLReader parser = initializeParser(conn, action, newdocid, user);
640
        conn.setAutoCommit(false);
641
        parser.parse(new InputSource(xml));
642
        conn.commit();
643
        conn.setAutoCommit(true);
644
        //return newdocid;
645
      } catch (SAXParseException e) {
646
        conn.rollback();
647
        conn.setAutoCommit(true);
648
        throw e;
649
      } catch (SAXException e) {
650
        conn.rollback();
651
        conn.setAutoCommit(true);
652
        throw e;
653
/*
654
        // If its a problem with the accession number its ok, just the 
655
        // accession number was regenerated
656
        AccessionNumberGeneratedException ang = null;
657
        try {
658
          Exception embedded = e.getException();
659
          if ((embedded != null) && 
660
              (embedded instanceof AccessionNumberGeneratedException)) {
661
            ang = (AccessionNumberGeneratedException)e.getException();
662
          }
663
        } catch (ClassCastException cce) {
664
          // Do nothing and just fall through to the ang != null test
665
        }
666
        if (ang != null) {
667
          conn.commit();
668
          conn.setAutoCommit(true);
669
          return (ang.getMessage());
670
        } else {
671
          conn.rollback();
672
          throw e;
673
        }
674
*/        
675
      } catch (Exception e) {
676
        conn.rollback();
677
        conn.setAutoCommit(true);
678
        throw e;
679
      }
680
      
681
      if ( (docid != null) && !(newdocid.equals(docid)) ) {
682
        return new String("New document ID generated:" + newdocid);
683
      } else {
684
        return newdocid;
685
      }
686
  }
687

    
688
  /**
689
   * Delete an XML file from the database (actually, just make it a revision
690
   * in the xml_revisions table)
691
   *
692
   * @param docid the ID of the document to be deleted from the database
693
   */
694
  public static void delete( Connection conn, String docid,
695
                                 String user, String group )
696
                throws Exception {
697

    
698
    // Determine if the docid is OK for DELETE
699
    AccessionNumber ac = new AccessionNumber(conn);
700
    String newdocid = ac.generate(docid, "DELETE");
701

    
702
    // check for 'write' permission for 'user' to delete this document
703
    if ( !hasWritePermission(conn, docid, user, group) ) {
704
      throw new Exception("User " + user + 
705
              " does not have permission to delete XML Document #" + docid);
706
    }
707

    
708
    conn.setAutoCommit(false);
709
    // Copy the record to the xml_revisions table
710
    DocumentImpl.archiveDocRevision( conn, docid, user );
711

    
712
    // Now delete it from the xml_documents table
713
    Statement stmt = conn.createStatement();
714
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
715
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
716
    stmt.close();
717
    conn.commit();
718
    conn.setAutoCommit(true);
719
  }
720
  
721
  /** Check for "write" permissions from DB connection */
722
  private static boolean hasWritePermission(Connection conn, String docid, 
723
                                     String user, String group) 
724
                                     throws SQLException {
725
    // b' of the command line invocation
726
    if ( (user == null) && (group == null) ) {
727
      return true;
728
    }
729

    
730
    PreparedStatement pstmt;
731
    // checking if user is owner of docid
732
    try {
733
      pstmt = conn.prepareStatement(
734
                   "SELECT 'x' FROM xml_documents " +
735
                   "WHERE docid LIKE ? AND user_owner LIKE ?");
736
      // Bind the values to the query
737
      pstmt.setString(1, docid);
738
      pstmt.setString(2, user);
739

    
740
      pstmt.execute();
741
      ResultSet rs = pstmt.getResultSet();
742
      boolean hasRow = rs.next();
743
      pstmt.close();
744
      if (hasRow) {
745
        return true;
746
      }
747
      
748
    } catch (SQLException e) {
749
      throw new 
750
        SQLException("Error checking document's owner: " + e.getMessage());
751
    }
752

    
753
    // checking access type from xml_access table
754
    int accesstype = 0;
755
    try {
756
      pstmt = conn.prepareStatement(
757
                   "SELECT access_type FROM xml_access " +
758
                   "WHERE docid LIKE ? " + 
759
                   "AND principal_name LIKE ? " +
760
                   "AND principal_type = 'user' " +
761
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
762
                                   "AND nvl(end_time,sysdate) " +
763
                   "UNION " +
764
                   "SELECT access_type FROM xml_access " +
765
                   "WHERE docid LIKE ? " + 
766
                   "AND principal_name LIKE ? " +
767
                   "AND principal_type = 'group' " +
768
                   "AND sysdate BETWEEN nvl(begin_time,sysdate) " +
769
                                   "AND nvl(end_time,sysdate)");
770
      // Bind the values to the query
771
      pstmt.setString(1, docid);
772
      pstmt.setString(2, user);
773
      pstmt.setString(3, docid);
774
      pstmt.setString(2, group);
775

    
776
      pstmt.execute();
777
      ResultSet rs = pstmt.getResultSet();
778
      boolean hasRows = rs.next();
779
      while ( hasRows ) {
780
        accesstype = rs.getInt(1);
781
        if ( (accesstype & WRITE) == WRITE ) {
782
          pstmt.close();
783
          return true;
784
        }
785
        hasRows = rs.next();
786
      }
787

    
788
      pstmt.close();
789
      return false;
790
      
791
    } catch (SQLException e) {
792
      throw new 
793
      SQLException("Error getting document's permissions: " + e.getMessage());
794
    }
795
  }
796

    
797
  /**
798
   * Set up the parser handlers for writing the document to the database
799
   */
800
  private static XMLReader initializeParser(Connection conn,
801
                           String action, String docid, String user) 
802
                           throws Exception {
803
    XMLReader parser = null;
804
    //
805
    // Set up the SAX document handlers for parsing
806
    //
807
    try {
808
      ContentHandler chandler   = new DBSAXHandler(conn, action, docid, user);
809
      EntityResolver dbresolver = new DBEntityResolver(conn, 
810
                                      (DBSAXHandler)chandler);
811
      DTDHandler dtdhandler     = new DBDTDHandler(conn);
812

    
813
      // Get an instance of the parser
814
      MetaCatUtil util = new MetaCatUtil();
815
      String parserName = util.getOption("saxparser");
816
      parser = XMLReaderFactory.createXMLReader(parserName);
817

    
818
      // Turn off validation
819
      parser.setFeature("http://xml.org/sax/features/validation", false);
820
      
821
      // Set Handlers in the parser
822
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
823
                         chandler);
824
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
825
                         chandler);
826
      parser.setContentHandler((ContentHandler)chandler);
827
      parser.setEntityResolver((EntityResolver)dbresolver);
828
      parser.setDTDHandler((DTDHandler)dtdhandler);
829
      parser.setErrorHandler((ErrorHandler)chandler);
830

    
831
    } catch (Exception e) {
832
      throw e;
833
    }
834

    
835
    return parser;
836
  }
837

    
838
  /** Save a document entry in the xml_revisions table */
839
  private static void archiveDocRevision(Connection conn, String docid,
840
                                         String user) throws SQLException {
841
    // create a record in xml_revisions table 
842
    // for that document as selected from xml_documents
843
    PreparedStatement pstmt = conn.prepareStatement(
844
      "INSERT INTO xml_revisions " +
845
        "(revisionid, docid, rootnodeid, docname, doctype, doctitle, " +
846
        "user_owner, user_updated, date_created, date_updated) " +
847
      "SELECT null, ?, rootnodeid, docname, doctype, doctitle," + 
848
        "user_owner, ?, sysdate, sysdate "+
849
      "FROM xml_documents " +
850
      "WHERE docid = ?");
851
    // Bind the values to the query and execute it
852
    pstmt.setString(1, docid);
853
    pstmt.setString(2, user);
854
    pstmt.setString(3, docid);
855
    pstmt.execute();
856
    pstmt.close();
857

    
858
  }
859

    
860
  /**
861
   * the main routine used to test the DBWriter utility.
862
   * <p>
863
   * Usage: java DocumentImpl <-f filename -a action -d docid>
864
   *
865
   * @param filename the filename to be loaded into the database
866
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
867
   * @param docid the id of the document to process
868
   */
869
  static public void main(String[] args) {
870
     
871
    try {
872
      String filename = null;
873
      String action   = null;
874
      String docid    = null;
875
      boolean showRuntime = false;
876
      boolean useOldReadAlgorithm = false;
877

    
878
      // Parse the command line arguments
879
      for ( int i=0 ; i < args.length; ++i ) {
880
        if ( args[i].equals( "-f" ) ) {
881
          filename =  args[++i];
882
        } else if ( args[i].equals( "-a" ) ) {
883
          action =  args[++i];
884
        } else if ( args[i].equals( "-d" ) ) {
885
          docid =  args[++i];
886
        } else if ( args[i].equals( "-t" ) ) {
887
          showRuntime = true;
888
        } else if ( args[i].equals( "-old" ) ) {
889
          useOldReadAlgorithm = true;
890
        } else {
891
          System.err.println
892
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
893
        }
894
      }
895
      
896
      // Check if the required arguments are provided
897
      boolean argsAreValid = false;
898
      if (action != null) {
899
        if (action.equals("INSERT")) {
900
          if (filename != null) {
901
            argsAreValid = true;
902
          } 
903
        } else if (action.equals("UPDATE")) {
904
          if ((filename != null) && (docid != null)) {
905
            argsAreValid = true;
906
          } 
907
        } else if (action.equals("DELETE")) {
908
          if (docid != null) {
909
            argsAreValid = true;
910
          } 
911
        } else if (action.equals("READ")) {
912
          if (docid != null) {
913
            argsAreValid = true;
914
          } 
915
        } 
916
      } 
917

    
918
      // Print usage message if the arguments are not valid
919
      if (!argsAreValid) {
920
        System.err.println("Wrong number of arguments!!!");
921
        System.err.println(
922
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename>");
923
        System.err.println(
924
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename>");
925
        System.err.println(
926
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
927
        System.err.println(
928
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
929
        return;
930
      }
931
      
932
      // Time the request if asked for
933
      double startTime = System.currentTimeMillis();
934
      
935
      // Open a connection to the database
936
      MetaCatUtil util = new MetaCatUtil();
937
      Connection dbconn = util.openDBConnection();
938

    
939
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
940
      if (action.equals("READ")) {
941
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
942
          if (useOldReadAlgorithm) {
943
            System.out.println(xmldoc.readUsingSlowAlgorithm());
944
          } else {
945
            xmldoc.toXml(new PrintWriter(System.out));
946
          }
947
      } else if (action.equals("DELETE")) {
948
        DocumentImpl.delete(dbconn, docid, null, null);
949
        System.out.println("Document deleted: " + docid);
950
      } else {
951
        String newdocid = DocumentImpl.write(dbconn, filename, action, docid,
952
                                                                  null, null);
953
        if ((docid != null) && (!docid.equals(newdocid))) {
954
          if (action.equals("INSERT")) {
955
            System.out.println("New document ID generated!!! ");
956
          } else if (action.equals("UPDATE")) {
957
            System.out.println("ERROR: Couldn't update document!!! ");
958
          }
959
        } else if ((docid == null) && (action.equals("UPDATE"))) {
960
          System.out.println("ERROR: Couldn't update document!!! ");
961
        }
962
        System.out.println("Document processing finished for: " + filename
963
              + " (" + newdocid + ")");
964
      }
965

    
966
      double stopTime = System.currentTimeMillis();
967
      double executionTime = (stopTime - startTime)/1000;
968
      if (showRuntime) {
969
        System.out.println("\n\nExecution time was: " + 
970
                           executionTime + " seconds");
971
      }
972
    } catch (McdbException me) {
973
      me.toXml(new PrintWriter(System.err));
974
    } catch (AccessionNumberException ane) {
975
      System.out.println("ERROR: Couldn't delete document!!! ");
976
      System.out.println(ane.getMessage());
977
    } catch (Exception e) {
978
      System.err.println("EXCEPTION HANDLING REQUIRED");
979
      System.err.println(e.getMessage());
980
      e.printStackTrace(System.err);
981
    }
982
  }
983
}
(15-15/28)