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-01-31 10:50:25 -0800 (Wed, 31 Jan 2001) $'
11
 * '$Revision: 691 $'
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 int 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, 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, 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 int 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, ");
506
      sql.append("user_owner, user_updated, server_location, ");
507
      sql.append("rev 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(5);
527
        this.updatedate     = rs.getString(6);
528
        this.userowner      = rs.getString(7);
529
        this.userupdated    = rs.getString(8);
530
        this.serverlocation = rs.getInt(9);
531
        //this.publicaccess   = rs.getInt(10);
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
  /** creates SQL code and inserts new document into DB connection 
628
   default serverCode of 1*/
629
  private void writeDocumentToDB(String action, String user)
630
               throws SQLException, Exception
631
  {
632
    writeDocumentToDB(action, user, null, 1);
633
  }
634

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

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

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

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

    
706
      } else {
707
        System.err.println("Action not supported: " + action);
708
      }
709

    
710
      // Do the insertion
711
      pstmt.execute();
712
      pstmt.close();
713

    
714
    } catch (SQLException sqle) {
715
      throw sqle;
716
    } catch (Exception e) {
717
      throw e;
718
    }
719
  }
720

    
721
// DOCTITLE attr cleared from the db
722
//  /**
723
//   * Get the document title
724
//   */
725
//  public String getTitle() {
726
//    return doctitle;
727
//  }
728

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

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

    
782
  public static String write(Connection conn,Reader xml,String pub,Reader dtd,
783
                             String action, String docid, String user,
784
                             String group )
785
                throws Exception {
786
    return write ( conn, xml, pub, dtd, action, docid, user, group, 1, false);
787
  }
788

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

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

    
948
      if ( !hasPermission(conn, user, group, docid) ) {
949
        throw new Exception("User " + user + 
950
              " does not have permission to update XML Document #" + docid);
951
      }          
952
    }
953

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

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

    
1003
    // check for 'write' permission for 'user' to delete this document
1004
    if ( !hasPermission(conn, user, group, docid) ) {
1005
      throw new Exception("User " + user + 
1006
              " does not have permission to delete XML Document #" + docid);
1007
    }
1008

    
1009
    conn.setAutoCommit(false);
1010
    // Copy the record to the xml_revisions table
1011
    DocumentImpl.archiveDocRevision( conn, docid, user );
1012

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

    
1033
  /** 
1034
    * Check for "WRITE" permission on @docid for @user and/or @group 
1035
    * from DB connection 
1036
    */
1037
  private static boolean hasPermission( Connection conn, String user,
1038
                                        String group, String docid) 
1039
                         throws SQLException 
1040
  {
1041
    // b' of the command line invocation
1042
    if ( (user == null) && (group == null) ) {
1043
      return true;
1044
    }
1045

    
1046
    // Check for WRITE permission on @docid for @user and/or @group
1047
    AccessControlList aclobj = new AccessControlList(conn);
1048
    boolean hasPermission = aclobj.hasPermission("WRITE",user,docid);
1049
    if ( !hasPermission && group != null ) {
1050
      hasPermission = aclobj.hasPermission("WRITE",group,docid);
1051
    }
1052
    
1053
    return hasPermission;
1054
  }
1055

    
1056
  /**
1057
   * Set up the parser handlers for writing the document to the database
1058
   */
1059
  private static XMLReader initializeParser(Connection conn, String action,
1060
                                   String docid, String user, String group,
1061
                                   String pub, int serverCode, Reader dtd) 
1062
                           throws Exception 
1063
  {
1064
    XMLReader parser = null;
1065
    //
1066
    // Set up the SAX document handlers for parsing
1067
    //
1068
    try {
1069
      ContentHandler chandler = new DBSAXHandler(conn, action, docid,
1070
                                                 user, group, pub, serverCode);
1071
      EntityResolver eresolver= new DBEntityResolver(conn,
1072
                                                 (DBSAXHandler)chandler, dtd);
1073
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1074

    
1075
      // Get an instance of the parser
1076
      MetaCatUtil util = new MetaCatUtil();
1077
      String parserName = util.getOption("saxparser");
1078
      parser = XMLReaderFactory.createXMLReader(parserName);
1079

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

    
1098
    } catch (Exception e) {
1099
      throw e;
1100
    }
1101

    
1102
    return parser;
1103
  }
1104

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

    
1127
  }
1128

    
1129
  /**
1130
   * the main routine used to test the DBWriter utility.
1131
   * <p>
1132
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1133
   *
1134
   * @param filename the filename to be loaded into the database
1135
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1136
   * @param docid the id of the document to process
1137
   */
1138
  static public void main(String[] args) {
1139
     
1140
    try {
1141
      String filename    = null;
1142
      String dtdfilename = null;
1143
      String action      = null;
1144
      String docid       = null;
1145
      boolean showRuntime = false;
1146
      boolean useOldReadAlgorithm = false;
1147

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

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

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

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