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: 2003-08-14 16:41:43 -0700 (Thu, 14 Aug 2003) $'
11
 * '$Revision: 1804 $'
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.Hashtable;
44
import java.util.Iterator;
45
import java.util.Stack;
46
import java.util.TreeSet;
47
import java.util.Vector;
48
import java.util.Enumeration;
49

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

    
61
import java.net.URL;
62

    
63
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
64

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

    
72
   /* Constants */
73
   public static final String SCHEMA                 = "Schema";
74
   public static final String DTD                    = "DTD";
75
   public static final String EML2                   = "eml2";
76
   public static final String EXTERNALSCHEMALOCATIONPROPERTY = 
77
              "http://apache.org/xml/properties/schema/external-schemaLocation";
78
   /*public static final String EXTERNALSCHEMALOCATION = 
79
     "eml://ecoinformatics.org/eml-2.0.0 http://dev.nceas.ucsb.edu/tao/schema/eml.xsd"+
80
      " http://www.xml-cml.org/schema/stmml http://dev.nceas.ucsb.edu/tao/schema/stmml.xsd";*/
81
   public static final String DECLARATIONHANDLERPROPERTY =
82
                            "http://xml.org/sax/properties/declaration-handler";
83
   public static final String LEXICALPROPERTY =
84
                             "http://xml.org/sax/properties/lexical-handler";
85
   public static final String VALIDATIONFEATURE = 
86
                             "http://xml.org/sax/features/validation";
87
   public static final String SCHEMAVALIDATIONFEATURE = 
88
                             "http://apache.org/xml/features/validation/schema";
89
   public static final String NAMESPACEFEATURE = 
90
                              "http://xml.org/sax/features/namespaces";
91
   public static final String NAMESPACEPREFIXESFEATURE = 
92
                              "http://xml.org/sax/features/namespace-prefixes";
93
   public static final String EMLNAMESPACE =
94
                                         MetaCatUtil.getOption("eml2namespace"); 
95
                                         // "eml://ecoinformatics.org/eml-2.0.0";
96
  
97
  public static final String DOCNAME = "docname";
98
  public static final String PUBLICID = "publicid";
99
  public static final String SYSTEMID = "systemid";
100
  static final int ALL = 1;
101
  static final int WRITE = 2;
102
  static final int READ = 4;
103
  private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
104

    
105
  private DBConnection connection = null;
106
  private String docid = null;
107
  private String updatedVersion=null;
108
  private String docname = null;
109
  private String doctype = null;
110
  private String validateType = null; //base on dtd or schema
111
// DOCTITLE attr cleared from the db
112
//  private String doctitle = null;
113
  private String createdate = null;
114
  private String updatedate = null;
115
  private String system_id = null;
116
  private String userowner = null;
117
  private String userupdated = null;
118
  private int rev;
119
  private int serverlocation;
120
  private String docHomeServer;
121
  private String publicaccess; 
122
  private long rootnodeid;
123
  private ElementNode rootNode = null;
124
  private TreeSet nodeRecordList = null;
125
  //private static 
126
  //ReplicationServerList serverList = new ReplicationServerList();
127
  
128
  /**
129
   * Constructor used to create a document and read the document information
130
   * from the database.  If readNodes is false, then the node data is not 
131
   * read at this time, but is deferred until it is needed (such as when a 
132
   * call to toXml() is made).  
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
   * @param readNodes flag indicating whether the xmlnodes should be read
137
   */
138
  public DocumentImpl(String docid, boolean readNodes) 
139
         throws McdbException 
140
  {
141
    try {
142
      //this.conn = conn;
143
      this.docid = docid;
144
      
145
      // Look up the document information
146
      getDocumentInfo(docid);
147
      
148
      if (readNodes) {
149
        // Download all of the document nodes using a single SQL query
150
        // The sort order of the records is determined by the NodeComparator
151
        // class, and needs to represent a depth-first traversal for the
152
        // toXml() method to work properly
153
        nodeRecordList = getNodeRecordList(rootnodeid);
154
        
155
      }
156
    
157
    } catch (McdbException ex) {
158
      throw ex;
159
    } catch (Throwable t) {
160
      throw new McdbException("Error reading document from " +
161
                              "DocumentImpl.DocumentImpl: " + docid);
162
    }
163
  }
164

    
165
  /**
166
   * Constructor, creates document from database connection, used 
167
   * for reading the document
168
   *
169
   * @param conn the database connection from which to read the document
170
   * @param docid the identifier of the document to be created
171
   */
172
  public DocumentImpl(String docid) throws McdbException 
173
  {
174
    this(docid, true);
175
  }
176

    
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(DBConnection 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.connection = 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
    
231
    try
232
    {
233
       // get server location for this doc
234
      int serverLocation=getServerLocationNumber(accnum);
235
      registerDocument(docname, doctype,accnum, user, serverLocation);
236
    }
237
    catch (Exception e)
238
    {
239
      throw e;
240
    }
241
   
242
    
243
  }
244
  /**
245
   * Register a document that resides on the filesystem with the database.
246
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
247
   * Creates a reference to a filesystem document (used for non-xml data files).
248
   * This class only be called in MetaCatServerlet.
249
   * @param conn the JDBC Connection to which all information is written
250
   * @param docname - the name of DTD, i.e. the name immediately following 
251
   *        the DOCTYPE keyword ( should be the root element name ) or
252
   *        the root element name if no DOCTYPE declaration provided
253
   *        (Oracle's and IBM parsers are not aware if it is not the 
254
   *        root element name)
255
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
256
   *                  following the PUBLIC keyword in DOCTYPE declaration or
257
   *                  the docname if no Public ID provided or
258
   *                  null if no DOCTYPE declaration provided
259
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
260
   *               includes a revision number for this revision of the document 
261
   *               (e.g., knb.1.1)
262
   * @param user the user that owns the document
263
   * @param serverCode the serverid from xml_replication on which this document
264
   *        resides.
265
   */
266
  public static void registerDocument(
267
                     String docname, String doctype, String accnum, 
268
                     String user, int serverCode)
269
                     throws SQLException, AccessionNumberException, Exception
270
  {
271
    DBConnection dbconn = null;
272
    int serialNumber = -1;
273
    PreparedStatement pstmt = null;
274
    //MetaCatUtil util = new MetaCatUtil();
275
    AccessionNumber ac;
276
    String action = null;
277
    try {
278
      //dbconn = util.openDBConnection();
279
      //check out DBConnection
280
      dbconn=DBConnectionPool.
281
                    getDBConnection("DocumentImpl.registerDocument");
282
      serialNumber=dbconn.getCheckOutSerialNumber();
283
      String docIdWithoutRev=MetaCatUtil.getDocIdFromString(accnum);
284
      int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
285
      int revInDataBase=getLatestRevisionNumber(docIdWithoutRev);
286
      //revIndataBase=-1, there is no record in xml_documents table
287
      //the data file is a new one, inert it into table
288
      //user specified rev should be great than 0
289
      if (revInDataBase==-1 && userSpecifyRev>0 )
290
      {
291
        ac = new AccessionNumber(accnum, "insert");
292
        action = "insert";
293
      }
294
      //rev is greater the last revsion number and revInDataBase isn't -1
295
      // it is a updated data file
296
      else if (userSpecifyRev>revInDataBase && revInDataBase>0)
297
      {
298
        
299
        //archive the old entry 
300
        archiveDocRevision(docIdWithoutRev, user);
301
        //delete the old entry in xml_documents
302
        //deleteXMLDocuments(docIdWithoutRev);
303
        ac = new AccessionNumber(accnum, "update");
304
        action = "update";
305
      }
306
      //other situation
307
      else
308
      {
309
        
310
        throw new Exception("Revision number couldn't be "
311
                    +userSpecifyRev);
312
      }
313
      String docid = ac.getDocid();
314
      String rev = ac.getRev();
315
      /*SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy HH:mm:ss");
316
      Date localtime = new Date();
317
      String dateString = formatter.format(localtime);
318
      String sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY HH24:MI:SS");*/
319
      String  sqlDateString = dbAdapter.getDateTimeFunction();
320
  
321
      StringBuffer sql = new StringBuffer();
322
      if (action != null && action.equals("insert"))
323
      {
324
        sql.append("insert into xml_documents (docid, docname, doctype, ");
325
        sql.append("user_owner, user_updated, server_location, rev,date_created");
326
        sql.append(", date_updated, public_access) values ('");
327
        sql.append(docid).append("','");
328
        sql.append(docname).append("','");
329
        sql.append(doctype).append("','");
330
        sql.append(user).append("','");
331
        sql.append(user).append("','");
332
        sql.append(serverCode).append("','");
333
        sql.append(rev).append("',");
334
        sql.append(sqlDateString).append(",");
335
        sql.append(sqlDateString).append(",");
336
        sql.append("'0')");
337
      }
338
      else if (action != null && action.equals("update"))
339
      {
340
        sql.append("update xml_documents set docname ='");
341
        sql.append(docname).append("', ");
342
        sql.append("user_updated='");
343
        sql.append(user).append("', ");
344
        sql.append("server_location='");
345
        sql.append(serverCode).append("',");
346
        sql.append("rev='");
347
        sql.append(rev).append("',");
348
        sql.append("date_updated=");
349
        sql.append(sqlDateString);
350
        sql.append(" where docid='");
351
        sql.append(docid).append("'");
352
      }
353
      pstmt = dbconn.prepareStatement(sql.toString());
354
      pstmt.execute();
355
      pstmt.close();
356
      //dbconn.close();
357
    } 
358
    finally 
359
    {
360
      try
361
      {
362
        pstmt.close();
363
      }
364
      finally
365
      {
366
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
367
      }
368
    }    
369
  }
370
  
371
    /**
372
   * Register a document that resides on the filesystem with the database.
373
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
374
   * Creates a reference to a filesystem document (used for non-xml data files)
375
   * This method will be called for register data file in xml_documents in 
376
   * Replication.
377
   * This method is revised from registerDocument.
378
   *
379
   * @param conn the JDBC Connection to which all information is written
380
   * @param docname - the name of DTD, i.e. the name immediately following 
381
   *        the DOCTYPE keyword ( should be the root element name ) or
382
   *        the root element name if no DOCTYPE declaration provided
383
   *        (Oracle's and IBM parsers are not aware if it is not the 
384
   *        root element name)
385
   * @param doctype - Public ID of the DTD, i.e. the name immediately 
386
   *                  following the PUBLIC keyword in DOCTYPE declaration or
387
   *                  the docname if no Public ID provided or
388
   *                  null if no DOCTYPE declaration provided
389
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
390
   *               includes a revision number for this revision of the document 
391
   *               (e.g., knb.1.1)
392
   * @param user the user that owns the document
393
   * @param serverCode the serverid from xml_replication on which this document
394
   *        resides.
395
   */
396
  public static void registerDocumentInReplication(
397
                     String docname, String doctype, String accnum, 
398
                     String user, int serverCode)
399
                     throws SQLException, AccessionNumberException, Exception
400
  {
401
    DBConnection dbconn = null;
402
    int serialNumber = -1;
403
    //MetaCatUtil util = new MetaCatUtil();
404
    AccessionNumber ac;
405
    PreparedStatement pstmt = null;
406
    String action = null;
407
    try {
408
      //dbconn = util.openDBConnection();
409
       dbconn=DBConnectionPool.
410
                  getDBConnection("DocumentImpl.registerDocumentInReplication");
411
      serialNumber=dbconn.getCheckOutSerialNumber();
412
      String docIdWithoutRev=MetaCatUtil.getDocIdFromString(accnum);
413
      int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
414
      int revInDataBase=getLatestRevisionNumber(docIdWithoutRev);
415
      //revIndataBase=-1, there is no record in xml_documents table
416
      //the data file is a new one, inert it into table
417
      //user specified rev should be great than 0
418
      if (revInDataBase==-1 && userSpecifyRev>0 )
419
      {
420
       
421
        ac = new AccessionNumber(accnum, "insert");
422
        action = "insert";
423
      }
424
      //rev is greater the last revsion number and revInDataBase isn't -1
425
      // it is a updated data file
426
      else if (userSpecifyRev>revInDataBase && revInDataBase>0)
427
      {
428
        
429
        //archive the old entry 
430
        archiveDocRevision(docIdWithoutRev, user);
431
        //delete the old entry in xml_documents
432
        //deleteXMLDocuments(docIdWithoutRev);
433
        ac = new AccessionNumber(accnum, "update");
434
        action = "update";
435
      }
436
      // local server has newer version, then notify the remote server
437
      else if ( userSpecifyRev < revInDataBase && revInDataBase > 0)
438
      {
439
        throw new Exception("Local server: "+MetaCatUtil.getOption("server")+
440
                 " has newer revision of doc: "+docIdWithoutRev+"."
441
                  +revInDataBase+". Please notify it.");
442
      }
443
      //other situation
444
      else
445
      {
446
        
447
        throw new Exception("Revision number couldn't be "
448
                    +userSpecifyRev);
449
      }
450
      String docid = ac.getDocid();
451
      String rev = ac.getRev();
452
      /*SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy HH:mm:ss");
453
      Date localtime = new Date();
454
      String dateString = formatter.format(localtime);
455
      String sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY HH24:MI:SS");*/
456
      String sqlDateString = dbAdapter.getDateTimeFunction();
457
      
458
      StringBuffer sql = new StringBuffer();
459
      if (action != null && action.equals("insert"))
460
      {
461
        sql.append("insert into xml_documents (docid, docname, doctype, ");
462
        sql.append("user_owner, user_updated, server_location, rev,date_created");
463
        sql.append(", date_updated, public_access) values ('");
464
        sql.append(docid).append("','");
465
        sql.append(docname).append("','");
466
        sql.append(doctype).append("','");
467
        sql.append(user).append("','");
468
        sql.append(user).append("','");
469
        sql.append(serverCode).append("','");
470
        sql.append(rev).append("',");
471
        sql.append(sqlDateString).append(",");
472
        sql.append(sqlDateString).append(",");
473
        sql.append("'0')");
474
      }
475
      else if (action != null && action.equals("update"))
476
      {
477
        sql.append("update xml_documents set docname ='");
478
        sql.append(docname).append("', ");
479
        sql.append("user_updated='");
480
        sql.append(user).append("', ");
481
        sql.append("server_location='");
482
        sql.append(serverCode).append("',");
483
        sql.append("rev='");
484
        sql.append(rev).append("',");
485
        sql.append("date_updated=");
486
        sql.append(sqlDateString);
487
        sql.append(" where docid='");
488
        sql.append(docid).append("'");
489
      }
490
      // Set auto commit fasle
491
      dbconn.setAutoCommit(false);
492
      pstmt = dbconn.prepareStatement(sql.toString());
493
     
494
      pstmt.execute();
495
      // Commit the insert
496
      dbconn.commit();
497
      pstmt.close();
498
      //dbconn.close();
499
    } 
500
    finally 
501
    {
502
      // Set DBConnection auto commit true
503
      dbconn.setAutoCommit(true);
504
      pstmt.close();
505
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
506
    }    
507
  }
508
  
509
 /**
510
   * This method will register a data file entry in xml_documents and save a
511
   * data file input Stream into file system.. It is only used in replication
512
   *
513
   * @param  input, the input stream which contain the file content.
514
   * @param  , the input stream which contain the file content
515
   * @param docname - the name of DTD, for data file, it is a docid number.
516
   * @param doctype - "BIN" for data file
517
   * @param accnum the accession number to use for the INSERT OR UPDATE, which 
518
   *               includes a revision number for this revision of the document 
519
   *               (e.g., knb.1.1)
520
   * @param user the user that owns the document
521
   * @param docHomeServer, the home server of the docid
522
   * @param notificationServer, the server to notify force replication info to 
523
   *                            local metacat
524
   */
525
 public static void writeDataFileInReplication(InputStream input, 
526
                 String filePath, String docname, String doctype, String accnum, 
527
                   String user, String docHomeServer, String notificationServer)
528
                     throws SQLException, AccessionNumberException, Exception
529
 {
530
    int serverCode=-2;
531
   
532
    
533
    if (filePath==null||filePath.equals(""))
534
    {
535
      throw new 
536
            Exception("Please specify the directory where file will be store");
537
    }
538
    if (accnum==null||accnum.equals(""))
539
    {
540
      throw new Exception("Please specify the stored file name");
541
    }
542
    
543
   
544
    
545
    // If server is not int the xml replication talbe, insert it into 
546
    // xml_replication table
547
    //serverList.addToServerListIfItIsNot(docHomeServer);
548
    insertServerIntoReplicationTable(docHomeServer);
549
    
550
    // Get server code again
551
    serverCode = getServerCode(docHomeServer);
552
    
553
    
554
    //register data file into xml_documents table
555
    registerDocumentInReplication(docname, doctype, accnum, user, serverCode);
556
    //write inputstream into file system.
557
    File dataDirectory = new File(filePath);
558
    File newFile = new File(dataDirectory, accnum); 
559
       
560
    // create a buffered byte output stream
561
    // that uses a default-sized output buffer
562
    FileOutputStream fos = new FileOutputStream(newFile);
563
    BufferedOutputStream outPut = new BufferedOutputStream(fos);
564

    
565
    BufferedInputStream bis = null;
566
    bis = new BufferedInputStream(input);
567
    byte[] buf = new byte[4 * 1024]; // 4K buffer
568
    int b = bis.read(buf);
569
       
570
    while (b != -1) 
571
    {
572
        outPut.write(buf, 0, b);
573
        b = bis.read(buf);
574
    }
575
    bis.close();
576
	  outPut.close();
577
	  fos.close();
578
      
579
    // Force replicate data file
580
    ForceReplicationHandler forceReplication = new ForceReplicationHandler
581
                                    (accnum, false, notificationServer);
582
  
583
 }
584
  
585

    
586
  
587
  public static boolean getDataFileLockGrant(String accnum) 
588
                                                  throws Exception
589
  { 
590
    
591
    try
592
    {
593
      
594
      int serverLocation=getServerLocationNumber(accnum);
595
    
596
      return getDataFileLockGrant(accnum,serverLocation);
597
    }
598
    catch (Exception e)
599
    {
600
     
601
      throw e;
602
    }
603
  }
604
    
605
  /**
606
   * The method will check if metacat can get data file lock grant
607
   * If server code is 1, it get.
608
   * If server code is not 1 but call replication getlock successfully,
609
   * it get
610
   * else, it didn't get
611
   * @param accnum, the ID of the document 
612
   * @param action, the action to the document
613
   * @param serverCode, the server location code
614
   */
615
  public static boolean getDataFileLockGrant(String accnum, int serverCode)
616
                                          throws Exception 
617
  {
618
    boolean flag=true;
619
    //MetaCatUtil util = new MetaCatUtil();
620
    String docid = MetaCatUtil.getDocIdFromString(accnum);
621
    int rev = MetaCatUtil.getVersionFromString(accnum);
622
    
623
    if (serverCode == 1)
624
    {
625
      flag=true;
626
      return flag;
627
    }
628
    
629
    //if((serverCode != 1 && action.equals("UPDATE")) )
630
    if (serverCode != 1)
631
    { //if this document being written is not a resident of this server then
632
      //we need to try to get a lock from it's resident server.  If the
633
      //resident server will not give a lock then we send the user a message
634
      //saying that he/she needs to download a new copy of the file and
635
      //merge the differences manually.
636
      
637
      String server=MetacatReplication.getServerNameForServerCode(serverCode);
638
      MetacatReplication.replLog("attempting to lock " + accnum);
639
      URL u = new URL("https://" + server + "?server=" +
640
        MetaCatUtil.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
641
           +rev + "&docid=" + docid);
642
      //System.out.println("sending message: " + u.toString());
643
      String serverResStr = MetacatReplication.getURLContent(u);
644
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
645
      if(openingtag.equals("<lockgranted>"))
646
      {
647
        //the lock was granted go ahead with the insert
648
        //System.out.println("In lockgranted");
649
        MetacatReplication.replLog("lock granted for " + accnum + " from " +
650
                                      server);
651
        flag=true;  
652
        return flag;
653
      }//if
654

    
655
      else if(openingtag.equals("<filelocked>"))
656
      {//the file is currently locked by another user
657
       //notify our user to wait a few minutes, check out a new copy and try
658
       //again.
659
        //System.out.println("file locked");
660
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
661
                                   server + " reason: file already locked");
662
        throw new Exception("The file specified is already locked by another " +
663
                            "user.  Please wait 30 seconds, checkout the " +
664
                            "newer document, merge your changes and try " +
665
                            "again.");
666
      }
667
      else if(openingtag.equals("<outdatedfile>"))
668
      {//our file is outdated.  notify our user to check out a new copy of the
669
       //file and merge his version with the new version.
670
        //System.out.println("outdated file");
671
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
672
                                    server + " reason: local file outdated");
673
        throw new Exception("The file you are trying to update is an outdated" +
674
                            " version.  Please checkout the newest document, " +
675
                            "merge your changes and try again.");
676
      }//else if
677
    }//if
678
    
679
   return flag;
680
   
681
  }//getDataFileLockGrant
682
  
683
  /**
684
   * get the document name
685
   */
686
  public String getDocname() {
687
    return docname;
688
  }
689

    
690
  /**
691
   * get the document type (which is the PublicID)
692
   */
693
  public String getDoctype() {
694
    return doctype;
695
  }
696

    
697
  /**
698
   * get the system identifier
699
   */
700
  public String getSystemID() {
701
    return system_id;
702
  }
703

    
704
  /**
705
   * get the root node identifier
706
   */
707
  public long getRootNodeID() {
708
    return rootnodeid;
709
  }
710
  
711
  /**
712
   * get the creation date
713
   */
714
  public String getCreateDate() {
715
    return createdate;
716
  }
717
  
718
  /**
719
   * get the update date
720
   */
721
  public String getUpdateDate() {
722
    return updatedate;
723
  }
724

    
725
  /** 
726
   * Get the document identifier (docid)
727
   */
728
  public String getDocID() {
729
    return docid;
730
  }
731
  
732
// DOCTITLE attr cleared from the db
733
//  /**
734
//   *get the document title
735
//   */
736
//  public String getDocTitle() {
737
//    return doctitle;
738
//  }
739
  
740
  public String getUserowner() {
741
    return userowner;
742
  }
743
  
744
  public String getUserupdated() {
745
    return userupdated;
746
  }
747
  
748
  public int getServerlocation() {
749
    return serverlocation;
750
  }
751
  
752
  public String getDocHomeServer() {
753
    return docHomeServer;
754
  }
755
  
756
 
757
 
758
  public String getPublicaccess() {
759
    return publicaccess;
760
  }
761
  
762
  public int getRev() {
763
    return rev;
764
  }
765
  
766
  public String getValidateType()
767
  {
768
    return validateType;
769
  }
770
  
771
  /**
772
   * Print a string representation of the XML document
773
   */
774
  public String toString(String user, String[] groups, boolean withInlinedata)
775
  {
776
    StringWriter docwriter = new StringWriter();
777
    try 
778
    {
779
      this.toXml(docwriter, user, groups, withInlinedata);
780
    } 
781
    catch (McdbException mcdbe) 
782
    {
783
      return null;
784
    }
785
    String document = docwriter.toString();
786
    return document;
787
  }
788
  
789
   /**
790
   * Print a string representation of the XML document
791
   */
792
  public String toString()
793
  {
794
    StringWriter docwriter = new StringWriter();
795
    String userName = null;
796
    String[] groupNames = null;
797
    boolean withInlineData = false;
798
    try 
799
    {
800
      this.toXml(docwriter, userName, groupNames, withInlineData);
801
    } 
802
    catch (McdbException mcdbe) 
803
    {
804
      return null;
805
    }
806
    String document = docwriter.toString();
807
    return document;
808
  }
809

    
810
  /**
811
   * Get a text representation of the XML document as a string
812
   * This older algorithm uses a recursive tree of Objects to represent the
813
   * nodes of the tree.  Each object is passed the data for the document 
814
   * and searches all of the document data to find its children nodes and
815
   * recursively build.  Thus, because each node reads the whole document,
816
   * this algorithm is extremely slow for larger documents, and the time
817
   * to completion is O(N^N) wrt the number of nodes.  See toXml() for a
818
   * better algorithm.
819
   */
820
  public String readUsingSlowAlgorithm() throws McdbException
821
  {
822
    StringBuffer doc = new StringBuffer();
823

    
824
    // First, check that we have the needed node data, and get it if not
825
    if (nodeRecordList == null) {
826
      nodeRecordList = getNodeRecordList(rootnodeid);
827
    }
828

    
829
    // Create the elements from the downloaded data in the TreeSet
830
    rootNode = new ElementNode(nodeRecordList, rootnodeid);
831

    
832
    // Append the resulting document to the StringBuffer and return it
833
    doc.append("<?xml version=\"1.0\"?>\n");
834
      
835
    if (docname != null) {
836
      if ((doctype != null) && (system_id != null)) {
837
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
838
                   "\" \"" + system_id + "\">\n");
839
      } else {
840
        doc.append("<!DOCTYPE " + docname + ">\n");
841
      }
842
    }
843
    doc.append(rootNode.toString());
844
  
845
    return (doc.toString());
846
  }
847

    
848
  /**
849
   * Print a text representation of the XML document to a Writer
850
   *
851
   * @param pw the Writer to which we print the document
852
   */
853
  public void toXml(Writer pw, String user, String[] groups, boolean withInLineData) 
854
                                                          throws McdbException
855
  {
856
    // flag for process  eml2
857
    boolean proccessEml2 = false;
858
    boolean storedDTD = false;//flag to inidate publicid or system 
859
                              // id stored in db or not
860
    boolean firstElement = true;
861
    String dbDocName = null;
862
    String dbPublicID = null;
863
    String dbSystemID = null;
864
    
865
    if (doctype != null && doctype.equals(EMLNAMESPACE))
866
    {
867
      proccessEml2 = true;
868
    }
869
    // flag for process inline data
870
    boolean prcocessInlineData = false;
871
    
872
    TreeSet nodeRecordLists = null;
873
    PrintWriter out = null;
874
    if (pw instanceof PrintWriter) {
875
      out = (PrintWriter)pw;
876
    } else {
877
      out = new PrintWriter(pw);
878
    }
879

    
880
    MetaCatUtil util = new MetaCatUtil();
881
    
882
    // Here add code to handle subtree access control
883
    PermissionController control = new PermissionController(docid);
884
    Hashtable unaccessableSubTree =control.hasUnaccessableSubTree(user, groups, 
885
                                             AccessControlInterface.READSTRING);
886
    
887
    if (!unaccessableSubTree.isEmpty())
888
    {
889
     
890
      nodeRecordLists = getPartNodeRecordList(rootnodeid, unaccessableSubTree);
891
      
892
    }
893
    else 
894
    {
895
      nodeRecordLists = getNodeRecordList(rootnodeid);
896
    }
897

    
898
    Stack openElements = new Stack();
899
    boolean atRootElement = true;
900
    boolean previousNodeWasElement = false;
901

    
902
    // Step through all of the node records we were given
903
   
904
    Iterator it = nodeRecordLists.iterator();
905
   
906
    while (it.hasNext()) 
907
    {
908
      
909
      NodeRecord currentNode = (NodeRecord)it.next();
910
      util.debugMessage("[Got Node ID: " + currentNode.nodeid +
911
                          " (" + currentNode.parentnodeid +
912
                          ", " + currentNode.nodeindex + 
913
                          ", " + currentNode.nodetype + 
914
                          ", " + currentNode.nodename + 
915
                          ", " + currentNode.nodedata + ")]", 50);
916
      // Print the end tag for the previous node if needed
917
      //
918
      // This is determined by inspecting the parent nodeid for the
919
      // currentNode.  If it is the same as the nodeid of the last element
920
      // that was pushed onto the stack, then we are still in that previous
921
      // parent element, and we do nothing.  However, if it differs, then we
922
      // have returned to a level above the previous parent, so we go into
923
      // a loop and pop off nodes and print out their end tags until we get
924
      // the node on the stack to match the currentNode parentnodeid
925
      //
926
      // So, this of course means that we rely on the list of elements
927
      // having been sorted in a depth first traversal of the nodes, which
928
      // is handled by the NodeComparator class used by the TreeSet
929
      if (!atRootElement) {
930
        NodeRecord currentElement = (NodeRecord)openElements.peek();
931
        if ( currentNode.parentnodeid != currentElement.nodeid ) {
932
          while ( currentNode.parentnodeid != currentElement.nodeid ) {
933
            currentElement = (NodeRecord)openElements.pop();
934
            util.debugMessage("\n POPPED: " + currentElement.nodename, 50);
935
            if (previousNodeWasElement) {
936
              out.print(">");
937
              previousNodeWasElement = false;
938
            }  
939
            if ( currentElement.nodeprefix != null ) {
940
              out.print("</" + currentElement.nodeprefix + ":" + 
941
                        currentElement.nodename + ">" );
942
            } else {
943
              out.print("</" + currentElement.nodename + ">" );
944
            }
945
            currentElement = (NodeRecord)openElements.peek();
946
          }
947
        }
948
      }
949

    
950
      // Handle the DOCUMENT node
951
      if (currentNode.nodetype.equals("DOCUMENT")) {
952
        out.print("<?xml version=\"1.0\"?>");
953
       
954

    
955
      // Handle the ELEMENT nodes
956
      } else if (currentNode.nodetype.equals("ELEMENT")) {
957
        if (atRootElement) {
958
          atRootElement = false;
959
        } else {
960
          if (previousNodeWasElement) {
961
            out.print(">");
962
          }
963
        }
964
        
965
        // if publicid or system is not stored into db send it out by default
966
        if ( !storedDTD & firstElement )
967
        {
968
           if (docname != null && validateType != null && validateType.equals(DTD)) 
969
           {
970
              if ((doctype != null) && (system_id != null)) 
971
              {
972
            
973
                  out.print("<!DOCTYPE " + docname + " PUBLIC \"" + doctype + 
974
                       "\" \"" + system_id + "\">");
975
               } 
976
               else 
977
               {
978
            
979
                  out.print("<!DOCTYPE " + docname + ">");
980
               }
981
           }
982
        }
983
        firstElement = false;
984
        openElements.push(currentNode);
985
        util.debugMessage("\n PUSHED: " + currentNode.nodename, 50);
986
        previousNodeWasElement = true;
987
        if ( currentNode.nodeprefix != null ) {
988
          out.print("<" + currentNode.nodeprefix + ":" + currentNode.nodename);
989
        } else {
990
          out.print("<" + currentNode.nodename);
991
        }
992
        
993
        // if currentNode is inline and handle eml2, set flag proccess in
994
        if (currentNode.nodename != null && 
995
            currentNode.nodename.equals(EmlSAXHandler.INLINE) && proccessEml2)
996
        {
997
          prcocessInlineData = true;
998
        }
999

    
1000
      // Handle the ATTRIBUTE nodes
1001
      } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
1002
        if ( currentNode.nodeprefix != null ) {
1003
          out.print(" " + currentNode.nodeprefix + ":" + currentNode.nodename +
1004
                    "=\"" + currentNode.nodedata + "\"");
1005
        } else {
1006
          out.print(" " + currentNode.nodename + "=\"" +
1007
                    currentNode.nodedata + "\"");
1008
        }
1009

    
1010
      // Handle the NAMESPACE nodes
1011
      } else if (currentNode.nodetype.equals("NAMESPACE")) {
1012
        out.print(" xmlns:" + currentNode.nodename + "=\""
1013
                 + currentNode.nodedata + "\"");
1014

    
1015
      // Handle the TEXT nodes
1016
      } else if (currentNode.nodetype.equals("TEXT")) {
1017
        if (previousNodeWasElement) {
1018
          out.print(">");
1019
        }
1020
        if (!prcocessInlineData || !withInLineData)
1021
        {
1022
          // if it is not inline data just out put data or it is line data
1023
          // but user don't want it, just put local id in inlinedata
1024
          out.print(currentNode.nodedata);
1025
        }
1026
        else
1027
        {
1028
          // if it is inline data and user want to see it, pull out from 
1029
          // file system and output it
1030
          // for inline data, the data base only store the file name, so we
1031
          // can combine the file name and inline data file path, to get it
1032
          String fileName = currentNode.nodedata;
1033
          Reader reader = 
1034
                          EmlSAXHandler.readInlineDataFromFileSystem(fileName);
1035
          char [] characterArray = new char [4*1024];
1036
          try
1037
          {
1038
            int length = reader.read(characterArray);
1039
            while ( length != -1)
1040
            {
1041
              out.print(new String(characterArray, 0, length));
1042
              out.flush();
1043
              length = reader.read(characterArray);
1044
            }
1045
            reader.close();
1046
          }
1047
          catch (IOException e)
1048
          {
1049
            throw new McdbException(e.getMessage());
1050
          }
1051
        }
1052
        
1053
        // reset proccess inline data false
1054
        prcocessInlineData =false;
1055
        previousNodeWasElement = false;
1056

    
1057
      // Handle the COMMENT nodes
1058
      } else if (currentNode.nodetype.equals("COMMENT")) {
1059
        if (previousNodeWasElement) {
1060
          out.print(">");
1061
        }
1062
        out.print("<!--" + currentNode.nodedata + "-->");
1063
        previousNodeWasElement = false;
1064

    
1065
      // Handle the PI nodes
1066
      } else if (currentNode.nodetype.equals("PI")) {
1067
        if (previousNodeWasElement) {
1068
          out.print(">");
1069
        }
1070
        out.print("<?" + currentNode.nodename + " " +
1071
                        currentNode.nodedata + "?>");
1072
        previousNodeWasElement = false;
1073
     // Handle the DTD nodes (docname, publicid, systemid)  
1074
     } else if (currentNode.nodetype.equals(DTD)) {
1075
         storedDTD = true;
1076
         if (currentNode.getNodeName().equals(DOCNAME))
1077
         {
1078
           dbDocName = currentNode.getNodeData();
1079
         }
1080
         if (currentNode.getNodeName().equals(PUBLICID))
1081
         {
1082
           dbPublicID = currentNode.getNodeData();
1083
         }
1084
         if (currentNode.getNodeName().equals(SYSTEMID))
1085
         {
1086
           dbSystemID = currentNode.getNodeData();
1087
           // send out <!doctype .../>
1088
           if (dbDocName != null ) 
1089
           {
1090
              if ((dbPublicID!= null) && (dbSystemID != null)) 
1091
              {
1092
            
1093
                 out.print("<!DOCTYPE " + dbDocName+" PUBLIC \""+dbPublicID + 
1094
                       "\" \"" + dbSystemID + "\">");
1095
              } 
1096
              else 
1097
              {
1098
            
1099
                out.print("<!DOCTYPE " + dbDocName + ">");
1100
              }
1101
           }
1102
           
1103
           //reset these variable
1104
           dbDocName = null;
1105
           dbPublicID = null;
1106
           dbSystemID = null;
1107
         }
1108
       
1109
      // Handle any other node type (do nothing)
1110
      } else {
1111
        // Any other types of nodes are not handled.
1112
        // Probably should throw an exception here to indicate this
1113
      }
1114
      out.flush();
1115
    }
1116
    
1117
    // Print the final end tag for the root element
1118
    while(!openElements.empty())
1119
    {
1120
      NodeRecord currentElement = (NodeRecord)openElements.pop();
1121
      util.debugMessage("\n POPPED: " + currentElement.nodename, 50);
1122
      if ( currentElement.nodeprefix != null ) {
1123
        out.print("</" + currentElement.nodeprefix + ":" + 
1124
                  currentElement.nodename + ">" );
1125
      } else {
1126
        out.print("</" + currentElement.nodename + ">" );
1127
      }
1128
    }
1129
    out.flush();
1130
  }
1131
  
1132

    
1133
  
1134
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
1135
  {
1136
    //System.out.println("inRevisionOnly");
1137
    DBConnection dbconn = null;
1138
    int serialNumber = -1;
1139
    PreparedStatement pstmt =null;
1140
    String rev = docid.getRev();
1141
    String newid = docid.getIdentifier();
1142
    try
1143
    {
1144
      dbconn=DBConnectionPool.
1145
                    getDBConnection("DocumentImpl.isRevisionOnly");
1146
      serialNumber=dbconn.getCheckOutSerialNumber();
1147
      pstmt = dbconn.prepareStatement("select rev from xml_documents " +
1148
                                  "where docid like '" + newid + "'");
1149
      pstmt.execute();
1150
      ResultSet rs = pstmt.getResultSet();
1151
      boolean tablehasrows = rs.next();
1152
      if(rev.equals("newest") || rev.equals("all"))
1153
      {
1154
        return false;
1155
      }
1156
    
1157
      if(tablehasrows)
1158
      {
1159
        int r = rs.getInt(1);
1160
        pstmt.close();
1161
        if(new Integer(rev).intValue() == r)
1162
        { //the current revision in in xml_documents
1163
          //System.out.println("returning false");
1164
          return false;
1165
        }
1166
        else if(new Integer(rev).intValue() < r)
1167
        { //the current revision is in xml_revisions.
1168
          //System.out.println("returning true");
1169
          return true;
1170
        }
1171
        else if(new Integer(rev).intValue() > r)
1172
        { //error, rev cannot be greater than r
1173
          throw new Exception("requested revision cannot be greater than " +
1174
                            "the latest revision number.");
1175
        }
1176
      }
1177
      // Get miss docid and rev, throw to McdDocNotFoundException
1178
      String missDocId = MetaCatUtil.getDocIdFromString(docid.toString());
1179
      String missRevision = 
1180
                  MetaCatUtil.getRevisionStringFromString(docid.toString());
1181
      throw new McdbDocNotFoundException("the requested docid '" + 
1182
                docid.toString() + "' does not exist", missDocId, missRevision);
1183
    }//try
1184
    finally
1185
    {
1186
      pstmt.close();
1187
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1188
    }//finally
1189
  }
1190

    
1191
  private void getDocumentInfo(String docid) throws McdbException, 
1192
                                        AccessionNumberException, Exception
1193
  {
1194
    getDocumentInfo(new DocumentIdentifier(docid));
1195
  }
1196
  
1197
  /**
1198
   * Look up the document type information from the database
1199
   *
1200
   * @param docid the id of the document to look up
1201
   */
1202
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException
1203
                                                          , Exception
1204
  {
1205
    DBConnection dbconn = null;
1206
    int serialNumber = -1;
1207
    PreparedStatement pstmt = null;
1208
    String table = "xml_documents";
1209
        
1210
    try
1211
    {
1212
      if(isRevisionOnly(docid))
1213
      { //pull the document from xml_revisions instead of from xml_documents;
1214
        table = "xml_revisions";
1215
      }
1216
    }
1217
    // catch a McdbDocNotFoundException throw it
1218
    catch (McdbDocNotFoundException notFound)
1219
    {
1220
      throw notFound;
1221
    }
1222
    catch(Exception e)
1223
    {
1224
      
1225
      MetaCatUtil.debugMessage("error in DocumentImpl.getDocumentInfo: " + 
1226
                          e.getMessage(), 30);
1227
      throw e;
1228
    }
1229
    
1230

    
1231
    
1232
    try 
1233
    {
1234
      dbconn=DBConnectionPool.getDBConnection("DocumentImpl.getDocumentInfo");
1235
      serialNumber=dbconn.getCheckOutSerialNumber();
1236
      StringBuffer sql = new StringBuffer();
1237
// DOCTITLE attr cleared from the db
1238
//      sql.append("SELECT docname, doctype, rootnodeid, doctitle, ");
1239
      sql.append("SELECT docname, doctype, rootnodeid, ");
1240
      sql.append("date_created, date_updated, user_owner, user_updated, ");
1241
      sql.append("server_location, public_access, rev");
1242
      sql.append(" FROM ").append(table);
1243
      sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
1244
      sql.append("' and rev like '").append(docid.getRev()).append("'");
1245
      //System.out.println(sql.toString());
1246
      pstmt =
1247
        dbconn.prepareStatement(sql.toString());
1248
      // Bind the values to the query
1249
      //pstmt.setString(1, docid.getIdentifier());
1250
      //pstmt.setString(2, docid.getRev());
1251

    
1252
      pstmt.execute();
1253
      ResultSet rs = pstmt.getResultSet();
1254
      boolean tableHasRows = rs.next();
1255
      if (tableHasRows) {
1256
        this.docname        = rs.getString(1);
1257
        this.doctype        = rs.getString(2);
1258
        this.rootnodeid     = rs.getLong(3);
1259
// DOCTITLE attr cleared from the db
1260
//        this.doctitle       = rs.getString(4);
1261
        this.createdate     = rs.getString(4);
1262
        this.updatedate     = rs.getString(5);
1263
        this.userowner      = rs.getString(6);
1264
        this.userupdated    = rs.getString(7);
1265
        this.serverlocation = rs.getInt(8);
1266
        this.publicaccess   = rs.getString(9);
1267
        this.rev            = rs.getInt(10);
1268
      } 
1269
      pstmt.close();
1270
      
1271
      //get doc  home server name
1272
      
1273
      pstmt = dbconn.prepareStatement("select server " +
1274
                        "from xml_replication where serverid = ?");
1275
      //because connection use twise here, so we need to increase one
1276
      dbconn.increaseUsageCount(1);
1277
      pstmt.setInt(1, serverlocation);
1278
      pstmt.execute();
1279
      rs = pstmt.getResultSet();
1280
      tableHasRows = rs.next();
1281
      if (tableHasRows)
1282
      {
1283
        
1284
          String server = rs.getString(1);
1285
          //get homeserver name
1286
          if(!server.equals("localhost"))
1287
          {
1288
            this.docHomeServer=server;
1289
          }
1290
          else
1291
          {
1292
            this.docHomeServer=MetaCatUtil.getLocalReplicationServerName();
1293
          }
1294
          MetaCatUtil.debugMessage("server: "+docHomeServer, 50);
1295
        
1296
      }
1297
      pstmt.close();
1298
      if (this.doctype != null) {
1299
        pstmt =
1300
          dbconn.prepareStatement("SELECT system_id, entry_type " +
1301
                                  "FROM xml_catalog " +
1302
                                 "WHERE public_id = ?");
1303
        //should increase usage count again
1304
        dbconn.increaseUsageCount(1);
1305
        // Bind the values to the query
1306
        pstmt.setString(1, doctype);
1307
  
1308
        pstmt.execute();
1309
        rs = pstmt.getResultSet();
1310
        tableHasRows = rs.next();
1311
        if (tableHasRows) {
1312
          this.system_id  = rs.getString(1);
1313
          this.validateType = rs.getString(2);
1314
          
1315
        } 
1316
        pstmt.close();
1317
      }
1318
    } catch (SQLException e) {
1319
      System.out.println("error in DocumentImpl.getDocumentInfo: " + 
1320
                          e.getMessage());
1321
      e.printStackTrace(System.out);
1322
      throw new McdbException("Error accessing database connection in " +
1323
                              "DocumentImpl.getDocumentInfo: ", e);
1324
    }
1325
    finally
1326
    {
1327
      try
1328
      {
1329
        pstmt.close();
1330
      }
1331
      catch (SQLException ee)
1332
      {
1333
        MetaCatUtil.debugMessage("error in DocumentImple.getDocumentInfo: "
1334
                                    +ee.getMessage(), 30);
1335
      }//catch
1336
      finally
1337
      {
1338
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1339
      }
1340
    }
1341

    
1342
    if (this.docname == null) {
1343
      throw new McdbDocNotFoundException("Document not found: " + docid,
1344
                                 docid.getIdentifier(), docid.getRev());
1345
    }
1346
  }
1347
  
1348
  /**
1349
   * Look up the node data from the database, but some node would be shown
1350
   * because of access control
1351
   * @param rootnodeid the id of the root node of the node tree to look up
1352
   * @param accessControl  the hashtable has control info
1353
   */
1354
  private TreeSet getPartNodeRecordList(long rootnodeid, 
1355
                                        Hashtable accessControl) 
1356
                                        throws McdbException
1357
  {
1358
    PreparedStatement pstmt = null;
1359
    DBConnection dbconn = null;
1360
    int serialNumber = -1;
1361
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1362
    long nodeid = 0;
1363
    long parentnodeid = 0;
1364
    long nodeindex = 0;
1365
    String nodetype = null;
1366
    String nodename = null;
1367
    String nodeprefix = null;
1368
    String nodedata = null;
1369
    String quotechar = dbAdapter.getStringDelimiter();
1370
    String sql = "SELECT nodeid,parentnodeid,nodeindex, " +
1371
                 "nodetype,nodename,nodeprefix,nodedata " +               
1372
                  "FROM xml_nodes WHERE rootnodeid = ?";
1373
                  
1374
    // go through the access control for some nodes
1375
     Enumeration en = accessControl.elements();
1376
     while (en.hasMoreElements())
1377
     {
1378
         SubTree tree = (SubTree)en.nextElement();
1379
         long startId = tree.getStartNodeId();
1380
         long endId  = tree.getEndNodeId();
1381
         sql = sql +" AND(nodeid < " + startId + " OR nodeid > " +endId +")";
1382
         
1383
     }
1384
     MetaCatUtil.debugMessage("The final query to select part node tree: " +
1385
                              sql, 25);
1386

    
1387
    try 
1388
    {
1389
      dbconn=DBConnectionPool.
1390
                    getDBConnection("DocumentImpl.getPartNodeRecordList");
1391
      serialNumber=dbconn.getCheckOutSerialNumber();
1392
      pstmt = dbconn.prepareStatement(sql);
1393

    
1394
      // Bind the values to the query
1395
      pstmt.setLong(1, rootnodeid);
1396
      pstmt.execute();
1397
      ResultSet rs = pstmt.getResultSet();
1398
      boolean tableHasRows = rs.next();
1399
      while (tableHasRows) 
1400
      {
1401
        nodeid = rs.getLong(1);
1402
        parentnodeid = rs.getLong(2);
1403
        nodeindex = rs.getLong(3);
1404
        nodetype = rs.getString(4);
1405
        nodename = rs.getString(5);
1406
        nodeprefix = rs.getString(6);
1407
        nodedata = rs.getString(7);
1408
        nodedata = MetaCatUtil.normalize(nodedata);
1409
        // add the data to the node record list hashtable
1410
        NodeRecord currentRecord = new NodeRecord(nodeid,parentnodeid,nodeindex,
1411
                                      nodetype, nodename, nodeprefix, nodedata);
1412
        nodeRecordList.add(currentRecord);
1413

    
1414
        // Advance to the next node
1415
        tableHasRows = rs.next();
1416
      } 
1417
      pstmt.close();
1418

    
1419
    } 
1420
    catch (SQLException e) 
1421
    {
1422
      throw new McdbException("Error in DocumentImpl.getPartNodeRecordList " +
1423
                              e.getMessage());
1424
    }
1425
    finally
1426
    {
1427
      try
1428
      {
1429
        pstmt.close();
1430
      }
1431
      catch (SQLException ee)
1432
      {
1433
        MetaCatUtil.debugMessage("error in DocumentImpl.getPartNodeRecordList: "
1434
                                    +ee.getMessage(), 30);
1435
      }
1436
      finally
1437
      {
1438
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1439
      }
1440
    }
1441
      
1442

    
1443
    if (!nodeRecordList.isEmpty()) 
1444
    {
1445
     
1446
      return nodeRecordList;
1447
    } 
1448
    else 
1449
    {
1450
      
1451
      throw new McdbException("Error getting node data: " + docid);
1452
    }
1453
  }
1454
  
1455
  /**
1456
   * Look up the node data from the database
1457
   *
1458
   * @param rootnodeid the id of the root node of the node tree to look up
1459
   */
1460
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException
1461
  {
1462
    PreparedStatement pstmt = null;
1463
    DBConnection dbconn = null;
1464
    int serialNumber = -1;
1465
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1466
    long nodeid = 0;
1467
    long parentnodeid = 0;
1468
    long nodeindex = 0;
1469
    String nodetype = null;
1470
    String nodename = null;
1471
    String nodeprefix = null;
1472
    String nodedata = null;
1473
    String quotechar = dbAdapter.getStringDelimiter();
1474

    
1475
    try {
1476
      dbconn=DBConnectionPool.
1477
                    getDBConnection("DocumentImpl.getNodeRecordList");
1478
      serialNumber=dbconn.getCheckOutSerialNumber();
1479
      pstmt =
1480
      dbconn.prepareStatement("SELECT nodeid,parentnodeid,nodeindex, " +
1481
           "nodetype,nodename,nodeprefix,nodedata " +               
1482
           "FROM xml_nodes WHERE rootnodeid = ?");
1483

    
1484
      // Bind the values to the query
1485
      pstmt.setLong(1, rootnodeid);
1486

    
1487
      pstmt.execute();
1488
      ResultSet rs = pstmt.getResultSet();
1489
      boolean tableHasRows = rs.next();
1490
      while (tableHasRows) {
1491
        nodeid = rs.getLong(1);
1492
        parentnodeid = rs.getLong(2);
1493
        nodeindex = rs.getLong(3);
1494
        nodetype = rs.getString(4);
1495
        nodename = rs.getString(5);
1496
        nodeprefix = rs.getString(6);
1497
        nodedata = rs.getString(7);
1498
        nodedata = MetaCatUtil.normalize(nodedata);
1499
        // add the data to the node record list hashtable
1500
        NodeRecord currentRecord = new NodeRecord(nodeid,parentnodeid,nodeindex,
1501
                                      nodetype, nodename, nodeprefix, nodedata);
1502
        nodeRecordList.add(currentRecord);
1503

    
1504
        // Advance to the next node
1505
        tableHasRows = rs.next();
1506
      } 
1507
      pstmt.close();
1508

    
1509
    } catch (SQLException e) {
1510
      throw new McdbException("Error in DocumentImpl.getNodeRecordList " +
1511
                              e.getMessage());
1512
    }
1513
    finally
1514
    {
1515
      try
1516
      {
1517
        pstmt.close();
1518
      }
1519
      catch (SQLException ee)
1520
      {
1521
        MetaCatUtil.debugMessage("error in DocumentImpl.getNodeRecordList: "
1522
                                    +ee.getMessage(), 30);
1523
      }
1524
      finally
1525
      {
1526
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1527
      }
1528
    }
1529
  
1530
    return nodeRecordList;
1531
   
1532
  }
1533
  
1534
// NOT USED ANY MORE
1535
//  /** creates SQL code and inserts new document into DB connection 
1536
//   default serverCode of 1*/
1537
//  private void writeDocumentToDB(String action, String user)
1538
//               throws SQLException, Exception
1539
//  {
1540
//    writeDocumentToDB(action, user, null, 1);
1541
//  }
1542

    
1543
 /** creates SQL code and inserts new document into DB connection */
1544
  private void writeDocumentToDB(String action, String user, String pub, 
1545
                                 String catalogid, int serverCode) 
1546
               throws SQLException, Exception {
1547
    String sysdate = dbAdapter.getDateTimeFunction();
1548

    
1549
    try {
1550
      PreparedStatement pstmt = null;
1551

    
1552
      if (action.equals("INSERT")) {
1553
        //AccessionNumber ac = new AccessionNumber();
1554
        //this.docid = ac.generate(docid, "INSERT");
1555
        
1556
        pstmt = connection.prepareStatement(
1557
                "INSERT INTO xml_documents " +
1558
                "(docid, rootnodeid, docname, doctype, " + 
1559
                "user_owner, user_updated, date_created, date_updated, " + 
1560
                "public_access, catalog_id, server_location, rev) " +
1561
                "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", " + sysdate + 
1562
                ", ?, ?, ?, ?)");
1563
        // Increase dbconnection usage count
1564
        connection.increaseUsageCount(1);
1565
        
1566
        //note that the server_location is set to 1. 
1567
        //this means that "localhost" in the xml_replication table must
1568
        //always be the first entry!!!!!
1569
        
1570
        // Bind the values to the query
1571
        pstmt.setString(1, this.docid);
1572
        pstmt.setLong(2, rootnodeid);
1573
        pstmt.setString(3, docname);
1574
        pstmt.setString(4, doctype);
1575
        pstmt.setString(5, user);
1576
        pstmt.setString(6, user);
1577
        //public access is usefulless, so set it to null
1578
        pstmt.setString(7, null);
1579
        /*if ( pub == null ) {
1580
          pstmt.setString(7, null);
1581
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1582
          pstmt.setInt(7, 1);
1583
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1584
          pstmt.setInt(7, 0);
1585
        }*/
1586
        pstmt.setString(8, catalogid);
1587
        pstmt.setInt(9, serverCode);
1588
        pstmt.setInt(10, Integer.parseInt(updatedVersion)); 
1589
      } else if (action.equals("UPDATE")) {
1590

    
1591
        // Save the old document publicaccessentry in a backup table
1592
        DocumentImpl.archiveDocRevision(connection, docid, user );
1593
        DocumentImpl thisdoc = new DocumentImpl(docid, false);
1594
        int thisrev = thisdoc.getRev();
1595
        
1596
        //if the updated vesion is not greater than current one,
1597
        //throw it into a exception
1598
        if (Integer.parseInt(updatedVersion)<=thisrev)
1599
        {
1600
          throw new Exception("Next revision number couldn't be less"
1601
                               +" than or equal "+ thisrev);
1602
        }
1603
        else
1604
        {
1605
          //set the user specified revision 
1606
          thisrev=Integer.parseInt(updatedVersion);
1607
        }
1608
        
1609
        // Delete index for the old version of docid
1610
        // The new index is inserting on the next calls to DBSAXNode
1611
        pstmt = connection.prepareStatement(
1612
                "DELETE FROM xml_index WHERE docid='" + this.docid + "'");
1613
        // Increase dbconnection usage count
1614
        connection.increaseUsageCount(1);
1615
        
1616
        pstmt.execute();
1617
        pstmt.close();
1618

    
1619
        // Update the new document to reflect the new node tree
1620
        pstmt = connection.prepareStatement(
1621
            "UPDATE xml_documents " +
1622
            "SET rootnodeid = ?, docname = ?, doctype = ?, " +
1623
            "user_updated = ?, date_updated = " + sysdate + ", " +
1624
            "server_location = ?, rev = ?, public_access = ?, catalog_id = ? " +
1625
            "WHERE docid = ?");
1626
        // Increase dbconnection usage count
1627
        connection.increaseUsageCount(1);
1628
        // Bind the values to the query
1629
        pstmt.setLong(1, rootnodeid);
1630
        pstmt.setString(2, docname);
1631
        pstmt.setString(3, doctype);
1632
        pstmt.setString(4, user);
1633
        pstmt.setInt(5, serverCode);
1634
        pstmt.setInt(6, thisrev);
1635
        pstmt.setString(7, null);
1636
        /*if ( pub == null ) {
1637
          pstmt.setString(7, null);
1638
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1639
          pstmt .setInt(7, 1);
1640
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1641
          pstmt.setInt(7, 0);
1642
        }*/
1643
        pstmt.setString(8, catalogid);
1644
        pstmt.setString(9, this.docid);
1645

    
1646
      } else {
1647
        System.err.println("Action not supported: " + action);
1648
      }
1649

    
1650
      // Do the insertion
1651
      pstmt.execute();
1652
      
1653
      pstmt.close();
1654

    
1655
    } catch (SQLException sqle) {
1656
      throw sqle;
1657
    } catch (Exception e) {
1658
      throw e;
1659
    }
1660
  }
1661

    
1662
  /**
1663
   * Write an XML file to the database, given a filename
1664
   *
1665
   * @param conn the JDBC connection to the database
1666
   * @param filename the filename to be loaded into the database
1667
   * @param pub flag for public "read" access on document
1668
   * @param dtdfilename the dtd to be uploaded on server's file system
1669
   * @param action the action to be performed (INSERT OR UPDATE)
1670
   * @param docid the docid to use for the INSERT OR UPDATE
1671
   * @param user the user that owns the document
1672
   * @param groups the groups to which user belongs
1673
   */
1674
  /*public static String write(DBConnection conn,String filename,
1675
                             String pub, String dtdfilename,
1676
                             String action, String docid, String user,
1677
                             String[] groups )
1678
                throws Exception {
1679
                  
1680
    Reader dtd = null;
1681
    if ( dtdfilename != null ) {
1682
      dtd = new FileReader(new File(dtdfilename).toString());
1683
    }
1684
    return write ( conn, new FileReader(new File(filename).toString()),
1685
                   pub, dtd, action, docid, user, groups, false);
1686
  }*/
1687

    
1688
  public static String write(DBConnection conn,Reader xml,String pub,Reader dtd,
1689
                             String action, String docid, String user,
1690
                             String[] groups, String ruleBase, 
1691
                             boolean needValidation)
1692
                throws Exception {
1693
    //this method will be called in handleUpdateOrInsert method 
1694
    //in MetacatServlet class and now is wrapper into documentImple
1695
    // get server location for this doc
1696
    int serverLocation=getServerLocationNumber(docid);
1697
    return write(conn,xml,pub,dtd,action,docid,user,groups,serverLocation,false,
1698
                 ruleBase, needValidation);
1699
  }
1700

    
1701
 
1702
  
1703
  /**
1704
   * Write an XML file to the database, given a Reader
1705
   *
1706
   * @param conn the JDBC connection to the database
1707
   * @param xml the xml stream to be loaded into the database
1708
   * @param pub flag for public "read" access on xml document
1709
   * @param dtd the dtd to be uploaded on server's file system
1710
   * @param action the action to be performed (INSERT or UPDATE)
1711
   * @param accnum the docid + rev# to use on INSERT or UPDATE
1712
   * @param user the user that owns the document
1713
   * @param groups the groups to which user belongs
1714
   * @param serverCode the serverid from xml_replication on which this document
1715
   *        resides.
1716
   * @param override flag to stop insert replication checking.
1717
   *        if override = true then a document not belonging to the local server
1718
   *        will not be checked upon update for a file lock.
1719
   *        if override = false then a document not from this server, upon 
1720
   *        update will be locked and version checked.
1721
   */
1722

    
1723
  public static String write(DBConnection conn, Reader xml,String pub,
1724
                         Reader dtd, String action, String accnum, String user,
1725
                         String[] groups, int serverCode, boolean override,
1726
                         String ruleBase, boolean needValidation)
1727
                throws Exception
1728
  {
1729
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1730
    //MetaCatUtil util = new MetaCatUtil();
1731
    MetaCatUtil.debugMessage("conn usage count before writting: "
1732
                                      +conn.getUsageCount(), 50);
1733
    AccessionNumber ac = new AccessionNumber(accnum, action);
1734
    String docid = ac.getDocid();
1735
    String rev = ac.getRev();
1736
    MetaCatUtil.debugMessage("action: " + action + " servercode: " + 
1737
                             serverCode + " override: " + override, 10);
1738
                     
1739
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
1740
    { //if this document being written is not a resident of this server then
1741
      //we need to try to get a lock from it's resident server.  If the
1742
      //resident server will not give a lock then we send the user a message
1743
      //saying that he/she needs to download a new copy of the file and
1744
      //merge the differences manually.
1745
      int istreamInt; 
1746
      char istreamChar;
1747
     
1748
      // check for 'write' permission for 'user' to update this document
1749
      if ( !hasWritePermission(user, groups, docid) ) {
1750
        throw new Exception("User " + user + 
1751
              " does not have permission to update XML Document #" + accnum);
1752
      }        
1753
  
1754
      DocumentIdentifier id = new DocumentIdentifier(accnum);
1755
      String updaterev = id.getRev();
1756
      String server=MetacatReplication.getServerNameForServerCode(serverCode);
1757
      MetacatReplication.replLog("attempting to lock " + accnum);
1758
      URL u = new URL("https://" + server + "?server="+
1759
        MetaCatUtil.getLocalReplicationServerName()+"&action=getlock&updaterev=" 
1760
           +updaterev + "&docid=" + docid);
1761
      //System.out.println("sending message: " + u.toString());
1762
      String serverResStr = MetacatReplication.getURLContent(u);
1763
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
1764
      if(openingtag.equals("<lockgranted>"))
1765
      {//the lock was granted go ahead with the insert
1766
        XMLReader parser = null;
1767
        try 
1768
        {
1769
          //System.out.println("In lockgranted");
1770
          MetacatReplication.replLog("lock granted for " + accnum + " from " +
1771
                                      server);
1772
          /*XMLReader parser = initializeParser(conn, action, docid, updaterev,
1773
                               validate, user, groups, pub, serverCode, dtd);*/
1774
          parser = initializeParser(conn, action, docid, updaterev,
1775
                                        user, groups, pub, serverCode, 
1776
                                        dtd,ruleBase, needValidation); 
1777
          conn.setAutoCommit(false);
1778
          parser.parse(new InputSource(xml)); 
1779
          conn.commit();
1780
          conn.setAutoCommit(true);
1781
        } 
1782
        catch (Exception e) 
1783
        {
1784
          conn.rollback();
1785
          conn.setAutoCommit(true);
1786
          //if it is a eml2 document, we need delete online data
1787
          if ( parser != null)
1788
          {
1789
            ContentHandler handler = parser.getContentHandler();
1790
            if (handler instanceof EmlSAXHandler)
1791
            {
1792
              EmlSAXHandler eml = (EmlSAXHandler) handler;
1793
              eml.deleteInlineFiles();
1794
            }
1795
          }
1796
          throw e;
1797
        }
1798
        // run write into access db base one relation table and access object 
1799
        runRelationAndAccessHandler(accnum, user, groups, serverCode);
1800
        
1801
        // Force replication the docid
1802
        ForceReplicationHandler frh = new ForceReplicationHandler
1803
                                                          (accnum, true, null);
1804
        return(accnum);
1805
   
1806
      }
1807

    
1808
      else if(openingtag.equals("<filelocked>"))
1809
      {//the file is currently locked by another user
1810
       //notify our user to wait a few minutes, check out a new copy and try
1811
       //again.
1812
        //System.out.println("file locked");
1813
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1814
                                   server + " reason: file already locked");
1815
        throw new Exception("The file specified is already locked by another " +
1816
                            "user.  Please wait 30 seconds, checkout the " +
1817
                            "newer document, merge your changes and try " +
1818
                            "again.");
1819
      }
1820
      else if(openingtag.equals("<outdatedfile>"))
1821
      {//our file is outdated.  notify our user to check out a new copy of the
1822
       //file and merge his version with the new version.
1823
        //System.out.println("outdated file");
1824
        MetacatReplication.replLog("lock denied for " + accnum + " on " +
1825
                                    server + " reason: local file outdated");
1826
        throw new Exception("The file you are trying to update is an outdated" +
1827
                            " version.  Please checkout the newest document, " +
1828
                            "merge your changes and try again.");
1829
      }
1830
    }
1831
    
1832
    if ( action.equals("UPDATE") ) {
1833
      // check for 'write' permission for 'user' to update this document
1834

    
1835
      if ( !hasWritePermission(user, groups, docid) ) {
1836
        throw new Exception("User " + user + 
1837
              " does not have permission to update XML Document #" + accnum);
1838
      }          
1839
    }
1840
    XMLReader parser = null;
1841
    try 
1842
    { 
1843
      
1844
      parser = initializeParser(conn, action, docid, rev, 
1845
                                          user, groups, pub, serverCode, 
1846
                                          dtd, ruleBase, needValidation);
1847
   
1848
      conn.setAutoCommit(false);
1849
      parser.parse(new InputSource(xml));
1850
      conn.commit();
1851
      conn.setAutoCommit(true);
1852
    } 
1853
    catch (Exception e) 
1854
    {
1855
      conn.rollback();
1856
      conn.setAutoCommit(true);
1857
       //if it is a eml2 document, we need delete online data
1858
       if ( parser != null)
1859
       {
1860
          ContentHandler handler = parser.getContentHandler();
1861
          if (handler instanceof EmlSAXHandler)
1862
          {
1863
            EmlSAXHandler eml = (EmlSAXHandler) handler;
1864
            eml.deleteInlineFiles();
1865
          }
1866
       }
1867
      throw e;
1868
    }
1869
    
1870
    // run access db base on relation table and access object       
1871
    runRelationAndAccessHandler(accnum, user, groups, serverCode);
1872
    
1873
    // Force replicate out the new document to each server in our server list.
1874
    // Start the thread to replicate this new document out to the other servers
1875
    // true mean it is xml document
1876
    // null is because no metacat notify the force replication.
1877
    ForceReplicationHandler frh = new ForceReplicationHandler
1878
                                                  (accnum, action, true, null);
1879
      
1880
   
1881
    MetaCatUtil.debugMessage("Conn Usage count after writting: "
1882
                                                      +conn.getUsageCount(),50); 
1883
    return(accnum);
1884
  }
1885

    
1886
  /**
1887
   * Write an XML file to the database during replication
1888
   *
1889
   * @param conn the JDBC connection to the database
1890
   * @param xml the xml stream to be loaded into the database
1891
   * @param pub flag for public "read" access on xml document
1892
   * @param dtd the dtd to be uploaded on server's file system
1893
   * @param action the action to be performed (INSERT or UPDATE)
1894
   * @param accnum the docid + rev# to use on INSERT or UPDATE
1895
   * @param user the user that owns the document
1896
   * @param groups the groups to which user belongs
1897
   * @param homeServer the name of server which the document origanlly create
1898
   * @param validate, if the xml document is valid or not
1899
   * @param notifyServer, the server which notify local server the force 
1900
   *                       replication command
1901
   */
1902

    
1903
  public static String writeReplication(DBConnection conn, Reader xml, 
1904
                                        String pub, Reader dtd, String action, 
1905
                                        String accnum, String user,
1906
                                        String[] groups,String homeServer, 
1907
                                        String notifyServer,
1908
                                        String ruleBase, boolean needValidation)
1909
                                        throws Exception
1910
  {
1911
    // Docid without revision
1912
    String docid=MetaCatUtil.getDocIdFromString(accnum);
1913
    // Revision specified by user (int)
1914
    int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
1915
    // Revision for this docid in current database
1916
    int revInDataBase=getLatestRevisionNumber(docid);
1917
    // String to store the revision
1918
    String rev = null;
1919
   
1920
    
1921
    //revIndataBase=-1, there is no record in xml_documents table
1922
    //the document is a new one for local server, inert it into table
1923
    //user specified rev should be great than 0
1924
    if (revInDataBase==-1 && userSpecifyRev>0 )
1925
    {
1926
        // rev equals user specified
1927
        rev=(new Integer(userSpecifyRev)).toString();
1928
        // action should be INSERT
1929
        action = "INSERT";
1930
    }
1931
    //rev is greater the last revsion number and revInDataBase isn't -1
1932
    // it is a updated  file
1933
    else if (userSpecifyRev>revInDataBase && revInDataBase>0)
1934
    {
1935
       // rev equals user specified
1936
       rev=(new Integer(userSpecifyRev)).toString();
1937
       // action should be update
1938
       action = "UPDATE";
1939
    }
1940
    // local server has newer version, then notify the remote server
1941
    else if ( userSpecifyRev < revInDataBase && revInDataBase > 0)
1942
    {
1943
      throw new Exception("Local server: "+MetaCatUtil.getOption("server")+
1944
                " has newer revision of doc: "+docid+"."+revInDataBase
1945
                 +". Please notify it.");
1946
    }
1947
    //other situation
1948
    else
1949
    {
1950
        
1951
        throw new Exception("The docid"+docid+"'s revision number couldn't be "
1952
                    +userSpecifyRev);
1953
    }
1954
    // Variable to store homeserver code
1955
    int serverCode=-2;
1956
    
1957
     // If server is not int the xml replication talbe, insert it into
1958
    // xml_replication table
1959
    //serverList.addToServerListIfItIsNot(homeServer);
1960
    insertServerIntoReplicationTable(homeServer);
1961
    // Get server code again
1962
    serverCode = getServerCode(homeServer);
1963
    
1964
    
1965
    MetaCatUtil.debugMessage("Document "+docid+"."+rev+" "+action+ " into local"
1966
                               +" metacat with servercode: "+ serverCode, 10);
1967
                        
1968
  
1969
    // insert into xml_nodes and xml_index table
1970
    XMLReader parser = null;
1971
    try 
1972
    { 
1973
      
1974
      parser = initializeParser(conn, action, docid, rev,
1975
                                          user, groups, pub, serverCode, dtd,
1976
                                          ruleBase, needValidation);
1977
      conn.setAutoCommit(false);
1978
      parser.parse(new InputSource(xml));
1979
      conn.commit();
1980
      conn.setAutoCommit(true);
1981
    } 
1982
    catch (Exception e) 
1983
    {
1984
      conn.rollback();
1985
      conn.setAutoCommit(true);
1986
      if ( parser != null)
1987
      {
1988
          ContentHandler handler = parser.getContentHandler();
1989
          if (handler instanceof EmlSAXHandler)
1990
          {
1991
            EmlSAXHandler eml = (EmlSAXHandler) handler;
1992
            eml.deleteInlineFiles();
1993
          }
1994
       }
1995
      throw e;
1996
    }
1997
    
1998
    // run write into access db base on relation table and access rule
1999
    try
2000
    {       
2001
      runRelationAndAccessHandler(accnum, user, groups, serverCode);
2002
    }
2003
    catch (Exception ee)
2004
    {
2005
      MetacatReplication.replErrorLog("Failed to " + 
2006
                                       "create access " + 
2007
                                       "rule for package: " + accnum +
2008
                                       " because " +ee.getMessage());
2009
      MetaCatUtil.debugMessage("Failed to  " + 
2010
                                       "create access " + 
2011
                                       "rule for package: "+ accnum +
2012
                                       " because " +ee.getMessage(), 30);
2013
    }
2014
    //Force replication to other server
2015
    ForceReplicationHandler forceReplication = new ForceReplicationHandler
2016
                                  (accnum, action, true, notifyServer);
2017
    
2018

    
2019
    return(accnum);
2020
  }
2021
  
2022
  /* Running write record to xml_relation and xml_access*/
2023
  private static void runRelationAndAccessHandler(String accnumber, 
2024
                                                  String userName, 
2025
                                                  String[]group, int servercode) 
2026
                                                   throws Exception
2027
  {
2028
    DBConnection dbconn = null;
2029
    int serialNumber = -1;
2030
    PreparedStatement pstmt =null;
2031
    String documenttype = getDocTypeFromDBForCurrentDocument(accnumber);
2032
    try
2033
    {
2034
      String packagedoctype = MetaCatUtil.getOption("packagedoctype");
2035
      Vector packagedoctypes = new Vector();
2036
      packagedoctypes = MetaCatUtil.getOptionList(packagedoctype);
2037
      String docIdWithoutRev = MetaCatUtil.getDocIdFromString(accnumber);
2038
      if (documenttype != null && packagedoctypes.contains(documenttype) )
2039
      {
2040
        dbconn=DBConnectionPool.
2041
           getDBConnection("DocumentImpl.runRelationAndAccessHandeler");
2042
        serialNumber=dbconn.getCheckOutSerialNumber();
2043
        dbconn.setAutoCommit(false);
2044
        // from the relations get the access file id for that package
2045
        String aclid = RelationHandler.getAccessFileID(docIdWithoutRev);
2046
        // if there are access file, write ACL for that package
2047
        if ( aclid != null ) 
2048
        {
2049
          runAccessControlList(dbconn, aclid, userName, group, servercode);
2050
        }
2051
        dbconn.commit();
2052
        dbconn.setAutoCommit(true);
2053
      }
2054
        // if it is an access file
2055
      else if ( documenttype != null && MetaCatUtil.getOptionList(
2056
                 MetaCatUtil.getOption("accessdoctype")).contains(documenttype))
2057
      {
2058
        dbconn=DBConnectionPool.
2059
           getDBConnection("DocumentImpl.runRelationAndAccessHandeler");
2060
        serialNumber=dbconn.getCheckOutSerialNumber();
2061
        dbconn.setAutoCommit(false);
2062
        // write ACL for the package
2063
        runAccessControlList(dbconn, docIdWithoutRev, 
2064
                             userName, group, servercode);
2065
        dbconn.commit();
2066
        dbconn.setAutoCommit(true);
2067
        
2068
      }
2069
      
2070
    } 
2071
    catch (Exception e) 
2072
    {
2073
      if( dbconn != null)
2074
      {
2075
        dbconn.rollback();
2076
        dbconn.setAutoCommit(true);
2077
      }
2078
      MetaCatUtil.debugMessage("Error in DocumentImple.runRelationAndAccessHandler " +
2079
                                e.getMessage(), 30);
2080
      throw e;
2081
    }
2082
    finally
2083
    {
2084
      if (dbconn != null)
2085
      {
2086
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
2087
      }
2088
    }//
2089
  }
2090
  
2091
  // It runs in xmlIndex thread. It writes ACL for a package.
2092
  private static void runAccessControlList (DBConnection conn, String aclid, 
2093
                                    String users, String[]group, int servercode)
2094
                                                throws Exception
2095
  {
2096
    // read the access file from xml_nodes
2097
    // parse the access file and store the access info into xml_access
2098
    AccessControlList aclobj =
2099
    new AccessControlList(conn, aclid, users, group, servercode);
2100
   
2101
  }
2102
  
2103
  /* Method get document type from db*/
2104
  private static String getDocTypeFromDBForCurrentDocument(String accnumber)
2105
                                                  throws SQLException
2106
  {
2107
    String docoumentType = null;
2108
    String docid = null;
2109
    PreparedStatement pstate = null;
2110
    ResultSet rs = null;
2111
    String sql = "SELECT doctype FROM xml_documents where docid = ?";
2112
    DBConnection dbConnection = null;
2113
    int serialNumber = -1;
2114
    try
2115
    {
2116
      //get rid of revision number
2117
      docid = MetaCatUtil.getDocIdFromString(accnumber);
2118
      dbConnection=DBConnectionPool.
2119
           getDBConnection("DocumentImpl.getDocTypeFromDBForCurrentDoc");
2120
      serialNumber=dbConnection.getCheckOutSerialNumber();
2121
      pstate = dbConnection.prepareStatement(sql);
2122
      //bind variable
2123
      pstate.setString(1, docid);
2124
      //excute query
2125
      pstate.execute();
2126
      //handle resultset
2127
      rs = pstate.getResultSet();
2128
      if (rs.next())
2129
      {
2130
        docoumentType = rs.getString(1);
2131
      }
2132
      rs.close();
2133
      pstate.close();
2134
    }//try
2135
    catch (SQLException e)
2136
    {
2137
      MetaCatUtil.debugMessage("error in DocumentImpl."+
2138
                      "getDocTypeFromDBForCurrentDocument "+e.getMessage(), 30);
2139
      throw e;
2140
    }//catch
2141
    finally
2142
    {
2143
      pstate.close();
2144
      DBConnectionPool.returnDBConnection(dbConnection, serialNumber);
2145
    }//
2146
    MetaCatUtil.debugMessage("The current doctype from db is: "+
2147
                              docoumentType, 35);
2148
    return docoumentType;
2149
  }
2150
  /**
2151
   * Delete an XML file from the database (actually, just make it a revision
2152
   * in the xml_revisions table)
2153
   *
2154
   * @param docid the ID of the document to be deleted from the database
2155
   */
2156
  public static void delete(String accnum,
2157
                                 String user, String[] groups )
2158
                throws Exception 
2159
  {
2160
    // OLD
2161
    //DocumentIdentifier id = new DocumentIdentifier(accnum);
2162
    //String docid = id.getIdentifier();
2163
    //String rev = id.getRev();
2164
    
2165
    // OLD
2166
    // Determine if the docid,rev are OK for DELETE
2167
    //AccessionNumber ac = new AccessionNumber(conn);
2168
    //docid = ac.generate(docid, rev, "DELETE");
2169
    DBConnection conn = null;
2170
    int serialNumber = -1;
2171
    PreparedStatement pstmt =null;
2172
    try
2173
    {
2174
      //check out DBConnection
2175
      conn=DBConnectionPool.
2176
                    getDBConnection("DocumentImpl.delete");
2177
      serialNumber=conn.getCheckOutSerialNumber();
2178

    
2179
      // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
2180
      AccessionNumber ac = new AccessionNumber(accnum, "DELETE");
2181
      String docid = ac.getDocid();
2182
      String rev = ac.getRev();
2183
    
2184
      MetaCatUtil.debugMessage("Start deleting doc "+docid+ "...", 20);
2185
    // check for 'write' permission for 'user' to delete this document
2186
      if ( !hasWritePermission(user, groups, docid) ) {
2187
        throw new Exception("User " + user + 
2188
              " does not have permission to delete XML Document #" + accnum);
2189
      }
2190

    
2191
      conn.setAutoCommit(false);
2192
      // Copy the record to the xml_revisions table
2193
      DocumentImpl.archiveDocRevision(conn, docid, user );
2194

    
2195
      // Now delete it from the xml_index table
2196
      pstmt = conn.prepareStatement("DELETE FROM xml_index WHERE docid = ?");
2197
      pstmt.setString(1,docid);
2198
      pstmt.execute();
2199
      pstmt.close();
2200
      conn.increaseUsageCount(1);
2201
      
2202
      //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
2203
      // Now delete it from xml_access table
2204
      pstmt = conn.
2205
              prepareStatement("DELETE FROM xml_access WHERE accessfileid = ?");
2206
      pstmt.setString(1, docid);
2207
      pstmt.execute();
2208
      pstmt.close();
2209
      conn.increaseUsageCount(1);
2210
      
2211
      // Delete it from relation table
2212
      pstmt = conn.
2213
               prepareStatement("DELETE FROM xml_relation WHERE docid = ?");
2214
      //increase usage count
2215
      conn.increaseUsageCount(1);
2216
      pstmt.setString(1, docid);
2217
      pstmt.execute();
2218
      pstmt.close();
2219
      
2220
      // Delete it from xml_accesssubtree table
2221
      pstmt = conn.
2222
               prepareStatement("DELETE FROM xml_accesssubtree WHERE docid = ?");
2223
      //increase usage count
2224
      conn.increaseUsageCount(1);
2225
      pstmt.setString(1, docid);
2226
      pstmt.execute();
2227
      pstmt.close();
2228
      
2229
      // Delete it from xml_doucments table
2230
      pstmt =conn.prepareStatement("DELETE FROM xml_documents WHERE docid = ?");
2231
      pstmt.setString(1, docid);
2232
      pstmt.execute();
2233
      pstmt.close();
2234
      //Usaga count increase 1
2235
      conn.increaseUsageCount(1);
2236
      
2237
      conn.commit();
2238
      conn.setAutoCommit(true);
2239
    }//try
2240
    catch (Exception e)
2241
    {
2242
      MetaCatUtil.debugMessage("error in DocumentImpl.delete: " + 
2243
                                e.getMessage(), 30);
2244
      throw e;
2245
    }
2246
    finally
2247
    {
2248
      
2249
      try
2250
      {
2251
        // close preparedStatement
2252
        pstmt.close();
2253
      }//try
2254
      finally
2255
      {
2256
        //check in DBonnection
2257
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2258
      }//finally
2259
    }//finally
2260
    //IF this is a package document:
2261
    //delete all of the relations that this document created.
2262
    //if the deleted document is a package document its relations should 
2263
    //no longer be active if it has been deleted from the system.
2264
    
2265
  }
2266

    
2267
  /** 
2268
    * Check for "WRITE" permission on @docid for @user and/or @groups 
2269
    * from DB connection 
2270
    */
2271
  private static boolean hasWritePermission (String user,
2272
                                  String[] groups, String docid ) 
2273
                  throws SQLException, Exception
2274
  {
2275
    // Check for WRITE permission on @docid for @user and/or @groups
2276
    PermissionController controller = new PermissionController(docid);
2277
    return controller.hasPermission(user,groups,
2278
                                    AccessControlInterface.WRITESTRING);
2279
  }
2280

    
2281
  /** 
2282
    * Check for "READ" permission base on docid, user and group
2283
    *@docid, the document
2284
    *@user, user name
2285
    *@group, user's group
2286
    * 
2287
    */
2288
  public static boolean hasReadPermission (String user,
2289
                                  String[] groups, String docId ) 
2290
                  throws SQLException, Exception
2291
  {
2292
    // Check for READ permission on @docid for @user and/or @groups
2293
    PermissionController controller = 
2294
                        new PermissionController(docId);
2295
    return controller.hasPermission(user,groups,
2296
                                            AccessControlInterface.READSTRING);
2297
  }  
2298

    
2299
  
2300
   /**
2301
   * Set up the parser handlers for writing the document to the database
2302
   */
2303
  private static XMLReader initializeParser(DBConnection dbconn, String action,
2304
                                            String docid, String rev, 
2305
                                            String user, 
2306
                                            String[] groups, String pub, 
2307
                                            int serverCode, Reader dtd,
2308
                                            String ruleBase, 
2309
                                            boolean needValidation) 
2310
                                            throws Exception 
2311
  {
2312
    XMLReader parser = null;
2313
    try 
2314
    {
2315
      // handler
2316
      ContentHandler chandler;
2317
      EntityResolver eresolver;
2318
      DTDHandler dtdhandler;  
2319
      // Get an instance of the parser
2320
      String parserName = MetaCatUtil.getOption("saxparser");
2321
      parser = XMLReaderFactory.createXMLReader(parserName);
2322
      if (ruleBase != null && ruleBase.equals(EML2))
2323
      {
2324
        MetaCatUtil.debugMessage("eml 2 parser", 20);
2325
        chandler = new EmlSAXHandler(dbconn, action, 
2326
                                    docid, rev, user, groups, pub, serverCode);
2327
        parser.setContentHandler((ContentHandler)chandler);
2328
        parser.setErrorHandler((ErrorHandler)chandler);
2329
        parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2330
        parser.setProperty(LEXICALPROPERTY, chandler);
2331
        // turn on schema validation feature
2332
        parser.setFeature(VALIDATIONFEATURE, true);
2333
        parser.setFeature(NAMESPACEFEATURE, true);
2334
        //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2335
        parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2336
        // From DB to find the register external schema location
2337
        String externalSchemaLocation = null;
2338
        SchemaLocationResolver resolver = new SchemaLocationResolver();
2339
        externalSchemaLocation = resolver.getNameSpaceAndLocationString();
2340
        // Set external schemalocation.
2341
        if (externalSchemaLocation != null && 
2342
            !(externalSchemaLocation.trim()).equals(""))
2343
        {
2344
            parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2345
                             externalSchemaLocation);
2346
        }
2347
      }
2348
      else
2349
      {
2350
        //create a DBSAXHandler object which has the revision specification
2351
        chandler = new DBSAXHandler(dbconn, action, 
2352
                                    docid, rev, user, groups, pub, serverCode);
2353
        parser.setContentHandler((ContentHandler)chandler);
2354
        parser.setErrorHandler((ErrorHandler)chandler);
2355
        parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2356
        parser.setProperty(LEXICALPROPERTY, chandler);
2357
      
2358
        if (ruleBase != null && ruleBase.equals(SCHEMA) && needValidation)
2359
        {
2360
          MetaCatUtil.debugMessage("General schema parser", 20);
2361
          // turn on schema validation feature
2362
          parser.setFeature(VALIDATIONFEATURE, true);
2363
          parser.setFeature(NAMESPACEFEATURE, true);
2364
          //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2365
          parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2366
          // From DB to find the register external schema location
2367
          String externalSchemaLocation = null;
2368
          SchemaLocationResolver resolver = new SchemaLocationResolver();
2369
          externalSchemaLocation = resolver.getNameSpaceAndLocationString();
2370
          // Set external schemalocation.
2371
          if (externalSchemaLocation != null && 
2372
            !(externalSchemaLocation.trim()).equals(""))
2373
          {
2374
            parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2375
                             externalSchemaLocation);
2376
          }
2377
     
2378
        }
2379
        else if (ruleBase != null && ruleBase.equals(DTD) && needValidation)
2380
        {
2381
          MetaCatUtil.debugMessage("dtd parser", 20);
2382
          // turn on dtd validaton feature
2383
          parser.setFeature(VALIDATIONFEATURE, true);
2384
          eresolver= new DBEntityResolver(dbconn, (DBSAXHandler)chandler, dtd);
2385
          dtdhandler = new DBDTDHandler(dbconn);
2386
          parser.setEntityResolver((EntityResolver)eresolver);
2387
          parser.setDTDHandler((DTDHandler)dtdhandler);
2388
        }
2389
        else
2390
        {
2391
          MetaCatUtil.debugMessage("other parser", 20);
2392
          // non validation
2393
          parser.setFeature(VALIDATIONFEATURE, false);
2394
          eresolver= new DBEntityResolver(dbconn, (DBSAXHandler)chandler, dtd);
2395
          dtdhandler = new DBDTDHandler(dbconn);
2396
          parser.setEntityResolver((EntityResolver)eresolver);
2397
          parser.setDTDHandler((DTDHandler)dtdhandler);
2398
        }
2399
      }//else
2400
    } 
2401
    catch (Exception e) 
2402
    {
2403
      throw e;
2404
    }
2405
    return parser;
2406
  }
2407

    
2408
  
2409
  /**
2410
   * Set up the parser handlers for writing the document to the database
2411
   */
2412
  /*private static XMLReader initializeParser(DBConnection dbconn, String action,
2413
                               String docid, String rev, boolean validate, 
2414
                                   String user, String[] groups, String pub, 
2415
                                   int serverCode, Reader dtd) 
2416
                           throws Exception 
2417
  {
2418
    XMLReader parser = null;
2419
    //DBConnection conn = null;
2420
    //int serialNumber = -1;
2421
    //
2422
    // Set up the SAX document handlers for parsing
2423
    //
2424
    try {
2425
       //check out DBConnection
2426
     
2427
      //create a DBSAXHandler object which has the revision specification
2428
      ContentHandler chandler = new DBSAXHandler(dbconn, action, 
2429
                                    docid, rev, user, groups, pub, serverCode);
2430
      EntityResolver eresolver= new DBEntityResolver(dbconn,
2431
                                                 (DBSAXHandler)chandler, dtd);
2432
      DTDHandler dtdhandler   = new DBDTDHandler(dbconn);
2433

    
2434
      // Get an instance of the parser
2435
      String parserName = MetaCatUtil.getOption("saxparser");
2436
      parser = XMLReaderFactory.createXMLReader(parserName);
2437

    
2438
      // Turn on validation
2439
      parser.setFeature("http://xml.org/sax/features/validation", validate);
2440
      // Turn off Including all external parameter entities
2441
      // (the external DTD subset also)
2442
      // Doesn't work well, probably the feature name is not correct
2443
      // parser.setFeature(
2444
      //  "http://xml.org/sax/features/external-parameter-entities", false);
2445
      
2446
      // Set Handlers in the parser
2447
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
2448
                         chandler);
2449
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
2450
                         chandler);
2451
      parser.setContentHandler((ContentHandler)chandler);
2452
      parser.setEntityResolver((EntityResolver)eresolver);
2453
      parser.setDTDHandler((DTDHandler)dtdhandler);
2454
      parser.setErrorHandler((ErrorHandler)chandler);
2455

    
2456
    } catch (Exception e) {
2457
      throw e;
2458
    }
2459
    //finally
2460
    //{
2461
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
2462
    //}
2463

    
2464
    return parser;
2465
  }*/
2466

    
2467
  /**
2468
   * Save a document entry in the xml_revisions table 
2469
   * Connection use as a paramter is in order to rollback feature
2470
   */
2471
  private static void archiveDocRevision(DBConnection dbconn, String docid, 
2472
                                                    String user) 
2473
 {
2474
    String sysdate = dbAdapter.getDateTimeFunction();
2475
    //DBConnection conn = null;
2476
    //int serialNumber = -1;
2477
    PreparedStatement pstmt = null;
2478
    
2479
    // create a record in xml_revisions table 
2480
    // for that document as selected from xml_documents
2481
   
2482
   try
2483
   {
2484
     //check out DBConnection
2485
     /*conn=DBConnectionPool.
2486
                    getDBConnection("DocumentImpl.archiveDocRevision");
2487
     serialNumber=conn.getCheckOutSerialNumber();*/
2488
     pstmt = dbconn.prepareStatement(
2489
      "INSERT INTO xml_revisions " +
2490
        "(docid, rootnodeid, docname, doctype, " +
2491
        "user_owner, user_updated, date_created, date_updated, " +
2492
        "server_location, rev, public_access, catalog_id) " +
2493
      "SELECT ?, rootnodeid, docname, doctype, " + 
2494
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
2495
        "server_location, rev, public_access, catalog_id " +
2496
      "FROM xml_documents " +
2497
      "WHERE docid = ?");
2498
      // Increase dbconnection usage count
2499
      dbconn.increaseUsageCount(1);
2500
      // Bind the values to the query and execute it
2501
      pstmt.setString(1, docid);
2502
      pstmt.setString(2, user);
2503
      pstmt.setString(3, docid);
2504
      pstmt.execute();
2505
      pstmt.close();
2506
   }//try
2507
   catch (SQLException e)
2508
   {
2509
     MetaCatUtil.debugMessage("Error in DocumentImpl.archiveDocRevision : "+
2510
                                e.getMessage(), 30);
2511
   }//catch
2512
   finally
2513
   {
2514
     try
2515
     {
2516
       pstmt.close();
2517
     }//try
2518
     catch (SQLException ee)
2519
     {
2520
       MetaCatUtil.debugMessage("Error in DocumnetImpl.archiveDocRevision: "+
2521
                                  ee.getMessage(), 50);
2522
     }//catch
2523
     //finally
2524
     //{
2525
       
2526
       //check in DBConnection
2527
       //DBConnectionPool.returnDBConnection(conn, serialNumber);
2528
     //}//finally
2529
   }//finnally
2530
                                  
2531

    
2532
  }//achiveDocRevision
2533
  
2534
  /** Save a document entry in the xml_revisions table */
2535
  private static void archiveDocRevision(String docid, String user) 
2536
 {
2537
    String sysdate = dbAdapter.getDateTimeFunction();
2538
    DBConnection conn = null;
2539
    int serialNumber = -1;
2540
    PreparedStatement pstmt = null;
2541
    
2542
    // create a record in xml_revisions table 
2543
    // for that document as selected from xml_documents
2544
   
2545
   try
2546
   {
2547
     //check out DBConnection
2548
     conn=DBConnectionPool.
2549
                    getDBConnection("DocumentImpl.archiveDocRevision");
2550
     serialNumber=conn.getCheckOutSerialNumber();
2551
     pstmt = conn.prepareStatement(
2552
      "INSERT INTO xml_revisions " +
2553
        "(docid, rootnodeid, docname, doctype, " +
2554
        "user_owner, user_updated, date_created, date_updated, " +
2555
        "server_location, rev, public_access, catalog_id) " +
2556
      "SELECT ?, rootnodeid, docname, doctype, " + 
2557
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
2558
        "server_location, rev, public_access, catalog_id " +
2559
      "FROM xml_documents " +
2560
      "WHERE docid = ?");
2561
      // Bind the values to the query and execute it
2562
      pstmt.setString(1, docid);
2563
      pstmt.setString(2, user);
2564
      pstmt.setString(3, docid);
2565
      pstmt.execute();
2566
      pstmt.close();
2567
   }//try
2568
   catch (SQLException e)
2569
   {
2570
     MetaCatUtil.debugMessage("Error in DocumentImpl.archiveDocRevision : "+
2571
                                e.getMessage(), 30);
2572
   }//catch
2573
   finally
2574
   {
2575
     try
2576
     {
2577
       pstmt.close();
2578
     }//try
2579
     catch (SQLException ee)
2580
     {
2581
       MetaCatUtil.debugMessage("Error in DocumnetImpl.archiveDocRevision: "+
2582
                                  ee.getMessage(), 50);
2583
     }//catch
2584
     finally
2585
     {
2586
       //check in DBConnection
2587
       DBConnectionPool.returnDBConnection(conn, serialNumber);
2588
     }//finally
2589
   }//finnally
2590
                                  
2591

    
2592
  }//achiveDocRevision
2593
  
2594
  /**
2595
    * delete a entry in xml_table for given docid
2596
    * @param docId, the id of the document need to be delete
2597
    */
2598
  private static void deleteXMLDocuments(String docId) 
2599
                                         throws SQLException 
2600
  {
2601
    DBConnection conn = null;
2602
    int serialNumber = -1;
2603
    PreparedStatement pStmt = null;
2604
    try
2605
    {
2606
      //check out DBConnection
2607
      conn=DBConnectionPool.
2608
                    getDBConnection("DocumentImpl.deleteXMLDocuments");
2609
      serialNumber=conn.getCheckOutSerialNumber();
2610
      //delete a record 
2611
      pStmt = 
2612
             conn.prepareStatement("DELETE FROM xml_documents WHERE docid = '" 
2613
                                              + docId + "'");
2614
    pStmt.execute();
2615
    }//try
2616
    finally
2617
    {
2618
      try
2619
      {
2620
        pStmt.close();
2621
      }//try
2622
      catch (SQLException e)
2623
      {
2624
        MetaCatUtil.debugMessage("error in DocumentImpl.deleteXMLDocuments: "+
2625
                                  e.getMessage(), 50);
2626
      }//catch
2627
      finally
2628
      {
2629
        //return back DBconnection
2630
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2631
      }//finally
2632
    }//finally
2633
      
2634

    
2635
  }//deleteXMLDocuments
2636
  
2637
  /**
2638
    * Get last revision number from database for a docid
2639
    * If couldn't find an entry,  -1 will return
2640
    * The return value is integer because we want compare it to there new one
2641
    * @param docid <sitecode>.<uniqueid> part of Accession Number
2642
    */
2643
  public static int getLatestRevisionNumber(String docId)
2644
                                      throws SQLException
2645
  {
2646
    int rev = 1;
2647
    PreparedStatement pStmt = null;
2648
    DBConnection dbConn = null;
2649
    int serialNumber = -1;
2650
    // get rid of rev
2651
    docId = MetaCatUtil.getDocIdFromString(docId);
2652
    try
2653
    {
2654
      //check out DBConnection
2655
      dbConn=DBConnectionPool.
2656
                    getDBConnection("DocumentImpl.getLatestRevisionNumber");
2657
      serialNumber=dbConn.getCheckOutSerialNumber();
2658
     
2659
      pStmt = dbConn.prepareStatement
2660
              ("SELECT rev FROM xml_documents WHERE docid='" + docId + "'");
2661
      pStmt.execute();
2662

    
2663
      ResultSet rs = pStmt.getResultSet();
2664
      boolean hasRow = rs.next();
2665
      if (hasRow)
2666
      {
2667
        rev = rs.getInt(1);
2668
        pStmt.close();
2669
      }
2670
      else
2671
      {
2672
        rev=-1;
2673
        pStmt.close();
2674
      }
2675
    }//try
2676
    finally
2677
    {
2678
      try
2679
      {
2680
        pStmt.close();
2681
      }
2682
      catch (Exception ee)
2683
      {
2684
        MetaCatUtil.debugMessage("Error in DocumentImpl."+
2685
                        "getLatestRevisionNumber: "+ee.getMessage(), 50);
2686
      }
2687
      finally
2688
      {
2689
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2690
      }
2691
    }//finally  
2692
      
2693
    return rev;
2694
  }//getLatestRevisionNumber
2695
  
2696
  /**
2697
   * Get server location form database for a accNum
2698
   * 
2699
   * @param accum <sitecode>.<uniqueid>.<rev>
2700
   */
2701
  private static int getServerLocationNumber(String accNum)
2702
                                            throws SQLException
2703
  {
2704
    //get rid of revNum part
2705
    String docId=MetaCatUtil.getDocIdFromString(accNum);
2706
    PreparedStatement pStmt = null;
2707
    int serverLocation = 1;
2708
    DBConnection conn = null;
2709
    int serialNumber = -1;
2710
    
2711
    try
2712
    {
2713
      //check out DBConnection
2714
      conn=DBConnectionPool.
2715
                    getDBConnection("DocumentImpl.getServerLocationNumber");
2716
      serialNumber=conn.getCheckOutSerialNumber();
2717
     
2718
      pStmt = conn.prepareStatement
2719
      ("SELECT server_location FROM xml_documents WHERE docid='" + docId + "'");
2720
      pStmt.execute();
2721

    
2722
      ResultSet rs = pStmt.getResultSet();
2723
      boolean hasRow = rs.next();
2724
      //if there is entry in xml_documents, get the serverlocation
2725
      if (hasRow)
2726
      {
2727
        serverLocation = rs.getInt(1);
2728
        pStmt.close();
2729
      }
2730
      else
2731
      {
2732
        //if htere is no entry in xml_documents, we consider it is new document
2733
        //the server location is local host and value is 1
2734
        serverLocation=1;
2735
        pStmt.close();
2736
      }
2737
    }//try
2738
    finally
2739
    {
2740
      try
2741
      {
2742
        pStmt.close();
2743
      }//try
2744
      catch (Exception ee)
2745
      {
2746
        MetaCatUtil.debugMessage("Error in DocumentImpl.getServerLocationNu(): "
2747
                                    +ee.getMessage(), 50);
2748
      }//catch
2749
      finally
2750
      {
2751
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2752
      }//finally
2753
    }//finally
2754
      
2755
    return serverLocation;
2756
  }
2757
  
2758
  /**
2759
   * Given a server name, return its servercode in xml_replication table.
2760
   * If no server is found, -1 will return
2761
   * @param serverName, 
2762
   */
2763
  private static int getServerCode(String serverName) 
2764
  {
2765
    PreparedStatement pStmt=null;
2766
    int serverLocation=-2;
2767
    DBConnection dbConn = null;
2768
    int serialNumber = -1;
2769
    //MetaCatUtil util = new MetaCatUtil();
2770
    
2771
    
2772
    //we should consider about local host too
2773
    if (serverName.equals(MetaCatUtil.getLocalReplicationServerName()))
2774
    { 
2775
      serverLocation=1;
2776
      return serverLocation;
2777
    }
2778
    
2779
   
2780
    try
2781
    {
2782
      //check xml_replication table
2783
      //dbConn=util.openDBConnection();
2784
      //check out DBConnection
2785
      dbConn=DBConnectionPool.getDBConnection("DocumentImpl.getServerCode");
2786
      serialNumber=dbConn.getCheckOutSerialNumber();
2787
      pStmt = dbConn.prepareStatement
2788
      ("SELECT serverid FROM xml_replication WHERE server='" + serverName +"'");
2789
      pStmt.execute();
2790

    
2791
      ResultSet rs = pStmt.getResultSet();
2792
      boolean hasRow = rs.next();
2793
      //if there is entry in xml_replication, get the serverid
2794
      if (hasRow)
2795
      {
2796
        serverLocation = rs.getInt(1);
2797
        pStmt.close();
2798
      }
2799
      else
2800
      {
2801
        // if htere is no entry in xml_replication, -1 will return
2802
        serverLocation=-1;
2803
        pStmt.close();
2804
      }
2805
    }
2806
    catch (Exception e)
2807
    {
2808
      MetaCatUtil.debugMessage("Error in DocumentImpl.getServerCode(): "
2809
                                    +e.getMessage(), 30);
2810
    }
2811
    finally
2812
    {
2813
      try
2814
      {
2815
        pStmt.close();
2816
      }
2817
      catch (Exception ee)
2818
      {
2819
        MetaCatUtil.debugMessage("Error in DocumentImpl.getServerCode(): "
2820
                                    +ee.getMessage(), 50);
2821
      }
2822
      finally
2823
      {
2824
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2825
      }
2826
    }
2827
                 
2828
      
2829
    return serverLocation;
2830
  }
2831
  
2832
  /**
2833
   * Insert a server into xml_replcation table
2834
   * @param server, the name of server 
2835
   */
2836
  private static synchronized void 
2837
                                insertServerIntoReplicationTable(String server)
2838
  {
2839
    PreparedStatement pStmt=null;
2840
    DBConnection dbConn = null;
2841
    int serialNumber = -1;
2842
    
2843
    // Initial value for the server
2844
    int replicate = 0;
2845
    int dataReplicate = 0;
2846
    int hub = 0;
2847
   
2848
    try
2849
    {
2850
       // Get DBConnection
2851
       dbConn=DBConnectionPool.
2852
                getDBConnection("DocumentImpl.insertServIntoReplicationTable");
2853
       serialNumber=dbConn.getCheckOutSerialNumber();
2854
      
2855
      // Compare the server to dabase
2856
      pStmt = dbConn.prepareStatement
2857
      ("SELECT serverid FROM xml_replication WHERE server='" + server +"'");
2858
      pStmt.execute();
2859
      ResultSet rs = pStmt.getResultSet();
2860
      boolean hasRow = rs.next();
2861
      // Close preparedstatement and result set
2862
      pStmt.close();
2863
      rs.close();
2864
      
2865
      // If the server is not in the table, and server is not local host,
2866
      // insert it
2867
      if ( !hasRow 
2868
         && !server.equals(MetaCatUtil.getLocalReplicationServerName()))
2869
      {
2870
        // Set auto commit false
2871
        dbConn.setAutoCommit(false);
2872
        /*pStmt = dbConn.prepareStatement("INSERT INTO xml_replication " +
2873
                      "(server, last_checked, replicate, datareplicate, hub) " +
2874
                       "VALUES ('" + server + "', to_date(" +
2875
                       "'01/01/00', 'MM/DD/YY'), '" +
2876
                       replicate +"', '"+dataReplicate+"','"+ hub + "')");*/
2877
        pStmt = dbConn.prepareStatement("INSERT INTO xml_replication " +
2878
                      "(server, last_checked, replicate, datareplicate, hub) " +
2879
                       "VALUES ('" + server + "', " + 
2880
                       dbAdapter.toDate("01/01/1980", "MM/DD/YYYY") + ", '" +
2881
                       replicate +"', '"+dataReplicate+"','"+ hub + "')");
2882
        
2883
                              
2884
        pStmt.execute();
2885
        dbConn.commit();
2886
        // Increase usage number
2887
        dbConn.increaseUsageCount(1);
2888
        pStmt.close();
2889
        
2890
      }
2891
    }//try
2892
    catch (Exception e)
2893
    {
2894
      MetaCatUtil.debugMessage("Error in DocumentImpl.insertServerIntoRepli(): "
2895
                                    +e.getMessage(), 30);
2896
    }//catch
2897
    finally
2898
    {
2899
     
2900
      try
2901
      {
2902
        // Set auto commit true
2903
        dbConn.setAutoCommit(true);
2904
        pStmt.close();
2905
        
2906
      }//try
2907
      catch (Exception ee)
2908
      {
2909
        MetaCatUtil.debugMessage("Error in DocumentImpl.insetServerIntoRepl(): "
2910
                                    +ee.getMessage(), 50);
2911
      }//catch
2912
      finally
2913
      {
2914
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2915
      }
2916
    
2917
    }//finally
2918

    
2919
  }
2920
  
2921
  
2922
  /**
2923
   * the main routine used to test the DBWriter utility.
2924
   * <p>
2925
   * Usage: java DocumentImpl <-f filename -a action -d docid>
2926
   *
2927
   * @param filename the filename to be loaded into the database
2928
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
2929
   * @param docid the id of the document to process
2930
   */
2931
  static public void main(String[] args) {
2932
    DBConnection dbconn = null;
2933
    int serialNumber = -1;
2934
    try {
2935
      String filename    = null;
2936
      String dtdfilename = null;
2937
      String action      = null;
2938
      String docid       = null;
2939
      boolean showRuntime = false;
2940
      boolean useOldReadAlgorithm = false;
2941

    
2942
      // Parse the command line arguments
2943
      for ( int i=0 ; i < args.length; ++i ) {
2944
        if ( args[i].equals( "-f" ) ) {
2945
          filename =  args[++i];
2946
        } else if ( args[i].equals( "-r" ) ) {
2947
          dtdfilename =  args[++i];
2948
        } else if ( args[i].equals( "-a" ) ) {
2949
          action =  args[++i];
2950
        } else if ( args[i].equals( "-d" ) ) {
2951
          docid =  args[++i];
2952
        } else if ( args[i].equals( "-t" ) ) {
2953
          showRuntime = true;
2954
        } else if ( args[i].equals( "-old" ) ) {
2955
          useOldReadAlgorithm = true;
2956
        } else {
2957
          System.err.println
2958
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
2959
        }
2960
      }
2961
      
2962
      // Check if the required arguments are provided
2963
      boolean argsAreValid = false;
2964
      if (action != null) {
2965
        if (action.equals("INSERT")) {
2966
          if (filename != null) {
2967
            argsAreValid = true;
2968
          } 
2969
        } else if (action.equals("UPDATE")) {
2970
          if ((filename != null) && (docid != null)) {
2971
            argsAreValid = true;
2972
          } 
2973
        } else if (action.equals("DELETE")) {
2974
          if (docid != null) {
2975
            argsAreValid = true;
2976
          } 
2977
        } else if (action.equals("READ")) {
2978
          if (docid != null) {
2979
            argsAreValid = true;
2980
          } 
2981
        } 
2982
      } 
2983

    
2984
      // Print usage message if the arguments are not valid
2985
      if (!argsAreValid) {
2986
        System.err.println("Wrong number of arguments!!!");
2987
        System.err.println(
2988
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
2989
          "[-r dtdfilename]");
2990
        System.err.println(
2991
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
2992
          "[-r dtdfilename]");
2993
        System.err.println(
2994
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
2995
        System.err.println(
2996
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
2997
        return;
2998
      }
2999
      
3000
      // Time the request if asked for
3001
      double startTime = System.currentTimeMillis();
3002
      
3003
      // Open a connection to the database
3004
      MetaCatUtil util = new MetaCatUtil();
3005
     
3006
      dbconn=DBConnectionPool.getDBConnection("DocumentImpl.main");
3007
      serialNumber=dbconn.getCheckOutSerialNumber();
3008

    
3009
      double connTime = System.currentTimeMillis();
3010
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
3011
      if (action.equals("READ")) {
3012
          DocumentImpl xmldoc = new DocumentImpl(docid );
3013
          if (useOldReadAlgorithm) {
3014
            System.out.println(xmldoc.readUsingSlowAlgorithm());
3015
          } else {
3016
            xmldoc.toXml(new PrintWriter(System.out), null, null, true);
3017
          }
3018
      } else if (action.equals("DELETE")) {
3019
        DocumentImpl.delete(docid, null, null);
3020
        System.out.println("Document deleted: " + docid);
3021
      } else {
3022
        /*String newdocid = DocumentImpl.write(dbconn, filename, null,
3023
                                             dtdfilename, action, docid,
3024
                                             null, null);
3025
        if ((docid != null) && (!docid.equals(newdocid))) {
3026
          if (action.equals("INSERT")) {
3027
            System.out.println("New document ID generated!!! ");
3028
          } else if (action.equals("UPDATE")) {
3029
            System.out.println("ERROR: Couldn't update document!!! ");
3030
          }
3031
        } else if ((docid == null) && (action.equals("UPDATE"))) {
3032
          System.out.println("ERROR: Couldn't update document!!! ");
3033
        }
3034
        System.out.println("Document processing finished for: " + filename
3035
              + " (" + newdocid + ")");*/
3036
      }
3037

    
3038
      double stopTime = System.currentTimeMillis();
3039
      double dbOpenTime = (connTime - startTime)/1000;
3040
      double insertTime = (stopTime - connTime)/1000;
3041
      double executionTime = (stopTime - startTime)/1000;
3042
      if (showRuntime) {
3043
        System.out.println("\n\nTotal Execution time was: " + 
3044
                           executionTime + " seconds.");
3045
        System.out.println("Time to open DB connection was: " + dbOpenTime + 
3046
                           " seconds.");
3047
        System.out.println("Time to insert document was: " + insertTime +
3048
                           " seconds.");
3049
      }
3050
      dbconn.close();
3051
    } catch (McdbException me) {
3052
      me.toXml(new PrintWriter(System.err));
3053
    } catch (AccessionNumberException ane) {
3054
      System.out.println(ane.getMessage());
3055
    } catch (Exception e) {
3056
      System.err.println("EXCEPTION HANDLING REQUIRED");
3057
      System.err.println(e.getMessage());
3058
      e.printStackTrace(System.err);
3059
    }
3060
    finally
3061
    {
3062
      // Return db connection
3063
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
3064
    }
3065
  }
3066
}
(31-31/58)