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: 2001-02-07 14:07:17 -0800 (Wed, 07 Feb 2001) $'
11
 * '$Revision: 697 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat;
29

    
30
import java.sql.*;
31
import java.io.File;
32
import java.io.FileReader;
33
import java.io.IOException;
34
import java.io.PrintWriter;
35
import java.io.Reader;
36
import java.io.StringWriter;
37
import java.io.Writer;
38
import java.io.InputStreamReader;
39

    
40
import java.util.Iterator;
41
import java.util.Stack;
42
import java.util.TreeSet;
43
import java.util.Enumeration;
44

    
45
import org.xml.sax.AttributeList;
46
import org.xml.sax.ContentHandler;
47
import org.xml.sax.DTDHandler;
48
import org.xml.sax.EntityResolver;
49
import org.xml.sax.ErrorHandler;
50
import org.xml.sax.InputSource;
51
import org.xml.sax.XMLReader;
52
import org.xml.sax.SAXException;
53
import org.xml.sax.SAXParseException;
54
import org.xml.sax.helpers.XMLReaderFactory;
55

    
56
import java.net.URL;
57

    
58
/**
59
 * A class that represents an XML document. It can be created with a simple
60
 * document identifier from a database connection.  It also will write an
61
 * XML text document to a database connection using SAX.
62
 */
63
public class DocumentImpl {
64

    
65
  static final int ALL = 1;
66
  static final int WRITE = 2;
67
  static final int READ = 4;
68

    
69
  private Connection conn = null;
70
  private String docid = null;
71
  private String docname = null;
72
  private String doctype = null;
73
// DOCTITLE attr cleared from the db
74
//  private String doctitle = null;
75
  private String createdate = null;
76
  private String updatedate = null;
77
  private String system_id = null;
78
  private String userowner = null;
79
  private String userupdated = null;
80
  private int rev;
81
  private int serverlocation;
82
  private String publicaccess; 
83
  private long rootnodeid;
84
  private ElementNode rootNode = null;
85
  private TreeSet nodeRecordList = null;
86

    
87
  /**
88
   * Constructor, creates document from database connection, used 
89
   * for reading the document
90
   *
91
   * @param conn the database connection from which to read the document
92
   * @param docid the identifier of the document to be created
93
   */
94
  public DocumentImpl(Connection conn, String docid) throws McdbException 
95
  {
96
    try { 
97
      this.conn = conn;
98
      this.docid = docid;
99
      
100
      DocumentIdentifier id = new DocumentIdentifier(docid);
101
      
102
  
103
      // Look up the document information
104
      getDocumentInfo(docid);
105
      
106
      // Download all of the document nodes using a single SQL query
107
      // The sort order of the records is determined by the NodeComparator
108
      // class, and needs to represent a depth-first traversal for the
109
      // toXml() method to work properly
110
      nodeRecordList = getNodeRecordList(rootnodeid);
111
  
112
    } catch (McdbException ex) {
113
      throw ex;
114
    } catch (Throwable t) {
115
      throw new McdbException("Error reading document from " +
116
                              "DocumentImpl.DocumentImpl: " + docid);
117
    }
118
  }
119

    
120
  /** 
121
   * Construct a new document instance, writing the contents to the database.
122
   * This method is called from DBSAXHandler because we need to know the
123
   * root element name for documents without a DOCTYPE before creating it.
124
   *
125
   * @param conn the JDBC Connection to which all information is written
126
   * @param rootnodeid - sequence id of the root node in the document
127
   * @param docname - the name of DTD, i.e. the name immediately following 
128
   *        the DOCTYPE keyword ( should be the root element name ) or
129
   *        the root element name if no DOCTYPE declaration provided
130
   *        (Oracle's and IBM parsers are not aware if it is not the 
131
   *        root element name)
132
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
133
   *                  following the PUBLIC keyword in DOCTYPE declaration or
134
   *                  the docname if no Public ID provided or
135
   *                  null if no DOCTYPE declaration provided
136
   * @param docid the docid to use for the INSERT OR UPDATE
137
   * @param action the action to be performed (INSERT OR UPDATE)
138
   * @param user the user that owns the document
139
   * @param pub flag for public "read" access on document
140
   * @param serverCode the serverid from xml_replication on which this document
141
   *        resides.
142
   *
143
   */
144
  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
145
                      String doctype, String docid, String action, String user,
146
                      String pub, String catalogid, int serverCode)
147
                      throws SQLException, Exception
148
  {
149
    this.conn = conn;
150
    this.rootnodeid = rootnodeid;
151
    this.docname = docname;
152
    this.doctype = doctype;
153
    this.docid = docid;
154
    writeDocumentToDB(action, user, pub, catalogid, serverCode);
155
  }
156
  
157
// NOT USED ANY MORE
158
//  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
159
//                      String doctype, String docid, String action, String user)
160
//                      throws SQLException, Exception
161
//  {
162
//    this.conn = conn;
163
//    this.rootnodeid = rootnodeid;
164
//    this.docname = docname;
165
//    this.doctype = doctype;
166
//    this.docid = docid;
167
//    writeDocumentToDB(action, user);
168
//  }
169

    
170
  /**
171
   * get the document name
172
   */
173
  public String getDocname() {
174
    return docname;
175
  }
176

    
177
  /**
178
   * get the document type (which is the PublicID)
179
   */
180
  public String getDoctype() {
181
    return doctype;
182
  }
183

    
184
  /**
185
   * get the system identifier
186
   */
187
  public String getSystemID() {
188
    return system_id;
189
  }
190

    
191
  /**
192
   * get the root node identifier
193
   */
194
  public long getRootNodeID() {
195
    return rootnodeid;
196
  }
197
  
198
  /**
199
   * get the creation date
200
   */
201
  public String getCreateDate() {
202
    return createdate;
203
  }
204
  
205
  /**
206
   * get the update date
207
   */
208
  public String getUpdateDate() {
209
    return updatedate;
210
  }
211

    
212
  /** 
213
   * Get the document identifier (docid)
214
   */
215
  public String getDocID() {
216
    return docid;
217
  }
218
  
219
// DOCTITLE attr cleared from the db
220
//  /**
221
//   *get the document title
222
//   */
223
//  public String getDocTitle() {
224
//    return doctitle;
225
//  }
226
  
227
  public String getUserowner() {
228
    return userowner;
229
  }
230
  
231
  public String getUserupdated() {
232
    return userupdated;
233
  }
234
  
235
  public int getServerlocation() {
236
    return serverlocation;
237
  }
238
  
239
  public String getPublicaccess() {
240
    return publicaccess;
241
  }
242
  
243
  public int getRev() {
244
    return rev;
245
  }
246

    
247
  /**
248
   * Print a string representation of the XML document
249
   */
250
  public String toString()
251
  {
252
    StringWriter docwriter = new StringWriter();
253
    this.toXml(docwriter);
254
    String document = docwriter.toString();
255
    return document;
256
  }
257

    
258
  /**
259
   * Get a text representation of the XML document as a string
260
   * This older algorithm uses a recursive tree of Objects to represent the
261
   * nodes of the tree.  Each object is passed the data for the document 
262
   * and searches all of the document data to find its children nodes and
263
   * recursively build.  Thus, because each node reads the whole document,
264
   * this algorithm is extremely slow for larger documents, and the time
265
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
266
   * better algorithm.
267
   */
268
  public String readUsingSlowAlgorithm()
269
  {
270
    StringBuffer doc = new StringBuffer();
271

    
272
    // Create the elements from the downloaded data in the TreeSet
273
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
274

    
275
    // Append the resulting document to the StringBuffer and return it
276
    doc.append("<?xml version=\"1.0\"?>\n");
277
      
278
    if (docname != null) {
279
      if ((doctype != null) && (system_id != null)) {
280
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
281
                   "\" \"" + system_id + "\">\n");
282
      } else {
283
        doc.append("<!DOCTYPE " + docname + ">\n");
284
      }
285
    }
286
    doc.append(rootNode.toString());
287
  
288
    return (doc.toString());
289
  }
290

    
291
  /**
292
   * Print a text representation of the XML document to a Writer
293
   *
294
   * @param pw the Writer to which we print the document
295
   */
296
  public void toXml(Writer pw)
297
  {
298
    PrintWriter out = null;
299
    if (pw instanceof PrintWriter) {
300
      out = (PrintWriter)pw;
301
    } else {
302
      out = new PrintWriter(pw);
303
    }
304

    
305
    MetaCatUtil util = new MetaCatUtil();
306
    
307
    Stack openElements = new Stack();
308
    boolean atRootElement = true;
309
    boolean previousNodeWasElement = false;
310

    
311
    // Step through all of the node records we were given
312
    Iterator it = nodeRecordList.iterator();
313
    while (it.hasNext()) {
314
      NodeRecord currentNode = (NodeRecord)it.next();
315
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
316
                          //" (" + currentNode.parentnodeid +
317
                          //", " + currentNode.nodeindex + 
318
                          //", " + currentNode.nodetype + 
319
                          //", " + currentNode.nodename + 
320
                          //", " + currentNode.nodedata + ")]");
321

    
322
      // Print the end tag for the previous node if needed
323
      //
324
      // This is determined by inspecting the parent nodeid for the
325
      // currentNode.  If it is the same as the nodeid of the last element
326
      // that was pushed onto the stack, then we are still in that previous
327
      // parent element, and we do nothing.  However, if it differs, then we
328
      // have returned to a level above the previous parent, so we go into
329
      // a loop and pop off nodes and print out their end tags until we get
330
      // the node on the stack to match the currentNode parentnodeid
331
      //
332
      // So, this of course means that we rely on the list of elements
333
      // having been sorted in a depth first traversal of the nodes, which
334
      // is handled by the NodeComparator class used by the TreeSet
335
      if (!atRootElement) {
336
        NodeRecord currentElement = (NodeRecord)openElements.peek();
337
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
338
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
339
            currentElement = (NodeRecord)openElements.pop();
340
            util.debugMessage("\n POPPED: " + currentElement.nodename);
341
            if (previousNodeWasElement) {
342
              out.print(">");
343
              previousNodeWasElement = false;
344
            }  
345
            out.print("</" + currentElement.nodename + ">" );
346
            currentElement = (NodeRecord)openElements.peek();
347
          }
348
        }
349
      }
350

    
351
      // Handle the DOCUMENT node
352
      if (currentNode.nodetype.equals("DOCUMENT")) {
353
        out.println("<?xml version=\"1.0\"?>");
354
      
355
        if (docname != null) {
356
          if ((doctype != null) && (system_id != null)) {
357
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
358
                       "\" \"" + system_id + "\">");
359
          } else {
360
            out.println("<!DOCTYPE " + docname + ">");
361
          }
362
        }
363

    
364
      // Handle the ELEMENT nodes
365
      } else if (currentNode.nodetype.equals("ELEMENT")) {
366
        if (atRootElement) {
367
          atRootElement = false;
368
        } else {
369
          if (previousNodeWasElement) {
370
            out.print(">");
371
          }
372
        }
373
        openElements.push(currentNode);
374
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
375
        previousNodeWasElement = true;
376
        out.print("<" + currentNode.nodename);
377

    
378
      // Handle the ATTRIBUTE nodes
379
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
380
        out.print(" " + currentNode.nodename + "=\""
381
                 + currentNode.nodedata + "\"");
382
      } else if (currentNode.nodetype.equals("TEXT")) {
383
        if (previousNodeWasElement) {
384
          out.print(">");
385
        }
386
        out.print(currentNode.nodedata);
387
        previousNodeWasElement = false;
388

    
389
      // Handle the COMMENT nodes
390
      } else if (currentNode.nodetype.equals("COMMENT")) {
391
        if (previousNodeWasElement) {
392
          out.print(">");
393
        }
394
        out.print("<!--" + currentNode.nodedata + "-->");
395
        previousNodeWasElement = false;
396

    
397
      // Handle the PI nodes
398
      } else if (currentNode.nodetype.equals("PI")) {
399
        if (previousNodeWasElement) {
400
          out.print(">");
401
        }
402
        out.print("<?" + currentNode.nodename + " " +
403
                        currentNode.nodedata + "?>");
404
        previousNodeWasElement = false;
405

    
406
      // Handle any other node type (do nothing)
407
      } else {
408
        // Any other types of nodes are not handled.
409
        // Probably should throw an exception here to indicate this
410
      }
411
      out.flush();
412
    }
413

    
414
    // Print the final end tag for the root element
415
    while(!openElements.empty())
416
    {
417
      NodeRecord currentElement = (NodeRecord)openElements.pop();
418
      util.debugMessage("\n POPPED: " + currentElement.nodename);
419
      out.print("</" + currentElement.nodename + ">" );
420
    }
421
    out.flush();
422
  }
423
  
424
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
425
  {
426
    //System.out.println("inRevisionOnly");
427
    PreparedStatement pstmt;
428
    String rev = docid.getRev();
429
    String newid = docid.getIdentifier();
430
    pstmt = conn.prepareStatement("select rev from xml_documents " +
431
                                  "where docid like '" + newid + "'");
432
    pstmt.execute();
433
    ResultSet rs = pstmt.getResultSet();
434
    boolean tablehasrows = rs.next();
435
    if(rev.equals("newest") || rev.equals("all"))
436
    {
437
      return false;
438
    }
439
    
440
    if(tablehasrows)
441
    {
442
      int r = rs.getInt(1);
443
      pstmt.close();
444
      if(new Integer(rev).intValue() == r)
445
      { //the current revision in in xml_documents
446
        //System.out.println("returning false");
447
        return false;
448
      }
449
      else if(new Integer(rev).intValue() < r)
450
      { //the current revision is in xml_revisions.
451
        //System.out.println("returning true");
452
        return true;
453
      }
454
      else if(new Integer(rev).intValue() > r)
455
      { //error, rev cannot be greater than r
456
        throw new Exception("requested revision cannot be greater than " +
457
                            "the latest revision number.");
458
      }
459
    }
460
    throw new Exception("the requested docid '" + docid.toString() + 
461
                        "' does not exist");
462
  }
463

    
464
  private void getDocumentInfo(String docid) throws McdbException, 
465
                                                    AccessionNumberException
466
  {
467
    getDocumentInfo(new DocumentIdentifier(docid));
468
  }
469
  
470
  /**
471
   * Look up the document type information from the database
472
   *
473
   * @param docid the id of the document to look up
474
   */
475
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException 
476
  {
477
    PreparedStatement pstmt;
478
    String table = "xml_documents";
479
    
480
    try
481
    {
482
      if(isRevisionOnly(docid))
483
      { //pull the document from xml_revisions instead of from xml_documents;
484
        table = "xml_revisions";
485
      }
486
    }
487
    catch(Exception e)
488
    {
489
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
490
                          e.getMessage());
491
    }
492
    
493
    //deal with the key words here.
494
    
495
    if(docid.getRev().equals("all"))
496
    {
497
      
498
    }
499
    
500
    try {
501
      StringBuffer sql = new StringBuffer();
502
// DOCTITLE attr cleared from the db
503
//      sql.append("SELECT docname, doctype, rootnodeid, doctitle, ");
504
      sql.append("SELECT docname, doctype, rootnodeid, ");
505
      sql.append("date_created, date_updated, user_owner, user_updated, ");
506
      sql.append("server_location, public_access, rev");
507
      sql.append(" FROM ").append(table);
508
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
509
      sql.append("' and rev like '").append(docid.getRev()).append("'");
510
      //System.out.println(sql.toString());
511
      pstmt =
512
        conn.prepareStatement(sql.toString());
513
      // Bind the values to the query
514
      //pstmt.setString(1, docid.getIdentifier());
515
      //pstmt.setString(2, docid.getRev());
516

    
517
      pstmt.execute();
518
      ResultSet rs = pstmt.getResultSet();
519
      boolean tableHasRows = rs.next();
520
      if (tableHasRows) {
521
        this.docname        = rs.getString(1);
522
        this.doctype        = rs.getString(2);
523
        this.rootnodeid     = rs.getLong(3);
524
// DOCTITLE attr cleared from the db
525
//        this.doctitle       = rs.getString(4);
526
        this.createdate     = rs.getString(4);
527
        this.updatedate     = rs.getString(5);
528
        this.userowner      = rs.getString(6);
529
        this.userupdated    = rs.getString(7);
530
        this.serverlocation = rs.getInt(8);
531
        this.publicaccess   = rs.getString(9);
532
        this.rev            = rs.getInt(10);
533
      } 
534

    
535
      if (this.doctype != null) {
536
        pstmt =
537
          conn.prepareStatement("SELECT system_id " +
538
                                  "FROM xml_catalog " +
539
                                 "WHERE public_id LIKE ?");
540
        // Bind the values to the query
541
        pstmt.setString(1, doctype);
542
  
543
        pstmt.execute();
544
        rs = pstmt.getResultSet();
545
        tableHasRows = rs.next();
546
        if (tableHasRows) {
547
          this.system_id  = rs.getString(1);
548
        } 
549
        //pstmt.close();
550
      }
551
    } catch (SQLException e) {
552
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
553
                          e.getMessage());
554
      e.printStackTrace(System.out);
555
      throw new McdbException("Error accessing database connection in " +
556
                              "DocumentImpl.getDocumentInfo: ", e);
557
    }
558

    
559
    if (this.docname == null) {
560
      throw new McdbDocNotFoundException("Document not found: " + docid);
561
    }
562
  }
563

    
564
  /**
565
   * Look up the node data from the database
566
   *
567
   * @param rootnodeid the id of the root node of the node tree to look up
568
   */
569
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
570
  {
571
    PreparedStatement pstmt;
572
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
573
    long nodeid = 0;
574
    long parentnodeid = 0;
575
    long nodeindex = 0;
576
    String nodetype = null;
577
    String nodename = null;
578
    String nodedata = null;
579

    
580
    try {
581
      pstmt =
582
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
583
           "nodetype,nodename,"+               
584
           "replace(" +
585
           "replace(" +
586
           "replace(nodedata,'&','&amp;') " +
587
           ",'<','&lt;') " +
588
           ",'>','&gt;') " +
589
           "FROM xml_nodes WHERE rootnodeid = ?");
590

    
591
      // Bind the values to the query
592
      pstmt.setLong(1, rootnodeid);
593

    
594
      pstmt.execute();
595
      ResultSet rs = pstmt.getResultSet();
596
      boolean tableHasRows = rs.next();
597
      while (tableHasRows) {
598
        nodeid = rs.getLong(1);
599
        parentnodeid = rs.getLong(2);
600
        nodeindex = rs.getLong(3);
601
        nodetype = rs.getString(4);
602
        nodename = rs.getString(5);
603
        nodedata = rs.getString(6);
604

    
605
        // add the data to the node record list hashtable
606
        NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid, 
607
                                   nodeindex, nodetype, nodename, nodedata);
608
        nodeRecordList.add(currentRecord);
609

    
610
        // Advance to the next node
611
        tableHasRows = rs.next();
612
      } 
613
      pstmt.close();
614

    
615
    } catch (SQLException e) {
616
      throw new McdbException("Error accessing database connection from " +
617
                              "DocumentImpl.getNodeRecordList ", e);
618
    }
619

    
620
    if (nodeRecordList != null) {
621
      return nodeRecordList;
622
    } else {
623
      throw new McdbException("Error getting node data: " + docid);
624
    }
625
  }
626
  
627
// NOT USED ANY MORE
628
//  /** creates SQL code and inserts new document into DB connection 
629
//   default serverCode of 1*/
630
//  private void writeDocumentToDB(String action, String user)
631
//               throws SQLException, Exception
632
//  {
633
//    writeDocumentToDB(action, user, null, 1);
634
//  }
635

    
636
 /** creates SQL code and inserts new document into DB connection */
637
  private void writeDocumentToDB(String action, String user, String pub, 
638
                                 String catalogid, int serverCode) 
639
               throws SQLException, Exception {
640
    try {
641
      PreparedStatement pstmt = null;
642

    
643
      if (action.equals("INSERT")) {
644
        //AccessionNumber ac = new AccessionNumber();
645
        //this.docid = ac.generate(docid, "INSERT");
646
        pstmt = conn.prepareStatement(
647
                "INSERT INTO xml_documents " +
648
                "(docid, rootnodeid, docname, doctype, " + 
649
                "user_owner, user_updated, date_created, date_updated, " + 
650
                "public_access, catalog_id, server_location) " +
651
                "VALUES (?, ?, ?, ?, ?, ?, sysdate, sysdate, ?, ?, ?)");
652
        //note that the server_location is set to 1. 
653
        //this means that "localhost" in the xml_replication table must
654
        //always be the first entry!!!!!
655
        
656
        // Bind the values to the query
657
        pstmt.setString(1, this.docid);
658
        pstmt.setLong(2, rootnodeid);
659
        pstmt.setString(3, docname);
660
        pstmt.setString(4, doctype);
661
        pstmt.setString(5, user);
662
        pstmt.setString(6, user);
663
        if ( pub == null ) {
664
          pstmt.setString(7, null);
665
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
666
          pstmt.setInt(7, 1);
667
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
668
          pstmt.setInt(7, 0);
669
        }
670
        pstmt.setString(8, catalogid);
671
        pstmt.setInt(9, serverCode);
672
      } else if (action.equals("UPDATE")) {
673

    
674
        // Save the old document entry in a backup table
675
        DocumentImpl.archiveDocRevision( conn, docid, user );
676
        DocumentImpl thisdoc = new DocumentImpl(conn, docid);
677
        int thisrev = thisdoc.getRev();
678
        thisrev++;
679
        // Delete index for the old version of docid
680
        // The new index is inserting on the next calls to DBSAXNode
681
        pstmt = conn.prepareStatement(
682
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
683
        pstmt.execute();
684
        //pstmt.close();
685

    
686
        // Update the new document to reflect the new node tree
687
        pstmt = conn.prepareStatement(
688
            "UPDATE xml_documents " +
689
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
690
            "user_updated = ?, date_updated = sysdate, " +
691
            "server_location = ?, rev = ?, public_access = ?, catalog_id = ? " +
692
            "WHERE docid LIKE ?");
693
        // Bind the values to the query
694
        pstmt.setLong(1, rootnodeid);
695
        pstmt.setString(2, docname);
696
        pstmt.setString(3, doctype);
697
        pstmt.setString(4, user);
698
        pstmt.setInt(5, serverCode);
699
        pstmt.setInt(6, thisrev);
700
        if ( pub == null ) {
701
          pstmt.setString(7, null);
702
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
703
          pstmt .setInt(7, 1);
704
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
705
          pstmt.setInt(7, 0);
706
        }
707
        pstmt.setString(8, catalogid);
708
        pstmt.setString(9, this.docid);
709

    
710
      } else {
711
        System.err.println("Action not supported: " + action);
712
      }
713

    
714
      // Do the insertion
715
      pstmt.execute();
716
      pstmt.close();
717

    
718
    } catch (SQLException sqle) {
719
      throw sqle;
720
    } catch (Exception e) {
721
      throw e;
722
    }
723
  }
724

    
725
// DOCTITLE attr cleared from the db
726
//  /**
727
//   * Get the document title
728
//   */
729
//  public String getTitle() {
730
//    return doctitle;
731
//  }
732

    
733
// DOCTITLE attr cleared from the db
734
//  /**
735
//   * Set the document title
736
//   *
737
//   * @param title the new title for the document
738
//   */
739
//  public void setTitle( String title ) {
740
//    this.doctitle = title;
741
//    try {
742
//      PreparedStatement pstmt;
743
//      pstmt = conn.prepareStatement(
744
//            "UPDATE xml_documents " +
745
//            " SET doctitle = ? " +
746
//            "WHERE docid = ?");
747
//
748
//      // Bind the values to the query
749
//      pstmt.setString(1, doctitle);
750
//      pstmt.setString(2, docid);
751
//
752
//      // Do the insertion
753
//      pstmt.execute();
754
//      pstmt.close();
755
//    } catch (SQLException e) {
756
//      System.out.println("Error in DocumentImpl.setTitle: " + e.getMessage());
757
//    }
758
//  }
759

    
760
  /**
761
   * Write an XML file to the database, given a filename
762
   *
763
   * @param conn the JDBC connection to the database
764
   * @param filename the filename to be loaded into the database
765
   * @param pub flag for public "read" access on document
766
   * @param dtdfilename the dtd to be uploaded on server's file system
767
   * @param action the action to be performed (INSERT OR UPDATE)
768
   * @param docid the docid to use for the INSERT OR UPDATE
769
   * @param user the user that owns the document
770
   * @param group the group to which user belongs
771
   */
772
  public static String write(Connection conn,String filename,
773
                             String pub, String dtdfilename,
774
                             String action, String docid, String user,
775
                             String group )
776
                throws Exception {
777
                  
778
    Reader dtd = null;
779
    if ( dtdfilename != null ) {
780
      dtd = new FileReader(new File(dtdfilename).toString());
781
    }
782
    return write ( conn, new FileReader(new File(filename).toString()),
783
                   pub, dtd, action, docid, user, group, false);
784
  }
785

    
786
  public static String write(Connection conn,Reader xml,String pub,Reader dtd,
787
                             String action, String docid, String user,
788
                             String group, boolean validate)
789
                throws Exception {
790
    return write(conn,xml,pub,dtd,action,docid,user,group,1,false,validate);
791
  }
792

    
793
  public static String write(Connection conn, Reader xml, String pub,
794
                             String action, String docid, String user,
795
                             String group )
796
                throws Exception {
797
    if(action.equals("UPDATE"))
798
    {//if the document is being updated then use the servercode from the 
799
     //originally inserted document.
800
      DocumentImpl doc = new DocumentImpl(conn, docid);
801
      int servercode = doc.getServerlocation();
802
      return write(conn, xml, pub, action, docid, user, group, servercode);
803
    }
804
    else
805
    {//if the file is being inserted then the servercode is always 1
806
      return write(conn, xml, pub, action, docid, user, group, 1);
807
    }
808
  }
809
  
810
  public static String write( Connection conn, Reader xml,
811
                              String action, String docid, String user,
812
                              String group, int serverCode )
813
                throws Exception
814
  {
815
    return write(conn,xml,null,action,docid,user,group,serverCode);
816
  }
817
  
818
  public static String write( Connection conn, Reader xml, String pub,
819
                              String action, String docid, String user,
820
                              String group, int serverCode) 
821
                throws Exception
822
  {
823
    return write(conn,xml,pub,null,action,docid,user,group,
824
                 serverCode,false,false);
825
  }
826
  
827
  public static String write( Connection conn, Reader xml, String pub,
828
                              String action, String docid, String user,
829
                              String group, int serverCode, boolean override)
830
                throws Exception
831
  {
832
    return write(conn,xml,pub,null,action,docid,user,group,
833
                 serverCode,override,false);
834
  }
835
  
836
  /**
837
   * Write an XML file to the database, given a Reader
838
   *
839
   * @param conn the JDBC connection to the database
840
   * @param xml the xml stream to be loaded into the database
841
   * @param pub flag for public "read" access on xml document
842
   * @param dtd the dtd to be uploaded on server's file system
843
   * @param action the action to be performed (INSERT OR UPDATE)
844
   * @param docid the docid to use for the INSERT OR UPDATE
845
   * @param user the user that owns the document
846
   * @param group the group to which user belongs
847
   * @param serverCode the serverid from xml_replication on which this document
848
   *        resides.
849
   * @param override flag to stop insert replication checking.
850
   *        if override = true then a document not belonging to the local server
851
   *        will not be checked upon update for a file lock.
852
   *        if override = false then a document not from this server, upon 
853
   *        update will be locked and version checked.
854
   */
855

    
856
  public static String write( Connection conn,Reader xml,String pub,Reader dtd,
857
                              String action, String docid, String user,
858
                              String group, int serverCode, boolean override,
859
                              boolean validate)
860
                throws Exception
861
  {
862
    //MOVED DOWN IN RelationHandler obj before write of relations
863
    //if(action.equals("UPDATE"))
864
    //{
865
    //  RelationHandler.deleteRelations(docid);
866
    //}
867
    
868
    int updaterev;
869
    MetaCatUtil util = new MetaCatUtil();
870
        // Determine if the docid is OK for INSERT or UPDATE
871
    AccessionNumber ac = new AccessionNumber(conn);
872
    String newdocid = ac.generate(docid, action);
873
    
874
    MetaCatUtil.debugMessage("action: " + action + " servercode: " + 
875
                             serverCode + " override: " + override);
876
                        
877
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
878
    { //if this document being written is not a resident of this server then
879
      //we need to try to get a lock from it's resident server.  If the
880
      //resident server will not give a lock then we send the user a message
881
      //saying that he/she needs to download a new copy of the file and
882
      //merge the differences manually.
883
      int istreamInt; 
884
      char istreamChar;
885
      DocumentImpl newdoc = new DocumentImpl(conn, docid);
886
      updaterev = newdoc.getRev();
887
      String server = MetacatReplication.getServer(serverCode);
888
      MetacatReplication.replLog("attempting to lock " + docid);
889
      URL u = new URL("http://" + server + "?action=getlock&updaterev=" + 
890
                      updaterev + "&docid=" + docid);
891
      System.out.println("sending message: " + u.toString());
892
      String serverResStr = MetacatReplication.getURLContent(u);
893
      String openingtag = serverResStr.substring(0, serverResStr.indexOf(">")+1);
894
      
895
      if(openingtag.equals("<lockgranted>"))
896
      {//the lock was granted go ahead with the insert
897
        try 
898
        {
899
          MetacatReplication.replLog("lock granted for " + docid + " from " +
900
                                      server);
901
          XMLReader parser = initializeParser(conn, action, newdocid, validate,
902
                                              user, group, pub, serverCode, dtd);
903
          conn.setAutoCommit(false);
904
          parser.parse(new InputSource(xml)); 
905
          conn.commit();
906
          conn.setAutoCommit(true);
907
        } 
908
        catch (Exception e) 
909
        {
910
          conn.rollback();
911
          conn.setAutoCommit(true);
912
          throw e;
913
        }
914
                
915
        //after inserting the document locally, tell the document's home server
916
        //to come get a copy from here.
917
        ForceReplicationHandler frh = new ForceReplicationHandler(docid);
918
        
919
        if ( (docid != null) && !(newdocid.equals(docid)) ) 
920
        {
921
          return new String("New document ID generated:" + newdocid);
922
        } 
923
        else 
924
        {
925
          return newdocid;
926
        }
927
      }
928
      else if(openingtag.equals("<filelocked>"))
929
      {//the file is currently locked by another user
930
       //notify our user to wait a few minutes, check out a new copy and try
931
       //again.
932
        //System.out.println("file locked");
933
        MetacatReplication.replLog("lock denied for " + docid + " on " +
934
                                   server + " reason: file already locked");
935
        throw new Exception("The file specified is already locked by another " +
936
                            "user.  Please wait 30 seconds, checkout the " +
937
                            "newer document, merge your changes and try " +
938
                            "again.");
939
      }
940
      else if(openingtag.equals("<outdatedfile>"))
941
      {//our file is outdated.  notify our user to check out a new copy of the
942
       //file and merge his version with the new version.
943
        //System.out.println("outdated file");
944
        MetacatReplication.replLog("lock denied for " + docid + " on " +
945
                                    server + " reason: local file outdated");
946
        throw new Exception("The file you are trying to update is an outdated" +
947
                            " version.  Please checkout the newest document, " +
948
                            "merge your changes and try again.");
949
      }
950
    }
951
    
952
    if ( action.equals("UPDATE") ) {
953
      // check for 'write' permission for 'user' to update this document
954

    
955
      if ( !hasPermission(conn, user, group, docid) ) {
956
        throw new Exception("User " + user + 
957
              " does not have permission to update XML Document #" + docid);
958
      }          
959
    }
960

    
961
    try 
962
    { 
963
      XMLReader parser = initializeParser(conn, action, newdocid, validate,
964
                                          user, group, pub, serverCode, dtd);
965
      conn.setAutoCommit(false);
966
      parser.parse(new InputSource(xml));
967
      conn.commit();
968
      conn.setAutoCommit(true);
969
    } 
970
    catch (Exception e) 
971
    {
972
      conn.rollback();
973
      conn.setAutoCommit(true);
974
      throw e;
975
    }
976
    
977
    //force replicate out the new document to each server in our server list.
978
    if(serverCode == 1)
979
    { //start the thread to replicate this new document out to the other servers
980
      ForceReplicationHandler frh = new ForceReplicationHandler(newdocid, 
981
                                                                action);
982
    }
983
      
984
    if ( (docid != null) && !(newdocid.equals(docid)) ) 
985
    {
986
      return new String("New document ID generated:" + newdocid);
987
    } 
988
    else 
989
    {
990
      return newdocid;
991
    }
992
  }
993

    
994
  /**
995
   * Delete an XML file from the database (actually, just make it a revision
996
   * in the xml_revisions table)
997
   *
998
   * @param docid the ID of the document to be deleted from the database
999
   */
1000
  public static void delete( Connection conn, String docid,
1001
                                 String user, String group )
1002
                throws Exception {
1003
    DocumentIdentifier id = new DocumentIdentifier(docid);
1004
    docid = id.getSiteCode() + id.getSeparator() + id.getUniqueId();
1005
    
1006
    // Determine if the docid is OK for DELETE
1007
    AccessionNumber ac = new AccessionNumber(conn);
1008
    String newdocid = ac.generate(docid, "DELETE");
1009

    
1010
    // check for 'write' permission for 'user' to delete this document
1011
    if ( !hasPermission(conn, user, group, docid) ) {
1012
      throw new Exception("User " + user + 
1013
              " does not have permission to delete XML Document #" + docid);
1014
    }
1015

    
1016
    conn.setAutoCommit(false);
1017
    // Copy the record to the xml_revisions table
1018
    DocumentImpl.archiveDocRevision( conn, docid, user );
1019

    
1020
    // Now delete it from the xml_documents table
1021
    
1022
    Statement stmt = conn.createStatement();
1023
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
1024
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
1025
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
1026
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + docid + "'");
1027
    stmt.execute("DELETE FROM xml_relation WHERE docid = '" + docid + "'");
1028
    stmt.close();
1029
    conn.commit();
1030
    conn.setAutoCommit(true);
1031
    //IF this is a package document:
1032
    //delete all of the relations that this document created.
1033
    //if the deleted document is a package document its relations should 
1034
    //no longer be active if it has been deleted from the system.
1035
    
1036
//    MOVED UP IN THE TRANSACTION
1037
//    RelationHandler.deleteRelations(docid);
1038
  }
1039

    
1040
  /** 
1041
    * Check for "WRITE" permission on @docid for @user and/or @group 
1042
    * from DB connection 
1043
    */
1044
  private static boolean hasPermission( Connection conn, String user,
1045
                                        String group, String docid) 
1046
                         throws SQLException 
1047
  {
1048
    // b' of the command line invocation
1049
    if ( (user == null) && (group == null) ) {
1050
      return true;
1051
    }
1052

    
1053
    // Check for WRITE permission on @docid for @user and/or @group
1054
    AccessControlList aclobj = new AccessControlList(conn);
1055
    boolean hasPermission = aclobj.hasPermission("WRITE",user,docid);
1056
    if ( !hasPermission && group != null ) {
1057
      hasPermission = aclobj.hasPermission("WRITE",group,docid);
1058
    }
1059
    
1060
    return hasPermission;
1061
  }
1062

    
1063
  /**
1064
   * Set up the parser handlers for writing the document to the database
1065
   */
1066
  private static XMLReader initializeParser(Connection conn, String action,
1067
                                   String docid, boolean validate, 
1068
                                   String user, String group, String pub, 
1069
                                   int serverCode, Reader dtd) 
1070
                           throws Exception 
1071
  {
1072
    XMLReader parser = null;
1073
    //
1074
    // Set up the SAX document handlers for parsing
1075
    //
1076
    try {
1077
      ContentHandler chandler = new DBSAXHandler(conn, action, docid,
1078
                                                 user, group, pub, serverCode);
1079
      EntityResolver eresolver= new DBEntityResolver(conn,
1080
                                                 (DBSAXHandler)chandler, dtd);
1081
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1082

    
1083
      // Get an instance of the parser
1084
      MetaCatUtil util = new MetaCatUtil();
1085
      String parserName = util.getOption("saxparser");
1086
      parser = XMLReaderFactory.createXMLReader(parserName);
1087

    
1088
      // Turn on validation
1089
      parser.setFeature("http://xml.org/sax/features/validation", validate);
1090
      // Turn off Including all external parameter entities
1091
      // (the external DTD subset also)
1092
      // Doesn't work well, probably the feature name is not correct
1093
      // parser.setFeature(
1094
      //  "http://xml.org/sax/features/external-parameter-entities", false);
1095
      
1096
      // Set Handlers in the parser
1097
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
1098
                         chandler);
1099
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
1100
                         chandler);
1101
      parser.setContentHandler((ContentHandler)chandler);
1102
      parser.setEntityResolver((EntityResolver)eresolver);
1103
      parser.setDTDHandler((DTDHandler)dtdhandler);
1104
      parser.setErrorHandler((ErrorHandler)chandler);
1105

    
1106
    } catch (Exception e) {
1107
      throw e;
1108
    }
1109

    
1110
    return parser;
1111
  }
1112

    
1113
  /** Save a document entry in the xml_revisions table */
1114
  private static void archiveDocRevision(Connection conn, String docid,
1115
                                         String user) 
1116
                                         throws SQLException {
1117
    // create a record in xml_revisions table 
1118
    // for that document as selected from xml_documents
1119
    PreparedStatement pstmt = conn.prepareStatement(
1120
      "INSERT INTO xml_revisions " +
1121
        "(revisionid, docid, rootnodeid, docname, doctype, " +
1122
        "user_owner, user_updated, date_created, date_updated, " +
1123
        "server_location, rev, public_access, catalog_id) " +
1124
      "SELECT null, ?, rootnodeid, docname, doctype, " + 
1125
        "user_owner, ?, sysdate, sysdate, server_location, rev, " +
1126
        "public_access, catalog_id " +
1127
      "FROM xml_documents " +
1128
      "WHERE docid = ?");
1129
    // Bind the values to the query and execute it
1130
    pstmt.setString(1, docid);
1131
    pstmt.setString(2, user);
1132
    pstmt.setString(3, docid);
1133
    pstmt.execute();
1134
    pstmt.close();
1135

    
1136
  }
1137

    
1138
  /**
1139
   * the main routine used to test the DBWriter utility.
1140
   * <p>
1141
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1142
   *
1143
   * @param filename the filename to be loaded into the database
1144
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1145
   * @param docid the id of the document to process
1146
   */
1147
  static public void main(String[] args) {
1148
     
1149
    try {
1150
      String filename    = null;
1151
      String dtdfilename = null;
1152
      String action      = null;
1153
      String docid       = null;
1154
      boolean showRuntime = false;
1155
      boolean useOldReadAlgorithm = false;
1156

    
1157
      // Parse the command line arguments
1158
      for ( int i=0 ; i < args.length; ++i ) {
1159
        if ( args[i].equals( "-f" ) ) {
1160
          filename =  args[++i];
1161
        } else if ( args[i].equals( "-r" ) ) {
1162
          dtdfilename =  args[++i];
1163
        } else if ( args[i].equals( "-a" ) ) {
1164
          action =  args[++i];
1165
        } else if ( args[i].equals( "-d" ) ) {
1166
          docid =  args[++i];
1167
        } else if ( args[i].equals( "-t" ) ) {
1168
          showRuntime = true;
1169
        } else if ( args[i].equals( "-old" ) ) {
1170
          useOldReadAlgorithm = true;
1171
        } else {
1172
          System.err.println
1173
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
1174
        }
1175
      }
1176
      
1177
      // Check if the required arguments are provided
1178
      boolean argsAreValid = false;
1179
      if (action != null) {
1180
        if (action.equals("INSERT")) {
1181
          if (filename != null) {
1182
            argsAreValid = true;
1183
          } 
1184
        } else if (action.equals("UPDATE")) {
1185
          if ((filename != null) && (docid != null)) {
1186
            argsAreValid = true;
1187
          } 
1188
        } else if (action.equals("DELETE")) {
1189
          if (docid != null) {
1190
            argsAreValid = true;
1191
          } 
1192
        } else if (action.equals("READ")) {
1193
          if (docid != null) {
1194
            argsAreValid = true;
1195
          } 
1196
        } 
1197
      } 
1198

    
1199
      // Print usage message if the arguments are not valid
1200
      if (!argsAreValid) {
1201
        System.err.println("Wrong number of arguments!!!");
1202
        System.err.println(
1203
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
1204
          "[-r dtdfilename]");
1205
        System.err.println(
1206
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
1207
          "[-r dtdfilename]");
1208
        System.err.println(
1209
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
1210
        System.err.println(
1211
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
1212
        return;
1213
      }
1214
      
1215
      // Time the request if asked for
1216
      double startTime = System.currentTimeMillis();
1217
      
1218
      // Open a connection to the database
1219
      MetaCatUtil util = new MetaCatUtil();
1220
      Connection dbconn = util.openDBConnection();
1221

    
1222
      double connTime = System.currentTimeMillis();
1223
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1224
      if (action.equals("READ")) {
1225
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1226
          if (useOldReadAlgorithm) {
1227
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1228
          } else {
1229
            xmldoc.toXml(new PrintWriter(System.out));
1230
          }
1231
      } else if (action.equals("DELETE")) {
1232
        DocumentImpl.delete(dbconn, docid, null, null);
1233
        System.out.println("Document deleted: " + docid);
1234
      } else {
1235
        String newdocid = DocumentImpl.write(dbconn, filename, null,
1236
                                             dtdfilename, action, docid,
1237
                                             null, null);
1238
        if ((docid != null) && (!docid.equals(newdocid))) {
1239
          if (action.equals("INSERT")) {
1240
            System.out.println("New document ID generated!!! ");
1241
          } else if (action.equals("UPDATE")) {
1242
            System.out.println("ERROR: Couldn't update document!!! ");
1243
          }
1244
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1245
          System.out.println("ERROR: Couldn't update document!!! ");
1246
        }
1247
        System.out.println("Document processing finished for: " + filename
1248
              + " (" + newdocid + ")");
1249
      }
1250

    
1251
      double stopTime = System.currentTimeMillis();
1252
      double dbOpenTime = (connTime - startTime)/1000;
1253
      double insertTime = (stopTime - connTime)/1000;
1254
      double executionTime = (stopTime - startTime)/1000;
1255
      if (showRuntime) {
1256
        System.out.println("\n\nTotal Execution time was: " + 
1257
                           executionTime + " seconds.");
1258
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
1259
                           " seconds.");
1260
        System.out.println("Time to insert document was: " + insertTime +
1261
                           " seconds.");
1262
      }
1263
      dbconn.close();
1264
    } catch (McdbException me) {
1265
      me.toXml(new PrintWriter(System.err));
1266
    } catch (AccessionNumberException ane) {
1267
      System.out.println("ERROR: Couldn't delete document!!! ");
1268
      System.out.println(ane.getMessage());
1269
    } catch (Exception e) {
1270
      System.err.println("EXCEPTION HANDLING REQUIRED");
1271
      System.err.println(e.getMessage());
1272
      e.printStackTrace(System.err);
1273
    }
1274
  }
1275
}
(26-26/43)