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-16 18:18:52 -0700 (Tue, 16 Apr 2002) $'
11
 * '$Revision: 1012 $'
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
  /**
315
   * get the document name
316
   */
317
  public String getDocname() {
318
    return docname;
319
  }
320

    
321
  /**
322
   * get the document type (which is the PublicID)
323
   */
324
  public String getDoctype() {
325
    return doctype;
326
  }
327

    
328
  /**
329
   * get the system identifier
330
   */
331
  public String getSystemID() {
332
    return system_id;
333
  }
334

    
335
  /**
336
   * get the root node identifier
337
   */
338
  public long getRootNodeID() {
339
    return rootnodeid;
340
  }
341
  
342
  /**
343
   * get the creation date
344
   */
345
  public String getCreateDate() {
346
    return createdate;
347
  }
348
  
349
  /**
350
   * get the update date
351
   */
352
  public String getUpdateDate() {
353
    return updatedate;
354
  }
355

    
356
  /** 
357
   * Get the document identifier (docid)
358
   */
359
  public String getDocID() {
360
    return docid;
361
  }
362
  
363
// DOCTITLE attr cleared from the db
364
//  /**
365
//   *get the document title
366
//   */
367
//  public String getDocTitle() {
368
//    return doctitle;
369
//  }
370
  
371
  public String getUserowner() {
372
    return userowner;
373
  }
374
  
375
  public String getUserupdated() {
376
    return userupdated;
377
  }
378
  
379
  public int getServerlocation() {
380
    return serverlocation;
381
  }
382
  
383
  public String getPublicaccess() {
384
    return publicaccess;
385
  }
386
  
387
  public int getRev() {
388
    return rev;
389
  }
390
  
391
   /**
392
   * Print a string representation of the XML document
393
   */
394
  public String toString()
395
  {
396
    StringWriter docwriter = new StringWriter();
397
    try {
398
      this.toXml(docwriter);
399
    } catch (McdbException mcdbe) {
400
      return null;
401
    }
402
    String document = docwriter.toString();
403
    return document;
404
  }
405

    
406
  /**
407
   * Get a text representation of the XML document as a string
408
   * This older algorithm uses a recursive tree of Objects to represent the
409
   * nodes of the tree.  Each object is passed the data for the document 
410
   * and searches all of the document data to find its children nodes and
411
   * recursively build.  Thus, because each node reads the whole document,
412
   * this algorithm is extremely slow for larger documents, and the time
413
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
414
   * better algorithm.
415
   */
416
  public String readUsingSlowAlgorithm() throws McdbException
417
  {
418
    StringBuffer doc = new StringBuffer();
419

    
420
    // First, check that we have the needed node data, and get it if not
421
    if (nodeRecordList == null) {
422
      nodeRecordList = getNodeRecordList(rootnodeid);
423
    }
424

    
425
    // Create the elements from the downloaded data in the TreeSet
426
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
427

    
428
    // Append the resulting document to the StringBuffer and return it
429
    doc.append("<?xml version=\"1.0\"?>\n");
430
      
431
    if (docname != null) {
432
      if ((doctype != null) && (system_id != null)) {
433
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
434
                   "\" \"" + system_id + "\">\n");
435
      } else {
436
        doc.append("<!DOCTYPE " + docname + ">\n");
437
      }
438
    }
439
    doc.append(rootNode.toString());
440
  
441
    return (doc.toString());
442
  }
443

    
444
  /**
445
   * Print a text representation of the XML document to a Writer
446
   *
447
   * @param pw the Writer to which we print the document
448
   */
449
  public void toXml(Writer pw) throws McdbException
450
  {
451
    PrintWriter out = null;
452
    if (pw instanceof PrintWriter) {
453
      out = (PrintWriter)pw;
454
    } else {
455
      out = new PrintWriter(pw);
456
    }
457

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

    
465
    Stack openElements = new Stack();
466
    boolean atRootElement = true;
467
    boolean previousNodeWasElement = false;
468

    
469
    // Step through all of the node records we were given
470
    Iterator it = nodeRecordList.iterator();
471
    while (it.hasNext()) {
472
      NodeRecord currentNode = (NodeRecord)it.next();
473
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
474
                          //" (" + currentNode.parentnodeid +
475
                          //", " + currentNode.nodeindex + 
476
                          //", " + currentNode.nodetype + 
477
                          //", " + currentNode.nodename + 
478
                          //", " + currentNode.nodedata + ")]");
479
      // Print the end tag for the previous node if needed
480
      //
481
      // This is determined by inspecting the parent nodeid for the
482
      // currentNode.  If it is the same as the nodeid of the last element
483
      // that was pushed onto the stack, then we are still in that previous
484
      // parent element, and we do nothing.  However, if it differs, then we
485
      // have returned to a level above the previous parent, so we go into
486
      // a loop and pop off nodes and print out their end tags until we get
487
      // the node on the stack to match the currentNode parentnodeid
488
      //
489
      // So, this of course means that we rely on the list of elements
490
      // having been sorted in a depth first traversal of the nodes, which
491
      // is handled by the NodeComparator class used by the TreeSet
492
      if (!atRootElement) {
493
        NodeRecord currentElement = (NodeRecord)openElements.peek();
494
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
495
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
496
            currentElement = (NodeRecord)openElements.pop();
497
            util.debugMessage("\n POPPED: " + currentElement.nodename);
498
            if (previousNodeWasElement) {
499
              out.print(">");
500
              previousNodeWasElement = false;
501
            }  
502
            if ( currentElement.nodeprefix != null ) {
503
              out.print("</" + currentElement.nodeprefix + ":" + 
504
                        currentElement.nodename + ">" );
505
            } else {
506
              out.print("</" + currentElement.nodename + ">" );
507
            }
508
            currentElement = (NodeRecord)openElements.peek();
509
          }
510
        }
511
      }
512

    
513
      // Handle the DOCUMENT node
514
      if (currentNode.nodetype.equals("DOCUMENT")) {
515
        out.println("<?xml version=\"1.0\"?>");
516
      
517
        if (docname != null) {
518
          if ((doctype != null) && (system_id != null)) {
519
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
520
                       "\" \"" + system_id + "\">");
521
          } else {
522
            out.println("<!DOCTYPE " + docname + ">");
523
          }
524
        }
525

    
526
      // Handle the ELEMENT nodes
527
      } else if (currentNode.nodetype.equals("ELEMENT")) {
528
        if (atRootElement) {
529
          atRootElement = false;
530
        } else {
531
          if (previousNodeWasElement) {
532
            out.print(">");
533
          }
534
        }
535
        openElements.push(currentNode);
536
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
537
        previousNodeWasElement = true;
538
        if ( currentNode.nodeprefix != null ) {
539
          out.print("<" + currentNode.nodeprefix + ":" + currentNode.nodename);
540
        } else {
541
          out.print("<" + currentNode.nodename);
542
        }
543

    
544
      // Handle the ATTRIBUTE nodes
545
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
546
        if ( currentNode.nodeprefix != null ) {
547
          out.print(" " + currentNode.nodeprefix + ":" + currentNode.nodename +
548
                    "=\"" + currentNode.nodedata + "\"");
549
        } else {
550
          out.print(" " + currentNode.nodename + "=\"" +
551
                    currentNode.nodedata + "\"");
552
        }
553

    
554
      // Handle the NAMESPACE nodes
555
      } else if (currentNode.nodetype.equals("NAMESPACE")) {
556
        out.print(" xmlns:" + currentNode.nodename + "=\""
557
                 + currentNode.nodedata + "\"");
558

    
559
      // Handle the TEXT nodes
560
      } else if (currentNode.nodetype.equals("TEXT")) {
561
        if (previousNodeWasElement) {
562
          out.print(">");
563
        }
564
        out.print(currentNode.nodedata);
565
        previousNodeWasElement = false;
566

    
567
      // Handle the COMMENT nodes
568
      } else if (currentNode.nodetype.equals("COMMENT")) {
569
        if (previousNodeWasElement) {
570
          out.print(">");
571
        }
572
        out.print("<!--" + currentNode.nodedata + "-->");
573
        previousNodeWasElement = false;
574

    
575
      // Handle the PI nodes
576
      } else if (currentNode.nodetype.equals("PI")) {
577
        if (previousNodeWasElement) {
578
          out.print(">");
579
        }
580
        out.print("<?" + currentNode.nodename + " " +
581
                        currentNode.nodedata + "?>");
582
        previousNodeWasElement = false;
583

    
584
      // Handle any other node type (do nothing)
585
      } else {
586
        // Any other types of nodes are not handled.
587
        // Probably should throw an exception here to indicate this
588
      }
589
      out.flush();
590
    }
591

    
592
    // Print the final end tag for the root element
593
    while(!openElements.empty())
594
    {
595
      NodeRecord currentElement = (NodeRecord)openElements.pop();
596
      util.debugMessage("\n POPPED: " + currentElement.nodename);
597
      if ( currentElement.nodeprefix != null ) {
598
        out.print("</" + currentElement.nodeprefix + ":" + 
599
                  currentElement.nodename + ">" );
600
      } else {
601
        out.print("</" + currentElement.nodename + ">" );
602
      }
603
    }
604
    out.flush();
605
  }
606
  
607
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
608
  {
609
    //System.out.println("inRevisionOnly");
610
    PreparedStatement pstmt;
611
    String rev = docid.getRev();
612
    String newid = docid.getIdentifier();
613
    pstmt = conn.prepareStatement("select rev from xml_documents " +
614
                                  "where docid like '" + newid + "'");
615
    pstmt.execute();
616
    ResultSet rs = pstmt.getResultSet();
617
    boolean tablehasrows = rs.next();
618
    if(rev.equals("newest") || rev.equals("all"))
619
    {
620
      return false;
621
    }
622
    
623
    if(tablehasrows)
624
    {
625
      int r = rs.getInt(1);
626
      pstmt.close();
627
      if(new Integer(rev).intValue() == r)
628
      { //the current revision in in xml_documents
629
        //System.out.println("returning false");
630
        return false;
631
      }
632
      else if(new Integer(rev).intValue() < r)
633
      { //the current revision is in xml_revisions.
634
        //System.out.println("returning true");
635
        return true;
636
      }
637
      else if(new Integer(rev).intValue() > r)
638
      { //error, rev cannot be greater than r
639
        throw new Exception("requested revision cannot be greater than " +
640
                            "the latest revision number.");
641
      }
642
    }
643
    throw new Exception("the requested docid '" + docid.toString() + 
644
                        "' does not exist");
645
  }
646

    
647
  private void getDocumentInfo(String docid) throws McdbException, 
648
                                                    AccessionNumberException
649
  {
650
    getDocumentInfo(new DocumentIdentifier(docid));
651
  }
652
  
653
  /**
654
   * Look up the document type information from the database
655
   *
656
   * @param docid the id of the document to look up
657
   */
658
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException 
659
  {
660
    PreparedStatement pstmt;
661
    String table = "xml_documents";
662
    
663
    try
664
    {
665
      if(isRevisionOnly(docid))
666
      { //pull the document from xml_revisions instead of from xml_documents;
667
        table = "xml_revisions";
668
      }
669
    }
670
    catch(Exception e)
671
    {
672
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
673
                          e.getMessage());
674
    }
675
    
676
    //deal with the key words here.
677
    
678
    if(docid.getRev().equals("all"))
679
    {
680
      
681
    }
682
    
683
    try {
684
      StringBuffer sql = new StringBuffer();
685
// DOCTITLE attr cleared from the db
686
//      sql.append("SELECT docname, doctype, rootnodeid, doctitle, ");
687
      sql.append("SELECT docname, doctype, rootnodeid, ");
688
      sql.append("date_created, date_updated, user_owner, user_updated, ");
689
      sql.append("server_location, public_access, rev");
690
      sql.append(" FROM ").append(table);
691
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
692
      sql.append("' and rev like '").append(docid.getRev()).append("'");
693
      //System.out.println(sql.toString());
694
      pstmt =
695
        conn.prepareStatement(sql.toString());
696
      // Bind the values to the query
697
      //pstmt.setString(1, docid.getIdentifier());
698
      //pstmt.setString(2, docid.getRev());
699

    
700
      pstmt.execute();
701
      ResultSet rs = pstmt.getResultSet();
702
      boolean tableHasRows = rs.next();
703
      if (tableHasRows) {
704
        this.docname        = rs.getString(1);
705
        this.doctype        = rs.getString(2);
706
        this.rootnodeid     = rs.getLong(3);
707
// DOCTITLE attr cleared from the db
708
//        this.doctitle       = rs.getString(4);
709
        this.createdate     = rs.getString(4);
710
        this.updatedate     = rs.getString(5);
711
        this.userowner      = rs.getString(6);
712
        this.userupdated    = rs.getString(7);
713
        this.serverlocation = rs.getInt(8);
714
        this.publicaccess   = rs.getString(9);
715
        this.rev            = rs.getInt(10);
716
      } 
717
      pstmt.close();
718
      if (this.doctype != null) {
719
        pstmt =
720
          conn.prepareStatement("SELECT system_id " +
721
                                  "FROM xml_catalog " +
722
                                 "WHERE public_id = ?");
723
        // Bind the values to the query
724
        pstmt.setString(1, doctype);
725
  
726
        pstmt.execute();
727
        rs = pstmt.getResultSet();
728
        tableHasRows = rs.next();
729
        if (tableHasRows) {
730
          this.system_id  = rs.getString(1);
731
        } 
732
        pstmt.close();
733
      }
734
    } catch (SQLException e) {
735
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
736
                          e.getMessage());
737
      e.printStackTrace(System.out);
738
      throw new McdbException("Error accessing database connection in " +
739
                              "DocumentImpl.getDocumentInfo: ", e);
740
    }
741

    
742
    if (this.docname == null) {
743
      throw new McdbDocNotFoundException("Document not found: " + docid);
744
    }
745
  }
746

    
747
  /**
748
   * Look up the node data from the database
749
   *
750
   * @param rootnodeid the id of the root node of the node tree to look up
751
   */
752
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
753
  {
754
    PreparedStatement pstmt;
755
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
756
    long nodeid = 0;
757
    long parentnodeid = 0;
758
    long nodeindex = 0;
759
    String nodetype = null;
760
    String nodename = null;
761
    String nodeprefix = null;
762
    String nodedata = null;
763
    String quotechar = dbAdapter.getStringDelimiter();
764

    
765
    try {
766
      pstmt =
767
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
768
           "nodetype,nodename,nodeprefix,nodedata " +               
769
           "FROM xml_nodes WHERE rootnodeid = ?");
770

    
771
      // Bind the values to the query
772
      pstmt.setLong(1, rootnodeid);
773

    
774
      pstmt.execute();
775
      ResultSet rs = pstmt.getResultSet();
776
      boolean tableHasRows = rs.next();
777
      while (tableHasRows) {
778
        nodeid = rs.getLong(1);
779
        parentnodeid = rs.getLong(2);
780
        nodeindex = rs.getLong(3);
781
        nodetype = rs.getString(4);
782
        nodename = rs.getString(5);
783
        nodeprefix = rs.getString(6);
784
        nodedata = rs.getString(7);
785
        nodedata = MetaCatUtil.normalize(nodedata);
786
        // add the data to the node record list hashtable
787
        NodeRecord currentRecord = new NodeRecord(nodeid,parentnodeid,nodeindex,
788
                                      nodetype, nodename, nodeprefix, nodedata);
789
        nodeRecordList.add(currentRecord);
790

    
791
        // Advance to the next node
792
        tableHasRows = rs.next();
793
      } 
794
      pstmt.close();
795

    
796
    } catch (SQLException e) {
797
      throw new McdbException("Error in DocumentImpl.getNodeRecordList " +
798
                              e.getMessage());
799
    }
800

    
801
    if (nodeRecordList != null) {
802
      return nodeRecordList;
803
    } else {
804
      throw new McdbException("Error getting node data: " + docid);
805
    }
806
  }
807
  
808
// NOT USED ANY MORE
809
//  /** creates SQL code and inserts new document into DB connection 
810
//   default serverCode of 1*/
811
//  private void writeDocumentToDB(String action, String user)
812
//               throws SQLException, Exception
813
//  {
814
//    writeDocumentToDB(action, user, null, 1);
815
//  }
816

    
817
 /** creates SQL code and inserts new document into DB connection */
818
  private void writeDocumentToDB(String action, String user, String pub, 
819
                                 String catalogid, int serverCode) 
820
               throws SQLException, Exception {
821
    String sysdate = dbAdapter.getDateTimeFunction();
822

    
823
    try {
824
      PreparedStatement pstmt = null;
825

    
826
      if (action.equals("INSERT")) {
827
        //AccessionNumber ac = new AccessionNumber();
828
        //this.docid = ac.generate(docid, "INSERT");
829
        pstmt = conn.prepareStatement(
830
                "INSERT INTO xml_documents " +
831
                "(docid, rootnodeid, docname, doctype, " + 
832
                "user_owner, user_updated, date_created, date_updated, " + 
833
                "public_access, catalog_id, server_location) " +
834
                "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", " + sysdate + 
835
                ", ?, ?, ?)");
836
        //note that the server_location is set to 1. 
837
        //this means that "localhost" in the xml_replication table must
838
        //always be the first entry!!!!!
839
        
840
        // Bind the values to the query
841
        pstmt.setString(1, this.docid);
842
        pstmt.setLong(2, rootnodeid);
843
        pstmt.setString(3, docname);
844
        pstmt.setString(4, doctype);
845
        pstmt.setString(5, user);
846
        pstmt.setString(6, user);
847
        if ( pub == null ) {
848
          pstmt.setString(7, null);
849
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
850
          pstmt.setInt(7, 1);
851
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
852
          pstmt.setInt(7, 0);
853
        }
854
        pstmt.setString(8, catalogid);
855
        pstmt.setInt(9, serverCode);
856
      } else if (action.equals("UPDATE")) {
857

    
858
        // Save the old document entry in a backup table
859
        DocumentImpl.archiveDocRevision( conn, docid, user );
860
        DocumentImpl thisdoc = new DocumentImpl(conn, docid);
861
        int thisrev = thisdoc.getRev();
862
        
863
        //if the updated vesion is not greater than current one,
864
        //throw it into a exception
865
        if (Integer.parseInt(updatedVersion)<=thisrev)
866
        {
867
          throw new Exception("Next revision number couldn't be less"
868
                               +" than or equal "+ thisrev);
869
        }
870
        else
871
        {
872
          //set the user specified revision 
873
          thisrev=Integer.parseInt(updatedVersion);
874
        }
875
        
876
        // Delete index for the old version of docid
877
        // The new index is inserting on the next calls to DBSAXNode
878
        pstmt = conn.prepareStatement(
879
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
880
        pstmt.execute();
881
        pstmt.close();
882

    
883
        // Update the new document to reflect the new node tree
884
        pstmt = conn.prepareStatement(
885
            "UPDATE xml_documents " +
886
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
887
            "user_updated = ?, date_updated = " + sysdate + ", " +
888
            "server_location = ?, rev = ?, public_access = ?, catalog_id = ? " +
889
            "WHERE docid = ?");
890
        // Bind the values to the query
891
        pstmt.setLong(1, rootnodeid);
892
        pstmt.setString(2, docname);
893
        pstmt.setString(3, doctype);
894
        pstmt.setString(4, user);
895
        pstmt.setInt(5, serverCode);
896
        pstmt.setInt(6, thisrev);
897
        if ( pub == null ) {
898
          pstmt.setString(7, null);
899
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
900
          pstmt .setInt(7, 1);
901
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
902
          pstmt.setInt(7, 0);
903
        }
904
        pstmt.setString(8, catalogid);
905
        pstmt.setString(9, this.docid);
906

    
907
      } else {
908
        System.err.println("Action not supported: " + action);
909
      }
910

    
911
      // Do the insertion
912
      pstmt.execute();
913
      pstmt.close();
914

    
915
    } catch (SQLException sqle) {
916
      throw sqle;
917
    } catch (Exception e) {
918
      throw e;
919
    }
920
  }
921

    
922
  /**
923
   * Write an XML file to the database, given a filename
924
   *
925
   * @param conn the JDBC connection to the database
926
   * @param filename the filename to be loaded into the database
927
   * @param pub flag for public "read" access on document
928
   * @param dtdfilename the dtd to be uploaded on server's file system
929
   * @param action the action to be performed (INSERT OR UPDATE)
930
   * @param docid the docid to use for the INSERT OR UPDATE
931
   * @param user the user that owns the document
932
   * @param groups the groups to which user belongs
933
   */
934
  public static String write(Connection conn,String filename,
935
                             String pub, String dtdfilename,
936
                             String action, String docid, String user,
937
                             String[] groups )
938
                throws Exception {
939
                  
940
    Reader dtd = null;
941
    if ( dtdfilename != null ) {
942
      dtd = new FileReader(new File(dtdfilename).toString());
943
    }
944
    return write ( conn, new FileReader(new File(filename).toString()),
945
                   pub, dtd, action, docid, user, groups, false);
946
  }
947

    
948
  public static String write(Connection conn,Reader xml,String pub,Reader dtd,
949
                             String action, String docid, String user,
950
                             String[] groups, boolean validate)
951
                throws Exception {
952
    //this method will be called in handleUpdateOrInsert method 
953
    //in MetacatServlet class
954
    // get server location for this doc
955
    int serverLocation=getServerLocationNumber(conn,docid);
956
    //System.out.println("server location: "+serverLocation);
957
    return write(conn,xml,pub,dtd,action,docid,user,groups,serverLocation,false,
958
                 validate);
959
  }
960

    
961
  public static String write(Connection conn, Reader xml, String pub,
962
                             String action, String docid, String user,
963
                             String[] groups )
964
                throws Exception {
965
    if(action.equals("UPDATE"))
966
    {//if the document is being updated then use the servercode from the 
967
     //originally inserted document.
968
      DocumentImpl doc = new DocumentImpl(conn, docid);
969
      int servercode = doc.getServerlocation();
970
      return write(conn, xml, pub, action, docid, user, groups, servercode);
971
    }
972
    else
973
    {//if the file is being inserted then the servercode is always 1
974
      return write(conn, xml, pub, action, docid, user, groups, 1);
975
    }
976
  }
977
  
978
  public static String write( Connection conn, Reader xml,
979
                              String action, String docid, String user,
980
                              String[] groups, int serverCode )
981
                throws Exception
982
  {
983
    return write(conn,xml,null,action,docid,user,groups,serverCode);
984
  }
985
  
986
  public static String write( Connection conn, Reader xml, String pub,
987
                              String action, String docid, String user,
988
                              String[] groups, int serverCode) 
989
                throws Exception
990
  {
991
    return write(conn,xml,pub,null,action,docid,user,groups,
992
                 serverCode,false,false);
993
  }
994
  
995
  public static String write( Connection conn, Reader xml, String pub,
996
                              String action, String docid, String user,
997
                              String[] groups, int serverCode, boolean override)
998
                throws Exception
999
  {
1000
    return write(conn,xml,pub,null,action,docid,user,groups,
1001
                 serverCode,override,false);
1002
  }
1003
  
1004
  /**
1005
   * Write an XML file to the database, given a Reader
1006
   *
1007
   * @param conn the JDBC connection to the database
1008
   * @param xml the xml stream to be loaded into the database
1009
   * @param pub flag for public "read" access on xml document
1010
   * @param dtd the dtd to be uploaded on server's file system
1011
   * @param action the action to be performed (INSERT or UPDATE)
1012
   * @param accnum the docid + rev# to use on INSERT or UPDATE
1013
   * @param user the user that owns the document
1014
   * @param groups the groups to which user belongs
1015
   * @param serverCode the serverid from xml_replication on which this document
1016
   *        resides.
1017
   * @param override flag to stop insert replication checking.
1018
   *        if override = true then a document not belonging to the local server
1019
   *        will not be checked upon update for a file lock.
1020
   *        if override = false then a document not from this server, upon 
1021
   *        update will be locked and version checked.
1022
   */
1023

    
1024
  public static String write( Connection conn,Reader xml,String pub,Reader dtd,
1025
                              String action, String accnum, String user,
1026
                              String[] groups, int serverCode, boolean override,
1027
                              boolean validate)
1028
                throws Exception
1029
  {
1030
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1031
    MetaCatUtil util = new MetaCatUtil();
1032
    AccessionNumber ac = new AccessionNumber(conn, accnum, action);
1033
    String docid = ac.getDocid();
1034
    String rev = ac.getRev();
1035
    MetaCatUtil.debugMessage("action: " + action + " servercode: " + 
1036
                             serverCode + " override: " + override);
1037
                        
1038
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
1039
    { //if this document being written is not a resident of this server then
1040
      //we need to try to get a lock from it's resident server.  If the
1041
      //resident server will not give a lock then we send the user a message
1042
      //saying that he/she needs to download a new copy of the file and
1043
      //merge the differences manually.
1044
      int istreamInt; 
1045
      char istreamChar;
1046
      DocumentIdentifier id = new DocumentIdentifier(accnum);
1047
      String updaterev = id.getRev();
1048
      String server = MetacatReplication.getServer(serverCode);
1049
      MetacatReplication.replLog("attempting to lock " + accnum);
1050
      URL u = new URL("https://" + server + "?server="
1051
           +util.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
1052
           +updaterev + "&docid=" + docid);
1053
      //System.out.println("sending message: " + u.toString());
1054
      String serverResStr = MetacatReplication.getURLContent(u);
1055
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
1056
      if(openingtag.equals("<lockgranted>"))
1057
      {//the lock was granted go ahead with the insert
1058
        try 
1059
        {
1060
          //System.out.println("In lockgranted");
1061
          MetacatReplication.replLog("lock granted for " + accnum + " from " +
1062
                                      server);
1063
          XMLReader parser = initializeParser(conn, action, docid, updaterev,
1064
                                  validate, user, groups, pub, serverCode, dtd);
1065
          conn.setAutoCommit(false);
1066
          parser.parse(new InputSource(xml)); 
1067
          conn.commit();
1068
          conn.setAutoCommit(true);
1069
        } 
1070
        catch (Exception e) 
1071
        {
1072
          conn.rollback();
1073
          conn.setAutoCommit(true);
1074
          throw e;
1075
        }
1076
                
1077
        //after inserting the document locally, tell the document's home server
1078
        //to come get a copy from here.
1079
        ForceReplicationHandler frh = new ForceReplicationHandler(accnum);
1080
        
1081
        return (accnum);
1082
      }
1083

    
1084
      else if(openingtag.equals("<filelocked>"))
1085
      {//the file is currently locked by another user
1086
       //notify our user to wait a few minutes, check out a new copy and try
1087
       //again.
1088
        //System.out.println("file locked");
1089
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1090
                                   server + " reason: file already locked");
1091
        throw new Exception("The file specified is already locked by another " +
1092
                            "user.  Please wait 30 seconds, checkout the " +
1093
                            "newer document, merge your changes and try " +
1094
                            "again.");
1095
      }
1096
      else if(openingtag.equals("<outdatedfile>"))
1097
      {//our file is outdated.  notify our user to check out a new copy of the
1098
       //file and merge his version with the new version.
1099
        //System.out.println("outdated file");
1100
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1101
                                    server + " reason: local file outdated");
1102
        throw new Exception("The file you are trying to update is an outdated" +
1103
                            " version.  Please checkout the newest document, " +
1104
                            "merge your changes and try again.");
1105
      }
1106
    }
1107
    
1108
    if ( action.equals("UPDATE") ) {
1109
      // check for 'write' permission for 'user' to update this document
1110

    
1111
      if ( !hasPermission(conn, user, groups, docid) ) {
1112
        throw new Exception("User " + user + 
1113
              " does not have permission to update XML Document #" + accnum);
1114
      }          
1115
    }
1116

    
1117
    try 
1118
    { 
1119
      
1120
      XMLReader parser = initializeParser(conn, action, docid, rev, validate,
1121
                                          user, groups, pub, serverCode, dtd);
1122
      conn.setAutoCommit(false);
1123
      parser.parse(new InputSource(xml));
1124
      conn.commit();
1125
      conn.setAutoCommit(true);
1126
    } 
1127
    catch (Exception e) 
1128
    {
1129
      conn.rollback();
1130
      conn.setAutoCommit(true);
1131
      throw e;
1132
    }
1133
    
1134
    //force replicate out the new document to each server in our server list.
1135
    if(serverCode == 1)
1136
    { 
1137
      
1138
      //start the thread to replicate this new document out to the other servers
1139
      ForceReplicationHandler frh = new ForceReplicationHandler(accnum, action);
1140
      
1141
    }
1142
      
1143
    return(accnum);
1144
  }
1145

    
1146
  /**
1147
   * Delete an XML file from the database (actually, just make it a revision
1148
   * in the xml_revisions table)
1149
   *
1150
   * @param docid the ID of the document to be deleted from the database
1151
   */
1152
  public static void delete( Connection conn, String accnum,
1153
                                 String user, String[] groups )
1154
                throws Exception 
1155
  {
1156
    // OLD
1157
    //DocumentIdentifier id = new DocumentIdentifier(accnum);
1158
    //String docid = id.getIdentifier();
1159
    //String rev = id.getRev();
1160
    
1161
    // OLD
1162
    // Determine if the docid,rev are OK for DELETE
1163
    //AccessionNumber ac = new AccessionNumber(conn);
1164
    //docid = ac.generate(docid, rev, "DELETE");
1165

    
1166
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1167
    AccessionNumber ac = new AccessionNumber(conn, accnum, "DELETE");
1168
    String docid = ac.getDocid();
1169
    String rev = ac.getRev();
1170
    
1171

    
1172
    // check for 'write' permission for 'user' to delete this document
1173
    if ( !hasPermission(conn, user, groups, docid) ) {
1174
      throw new Exception("User " + user + 
1175
              " does not have permission to delete XML Document #" + accnum);
1176
    }
1177

    
1178
    conn.setAutoCommit(false);
1179
    // Copy the record to the xml_revisions table
1180
    DocumentImpl.archiveDocRevision( conn, docid, user );
1181

    
1182
    // Now delete it from the xml_documents table
1183
    
1184
    Statement stmt = conn.createStatement();
1185
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
1186
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
1187
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + docid + "'");
1188
    stmt.execute("DELETE FROM xml_relation WHERE docid = '" + docid + "'");
1189
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
1190
    stmt.close();
1191
    conn.commit();
1192
    conn.setAutoCommit(true);
1193
    //IF this is a package document:
1194
    //delete all of the relations that this document created.
1195
    //if the deleted document is a package document its relations should 
1196
    //no longer be active if it has been deleted from the system.
1197
    
1198
  }
1199

    
1200
  /** 
1201
    * Check for "WRITE" permission on @docid for @user and/or @groups 
1202
    * from DB connection 
1203
    */
1204
  private static boolean hasPermission ( Connection conn, String user,
1205
                                  String[] groups, String docid ) 
1206
                  throws SQLException, Exception
1207
  {
1208
    // Check for WRITE permission on @docid for @user and/or @groups
1209
    AccessControlList aclobj = new AccessControlList(conn);
1210
    return aclobj.hasPermission("WRITE", user, groups, docid);
1211
  }
1212

    
1213
  /** 
1214
    * Check for "READ" permission base on docid, user and group
1215
    *@docid, the document
1216
    *@user, user name
1217
    *@group, user's group
1218
    * 
1219
    */
1220
  public boolean hasReadPermission ( Connection conn, String user,
1221
                                  String[] groups, String docId ) 
1222
                  throws SQLException, Exception
1223
  {
1224
    // Check for READ permission on @docid for @user and/or @groups
1225
    AccessControlList aclObj = new AccessControlList(conn);
1226
    return aclObj.hasPermission("READ", user, groups, docId);
1227
  }  
1228

    
1229
  /**
1230
   * Set up the parser handlers for writing the document to the database
1231
   */
1232
  private static XMLReader initializeParser(Connection conn, String action,
1233
                               String docid, String rev, boolean validate, 
1234
                                   String user, String[] groups, String pub, 
1235
                                   int serverCode, Reader dtd) 
1236
                           throws Exception 
1237
  {
1238
    XMLReader parser = null;
1239
    //
1240
    // Set up the SAX document handlers for parsing
1241
    //
1242
    try {
1243
      //create a DBSAXHandler object which has the revision specification
1244
      ContentHandler chandler = new DBSAXHandler(conn, action, docid, rev,
1245
                                                 user, groups, pub, serverCode);
1246
      EntityResolver eresolver= new DBEntityResolver(conn,
1247
                                                 (DBSAXHandler)chandler, dtd);
1248
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1249

    
1250
      // Get an instance of the parser
1251
      String parserName = MetaCatUtil.getOption("saxparser");
1252
      parser = XMLReaderFactory.createXMLReader(parserName);
1253

    
1254
      // Turn on validation
1255
      parser.setFeature("http://xml.org/sax/features/validation", validate);
1256
      // Turn off Including all external parameter entities
1257
      // (the external DTD subset also)
1258
      // Doesn't work well, probably the feature name is not correct
1259
      // parser.setFeature(
1260
      //  "http://xml.org/sax/features/external-parameter-entities", false);
1261
      
1262
      // Set Handlers in the parser
1263
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
1264
                         chandler);
1265
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
1266
                         chandler);
1267
      parser.setContentHandler((ContentHandler)chandler);
1268
      parser.setEntityResolver((EntityResolver)eresolver);
1269
      parser.setDTDHandler((DTDHandler)dtdhandler);
1270
      parser.setErrorHandler((ErrorHandler)chandler);
1271

    
1272
    } catch (Exception e) {
1273
      throw e;
1274
    }
1275

    
1276
    return parser;
1277
  }
1278

    
1279
  /** Save a document entry in the xml_revisions table */
1280
  private static void archiveDocRevision(Connection conn, String docid,
1281
                                         String user) 
1282
                                         throws SQLException {
1283
    String sysdate = dbAdapter.getDateTimeFunction();
1284
    // create a record in xml_revisions table 
1285
    // for that document as selected from xml_documents
1286
    PreparedStatement pstmt = conn.prepareStatement(
1287
      "INSERT INTO xml_revisions " +
1288
        "(docid, rootnodeid, docname, doctype, " +
1289
        "user_owner, user_updated, date_created, date_updated, " +
1290
        "server_location, rev, public_access, catalog_id) " +
1291
      "SELECT ?, rootnodeid, docname, doctype, " + 
1292
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
1293
        "server_location, rev, public_access, catalog_id " +
1294
      "FROM xml_documents " +
1295
      "WHERE docid = ?");
1296
    // Bind the values to the query and execute it
1297
    pstmt.setString(1, docid);
1298
    pstmt.setString(2, user);
1299
    pstmt.setString(3, docid);
1300
    pstmt.execute();
1301
    pstmt.close();
1302

    
1303
  }
1304
  
1305
  /**
1306
    * delete a entry in xml_table for given docid
1307
    * @param docId, the id of the document need to be delete
1308
    */
1309
  private static void deleteXMLDocuments(Connection conn, String docId) 
1310
                                         throws SQLException 
1311
  {
1312
    //delete a record 
1313
    PreparedStatement pStmt = 
1314
             conn.prepareStatement("DELETE FROM xml_documents WHERE docid = '" 
1315
                                              + docId + "'");
1316
    pStmt.execute();
1317
    pStmt.close();
1318

    
1319
  }//deleteXMLDocuments
1320
  
1321
  /**
1322
    * Get last revision number from database for a docid
1323
    * If couldn't find an entry,  -1 will return
1324
    * The return value is integer because we want compare it to there new one
1325
    * @param docid <sitecode>.<uniqueid> part of Accession Number
1326
    */
1327
  private static int getLatestRevisionNumber(Connection conn, String docId)
1328
                                      throws SQLException
1329
  {
1330
    int rev = 1;
1331
    PreparedStatement pStmt;
1332
     
1333
    pStmt = conn.prepareStatement
1334
              ("SELECT rev FROM xml_documents WHERE docid='" + docId + "'");
1335
    pStmt.execute();
1336

    
1337
    ResultSet rs = pStmt.getResultSet();
1338
    boolean hasRow = rs.next();
1339
    if (hasRow)
1340
    {
1341
      rev = rs.getInt(1);
1342
      pStmt.close();
1343
    }
1344
    else
1345
    {
1346
      rev=-1;
1347
      pStmt.close();
1348
    }
1349
      
1350
    return rev;
1351
  }//getLatestRevisionNumber
1352
  
1353
  /**
1354
   * Get server location form database for a accNum
1355
   * 
1356
   * @param accum <sitecode>.<uniqueid>.<rev>
1357
   */
1358
  private static int getServerLocationNumber(Connection conn, String accNum)
1359
                                            throws SQLException
1360
  {
1361
    //get rid of revNum part
1362
    String docId=MetaCatUtil.getDocIdFromString(accNum);
1363
    PreparedStatement pStmt;
1364
    int serverLocation;
1365
     
1366
    pStmt = conn.prepareStatement
1367
      ("SELECT server_location FROM xml_documents WHERE docid='" + docId + "'");
1368
    pStmt.execute();
1369

    
1370
    ResultSet rs = pStmt.getResultSet();
1371
    boolean hasRow = rs.next();
1372
    //if there is entry in xml_documents, get the serverlocation
1373
    if (hasRow)
1374
    {
1375
      serverLocation = rs.getInt(1);
1376
      pStmt.close();
1377
    }
1378
    else
1379
    {
1380
      // if htere is no entry in xml_documents, we consider it is new document
1381
      // the server location is local host and value is 1
1382
      serverLocation=1;
1383
      pStmt.close();
1384
    }
1385
      
1386
    return serverLocation;
1387
  }
1388
  /**
1389
   * the main routine used to test the DBWriter utility.
1390
   * <p>
1391
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1392
   *
1393
   * @param filename the filename to be loaded into the database
1394
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1395
   * @param docid the id of the document to process
1396
   */
1397
  static public void main(String[] args) {
1398
     
1399
    try {
1400
      String filename    = null;
1401
      String dtdfilename = null;
1402
      String action      = null;
1403
      String docid       = null;
1404
      boolean showRuntime = false;
1405
      boolean useOldReadAlgorithm = false;
1406

    
1407
      // Parse the command line arguments
1408
      for ( int i=0 ; i < args.length; ++i ) {
1409
        if ( args[i].equals( "-f" ) ) {
1410
          filename =  args[++i];
1411
        } else if ( args[i].equals( "-r" ) ) {
1412
          dtdfilename =  args[++i];
1413
        } else if ( args[i].equals( "-a" ) ) {
1414
          action =  args[++i];
1415
        } else if ( args[i].equals( "-d" ) ) {
1416
          docid =  args[++i];
1417
        } else if ( args[i].equals( "-t" ) ) {
1418
          showRuntime = true;
1419
        } else if ( args[i].equals( "-old" ) ) {
1420
          useOldReadAlgorithm = true;
1421
        } else {
1422
          System.err.println
1423
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
1424
        }
1425
      }
1426
      
1427
      // Check if the required arguments are provided
1428
      boolean argsAreValid = false;
1429
      if (action != null) {
1430
        if (action.equals("INSERT")) {
1431
          if (filename != null) {
1432
            argsAreValid = true;
1433
          } 
1434
        } else if (action.equals("UPDATE")) {
1435
          if ((filename != null) && (docid != null)) {
1436
            argsAreValid = true;
1437
          } 
1438
        } else if (action.equals("DELETE")) {
1439
          if (docid != null) {
1440
            argsAreValid = true;
1441
          } 
1442
        } else if (action.equals("READ")) {
1443
          if (docid != null) {
1444
            argsAreValid = true;
1445
          } 
1446
        } 
1447
      } 
1448

    
1449
      // Print usage message if the arguments are not valid
1450
      if (!argsAreValid) {
1451
        System.err.println("Wrong number of arguments!!!");
1452
        System.err.println(
1453
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
1454
          "[-r dtdfilename]");
1455
        System.err.println(
1456
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
1457
          "[-r dtdfilename]");
1458
        System.err.println(
1459
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
1460
        System.err.println(
1461
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
1462
        return;
1463
      }
1464
      
1465
      // Time the request if asked for
1466
      double startTime = System.currentTimeMillis();
1467
      
1468
      // Open a connection to the database
1469
      MetaCatUtil util = new MetaCatUtil();
1470
      Connection dbconn = util.openDBConnection();
1471

    
1472
      double connTime = System.currentTimeMillis();
1473
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1474
      if (action.equals("READ")) {
1475
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1476
          if (useOldReadAlgorithm) {
1477
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1478
          } else {
1479
            xmldoc.toXml(new PrintWriter(System.out));
1480
          }
1481
      } else if (action.equals("DELETE")) {
1482
        DocumentImpl.delete(dbconn, docid, null, null);
1483
        System.out.println("Document deleted: " + docid);
1484
      } else {
1485
        String newdocid = DocumentImpl.write(dbconn, filename, null,
1486
                                             dtdfilename, action, docid,
1487
                                             null, null);
1488
        if ((docid != null) && (!docid.equals(newdocid))) {
1489
          if (action.equals("INSERT")) {
1490
            System.out.println("New document ID generated!!! ");
1491
          } else if (action.equals("UPDATE")) {
1492
            System.out.println("ERROR: Couldn't update document!!! ");
1493
          }
1494
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1495
          System.out.println("ERROR: Couldn't update document!!! ");
1496
        }
1497
        System.out.println("Document processing finished for: " + filename
1498
              + " (" + newdocid + ")");
1499
      }
1500

    
1501
      double stopTime = System.currentTimeMillis();
1502
      double dbOpenTime = (connTime - startTime)/1000;
1503
      double insertTime = (stopTime - connTime)/1000;
1504
      double executionTime = (stopTime - startTime)/1000;
1505
      if (showRuntime) {
1506
        System.out.println("\n\nTotal Execution time was: " + 
1507
                           executionTime + " seconds.");
1508
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
1509
                           " seconds.");
1510
        System.out.println("Time to insert document was: " + insertTime +
1511
                           " seconds.");
1512
      }
1513
      dbconn.close();
1514
    } catch (McdbException me) {
1515
      me.toXml(new PrintWriter(System.err));
1516
    } catch (AccessionNumberException ane) {
1517
      System.out.println(ane.getMessage());
1518
    } catch (Exception e) {
1519
      System.err.println("EXCEPTION HANDLING REQUIRED");
1520
      System.err.println(e.getMessage());
1521
      e.printStackTrace(System.err);
1522
    }
1523
  }
1524
}
(23-23/40)