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: tao $'
10
 *     '$Date: 2002-04-23 18:06:29 -0700 (Tue, 23 Apr 2002) $'
11
 * '$Revision: 1021 $'
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
import java.text.SimpleDateFormat;
40

    
41
import java.util.Date;
42
import java.util.Iterator;
43
import java.util.Stack;
44
import java.util.TreeSet;
45
import java.util.Enumeration;
46

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

    
58
import java.net.URL;
59

    
60
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
61

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

    
69
  static final int ALL = 1;
70
  static final int WRITE = 2;
71
  static final int READ = 4;
72
  private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
73

    
74
  private Connection conn = null;
75
  private String docid = null;
76
  private String updatedVersion=null;
77
  private String docname = null;
78
  private String doctype = null;
79
// DOCTITLE attr cleared from the db
80
//  private String doctitle = null;
81
  private String createdate = null;
82
  private String updatedate = null;
83
  private String system_id = null;
84
  private String userowner = null;
85
  private String userupdated = null;
86
  private int rev;
87
  private int serverlocation;
88
  private String publicaccess; 
89
  private long rootnodeid;
90
  private ElementNode rootNode = null;
91
  private TreeSet nodeRecordList = null;
92
  
93
  /**
94
   * Constructor used to create a document and read the document information
95
   * from the database.  If readNodes is false, then the node data is not 
96
   * read at this time, but is deferred until it is needed (such as when a 
97
   * call to toXml() is made).  
98
   *
99
   * @param conn the database connection from which to read the document
100
   * @param docid the identifier of the document to be created
101
   * @param readNodes flag indicating whether the xmlnodes should be read
102
   */
103
  public DocumentImpl(Connection conn, String docid, boolean readNodes) 
104
         throws McdbException 
105
  {
106
    try {
107
      this.conn = conn;
108
      this.docid = docid;
109
      
110
      // Look up the document information
111
      getDocumentInfo(docid);
112

    
113
      if (readNodes) {
114
        // Download all of the document nodes using a single SQL query
115
        // The sort order of the records is determined by the NodeComparator
116
        // class, and needs to represent a depth-first traversal for the
117
        // toXml() method to work properly
118
        nodeRecordList = getNodeRecordList(rootnodeid);
119
      }
120
    
121
    } catch (McdbException ex) {
122
      throw ex;
123
    } catch (Throwable t) {
124
      throw new McdbException("Error reading document from " +
125
                              "DocumentImpl.DocumentImpl: " + docid);
126
    }
127
  }
128

    
129
  /**
130
   * Constructor, creates document from database connection, used 
131
   * for reading the document
132
   *
133
   * @param conn the database connection from which to read the document
134
   * @param docid the identifier of the document to be created
135
   */
136
  public DocumentImpl(Connection conn, String docid) throws McdbException 
137
  {
138
    this(conn, docid, true);
139
  }
140

    
141
  /** 
142
   * Construct a new document instance, writing the contents to the database.
143
   * This method is called from DBSAXHandler because we need to know the
144
   * root element name for documents without a DOCTYPE before creating it.
145
   *
146
   * @param conn the JDBC Connection to which all information is written
147
   * @param rootnodeid - sequence id of the root node in the document
148
   * @param docname - the name of DTD, i.e. the name immediately following 
149
   *        the DOCTYPE keyword ( should be the root element name ) or
150
   *        the root element name if no DOCTYPE declaration provided
151
   *        (Oracle's and IBM parsers are not aware if it is not the 
152
   *        root element name)
153
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
154
   *                  following the PUBLIC keyword in DOCTYPE declaration or
155
   *                  the docname if no Public ID provided or
156
   *                  null if no DOCTYPE declaration provided
157
   * @param docid the docid to use for the INSERT OR UPDATE
158
   * @param action the action to be performed (INSERT OR UPDATE)
159
   * @param user the user that owns the document
160
   * @param pub flag for public "read" access on document
161
   * @param serverCode the serverid from xml_replication on which this document
162
   *        resides.
163
   *
164
   */
165
  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
166
                      String doctype, String docid, String action, String user,
167
                      String pub, String catalogid, int serverCode)
168
                      throws SQLException, Exception
169
  {
170
    this.conn = conn;
171
    this.rootnodeid = rootnodeid;
172
    this.docname = docname;
173
    this.doctype = doctype;
174
    this.docid = docid;
175
    writeDocumentToDB(action, user, pub, catalogid, serverCode);
176
  }
177
  
178
  /** 
179
   * Construct a new document instance, writing the contents to the database.
180
   * This method is called from DBSAXHandler because we need to know the
181
   * root element name for documents without a DOCTYPE before creating it.
182
   *
183
   * In this constructor, the docid is without rev. There is a string rev to 
184
   * specify the revision user want to upadate. The revion is only need to be
185
   * greater than current one. It is not need to be sequent number just after
186
   * current one. So it is only used in update action
187
   * @param conn the JDBC Connection to which all information is written
188
   * @param rootnodeid - sequence id of the root node in the document
189
   * @param docname - the name of DTD, i.e. the name immediately following 
190
   *        the DOCTYPE keyword ( should be the root element name ) or
191
   *        the root element name if no DOCTYPE declaration provided
192
   *        (Oracle's and IBM parsers are not aware if it is not the 
193
   *        root element name)
194
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
195
   *                  following the PUBLIC keyword in DOCTYPE declaration or
196
   *                  the docname if no Public ID provided or
197
   *                  null if no DOCTYPE declaration provided
198
   * @param docid the docid to use for the UPDATE, no version number
199
   * @param version, need to be update
200
   * @param action the action to be performed (INSERT OR UPDATE)
201
   * @param user the user that owns the document
202
   * @param pub flag for public "read" access on document
203
   * @param serverCode the serverid from xml_replication on which this document
204
   *        resides.
205
   *
206
   */
207
  public DocumentImpl(Connection conn, long rootNodeId, String docName, 
208
                      String docType, String docId, String newRevision, 
209
                      String action, String user,
210
                      String pub, String catalogId, int serverCode)
211
                      throws SQLException, Exception
212
  {
213
    this.conn = conn;
214
    this.rootnodeid = rootNodeId;
215
    this.docname = docName;
216
    this.doctype = docType;
217
    this.docid = docId;
218
    this.updatedVersion = newRevision;
219
    writeDocumentToDB(action, user, pub, catalogId, serverCode);
220
  }
221
  
222
  /**
223
   * Register a document that resides on the filesystem with the database.
224
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
225
   * Creates a reference to a filesystem document (used for non-xml data files).
226
   *
227
   * @param conn the JDBC Connection to which all information is written
228
   * @param docname - the name of DTD, i.e. the name immediately following 
229
   *        the DOCTYPE keyword ( should be the root element name ) or
230
   *        the root element name if no DOCTYPE declaration provided
231
   *        (Oracle's and IBM parsers are not aware if it is not the 
232
   *        root element name)
233
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
234
   *                  following the PUBLIC keyword in DOCTYPE declaration or
235
   *                  the docname if no Public ID provided or
236
   *                  null if no DOCTYPE declaration provided
237
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
238
   *               includes a revision number for this revision of the document 
239
   *               (e.g., knb.1.1)
240
   * @param user the user that owns the document
241
   * @param serverCode the serverid from xml_replication on which this document
242
   *        resides.
243
   */
244
  public static void registerDocument(
245
                     String docname, String doctype, String accnum, 
246
                     String user, int serverCode)
247
                     throws SQLException, AccessionNumberException, Exception
248
  {
249
    Connection dbconn = null;
250
    MetaCatUtil util = new MetaCatUtil();
251
    AccessionNumber ac;
252
    try {
253
      dbconn = util.openDBConnection();
254
      String docIdWithoutRev=MetaCatUtil.getDocIdFromString(accnum);
255
      int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
256
      int revInDataBase=getLatestRevisionNumber(dbconn, docIdWithoutRev);
257
      //revIndataBase=-1, there is no record in xml_documents table
258
      //the data file is a new one, inert it into table
259
      //user specified rev should be great than 0
260
      if (revInDataBase==-1 && userSpecifyRev>0 )
261
      {
262
        ac = new AccessionNumber(dbconn, accnum, "insert");
263
      }
264
      //rev is greater the last revsion number and revInDataBase isn't -1
265
      // it is a updated data file
266
      else if (userSpecifyRev>revInDataBase && revInDataBase>0)
267
      {
268
        
269
        //archive the old entry 
270
        archiveDocRevision(dbconn, docIdWithoutRev, user);
271
        //delete the old entry in xml_documents
272
        deleteXMLDocuments(dbconn, docIdWithoutRev);
273
        ac = new AccessionNumber(dbconn, accnum, "update");
274
      }
275
      //other situation
276
      else
277
      {
278
        
279
        throw new Exception("Revision number couldn't be "
280
                    +userSpecifyRev);
281
      }
282
      String docid = ac.getDocid();
283
      String rev = ac.getRev();
284
      SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
285
      Date localtime = new Date();
286
      String dateString = formatter.format(localtime);
287
  
288
      String sqlDateString = "to_date('" + dateString + 
289
                                          "', 'YY-MM-DD HH24:MI:SS')";
290
  
291
      StringBuffer sql = new StringBuffer();
292
      sql.append("insert into xml_documents (docid, docname, doctype, ");
293
      sql.append("user_owner, user_updated, server_location, rev,date_created");
294
      sql.append(", date_updated, public_access) values ('");
295
      sql.append(docid).append("','");
296
      sql.append(docname).append("','");
297
      sql.append(doctype).append("','");
298
      sql.append(user).append("','");
299
      sql.append(user).append("','");
300
      sql.append(serverCode).append("','");
301
      sql.append(rev).append("',");
302
      sql.append(sqlDateString).append(",");
303
      sql.append(sqlDateString).append(",");
304
      sql.append("'0')");
305
      PreparedStatement pstmt = dbconn.prepareStatement(sql.toString());
306
      pstmt.execute();
307
      pstmt.close();
308
      dbconn.close();
309
    } finally {
310
      util.returnConnection(dbconn);
311
    }    
312
  }
313
 /**
314
   * unRegister a an XML file (data file) from the database (actually, 
315
   * just make it a revision in the xml_revisions table and delete it
316
   * from xml_documents table). 
317
   *
318
   * @param docid the ID of the document to be deleted from the database
319
   */
320
  public static void unRegisterDocument( Connection conn, String accnum,
321
                                 String user, String[] groups )
322
                throws Exception 
323
  {
324
    
325

    
326
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
327
    AccessionNumber ac = new AccessionNumber(conn, accnum, "DELETE");
328
    String docid = ac.getDocid();
329
    String rev = ac.getRev();
330
    
331

    
332
    // check for 'write' permission for 'user' to delete this document
333
    if ( !hasPermission(conn, user, groups, docid) ) {
334
      throw new Exception("User " + user + 
335
              " does not have permission to delete XML Document #" + accnum);
336
    }
337

    
338
    conn.setAutoCommit(false);
339
    // Copy the record to the xml_revisions table
340
    DocumentImpl.archiveDocRevision( conn, docid, user );
341

    
342
    // Now delete it from the xml_documents table
343
    
344
    Statement stmt = conn.createStatement();
345
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
346
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
347
    stmt.close();
348
    conn.commit();
349
    conn.setAutoCommit(true);
350
   
351
    
352
  }
353
  /**
354
   * get the document name
355
   */
356
  public String getDocname() {
357
    return docname;
358
  }
359

    
360
  /**
361
   * get the document type (which is the PublicID)
362
   */
363
  public String getDoctype() {
364
    return doctype;
365
  }
366

    
367
  /**
368
   * get the system identifier
369
   */
370
  public String getSystemID() {
371
    return system_id;
372
  }
373

    
374
  /**
375
   * get the root node identifier
376
   */
377
  public long getRootNodeID() {
378
    return rootnodeid;
379
  }
380
  
381
  /**
382
   * get the creation date
383
   */
384
  public String getCreateDate() {
385
    return createdate;
386
  }
387
  
388
  /**
389
   * get the update date
390
   */
391
  public String getUpdateDate() {
392
    return updatedate;
393
  }
394

    
395
  /** 
396
   * Get the document identifier (docid)
397
   */
398
  public String getDocID() {
399
    return docid;
400
  }
401
  
402
// DOCTITLE attr cleared from the db
403
//  /**
404
//   *get the document title
405
//   */
406
//  public String getDocTitle() {
407
//    return doctitle;
408
//  }
409
  
410
  public String getUserowner() {
411
    return userowner;
412
  }
413
  
414
  public String getUserupdated() {
415
    return userupdated;
416
  }
417
  
418
  public int getServerlocation() {
419
    return serverlocation;
420
  }
421
  
422
  public String getPublicaccess() {
423
    return publicaccess;
424
  }
425
  
426
  public int getRev() {
427
    return rev;
428
  }
429
  
430
   /**
431
   * Print a string representation of the XML document
432
   */
433
  public String toString()
434
  {
435
    StringWriter docwriter = new StringWriter();
436
    try {
437
      this.toXml(docwriter);
438
    } catch (McdbException mcdbe) {
439
      return null;
440
    }
441
    String document = docwriter.toString();
442
    return document;
443
  }
444

    
445
  /**
446
   * Get a text representation of the XML document as a string
447
   * This older algorithm uses a recursive tree of Objects to represent the
448
   * nodes of the tree.  Each object is passed the data for the document 
449
   * and searches all of the document data to find its children nodes and
450
   * recursively build.  Thus, because each node reads the whole document,
451
   * this algorithm is extremely slow for larger documents, and the time
452
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
453
   * better algorithm.
454
   */
455
  public String readUsingSlowAlgorithm() throws McdbException
456
  {
457
    StringBuffer doc = new StringBuffer();
458

    
459
    // First, check that we have the needed node data, and get it if not
460
    if (nodeRecordList == null) {
461
      nodeRecordList = getNodeRecordList(rootnodeid);
462
    }
463

    
464
    // Create the elements from the downloaded data in the TreeSet
465
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
466

    
467
    // Append the resulting document to the StringBuffer and return it
468
    doc.append("<?xml version=\"1.0\"?>\n");
469
      
470
    if (docname != null) {
471
      if ((doctype != null) && (system_id != null)) {
472
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
473
                   "\" \"" + system_id + "\">\n");
474
      } else {
475
        doc.append("<!DOCTYPE " + docname + ">\n");
476
      }
477
    }
478
    doc.append(rootNode.toString());
479
  
480
    return (doc.toString());
481
  }
482

    
483
  /**
484
   * Print a text representation of the XML document to a Writer
485
   *
486
   * @param pw the Writer to which we print the document
487
   */
488
  public void toXml(Writer pw) throws McdbException
489
  {
490
    PrintWriter out = null;
491
    if (pw instanceof PrintWriter) {
492
      out = (PrintWriter)pw;
493
    } else {
494
      out = new PrintWriter(pw);
495
    }
496

    
497
    MetaCatUtil util = new MetaCatUtil();
498
    
499
    // First, check that we have the needed node data, and get it if not
500
    if (nodeRecordList == null) {
501
      nodeRecordList = getNodeRecordList(rootnodeid);
502
    }
503

    
504
    Stack openElements = new Stack();
505
    boolean atRootElement = true;
506
    boolean previousNodeWasElement = false;
507

    
508
    // Step through all of the node records we were given
509
    Iterator it = nodeRecordList.iterator();
510
    while (it.hasNext()) {
511
      NodeRecord currentNode = (NodeRecord)it.next();
512
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
513
                          //" (" + currentNode.parentnodeid +
514
                          //", " + currentNode.nodeindex + 
515
                          //", " + currentNode.nodetype + 
516
                          //", " + currentNode.nodename + 
517
                          //", " + currentNode.nodedata + ")]");
518
      // Print the end tag for the previous node if needed
519
      //
520
      // This is determined by inspecting the parent nodeid for the
521
      // currentNode.  If it is the same as the nodeid of the last element
522
      // that was pushed onto the stack, then we are still in that previous
523
      // parent element, and we do nothing.  However, if it differs, then we
524
      // have returned to a level above the previous parent, so we go into
525
      // a loop and pop off nodes and print out their end tags until we get
526
      // the node on the stack to match the currentNode parentnodeid
527
      //
528
      // So, this of course means that we rely on the list of elements
529
      // having been sorted in a depth first traversal of the nodes, which
530
      // is handled by the NodeComparator class used by the TreeSet
531
      if (!atRootElement) {
532
        NodeRecord currentElement = (NodeRecord)openElements.peek();
533
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
534
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
535
            currentElement = (NodeRecord)openElements.pop();
536
            util.debugMessage("\n POPPED: " + currentElement.nodename);
537
            if (previousNodeWasElement) {
538
              out.print(">");
539
              previousNodeWasElement = false;
540
            }  
541
            if ( currentElement.nodeprefix != null ) {
542
              out.print("</" + currentElement.nodeprefix + ":" + 
543
                        currentElement.nodename + ">" );
544
            } else {
545
              out.print("</" + currentElement.nodename + ">" );
546
            }
547
            currentElement = (NodeRecord)openElements.peek();
548
          }
549
        }
550
      }
551

    
552
      // Handle the DOCUMENT node
553
      if (currentNode.nodetype.equals("DOCUMENT")) {
554
        out.println("<?xml version=\"1.0\"?>");
555
      
556
        if (docname != null) {
557
          if ((doctype != null) && (system_id != null)) {
558
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
559
                       "\" \"" + system_id + "\">");
560
          } else {
561
            out.println("<!DOCTYPE " + docname + ">");
562
          }
563
        }
564

    
565
      // Handle the ELEMENT nodes
566
      } else if (currentNode.nodetype.equals("ELEMENT")) {
567
        if (atRootElement) {
568
          atRootElement = false;
569
        } else {
570
          if (previousNodeWasElement) {
571
            out.print(">");
572
          }
573
        }
574
        openElements.push(currentNode);
575
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
576
        previousNodeWasElement = true;
577
        if ( currentNode.nodeprefix != null ) {
578
          out.print("<" + currentNode.nodeprefix + ":" + currentNode.nodename);
579
        } else {
580
          out.print("<" + currentNode.nodename);
581
        }
582

    
583
      // Handle the ATTRIBUTE nodes
584
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
585
        if ( currentNode.nodeprefix != null ) {
586
          out.print(" " + currentNode.nodeprefix + ":" + currentNode.nodename +
587
                    "=\"" + currentNode.nodedata + "\"");
588
        } else {
589
          out.print(" " + currentNode.nodename + "=\"" +
590
                    currentNode.nodedata + "\"");
591
        }
592

    
593
      // Handle the NAMESPACE nodes
594
      } else if (currentNode.nodetype.equals("NAMESPACE")) {
595
        out.print(" xmlns:" + currentNode.nodename + "=\""
596
                 + currentNode.nodedata + "\"");
597

    
598
      // Handle the TEXT nodes
599
      } else if (currentNode.nodetype.equals("TEXT")) {
600
        if (previousNodeWasElement) {
601
          out.print(">");
602
        }
603
        out.print(currentNode.nodedata);
604
        previousNodeWasElement = false;
605

    
606
      // Handle the COMMENT nodes
607
      } else if (currentNode.nodetype.equals("COMMENT")) {
608
        if (previousNodeWasElement) {
609
          out.print(">");
610
        }
611
        out.print("<!--" + currentNode.nodedata + "-->");
612
        previousNodeWasElement = false;
613

    
614
      // Handle the PI nodes
615
      } else if (currentNode.nodetype.equals("PI")) {
616
        if (previousNodeWasElement) {
617
          out.print(">");
618
        }
619
        out.print("<?" + currentNode.nodename + " " +
620
                        currentNode.nodedata + "?>");
621
        previousNodeWasElement = false;
622

    
623
      // Handle any other node type (do nothing)
624
      } else {
625
        // Any other types of nodes are not handled.
626
        // Probably should throw an exception here to indicate this
627
      }
628
      out.flush();
629
    }
630

    
631
    // Print the final end tag for the root element
632
    while(!openElements.empty())
633
    {
634
      NodeRecord currentElement = (NodeRecord)openElements.pop();
635
      util.debugMessage("\n POPPED: " + currentElement.nodename);
636
      if ( currentElement.nodeprefix != null ) {
637
        out.print("</" + currentElement.nodeprefix + ":" + 
638
                  currentElement.nodename + ">" );
639
      } else {
640
        out.print("</" + currentElement.nodename + ">" );
641
      }
642
    }
643
    out.flush();
644
  }
645
  
646
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
647
  {
648
    //System.out.println("inRevisionOnly");
649
    PreparedStatement pstmt;
650
    String rev = docid.getRev();
651
    String newid = docid.getIdentifier();
652
    pstmt = conn.prepareStatement("select rev from xml_documents " +
653
                                  "where docid like '" + newid + "'");
654
    pstmt.execute();
655
    ResultSet rs = pstmt.getResultSet();
656
    boolean tablehasrows = rs.next();
657
    if(rev.equals("newest") || rev.equals("all"))
658
    {
659
      return false;
660
    }
661
    
662
    if(tablehasrows)
663
    {
664
      int r = rs.getInt(1);
665
      pstmt.close();
666
      if(new Integer(rev).intValue() == r)
667
      { //the current revision in in xml_documents
668
        //System.out.println("returning false");
669
        return false;
670
      }
671
      else if(new Integer(rev).intValue() < r)
672
      { //the current revision is in xml_revisions.
673
        //System.out.println("returning true");
674
        return true;
675
      }
676
      else if(new Integer(rev).intValue() > r)
677
      { //error, rev cannot be greater than r
678
        throw new Exception("requested revision cannot be greater than " +
679
                            "the latest revision number.");
680
      }
681
    }
682
    throw new Exception("the requested docid '" + docid.toString() + 
683
                        "' does not exist");
684
  }
685

    
686
  private void getDocumentInfo(String docid) throws McdbException, 
687
                                                    AccessionNumberException
688
  {
689
    getDocumentInfo(new DocumentIdentifier(docid));
690
  }
691
  
692
  /**
693
   * Look up the document type information from the database
694
   *
695
   * @param docid the id of the document to look up
696
   */
697
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException 
698
  {
699
    PreparedStatement pstmt;
700
    String table = "xml_documents";
701
    
702
    try
703
    {
704
      if(isRevisionOnly(docid))
705
      { //pull the document from xml_revisions instead of from xml_documents;
706
        table = "xml_revisions";
707
      }
708
    }
709
    catch(Exception e)
710
    {
711
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
712
                          e.getMessage());
713
    }
714
    
715
    //deal with the key words here.
716
    
717
    if(docid.getRev().equals("all"))
718
    {
719
      
720
    }
721
    
722
    try {
723
      StringBuffer sql = new StringBuffer();
724
// DOCTITLE attr cleared from the db
725
//      sql.append("SELECT docname, doctype, rootnodeid, doctitle, ");
726
      sql.append("SELECT docname, doctype, rootnodeid, ");
727
      sql.append("date_created, date_updated, user_owner, user_updated, ");
728
      sql.append("server_location, public_access, rev");
729
      sql.append(" FROM ").append(table);
730
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
731
      sql.append("' and rev like '").append(docid.getRev()).append("'");
732
      //System.out.println(sql.toString());
733
      pstmt =
734
        conn.prepareStatement(sql.toString());
735
      // Bind the values to the query
736
      //pstmt.setString(1, docid.getIdentifier());
737
      //pstmt.setString(2, docid.getRev());
738

    
739
      pstmt.execute();
740
      ResultSet rs = pstmt.getResultSet();
741
      boolean tableHasRows = rs.next();
742
      if (tableHasRows) {
743
        this.docname        = rs.getString(1);
744
        this.doctype        = rs.getString(2);
745
        this.rootnodeid     = rs.getLong(3);
746
// DOCTITLE attr cleared from the db
747
//        this.doctitle       = rs.getString(4);
748
        this.createdate     = rs.getString(4);
749
        this.updatedate     = rs.getString(5);
750
        this.userowner      = rs.getString(6);
751
        this.userupdated    = rs.getString(7);
752
        this.serverlocation = rs.getInt(8);
753
        this.publicaccess   = rs.getString(9);
754
        this.rev            = rs.getInt(10);
755
      } 
756
      pstmt.close();
757
      if (this.doctype != null) {
758
        pstmt =
759
          conn.prepareStatement("SELECT system_id " +
760
                                  "FROM xml_catalog " +
761
                                 "WHERE public_id = ?");
762
        // Bind the values to the query
763
        pstmt.setString(1, doctype);
764
  
765
        pstmt.execute();
766
        rs = pstmt.getResultSet();
767
        tableHasRows = rs.next();
768
        if (tableHasRows) {
769
          this.system_id  = rs.getString(1);
770
        } 
771
        pstmt.close();
772
      }
773
    } catch (SQLException e) {
774
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
775
                          e.getMessage());
776
      e.printStackTrace(System.out);
777
      throw new McdbException("Error accessing database connection in " +
778
                              "DocumentImpl.getDocumentInfo: ", e);
779
    }
780

    
781
    if (this.docname == null) {
782
      throw new McdbDocNotFoundException("Document not found: " + docid);
783
    }
784
  }
785

    
786
  /**
787
   * Look up the node data from the database
788
   *
789
   * @param rootnodeid the id of the root node of the node tree to look up
790
   */
791
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
792
  {
793
    PreparedStatement pstmt;
794
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
795
    long nodeid = 0;
796
    long parentnodeid = 0;
797
    long nodeindex = 0;
798
    String nodetype = null;
799
    String nodename = null;
800
    String nodeprefix = null;
801
    String nodedata = null;
802
    String quotechar = dbAdapter.getStringDelimiter();
803

    
804
    try {
805
      pstmt =
806
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
807
           "nodetype,nodename,nodeprefix,nodedata " +               
808
           "FROM xml_nodes WHERE rootnodeid = ?");
809

    
810
      // Bind the values to the query
811
      pstmt.setLong(1, rootnodeid);
812

    
813
      pstmt.execute();
814
      ResultSet rs = pstmt.getResultSet();
815
      boolean tableHasRows = rs.next();
816
      while (tableHasRows) {
817
        nodeid = rs.getLong(1);
818
        parentnodeid = rs.getLong(2);
819
        nodeindex = rs.getLong(3);
820
        nodetype = rs.getString(4);
821
        nodename = rs.getString(5);
822
        nodeprefix = rs.getString(6);
823
        nodedata = rs.getString(7);
824
        nodedata = MetaCatUtil.normalize(nodedata);
825
        // add the data to the node record list hashtable
826
        NodeRecord currentRecord = new NodeRecord(nodeid,parentnodeid,nodeindex,
827
                                      nodetype, nodename, nodeprefix, nodedata);
828
        nodeRecordList.add(currentRecord);
829

    
830
        // Advance to the next node
831
        tableHasRows = rs.next();
832
      } 
833
      pstmt.close();
834

    
835
    } catch (SQLException e) {
836
      throw new McdbException("Error in DocumentImpl.getNodeRecordList " +
837
                              e.getMessage());
838
    }
839

    
840
    if (nodeRecordList != null) {
841
      return nodeRecordList;
842
    } else {
843
      throw new McdbException("Error getting node data: " + docid);
844
    }
845
  }
846
  
847
// NOT USED ANY MORE
848
//  /** creates SQL code and inserts new document into DB connection 
849
//   default serverCode of 1*/
850
//  private void writeDocumentToDB(String action, String user)
851
//               throws SQLException, Exception
852
//  {
853
//    writeDocumentToDB(action, user, null, 1);
854
//  }
855

    
856
 /** creates SQL code and inserts new document into DB connection */
857
  private void writeDocumentToDB(String action, String user, String pub, 
858
                                 String catalogid, int serverCode) 
859
               throws SQLException, Exception {
860
    String sysdate = dbAdapter.getDateTimeFunction();
861

    
862
    try {
863
      PreparedStatement pstmt = null;
864

    
865
      if (action.equals("INSERT")) {
866
        //AccessionNumber ac = new AccessionNumber();
867
        //this.docid = ac.generate(docid, "INSERT");
868
        pstmt = conn.prepareStatement(
869
                "INSERT INTO xml_documents " +
870
                "(docid, rootnodeid, docname, doctype, " + 
871
                "user_owner, user_updated, date_created, date_updated, " + 
872
                "public_access, catalog_id, server_location) " +
873
                "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", " + sysdate + 
874
                ", ?, ?, ?)");
875
        //note that the server_location is set to 1. 
876
        //this means that "localhost" in the xml_replication table must
877
        //always be the first entry!!!!!
878
        
879
        // Bind the values to the query
880
        pstmt.setString(1, this.docid);
881
        pstmt.setLong(2, rootnodeid);
882
        pstmt.setString(3, docname);
883
        pstmt.setString(4, doctype);
884
        pstmt.setString(5, user);
885
        pstmt.setString(6, user);
886
        if ( pub == null ) {
887
          pstmt.setString(7, null);
888
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
889
          pstmt.setInt(7, 1);
890
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
891
          pstmt.setInt(7, 0);
892
        }
893
        pstmt.setString(8, catalogid);
894
        pstmt.setInt(9, serverCode);
895
      } else if (action.equals("UPDATE")) {
896

    
897
        // Save the old document entry in a backup table
898
        DocumentImpl.archiveDocRevision( conn, docid, user );
899
        DocumentImpl thisdoc = new DocumentImpl(conn, docid);
900
        int thisrev = thisdoc.getRev();
901
        
902
        //if the updated vesion is not greater than current one,
903
        //throw it into a exception
904
        if (Integer.parseInt(updatedVersion)<=thisrev)
905
        {
906
          throw new Exception("Next revision number couldn't be less"
907
                               +" than or equal "+ thisrev);
908
        }
909
        else
910
        {
911
          //set the user specified revision 
912
          thisrev=Integer.parseInt(updatedVersion);
913
        }
914
        
915
        // Delete index for the old version of docid
916
        // The new index is inserting on the next calls to DBSAXNode
917
        pstmt = conn.prepareStatement(
918
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
919
        pstmt.execute();
920
        pstmt.close();
921

    
922
        // Update the new document to reflect the new node tree
923
        pstmt = conn.prepareStatement(
924
            "UPDATE xml_documents " +
925
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
926
            "user_updated = ?, date_updated = " + sysdate + ", " +
927
            "server_location = ?, rev = ?, public_access = ?, catalog_id = ? " +
928
            "WHERE docid = ?");
929
        // Bind the values to the query
930
        pstmt.setLong(1, rootnodeid);
931
        pstmt.setString(2, docname);
932
        pstmt.setString(3, doctype);
933
        pstmt.setString(4, user);
934
        pstmt.setInt(5, serverCode);
935
        pstmt.setInt(6, thisrev);
936
        if ( pub == null ) {
937
          pstmt.setString(7, null);
938
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
939
          pstmt .setInt(7, 1);
940
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
941
          pstmt.setInt(7, 0);
942
        }
943
        pstmt.setString(8, catalogid);
944
        pstmt.setString(9, this.docid);
945

    
946
      } else {
947
        System.err.println("Action not supported: " + action);
948
      }
949

    
950
      // Do the insertion
951
      pstmt.execute();
952
      pstmt.close();
953

    
954
    } catch (SQLException sqle) {
955
      throw sqle;
956
    } catch (Exception e) {
957
      throw e;
958
    }
959
  }
960

    
961
  /**
962
   * Write an XML file to the database, given a filename
963
   *
964
   * @param conn the JDBC connection to the database
965
   * @param filename the filename to be loaded into the database
966
   * @param pub flag for public "read" access on document
967
   * @param dtdfilename the dtd to be uploaded on server's file system
968
   * @param action the action to be performed (INSERT OR UPDATE)
969
   * @param docid the docid to use for the INSERT OR UPDATE
970
   * @param user the user that owns the document
971
   * @param groups the groups to which user belongs
972
   */
973
  public static String write(Connection conn,String filename,
974
                             String pub, String dtdfilename,
975
                             String action, String docid, String user,
976
                             String[] groups )
977
                throws Exception {
978
                  
979
    Reader dtd = null;
980
    if ( dtdfilename != null ) {
981
      dtd = new FileReader(new File(dtdfilename).toString());
982
    }
983
    return write ( conn, new FileReader(new File(filename).toString()),
984
                   pub, dtd, action, docid, user, groups, false);
985
  }
986

    
987
  public static String write(Connection conn,Reader xml,String pub,Reader dtd,
988
                             String action, String docid, String user,
989
                             String[] groups, boolean validate)
990
                throws Exception {
991
    //this method will be called in handleUpdateOrInsert method 
992
    //in MetacatServlet class
993
    // get server location for this doc
994
    int serverLocation=getServerLocationNumber(conn,docid);
995
    //System.out.println("server location: "+serverLocation);
996
    return write(conn,xml,pub,dtd,action,docid,user,groups,serverLocation,false,
997
                 validate);
998
  }
999

    
1000
  public static String write(Connection conn, Reader xml, String pub,
1001
                             String action, String docid, String user,
1002
                             String[] groups )
1003
                throws Exception {
1004
    if(action.equals("UPDATE"))
1005
    {//if the document is being updated then use the servercode from the 
1006
     //originally inserted document.
1007
      DocumentImpl doc = new DocumentImpl(conn, docid);
1008
      int servercode = doc.getServerlocation();
1009
      return write(conn, xml, pub, action, docid, user, groups, servercode);
1010
    }
1011
    else
1012
    {//if the file is being inserted then the servercode is always 1
1013
      return write(conn, xml, pub, action, docid, user, groups, 1);
1014
    }
1015
  }
1016
  
1017
  public static String write( Connection conn, Reader xml,
1018
                              String action, String docid, String user,
1019
                              String[] groups, int serverCode )
1020
                throws Exception
1021
  {
1022
    return write(conn,xml,null,action,docid,user,groups,serverCode);
1023
  }
1024
  
1025
  public static String write( Connection conn, Reader xml, String pub,
1026
                              String action, String docid, String user,
1027
                              String[] groups, int serverCode) 
1028
                throws Exception
1029
  {
1030
    return write(conn,xml,pub,null,action,docid,user,groups,
1031
                 serverCode,false,false);
1032
  }
1033
  
1034
  public static String write( Connection conn, Reader xml, String pub,
1035
                              String action, String docid, String user,
1036
                              String[] groups, int serverCode, boolean override)
1037
                throws Exception
1038
  {
1039
    return write(conn,xml,pub,null,action,docid,user,groups,
1040
                 serverCode,override,false);
1041
  }
1042
  
1043
  /**
1044
   * Write an XML file to the database, given a Reader
1045
   *
1046
   * @param conn the JDBC connection to the database
1047
   * @param xml the xml stream to be loaded into the database
1048
   * @param pub flag for public "read" access on xml document
1049
   * @param dtd the dtd to be uploaded on server's file system
1050
   * @param action the action to be performed (INSERT or UPDATE)
1051
   * @param accnum the docid + rev# to use on INSERT or UPDATE
1052
   * @param user the user that owns the document
1053
   * @param groups the groups to which user belongs
1054
   * @param serverCode the serverid from xml_replication on which this document
1055
   *        resides.
1056
   * @param override flag to stop insert replication checking.
1057
   *        if override = true then a document not belonging to the local server
1058
   *        will not be checked upon update for a file lock.
1059
   *        if override = false then a document not from this server, upon 
1060
   *        update will be locked and version checked.
1061
   */
1062

    
1063
  public static String write( Connection conn,Reader xml,String pub,Reader dtd,
1064
                              String action, String accnum, String user,
1065
                              String[] groups, int serverCode, boolean override,
1066
                              boolean validate)
1067
                throws Exception
1068
  {
1069
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1070
    MetaCatUtil util = new MetaCatUtil();
1071
    AccessionNumber ac = new AccessionNumber(conn, accnum, action);
1072
    String docid = ac.getDocid();
1073
    String rev = ac.getRev();
1074
    MetaCatUtil.debugMessage("action: " + action + " servercode: " + 
1075
                             serverCode + " override: " + override);
1076
                        
1077
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
1078
    { //if this document being written is not a resident of this server then
1079
      //we need to try to get a lock from it's resident server.  If the
1080
      //resident server will not give a lock then we send the user a message
1081
      //saying that he/she needs to download a new copy of the file and
1082
      //merge the differences manually.
1083
      int istreamInt; 
1084
      char istreamChar;
1085
      DocumentIdentifier id = new DocumentIdentifier(accnum);
1086
      String updaterev = id.getRev();
1087
      String server = MetacatReplication.getServer(serverCode);
1088
      MetacatReplication.replLog("attempting to lock " + accnum);
1089
      URL u = new URL("https://" + server + "?server="
1090
           +util.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
1091
           +updaterev + "&docid=" + docid);
1092
      //System.out.println("sending message: " + u.toString());
1093
      String serverResStr = MetacatReplication.getURLContent(u);
1094
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
1095
      if(openingtag.equals("<lockgranted>"))
1096
      {//the lock was granted go ahead with the insert
1097
        try 
1098
        {
1099
          //System.out.println("In lockgranted");
1100
          MetacatReplication.replLog("lock granted for " + accnum + " from " +
1101
                                      server);
1102
          XMLReader parser = initializeParser(conn, action, docid, updaterev,
1103
                                  validate, user, groups, pub, serverCode, dtd);
1104
          conn.setAutoCommit(false);
1105
          parser.parse(new InputSource(xml)); 
1106
          conn.commit();
1107
          conn.setAutoCommit(true);
1108
        } 
1109
        catch (Exception e) 
1110
        {
1111
          conn.rollback();
1112
          conn.setAutoCommit(true);
1113
          throw e;
1114
        }
1115
                
1116
        //after inserting the document locally, tell the document's home server
1117
        //to come get a copy from here.
1118
        //ture mean it is xml document
1119
        ForceReplicationHandler frh = new ForceReplicationHandler(accnum, true);
1120
        
1121
        return (accnum);
1122
      }
1123

    
1124
      else if(openingtag.equals("<filelocked>"))
1125
      {//the file is currently locked by another user
1126
       //notify our user to wait a few minutes, check out a new copy and try
1127
       //again.
1128
        //System.out.println("file locked");
1129
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1130
                                   server + " reason: file already locked");
1131
        throw new Exception("The file specified is already locked by another " +
1132
                            "user.  Please wait 30 seconds, checkout the " +
1133
                            "newer document, merge your changes and try " +
1134
                            "again.");
1135
      }
1136
      else if(openingtag.equals("<outdatedfile>"))
1137
      {//our file is outdated.  notify our user to check out a new copy of the
1138
       //file and merge his version with the new version.
1139
        //System.out.println("outdated file");
1140
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1141
                                    server + " reason: local file outdated");
1142
        throw new Exception("The file you are trying to update is an outdated" +
1143
                            " version.  Please checkout the newest document, " +
1144
                            "merge your changes and try again.");
1145
      }
1146
    }
1147
    
1148
    if ( action.equals("UPDATE") ) {
1149
      // check for 'write' permission for 'user' to update this document
1150

    
1151
      if ( !hasPermission(conn, user, groups, docid) ) {
1152
        throw new Exception("User " + user + 
1153
              " does not have permission to update XML Document #" + accnum);
1154
      }          
1155
    }
1156

    
1157
    try 
1158
    { 
1159
      
1160
      XMLReader parser = initializeParser(conn, action, docid, rev, validate,
1161
                                          user, groups, pub, serverCode, dtd);
1162
      conn.setAutoCommit(false);
1163
      parser.parse(new InputSource(xml));
1164
      conn.commit();
1165
      conn.setAutoCommit(true);
1166
    } 
1167
    catch (Exception e) 
1168
    {
1169
      conn.rollback();
1170
      conn.setAutoCommit(true);
1171
      throw e;
1172
    }
1173
    
1174
    //force replicate out the new document to each server in our server list.
1175
    if(serverCode == 1)
1176
    { 
1177
      
1178
      //start the thread to replicate this new document out to the other servers
1179
      //true mean it is xml document
1180
      ForceReplicationHandler frh = new ForceReplicationHandler
1181
                                                         (accnum, action, true);
1182
      
1183
    }
1184
      
1185
    return(accnum);
1186
  }
1187

    
1188
  /**
1189
   * Delete an XML file from the database (actually, just make it a revision
1190
   * in the xml_revisions table)
1191
   *
1192
   * @param docid the ID of the document to be deleted from the database
1193
   */
1194
  public static void delete( Connection conn, String accnum,
1195
                                 String user, String[] groups )
1196
                throws Exception 
1197
  {
1198
    // OLD
1199
    //DocumentIdentifier id = new DocumentIdentifier(accnum);
1200
    //String docid = id.getIdentifier();
1201
    //String rev = id.getRev();
1202
    
1203
    // OLD
1204
    // Determine if the docid,rev are OK for DELETE
1205
    //AccessionNumber ac = new AccessionNumber(conn);
1206
    //docid = ac.generate(docid, rev, "DELETE");
1207

    
1208
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1209
    AccessionNumber ac = new AccessionNumber(conn, accnum, "DELETE");
1210
    String docid = ac.getDocid();
1211
    String rev = ac.getRev();
1212
    
1213

    
1214
    // check for 'write' permission for 'user' to delete this document
1215
    if ( !hasPermission(conn, user, groups, docid) ) {
1216
      throw new Exception("User " + user + 
1217
              " does not have permission to delete XML Document #" + accnum);
1218
    }
1219

    
1220
    conn.setAutoCommit(false);
1221
    // Copy the record to the xml_revisions table
1222
    DocumentImpl.archiveDocRevision( conn, docid, user );
1223

    
1224
    // Now delete it from the xml_documents table
1225
    
1226
    Statement stmt = conn.createStatement();
1227
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
1228
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
1229
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + docid + "'");
1230
    stmt.execute("DELETE FROM xml_relation WHERE docid = '" + docid + "'");
1231
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
1232
    stmt.close();
1233
    conn.commit();
1234
    conn.setAutoCommit(true);
1235
    //IF this is a package document:
1236
    //delete all of the relations that this document created.
1237
    //if the deleted document is a package document its relations should 
1238
    //no longer be active if it has been deleted from the system.
1239
    
1240
  }
1241

    
1242
  /** 
1243
    * Check for "WRITE" permission on @docid for @user and/or @groups 
1244
    * from DB connection 
1245
    */
1246
  private static boolean hasPermission ( Connection conn, String user,
1247
                                  String[] groups, String docid ) 
1248
                  throws SQLException, Exception
1249
  {
1250
    // Check for WRITE permission on @docid for @user and/or @groups
1251
    AccessControlList aclobj = new AccessControlList(conn);
1252
    return aclobj.hasPermission("WRITE", user, groups, docid);
1253
  }
1254

    
1255
  /** 
1256
    * Check for "READ" permission base on docid, user and group
1257
    *@docid, the document
1258
    *@user, user name
1259
    *@group, user's group
1260
    * 
1261
    */
1262
  public boolean hasReadPermission ( Connection conn, String user,
1263
                                  String[] groups, String docId ) 
1264
                  throws SQLException, Exception
1265
  {
1266
    // Check for READ permission on @docid for @user and/or @groups
1267
    AccessControlList aclObj = new AccessControlList(conn);
1268
    return aclObj.hasPermission("READ", user, groups, docId);
1269
  }  
1270

    
1271
  /**
1272
   * Set up the parser handlers for writing the document to the database
1273
   */
1274
  private static XMLReader initializeParser(Connection conn, String action,
1275
                               String docid, String rev, boolean validate, 
1276
                                   String user, String[] groups, String pub, 
1277
                                   int serverCode, Reader dtd) 
1278
                           throws Exception 
1279
  {
1280
    XMLReader parser = null;
1281
    //
1282
    // Set up the SAX document handlers for parsing
1283
    //
1284
    try {
1285
      //create a DBSAXHandler object which has the revision specification
1286
      ContentHandler chandler = new DBSAXHandler(conn, action, docid, rev,
1287
                                                 user, groups, pub, serverCode);
1288
      EntityResolver eresolver= new DBEntityResolver(conn,
1289
                                                 (DBSAXHandler)chandler, dtd);
1290
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1291

    
1292
      // Get an instance of the parser
1293
      String parserName = MetaCatUtil.getOption("saxparser");
1294
      parser = XMLReaderFactory.createXMLReader(parserName);
1295

    
1296
      // Turn on validation
1297
      parser.setFeature("http://xml.org/sax/features/validation", validate);
1298
      // Turn off Including all external parameter entities
1299
      // (the external DTD subset also)
1300
      // Doesn't work well, probably the feature name is not correct
1301
      // parser.setFeature(
1302
      //  "http://xml.org/sax/features/external-parameter-entities", false);
1303
      
1304
      // Set Handlers in the parser
1305
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
1306
                         chandler);
1307
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
1308
                         chandler);
1309
      parser.setContentHandler((ContentHandler)chandler);
1310
      parser.setEntityResolver((EntityResolver)eresolver);
1311
      parser.setDTDHandler((DTDHandler)dtdhandler);
1312
      parser.setErrorHandler((ErrorHandler)chandler);
1313

    
1314
    } catch (Exception e) {
1315
      throw e;
1316
    }
1317

    
1318
    return parser;
1319
  }
1320

    
1321
  /** Save a document entry in the xml_revisions table */
1322
  private static void archiveDocRevision(Connection conn, String docid,
1323
                                         String user) 
1324
                                         throws SQLException {
1325
    String sysdate = dbAdapter.getDateTimeFunction();
1326
    // create a record in xml_revisions table 
1327
    // for that document as selected from xml_documents
1328
    PreparedStatement pstmt = conn.prepareStatement(
1329
      "INSERT INTO xml_revisions " +
1330
        "(docid, rootnodeid, docname, doctype, " +
1331
        "user_owner, user_updated, date_created, date_updated, " +
1332
        "server_location, rev, public_access, catalog_id) " +
1333
      "SELECT ?, rootnodeid, docname, doctype, " + 
1334
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
1335
        "server_location, rev, public_access, catalog_id " +
1336
      "FROM xml_documents " +
1337
      "WHERE docid = ?");
1338
    // Bind the values to the query and execute it
1339
    pstmt.setString(1, docid);
1340
    pstmt.setString(2, user);
1341
    pstmt.setString(3, docid);
1342
    pstmt.execute();
1343
    pstmt.close();
1344

    
1345
  }
1346
  
1347
  /**
1348
    * delete a entry in xml_table for given docid
1349
    * @param docId, the id of the document need to be delete
1350
    */
1351
  private static void deleteXMLDocuments(Connection conn, String docId) 
1352
                                         throws SQLException 
1353
  {
1354
    //delete a record 
1355
    PreparedStatement pStmt = 
1356
             conn.prepareStatement("DELETE FROM xml_documents WHERE docid = '" 
1357
                                              + docId + "'");
1358
    pStmt.execute();
1359
    pStmt.close();
1360

    
1361
  }//deleteXMLDocuments
1362
  
1363
  /**
1364
    * Get last revision number from database for a docid
1365
    * If couldn't find an entry,  -1 will return
1366
    * The return value is integer because we want compare it to there new one
1367
    * @param docid <sitecode>.<uniqueid> part of Accession Number
1368
    */
1369
  private static int getLatestRevisionNumber(Connection conn, String docId)
1370
                                      throws SQLException
1371
  {
1372
    int rev = 1;
1373
    PreparedStatement pStmt;
1374
     
1375
    pStmt = conn.prepareStatement
1376
              ("SELECT rev FROM xml_documents WHERE docid='" + docId + "'");
1377
    pStmt.execute();
1378

    
1379
    ResultSet rs = pStmt.getResultSet();
1380
    boolean hasRow = rs.next();
1381
    if (hasRow)
1382
    {
1383
      rev = rs.getInt(1);
1384
      pStmt.close();
1385
    }
1386
    else
1387
    {
1388
      rev=-1;
1389
      pStmt.close();
1390
    }
1391
      
1392
    return rev;
1393
  }//getLatestRevisionNumber
1394
  
1395
  /**
1396
   * Get server location form database for a accNum
1397
   * 
1398
   * @param accum <sitecode>.<uniqueid>.<rev>
1399
   */
1400
  private static int getServerLocationNumber(Connection conn, String accNum)
1401
                                            throws SQLException
1402
  {
1403
    //get rid of revNum part
1404
    String docId=MetaCatUtil.getDocIdFromString(accNum);
1405
    PreparedStatement pStmt;
1406
    int serverLocation;
1407
     
1408
    pStmt = conn.prepareStatement
1409
      ("SELECT server_location FROM xml_documents WHERE docid='" + docId + "'");
1410
    pStmt.execute();
1411

    
1412
    ResultSet rs = pStmt.getResultSet();
1413
    boolean hasRow = rs.next();
1414
    //if there is entry in xml_documents, get the serverlocation
1415
    if (hasRow)
1416
    {
1417
      serverLocation = rs.getInt(1);
1418
      pStmt.close();
1419
    }
1420
    else
1421
    {
1422
      // if htere is no entry in xml_documents, we consider it is new document
1423
      // the server location is local host and value is 1
1424
      serverLocation=1;
1425
      pStmt.close();
1426
    }
1427
      
1428
    return serverLocation;
1429
  }
1430
  /**
1431
   * the main routine used to test the DBWriter utility.
1432
   * <p>
1433
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1434
   *
1435
   * @param filename the filename to be loaded into the database
1436
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1437
   * @param docid the id of the document to process
1438
   */
1439
  static public void main(String[] args) {
1440
     
1441
    try {
1442
      String filename    = null;
1443
      String dtdfilename = null;
1444
      String action      = null;
1445
      String docid       = null;
1446
      boolean showRuntime = false;
1447
      boolean useOldReadAlgorithm = false;
1448

    
1449
      // Parse the command line arguments
1450
      for ( int i=0 ; i < args.length; ++i ) {
1451
        if ( args[i].equals( "-f" ) ) {
1452
          filename =  args[++i];
1453
        } else if ( args[i].equals( "-r" ) ) {
1454
          dtdfilename =  args[++i];
1455
        } else if ( args[i].equals( "-a" ) ) {
1456
          action =  args[++i];
1457
        } else if ( args[i].equals( "-d" ) ) {
1458
          docid =  args[++i];
1459
        } else if ( args[i].equals( "-t" ) ) {
1460
          showRuntime = true;
1461
        } else if ( args[i].equals( "-old" ) ) {
1462
          useOldReadAlgorithm = true;
1463
        } else {
1464
          System.err.println
1465
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
1466
        }
1467
      }
1468
      
1469
      // Check if the required arguments are provided
1470
      boolean argsAreValid = false;
1471
      if (action != null) {
1472
        if (action.equals("INSERT")) {
1473
          if (filename != null) {
1474
            argsAreValid = true;
1475
          } 
1476
        } else if (action.equals("UPDATE")) {
1477
          if ((filename != null) && (docid != null)) {
1478
            argsAreValid = true;
1479
          } 
1480
        } else if (action.equals("DELETE")) {
1481
          if (docid != null) {
1482
            argsAreValid = true;
1483
          } 
1484
        } else if (action.equals("READ")) {
1485
          if (docid != null) {
1486
            argsAreValid = true;
1487
          } 
1488
        } 
1489
      } 
1490

    
1491
      // Print usage message if the arguments are not valid
1492
      if (!argsAreValid) {
1493
        System.err.println("Wrong number of arguments!!!");
1494
        System.err.println(
1495
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
1496
          "[-r dtdfilename]");
1497
        System.err.println(
1498
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
1499
          "[-r dtdfilename]");
1500
        System.err.println(
1501
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
1502
        System.err.println(
1503
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
1504
        return;
1505
      }
1506
      
1507
      // Time the request if asked for
1508
      double startTime = System.currentTimeMillis();
1509
      
1510
      // Open a connection to the database
1511
      MetaCatUtil util = new MetaCatUtil();
1512
      Connection dbconn = util.openDBConnection();
1513

    
1514
      double connTime = System.currentTimeMillis();
1515
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1516
      if (action.equals("READ")) {
1517
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1518
          if (useOldReadAlgorithm) {
1519
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1520
          } else {
1521
            xmldoc.toXml(new PrintWriter(System.out));
1522
          }
1523
      } else if (action.equals("DELETE")) {
1524
        DocumentImpl.delete(dbconn, docid, null, null);
1525
        System.out.println("Document deleted: " + docid);
1526
      } else {
1527
        String newdocid = DocumentImpl.write(dbconn, filename, null,
1528
                                             dtdfilename, action, docid,
1529
                                             null, null);
1530
        if ((docid != null) && (!docid.equals(newdocid))) {
1531
          if (action.equals("INSERT")) {
1532
            System.out.println("New document ID generated!!! ");
1533
          } else if (action.equals("UPDATE")) {
1534
            System.out.println("ERROR: Couldn't update document!!! ");
1535
          }
1536
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1537
          System.out.println("ERROR: Couldn't update document!!! ");
1538
        }
1539
        System.out.println("Document processing finished for: " + filename
1540
              + " (" + newdocid + ")");
1541
      }
1542

    
1543
      double stopTime = System.currentTimeMillis();
1544
      double dbOpenTime = (connTime - startTime)/1000;
1545
      double insertTime = (stopTime - connTime)/1000;
1546
      double executionTime = (stopTime - startTime)/1000;
1547
      if (showRuntime) {
1548
        System.out.println("\n\nTotal Execution time was: " + 
1549
                           executionTime + " seconds.");
1550
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
1551
                           " seconds.");
1552
        System.out.println("Time to insert document was: " + insertTime +
1553
                           " seconds.");
1554
      }
1555
      dbconn.close();
1556
    } catch (McdbException me) {
1557
      me.toXml(new PrintWriter(System.err));
1558
    } catch (AccessionNumberException ane) {
1559
      System.out.println(ane.getMessage());
1560
    } catch (Exception e) {
1561
      System.err.println("EXCEPTION HANDLING REQUIRED");
1562
      System.err.println(e.getMessage());
1563
      e.printStackTrace(System.err);
1564
    }
1565
  }
1566
}
(24-24/41)