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-26 17:27:29 -0700 (Fri, 26 Apr 2002) $'
11
 * '$Revision: 1034 $'
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.io.*;
40
import java.text.SimpleDateFormat;
41

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

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

    
59
import java.net.URL;
60

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

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

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

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

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

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

    
142
  /** 
143
   * Construct a new document instance, writing the contents to the database.
144
   * This method is called from DBSAXHandler because we need to know the
145
   * root element name for documents without a DOCTYPE before creating it.
146
   *
147
   * @param conn the JDBC Connection to which all information is written
148
   * @param rootnodeid - sequence id of the root node in the document
149
   * @param docname - the name of DTD, i.e. the name immediately following 
150
   *        the DOCTYPE keyword ( should be the root element name ) or
151
   *        the root element name if no DOCTYPE declaration provided
152
   *        (Oracle's and IBM parsers are not aware if it is not the 
153
   *        root element name)
154
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
155
   *                  following the PUBLIC keyword in DOCTYPE declaration or
156
   *                  the docname if no Public ID provided or
157
   *                  null if no DOCTYPE declaration provided
158
   * @param docid the docid to use for the INSERT OR UPDATE
159
   * @param action the action to be performed (INSERT OR UPDATE)
160
   * @param user the user that owns the document
161
   * @param pub flag for public "read" access on document
162
   * @param serverCode the serverid from xml_replication on which this document
163
   *        resides.
164
   *
165
   */
166
  public DocumentImpl(Connection conn, long rootnodeid, String docname, 
167
                      String doctype, String docid, String action, String user,
168
                      String pub, String catalogid, int serverCode)
169
                      throws SQLException, Exception
170
  {
171
    this.conn = conn;
172
    this.rootnodeid = rootnodeid;
173
    this.docname = docname;
174
    this.doctype = doctype;
175
    this.docid = docid;
176
    writeDocumentToDB(action, user, pub, catalogid, serverCode);
177
  }
178
  
179
  /** 
180
   * Construct a new document instance, writing the contents to the database.
181
   * This method is called from DBSAXHandler because we need to know the
182
   * root element name for documents without a DOCTYPE before creating it.
183
   *
184
   * In this constructor, the docid is without rev. There is a string rev to 
185
   * specify the revision user want to upadate. The revion is only need to be
186
   * greater than current one. It is not need to be sequent number just after
187
   * current one. So it is only used in update action
188
   * @param conn the JDBC Connection to which all information is written
189
   * @param rootnodeid - sequence id of the root node in the document
190
   * @param docname - the name of DTD, i.e. the name immediately following 
191
   *        the DOCTYPE keyword ( should be the root element name ) or
192
   *        the root element name if no DOCTYPE declaration provided
193
   *        (Oracle's and IBM parsers are not aware if it is not the 
194
   *        root element name)
195
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
196
   *                  following the PUBLIC keyword in DOCTYPE declaration or
197
   *                  the docname if no Public ID provided or
198
   *                  null if no DOCTYPE declaration provided
199
   * @param docid the docid to use for the UPDATE, no version number
200
   * @param version, need to be update
201
   * @param action the action to be performed (INSERT OR UPDATE)
202
   * @param user the user that owns the document
203
   * @param pub flag for public "read" access on document
204
   * @param serverCode the serverid from xml_replication on which this document
205
   *        resides.
206
   *
207
   */
208
  public DocumentImpl(Connection conn, long rootNodeId, String docName, 
209
                      String docType, String docId, String newRevision, 
210
                      String action, String user,
211
                      String pub, String catalogId, int serverCode)
212
                      throws SQLException, Exception
213
  {
214
    this.conn = conn;
215
    this.rootnodeid = rootNodeId;
216
    this.docname = docName;
217
    this.doctype = docType;
218
    this.docid = docId;
219
    this.updatedVersion = newRevision;
220
    writeDocumentToDB(action, user, pub, catalogId, serverCode);
221
  }
222
  
223
  /**
224
   * This method will be call in handleUploadRequest in MetacatServlet class
225
   */
226
  public static void registerDocument(
227
                     String docname, String doctype, String accnum, String user)
228
                     throws SQLException, AccessionNumberException, Exception
229
  {
230
    Connection dbConn = null;
231
    MetaCatUtil ut = new MetaCatUtil();
232
    dbConn = ut.getConnection();
233
    // get server location for this doc
234
    int serverLocation=getServerLocationNumber(dbConn,accnum);
235
    ut.returnConnection(dbConn);
236
    registerDocument(docname, doctype,accnum, user, serverLocation);
237
    
238
  }
239
  /**
240
   * Register a document that resides on the filesystem with the database.
241
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
242
   * Creates a reference to a filesystem document (used for non-xml data files).
243
   *
244
   * @param conn the JDBC Connection to which all information is written
245
   * @param docname - the name of DTD, i.e. the name immediately following 
246
   *        the DOCTYPE keyword ( should be the root element name ) or
247
   *        the root element name if no DOCTYPE declaration provided
248
   *        (Oracle's and IBM parsers are not aware if it is not the 
249
   *        root element name)
250
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
251
   *                  following the PUBLIC keyword in DOCTYPE declaration or
252
   *                  the docname if no Public ID provided or
253
   *                  null if no DOCTYPE declaration provided
254
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
255
   *               includes a revision number for this revision of the document 
256
   *               (e.g., knb.1.1)
257
   * @param user the user that owns the document
258
   * @param serverCode the serverid from xml_replication on which this document
259
   *        resides.
260
   */
261
  public static void registerDocument(
262
                     String docname, String doctype, String accnum, 
263
                     String user, int serverCode)
264
                     throws SQLException, AccessionNumberException, Exception
265
  {
266
    Connection dbconn = null;
267
    MetaCatUtil util = new MetaCatUtil();
268
    AccessionNumber ac;
269
    try {
270
      dbconn = util.openDBConnection();
271
      String docIdWithoutRev=MetaCatUtil.getDocIdFromString(accnum);
272
      int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
273
      int revInDataBase=getLatestRevisionNumber(dbconn, docIdWithoutRev);
274
      //revIndataBase=-1, there is no record in xml_documents table
275
      //the data file is a new one, inert it into table
276
      //user specified rev should be great than 0
277
      if (revInDataBase==-1 && userSpecifyRev>0 )
278
      {
279
        ac = new AccessionNumber(dbconn, accnum, "insert");
280
      }
281
      //rev is greater the last revsion number and revInDataBase isn't -1
282
      // it is a updated data file
283
      else if (userSpecifyRev>revInDataBase && revInDataBase>0)
284
      {
285
        
286
        //archive the old entry 
287
        archiveDocRevision(dbconn, docIdWithoutRev, user);
288
        //delete the old entry in xml_documents
289
        deleteXMLDocuments(dbconn, docIdWithoutRev);
290
        ac = new AccessionNumber(dbconn, accnum, "update");
291
      }
292
      //other situation
293
      else
294
      {
295
        
296
        throw new Exception("Revision number couldn't be "
297
                    +userSpecifyRev);
298
      }
299
      String docid = ac.getDocid();
300
      String rev = ac.getRev();
301
      SimpleDateFormat formatter = new SimpleDateFormat ("yy-MM-dd HH:mm:ss");
302
      Date localtime = new Date();
303
      String dateString = formatter.format(localtime);
304
  
305
      String sqlDateString = "to_date('" + dateString + 
306
                                          "', 'YY-MM-DD HH24:MI:SS')";
307
  
308
      StringBuffer sql = new StringBuffer();
309
      sql.append("insert into xml_documents (docid, docname, doctype, ");
310
      sql.append("user_owner, user_updated, server_location, rev,date_created");
311
      sql.append(", date_updated, public_access) values ('");
312
      sql.append(docid).append("','");
313
      sql.append(docname).append("','");
314
      sql.append(doctype).append("','");
315
      sql.append(user).append("','");
316
      sql.append(user).append("','");
317
      sql.append(serverCode).append("','");
318
      sql.append(rev).append("',");
319
      sql.append(sqlDateString).append(",");
320
      sql.append(sqlDateString).append(",");
321
      sql.append("'0')");
322
      PreparedStatement pstmt = dbconn.prepareStatement(sql.toString());
323
      pstmt.execute();
324
      pstmt.close();
325
      dbconn.close();
326
    } finally {
327
      util.returnConnection(dbconn);
328
    }    
329
  }
330
  
331
 /**
332
   * This method will register a data file entry in xml_documents and save a
333
   * data file input Stream into file system..
334
   *
335
   * @param  input, the input stream which contain the file content.
336
   * @param  , the input stream which contain the file content
337
   * @param docname - the name of DTD, for data file, it is a docid number.
338
   * @param doctype - "BIN" for data file
339
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
340
   *               includes a revision number for this revision of the document 
341
   *               (e.g., knb.1.1)
342
   * @param user the user that owns the document
343
   * @param serverCode the serverid from xml_replication on which this document
344
   *        resides.
345
   */
346
 public static void writeDataFile(InputStream input, String filePath, 
347
    String docname, String doctype, String accnum, String user, int serverCode)
348
                     throws SQLException, AccessionNumberException, Exception
349
 {
350
    if (filePath==null||filePath.equals(""))
351
    {
352
      throw new 
353
            Exception("Please specify the directory where file will be store");
354
    }
355
    if (accnum==null||accnum.equals(""))
356
    {
357
      throw new Exception("Please specify the stored file name");
358
    }
359
    //make sure user have file lock grant(local metacat means have it too)
360
    if (getDataFileLockGrant(accnum))
361
    {
362
      //register data file into xml_documents table
363
      registerDocument(docname, doctype, accnum, user, serverCode);
364
      //write inputstream into file system.
365
      File dataDirectory = new File(filePath);
366
      File newFile = new File(dataDirectory, accnum); 
367
       
368
      // create a buffered byte output stream
369
      // that uses a default-sized output buffer
370
      FileOutputStream fos = new FileOutputStream(newFile);
371
      BufferedOutputStream outPut = new BufferedOutputStream(fos);
372

    
373
      BufferedInputStream bis = null;
374
      bis = new BufferedInputStream(input);
375
      byte[] buf = new byte[4 * 1024]; // 4K buffer
376
      int b = bis.read(buf);
377
       
378
      while (b != -1) 
379
      {
380
        outPut.write(buf, 0, b);
381
        b = bis.read(buf);
382
      }
383
      bis.close();
384
	    outPut.close();
385
	    fos.close();
386
      //force replication to other server
387
      MetaCatUtil util = new MetaCatUtil();
388
      if ((util.getOption("replicationdata")).equals("on"))
389
      {
390
        ForceReplicationHandler frh = new 
391
                               ForceReplicationHandler(accnum, "insert", false);
392
      }
393
    }//if
394
 }
395
  
396
 /**
397
   * unRegister a an XML file (data file) from the database (actually, 
398
   * just make it a revision in the xml_revisions table and delete it
399
   * from xml_documents table). 
400
   *
401
   * @param docid the ID of the document to be deleted from the database
402
   */
403
  public static void unRegisterDocument( Connection conn, String accnum,
404
                                 String user, String[] groups )
405
                throws Exception 
406
  {
407
    
408

    
409
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
410
    AccessionNumber ac = new AccessionNumber(conn, accnum, "DELETE");
411
    String docid = ac.getDocid();
412
    String rev = ac.getRev();
413
    
414

    
415
    // check for 'write' permission for 'user' to delete this document
416
    if ( !hasPermission(conn, user, groups, docid) ) {
417
      throw new Exception("User " + user + 
418
              " does not have permission to delete XML Document #" + accnum);
419
    }
420

    
421
    conn.setAutoCommit(false);
422
    // Copy the record to the xml_revisions table
423
    DocumentImpl.archiveDocRevision( conn, docid, user );
424

    
425
    // Now delete it from the xml_documents table
426
    
427
    Statement stmt = conn.createStatement();
428
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
429
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
430
    stmt.close();
431
    conn.commit();
432
    conn.setAutoCommit(true);
433
   
434
    
435
  }
436
  
437
  public static boolean getDataFileLockGrant(String accnum) 
438
                                                  throws Exception
439
  { 
440
    Connection dbConn = null;
441
    MetaCatUtil ut= new MetaCatUtil();
442
    dbConn = ut.getConnection();
443
    int serverLocation=getServerLocationNumber(dbConn,accnum);
444
    ut.returnConnection(dbConn);
445
    return getDataFileLockGrant(accnum,serverLocation);
446
  }
447
    
448
  /**
449
   * The method will check if metacat can get data file lock grant
450
   * If server code is 1, it get.
451
   * If server code is not 1 but call replication getlock successfully,
452
   * it get
453
   * else, it didn't get
454
   * @param accnum, the ID of the document 
455
   * @param action, the action to the document
456
   * @param serverCode, the server location code
457
   */
458
  public static boolean getDataFileLockGrant(String accnum, int serverCode)
459
                                          throws Exception 
460
  {
461
    boolean flag=true;
462
    MetaCatUtil util = new MetaCatUtil();
463
    String docid = util.getDocIdFromString(accnum);
464
    int rev = util.getVersionFromString(accnum);
465
    
466
    if (serverCode == 1)
467
    {
468
      flag=true;
469
      return flag;
470
    }
471
    
472
    //if((serverCode != 1 && action.equals("UPDATE")) )
473
    if (serverCode != 1)
474
    { //if this document being written is not a resident of this server then
475
      //we need to try to get a lock from it's resident server.  If the
476
      //resident server will not give a lock then we send the user a message
477
      //saying that he/she needs to download a new copy of the file and
478
      //merge the differences manually.
479
      
480
      String server = MetacatReplication.getServer(serverCode);
481
      MetacatReplication.replLog("attempting to lock " + accnum);
482
      URL u = new URL("https://" + server + "?server="
483
           +util.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
484
           +rev + "&docid=" + docid);
485
      //System.out.println("sending message: " + u.toString());
486
      String serverResStr = MetacatReplication.getURLContent(u);
487
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
488
      if(openingtag.equals("<lockgranted>"))
489
      {
490
        //the lock was granted go ahead with the insert
491
        //System.out.println("In lockgranted");
492
        MetacatReplication.replLog("lock granted for " + accnum + " from " +
493
                                      server);
494
        flag=true;  
495
        return flag;
496
      }//if
497

    
498
      else if(openingtag.equals("<filelocked>"))
499
      {//the file is currently locked by another user
500
       //notify our user to wait a few minutes, check out a new copy and try
501
       //again.
502
        //System.out.println("file locked");
503
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
504
                                   server + " reason: file already locked");
505
        throw new Exception("The file specified is already locked by another " +
506
                            "user.  Please wait 30 seconds, checkout the " +
507
                            "newer document, merge your changes and try " +
508
                            "again.");
509
      }
510
      else if(openingtag.equals("<outdatedfile>"))
511
      {//our file is outdated.  notify our user to check out a new copy of the
512
       //file and merge his version with the new version.
513
        //System.out.println("outdated file");
514
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
515
                                    server + " reason: local file outdated");
516
        throw new Exception("The file you are trying to update is an outdated" +
517
                            " version.  Please checkout the newest document, " +
518
                            "merge your changes and try again.");
519
      }//else if
520
    }//if
521
    
522
   return flag;
523
   
524
  }//getDataFileLockGrant
525
  
526
  /**
527
   * get the document name
528
   */
529
  public String getDocname() {
530
    return docname;
531
  }
532

    
533
  /**
534
   * get the document type (which is the PublicID)
535
   */
536
  public String getDoctype() {
537
    return doctype;
538
  }
539

    
540
  /**
541
   * get the system identifier
542
   */
543
  public String getSystemID() {
544
    return system_id;
545
  }
546

    
547
  /**
548
   * get the root node identifier
549
   */
550
  public long getRootNodeID() {
551
    return rootnodeid;
552
  }
553
  
554
  /**
555
   * get the creation date
556
   */
557
  public String getCreateDate() {
558
    return createdate;
559
  }
560
  
561
  /**
562
   * get the update date
563
   */
564
  public String getUpdateDate() {
565
    return updatedate;
566
  }
567

    
568
  /** 
569
   * Get the document identifier (docid)
570
   */
571
  public String getDocID() {
572
    return docid;
573
  }
574
  
575
// DOCTITLE attr cleared from the db
576
//  /**
577
//   *get the document title
578
//   */
579
//  public String getDocTitle() {
580
//    return doctitle;
581
//  }
582
  
583
  public String getUserowner() {
584
    return userowner;
585
  }
586
  
587
  public String getUserupdated() {
588
    return userupdated;
589
  }
590
  
591
  public int getServerlocation() {
592
    return serverlocation;
593
  }
594
  
595
  public String getPublicaccess() {
596
    return publicaccess;
597
  }
598
  
599
  public int getRev() {
600
    return rev;
601
  }
602
  
603
   /**
604
   * Print a string representation of the XML document
605
   */
606
  public String toString()
607
  {
608
    StringWriter docwriter = new StringWriter();
609
    try {
610
      this.toXml(docwriter);
611
    } catch (McdbException mcdbe) {
612
      return null;
613
    }
614
    String document = docwriter.toString();
615
    return document;
616
  }
617

    
618
  /**
619
   * Get a text representation of the XML document as a string
620
   * This older algorithm uses a recursive tree of Objects to represent the
621
   * nodes of the tree.  Each object is passed the data for the document 
622
   * and searches all of the document data to find its children nodes and
623
   * recursively build.  Thus, because each node reads the whole document,
624
   * this algorithm is extremely slow for larger documents, and the time
625
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
626
   * better algorithm.
627
   */
628
  public String readUsingSlowAlgorithm() throws McdbException
629
  {
630
    StringBuffer doc = new StringBuffer();
631

    
632
    // First, check that we have the needed node data, and get it if not
633
    if (nodeRecordList == null) {
634
      nodeRecordList = getNodeRecordList(rootnodeid);
635
    }
636

    
637
    // Create the elements from the downloaded data in the TreeSet
638
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
639

    
640
    // Append the resulting document to the StringBuffer and return it
641
    doc.append("<?xml version=\"1.0\"?>\n");
642
      
643
    if (docname != null) {
644
      if ((doctype != null) && (system_id != null)) {
645
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
646
                   "\" \"" + system_id + "\">\n");
647
      } else {
648
        doc.append("<!DOCTYPE " + docname + ">\n");
649
      }
650
    }
651
    doc.append(rootNode.toString());
652
  
653
    return (doc.toString());
654
  }
655

    
656
  /**
657
   * Print a text representation of the XML document to a Writer
658
   *
659
   * @param pw the Writer to which we print the document
660
   */
661
  public void toXml(Writer pw) throws McdbException
662
  {
663
    PrintWriter out = null;
664
    if (pw instanceof PrintWriter) {
665
      out = (PrintWriter)pw;
666
    } else {
667
      out = new PrintWriter(pw);
668
    }
669

    
670
    MetaCatUtil util = new MetaCatUtil();
671
    
672
    // First, check that we have the needed node data, and get it if not
673
    if (nodeRecordList == null) {
674
      nodeRecordList = getNodeRecordList(rootnodeid);
675
    }
676

    
677
    Stack openElements = new Stack();
678
    boolean atRootElement = true;
679
    boolean previousNodeWasElement = false;
680

    
681
    // Step through all of the node records we were given
682
    Iterator it = nodeRecordList.iterator();
683
    while (it.hasNext()) {
684
      NodeRecord currentNode = (NodeRecord)it.next();
685
      //util.debugMessage("[Got Node ID: " + currentNode.nodeid +
686
                          //" (" + currentNode.parentnodeid +
687
                          //", " + currentNode.nodeindex + 
688
                          //", " + currentNode.nodetype + 
689
                          //", " + currentNode.nodename + 
690
                          //", " + currentNode.nodedata + ")]");
691
      // Print the end tag for the previous node if needed
692
      //
693
      // This is determined by inspecting the parent nodeid for the
694
      // currentNode.  If it is the same as the nodeid of the last element
695
      // that was pushed onto the stack, then we are still in that previous
696
      // parent element, and we do nothing.  However, if it differs, then we
697
      // have returned to a level above the previous parent, so we go into
698
      // a loop and pop off nodes and print out their end tags until we get
699
      // the node on the stack to match the currentNode parentnodeid
700
      //
701
      // So, this of course means that we rely on the list of elements
702
      // having been sorted in a depth first traversal of the nodes, which
703
      // is handled by the NodeComparator class used by the TreeSet
704
      if (!atRootElement) {
705
        NodeRecord currentElement = (NodeRecord)openElements.peek();
706
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
707
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
708
            currentElement = (NodeRecord)openElements.pop();
709
            util.debugMessage("\n POPPED: " + currentElement.nodename);
710
            if (previousNodeWasElement) {
711
              out.print(">");
712
              previousNodeWasElement = false;
713
            }  
714
            if ( currentElement.nodeprefix != null ) {
715
              out.print("</" + currentElement.nodeprefix + ":" + 
716
                        currentElement.nodename + ">" );
717
            } else {
718
              out.print("</" + currentElement.nodename + ">" );
719
            }
720
            currentElement = (NodeRecord)openElements.peek();
721
          }
722
        }
723
      }
724

    
725
      // Handle the DOCUMENT node
726
      if (currentNode.nodetype.equals("DOCUMENT")) {
727
        out.println("<?xml version=\"1.0\"?>");
728
      
729
        if (docname != null) {
730
          if ((doctype != null) && (system_id != null)) {
731
            out.println("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
732
                       "\" \"" + system_id + "\">");
733
          } else {
734
            out.println("<!DOCTYPE " + docname + ">");
735
          }
736
        }
737

    
738
      // Handle the ELEMENT nodes
739
      } else if (currentNode.nodetype.equals("ELEMENT")) {
740
        if (atRootElement) {
741
          atRootElement = false;
742
        } else {
743
          if (previousNodeWasElement) {
744
            out.print(">");
745
          }
746
        }
747
        openElements.push(currentNode);
748
        util.debugMessage("\n PUSHED: " + currentNode.nodename);
749
        previousNodeWasElement = true;
750
        if ( currentNode.nodeprefix != null ) {
751
          out.print("<" + currentNode.nodeprefix + ":" + currentNode.nodename);
752
        } else {
753
          out.print("<" + currentNode.nodename);
754
        }
755

    
756
      // Handle the ATTRIBUTE nodes
757
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
758
        if ( currentNode.nodeprefix != null ) {
759
          out.print(" " + currentNode.nodeprefix + ":" + currentNode.nodename +
760
                    "=\"" + currentNode.nodedata + "\"");
761
        } else {
762
          out.print(" " + currentNode.nodename + "=\"" +
763
                    currentNode.nodedata + "\"");
764
        }
765

    
766
      // Handle the NAMESPACE nodes
767
      } else if (currentNode.nodetype.equals("NAMESPACE")) {
768
        out.print(" xmlns:" + currentNode.nodename + "=\""
769
                 + currentNode.nodedata + "\"");
770

    
771
      // Handle the TEXT nodes
772
      } else if (currentNode.nodetype.equals("TEXT")) {
773
        if (previousNodeWasElement) {
774
          out.print(">");
775
        }
776
        out.print(currentNode.nodedata);
777
        previousNodeWasElement = false;
778

    
779
      // Handle the COMMENT nodes
780
      } else if (currentNode.nodetype.equals("COMMENT")) {
781
        if (previousNodeWasElement) {
782
          out.print(">");
783
        }
784
        out.print("<!--" + currentNode.nodedata + "-->");
785
        previousNodeWasElement = false;
786

    
787
      // Handle the PI nodes
788
      } else if (currentNode.nodetype.equals("PI")) {
789
        if (previousNodeWasElement) {
790
          out.print(">");
791
        }
792
        out.print("<?" + currentNode.nodename + " " +
793
                        currentNode.nodedata + "?>");
794
        previousNodeWasElement = false;
795

    
796
      // Handle any other node type (do nothing)
797
      } else {
798
        // Any other types of nodes are not handled.
799
        // Probably should throw an exception here to indicate this
800
      }
801
      out.flush();
802
    }
803

    
804
    // Print the final end tag for the root element
805
    while(!openElements.empty())
806
    {
807
      NodeRecord currentElement = (NodeRecord)openElements.pop();
808
      util.debugMessage("\n POPPED: " + currentElement.nodename);
809
      if ( currentElement.nodeprefix != null ) {
810
        out.print("</" + currentElement.nodeprefix + ":" + 
811
                  currentElement.nodename + ">" );
812
      } else {
813
        out.print("</" + currentElement.nodename + ">" );
814
      }
815
    }
816
    out.flush();
817
  }
818
  
819
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
820
  {
821
    //System.out.println("inRevisionOnly");
822
    PreparedStatement pstmt;
823
    String rev = docid.getRev();
824
    String newid = docid.getIdentifier();
825
    pstmt = conn.prepareStatement("select rev from xml_documents " +
826
                                  "where docid like '" + newid + "'");
827
    pstmt.execute();
828
    ResultSet rs = pstmt.getResultSet();
829
    boolean tablehasrows = rs.next();
830
    if(rev.equals("newest") || rev.equals("all"))
831
    {
832
      return false;
833
    }
834
    
835
    if(tablehasrows)
836
    {
837
      int r = rs.getInt(1);
838
      pstmt.close();
839
      if(new Integer(rev).intValue() == r)
840
      { //the current revision in in xml_documents
841
        //System.out.println("returning false");
842
        return false;
843
      }
844
      else if(new Integer(rev).intValue() < r)
845
      { //the current revision is in xml_revisions.
846
        //System.out.println("returning true");
847
        return true;
848
      }
849
      else if(new Integer(rev).intValue() > r)
850
      { //error, rev cannot be greater than r
851
        throw new Exception("requested revision cannot be greater than " +
852
                            "the latest revision number.");
853
      }
854
    }
855
    throw new Exception("the requested docid '" + docid.toString() + 
856
                        "' does not exist");
857
  }
858

    
859
  private void getDocumentInfo(String docid) throws McdbException, 
860
                                                    AccessionNumberException
861
  {
862
    getDocumentInfo(new DocumentIdentifier(docid));
863
  }
864
  
865
  /**
866
   * Look up the document type information from the database
867
   *
868
   * @param docid the id of the document to look up
869
   */
870
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException 
871
  {
872
    PreparedStatement pstmt;
873
    String table = "xml_documents";
874
    
875
    try
876
    {
877
      if(isRevisionOnly(docid))
878
      { //pull the document from xml_revisions instead of from xml_documents;
879
        table = "xml_revisions";
880
      }
881
    }
882
    catch(Exception e)
883
    {
884
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
885
                          e.getMessage());
886
    }
887
    
888
    //deal with the key words here.
889
    
890
    if(docid.getRev().equals("all"))
891
    {
892
      
893
    }
894
    
895
    try {
896
      StringBuffer sql = new StringBuffer();
897
// DOCTITLE attr cleared from the db
898
//      sql.append("SELECT docname, doctype, rootnodeid, doctitle, ");
899
      sql.append("SELECT docname, doctype, rootnodeid, ");
900
      sql.append("date_created, date_updated, user_owner, user_updated, ");
901
      sql.append("server_location, public_access, rev");
902
      sql.append(" FROM ").append(table);
903
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
904
      sql.append("' and rev like '").append(docid.getRev()).append("'");
905
      //System.out.println(sql.toString());
906
      pstmt =
907
        conn.prepareStatement(sql.toString());
908
      // Bind the values to the query
909
      //pstmt.setString(1, docid.getIdentifier());
910
      //pstmt.setString(2, docid.getRev());
911

    
912
      pstmt.execute();
913
      ResultSet rs = pstmt.getResultSet();
914
      boolean tableHasRows = rs.next();
915
      if (tableHasRows) {
916
        this.docname        = rs.getString(1);
917
        this.doctype        = rs.getString(2);
918
        this.rootnodeid     = rs.getLong(3);
919
// DOCTITLE attr cleared from the db
920
//        this.doctitle       = rs.getString(4);
921
        this.createdate     = rs.getString(4);
922
        this.updatedate     = rs.getString(5);
923
        this.userowner      = rs.getString(6);
924
        this.userupdated    = rs.getString(7);
925
        this.serverlocation = rs.getInt(8);
926
        this.publicaccess   = rs.getString(9);
927
        this.rev            = rs.getInt(10);
928
      } 
929
      pstmt.close();
930
      if (this.doctype != null) {
931
        pstmt =
932
          conn.prepareStatement("SELECT system_id " +
933
                                  "FROM xml_catalog " +
934
                                 "WHERE public_id = ?");
935
        // Bind the values to the query
936
        pstmt.setString(1, doctype);
937
  
938
        pstmt.execute();
939
        rs = pstmt.getResultSet();
940
        tableHasRows = rs.next();
941
        if (tableHasRows) {
942
          this.system_id  = rs.getString(1);
943
        } 
944
        pstmt.close();
945
      }
946
    } catch (SQLException e) {
947
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
948
                          e.getMessage());
949
      e.printStackTrace(System.out);
950
      throw new McdbException("Error accessing database connection in " +
951
                              "DocumentImpl.getDocumentInfo: ", e);
952
    }
953

    
954
    if (this.docname == null) {
955
      throw new McdbDocNotFoundException("Document not found: " + docid);
956
    }
957
  }
958

    
959
  /**
960
   * Look up the node data from the database
961
   *
962
   * @param rootnodeid the id of the root node of the node tree to look up
963
   */
964
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException 
965
  {
966
    PreparedStatement pstmt;
967
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
968
    long nodeid = 0;
969
    long parentnodeid = 0;
970
    long nodeindex = 0;
971
    String nodetype = null;
972
    String nodename = null;
973
    String nodeprefix = null;
974
    String nodedata = null;
975
    String quotechar = dbAdapter.getStringDelimiter();
976

    
977
    try {
978
      pstmt =
979
      conn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
980
           "nodetype,nodename,nodeprefix,nodedata " +               
981
           "FROM xml_nodes WHERE rootnodeid = ?");
982

    
983
      // Bind the values to the query
984
      pstmt.setLong(1, rootnodeid);
985

    
986
      pstmt.execute();
987
      ResultSet rs = pstmt.getResultSet();
988
      boolean tableHasRows = rs.next();
989
      while (tableHasRows) {
990
        nodeid = rs.getLong(1);
991
        parentnodeid = rs.getLong(2);
992
        nodeindex = rs.getLong(3);
993
        nodetype = rs.getString(4);
994
        nodename = rs.getString(5);
995
        nodeprefix = rs.getString(6);
996
        nodedata = rs.getString(7);
997
        nodedata = MetaCatUtil.normalize(nodedata);
998
        // add the data to the node record list hashtable
999
        NodeRecord currentRecord = new NodeRecord(nodeid,parentnodeid,nodeindex,
1000
                                      nodetype, nodename, nodeprefix, nodedata);
1001
        nodeRecordList.add(currentRecord);
1002

    
1003
        // Advance to the next node
1004
        tableHasRows = rs.next();
1005
      } 
1006
      pstmt.close();
1007

    
1008
    } catch (SQLException e) {
1009
      throw new McdbException("Error in DocumentImpl.getNodeRecordList " +
1010
                              e.getMessage());
1011
    }
1012

    
1013
    if (nodeRecordList != null) {
1014
      return nodeRecordList;
1015
    } else {
1016
      throw new McdbException("Error getting node data: " + docid);
1017
    }
1018
  }
1019
  
1020
// NOT USED ANY MORE
1021
//  /** creates SQL code and inserts new document into DB connection 
1022
//   default serverCode of 1*/
1023
//  private void writeDocumentToDB(String action, String user)
1024
//               throws SQLException, Exception
1025
//  {
1026
//    writeDocumentToDB(action, user, null, 1);
1027
//  }
1028

    
1029
 /** creates SQL code and inserts new document into DB connection */
1030
  private void writeDocumentToDB(String action, String user, String pub, 
1031
                                 String catalogid, int serverCode) 
1032
               throws SQLException, Exception {
1033
    String sysdate = dbAdapter.getDateTimeFunction();
1034

    
1035
    try {
1036
      PreparedStatement pstmt = null;
1037

    
1038
      if (action.equals("INSERT")) {
1039
        //AccessionNumber ac = new AccessionNumber();
1040
        //this.docid = ac.generate(docid, "INSERT");
1041
        pstmt = conn.prepareStatement(
1042
                "INSERT INTO xml_documents " +
1043
                "(docid, rootnodeid, docname, doctype, " + 
1044
                "user_owner, user_updated, date_created, date_updated, " + 
1045
                "public_access, catalog_id, server_location) " +
1046
                "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", " + sysdate + 
1047
                ", ?, ?, ?)");
1048
        //note that the server_location is set to 1. 
1049
        //this means that "localhost" in the xml_replication table must
1050
        //always be the first entry!!!!!
1051
        
1052
        // Bind the values to the query
1053
        pstmt.setString(1, this.docid);
1054
        pstmt.setLong(2, rootnodeid);
1055
        pstmt.setString(3, docname);
1056
        pstmt.setString(4, doctype);
1057
        pstmt.setString(5, user);
1058
        pstmt.setString(6, user);
1059
        if ( pub == null ) {
1060
          pstmt.setString(7, null);
1061
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1062
          pstmt.setInt(7, 1);
1063
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1064
          pstmt.setInt(7, 0);
1065
        }
1066
        pstmt.setString(8, catalogid);
1067
        pstmt.setInt(9, serverCode);
1068
      } else if (action.equals("UPDATE")) {
1069

    
1070
        // Save the old document entry in a backup table
1071
        DocumentImpl.archiveDocRevision( conn, docid, user );
1072
        DocumentImpl thisdoc = new DocumentImpl(conn, docid);
1073
        int thisrev = thisdoc.getRev();
1074
        
1075
        //if the updated vesion is not greater than current one,
1076
        //throw it into a exception
1077
        if (Integer.parseInt(updatedVersion)<=thisrev)
1078
        {
1079
          throw new Exception("Next revision number couldn't be less"
1080
                               +" than or equal "+ thisrev);
1081
        }
1082
        else
1083
        {
1084
          //set the user specified revision 
1085
          thisrev=Integer.parseInt(updatedVersion);
1086
        }
1087
        
1088
        // Delete index for the old version of docid
1089
        // The new index is inserting on the next calls to DBSAXNode
1090
        pstmt = conn.prepareStatement(
1091
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
1092
        pstmt.execute();
1093
        pstmt.close();
1094

    
1095
        // Update the new document to reflect the new node tree
1096
        pstmt = conn.prepareStatement(
1097
            "UPDATE xml_documents " +
1098
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
1099
            "user_updated = ?, date_updated = " + sysdate + ", " +
1100
            "server_location = ?, rev = ?, public_access = ?, catalog_id = ? " +
1101
            "WHERE docid = ?");
1102
        // Bind the values to the query
1103
        pstmt.setLong(1, rootnodeid);
1104
        pstmt.setString(2, docname);
1105
        pstmt.setString(3, doctype);
1106
        pstmt.setString(4, user);
1107
        pstmt.setInt(5, serverCode);
1108
        pstmt.setInt(6, thisrev);
1109
        if ( pub == null ) {
1110
          pstmt.setString(7, null);
1111
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1112
          pstmt .setInt(7, 1);
1113
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1114
          pstmt.setInt(7, 0);
1115
        }
1116
        pstmt.setString(8, catalogid);
1117
        pstmt.setString(9, this.docid);
1118

    
1119
      } else {
1120
        System.err.println("Action not supported: " + action);
1121
      }
1122

    
1123
      // Do the insertion
1124
      pstmt.execute();
1125
      pstmt.close();
1126

    
1127
    } catch (SQLException sqle) {
1128
      throw sqle;
1129
    } catch (Exception e) {
1130
      throw e;
1131
    }
1132
  }
1133

    
1134
  /**
1135
   * Write an XML file to the database, given a filename
1136
   *
1137
   * @param conn the JDBC connection to the database
1138
   * @param filename the filename to be loaded into the database
1139
   * @param pub flag for public "read" access on document
1140
   * @param dtdfilename the dtd to be uploaded on server's file system
1141
   * @param action the action to be performed (INSERT OR UPDATE)
1142
   * @param docid the docid to use for the INSERT OR UPDATE
1143
   * @param user the user that owns the document
1144
   * @param groups the groups to which user belongs
1145
   */
1146
  public static String write(Connection conn,String filename,
1147
                             String pub, String dtdfilename,
1148
                             String action, String docid, String user,
1149
                             String[] groups )
1150
                throws Exception {
1151
                  
1152
    Reader dtd = null;
1153
    if ( dtdfilename != null ) {
1154
      dtd = new FileReader(new File(dtdfilename).toString());
1155
    }
1156
    return write ( conn, new FileReader(new File(filename).toString()),
1157
                   pub, dtd, action, docid, user, groups, false);
1158
  }
1159

    
1160
  public static String write(Connection conn,Reader xml,String pub,Reader dtd,
1161
                             String action, String docid, String user,
1162
                             String[] groups, boolean validate)
1163
                throws Exception {
1164
    //this method will be called in handleUpdateOrInsert method 
1165
    //in MetacatServlet class
1166
    // get server location for this doc
1167
    int serverLocation=getServerLocationNumber(conn,docid);
1168
    //System.out.println("server location: "+serverLocation);
1169
    return write(conn,xml,pub,dtd,action,docid,user,groups,serverLocation,false,
1170
                 validate);
1171
  }
1172

    
1173
  public static String write(Connection conn, Reader xml, String pub,
1174
                             String action, String docid, String user,
1175
                             String[] groups )
1176
                throws Exception {
1177
    if(action.equals("UPDATE"))
1178
    {//if the document is being updated then use the servercode from the 
1179
     //originally inserted document.
1180
      DocumentImpl doc = new DocumentImpl(conn, docid);
1181
      int servercode = doc.getServerlocation();
1182
      return write(conn, xml, pub, action, docid, user, groups, servercode);
1183
    }
1184
    else
1185
    {//if the file is being inserted then the servercode is always 1
1186
      return write(conn, xml, pub, action, docid, user, groups, 1);
1187
    }
1188
  }
1189
  
1190
  public static String write( Connection conn, Reader xml,
1191
                              String action, String docid, String user,
1192
                              String[] groups, int serverCode )
1193
                throws Exception
1194
  {
1195
    return write(conn,xml,null,action,docid,user,groups,serverCode);
1196
  }
1197
  
1198
  public static String write( Connection conn, Reader xml, String pub,
1199
                              String action, String docid, String user,
1200
                              String[] groups, int serverCode) 
1201
                throws Exception
1202
  {
1203
    return write(conn,xml,pub,null,action,docid,user,groups,
1204
                 serverCode,false,false);
1205
  }
1206
  
1207
  public static String write( Connection conn, Reader xml, String pub,
1208
                              String action, String docid, String user,
1209
                              String[] groups, int serverCode, boolean override)
1210
                throws Exception
1211
  {
1212
    return write(conn,xml,pub,null,action,docid,user,groups,
1213
                 serverCode,override,false);
1214
  }
1215
  
1216
  /**
1217
   * Write an XML file to the database, given a Reader
1218
   *
1219
   * @param conn the JDBC connection to the database
1220
   * @param xml the xml stream to be loaded into the database
1221
   * @param pub flag for public "read" access on xml document
1222
   * @param dtd the dtd to be uploaded on server's file system
1223
   * @param action the action to be performed (INSERT or UPDATE)
1224
   * @param accnum the docid + rev# to use on INSERT or UPDATE
1225
   * @param user the user that owns the document
1226
   * @param groups the groups to which user belongs
1227
   * @param serverCode the serverid from xml_replication on which this document
1228
   *        resides.
1229
   * @param override flag to stop insert replication checking.
1230
   *        if override = true then a document not belonging to the local server
1231
   *        will not be checked upon update for a file lock.
1232
   *        if override = false then a document not from this server, upon 
1233
   *        update will be locked and version checked.
1234
   */
1235

    
1236
  public static String write( Connection conn,Reader xml,String pub,Reader dtd,
1237
                              String action, String accnum, String user,
1238
                              String[] groups, int serverCode, boolean override,
1239
                              boolean validate)
1240
                throws Exception
1241
  {
1242
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1243
    MetaCatUtil util = new MetaCatUtil();
1244
    AccessionNumber ac = new AccessionNumber(conn, accnum, action);
1245
    String docid = ac.getDocid();
1246
    String rev = ac.getRev();
1247
    MetaCatUtil.debugMessage("action: " + action + " servercode: " + 
1248
                             serverCode + " override: " + override);
1249
                        
1250
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
1251
    { //if this document being written is not a resident of this server then
1252
      //we need to try to get a lock from it's resident server.  If the
1253
      //resident server will not give a lock then we send the user a message
1254
      //saying that he/she needs to download a new copy of the file and
1255
      //merge the differences manually.
1256
      int istreamInt; 
1257
      char istreamChar;
1258
      DocumentIdentifier id = new DocumentIdentifier(accnum);
1259
      String updaterev = id.getRev();
1260
      String server = MetacatReplication.getServer(serverCode);
1261
      MetacatReplication.replLog("attempting to lock " + accnum);
1262
      URL u = new URL("https://" + server + "?server="
1263
           +util.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
1264
           +updaterev + "&docid=" + docid);
1265
      //System.out.println("sending message: " + u.toString());
1266
      String serverResStr = MetacatReplication.getURLContent(u);
1267
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
1268
      if(openingtag.equals("<lockgranted>"))
1269
      {//the lock was granted go ahead with the insert
1270
        try 
1271
        {
1272
          //System.out.println("In lockgranted");
1273
          MetacatReplication.replLog("lock granted for " + accnum + " from " +
1274
                                      server);
1275
          XMLReader parser = initializeParser(conn, action, docid, updaterev,
1276
                                  validate, user, groups, pub, serverCode, dtd);
1277
          conn.setAutoCommit(false);
1278
          parser.parse(new InputSource(xml)); 
1279
          conn.commit();
1280
          conn.setAutoCommit(true);
1281
        } 
1282
        catch (Exception e) 
1283
        {
1284
          conn.rollback();
1285
          conn.setAutoCommit(true);
1286
          throw e;
1287
        }
1288
                
1289
        //after inserting the document locally, tell the document's home server
1290
        //to come get a copy from here.
1291
        //ture mean it is xml document
1292
        ForceReplicationHandler frh = new ForceReplicationHandler(accnum, true);
1293
        
1294
        return (accnum);
1295
      }
1296

    
1297
      else if(openingtag.equals("<filelocked>"))
1298
      {//the file is currently locked by another user
1299
       //notify our user to wait a few minutes, check out a new copy and try
1300
       //again.
1301
        //System.out.println("file locked");
1302
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1303
                                   server + " reason: file already locked");
1304
        throw new Exception("The file specified is already locked by another " +
1305
                            "user.  Please wait 30 seconds, checkout the " +
1306
                            "newer document, merge your changes and try " +
1307
                            "again.");
1308
      }
1309
      else if(openingtag.equals("<outdatedfile>"))
1310
      {//our file is outdated.  notify our user to check out a new copy of the
1311
       //file and merge his version with the new version.
1312
        //System.out.println("outdated file");
1313
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1314
                                    server + " reason: local file outdated");
1315
        throw new Exception("The file you are trying to update is an outdated" +
1316
                            " version.  Please checkout the newest document, " +
1317
                            "merge your changes and try again.");
1318
      }
1319
    }
1320
    
1321
    if ( action.equals("UPDATE") ) {
1322
      // check for 'write' permission for 'user' to update this document
1323

    
1324
      if ( !hasPermission(conn, user, groups, docid) ) {
1325
        throw new Exception("User " + user + 
1326
              " does not have permission to update XML Document #" + accnum);
1327
      }          
1328
    }
1329

    
1330
    try 
1331
    { 
1332
      
1333
      XMLReader parser = initializeParser(conn, action, docid, rev, validate,
1334
                                          user, groups, pub, serverCode, dtd);
1335
      conn.setAutoCommit(false);
1336
      parser.parse(new InputSource(xml));
1337
      conn.commit();
1338
      conn.setAutoCommit(true);
1339
    } 
1340
    catch (Exception e) 
1341
    {
1342
      conn.rollback();
1343
      conn.setAutoCommit(true);
1344
      throw e;
1345
    }
1346
    
1347
    //force replicate out the new document to each server in our server list.
1348
    if(serverCode == 1)
1349
    { 
1350
      
1351
      //start the thread to replicate this new document out to the other servers
1352
      //true mean it is xml document
1353
      ForceReplicationHandler frh = new ForceReplicationHandler
1354
                                                         (accnum, action, true);
1355
      
1356
    }
1357
      
1358
    return(accnum);
1359
  }
1360

    
1361
  /**
1362
   * Delete an XML file from the database (actually, just make it a revision
1363
   * in the xml_revisions table)
1364
   *
1365
   * @param docid the ID of the document to be deleted from the database
1366
   */
1367
  public static void delete( Connection conn, String accnum,
1368
                                 String user, String[] groups )
1369
                throws Exception 
1370
  {
1371
    // OLD
1372
    //DocumentIdentifier id = new DocumentIdentifier(accnum);
1373
    //String docid = id.getIdentifier();
1374
    //String rev = id.getRev();
1375
    
1376
    // OLD
1377
    // Determine if the docid,rev are OK for DELETE
1378
    //AccessionNumber ac = new AccessionNumber(conn);
1379
    //docid = ac.generate(docid, rev, "DELETE");
1380

    
1381
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1382
    AccessionNumber ac = new AccessionNumber(conn, accnum, "DELETE");
1383
    String docid = ac.getDocid();
1384
    String rev = ac.getRev();
1385
    
1386

    
1387
    // check for 'write' permission for 'user' to delete this document
1388
    if ( !hasPermission(conn, user, groups, docid) ) {
1389
      throw new Exception("User " + user + 
1390
              " does not have permission to delete XML Document #" + accnum);
1391
    }
1392

    
1393
    conn.setAutoCommit(false);
1394
    // Copy the record to the xml_revisions table
1395
    DocumentImpl.archiveDocRevision( conn, docid, user );
1396

    
1397
    // Now delete it from the xml_documents table
1398
    
1399
    Statement stmt = conn.createStatement();
1400
    stmt.execute("DELETE FROM xml_index WHERE docid = '" + docid + "'");
1401
    //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
1402
    stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + docid + "'");
1403
    stmt.execute("DELETE FROM xml_relation WHERE docid = '" + docid + "'");
1404
    stmt.execute("DELETE FROM xml_documents WHERE docid = '" + docid + "'");
1405
    stmt.close();
1406
    conn.commit();
1407
    conn.setAutoCommit(true);
1408
    //IF this is a package document:
1409
    //delete all of the relations that this document created.
1410
    //if the deleted document is a package document its relations should 
1411
    //no longer be active if it has been deleted from the system.
1412
    
1413
  }
1414

    
1415
  /** 
1416
    * Check for "WRITE" permission on @docid for @user and/or @groups 
1417
    * from DB connection 
1418
    */
1419
  private static boolean hasPermission ( Connection conn, String user,
1420
                                  String[] groups, String docid ) 
1421
                  throws SQLException, Exception
1422
  {
1423
    // Check for WRITE permission on @docid for @user and/or @groups
1424
    AccessControlList aclobj = new AccessControlList(conn);
1425
    return aclobj.hasPermission("WRITE", user, groups, docid);
1426
  }
1427

    
1428
  /** 
1429
    * Check for "READ" permission base on docid, user and group
1430
    *@docid, the document
1431
    *@user, user name
1432
    *@group, user's group
1433
    * 
1434
    */
1435
  public boolean hasReadPermission ( Connection conn, String user,
1436
                                  String[] groups, String docId ) 
1437
                  throws SQLException, Exception
1438
  {
1439
    // Check for READ permission on @docid for @user and/or @groups
1440
    AccessControlList aclObj = new AccessControlList(conn);
1441
    return aclObj.hasPermission("READ", user, groups, docId);
1442
  }  
1443

    
1444
  /**
1445
   * Set up the parser handlers for writing the document to the database
1446
   */
1447
  private static XMLReader initializeParser(Connection conn, String action,
1448
                               String docid, String rev, boolean validate, 
1449
                                   String user, String[] groups, String pub, 
1450
                                   int serverCode, Reader dtd) 
1451
                           throws Exception 
1452
  {
1453
    XMLReader parser = null;
1454
    //
1455
    // Set up the SAX document handlers for parsing
1456
    //
1457
    try {
1458
      //create a DBSAXHandler object which has the revision specification
1459
      ContentHandler chandler = new DBSAXHandler(conn, action, docid, rev,
1460
                                                 user, groups, pub, serverCode);
1461
      EntityResolver eresolver= new DBEntityResolver(conn,
1462
                                                 (DBSAXHandler)chandler, dtd);
1463
      DTDHandler dtdhandler   = new DBDTDHandler(conn);
1464

    
1465
      // Get an instance of the parser
1466
      String parserName = MetaCatUtil.getOption("saxparser");
1467
      parser = XMLReaderFactory.createXMLReader(parserName);
1468

    
1469
      // Turn on validation
1470
      parser.setFeature("http://xml.org/sax/features/validation", validate);
1471
      // Turn off Including all external parameter entities
1472
      // (the external DTD subset also)
1473
      // Doesn't work well, probably the feature name is not correct
1474
      // parser.setFeature(
1475
      //  "http://xml.org/sax/features/external-parameter-entities", false);
1476
      
1477
      // Set Handlers in the parser
1478
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
1479
                         chandler);
1480
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
1481
                         chandler);
1482
      parser.setContentHandler((ContentHandler)chandler);
1483
      parser.setEntityResolver((EntityResolver)eresolver);
1484
      parser.setDTDHandler((DTDHandler)dtdhandler);
1485
      parser.setErrorHandler((ErrorHandler)chandler);
1486

    
1487
    } catch (Exception e) {
1488
      throw e;
1489
    }
1490

    
1491
    return parser;
1492
  }
1493

    
1494
  /** Save a document entry in the xml_revisions table */
1495
  private static void archiveDocRevision(Connection conn, String docid,
1496
                                         String user) 
1497
                                         throws SQLException {
1498
    String sysdate = dbAdapter.getDateTimeFunction();
1499
    // create a record in xml_revisions table 
1500
    // for that document as selected from xml_documents
1501
    PreparedStatement pstmt = conn.prepareStatement(
1502
      "INSERT INTO xml_revisions " +
1503
        "(docid, rootnodeid, docname, doctype, " +
1504
        "user_owner, user_updated, date_created, date_updated, " +
1505
        "server_location, rev, public_access, catalog_id) " +
1506
      "SELECT ?, rootnodeid, docname, doctype, " + 
1507
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
1508
        "server_location, rev, public_access, catalog_id " +
1509
      "FROM xml_documents " +
1510
      "WHERE docid = ?");
1511
    // Bind the values to the query and execute it
1512
    pstmt.setString(1, docid);
1513
    pstmt.setString(2, user);
1514
    pstmt.setString(3, docid);
1515
    pstmt.execute();
1516
    pstmt.close();
1517

    
1518
  }
1519
  
1520
  /**
1521
    * delete a entry in xml_table for given docid
1522
    * @param docId, the id of the document need to be delete
1523
    */
1524
  private static void deleteXMLDocuments(Connection conn, String docId) 
1525
                                         throws SQLException 
1526
  {
1527
    //delete a record 
1528
    PreparedStatement pStmt = 
1529
             conn.prepareStatement("DELETE FROM xml_documents WHERE docid = '" 
1530
                                              + docId + "'");
1531
    pStmt.execute();
1532
    pStmt.close();
1533

    
1534
  }//deleteXMLDocuments
1535
  
1536
  /**
1537
    * Get last revision number from database for a docid
1538
    * If couldn't find an entry,  -1 will return
1539
    * The return value is integer because we want compare it to there new one
1540
    * @param docid <sitecode>.<uniqueid> part of Accession Number
1541
    */
1542
  private static int getLatestRevisionNumber(Connection conn, String docId)
1543
                                      throws SQLException
1544
  {
1545
    int rev = 1;
1546
    PreparedStatement pStmt;
1547
     
1548
    pStmt = conn.prepareStatement
1549
              ("SELECT rev FROM xml_documents WHERE docid='" + docId + "'");
1550
    pStmt.execute();
1551

    
1552
    ResultSet rs = pStmt.getResultSet();
1553
    boolean hasRow = rs.next();
1554
    if (hasRow)
1555
    {
1556
      rev = rs.getInt(1);
1557
      pStmt.close();
1558
    }
1559
    else
1560
    {
1561
      rev=-1;
1562
      pStmt.close();
1563
    }
1564
      
1565
    return rev;
1566
  }//getLatestRevisionNumber
1567
  
1568
  /**
1569
   * Get server location form database for a accNum
1570
   * 
1571
   * @param accum <sitecode>.<uniqueid>.<rev>
1572
   */
1573
  private static int getServerLocationNumber(Connection conn, String accNum)
1574
                                            throws SQLException
1575
  {
1576
    //get rid of revNum part
1577
    String docId=MetaCatUtil.getDocIdFromString(accNum);
1578
    PreparedStatement pStmt;
1579
    int serverLocation;
1580
     
1581
    pStmt = conn.prepareStatement
1582
      ("SELECT server_location FROM xml_documents WHERE docid='" + docId + "'");
1583
    pStmt.execute();
1584

    
1585
    ResultSet rs = pStmt.getResultSet();
1586
    boolean hasRow = rs.next();
1587
    //if there is entry in xml_documents, get the serverlocation
1588
    if (hasRow)
1589
    {
1590
      serverLocation = rs.getInt(1);
1591
      pStmt.close();
1592
    }
1593
    else
1594
    {
1595
      // if htere is no entry in xml_documents, we consider it is new document
1596
      // the server location is local host and value is 1
1597
      serverLocation=1;
1598
      pStmt.close();
1599
    }
1600
      
1601
    return serverLocation;
1602
  }
1603
  /**
1604
   * the main routine used to test the DBWriter utility.
1605
   * <p>
1606
   * Usage: java DocumentImpl <-f filename -a action -d docid>
1607
   *
1608
   * @param filename the filename to be loaded into the database
1609
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
1610
   * @param docid the id of the document to process
1611
   */
1612
  static public void main(String[] args) {
1613
     
1614
    try {
1615
      String filename    = null;
1616
      String dtdfilename = null;
1617
      String action      = null;
1618
      String docid       = null;
1619
      boolean showRuntime = false;
1620
      boolean useOldReadAlgorithm = false;
1621

    
1622
      // Parse the command line arguments
1623
      for ( int i=0 ; i < args.length; ++i ) {
1624
        if ( args[i].equals( "-f" ) ) {
1625
          filename =  args[++i];
1626
        } else if ( args[i].equals( "-r" ) ) {
1627
          dtdfilename =  args[++i];
1628
        } else if ( args[i].equals( "-a" ) ) {
1629
          action =  args[++i];
1630
        } else if ( args[i].equals( "-d" ) ) {
1631
          docid =  args[++i];
1632
        } else if ( args[i].equals( "-t" ) ) {
1633
          showRuntime = true;
1634
        } else if ( args[i].equals( "-old" ) ) {
1635
          useOldReadAlgorithm = true;
1636
        } else {
1637
          System.err.println
1638
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
1639
        }
1640
      }
1641
      
1642
      // Check if the required arguments are provided
1643
      boolean argsAreValid = false;
1644
      if (action != null) {
1645
        if (action.equals("INSERT")) {
1646
          if (filename != null) {
1647
            argsAreValid = true;
1648
          } 
1649
        } else if (action.equals("UPDATE")) {
1650
          if ((filename != null) && (docid != null)) {
1651
            argsAreValid = true;
1652
          } 
1653
        } else if (action.equals("DELETE")) {
1654
          if (docid != null) {
1655
            argsAreValid = true;
1656
          } 
1657
        } else if (action.equals("READ")) {
1658
          if (docid != null) {
1659
            argsAreValid = true;
1660
          } 
1661
        } 
1662
      } 
1663

    
1664
      // Print usage message if the arguments are not valid
1665
      if (!argsAreValid) {
1666
        System.err.println("Wrong number of arguments!!!");
1667
        System.err.println(
1668
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
1669
          "[-r dtdfilename]");
1670
        System.err.println(
1671
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
1672
          "[-r dtdfilename]");
1673
        System.err.println(
1674
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
1675
        System.err.println(
1676
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
1677
        return;
1678
      }
1679
      
1680
      // Time the request if asked for
1681
      double startTime = System.currentTimeMillis();
1682
      
1683
      // Open a connection to the database
1684
      MetaCatUtil util = new MetaCatUtil();
1685
      Connection dbconn = util.openDBConnection();
1686

    
1687
      double connTime = System.currentTimeMillis();
1688
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
1689
      if (action.equals("READ")) {
1690
          DocumentImpl xmldoc = new DocumentImpl( dbconn, docid );
1691
          if (useOldReadAlgorithm) {
1692
            System.out.println(xmldoc.readUsingSlowAlgorithm());
1693
          } else {
1694
            xmldoc.toXml(new PrintWriter(System.out));
1695
          }
1696
      } else if (action.equals("DELETE")) {
1697
        DocumentImpl.delete(dbconn, docid, null, null);
1698
        System.out.println("Document deleted: " + docid);
1699
      } else {
1700
        String newdocid = DocumentImpl.write(dbconn, filename, null,
1701
                                             dtdfilename, action, docid,
1702
                                             null, null);
1703
        if ((docid != null) && (!docid.equals(newdocid))) {
1704
          if (action.equals("INSERT")) {
1705
            System.out.println("New document ID generated!!! ");
1706
          } else if (action.equals("UPDATE")) {
1707
            System.out.println("ERROR: Couldn't update document!!! ");
1708
          }
1709
        } else if ((docid == null) && (action.equals("UPDATE"))) {
1710
          System.out.println("ERROR: Couldn't update document!!! ");
1711
        }
1712
        System.out.println("Document processing finished for: " + filename
1713
              + " (" + newdocid + ")");
1714
      }
1715

    
1716
      double stopTime = System.currentTimeMillis();
1717
      double dbOpenTime = (connTime - startTime)/1000;
1718
      double insertTime = (stopTime - connTime)/1000;
1719
      double executionTime = (stopTime - startTime)/1000;
1720
      if (showRuntime) {
1721
        System.out.println("\n\nTotal Execution time was: " + 
1722
                           executionTime + " seconds.");
1723
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
1724
                           " seconds.");
1725
        System.out.println("Time to insert document was: " + insertTime +
1726
                           " seconds.");
1727
      }
1728
      dbconn.close();
1729
    } catch (McdbException me) {
1730
      me.toXml(new PrintWriter(System.err));
1731
    } catch (AccessionNumberException ane) {
1732
      System.out.println(ane.getMessage());
1733
    } catch (Exception e) {
1734
      System.err.println("EXCEPTION HANDLING REQUIRED");
1735
      System.err.println(e.getMessage());
1736
      e.printStackTrace(System.err);
1737
    }
1738
  }
1739
}
(24-24/41)