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: sgarg $'
10
 *     '$Date: 2004-05-14 14:38:02 -0700 (Fri, 14 May 2004) $'
11
 * '$Revision: 2169 $'
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.io.BufferedInputStream;
31
import java.io.BufferedOutputStream;
32
import java.io.File;
33
import java.io.FileOutputStream;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.PrintWriter;
37
import java.io.Reader;
38
import java.io.StringWriter;
39
import java.io.Writer;
40
import java.net.URL;
41
import java.sql.PreparedStatement;
42
import java.sql.ResultSet;
43
import java.sql.SQLException;
44
import java.util.Enumeration;
45
import java.util.Hashtable;
46
import java.util.Iterator;
47
import java.util.Stack;
48
import java.util.TreeSet;
49
import java.util.Vector;
50

    
51
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
52

    
53
import org.xml.sax.ContentHandler;
54
import org.xml.sax.DTDHandler;
55
import org.xml.sax.EntityResolver;
56
import org.xml.sax.ErrorHandler;
57
import org.xml.sax.InputSource;
58
import org.xml.sax.XMLReader;
59
import org.xml.sax.helpers.XMLReaderFactory;
60

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

    
68
   /* Constants */
69
   public static final String SCHEMA                 = "Schema";
70
   public static final String DTD                    = "DTD";
71
   public static final String EML200                 = "eml200";
72
   public static final String EML210                 = "eml210";
73
   public static final String EXTERNALSCHEMALOCATIONPROPERTY =
74
              "http://apache.org/xml/properties/schema/external-schemaLocation";
75
   /*public static final String EXTERNALSCHEMALOCATION =
76
     "eml://ecoinformatics.org/eml-2.0.0 http://dev.nceas.ucsb.edu/tao/schema/eml.xsd"+
77
      " http://www.xml-cml.org/schema/stmml http://dev.nceas.ucsb.edu/tao/schema/stmml.xsd";*/
78
   public static final String DECLARATIONHANDLERPROPERTY =
79
                            "http://xml.org/sax/properties/declaration-handler";
80
   public static final String LEXICALPROPERTY =
81
                             "http://xml.org/sax/properties/lexical-handler";
82
   public static final String VALIDATIONFEATURE =
83
                             "http://xml.org/sax/features/validation";
84
   public static final String SCHEMAVALIDATIONFEATURE =
85
                             "http://apache.org/xml/features/validation/schema";
86
   public static final String NAMESPACEFEATURE =
87
                              "http://xml.org/sax/features/namespaces";
88
   public static final String NAMESPACEPREFIXESFEATURE =
89
                              "http://xml.org/sax/features/namespace-prefixes";
90
   public static final String EML2_1_0NAMESPACE =
91
                                         MetaCatUtil.getOption("eml2_1_0namespace");
92
                                         // "eml://ecoinformatics.org/eml-2.0.0";
93
   public static final String EML2_0_0NAMESPACE =
94
                                         MetaCatUtil.getOption("eml2_0_0namespace");
95
                                         // "eml://ecoinformatics.org/eml-2.0.0";
96

    
97

    
98
  public static final String DOCNAME = "docname";
99
  public static final String PUBLICID = "publicid";
100
  public static final String SYSTEMID = "systemid";
101
  static final int ALL = 1;
102
  static final int WRITE = 2;
103
  static final int READ = 4;
104
  private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
105

    
106
  private DBConnection connection = null;
107
  private String docid = null;
108
  private String updatedVersion=null;
109
  private String docname = null;
110
  private String doctype = null;
111
  private String validateType = null; //base on dtd or schema
112
// DOCTITLE attr cleared from the db
113
//  private String doctitle = null;
114
  private String createdate = null;
115
  private String updatedate = null;
116
  private String system_id = null;
117
  private String userowner = null;
118
  private String userupdated = null;
119
  private int rev;
120
  private int serverlocation;
121
  private String docHomeServer;
122
  private String publicaccess;
123
  private long rootnodeid;
124
  private ElementNode rootNode = null;
125
  private TreeSet nodeRecordList = null;
126
  //private static
127
  //ReplicationServerList serverList = new ReplicationServerList();
128

    
129
  /**
130
   * Constructor used to create a document and read the document information
131
   * from the database.  If readNodes is false, then the node data is not
132
   * read at this time, but is deferred until it is needed (such as when a
133
   * call to toXml() is made).
134
   *
135
   * @param conn the database connection from which to read the document
136
   * @param docid the identifier of the document to be created
137
   * @param readNodes flag indicating whether the xmlnodes should be read
138
   */
139
  public DocumentImpl(String docid, boolean readNodes)
140
         throws McdbException
141
  {
142
    try {
143
      //this.conn = conn;
144
      this.docid = docid;
145

    
146
      // Look up the document information
147
      getDocumentInfo(docid);
148

    
149
      if (readNodes) {
150
        // Download all of the document nodes using a single SQL query
151
        // The sort order of the records is determined by the NodeComparator
152
        // class, and needs to represent a depth-first traversal for the
153
        // toXml() method to work properly
154
        nodeRecordList = getNodeRecordList(rootnodeid);
155

    
156
      }
157

    
158
    } catch (McdbException ex) {
159
      throw ex;
160
    } catch (Throwable t) {
161
      throw new McdbException("Error reading document from " +
162
                              "DocumentImpl.DocumentImpl: " + docid);
163
    }
164
  }
165

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

    
178

    
179

    
180
  /**
181
   * Construct a new document instance, writing the contents to the database.
182
   * This method is called from DBSAXHandler because we need to know the
183
   * root element name for documents without a DOCTYPE before creating it.
184
   *
185
   * In this constructor, the docid is without rev. There is a string rev to
186
   * specify the revision user want to upadate. The revion is only need to be
187
   * greater than current one. It is not need to be sequent number just after
188
   * current one. So it is only used in update action
189
   * @param conn the JDBC Connection to which all information is written
190
   * @param rootnodeid - sequence id of the root node in the document
191
   * @param docname - the name of DTD, i.e. the name immediately following
192
   *        the DOCTYPE keyword ( should be the root element name ) or
193
   *        the root element name if no DOCTYPE declaration provided
194
   *        (Oracle's and IBM parsers are not aware if it is not the
195
   *        root element name)
196
   * @param doctype - Public ID of the DTD, i.e. the name immediately
197
   *                  following the PUBLIC keyword in DOCTYPE declaration or
198
   *                  the docname if no Public ID provided or
199
   *                  null if no DOCTYPE declaration provided
200
   * @param docid the docid to use for the UPDATE, no version number
201
   * @param version, need to be update
202
   * @param action the action to be performed (INSERT OR UPDATE)
203
   * @param user the user that owns the document
204
   * @param pub flag for public "read" access on document
205
   * @param serverCode the serverid from xml_replication on which this document
206
   *        resides.
207
   *
208
   */
209
  public DocumentImpl(DBConnection conn, long rootNodeId, String docName,
210
                      String docType, String docId, String newRevision,
211
                      String action, String user,
212
                      String pub, String catalogId, int serverCode)
213
                      throws SQLException, Exception
214
  {
215
    this.connection = conn;
216
    this.rootnodeid = rootNodeId;
217
    this.docname = docName;
218
    this.doctype = docType;
219
    this.docid = docId;
220
    this.updatedVersion = newRevision;
221
    writeDocumentToDB(action, user, pub, catalogId, serverCode);
222
  }
223

    
224
  /**
225
   * This method will be call in handleUploadRequest in MetacatServlet class
226
   */
227
  public static void registerDocument(
228
                     String docname, String doctype, String accnum, String user)
229
                     throws SQLException, AccessionNumberException, Exception
230
  {
231

    
232
    try
233
    {
234
       // get server location for this doc
235
      int serverLocation=getServerLocationNumber(accnum);
236
      registerDocument(docname, doctype,accnum, user, serverLocation);
237
    }
238
    catch (Exception e)
239
    {
240
      throw e;
241
    }
242

    
243

    
244
  }
245
  /**
246
   * Register a document that resides on the filesystem with the database.
247
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
248
   * Creates a reference to a filesystem document (used for non-xml data files).
249
   * This class only be called in MetaCatServerlet.
250
   * @param conn the JDBC Connection to which all information is written
251
   * @param docname - the name of DTD, i.e. the name immediately following
252
   *        the DOCTYPE keyword ( should be the root element name ) or
253
   *        the root element name if no DOCTYPE declaration provided
254
   *        (Oracle's and IBM parsers are not aware if it is not the
255
   *        root element name)
256
   * @param doctype - Public ID of the DTD, i.e. the name immediately
257
   *                  following the PUBLIC keyword in DOCTYPE declaration or
258
   *                  the docname if no Public ID provided or
259
   *                  null if no DOCTYPE declaration provided
260
   * @param accnum the accession number to use for the INSERT OR UPDATE, which
261
   *               includes a revision number for this revision of the document
262
   *               (e.g., knb.1.1)
263
   * @param user the user that owns the document
264
   * @param serverCode the serverid from xml_replication on which this document
265
   *        resides.
266
   */
267
  public static void registerDocument(
268
                     String docname, String doctype, String accnum,
269
                     String user, int serverCode)
270
                     throws SQLException, AccessionNumberException, Exception
271
  {
272
    DBConnection dbconn = null;
273
    int serialNumber = -1;
274
    PreparedStatement pstmt = null;
275
    //MetaCatUtil util = new MetaCatUtil();
276
    AccessionNumber ac;
277
    String action = null;
278
    try {
279
      //dbconn = util.openDBConnection();
280
      //check out DBConnection
281
      dbconn=DBConnectionPool.
282
                    getDBConnection("DocumentImpl.registerDocument");
283
      serialNumber=dbconn.getCheckOutSerialNumber();
284
      String docIdWithoutRev=MetaCatUtil.getDocIdFromString(accnum);
285
      int userSpecifyRev=MetaCatUtil.getVersionFromString(accnum);
286
      int revInDataBase=getLatestRevisionNumber(docIdWithoutRev);
287
      //revIndataBase=-1, there is no record in xml_documents table
288
      //the data file is a new one, inert it into table
289
      //user specified rev should be great than 0
290
      if (revInDataBase==-1 && userSpecifyRev>0 )
291
      {
292
        ac = new AccessionNumber(accnum, "insert");
293
        action = "insert";
294
      }
295
      //rev is greater the last revsion number and revInDataBase isn't -1
296
      // it is a updated data file
297
      else if (userSpecifyRev>revInDataBase && revInDataBase>0)
298
      {
299

    
300
        //archive the old entry
301
        archiveDocRevision(docIdWithoutRev, user);
302
        //delete the old entry in xml_documents
303
        //deleteXMLDocuments(docIdWithoutRev);
304
        ac = new AccessionNumber(accnum, "update");
305
        action = "update";
306
      }
307
      //other situation
308
      else
309
      {
310

    
311
        throw new Exception("Revision number couldn't be "
312
                    +userSpecifyRev);
313
      }
314
      String docid = ac.getDocid();
315
      String rev = ac.getRev();
316
      /*SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy HH:mm:ss");
317
      Date localtime = new Date();
318
      String dateString = formatter.format(localtime);
319
      String sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY HH24:MI:SS");*/
320
      String  sqlDateString = dbAdapter.getDateTimeFunction();
321

    
322
      StringBuffer sql = new StringBuffer();
323
      if (action != null && action.equals("insert"))
324
      {
325
        sql.append("insert into xml_documents (docid, docname, doctype, ");
326
        sql.append("user_owner, user_updated, server_location, rev,date_created");
327
        sql.append(", date_updated, public_access) values ('");
328
        sql.append(docid).append("','");
329
        sql.append(docname).append("','");
330
        sql.append(doctype).append("','");
331
        sql.append(user).append("','");
332
        sql.append(user).append("','");
333
        sql.append(serverCode).append("','");
334
        sql.append(rev).append("',");
335
        sql.append(sqlDateString).append(",");
336
        sql.append(sqlDateString).append(",");
337
        sql.append("'0')");
338
      }
339
      else if (action != null && action.equals("update"))
340
      {
341
        sql.append("update xml_documents set docname ='");
342
        sql.append(docname).append("', ");
343
        sql.append("user_updated='");
344
        sql.append(user).append("', ");
345
        sql.append("server_location='");
346
        sql.append(serverCode).append("',");
347
        sql.append("rev='");
348
        sql.append(rev).append("',");
349
        sql.append("date_updated=");
350
        sql.append(sqlDateString);
351
        sql.append(" where docid='");
352
        sql.append(docid).append("'");
353
      }
354
      pstmt = dbconn.prepareStatement(sql.toString());
355
      pstmt.execute();
356
      pstmt.close();
357
      //dbconn.close();
358
    }
359
    finally
360
    {
361
      try
362
      {
363
        pstmt.close();
364
      }
365
      finally
366
      {
367
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
368
      }
369
    }
370
  }
371

    
372
    /**
373
   * Register a document that resides on the filesystem with the database.
374
   * (ie, just an entry in xml_documents, nothing in xml_nodes).
375
   * Creates a reference to a filesystem document (used for non-xml data files)
376
   * This method will be called for register data file in xml_documents in
377
   * Replication.
378
   * This method is revised from registerDocument.
379
   *
380
   * @param conn the JDBC Connection to which all information is written
381
   * @param docname - the name of DTD, i.e. the name immediately following
382
   *        the DOCTYPE keyword ( should be the root element name ) or
383
   *        the root element name if no DOCTYPE declaration provided
384
   *        (Oracle's and IBM parsers are not aware if it is not the
385
   *        root element name)
386
   * @param doctype - Public ID of the DTD, i.e. the name immediately
387
   *                  following the PUBLIC keyword in DOCTYPE declaration or
388
   *                  the docname if no Public ID provided or
389
   *                  null if no DOCTYPE declaration provided
390
   * @param accnum the accession number to use for the INSERT OR UPDATE, which
391
   *               includes a revision number for this revision of the document
392
   *               (e.g., knb.1.1)
393
   * @param user the user that owns the document
394
   * @param serverCode the serverid from xml_replication on which this document
395
   *        resides.
396
   */
397
  public static void registerDocumentInReplication(
398
                     String docname, String doctype, String accnum,
399
                     String user, int serverCode)
400
                     throws SQLException, AccessionNumberException, Exception
401
  {
402
    DBConnection dbconn = null;
403
    int serialNumber = -1;
404
    //MetaCatUtil util = new MetaCatUtil();
405
    AccessionNumber ac;
406
    PreparedStatement pstmt = null;
407
    String action = null;
408
    try {
409
      //dbconn = util.openDBConnection();
410
       dbconn=DBConnectionPool.
411
                  getDBConnection("DocumentImpl.registerDocumentInReplication");
412
      serialNumber=dbconn.getCheckOutSerialNumber();
413
      String docIdWithoutRev=MetaCatUtil.getDocIdFromAccessionNumber(accnum);
414
      int userSpecifyRev=MetaCatUtil.getRevisionFromAccessionNumber(accnum);
415
      int revInDataBase=getLatestRevisionNumber(docIdWithoutRev);
416
      //revIndataBase=-1, there is no record in xml_documents table
417
      //the data file is a new one, inert it into table
418
      //user specified rev should be great than 0
419
      if (revInDataBase==-1 && userSpecifyRev>=0 )
420
      {
421

    
422
        ac = new AccessionNumber(accnum, "insert");
423
        action = "insert";
424
      }
425
      //rev is greater the last revsion number and revInDataBase isn't -1
426
      // it is a updated data file
427
      else if (userSpecifyRev>revInDataBase && revInDataBase>=0)
428
      {
429

    
430
        //archive the old entry
431
        archiveDocRevision(docIdWithoutRev, user);
432
        //delete the old entry in xml_documents
433
        //deleteXMLDocuments(docIdWithoutRev);
434
        ac = new AccessionNumber(accnum, "update");
435
        action = "update";
436
      }
437
      // local server has newer version, then notify the remote server
438
      else if ( userSpecifyRev < revInDataBase && revInDataBase > 0)
439
      {
440
        throw new Exception("Local server: "+MetaCatUtil.getOption("server")+
441
                 " has newer revision of doc: "+docIdWithoutRev+"."
442
                  +revInDataBase+". Please notify it.");
443
      }
444
      //other situation
445
      else
446
      {
447

    
448
        throw new Exception("Revision number couldn't be "
449
                    +userSpecifyRev);
450
      }
451
      String docid = ac.getDocid();
452
      String rev = ac.getRev();
453
      /*SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy HH:mm:ss");
454
      Date localtime = new Date();
455
      String dateString = formatter.format(localtime);
456
      String sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY HH24:MI:SS");*/
457
      String sqlDateString = dbAdapter.getDateTimeFunction();
458

    
459
      StringBuffer sql = new StringBuffer();
460
      if (action != null && action.equals("insert"))
461
      {
462
        sql.append("insert into xml_documents (docid, docname, doctype, ");
463
        sql.append("user_owner, user_updated, server_location, rev,date_created");
464
        sql.append(", date_updated, public_access) values ('");
465
        sql.append(docid).append("','");
466
        sql.append(docname).append("','");
467
        sql.append(doctype).append("','");
468
        sql.append(user).append("','");
469
        sql.append(user).append("','");
470
        sql.append(serverCode).append("','");
471
        sql.append(rev).append("',");
472
        sql.append(sqlDateString).append(",");
473
        sql.append(sqlDateString).append(",");
474
        sql.append("'0')");
475
      }
476
      else if (action != null && action.equals("update"))
477
      {
478
        sql.append("update xml_documents set docname ='");
479
        sql.append(docname).append("', ");
480
        sql.append("user_updated='");
481
        sql.append(user).append("', ");
482
        sql.append("server_location='");
483
        sql.append(serverCode).append("',");
484
        sql.append("rev='");
485
        sql.append(rev).append("',");
486
        sql.append("date_updated=");
487
        sql.append(sqlDateString);
488
        sql.append(" where docid='");
489
        sql.append(docid).append("'");
490
      }
491
      // Set auto commit fasle
492
      dbconn.setAutoCommit(false);
493
      pstmt = dbconn.prepareStatement(sql.toString());
494

    
495
      pstmt.execute();
496
      // Commit the insert
497
      dbconn.commit();
498
      pstmt.close();
499
      //dbconn.close();
500
    }
501
    finally
502
    {
503
      // Set DBConnection auto commit true
504
      dbconn.setAutoCommit(true);
505
      pstmt.close();
506
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
507
    }
508
  }
509

    
510
 /**
511
   * This method will register a data file entry in xml_documents and save a
512
   * data file input Stream into file system.. It is only used in replication
513
   *
514
   * @param  input, the input stream which contain the file content.
515
   * @param  , the input stream which contain the file content
516
   * @param docname - the name of DTD, for data file, it is a docid number.
517
   * @param doctype - "BIN" for data file
518
   * @param accnum the accession number to use for the INSERT OR UPDATE, which
519
   *               includes a revision number for this revision of the document
520
   *               (e.g., knb.1.1)
521
   * @param user the user that owns the document
522
   * @param docHomeServer, the home server of the docid
523
   * @param notificationServer, the server to notify force replication info to
524
   *                            local metacat
525
   */
526
 public static void writeDataFileInReplication(InputStream input,
527
                 String filePath, String docname, String doctype, String accnum,
528
                   String user, String docHomeServer, String notificationServer)
529
                     throws SQLException, AccessionNumberException, Exception
530
 {
531
    int serverCode=-2;
532

    
533

    
534
    if (filePath==null||filePath.equals(""))
535
    {
536
      throw new
537
            Exception("Please specify the directory where file will be store");
538
    }
539
    if (accnum==null||accnum.equals(""))
540
    {
541
      throw new Exception("Please specify the stored file name");
542
    }
543

    
544

    
545

    
546
    // If server is not int the xml replication talbe, insert it into
547
    // xml_replication table
548
    //serverList.addToServerListIfItIsNot(docHomeServer);
549
    insertServerIntoReplicationTable(docHomeServer);
550

    
551
    // Get server code again
552
    serverCode = getServerCode(docHomeServer);
553

    
554

    
555
    //register data file into xml_documents table
556
    registerDocumentInReplication(docname, doctype, accnum, user, serverCode);
557
    //write inputstream into file system.
558
    File dataDirectory = new File(filePath);
559
    File newFile = new File(dataDirectory, accnum);
560

    
561
    // create a buffered byte output stream
562
    // that uses a default-sized output buffer
563
    FileOutputStream fos = new FileOutputStream(newFile);
564
    BufferedOutputStream outPut = new BufferedOutputStream(fos);
565

    
566
    BufferedInputStream bis = null;
567
    bis = new BufferedInputStream(input);
568
    byte[] buf = new byte[4 * 1024]; // 4K buffer
569
    int b = bis.read(buf);
570

    
571
    while (b != -1)
572
    {
573
        outPut.write(buf, 0, b);
574
        b = bis.read(buf);
575
    }
576
    bis.close();
577
          outPut.close();
578
          fos.close();
579

    
580
    // Force replicate data file
581
    ForceReplicationHandler forceReplication = new ForceReplicationHandler
582
                                    (accnum, false, notificationServer);
583

    
584
 }
585

    
586

    
587

    
588
  public static boolean getDataFileLockGrant(String accnum)
589
                                                  throws Exception
590
  {
591

    
592
    try
593
    {
594

    
595
      int serverLocation=getServerLocationNumber(accnum);
596

    
597
      return getDataFileLockGrant(accnum,serverLocation);
598
    }
599
    catch (Exception e)
600
    {
601

    
602
      throw e;
603
    }
604
  }
605

    
606
  /**
607
   * The method will check if metacat can get data file lock grant
608
   * If server code is 1, it get.
609
   * If server code is not 1 but call replication getlock successfully,
610
   * it get
611
   * else, it didn't get
612
   * @param accnum, the ID of the document
613
   * @param action, the action to the document
614
   * @param serverCode, the server location code
615
   */
616
  public static boolean getDataFileLockGrant(String accnum, int serverCode)
617
                                          throws Exception
618
  {
619
    boolean flag=true;
620
    //MetaCatUtil util = new MetaCatUtil();
621
    String docid = MetaCatUtil.getDocIdFromString(accnum);
622
    int rev = MetaCatUtil.getVersionFromString(accnum);
623

    
624
    if (serverCode == 1)
625
    {
626
      flag=true;
627
      return flag;
628
    }
629

    
630
    //if((serverCode != 1 && action.equals("UPDATE")) )
631
    if (serverCode != 1)
632
    { //if this document being written is not a resident of this server then
633
      //we need to try to get a lock from it's resident server.  If the
634
      //resident server will not give a lock then we send the user a message
635
      //saying that he/she needs to download a new copy of the file and
636
      //merge the differences manually.
637

    
638
      String server=MetacatReplication.getServerNameForServerCode(serverCode);
639
      MetacatReplication.replLog("attempting to lock " + accnum);
640
      URL u = new URL("https://" + server + "?server=" +
641
        MetaCatUtil.getLocalReplicationServerName()+"&action=getlock&updaterev="
642
           +rev + "&docid=" + docid);
643
      //System.out.println("sending message: " + u.toString());
644
      String serverResStr = MetacatReplication.getURLContent(u);
645
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
646
      if(openingtag.equals("<lockgranted>"))
647
      {
648
        //the lock was granted go ahead with the insert
649
        //System.out.println("In lockgranted");
650
        MetacatReplication.replLog("lock granted for " + accnum + " from " +
651
                                      server);
652
        flag=true;
653
        return flag;
654
      }//if
655

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

    
680
   return flag;
681

    
682
  }//getDataFileLockGrant
683

    
684
  /**
685
   * get the document name
686
   */
687
  public String getDocname() {
688
    return docname;
689
  }
690

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

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

    
705
  /**
706
   * get the root node identifier
707
   */
708
  public long getRootNodeID() {
709
    return rootnodeid;
710
  }
711

    
712
  /**
713
   * get the creation date
714
   */
715
  public String getCreateDate() {
716
    return createdate;
717
  }
718

    
719
  /**
720
   * get the update date
721
   */
722
  public String getUpdateDate() {
723
    return updatedate;
724
  }
725

    
726
  /**
727
   * Get the document identifier (docid)
728
   */
729
  public String getDocID() {
730
    return docid;
731
  }
732

    
733
// DOCTITLE attr cleared from the db
734
//  /**
735
//   *get the document title
736
//   */
737
//  public String getDocTitle() {
738
//    return doctitle;
739
//  }
740

    
741
  public String getUserowner() {
742
    return userowner;
743
  }
744

    
745
  public String getUserupdated() {
746
    return userupdated;
747
  }
748

    
749
  public int getServerlocation() {
750
    return serverlocation;
751
  }
752

    
753
  public String getDocHomeServer() {
754
    return docHomeServer;
755
  }
756

    
757

    
758

    
759
  public String getPublicaccess() {
760
    return publicaccess;
761
  }
762

    
763
  public int getRev() {
764
    return rev;
765
  }
766

    
767
  public String getValidateType()
768
  {
769
    return validateType;
770
  }
771

    
772
  /**
773
   * Print a string representation of the XML document
774
   */
775
  public String toString(String user, String[] groups, boolean withInlinedata)
776
  {
777
    StringWriter docwriter = new StringWriter();
778
    try
779
    {
780
      this.toXml(docwriter, user, groups, withInlinedata);
781
    }
782
    catch (McdbException mcdbe)
783
    {
784
      return null;
785
    }
786
    String document = docwriter.toString();
787
    return document;
788
  }
789

    
790
   /**
791
   * Print a string representation of the XML document
792
   */
793
  public String toString()
794
  {
795
    StringWriter docwriter = new StringWriter();
796
    String userName = null;
797
    String[] groupNames = null;
798
    boolean withInlineData = false;
799
    try
800
    {
801
      this.toXml(docwriter, userName, groupNames, withInlineData);
802
    }
803
    catch (McdbException mcdbe)
804
    {
805
      return null;
806
    }
807
    String document = docwriter.toString();
808
    return document;
809
  }
810

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

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

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

    
833
    // Append the resulting document to the StringBuffer and return it
834
    doc.append("<?xml version=\"1.0\"?>\n");
835

    
836
    if (docname != null) {
837
      if ((doctype != null) && (system_id != null)) {
838
        doc.append("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
839
                   "\" \"" + system_id + "\">\n");
840
      } else {
841
        doc.append("<!DOCTYPE " + docname + ">\n");
842
      }
843
    }
844
    doc.append(rootNode.toString());
845

    
846
    return (doc.toString());
847
  }
848

    
849
  /**
850
   * Print a text representation of the XML document to a Writer
851
   *
852
   * @param pw the Writer to which we print the document
853
   */
854
  public void toXml(Writer pw, String user, String[] groups, boolean withInLineData)
855
                                                          throws McdbException
856
  {
857
    // flag for process  eml2
858
    boolean proccessEml2 = false;
859
    boolean storedDTD = false;//flag to inidate publicid or system
860
                              // id stored in db or not
861
    boolean firstElement = true;
862
    String dbDocName = null;
863
    String dbPublicID = null;
864
    String dbSystemID = null;
865

    
866
    if (doctype != null && doctype.equals(EML2_0_0NAMESPACE))
867
    {
868
      proccessEml2 = true;
869
    }
870
    // flag for process inline data
871
    boolean prcocessInlineData = false;
872

    
873
    TreeSet nodeRecordLists = null;
874
    PrintWriter out = null;
875
    if (pw instanceof PrintWriter) {
876
      out = (PrintWriter)pw;
877
    } else {
878
      out = new PrintWriter(pw);
879
    }
880

    
881
    MetaCatUtil util = new MetaCatUtil();
882

    
883
    // Here add code to handle subtree access control
884
    PermissionController control = new PermissionController(docid);
885
    Hashtable unaccessableSubTree =control.hasUnaccessableSubTree(user, groups,
886
                                             AccessControlInterface.READSTRING);
887

    
888
    if (!unaccessableSubTree.isEmpty())
889
    {
890

    
891
      nodeRecordLists = getPartNodeRecordList(rootnodeid, unaccessableSubTree);
892

    
893
    }
894
    else
895
    {
896
      nodeRecordLists = getNodeRecordList(rootnodeid);
897
    }
898

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

    
903
    // Step through all of the node records we were given
904

    
905
    Iterator it = nodeRecordLists.iterator();
906

    
907
    while (it.hasNext())
908
    {
909

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

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

    
955

    
956
      // Handle the ELEMENT nodes
957
      } else if (currentNode.nodetype.equals("ELEMENT")) {
958
        if (atRootElement) {
959
          atRootElement = false;
960
        } else {
961
          if (previousNodeWasElement) {
962
            out.print(">");
963
          }
964
        }
965

    
966
        // if publicid or system is not stored into db send it out by default
967
        if ( !storedDTD & firstElement )
968
        {
969
           if (docname != null && validateType != null && validateType.equals(DTD))
970
           {
971
              if ((doctype != null) && (system_id != null))
972
              {
973

    
974
                  out.print("<!DOCTYPE " + docname + " PUBLIC \"" + doctype +
975
                       "\" \"" + system_id + "\">");
976
               }
977
               else
978
               {
979

    
980
                  out.print("<!DOCTYPE " + docname + ">");
981
               }
982
           }
983
        }
984
        firstElement = false;
985
        openElements.push(currentNode);
986
        util.debugMessage("\n PUSHED: " + currentNode.nodename, 50);
987
        previousNodeWasElement = true;
988
        if ( currentNode.nodeprefix != null ) {
989
          out.print("<" + currentNode.nodeprefix + ":" + currentNode.nodename);
990
        } else {
991
          out.print("<" + currentNode.nodename);
992
        }
993

    
994
        // if currentNode is inline and handle eml2, set flag proccess in
995
        if (currentNode.nodename != null &&
996
            currentNode.nodename.equals(Eml200SAXHandler.INLINE) && proccessEml2)
997
        {
998
          prcocessInlineData = true;
999
        }
1000

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

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

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

    
1054
        // reset proccess inline data false
1055
        prcocessInlineData =false;
1056
        previousNodeWasElement = false;
1057

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

    
1066
      // Handle the PI nodes
1067
      } else if (currentNode.nodetype.equals("PI")) {
1068
        if (previousNodeWasElement) {
1069
          out.print(">");
1070
        }
1071
        out.print("<?" + currentNode.nodename + " " +
1072
                        currentNode.nodedata + "?>");
1073
        previousNodeWasElement = false;
1074
     // Handle the DTD nodes (docname, publicid, systemid)
1075
     } else if (currentNode.nodetype.equals(DTD)) {
1076
         storedDTD = true;
1077
         if (currentNode.getNodeName().equals(DOCNAME))
1078
         {
1079
           dbDocName = currentNode.getNodeData();
1080
         }
1081
         if (currentNode.getNodeName().equals(PUBLICID))
1082
         {
1083
           dbPublicID = currentNode.getNodeData();
1084
         }
1085
         if (currentNode.getNodeName().equals(SYSTEMID))
1086
         {
1087
           dbSystemID = currentNode.getNodeData();
1088
           // send out <!doctype .../>
1089
           if (dbDocName != null )
1090
           {
1091
              if ((dbPublicID!= null) && (dbSystemID != null))
1092
              {
1093

    
1094
                 out.print("<!DOCTYPE " + dbDocName+" PUBLIC \""+dbPublicID +
1095
                       "\" \"" + dbSystemID + "\">");
1096
              }
1097
              else
1098
              {
1099

    
1100
                out.print("<!DOCTYPE " + dbDocName + ">");
1101
              }
1102
           }
1103

    
1104
           //reset these variable
1105
           dbDocName = null;
1106
           dbPublicID = null;
1107
           dbSystemID = null;
1108
         }
1109

    
1110
      // Handle any other node type (do nothing)
1111
      } else {
1112
        // Any other types of nodes are not handled.
1113
        // Probably should throw an exception here to indicate this
1114
      }
1115
      out.flush();
1116
    }
1117

    
1118
    // Print the final end tag for the root element
1119
    while(!openElements.empty())
1120
    {
1121
      NodeRecord currentElement = (NodeRecord)openElements.pop();
1122
      util.debugMessage("\n POPPED: " + currentElement.nodename, 50);
1123
      if ( currentElement.nodeprefix != null ) {
1124
        out.print("</" + currentElement.nodeprefix + ":" +
1125
                  currentElement.nodename + ">" );
1126
      } else {
1127
        out.print("</" + currentElement.nodename + ">" );
1128
      }
1129
    }
1130
    out.flush();
1131
  }
1132

    
1133

    
1134

    
1135
  private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
1136
  {
1137
    //System.out.println("inRevisionOnly");
1138
    DBConnection dbconn = null;
1139
    int serialNumber = -1;
1140
    PreparedStatement pstmt =null;
1141
    String rev = docid.getRev();
1142
    String newid = docid.getIdentifier();
1143
    try
1144
    {
1145
      dbconn=DBConnectionPool.
1146
                    getDBConnection("DocumentImpl.isRevisionOnly");
1147
      serialNumber=dbconn.getCheckOutSerialNumber();
1148
      pstmt = dbconn.prepareStatement("select rev from xml_documents " +
1149
                                  "where docid like '" + newid + "'");
1150
      pstmt.execute();
1151
      ResultSet rs = pstmt.getResultSet();
1152
      boolean tablehasrows = rs.next();
1153
      if(rev.equals("newest") || rev.equals("all"))
1154
      {
1155
        return false;
1156
      }
1157

    
1158
      if(tablehasrows)
1159
      {
1160
        int r = rs.getInt(1);
1161
        pstmt.close();
1162
        if(new Integer(rev).intValue() == r)
1163
        { //the current revision in in xml_documents
1164
          //System.out.println("returning false");
1165
          return false;
1166
        }
1167
        else if(new Integer(rev).intValue() < r)
1168
        { //the current revision is in xml_revisions.
1169
          //System.out.println("returning true");
1170
          return true;
1171
        }
1172
        else if(new Integer(rev).intValue() > r)
1173
        { //error, rev cannot be greater than r
1174
          throw new Exception("requested revision cannot be greater than " +
1175
                            "the latest revision number.");
1176
        }
1177
      }
1178
      // Get miss docid and rev, throw to McdDocNotFoundException
1179
      String missDocId = MetaCatUtil.getDocIdFromString(docid.toString());
1180
      String missRevision =
1181
                  MetaCatUtil.getRevisionStringFromString(docid.toString());
1182
      throw new McdbDocNotFoundException("the requested docid '" +
1183
                docid.toString() + "' does not exist", missDocId, missRevision);
1184
    }//try
1185
    finally
1186
    {
1187
      pstmt.close();
1188
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1189
    }//finally
1190
  }
1191

    
1192
  private void getDocumentInfo(String docid) throws McdbException,
1193
                                        AccessionNumberException, Exception
1194
  {
1195
    getDocumentInfo(new DocumentIdentifier(docid));
1196
  }
1197

    
1198
  /**
1199
   * Look up the document type information from the database
1200
   *
1201
   * @param docid the id of the document to look up
1202
   */
1203
  private void getDocumentInfo(DocumentIdentifier docid) throws McdbException
1204
                                                          , Exception
1205
  {
1206
    DBConnection dbconn = null;
1207
    int serialNumber = -1;
1208
    PreparedStatement pstmt = null;
1209
    String table = "xml_documents";
1210

    
1211
    try
1212
    {
1213
      if(isRevisionOnly(docid))
1214
      { //pull the document from xml_revisions instead of from xml_documents;
1215
        table = "xml_revisions";
1216
      }
1217
    }
1218
    // catch a McdbDocNotFoundException throw it
1219
    catch (McdbDocNotFoundException notFound)
1220
    {
1221
      throw notFound;
1222
    }
1223
    catch(Exception e)
1224
    {
1225

    
1226
      MetaCatUtil.debugMessage("error in DocumentImpl.getDocumentInfo: " +
1227
                          e.getMessage(), 30);
1228
      throw e;
1229
    }
1230

    
1231

    
1232

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

    
1253
      pstmt.execute();
1254
      ResultSet rs = pstmt.getResultSet();
1255
      boolean tableHasRows = rs.next();
1256
      if (tableHasRows) {
1257
        this.docname        = rs.getString(1);
1258
        this.doctype        = rs.getString(2);
1259
        this.rootnodeid     = rs.getLong(3);
1260
// DOCTITLE attr cleared from the db
1261
//        this.doctitle       = rs.getString(4);
1262
        this.createdate     = rs.getString(4);
1263
        this.updatedate     = rs.getString(5);
1264
        this.userowner      = rs.getString(6);
1265
        this.userupdated    = rs.getString(7);
1266
        this.serverlocation = rs.getInt(8);
1267
        this.publicaccess   = rs.getString(9);
1268
        this.rev            = rs.getInt(10);
1269
      }
1270
      pstmt.close();
1271

    
1272
      //get doc  home server name
1273

    
1274
      pstmt = dbconn.prepareStatement("select server " +
1275
                        "from xml_replication where serverid = ?");
1276
      //because connection use twise here, so we need to increase one
1277
      dbconn.increaseUsageCount(1);
1278
      pstmt.setInt(1, serverlocation);
1279
      pstmt.execute();
1280
      rs = pstmt.getResultSet();
1281
      tableHasRows = rs.next();
1282
      if (tableHasRows)
1283
      {
1284

    
1285
          String server = rs.getString(1);
1286
          //get homeserver name
1287
          if(!server.equals("localhost"))
1288
          {
1289
            this.docHomeServer=server;
1290
          }
1291
          else
1292
          {
1293
            this.docHomeServer=MetaCatUtil.getLocalReplicationServerName();
1294
          }
1295
          MetaCatUtil.debugMessage("server: "+docHomeServer, 50);
1296

    
1297
      }
1298
      pstmt.close();
1299
      if (this.doctype != null) {
1300
        pstmt =
1301
          dbconn.prepareStatement("SELECT system_id, entry_type " +
1302
                                  "FROM xml_catalog " +
1303
                                 "WHERE public_id = ?");
1304
        //should increase usage count again
1305
        dbconn.increaseUsageCount(1);
1306
        // Bind the values to the query
1307
        pstmt.setString(1, doctype);
1308

    
1309
        pstmt.execute();
1310
        rs = pstmt.getResultSet();
1311
        tableHasRows = rs.next();
1312
        if (tableHasRows) {
1313
          this.system_id  = rs.getString(1);
1314
          this.validateType = rs.getString(2);
1315

    
1316
        }
1317
        pstmt.close();
1318
      }
1319
    } catch (SQLException e) {
1320
      System.out.println("error in DocumentImpl.getDocumentInfo: " +
1321
                          e.getMessage());
1322
      e.printStackTrace(System.out);
1323
      throw new McdbException("Error accessing database connection in " +
1324
                              "DocumentImpl.getDocumentInfo: ", e);
1325
    }
1326
    finally
1327
    {
1328
      try
1329
      {
1330
        pstmt.close();
1331
      }
1332
      catch (SQLException ee)
1333
      {
1334
        MetaCatUtil.debugMessage("error in DocumentImple.getDocumentInfo: "
1335
                                    +ee.getMessage(), 30);
1336
      }//catch
1337
      finally
1338
      {
1339
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1340
      }
1341
    }
1342

    
1343
    if (this.docname == null) {
1344
      throw new McdbDocNotFoundException("Document not found: " + docid,
1345
                                 docid.getIdentifier(), docid.getRev());
1346
    }
1347
  }
1348

    
1349
  /**
1350
   * Look up the node data from the database, but some node would be shown
1351
   * because of access control
1352
   * @param rootnodeid the id of the root node of the node tree to look up
1353
   * @param accessControl  the hashtable has control info
1354
   */
1355
  private TreeSet getPartNodeRecordList(long rootnodeid,
1356
                                        Hashtable accessControl)
1357
                                        throws McdbException
1358
  {
1359
    PreparedStatement pstmt = null;
1360
    DBConnection dbconn = null;
1361
    int serialNumber = -1;
1362
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1363
    long nodeid = 0;
1364
    long parentnodeid = 0;
1365
    long nodeindex = 0;
1366
    String nodetype = null;
1367
    String nodename = null;
1368
    String nodeprefix = null;
1369
    String nodedata = null;
1370
    String quotechar = dbAdapter.getStringDelimiter();
1371
    String sql = "SELECT nodeid,parentnodeid,nodeindex, " +
1372
                 "nodetype,nodename,nodeprefix,nodedata " +
1373
                  "FROM xml_nodes WHERE rootnodeid = ?";
1374

    
1375
    // go through the access control for some nodes
1376
     Enumeration en = accessControl.elements();
1377
     while (en.hasMoreElements())
1378
     {
1379
         SubTree tree = (SubTree)en.nextElement();
1380
         long startId = tree.getStartNodeId();
1381
         long endId  = tree.getEndNodeId();
1382
         sql = sql +" AND(nodeid < " + startId + " OR nodeid > " +endId +")";
1383

    
1384
     }
1385
     MetaCatUtil.debugMessage("The final query to select part node tree: " +
1386
                              sql, 25);
1387

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

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

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

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

    
1443

    
1444
    if (!nodeRecordList.isEmpty())
1445
    {
1446

    
1447
      return nodeRecordList;
1448
    }
1449
    else
1450
    {
1451

    
1452
      throw new McdbException("Error getting node data: " + docid);
1453
    }
1454
  }
1455

    
1456
  /**
1457
   * Look up the node data from the database
1458
   *
1459
   * @param rootnodeid the id of the root node of the node tree to look up
1460
   */
1461
  private TreeSet getNodeRecordList(long rootnodeid) throws McdbException
1462
  {
1463
    PreparedStatement pstmt = null;
1464
    DBConnection dbconn = null;
1465
    int serialNumber = -1;
1466
    TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1467
    long nodeid = 0;
1468
    long parentnodeid = 0;
1469
    long nodeindex = 0;
1470
    String nodetype = null;
1471
    String nodename = null;
1472
    String nodeprefix = null;
1473
    String nodedata = null;
1474
    String quotechar = dbAdapter.getStringDelimiter();
1475

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

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

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

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

    
1510
    } catch (SQLException e) {
1511
      throw new McdbException("Error in DocumentImpl.getNodeRecordList " +
1512
                              e.getMessage());
1513
    }
1514
    finally
1515
    {
1516
      try
1517
      {
1518
        pstmt.close();
1519
      }
1520
      catch (SQLException ee)
1521
      {
1522
        MetaCatUtil.debugMessage("error in DocumentImpl.getNodeRecordList: "
1523
                                    +ee.getMessage(), 30);
1524
      }
1525
      finally
1526
      {
1527
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1528
      }
1529
    }
1530

    
1531
    return nodeRecordList;
1532

    
1533
  }
1534

    
1535
// NOT USED ANY MORE
1536
//  /** creates SQL code and inserts new document into DB connection
1537
//   default serverCode of 1*/
1538
//  private void writeDocumentToDB(String action, String user)
1539
//               throws SQLException, Exception
1540
//  {
1541
//    writeDocumentToDB(action, user, null, 1);
1542
//  }
1543

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

    
1550
    try {
1551
      PreparedStatement pstmt = null;
1552

    
1553
      if (action.equals("INSERT")) {
1554
        //AccessionNumber ac = new AccessionNumber();
1555
        //this.docid = ac.generate(docid, "INSERT");
1556

    
1557
        pstmt = connection.prepareStatement(
1558
                "INSERT INTO xml_documents " +
1559
                "(docid, rootnodeid, docname, doctype, " +
1560
                "user_owner, user_updated, date_created, date_updated, " +
1561
                "public_access, catalog_id, server_location, rev) " +
1562
                "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", " + sysdate +
1563
                ", ?, ?, ?, ?)");
1564
        // Increase dbconnection usage count
1565
        connection.increaseUsageCount(1);
1566

    
1567
        //note that the server_location is set to 1.
1568
        //this means that "localhost" in the xml_replication table must
1569
        //always be the first entry!!!!!
1570

    
1571
        // Bind the values to the query
1572
        pstmt.setString(1, this.docid);
1573
        pstmt.setLong(2, rootnodeid);
1574
        pstmt.setString(3, docname);
1575
        pstmt.setString(4, doctype);
1576
        pstmt.setString(5, user);
1577
        pstmt.setString(6, user);
1578
        //public access is usefulless, so set it to null
1579
        pstmt.setString(7, null);
1580
        /*if ( pub == null ) {
1581
          pstmt.setString(7, null);
1582
        } else if ( pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1583
          pstmt.setInt(7, 1);
1584
        } else if ( pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1585
          pstmt.setInt(7, 0);
1586
        }*/
1587
        pstmt.setString(8, catalogid);
1588
        pstmt.setInt(9, serverCode);
1589
        pstmt.setInt(10, Integer.parseInt(updatedVersion));
1590
      } else if (action.equals("UPDATE")) {
1591

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

    
1623
            pstmt.execute();
1624
            pstmt.close();
1625
        }
1626

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

    
1654
      } else {
1655
        System.err.println("Action not supported: " + action);
1656
      }
1657

    
1658
      // Do the insertion
1659
      pstmt.execute();
1660

    
1661
      pstmt.close();
1662

    
1663
    } catch (SQLException sqle) {
1664
      throw sqle;
1665
    } catch (Exception e) {
1666
      throw e;
1667
    }
1668
  }
1669

    
1670
  /**
1671
   * Write an XML file to the database, given a filename
1672
   *
1673
   * @param conn the JDBC connection to the database
1674
   * @param filename the filename to be loaded into the database
1675
   * @param pub flag for public "read" access on document
1676
   * @param dtdfilename the dtd to be uploaded on server's file system
1677
   * @param action the action to be performed (INSERT OR UPDATE)
1678
   * @param docid the docid to use for the INSERT OR UPDATE
1679
   * @param user the user that owns the document
1680
   * @param groups the groups to which user belongs
1681
   */
1682
  /*public static String write(DBConnection conn,String filename,
1683
                             String pub, String dtdfilename,
1684
                             String action, String docid, String user,
1685
                             String[] groups )
1686
                throws Exception {
1687

    
1688
    Reader dtd = null;
1689
    if ( dtdfilename != null ) {
1690
      dtd = new FileReader(new File(dtdfilename).toString());
1691
    }
1692
    return write ( conn, new FileReader(new File(filename).toString()),
1693
                   pub, dtd, action, docid, user, groups, false);
1694
  }*/
1695

    
1696
  public static String write(DBConnection conn,Reader xml,String pub,Reader dtd,
1697
                             String action, String docid, String user,
1698
                             String[] groups, String ruleBase,
1699
                             boolean needValidation)
1700
                throws Exception {
1701
    //this method will be called in handleUpdateOrInsert method
1702
    //in MetacatServlet class and now is wrapper into documentImple
1703
    // get server location for this doc
1704
    int serverLocation=getServerLocationNumber(docid);
1705
    return write(conn,xml,pub,dtd,action,docid,user,groups,serverLocation,false,
1706
                 ruleBase, needValidation);
1707
  }
1708

    
1709

    
1710

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

    
1731
  public static String write(DBConnection conn, Reader xml,String pub,
1732
                         Reader dtd, String action, String accnum, String user,
1733
                         String[] groups, int serverCode, boolean override,
1734
                         String ruleBase, boolean needValidation)
1735
                throws Exception
1736
  {
1737
    // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1738
    //MetaCatUtil util = new MetaCatUtil();
1739
    MetaCatUtil.debugMessage("conn usage count before writting: "
1740
                                      +conn.getUsageCount(), 50);
1741
    AccessionNumber ac = new AccessionNumber(accnum, action);
1742
    String docid = ac.getDocid();
1743
    String rev = ac.getRev();
1744
    MetaCatUtil.debugMessage("action: " + action + " servercode: " +
1745
                             serverCode + " override: " + override, 10);
1746

    
1747
    if((serverCode != 1 && action.equals("UPDATE")) && !override)
1748
    { //if this document being written is not a resident of this server then
1749
      //we need to try to get a lock from it's resident server.  If the
1750
      //resident server will not give a lock then we send the user a message
1751
      //saying that he/she needs to download a new copy of the file and
1752
      //merge the differences manually.
1753
      int istreamInt;
1754
      char istreamChar;
1755

    
1756
      // check for 'write' permission for 'user' to update this document
1757
      if ( !hasWritePermission(user, groups, accnum) ) {
1758
        throw new Exception("User " + user +
1759
              " does not have permission to update XML Document #" + accnum);
1760
      }
1761

    
1762
      DocumentIdentifier id = new DocumentIdentifier(accnum);
1763
      String updaterev = id.getRev();
1764
      String server=MetacatReplication.getServerNameForServerCode(serverCode);
1765
      MetacatReplication.replLog("attempting to lock " + accnum);
1766
      URL u = new URL("https://" + server + "?server="+
1767
        MetaCatUtil.getLocalReplicationServerName()+"&action=getlock&updaterev="
1768
           +updaterev + "&docid=" + docid);
1769
      //System.out.println("sending message: " + u.toString());
1770
      String serverResStr = MetacatReplication.getURLContent(u);
1771
      String openingtag =serverResStr.substring(0, serverResStr.indexOf(">")+1);
1772
      if(openingtag.equals("<lockgranted>"))
1773
      {//the lock was granted go ahead with the insert
1774
        XMLReader parser = null;
1775
        try
1776
        {
1777
          //System.out.println("In lockgranted");
1778
          MetacatReplication.replLog("lock granted for " + accnum + " from " +
1779
                                      server);
1780
          /*XMLReader parser = initializeParser(conn, action, docid, updaterev,
1781
                               validate, user, groups, pub, serverCode, dtd);*/
1782
          parser = initializeParser(conn, action, docid, updaterev,
1783
                                        user, groups, pub, serverCode,
1784
                                        dtd,ruleBase, needValidation);
1785
          conn.setAutoCommit(false);
1786
          parser.parse(new InputSource(xml));
1787
          conn.commit();
1788
          conn.setAutoCommit(true);
1789
        }
1790
        catch (Exception e)
1791
        {
1792
          conn.rollback();
1793
          conn.setAutoCommit(true);
1794
          //if it is a eml2 document, we need delete online data
1795
          if ( parser != null)
1796
          {
1797
            ContentHandler handler = parser.getContentHandler();
1798
            if (handler instanceof Eml200SAXHandler)
1799
            {
1800
              Eml200SAXHandler eml = (Eml200SAXHandler) handler;
1801
              eml.deleteInlineFiles();
1802
            }
1803
          }
1804
          throw e;
1805
        }
1806
        // run write into access db base one relation table and access object
1807
        runRelationAndAccessHandler(accnum, user, groups, serverCode);
1808

    
1809
        // Force replication the docid
1810
        ForceReplicationHandler frh = new ForceReplicationHandler
1811
                                                          (accnum, true, null);
1812
        return(accnum);
1813

    
1814
      }
1815

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

    
1840
    if ( action.equals("UPDATE") ) {
1841
      // check for 'write' permission for 'user' to update this document
1842

    
1843
      if ( !hasWritePermission(user, groups, accnum) ) {
1844
        throw new Exception("User " + user +
1845
              " does not have permission to update XML Document #" + accnum);
1846
      }
1847
    }
1848
    XMLReader parser = null;
1849
    try
1850
    {
1851

    
1852
      parser = initializeParser(conn, action, docid, rev,
1853
                                          user, groups, pub, serverCode,
1854
                                          dtd, ruleBase, needValidation);
1855

    
1856
      conn.setAutoCommit(false);
1857
      parser.parse(new InputSource(xml));
1858
      conn.commit();
1859
      conn.setAutoCommit(true);
1860
    }
1861
    catch (Exception e)
1862
    {
1863
      conn.rollback();
1864
      conn.setAutoCommit(true);
1865
       //if it is a eml2 document, we need delete online data
1866
       if ( parser != null)
1867
       {
1868
          ContentHandler handler = parser.getContentHandler();
1869
          if (handler instanceof Eml200SAXHandler)
1870
          {
1871
            Eml200SAXHandler eml = (Eml200SAXHandler) handler;
1872
            eml.deleteInlineFiles();
1873
          }
1874
       }
1875
      throw e;
1876
    }
1877

    
1878
    // run access db base on relation table and access object
1879
    runRelationAndAccessHandler(accnum, user, groups, serverCode);
1880

    
1881
    // Force replicate out the new document to each server in our server list.
1882
    // Start the thread to replicate this new document out to the other servers
1883
    // true mean it is xml document
1884
    // null is because no metacat notify the force replication.
1885
    ForceReplicationHandler frh = new ForceReplicationHandler
1886
                                                  (accnum, action, true, null);
1887

    
1888

    
1889
    MetaCatUtil.debugMessage("Conn Usage count after writting: "
1890
                                                      +conn.getUsageCount(),50);
1891
    return(accnum);
1892
  }
1893

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

    
1911
  public static String writeReplication(DBConnection conn, Reader xml,
1912
                                        String pub, Reader dtd, String action,
1913
                                        String accnum, String user,
1914
                                        String[] groups,String homeServer,
1915
                                        String notifyServer,
1916
                                        String ruleBase, boolean needValidation)
1917
                                        throws Exception
1918
  {
1919
    MetaCatUtil.debugMessage("user in replication"+ user, 30);
1920
    // Docid without revision
1921
    String docid=MetaCatUtil.getDocIdFromAccessionNumber(accnum);
1922
    // Revision specified by user (int)
1923
    int userSpecifyRev=MetaCatUtil.getRevisionFromAccessionNumber(accnum);
1924
    MetaCatUtil.debugMessage("The user specifyRev: " + userSpecifyRev, 30);
1925
    // Revision for this docid in current database
1926
    int revInDataBase=getLatestRevisionNumber(docid);
1927
    MetaCatUtil.debugMessage("The rev in data base: "+revInDataBase, 30);
1928
    // String to store the revision
1929
    String rev = null;
1930

    
1931

    
1932
    //revIndataBase=-1, there is no record in xml_documents table
1933
    //the document is a new one for local server, inert it into table
1934
    //user specified rev should be great than 0
1935
    if (revInDataBase==-1 && userSpecifyRev>=0 )
1936
    {
1937
        // rev equals user specified
1938
        rev=(new Integer(userSpecifyRev)).toString();
1939
        // action should be INSERT
1940
        action = "INSERT";
1941
    }
1942
    //rev is greater the last revsion number and revInDataBase isn't -1
1943
    // it is a updated  file
1944
    else if (userSpecifyRev>revInDataBase && revInDataBase>=0)
1945
    {
1946
       // rev equals user specified
1947
       rev=(new Integer(userSpecifyRev)).toString();
1948
       // action should be update
1949
       action = "UPDATE";
1950
    }
1951
    // local server has newer version, then notify the remote server
1952
    else if ( userSpecifyRev < revInDataBase && revInDataBase > 0)
1953
    {
1954
      throw new Exception("Local server: "+MetaCatUtil.getOption("server")+
1955
                " has newer revision of doc: "+docid+"."+revInDataBase
1956
                 +". Please notify it.");
1957
    }
1958
    //other situation
1959
    else
1960
    {
1961

    
1962
        throw new Exception("The docid"+docid+"'s revision number couldn't be "
1963
                    +userSpecifyRev);
1964
    }
1965
    // Variable to store homeserver code
1966
    int serverCode=-2;
1967

    
1968
     // If server is not int the xml replication talbe, insert it into
1969
    // xml_replication table
1970
    //serverList.addToServerListIfItIsNot(homeServer);
1971
    insertServerIntoReplicationTable(homeServer);
1972
    // Get server code again
1973
    serverCode = getServerCode(homeServer);
1974

    
1975

    
1976
    MetaCatUtil.debugMessage("Document "+docid+"."+rev+" "+action+ " into local"
1977
                               +" metacat with servercode: "+ serverCode, 10);
1978

    
1979

    
1980
    // insert into xml_nodes table
1981
    XMLReader parser = null;
1982
    try
1983
    {
1984

    
1985
      parser = initializeParser(conn, action, docid, rev,
1986
                                          user, groups, pub, serverCode, dtd,
1987
                                          ruleBase, needValidation);
1988
      conn.setAutoCommit(false);
1989
      parser.parse(new InputSource(xml));
1990
      conn.commit();
1991
      conn.setAutoCommit(true);
1992
    }
1993
    catch (Exception e)
1994
    {
1995
      conn.rollback();
1996
      conn.setAutoCommit(true);
1997
      if ( parser != null)
1998
      {
1999
          ContentHandler handler = parser.getContentHandler();
2000
          if (handler instanceof Eml200SAXHandler)
2001
          {
2002
            Eml200SAXHandler eml = (Eml200SAXHandler) handler;
2003
            eml.deleteInlineFiles();
2004
          }
2005
       }
2006
      throw e;
2007
    }
2008

    
2009
    // run write into access db base on relation table and access rule
2010
    try
2011
    {
2012
      runRelationAndAccessHandler(accnum, user, groups, serverCode);
2013
    }
2014
    catch (Exception ee)
2015
    {
2016
      MetacatReplication.replErrorLog("Failed to " +
2017
                                       "create access " +
2018
                                       "rule for package: " + accnum +
2019
                                       " because " +ee.getMessage());
2020
      MetaCatUtil.debugMessage("Failed to  " +
2021
                                       "create access " +
2022
                                       "rule for package: "+ accnum +
2023
                                       " because " +ee.getMessage(), 30);
2024
    }
2025
    //Force replication to other server
2026
    ForceReplicationHandler forceReplication = new ForceReplicationHandler
2027
                                  (accnum, action, true, notifyServer);
2028

    
2029

    
2030
    return(accnum);
2031
  }
2032

    
2033
  /* Running write record to xml_relation and xml_access*/
2034
  private static void runRelationAndAccessHandler(String accnumber,
2035
                                                  String userName,
2036
                                                  String[]group, int servercode)
2037
                                                   throws Exception
2038
  {
2039
    DBConnection dbconn = null;
2040
    int serialNumber = -1;
2041
    PreparedStatement pstmt =null;
2042
    String documenttype = getDocTypeFromDBForCurrentDocument(accnumber);
2043
    try
2044
    {
2045
      String packagedoctype = MetaCatUtil.getOption("packagedoctype");
2046
      Vector packagedoctypes = new Vector();
2047
      packagedoctypes = MetaCatUtil.getOptionList(packagedoctype);
2048
      String docIdWithoutRev = MetaCatUtil.getDocIdFromString(accnumber);
2049
      if (documenttype != null && packagedoctypes.contains(documenttype) )
2050
      {
2051
        dbconn=DBConnectionPool.
2052
           getDBConnection("DocumentImpl.runRelationAndAccessHandeler");
2053
        serialNumber=dbconn.getCheckOutSerialNumber();
2054
        dbconn.setAutoCommit(false);
2055
        // from the relations get the access file id for that package
2056
        String aclid = RelationHandler.getAccessFileID(docIdWithoutRev);
2057
        // if there are access file, write ACL for that package
2058
        if ( aclid != null )
2059
        {
2060
          runAccessControlList(dbconn, aclid, userName, group, servercode);
2061
        }
2062
        dbconn.commit();
2063
        dbconn.setAutoCommit(true);
2064
      }
2065
        // if it is an access file
2066
      else if ( documenttype != null && MetaCatUtil.getOptionList(
2067
                 MetaCatUtil.getOption("accessdoctype")).contains(documenttype))
2068
      {
2069
        dbconn=DBConnectionPool.
2070
           getDBConnection("DocumentImpl.runRelationAndAccessHandeler");
2071
        serialNumber=dbconn.getCheckOutSerialNumber();
2072
        dbconn.setAutoCommit(false);
2073
        // write ACL for the package
2074
        runAccessControlList(dbconn, docIdWithoutRev,
2075
                             userName, group, servercode);
2076
        dbconn.commit();
2077
        dbconn.setAutoCommit(true);
2078

    
2079
      }
2080

    
2081
    }
2082
    catch (Exception e)
2083
    {
2084
      if( dbconn != null)
2085
      {
2086
        dbconn.rollback();
2087
        dbconn.setAutoCommit(true);
2088
      }
2089
      MetaCatUtil.debugMessage("Error in DocumentImple.runRelationAndAccessHandler " +
2090
                                e.getMessage(), 30);
2091
      throw e;
2092
    }
2093
    finally
2094
    {
2095
      if (dbconn != null)
2096
      {
2097
        DBConnectionPool.returnDBConnection(dbconn, serialNumber);
2098
      }
2099
    }//
2100
  }
2101

    
2102
  // It runs in xmlIndex thread. It writes ACL for a package.
2103
  private static void runAccessControlList (DBConnection conn, String aclid,
2104
                                    String users, String[]group, int servercode)
2105
                                                throws Exception
2106
  {
2107
    // read the access file from xml_nodes
2108
    // parse the access file and store the access info into xml_access
2109
    AccessControlList aclobj =
2110
    new AccessControlList(conn, aclid, users, group, servercode);
2111

    
2112
  }
2113

    
2114
  /* Method get document type from db*/
2115
  private static String getDocTypeFromDBForCurrentDocument(String accnumber)
2116
                                                  throws SQLException
2117
  {
2118
    String docoumentType = null;
2119
    String docid = null;
2120
    PreparedStatement pstate = null;
2121
    ResultSet rs = null;
2122
    String sql = "SELECT doctype FROM xml_documents where docid = ?";
2123
    DBConnection dbConnection = null;
2124
    int serialNumber = -1;
2125
    try
2126
    {
2127
      //get rid of revision number
2128
      docid = MetaCatUtil.getDocIdFromString(accnumber);
2129
      dbConnection=DBConnectionPool.
2130
           getDBConnection("DocumentImpl.getDocTypeFromDBForCurrentDoc");
2131
      serialNumber=dbConnection.getCheckOutSerialNumber();
2132
      pstate = dbConnection.prepareStatement(sql);
2133
      //bind variable
2134
      pstate.setString(1, docid);
2135
      //excute query
2136
      pstate.execute();
2137
      //handle resultset
2138
      rs = pstate.getResultSet();
2139
      if (rs.next())
2140
      {
2141
        docoumentType = rs.getString(1);
2142
      }
2143
      rs.close();
2144
      pstate.close();
2145
    }//try
2146
    catch (SQLException e)
2147
    {
2148
      MetaCatUtil.debugMessage("error in DocumentImpl."+
2149
                      "getDocTypeFromDBForCurrentDocument "+e.getMessage(), 30);
2150
      throw e;
2151
    }//catch
2152
    finally
2153
    {
2154
      pstate.close();
2155
      DBConnectionPool.returnDBConnection(dbConnection, serialNumber);
2156
    }//
2157
    MetaCatUtil.debugMessage("The current doctype from db is: "+
2158
                              docoumentType, 35);
2159
    return docoumentType;
2160
  }
2161
  /**
2162
   * Delete an XML file from the database (actually, just make it a revision
2163
   * in the xml_revisions table)
2164
   *
2165
   * @param docid the ID of the document to be deleted from the database
2166
   */
2167
  public static void delete(String accnum,
2168
                                 String user, String[] groups )
2169
                throws Exception
2170
  {
2171
    // OLD
2172
    //DocumentIdentifier id = new DocumentIdentifier(accnum);
2173
    //String docid = id.getIdentifier();
2174
    //String rev = id.getRev();
2175

    
2176
    // OLD
2177
    // Determine if the docid,rev are OK for DELETE
2178
    //AccessionNumber ac = new AccessionNumber(conn);
2179
    //docid = ac.generate(docid, rev, "DELETE");
2180
    DBConnection conn = null;
2181
    int serialNumber = -1;
2182
    PreparedStatement pstmt =null;
2183
    try
2184
    {
2185
      //check out DBConnection
2186
      conn=DBConnectionPool.
2187
                    getDBConnection("DocumentImpl.delete");
2188
      serialNumber=conn.getCheckOutSerialNumber();
2189

    
2190
      // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
2191
      AccessionNumber ac = new AccessionNumber(accnum, "DELETE");
2192
      String docid = ac.getDocid();
2193
      String rev = ac.getRev();
2194

    
2195
      MetaCatUtil.debugMessage("Start deleting doc "+docid+ "...", 20);
2196
    // check for 'write' permission for 'user' to delete this document
2197
      if ( !hasWritePermission(user, groups, accnum) ) {
2198
        throw new Exception("User " + user +
2199
              " does not have permission to delete XML Document #" + accnum);
2200
      }
2201

    
2202
      conn.setAutoCommit(false);
2203
      // Copy the record to the xml_revisions table
2204
      DocumentImpl.archiveDocRevision(conn, docid, user );
2205

    
2206
      // Now delete it from the xml_index table
2207
      boolean useXMLIndex =
2208
          (new Boolean(MetaCatUtil.getOption("usexmlindex"))).booleanValue();
2209
      //if (useXMLIndex) {
2210
          pstmt = conn.prepareStatement("DELETE FROM xml_index WHERE docid = ?");
2211
          pstmt.setString(1,docid);
2212
          pstmt.execute();
2213
          pstmt.close();
2214
          conn.increaseUsageCount(1);
2215
      //}
2216

    
2217
      //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid + "'");
2218
      // Now delete it from xml_access table
2219
      pstmt = conn.
2220
              prepareStatement("DELETE FROM xml_access WHERE accessfileid = ?");
2221
      pstmt.setString(1, docid);
2222
      pstmt.execute();
2223
      pstmt.close();
2224
      conn.increaseUsageCount(1);
2225

    
2226
      // Delete it from relation table
2227
      pstmt = conn.
2228
               prepareStatement("DELETE FROM xml_relation WHERE docid = ?");
2229
      //increase usage count
2230
      conn.increaseUsageCount(1);
2231
      pstmt.setString(1, docid);
2232
      pstmt.execute();
2233
      pstmt.close();
2234

    
2235
      // Delete it from xml_accesssubtree table
2236
      pstmt = conn.
2237
               prepareStatement("DELETE FROM xml_accesssubtree WHERE docid = ?");
2238
      //increase usage count
2239
      conn.increaseUsageCount(1);
2240
      pstmt.setString(1, docid);
2241
      pstmt.execute();
2242
      pstmt.close();
2243

    
2244
      // Delete it from xml_doucments table
2245
      pstmt =conn.prepareStatement("DELETE FROM xml_documents WHERE docid = ?");
2246
      pstmt.setString(1, docid);
2247
      pstmt.execute();
2248
      pstmt.close();
2249
      //Usaga count increase 1
2250
      conn.increaseUsageCount(1);
2251

    
2252
      conn.commit();
2253
      conn.setAutoCommit(true);
2254
    }//try
2255
    catch (Exception e)
2256
    {
2257
      MetaCatUtil.debugMessage("error in DocumentImpl.delete: " +
2258
                                e.getMessage(), 30);
2259
      throw e;
2260
    }
2261
    finally
2262
    {
2263

    
2264
      try
2265
      {
2266
        // close preparedStatement
2267
        pstmt.close();
2268
      }//try
2269
      finally
2270
      {
2271
        //check in DBonnection
2272
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2273
      }//finally
2274
    }//finally
2275
    //IF this is a package document:
2276
    //delete all of the relations that this document created.
2277
    //if the deleted document is a package document its relations should
2278
    //no longer be active if it has been deleted from the system.
2279

    
2280
  }
2281

    
2282
  /**
2283
    * Check for "WRITE" permission on @docid for @user and/or @groups
2284
    * from DB connection
2285
    */
2286
  private static boolean hasWritePermission (String user,
2287
                                  String[] groups, String docid )
2288
                  throws SQLException, Exception
2289
  {
2290
    // Check for WRITE permission on @docid for @user and/or @groups
2291
    PermissionController controller = new PermissionController(docid);
2292
    return controller.hasPermission(user,groups,
2293
                                    AccessControlInterface.WRITESTRING);
2294
  }
2295

    
2296
  /**
2297
    * Check for "READ" permission base on docid, user and group
2298
    *@docid, the document
2299
    *@user, user name
2300
    *@group, user's group
2301
    *
2302
    */
2303
  public static boolean hasReadPermission (String user,
2304
                                  String[] groups, String docId )
2305
                  throws SQLException, Exception
2306
  {
2307
    // Check for READ permission on @docid for @user and/or @groups
2308
    PermissionController controller =
2309
                        new PermissionController(docId);
2310
    return controller.hasPermission(user,groups,
2311
                                            AccessControlInterface.READSTRING);
2312
  }
2313

    
2314

    
2315
   /**
2316
   * Set up the parser handlers for writing the document to the database
2317
   */
2318
  private static XMLReader initializeParser(DBConnection dbconn, String action,
2319
                                            String docid, String rev,
2320
                                            String user,
2321
                                            String[] groups, String pub,
2322
                                            int serverCode, Reader dtd,
2323
                                            String ruleBase,
2324
                                            boolean needValidation)
2325
                                            throws Exception
2326
  {
2327
    XMLReader parser = null;
2328
    try
2329
    {
2330
      // handler
2331
      ContentHandler chandler;
2332
      EntityResolver eresolver;
2333
      DTDHandler dtdhandler;
2334
      // Get an instance of the parser
2335
      String parserName = MetaCatUtil.getOption("saxparser");
2336
      parser = XMLReaderFactory.createXMLReader(parserName);
2337
      if (ruleBase != null && ruleBase.equals(EML200))
2338
      {
2339
        MetaCatUtil.debugMessage("eml 2.0.0 parser", 20);
2340
        chandler = new Eml200SAXHandler(dbconn, action,
2341
                                    docid, rev, user, groups, pub, serverCode);
2342
        parser.setContentHandler((ContentHandler)chandler);
2343
        parser.setErrorHandler((ErrorHandler)chandler);
2344
        parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2345
        parser.setProperty(LEXICALPROPERTY, chandler);
2346
        // turn on schema validation feature
2347
        parser.setFeature(VALIDATIONFEATURE, true);
2348
        parser.setFeature(NAMESPACEFEATURE, true);
2349
        //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2350
        parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2351
        // From DB to find the register external schema location
2352
        String externalSchemaLocation = null;
2353
        SchemaLocationResolver resolver = new SchemaLocationResolver();
2354
        externalSchemaLocation = resolver.getNameSpaceAndLocationString();
2355
        // Set external schemalocation.
2356
        if (externalSchemaLocation != null &&
2357
            !(externalSchemaLocation.trim()).equals(""))
2358
        {
2359
            parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2360
                             externalSchemaLocation);
2361
        }
2362
      } else if (ruleBase != null && ruleBase.equals(EML210))
2363
      {
2364
        MetaCatUtil.debugMessage("eml 2.1.0 parser", 20);
2365
        chandler = new Eml210SAXHandler(dbconn, action,
2366
                                    docid, rev, user, groups, pub, serverCode);
2367
        parser.setContentHandler((ContentHandler)chandler);
2368
        parser.setErrorHandler((ErrorHandler)chandler);
2369
        parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2370
        parser.setProperty(LEXICALPROPERTY, chandler);
2371
        // turn on schema validation feature
2372
        parser.setFeature(VALIDATIONFEATURE, true);
2373
        parser.setFeature(NAMESPACEFEATURE, true);
2374
        //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2375
        parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2376
        // From DB to find the register external schema location
2377
        String externalSchemaLocation = null;
2378
        SchemaLocationResolver resolver = new SchemaLocationResolver();
2379
        externalSchemaLocation = resolver.getNameSpaceAndLocationString();
2380
        // Set external schemalocation.
2381
        if (externalSchemaLocation != null &&
2382
            !(externalSchemaLocation.trim()).equals(""))
2383
        {
2384
            parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2385
                             externalSchemaLocation);
2386
        }
2387
      }
2388
      else
2389
      {
2390
        //create a DBSAXHandler object which has the revision specification
2391
        chandler = new DBSAXHandler(dbconn, action,
2392
                                    docid, rev, user, groups, pub, serverCode);
2393
        parser.setContentHandler((ContentHandler)chandler);
2394
        parser.setErrorHandler((ErrorHandler)chandler);
2395
        parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2396
        parser.setProperty(LEXICALPROPERTY, chandler);
2397

    
2398
        if (ruleBase != null && ruleBase.equals(SCHEMA) && needValidation)
2399
        {
2400
          MetaCatUtil.debugMessage("General schema parser", 20);
2401
          // turn on schema validation feature
2402
          parser.setFeature(VALIDATIONFEATURE, true);
2403
          parser.setFeature(NAMESPACEFEATURE, true);
2404
          //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2405
          parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2406
          // From DB to find the register external schema location
2407
          String externalSchemaLocation = null;
2408
          SchemaLocationResolver resolver = new SchemaLocationResolver();
2409
          externalSchemaLocation = resolver.getNameSpaceAndLocationString();
2410
          // Set external schemalocation.
2411
          if (externalSchemaLocation != null &&
2412
            !(externalSchemaLocation.trim()).equals(""))
2413
          {
2414
            parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2415
                             externalSchemaLocation);
2416
          }
2417

    
2418
        }
2419
        else if (ruleBase != null && ruleBase.equals(DTD) && needValidation)
2420
        {
2421
          MetaCatUtil.debugMessage("dtd parser", 20);
2422
          // turn on dtd validaton feature
2423
          parser.setFeature(VALIDATIONFEATURE, true);
2424
          eresolver= new DBEntityResolver(dbconn, (DBSAXHandler)chandler, dtd);
2425
          dtdhandler = new DBDTDHandler(dbconn);
2426
          parser.setEntityResolver((EntityResolver)eresolver);
2427
          parser.setDTDHandler((DTDHandler)dtdhandler);
2428
        }
2429
        else
2430
        {
2431
          MetaCatUtil.debugMessage("other parser", 20);
2432
          // non validation
2433
          parser.setFeature(VALIDATIONFEATURE, false);
2434
          eresolver= new DBEntityResolver(dbconn, (DBSAXHandler)chandler, dtd);
2435
          dtdhandler = new DBDTDHandler(dbconn);
2436
          parser.setEntityResolver((EntityResolver)eresolver);
2437
          parser.setDTDHandler((DTDHandler)dtdhandler);
2438
        }
2439
      }//else
2440
    }
2441
    catch (Exception e)
2442
    {
2443
      throw e;
2444
    }
2445
    return parser;
2446
  }
2447

    
2448

    
2449
  /**
2450
   * Set up the parser handlers for writing the document to the database
2451
   */
2452
  /*private static XMLReader initializeParser(DBConnection dbconn, String action,
2453
                               String docid, String rev, boolean validate,
2454
                                   String user, String[] groups, String pub,
2455
                                   int serverCode, Reader dtd)
2456
                           throws Exception
2457
  {
2458
    XMLReader parser = null;
2459
    //DBConnection conn = null;
2460
    //int serialNumber = -1;
2461
    //
2462
    // Set up the SAX document handlers for parsing
2463
    //
2464
    try {
2465
       //check out DBConnection
2466

    
2467
      //create a DBSAXHandler object which has the revision specification
2468
      ContentHandler chandler = new DBSAXHandler(dbconn, action,
2469
                                    docid, rev, user, groups, pub, serverCode);
2470
      EntityResolver eresolver= new DBEntityResolver(dbconn,
2471
                                                 (DBSAXHandler)chandler, dtd);
2472
      DTDHandler dtdhandler   = new DBDTDHandler(dbconn);
2473

    
2474
      // Get an instance of the parser
2475
      String parserName = MetaCatUtil.getOption("saxparser");
2476
      parser = XMLReaderFactory.createXMLReader(parserName);
2477

    
2478
      // Turn on validation
2479
      parser.setFeature("http://xml.org/sax/features/validation", validate);
2480
      // Turn off Including all external parameter entities
2481
      // (the external DTD subset also)
2482
      // Doesn't work well, probably the feature name is not correct
2483
      // parser.setFeature(
2484
      //  "http://xml.org/sax/features/external-parameter-entities", false);
2485

    
2486
      // Set Handlers in the parser
2487
      parser.setProperty("http://xml.org/sax/properties/declaration-handler",
2488
                         chandler);
2489
      parser.setProperty("http://xml.org/sax/properties/lexical-handler",
2490
                         chandler);
2491
      parser.setContentHandler((ContentHandler)chandler);
2492
      parser.setEntityResolver((EntityResolver)eresolver);
2493
      parser.setDTDHandler((DTDHandler)dtdhandler);
2494
      parser.setErrorHandler((ErrorHandler)chandler);
2495

    
2496
    } catch (Exception e) {
2497
      throw e;
2498
    }
2499
    //finally
2500
    //{
2501
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
2502
    //}
2503

    
2504
    return parser;
2505
  }*/
2506

    
2507
  /**
2508
   * Save a document entry in the xml_revisions table
2509
   * Connection use as a paramter is in order to rollback feature
2510
   */
2511
  private static void archiveDocRevision(DBConnection dbconn, String docid,
2512
                                                    String user)
2513
 {
2514
    String sysdate = dbAdapter.getDateTimeFunction();
2515
    //DBConnection conn = null;
2516
    //int serialNumber = -1;
2517
    PreparedStatement pstmt = null;
2518

    
2519
    // create a record in xml_revisions table
2520
    // for that document as selected from xml_documents
2521

    
2522
   try
2523
   {
2524
     //check out DBConnection
2525
     /*conn=DBConnectionPool.
2526
                    getDBConnection("DocumentImpl.archiveDocRevision");
2527
     serialNumber=conn.getCheckOutSerialNumber();*/
2528
     pstmt = dbconn.prepareStatement(
2529
      "INSERT INTO xml_revisions " +
2530
        "(docid, rootnodeid, docname, doctype, " +
2531
        "user_owner, user_updated, date_created, date_updated, " +
2532
        "server_location, rev, public_access, catalog_id) " +
2533
      "SELECT ?, rootnodeid, docname, doctype, " +
2534
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
2535
        "server_location, rev, public_access, catalog_id " +
2536
      "FROM xml_documents " +
2537
      "WHERE docid = ?");
2538
      // Increase dbconnection usage count
2539
      dbconn.increaseUsageCount(1);
2540
      // Bind the values to the query and execute it
2541
      pstmt.setString(1, docid);
2542
      pstmt.setString(2, user);
2543
      pstmt.setString(3, docid);
2544
      pstmt.execute();
2545
      pstmt.close();
2546
   }//try
2547
   catch (SQLException e)
2548
   {
2549
     MetaCatUtil.debugMessage("Error in DocumentImpl.archiveDocRevision : "+
2550
                                e.getMessage(), 30);
2551
   }//catch
2552
   finally
2553
   {
2554
     try
2555
     {
2556
       pstmt.close();
2557
     }//try
2558
     catch (SQLException ee)
2559
     {
2560
       MetaCatUtil.debugMessage("Error in DocumnetImpl.archiveDocRevision: "+
2561
                                  ee.getMessage(), 50);
2562
     }//catch
2563
     //finally
2564
     //{
2565

    
2566
       //check in DBConnection
2567
       //DBConnectionPool.returnDBConnection(conn, serialNumber);
2568
     //}//finally
2569
   }//finnally
2570

    
2571

    
2572
  }//achiveDocRevision
2573

    
2574
  /** Save a document entry in the xml_revisions table */
2575
  private static void archiveDocRevision(String docid, String user)
2576
 {
2577
    String sysdate = dbAdapter.getDateTimeFunction();
2578
    DBConnection conn = null;
2579
    int serialNumber = -1;
2580
    PreparedStatement pstmt = null;
2581

    
2582
    // create a record in xml_revisions table
2583
    // for that document as selected from xml_documents
2584

    
2585
   try
2586
   {
2587
     //check out DBConnection
2588
     conn=DBConnectionPool.
2589
                    getDBConnection("DocumentImpl.archiveDocRevision");
2590
     serialNumber=conn.getCheckOutSerialNumber();
2591
     pstmt = conn.prepareStatement(
2592
      "INSERT INTO xml_revisions " +
2593
        "(docid, rootnodeid, docname, doctype, " +
2594
        "user_owner, user_updated, date_created, date_updated, " +
2595
        "server_location, rev, public_access, catalog_id) " +
2596
      "SELECT ?, rootnodeid, docname, doctype, " +
2597
        "user_owner, ?, " + sysdate + ", " + sysdate + ", "+
2598
        "server_location, rev, public_access, catalog_id " +
2599
      "FROM xml_documents " +
2600
      "WHERE docid = ?");
2601
      // Bind the values to the query and execute it
2602
      pstmt.setString(1, docid);
2603
      pstmt.setString(2, user);
2604
      pstmt.setString(3, docid);
2605
      pstmt.execute();
2606
      pstmt.close();
2607
   }//try
2608
   catch (SQLException e)
2609
   {
2610
     MetaCatUtil.debugMessage("Error in DocumentImpl.archiveDocRevision : "+
2611
                                e.getMessage(), 30);
2612
   }//catch
2613
   finally
2614
   {
2615
     try
2616
     {
2617
       pstmt.close();
2618
     }//try
2619
     catch (SQLException ee)
2620
     {
2621
       MetaCatUtil.debugMessage("Error in DocumnetImpl.archiveDocRevision: "+
2622
                                  ee.getMessage(), 50);
2623
     }//catch
2624
     finally
2625
     {
2626
       //check in DBConnection
2627
       DBConnectionPool.returnDBConnection(conn, serialNumber);
2628
     }//finally
2629
   }//finnally
2630

    
2631

    
2632
  }//achiveDocRevision
2633

    
2634
  /**
2635
    * delete a entry in xml_table for given docid
2636
    * @param docId, the id of the document need to be delete
2637
    */
2638
  private static void deleteXMLDocuments(String docId)
2639
                                         throws SQLException
2640
  {
2641
    DBConnection conn = null;
2642
    int serialNumber = -1;
2643
    PreparedStatement pStmt = null;
2644
    try
2645
    {
2646
      //check out DBConnection
2647
      conn=DBConnectionPool.
2648
                    getDBConnection("DocumentImpl.deleteXMLDocuments");
2649
      serialNumber=conn.getCheckOutSerialNumber();
2650
      //delete a record
2651
      pStmt =
2652
             conn.prepareStatement("DELETE FROM xml_documents WHERE docid = '"
2653
                                              + docId + "'");
2654
    pStmt.execute();
2655
    }//try
2656
    finally
2657
    {
2658
      try
2659
      {
2660
        pStmt.close();
2661
      }//try
2662
      catch (SQLException e)
2663
      {
2664
        MetaCatUtil.debugMessage("error in DocumentImpl.deleteXMLDocuments: "+
2665
                                  e.getMessage(), 50);
2666
      }//catch
2667
      finally
2668
      {
2669
        //return back DBconnection
2670
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2671
      }//finally
2672
    }//finally
2673

    
2674

    
2675
  }//deleteXMLDocuments
2676

    
2677
  /**
2678
    * Get last revision number from database for a docid
2679
    * If couldn't find an entry,  -1 will return
2680
    * The return value is integer because we want compare it to there new one
2681
    * @param docid <sitecode>.<uniqueid> part of Accession Number
2682
    */
2683
  public static int getLatestRevisionNumber(String docId)
2684
                                      throws SQLException
2685
  {
2686
    int rev = 1;
2687
    PreparedStatement pStmt = null;
2688
    DBConnection dbConn = null;
2689
    int serialNumber = -1;
2690
    // get rid of rev
2691
    docId = MetaCatUtil.getDocIdFromString(docId);
2692
    try
2693
    {
2694
      //check out DBConnection
2695
      dbConn=DBConnectionPool.
2696
                    getDBConnection("DocumentImpl.getLatestRevisionNumber");
2697
      serialNumber=dbConn.getCheckOutSerialNumber();
2698

    
2699
      pStmt = dbConn.prepareStatement
2700
              ("SELECT rev FROM xml_documents WHERE docid='" + docId + "'");
2701
      pStmt.execute();
2702

    
2703
      ResultSet rs = pStmt.getResultSet();
2704
      boolean hasRow = rs.next();
2705
      if (hasRow)
2706
      {
2707
        rev = rs.getInt(1);
2708
        pStmt.close();
2709
      }
2710
      else
2711
      {
2712
        rev=-1;
2713
        pStmt.close();
2714
      }
2715
    }//try
2716
    finally
2717
    {
2718
      try
2719
      {
2720
        pStmt.close();
2721
      }
2722
      catch (Exception ee)
2723
      {
2724
        MetaCatUtil.debugMessage("Error in DocumentImpl."+
2725
                        "getLatestRevisionNumber: "+ee.getMessage(), 50);
2726
      }
2727
      finally
2728
      {
2729
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2730
      }
2731
    }//finally
2732

    
2733
    return rev;
2734
  }//getLatestRevisionNumber
2735

    
2736
  /**
2737
   * Get server location form database for a accNum
2738
   *
2739
   * @param accum <sitecode>.<uniqueid>.<rev>
2740
   */
2741
  private static int getServerLocationNumber(String accNum)
2742
                                            throws SQLException
2743
  {
2744
    //get rid of revNum part
2745
    String docId=MetaCatUtil.getDocIdFromString(accNum);
2746
    PreparedStatement pStmt = null;
2747
    int serverLocation = 1;
2748
    DBConnection conn = null;
2749
    int serialNumber = -1;
2750

    
2751
    try
2752
    {
2753
      //check out DBConnection
2754
      conn=DBConnectionPool.
2755
                    getDBConnection("DocumentImpl.getServerLocationNumber");
2756
      serialNumber=conn.getCheckOutSerialNumber();
2757

    
2758
      pStmt = conn.prepareStatement
2759
      ("SELECT server_location FROM xml_documents WHERE docid='" + docId + "'");
2760
      pStmt.execute();
2761

    
2762
      ResultSet rs = pStmt.getResultSet();
2763
      boolean hasRow = rs.next();
2764
      //if there is entry in xml_documents, get the serverlocation
2765
      if (hasRow)
2766
      {
2767
        serverLocation = rs.getInt(1);
2768
        pStmt.close();
2769
      }
2770
      else
2771
      {
2772
        //if htere is no entry in xml_documents, we consider it is new document
2773
        //the server location is local host and value is 1
2774
        serverLocation=1;
2775
        pStmt.close();
2776
      }
2777
    }//try
2778
    finally
2779
    {
2780
      try
2781
      {
2782
        pStmt.close();
2783
      }//try
2784
      catch (Exception ee)
2785
      {
2786
        MetaCatUtil.debugMessage("Error in DocumentImpl.getServerLocationNu(): "
2787
                                    +ee.getMessage(), 50);
2788
      }//catch
2789
      finally
2790
      {
2791
        DBConnectionPool.returnDBConnection(conn, serialNumber);
2792
      }//finally
2793
    }//finally
2794

    
2795
    return serverLocation;
2796
  }
2797

    
2798
  /**
2799
   * Given a server name, return its servercode in xml_replication table.
2800
   * If no server is found, -1 will return
2801
   * @param serverName,
2802
   */
2803
  private static int getServerCode(String serverName)
2804
  {
2805
    PreparedStatement pStmt=null;
2806
    int serverLocation=-2;
2807
    DBConnection dbConn = null;
2808
    int serialNumber = -1;
2809
    //MetaCatUtil util = new MetaCatUtil();
2810

    
2811

    
2812
    //we should consider about local host too
2813
    if (serverName.equals(MetaCatUtil.getLocalReplicationServerName()))
2814
    {
2815
      serverLocation=1;
2816
      return serverLocation;
2817
    }
2818

    
2819

    
2820
    try
2821
    {
2822
      //check xml_replication table
2823
      //dbConn=util.openDBConnection();
2824
      //check out DBConnection
2825
      dbConn=DBConnectionPool.getDBConnection("DocumentImpl.getServerCode");
2826
      serialNumber=dbConn.getCheckOutSerialNumber();
2827
      pStmt = dbConn.prepareStatement
2828
      ("SELECT serverid FROM xml_replication WHERE server='" + serverName +"'");
2829
      pStmt.execute();
2830

    
2831
      ResultSet rs = pStmt.getResultSet();
2832
      boolean hasRow = rs.next();
2833
      //if there is entry in xml_replication, get the serverid
2834
      if (hasRow)
2835
      {
2836
        serverLocation = rs.getInt(1);
2837
        pStmt.close();
2838
      }
2839
      else
2840
      {
2841
        // if htere is no entry in xml_replication, -1 will return
2842
        serverLocation=-1;
2843
        pStmt.close();
2844
      }
2845
    }
2846
    catch (Exception e)
2847
    {
2848
      MetaCatUtil.debugMessage("Error in DocumentImpl.getServerCode(): "
2849
                                    +e.getMessage(), 30);
2850
    }
2851
    finally
2852
    {
2853
      try
2854
      {
2855
        pStmt.close();
2856
      }
2857
      catch (Exception ee)
2858
      {
2859
        MetaCatUtil.debugMessage("Error in DocumentImpl.getServerCode(): "
2860
                                    +ee.getMessage(), 50);
2861
      }
2862
      finally
2863
      {
2864
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2865
      }
2866
    }
2867

    
2868

    
2869
    return serverLocation;
2870
  }
2871

    
2872
  /**
2873
   * Insert a server into xml_replcation table
2874
   * @param server, the name of server
2875
   */
2876
  private static synchronized void
2877
                                insertServerIntoReplicationTable(String server)
2878
  {
2879
    PreparedStatement pStmt=null;
2880
    DBConnection dbConn = null;
2881
    int serialNumber = -1;
2882

    
2883
    // Initial value for the server
2884
    int replicate = 0;
2885
    int dataReplicate = 0;
2886
    int hub = 0;
2887

    
2888
    try
2889
    {
2890
       // Get DBConnection
2891
       dbConn=DBConnectionPool.
2892
                getDBConnection("DocumentImpl.insertServIntoReplicationTable");
2893
       serialNumber=dbConn.getCheckOutSerialNumber();
2894

    
2895
      // Compare the server to dabase
2896
      pStmt = dbConn.prepareStatement
2897
      ("SELECT serverid FROM xml_replication WHERE server='" + server +"'");
2898
      pStmt.execute();
2899
      ResultSet rs = pStmt.getResultSet();
2900
      boolean hasRow = rs.next();
2901
      // Close preparedstatement and result set
2902
      pStmt.close();
2903
      rs.close();
2904

    
2905
      // If the server is not in the table, and server is not local host,
2906
      // insert it
2907
      if ( !hasRow
2908
         && !server.equals(MetaCatUtil.getLocalReplicationServerName()))
2909
      {
2910
        // Set auto commit false
2911
        dbConn.setAutoCommit(false);
2912
        /*pStmt = dbConn.prepareStatement("INSERT INTO xml_replication " +
2913
                      "(server, last_checked, replicate, datareplicate, hub) " +
2914
                       "VALUES ('" + server + "', to_date(" +
2915
                       "'01/01/00', 'MM/DD/YY'), '" +
2916
                       replicate +"', '"+dataReplicate+"','"+ hub + "')");*/
2917
        pStmt = dbConn.prepareStatement("INSERT INTO xml_replication " +
2918
                      "(server, last_checked, replicate, datareplicate, hub) " +
2919
                       "VALUES ('" + server + "', " +
2920
                       dbAdapter.toDate("01/01/1980", "MM/DD/YYYY") + ", '" +
2921
                       replicate +"', '"+dataReplicate+"','"+ hub + "')");
2922

    
2923

    
2924
        pStmt.execute();
2925
        dbConn.commit();
2926
        // Increase usage number
2927
        dbConn.increaseUsageCount(1);
2928
        pStmt.close();
2929

    
2930
      }
2931
    }//try
2932
    catch (Exception e)
2933
    {
2934
      MetaCatUtil.debugMessage("Error in DocumentImpl.insertServerIntoRepli(): "
2935
                                    +e.getMessage(), 30);
2936
    }//catch
2937
    finally
2938
    {
2939

    
2940
      try
2941
      {
2942
        // Set auto commit true
2943
        dbConn.setAutoCommit(true);
2944
        pStmt.close();
2945

    
2946
      }//try
2947
      catch (Exception ee)
2948
      {
2949
        MetaCatUtil.debugMessage("Error in DocumentImpl.insetServerIntoRepl(): "
2950
                                    +ee.getMessage(), 50);
2951
      }//catch
2952
      finally
2953
      {
2954
        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2955
      }
2956

    
2957
    }//finally
2958

    
2959
  }
2960

    
2961

    
2962
  /**
2963
   * the main routine used to test the DBWriter utility.
2964
   * <p>
2965
   * Usage: java DocumentImpl <-f filename -a action -d docid>
2966
   *
2967
   * @param filename the filename to be loaded into the database
2968
   * @param action the action to perform (READ, INSERT, UPDATE, DELETE)
2969
   * @param docid the id of the document to process
2970
   */
2971
  static public void main(String[] args) {
2972
    DBConnection dbconn = null;
2973
    int serialNumber = -1;
2974
    try {
2975
      String filename    = null;
2976
      String dtdfilename = null;
2977
      String action      = null;
2978
      String docid       = null;
2979
      boolean showRuntime = false;
2980
      boolean useOldReadAlgorithm = false;
2981

    
2982
      // Parse the command line arguments
2983
      for ( int i=0 ; i < args.length; ++i ) {
2984
        if ( args[i].equals( "-f" ) ) {
2985
          filename =  args[++i];
2986
        } else if ( args[i].equals( "-r" ) ) {
2987
          dtdfilename =  args[++i];
2988
        } else if ( args[i].equals( "-a" ) ) {
2989
          action =  args[++i];
2990
        } else if ( args[i].equals( "-d" ) ) {
2991
          docid =  args[++i];
2992
        } else if ( args[i].equals( "-t" ) ) {
2993
          showRuntime = true;
2994
        } else if ( args[i].equals( "-old" ) ) {
2995
          useOldReadAlgorithm = true;
2996
        } else {
2997
          System.err.println
2998
            ( "   args[" +i+ "] '" +args[i]+ "' ignored." );
2999
        }
3000
      }
3001

    
3002
      // Check if the required arguments are provided
3003
      boolean argsAreValid = false;
3004
      if (action != null) {
3005
        if (action.equals("INSERT")) {
3006
          if (filename != null) {
3007
            argsAreValid = true;
3008
          }
3009
        } else if (action.equals("UPDATE")) {
3010
          if ((filename != null) && (docid != null)) {
3011
            argsAreValid = true;
3012
          }
3013
        } else if (action.equals("DELETE")) {
3014
          if (docid != null) {
3015
            argsAreValid = true;
3016
          }
3017
        } else if (action.equals("READ")) {
3018
          if (docid != null) {
3019
            argsAreValid = true;
3020
          }
3021
        }
3022
      }
3023

    
3024
      // Print usage message if the arguments are not valid
3025
      if (!argsAreValid) {
3026
        System.err.println("Wrong number of arguments!!!");
3027
        System.err.println(
3028
          "USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "+
3029
          "[-r dtdfilename]");
3030
        System.err.println(
3031
          "   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> " +
3032
          "[-r dtdfilename]");
3033
        System.err.println(
3034
          "   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
3035
        System.err.println(
3036
          "   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
3037
        return;
3038
      }
3039

    
3040
      // Time the request if asked for
3041
      double startTime = System.currentTimeMillis();
3042

    
3043
      // Open a connection to the database
3044
      MetaCatUtil util = new MetaCatUtil();
3045

    
3046
      dbconn=DBConnectionPool.getDBConnection("DocumentImpl.main");
3047
      serialNumber=dbconn.getCheckOutSerialNumber();
3048

    
3049
      double connTime = System.currentTimeMillis();
3050
      // Execute the action requested (READ, INSERT, UPDATE, DELETE)
3051
      if (action.equals("READ")) {
3052
          DocumentImpl xmldoc = new DocumentImpl(docid );
3053
          if (useOldReadAlgorithm) {
3054
            System.out.println(xmldoc.readUsingSlowAlgorithm());
3055
          } else {
3056
            xmldoc.toXml(new PrintWriter(System.out), null, null, true);
3057
          }
3058
      } else if (action.equals("DELETE")) {
3059
        DocumentImpl.delete(docid, null, null);
3060
        System.out.println("Document deleted: " + docid);
3061
      } else {
3062
        /*String newdocid = DocumentImpl.write(dbconn, filename, null,
3063
                                             dtdfilename, action, docid,
3064
                                             null, null);
3065
        if ((docid != null) && (!docid.equals(newdocid))) {
3066
          if (action.equals("INSERT")) {
3067
            System.out.println("New document ID generated!!! ");
3068
          } else if (action.equals("UPDATE")) {
3069
            System.out.println("ERROR: Couldn't update document!!! ");
3070
          }
3071
        } else if ((docid == null) && (action.equals("UPDATE"))) {
3072
          System.out.println("ERROR: Couldn't update document!!! ");
3073
        }
3074
        System.out.println("Document processing finished for: " + filename
3075
              + " (" + newdocid + ")");*/
3076
      }
3077

    
3078
      double stopTime = System.currentTimeMillis();
3079
      double dbOpenTime = (connTime - startTime)/1000;
3080
      double insertTime = (stopTime - connTime)/1000;
3081
      double executionTime = (stopTime - startTime)/1000;
3082
      if (showRuntime) {
3083
        System.out.println("\n\nTotal Execution time was: " +
3084
                           executionTime + " seconds.");
3085
        System.out.println("Time to open DB connection was: " + dbOpenTime +
3086
                           " seconds.");
3087
        System.out.println("Time to insert document was: " + insertTime +
3088
                           " seconds.");
3089
      }
3090
      dbconn.close();
3091
    } catch (McdbException me) {
3092
      me.toXml(new PrintWriter(System.err));
3093
    } catch (AccessionNumberException ane) {
3094
      System.out.println(ane.getMessage());
3095
    } catch (Exception e) {
3096
      System.err.println("EXCEPTION HANDLING REQUIRED");
3097
      System.err.println(e.getMessage());
3098
      e.printStackTrace(System.err);
3099
    }
3100
    finally
3101
    {
3102
      // Return db connection
3103
      DBConnectionPool.returnDBConnection(dbconn, serialNumber);
3104
    }
3105
  }
3106
}
(31-31/62)