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: jones $'
10
 *     '$Date: 2004-09-16 12:08:42 -0700 (Thu, 16 Sep 2004) $'
11
 * '$Revision: 2293 $'
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.HashMap;
47
import java.util.Iterator;
48
import java.util.Stack;
49
import java.util.TreeSet;
50
import java.util.Vector;
51

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

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

    
62
/**
63
 * A class that represents an XML document. It can be created with a simple
64
 * document identifier from a database connection. It also will write an XML
65
 * text document to a database connection using SAX.
66
 */
67
public class DocumentImpl
68
{
69
    /* Constants */
70
    public static final String SCHEMA = "Schema";
71
    public static final String DTD = "DTD";
72
    public static final String EML200 = "eml200";
73
    public static final String EML210 = "eml210";
74
    public static final String EXTERNALSCHEMALOCATIONPROPERTY = "http://apache.org/xml/properties/schema/external-schemaLocation";
75

    
76
    /*
77
     * public static final String EXTERNALSCHEMALOCATION =
78
     * "eml://ecoinformatics.org/eml-2.0.0
79
     * http://dev.nceas.ucsb.edu/tao/schema/eml.xsd"+ "
80
     * http://www.xml-cml.org/schema/stmml
81
     * http://dev.nceas.ucsb.edu/tao/schema/stmml.xsd";
82
     */
83
    public static final String DECLARATIONHANDLERPROPERTY = "http://xml.org/sax/properties/declaration-handler";
84
    public static final String LEXICALPROPERTY = "http://xml.org/sax/properties/lexical-handler";
85
    public static final String VALIDATIONFEATURE = "http://xml.org/sax/features/validation";
86
    public static final String SCHEMAVALIDATIONFEATURE = "http://apache.org/xml/features/validation/schema";
87
    public static final String NAMESPACEFEATURE = "http://xml.org/sax/features/namespaces";
88
    public static final String NAMESPACEPREFIXESFEATURE = "http://xml.org/sax/features/namespace-prefixes";
89
    public static final String EML2_1_0NAMESPACE = MetaCatUtil
90
            .getOption("eml2_1_0namespace");
91
    // "eml://ecoinformatics.org/eml-2.1.0";
92
    public static final String EML2_0_1NAMESPACE = MetaCatUtil
93
            .getOption("eml2_0_1namespace");
94
    // "eml://ecoinformatics.org/eml-2.0.1";
95
    public static final String EML2_0_0NAMESPACE = MetaCatUtil
96
            .getOption("eml2_0_0namespace");
97
    // "eml://ecoinformatics.org/eml-2.0.0";
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
    private DBConnection connection = null;
106
    private String docid = null;
107
    private String updatedVersion = null;
108
    private String docname = null;
109
    private String doctype = null;
110
    private String validateType = null; //base on dtd or schema
111
    private String createdate = null;
112
    private String updatedate = null;
113
    private String system_id = null;
114
    private String userowner = null;
115
    private String userupdated = null;
116
    private int rev;
117
    private int serverlocation;
118
    private String docHomeServer;
119
    private String publicaccess;
120
    private long rootnodeid;
121
    private ElementNode rootNode = null;
122
    private TreeSet nodeRecordList = null;
123

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

    
143
            // Look up the document information
144
            getDocumentInfo(docid);
145

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

    
155
        } catch (McdbException ex) {
156
            throw ex;
157
        } catch (Throwable t) {
158
            throw new McdbException("Error reading document: " + docid);
159
        }
160
    }
161

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

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

    
229
    /**
230
     * This method will be call in handleUploadRequest in MetacatServlet class
231
     */
232
    public static void registerDocument(String docname, String doctype,
233
            String accnum, String user, String[] groupnames) throws SQLException,
234
            AccessionNumberException, Exception
235
    {
236
        try {
237
            // get server location for this doc
238
            int serverLocation = getServerLocationNumber(accnum);
239
            registerDocument(docname, doctype, accnum, user, groupnames,
240
                             serverLocation);
241
        } catch (Exception e) {
242
            throw e;
243
        }
244
    }
245

    
246
    /**
247
     * Register a document that resides on the filesystem with the database.
248
     * (ie, just an entry in xml_documents, nothing in xml_nodes). Creates a
249
     * reference to a filesystem document (used for non-xml data files). This
250
     * class only be called in MetaCatServerlet.
251
     *
252
     * @param conn
253
     *            the JDBC Connection to which all information is written
254
     * @param docname -
255
     *            the name of DTD, i.e. the name immediately following the
256
     *            DOCTYPE keyword ( should be the root element name ) or the
257
     *            root element name if no DOCTYPE declaration provided (Oracle's
258
     *            and IBM parsers are not aware if it is not the root element
259
     *            name)
260
     * @param doctype -
261
     *            Public ID of the DTD, i.e. the name immediately following the
262
     *            PUBLIC keyword in DOCTYPE declaration or the docname if no
263
     *            Public ID provided or null if no DOCTYPE declaration provided
264
     * @param accnum
265
     *            the accession number to use for the INSERT OR UPDATE, which
266
     *            includes a revision number for this revision of the document
267
     *            (e.g., knb.1.1)
268
     * @param user
269
     *            the user that owns the document
270
     * @param groupnames
271
     *            the groups that owns the document
272
     * @param serverCode
273
     *            the serverid from xml_replication on which this document
274
     *            resides.
275
     */
276
    public static void registerDocument(String docname, String doctype,
277
            String accnum, String user, String[] groups, int serverCode)
278
            throws SQLException, AccessionNumberException, Exception
279
    {
280

    
281
        DBConnection dbconn = null;
282
        int serialNumber = -1;
283
        PreparedStatement pstmt = null;
284
        //MetaCatUtil util = new MetaCatUtil();
285
        AccessionNumber ac;
286
        String action = null;
287
        try {
288
            //dbconn = util.openDBConnection();
289
            //check out DBConnection
290
            dbconn = DBConnectionPool
291
                    .getDBConnection("DocumentImpl.registerDocument");
292
            serialNumber = dbconn.getCheckOutSerialNumber();
293
            String docIdWithoutRev = MetaCatUtil.getDocIdFromString(accnum);
294
            int userSpecifyRev = MetaCatUtil.getVersionFromString(accnum);
295
            int revInDataBase = getLatestRevisionNumber(docIdWithoutRev);
296
            //revIndataBase=-1, there is no record in xml_documents table
297
            //the data file is a new one, inert it into table
298
            //user specified rev should be great than 0
299
            if (revInDataBase == -1 && userSpecifyRev > 0) {
300
                ac = new AccessionNumber(accnum, "insert");
301
                action = "insert";
302
            }
303
            //rev is greater the last revsion number and revInDataBase isn't -1
304
            // it is a updated data file
305
            else if (userSpecifyRev > revInDataBase && revInDataBase > 0) {
306

    
307
                if (!hasWritePermission(user, groups, accnum)) { throw new Exception(
308
                   "User " + user
309
                   + " does not have permission to update the document"
310
                   + accnum); }
311

    
312
                //archive the old entry
313
                archiveDocRevision(docIdWithoutRev, user);
314
                //delete the old entry in xml_documents
315
                //deleteXMLDocuments(docIdWithoutRev);
316
                ac = new AccessionNumber(accnum, "update");
317
                action = "update";
318
            }
319
            //other situation
320
            else {
321

    
322
                throw new Exception("Revision number couldn't be "
323
                        + userSpecifyRev);
324
            }
325
            String docid = ac.getDocid();
326
            String rev = ac.getRev();
327
            /*
328
             * SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy
329
             * HH:mm:ss"); Date localtime = new Date(); String dateString =
330
             * formatter.format(localtime); String
331
             * sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY
332
             * HH24:MI:SS");
333
             */
334
            String sqlDateString = dbAdapter.getDateTimeFunction();
335

    
336
            StringBuffer sql = new StringBuffer();
337
            if (action != null && action.equals("insert")) {
338
                sql.append("insert into xml_documents (docid, docname, " +
339
                           "doctype, ");
340
                sql.append("user_owner, user_updated, server_location, " +
341
                           "rev,date_created");
342
                sql.append(", date_updated, public_access) values ('");
343
                sql.append(docid).append("','");
344
                sql.append(docname).append("','");
345
                sql.append(doctype).append("','");
346
                sql.append(user).append("','");
347
                sql.append(user).append("','");
348
                sql.append(serverCode).append("','");
349
                sql.append(rev).append("',");
350
                sql.append(sqlDateString).append(",");
351
                sql.append(sqlDateString).append(",");
352
                sql.append("'0')");
353
            } else if (action != null && action.equals("update")) {
354
                sql.append("update xml_documents set docname ='");
355
                sql.append(docname).append("', ");
356
                sql.append("user_updated='");
357
                sql.append(user).append("', ");
358
                sql.append("server_location='");
359
                sql.append(serverCode).append("',");
360
                sql.append("rev='");
361
                sql.append(rev).append("',");
362
                sql.append("date_updated=");
363
                sql.append(sqlDateString);
364
                sql.append(" where docid='");
365
                sql.append(docid).append("'");
366
            }
367
            pstmt = dbconn.prepareStatement(sql.toString());
368
            pstmt.execute();
369
            pstmt.close();
370
            //dbconn.close();
371
        } finally {
372
            try {
373
                if(pstmt != null){
374
                    pstmt.close();
375
                }
376
            } finally {
377
                DBConnectionPool.returnDBConnection(dbconn, serialNumber);
378
            }
379
        }
380
    }
381

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

    
442
                //archive the old entry
443
                archiveDocRevision(docIdWithoutRev, user);
444
                //delete the old entry in xml_documents
445
                //deleteXMLDocuments(docIdWithoutRev);
446
                ac = new AccessionNumber(accnum, "update");
447
                action = "update";
448
            }
449
            // local server has newer version, then notify the remote server
450
            else if (userSpecifyRev < revInDataBase && revInDataBase > 0) {
451
                throw new Exception("Local server: "
452
                        + MetaCatUtil.getOption("server")
453
                        + " has newer revision of doc: " + docIdWithoutRev
454
                        + "." + revInDataBase + ". Please notify it.");
455
            }
456
            //other situation
457
            else {
458

    
459
                throw new Exception("Revision number couldn't be "
460
                        + userSpecifyRev);
461
            }
462
            String docid = ac.getDocid();
463
            String rev = ac.getRev();
464
            /*
465
             * SimpleDateFormat formatter = new SimpleDateFormat ("MM/dd/yy
466
             * HH:mm:ss"); Date localtime = new Date(); String dateString =
467
             * formatter.format(localtime); String
468
             * sqlDateString=dbAdapter.toDate(dateString, "MM/DD/YY
469
             * HH24:MI:SS");
470
             */
471
            String sqlDateString = dbAdapter.getDateTimeFunction();
472

    
473
            StringBuffer sql = new StringBuffer();
474
            if (action != null && action.equals("insert")) {
475
                sql.append("insert into xml_documents (docid, docname, " +
476
                           "doctype, ");
477
                sql.append("user_owner, user_updated, server_location, " +
478
                           "rev,date_created");
479
                sql.append(", date_updated, public_access) values ('");
480
                sql.append(docid).append("','");
481
                sql.append(docname).append("','");
482
                sql.append(doctype).append("','");
483
                sql.append(user).append("','");
484
                sql.append(user).append("','");
485
                sql.append(serverCode).append("','");
486
                sql.append(rev).append("',");
487
                sql.append(sqlDateString).append(",");
488
                sql.append(sqlDateString).append(",");
489
                sql.append("'0')");
490
            } else if (action != null && action.equals("update")) {
491
                sql.append("update xml_documents set docname ='");
492
                sql.append(docname).append("', ");
493
                sql.append("user_updated='");
494
                sql.append(user).append("', ");
495
                sql.append("server_location='");
496
                sql.append(serverCode).append("',");
497
                sql.append("rev='");
498
                sql.append(rev).append("',");
499
                sql.append("date_updated=");
500
                sql.append(sqlDateString);
501
                sql.append(" where docid='");
502
                sql.append(docid).append("'");
503
            }
504
            // Set auto commit fasle
505
            dbconn.setAutoCommit(false);
506
            pstmt = dbconn.prepareStatement(sql.toString());
507

    
508
            pstmt.execute();
509
            // Commit the insert
510
            dbconn.commit();
511
            pstmt.close();
512
            //dbconn.close();
513
        } finally {
514
            // Set DBConnection auto commit true
515
            dbconn.setAutoCommit(true);
516
            pstmt.close();
517
            DBConnectionPool.returnDBConnection(dbconn, serialNumber);
518
        }
519
    }
520

    
521
    /**
522
     * This method will register a data file entry in xml_documents and save a
523
     * data file input Stream into file system.. It is only used in replication
524
     *
525
     * @param input,
526
     *            the input stream which contain the file content.
527
     * @param ,
528
     *            the input stream which contain the file content
529
     * @param docname -
530
     *            the name of DTD, for data file, it is a docid number.
531
     * @param doctype -
532
     *            "BIN" for data file
533
     * @param accnum
534
     *            the accession number to use for the INSERT OR UPDATE, which
535
     *            includes a revision number for this revision of the document
536
     *            (e.g., knb.1.1)
537
     * @param user
538
     *            the user that owns the document
539
     * @param docHomeServer,
540
     *            the home server of the docid
541
     * @param notificationServer,
542
     *            the server to notify force replication info to local metacat
543
     */
544
    public static void writeDataFileInReplication(InputStream input,
545
            String filePath, String docname, String doctype, String accnum,
546
            String user, String docHomeServer, String notificationServer)
547
            throws SQLException, AccessionNumberException, Exception
548
    {
549
        int serverCode = -2;
550

    
551
        if (filePath == null || filePath.equals("")) { throw new Exception(
552
                "Please specify the directory where file will be store"); }
553
        if (accnum == null || accnum.equals("")) { throw new Exception(
554
                "Please specify the stored file name"); }
555

    
556
        // If server is not int the xml replication talbe, insert it into
557
        // xml_replication table
558
        //serverList.addToServerListIfItIsNot(docHomeServer);
559
        insertServerIntoReplicationTable(docHomeServer);
560

    
561
        // Get server code again
562
        serverCode = getServerCode(docHomeServer);
563

    
564
        //register data file into xml_documents table
565
        registerDocumentInReplication(docname, doctype, accnum, user,
566
                serverCode);
567
        //write inputstream into file system.
568
        File dataDirectory = new File(filePath);
569
        File newFile = new File(dataDirectory, accnum);
570

    
571
        // create a buffered byte output stream
572
        // that uses a default-sized output buffer
573
        FileOutputStream fos = new FileOutputStream(newFile);
574
        BufferedOutputStream outPut = new BufferedOutputStream(fos);
575

    
576
        BufferedInputStream bis = null;
577
        bis = new BufferedInputStream(input);
578
        byte[] buf = new byte[4 * 1024]; // 4K buffer
579
        int b = bis.read(buf);
580

    
581
        while (b != -1) {
582
            outPut.write(buf, 0, b);
583
            b = bis.read(buf);
584
        }
585
        bis.close();
586
        outPut.close();
587
        fos.close();
588

    
589
        // Force replicate data file
590
        ForceReplicationHandler forceReplication = new ForceReplicationHandler(
591
                accnum, false, notificationServer);
592
    }
593

    
594
    /**
595
     * Get a lock for a given document.
596
     */
597
    public static boolean getDataFileLockGrant(String accnum) throws Exception
598
    {
599
        try {
600
            int serverLocation = getServerLocationNumber(accnum);
601
            return getDataFileLockGrant(accnum, serverLocation);
602
        } catch (Exception e) {
603
            throw e;
604
        }
605
    }
606

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

    
627
        if (serverCode == 1) {
628
            flag = true;
629
            return flag;
630
        }
631

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

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

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

    
692
    /**
693
     * get the document name
694
     */
695
    public String getDocname()
696
    {
697
        return docname;
698
    }
699

    
700
    /**
701
     * get the document type (which is the PublicID)
702
     */
703
    public String getDoctype()
704
    {
705
        return doctype;
706
    }
707

    
708
    /**
709
     * get the system identifier
710
     */
711
    public String getSystemID()
712
    {
713
        return system_id;
714
    }
715

    
716
    /**
717
     * get the root node identifier
718
     */
719
    public long getRootNodeID()
720
    {
721
        return rootnodeid;
722
    }
723

    
724
    /**
725
     * get the creation date
726
     */
727
    public String getCreateDate()
728
    {
729
        return createdate;
730
    }
731

    
732
    /**
733
     * get the update date
734
     */
735
    public String getUpdateDate()
736
    {
737
        return updatedate;
738
    }
739

    
740
    /**
741
     * Get the document identifier (docid)
742
     */
743
    public String getDocID()
744
    {
745
        return docid;
746
    }
747

    
748
    public String getUserowner()
749
    {
750
        return userowner;
751
    }
752

    
753
    public String getUserupdated()
754
    {
755
        return userupdated;
756
    }
757

    
758
    public int getServerlocation()
759
    {
760
        return serverlocation;
761
    }
762

    
763
    public String getDocHomeServer()
764
    {
765
        return docHomeServer;
766
    }
767

    
768
    public String getPublicaccess()
769
    {
770
        return publicaccess;
771
    }
772

    
773
    public int getRev()
774
    {
775
        return rev;
776
    }
777

    
778
    public String getValidateType()
779
    {
780
        return validateType;
781
    }
782

    
783
    /**
784
     * Print a string representation of the XML document
785
     */
786
    public String toString(String user, String[] groups, boolean withInlinedata)
787
    {
788
        StringWriter docwriter = new StringWriter();
789
        try {
790
            this.toXml(docwriter, user, groups, withInlinedata);
791
        } catch (McdbException mcdbe) {
792
            return null;
793
        }
794
        String document = docwriter.toString();
795
        return document;
796
    }
797

    
798
    /**
799
     * Print a string representation of the XML document
800
     */
801
    public String toString()
802
    {
803
        StringWriter docwriter = new StringWriter();
804
        String userName = null;
805
        String[] groupNames = null;
806
        boolean withInlineData = true;
807
        try {
808
            this.toXml(docwriter, userName, groupNames, withInlineData);
809
        } catch (McdbException mcdbe) {
810
            return null;
811
        }
812
        String document = docwriter.toString();
813
        return document;
814
    }
815

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

    
829
        // First, check that we have the needed node data, and get it if not
830
        if (nodeRecordList == null) {
831
            nodeRecordList = getNodeRecordList(rootnodeid);
832
        }
833

    
834
        // Create the elements from the downloaded data in the TreeSet
835
        rootNode = new ElementNode(nodeRecordList, rootnodeid);
836

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

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

    
850
        return (doc.toString());
851
    }
852

    
853
    /**
854
     * Print a text representation of the XML document to a Writer
855
     *
856
     * @param pw
857
     *            the Writer to which we print the document Now we decide no
858
     *            matter withinInlineData's value, the document will
859
     *
860
     */
861
    public void toXml(Writer pw, String user, String[] groups,
862
            boolean withInLineData) throws McdbException
863
    {
864
        // flag for process eml2
865
        boolean proccessEml2 = false;
866
        boolean storedDTD = false;//flag to inidate publicid or system
867
        // id stored in db or not
868
        boolean firstElement = true;
869
        String dbDocName = null;
870
        String dbPublicID = null;
871
        String dbSystemID = null;
872

    
873
        if (doctype != null
874
                && (doctype.equals(EML2_0_0NAMESPACE)
875
                        || doctype.equals(EML2_0_1NAMESPACE) || doctype
876
                        .equals(EML2_1_0NAMESPACE))) {
877
            proccessEml2 = true;
878
        }
879
        // flag for process inline data
880
        boolean prcocessInlineData = false;
881

    
882
        TreeSet nodeRecordLists = null;
883
        PrintWriter out = null;
884
        if (pw instanceof PrintWriter) {
885
            out = (PrintWriter) pw;
886
        } else {
887
            out = new PrintWriter(pw);
888
        }
889

    
890
        MetaCatUtil util = new MetaCatUtil();
891

    
892
        // Here add code to handle subtree access control
893
        /*
894
         * PermissionController control = new PermissionController(docid);
895
         * Hashtable unaccessableSubTree =control.hasUnaccessableSubTree(user,
896
         * groups, AccessControlInterface.READSTRING);
897
         *
898
         * if (!unaccessableSubTree.isEmpty()) {
899
         *
900
         * nodeRecordLists = getPartNodeRecordList(rootnodeid,
901
         * unaccessableSubTree);
902
         *  } else { nodeRecordLists = getNodeRecordList(rootnodeid); }
903
         */
904
        nodeRecordLists = getNodeRecordList(rootnodeid);
905
        Stack openElements = new Stack();
906
        boolean atRootElement = true;
907
        boolean previousNodeWasElement = false;
908

    
909
        // Step through all of the node records we were given
910

    
911
        Iterator it = nodeRecordLists.iterator();
912

    
913
        while (it.hasNext()) {
914

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

    
958
            // Handle the DOCUMENT node
959
            if (currentNode.nodetype.equals("DOCUMENT")) {
960
                out.print("<?xml version=\"1.0\"?>");
961

    
962
                // Handle the ELEMENT nodes
963
            } else if (currentNode.nodetype.equals("ELEMENT")) {
964
                if (atRootElement) {
965
                    atRootElement = false;
966
                } else {
967
                    if (previousNodeWasElement) {
968
                        out.print(">");
969
                    }
970
                }
971

    
972
                // if publicid or system is not stored into db send it out by
973
                // default
974
                if (!storedDTD & firstElement) {
975
                    if (docname != null && validateType != null
976
                            && validateType.equals(DTD)) {
977
                        if ((doctype != null) && (system_id != null)) {
978

    
979
                            out.print("<!DOCTYPE " + docname + " PUBLIC \""
980
                                    + doctype + "\" \"" + system_id + "\">");
981
                        } else {
982

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

    
998
                // if currentNode is inline and handle eml2, set flag proccess
999
                // in
1000
                if (currentNode.nodename != null
1001
                        && currentNode.nodename.equals(Eml200SAXHandler.INLINE)
1002
                        && proccessEml2) {
1003
                    prcocessInlineData = true;
1004
                }
1005

    
1006
                // Handle the ATTRIBUTE nodes
1007
            } else if (currentNode.nodetype.equals("ATTRIBUTE")) {
1008
                if (currentNode.nodeprefix != null) {
1009
                    out.print(" " + currentNode.nodeprefix + ":"
1010
                            + currentNode.nodename + "=\""
1011
                            + currentNode.nodedata + "\"");
1012
                } else {
1013
                    out.print(" " + currentNode.nodename + "=\""
1014
                            + currentNode.nodedata + "\"");
1015
                }
1016

    
1017
                // Handle the NAMESPACE nodes
1018
            } else if (currentNode.nodetype.equals("NAMESPACE")) {
1019
                out.print(" xmlns:" + currentNode.nodename + "=\""
1020
                        + currentNode.nodedata + "\"");
1021

    
1022
                // Handle the TEXT nodes
1023
            } else if (currentNode.nodetype.equals("TEXT")) {
1024
                if (previousNodeWasElement) {
1025
                    out.print(">");
1026
                }
1027
                if (!prcocessInlineData) {
1028
                    // if it is not inline data just out put data
1029
                    out.print(currentNode.nodedata);
1030
                } else {
1031
                    // if it is inline data first to get the inline data
1032
                    // internal id
1033
                    String fileName = currentNode.nodedata;
1034
                    String accessfileName = MetaCatUtil
1035
                            .getDocIdWithoutRevFromInlineDataID(fileName);
1036
                    // check if user has read permision for this inline data
1037
                    boolean readInlinedata = false;
1038
                    try {
1039
                        Hashtable unReadableInlineDataList =
1040
                            PermissionController
1041
                                .getUnReadableInlineDataIdList(accessfileName,
1042
                                        user, groups, true);
1043
                        if (!unReadableInlineDataList.containsValue(fileName)) {
1044
                            readInlinedata = true;
1045
                        }
1046
                    } catch (Exception e) {
1047
                        throw new McdbException(e.getMessage());
1048
                    }
1049

    
1050
                    if (readInlinedata) {
1051
                        //user want to see it, pull out from
1052
                        // file system and output it
1053
                        // for inline data, the data base only store the file
1054
                        // name, so we
1055
                        // can combine the file name and inline data file path,
1056
                        // to get it
1057

    
1058
                        Reader reader = Eml200SAXHandler
1059
                                .readInlineDataFromFileSystem(fileName);
1060
                        char[] characterArray = new char[4 * 1024];
1061
                        try {
1062
                            int length = reader.read(characterArray);
1063
                            while (length != -1) {
1064
                                out
1065
                                        .print(new String(characterArray, 0,
1066
                                                length));
1067
                                out.flush();
1068
                                length = reader.read(characterArray);
1069
                            }
1070
                            reader.close();
1071
                        } catch (IOException e) {
1072
                            throw new McdbException(e.getMessage());
1073
                        }
1074
                    }//if can read inline data
1075
                    else {
1076
                        // if user can't read it, we only send it back a empty
1077
                        // string
1078
                        // in inline element.
1079
                        out.print("");
1080
                    }// else can't read inlinedata
1081
                    // reset proccess inline data false
1082
                    prcocessInlineData = false;
1083
                }// in inlinedata part
1084
                previousNodeWasElement = false;
1085
                // Handle the COMMENT nodes
1086
            } else if (currentNode.nodetype.equals("COMMENT")) {
1087
                if (previousNodeWasElement) {
1088
                    out.print(">");
1089
                }
1090
                out.print("<!--" + currentNode.nodedata + "-->");
1091
                previousNodeWasElement = false;
1092

    
1093
                // Handle the PI nodes
1094
            } else if (currentNode.nodetype.equals("PI")) {
1095
                if (previousNodeWasElement) {
1096
                    out.print(">");
1097
                }
1098
                out.print("<?" + currentNode.nodename + " "
1099
                        + currentNode.nodedata + "?>");
1100
                previousNodeWasElement = false;
1101
                // Handle the DTD nodes (docname, publicid, systemid)
1102
            } else if (currentNode.nodetype.equals(DTD)) {
1103
                storedDTD = true;
1104
                if (currentNode.getNodeName().equals(DOCNAME)) {
1105
                    dbDocName = currentNode.getNodeData();
1106
                }
1107
                if (currentNode.getNodeName().equals(PUBLICID)) {
1108
                    dbPublicID = currentNode.getNodeData();
1109
                }
1110
                if (currentNode.getNodeName().equals(SYSTEMID)) {
1111
                    dbSystemID = currentNode.getNodeData();
1112
                    // send out <!doctype .../>
1113
                    if (dbDocName != null) {
1114
                        if ((dbPublicID != null) && (dbSystemID != null)) {
1115

    
1116
                            out
1117
                                    .print("<!DOCTYPE " + dbDocName
1118
                                            + " PUBLIC \"" + dbPublicID
1119
                                            + "\" \"" + dbSystemID + "\">");
1120
                        } else {
1121

    
1122
                            out.print("<!DOCTYPE " + dbDocName + ">");
1123
                        }
1124
                    }
1125

    
1126
                    //reset these variable
1127
                    dbDocName = null;
1128
                    dbPublicID = null;
1129
                    dbSystemID = null;
1130
                }
1131

    
1132
                // Handle any other node type (do nothing)
1133
            } else {
1134
                // Any other types of nodes are not handled.
1135
                // Probably should throw an exception here to indicate this
1136
            }
1137
            out.flush();
1138
        }
1139

    
1140
        // Print the final end tag for the root element
1141
        while (!openElements.empty()) {
1142
            NodeRecord currentElement = (NodeRecord) openElements.pop();
1143
            util.debugMessage("\n POPPED: " + currentElement.nodename, 50);
1144
            if (currentElement.nodeprefix != null) {
1145
                out.print("</" + currentElement.nodeprefix + ":"
1146
                        + currentElement.nodename + ">");
1147
            } else {
1148
                out.print("</" + currentElement.nodename + ">");
1149
            }
1150
        }
1151
        out.flush();
1152
    }
1153

    
1154
    /**
1155
     * Build the index records for this document.  For each node, all absolute
1156
     * and relative paths to the root of the document are created and inserted
1157
     * into the xml_index table.  This requires that the DocumentImpl instance 
1158
     * exists, so first call the constructor that reads the document from the 
1159
     * database.
1160
     */
1161
    public void buildIndex() throws McdbException
1162
    {
1163
        MetaCatUtil util = new MetaCatUtil();
1164
        TreeSet nodeRecordLists = getNodeRecordList(rootnodeid);
1165
        Stack openElements = new Stack();
1166
        boolean atRootElement = true;
1167
        long rootNodeId = -1;
1168

    
1169
        // Build a map of the same records that are present in the
1170
        // TreeSet so that any node can be easily accessed by nodeId
1171
        HashMap nodeRecordMap = new HashMap();
1172
        Iterator it = nodeRecordLists.iterator();
1173
        while (it.hasNext()) {
1174
            NodeRecord currentNode = (NodeRecord) it.next();
1175
            Long nodeId = new Long(currentNode.getNodeId());
1176
            nodeRecordMap.put(nodeId, currentNode);
1177
        }
1178

    
1179
        // Step through all of the node records we were given
1180
        it = nodeRecordLists.iterator();
1181
        while (it.hasNext()) {
1182
            NodeRecord currentNode = (NodeRecord) it.next();
1183
            if (currentNode.nodetype.equals("ELEMENT") ||
1184
                currentNode.nodetype.equals("ATTRIBUTE") ) {
1185

    
1186
                System.err.println("\nStarting Node: " + 
1187
                    currentNode.getNodeId() + " (" + 
1188
                    currentNode.getParentNodeId() + "): " + 
1189
                    currentNode.getNodeName() + " (" +
1190
                    currentNode.getNodeType() + ")");
1191
                if (atRootElement) {
1192
                    rootNodeId = currentNode.getNodeId();
1193
                    atRootElement = false;
1194
                }
1195
                traverseParents(nodeRecordMap, rootNodeId, 
1196
                        currentNode.getNodeId(), "");
1197
            }
1198
        }
1199
    }
1200

    
1201
    /**
1202
     * Recurse up the parent node hierarchy and add each node to the
1203
     * hashmap of paths to be indexed.
1204
     *
1205
     * @param records the set of records hashed by nodeId
1206
     * @param rootNodeId the id of the root element of the document
1207
     * @param id the id of the current node to be processed
1208
     * @param children the string representation of all child nodes of this id
1209
     */
1210
    private void traverseParents(HashMap records, long rootNodeId, long id, 
1211
            String children) {
1212
        NodeRecord current = (NodeRecord)records.get(new Long(id));
1213
        String currentName = current.getNodeName();
1214
        if (current.nodetype.equals("ELEMENT") ||
1215
            current.nodetype.equals("ATTRIBUTE") ) {
1216

    
1217
            if (children.equals("")) {
1218
                System.err.print("A: " + currentName +"\n");
1219
            }
1220
            currentName = "/" + currentName;
1221
            long parentId = current.getParentNodeId();
1222
            currentName = currentName + children;
1223
            if (parentId != 0) {
1224
                traverseParents(records, rootNodeId, parentId, currentName);
1225
            }
1226
            if (!children.equals("")) {
1227
                System.err.print("B: " + current.getNodeName() + children +"\n");
1228
            }
1229
            if (id == rootNodeId) {
1230
                System.err.print("C: " + '/' + current.getNodeName() + 
1231
                        children +"\n");
1232
            }
1233
        }
1234
    }
1235

    
1236
    private boolean isRevisionOnly(DocumentIdentifier docid) throws Exception
1237
    {
1238
        //System.out.println("inRevisionOnly");
1239
        DBConnection dbconn = null;
1240
        int serialNumber = -1;
1241
        PreparedStatement pstmt = null;
1242
        String rev = docid.getRev();
1243
        String newid = docid.getIdentifier();
1244
        try {
1245
            dbconn = DBConnectionPool
1246
                    .getDBConnection("DocumentImpl.isRevisionOnly");
1247
            serialNumber = dbconn.getCheckOutSerialNumber();
1248
            pstmt = dbconn.prepareStatement("select rev from xml_documents "
1249
                    + "where docid like '" + newid + "'");
1250
            pstmt.execute();
1251
            ResultSet rs = pstmt.getResultSet();
1252
            boolean tablehasrows = rs.next();
1253
            if (rev.equals("newest") || rev.equals("all")) { return false; }
1254

    
1255
            if (tablehasrows) {
1256
                int r = rs.getInt(1);
1257
                pstmt.close();
1258
                if (new Integer(rev).intValue() == r) { //the current revision
1259
                                                        // in in xml_documents
1260
                    //System.out.println("returning false");
1261
                    return false;
1262
                } else if (new Integer(rev).intValue() < r) { //the current
1263
                                                              // revision is in
1264
                                                              // xml_revisions.
1265
                    //System.out.println("returning true");
1266
                    return true;
1267
                } else if (new Integer(rev).intValue() > r) { //error, rev
1268
                                                              // cannot be
1269
                                                              // greater than r
1270
                throw new Exception(
1271
                        "requested revision cannot be greater than "
1272
                                + "the latest revision number."); }
1273
            }
1274
            // Get miss docid and rev, throw to McdDocNotFoundException
1275
            String missDocId = MetaCatUtil.getDocIdFromString(docid.toString());
1276
            String missRevision = MetaCatUtil.getRevisionStringFromString(docid
1277
                    .toString());
1278
            throw new McdbDocNotFoundException("the requested docid '"
1279
                    + docid.toString() + "' does not exist", missDocId,
1280
                    missRevision);
1281
        }//try
1282
        finally {
1283
            pstmt.close();
1284
            DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1285
        }//finally
1286
    }
1287

    
1288
    private void getDocumentInfo(String docid) throws McdbException,
1289
            AccessionNumberException, Exception
1290
    {
1291
        getDocumentInfo(new DocumentIdentifier(docid));
1292
    }
1293

    
1294
    /**
1295
     * Look up the document type information from the database
1296
     *
1297
     * @param docid
1298
     *            the id of the document to look up
1299
     */
1300
    private void getDocumentInfo(DocumentIdentifier docid)
1301
            throws McdbException, Exception
1302
    {
1303
        DBConnection dbconn = null;
1304
        int serialNumber = -1;
1305
        PreparedStatement pstmt = null;
1306
        String table = "xml_documents";
1307

    
1308
        try {
1309
            if (isRevisionOnly(docid)) { //pull the document from xml_revisions
1310
                                         // instead of from xml_documents;
1311
                table = "xml_revisions";
1312
            }
1313
        }
1314
        // catch a McdbDocNotFoundException throw it
1315
        catch (McdbDocNotFoundException notFound) {
1316
            throw notFound;
1317
        } catch (Exception e) {
1318

    
1319
            MetaCatUtil.debugMessage("error in DocumentImpl.getDocumentInfo: "
1320
                    + e.getMessage(), 30);
1321
            throw e;
1322
        }
1323

    
1324
        try {
1325
            dbconn = DBConnectionPool
1326
                    .getDBConnection("DocumentImpl.getDocumentInfo");
1327
            serialNumber = dbconn.getCheckOutSerialNumber();
1328
            StringBuffer sql = new StringBuffer();
1329
            sql.append("SELECT docname, doctype, rootnodeid, ");
1330
            sql.append("date_created, date_updated, user_owner, user_updated,");
1331
            sql.append(" server_location, public_access, rev");
1332
            sql.append(" FROM ").append(table);
1333
            sql.append(" WHERE docid LIKE '").append(docid.getIdentifier());
1334
            sql.append("' and rev like '").append(docid.getRev()).append("'");
1335

    
1336
            pstmt = dbconn.prepareStatement(sql.toString());
1337

    
1338
            pstmt.execute();
1339
            ResultSet rs = pstmt.getResultSet();
1340
            boolean tableHasRows = rs.next();
1341
            if (tableHasRows) {
1342
                this.docname = rs.getString(1);
1343
                this.doctype = rs.getString(2);
1344
                this.rootnodeid = rs.getLong(3);
1345
                this.createdate = rs.getString(4);
1346
                this.updatedate = rs.getString(5);
1347
                this.userowner = rs.getString(6);
1348
                this.userupdated = rs.getString(7);
1349
                this.serverlocation = rs.getInt(8);
1350
                this.publicaccess = rs.getString(9);
1351
                this.rev = rs.getInt(10);
1352
            }
1353
            pstmt.close();
1354

    
1355
            //get doc home server name
1356
            pstmt = dbconn.prepareStatement("select server "
1357
                    + "from xml_replication where serverid = ?");
1358
            //because connection use twice here, so we need to increase one
1359
            dbconn.increaseUsageCount(1);
1360
            pstmt.setInt(1, serverlocation);
1361
            pstmt.execute();
1362
            rs = pstmt.getResultSet();
1363
            tableHasRows = rs.next();
1364
            if (tableHasRows) {
1365

    
1366
                String server = rs.getString(1);
1367
                //get homeserver name
1368
                if (!server.equals("localhost")) {
1369
                    this.docHomeServer = server;
1370
                } else {
1371
                    this.docHomeServer = MetaCatUtil
1372
                            .getLocalReplicationServerName();
1373
                }
1374
                MetaCatUtil.debugMessage("server: " + docHomeServer, 50);
1375

    
1376
            }
1377
            pstmt.close();
1378
            if (this.doctype != null) {
1379
                pstmt = dbconn.prepareStatement("SELECT system_id, entry_type "
1380
                        + "FROM xml_catalog " + "WHERE public_id = ?");
1381
                //should increase usage count again
1382
                dbconn.increaseUsageCount(1);
1383
                // Bind the values to the query
1384
                pstmt.setString(1, doctype);
1385

    
1386
                pstmt.execute();
1387
                rs = pstmt.getResultSet();
1388
                tableHasRows = rs.next();
1389
                if (tableHasRows) {
1390
                    this.system_id = rs.getString(1);
1391
                    this.validateType = rs.getString(2);
1392

    
1393
                }
1394
                pstmt.close();
1395
            }
1396
        } catch (SQLException e) {
1397
            System.out.println("error in DocumentImpl.getDocumentInfo: "
1398
                    + e.getMessage());
1399
            e.printStackTrace(System.out);
1400
            throw new McdbException("Error accessing database connection in "
1401
                    + "DocumentImpl.getDocumentInfo: ", e);
1402
        } finally {
1403
            try {
1404
                pstmt.close();
1405
            } catch (SQLException ee) {
1406
                MetaCatUtil.debugMessage(
1407
                        "error in DocumentImple.getDocumentInfo: "
1408
                                + ee.getMessage(), 30);
1409
            } finally {
1410
                DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1411
            }
1412
        }
1413

    
1414
        if (this.docname == null) {
1415
            throw new McdbDocNotFoundException(
1416
                "Document not found: " + docid, docid.getIdentifier(), docid
1417
                        .getRev());
1418
        }
1419
    }
1420

    
1421
    /**
1422
     * Look up the node data from the database, but some node would be shown
1423
     * because of access control
1424
     *
1425
     * @param rootnodeid
1426
     *            the id of the root node of the node tree to look up
1427
     * @param accessControl
1428
     *            the hashtable has control info
1429
     */
1430
    private TreeSet getPartNodeRecordList(long rootnodeid,
1431
            Hashtable accessControl) throws McdbException
1432
    {
1433
        PreparedStatement pstmt = null;
1434
        DBConnection dbconn = null;
1435
        int serialNumber = -1;
1436
        TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1437
        long nodeid = 0;
1438
        long parentnodeid = 0;
1439
        long nodeindex = 0;
1440
        String nodetype = null;
1441
        String nodename = null;
1442
        String nodeprefix = null;
1443
        String nodedata = null;
1444
        String quotechar = dbAdapter.getStringDelimiter();
1445
        String sql = "SELECT nodeid,parentnodeid,nodeindex, "
1446
                + "nodetype,nodename,nodeprefix,nodedata "
1447
                + "FROM xml_nodes WHERE rootnodeid = ?";
1448

    
1449
        // go through the access control for some nodes
1450
        Enumeration en = accessControl.elements();
1451
        while (en.hasMoreElements()) {
1452
            SubTree tree = (SubTree) en.nextElement();
1453
            long startId = tree.getStartNodeId();
1454
            long endId = tree.getEndNodeId();
1455
            sql = sql + " AND(nodeid < " + startId + " OR nodeid > " + endId
1456
                    + ")";
1457

    
1458
        }
1459
        MetaCatUtil.debugMessage("The final query to select part node tree: "
1460
                + sql, 25);
1461

    
1462
        try {
1463
            dbconn = DBConnectionPool
1464
                    .getDBConnection("DocumentImpl.getPartNodeRecordList");
1465
            serialNumber = dbconn.getCheckOutSerialNumber();
1466
            pstmt = dbconn.prepareStatement(sql);
1467

    
1468
            // Bind the values to the query
1469
            pstmt.setLong(1, rootnodeid);
1470
            pstmt.execute();
1471
            ResultSet rs = pstmt.getResultSet();
1472
            boolean tableHasRows = rs.next();
1473
            while (tableHasRows) {
1474
                nodeid = rs.getLong(1);
1475
                parentnodeid = rs.getLong(2);
1476
                nodeindex = rs.getLong(3);
1477
                nodetype = rs.getString(4);
1478
                nodename = rs.getString(5);
1479
                nodeprefix = rs.getString(6);
1480
                nodedata = rs.getString(7);
1481
                nodedata = MetaCatUtil.normalize(nodedata);
1482
                // add the data to the node record list hashtable
1483
                NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid,
1484
                        nodeindex, nodetype, nodename, nodeprefix, nodedata);
1485
                nodeRecordList.add(currentRecord);
1486

    
1487
                // Advance to the next node
1488
                tableHasRows = rs.next();
1489
            }
1490
            pstmt.close();
1491

    
1492
        } catch (SQLException e) {
1493
            throw new McdbException(
1494
                    "Error in DocumentImpl.getPartNodeRecordList "
1495
                            + e.getMessage());
1496
        } finally {
1497
            try {
1498
                pstmt.close();
1499
            } catch (SQLException ee) {
1500
                MetaCatUtil.debugMessage(
1501
                        "error in DocumentImpl.getPartNodeRecordList: "
1502
                                + ee.getMessage(), 30);
1503
            } finally {
1504
                DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1505
            }
1506
        }
1507

    
1508
        if (!nodeRecordList.isEmpty()) {
1509

    
1510
            return nodeRecordList;
1511
        } else {
1512

    
1513
            throw new McdbException("Error getting node data: " + docid);
1514
        }
1515
    }
1516

    
1517
    /**
1518
     * Look up the node data from the database
1519
     *
1520
     * @param rootnodeid
1521
     *            the id of the root node of the node tree to look up
1522
     */
1523
    private TreeSet getNodeRecordList(long rootnodeid) throws McdbException
1524
    {
1525
        PreparedStatement pstmt = null;
1526
        DBConnection dbconn = null;
1527
        int serialNumber = -1;
1528
        TreeSet nodeRecordList = new TreeSet(new NodeComparator());
1529
        long nodeid = 0;
1530
        long parentnodeid = 0;
1531
        long nodeindex = 0;
1532
        String nodetype = null;
1533
        String nodename = null;
1534
        String nodeprefix = null;
1535
        String nodedata = null;
1536
        String quotechar = dbAdapter.getStringDelimiter();
1537

    
1538
        try {
1539
            dbconn = DBConnectionPool
1540
                    .getDBConnection("DocumentImpl.getNodeRecordList");
1541
            serialNumber = dbconn.getCheckOutSerialNumber();
1542
            pstmt = dbconn
1543
                    .prepareStatement("SELECT nodeid,parentnodeid,nodeindex, "
1544
                            + "nodetype,nodename,nodeprefix,nodedata "
1545
                            + "FROM xml_nodes WHERE rootnodeid = ?");
1546

    
1547
            // Bind the values to the query
1548
            pstmt.setLong(1, rootnodeid);
1549

    
1550
            pstmt.execute();
1551
            ResultSet rs = pstmt.getResultSet();
1552
            boolean tableHasRows = rs.next();
1553
            while (tableHasRows) {
1554
                nodeid = rs.getLong(1);
1555
                parentnodeid = rs.getLong(2);
1556
                nodeindex = rs.getLong(3);
1557
                nodetype = rs.getString(4);
1558
                nodename = rs.getString(5);
1559
                nodeprefix = rs.getString(6);
1560
                nodedata = rs.getString(7);
1561
                nodedata = MetaCatUtil.normalize(nodedata);
1562
                // add the data to the node record list hashtable
1563
                NodeRecord currentRecord = new NodeRecord(nodeid, parentnodeid,
1564
                        nodeindex, nodetype, nodename, nodeprefix, nodedata);
1565
                nodeRecordList.add(currentRecord);
1566

    
1567
                // Advance to the next node
1568
                tableHasRows = rs.next();
1569
            }
1570
            pstmt.close();
1571

    
1572
        } catch (SQLException e) {
1573
            throw new McdbException("Error in DocumentImpl.getNodeRecordList "
1574
                    + e.getMessage());
1575
        } finally {
1576
            try {
1577
                pstmt.close();
1578
            } catch (SQLException ee) {
1579
                MetaCatUtil.debugMessage(
1580
                        "error in DocumentImpl.getNodeRecordList: "
1581
                                + ee.getMessage(), 30);
1582
            } finally {
1583
                DBConnectionPool.returnDBConnection(dbconn, serialNumber);
1584
            }
1585
        }
1586

    
1587
        return nodeRecordList;
1588

    
1589
    }
1590

    
1591
    /** creates SQL code and inserts new document into DB connection */
1592
    private void writeDocumentToDB(String action, String user, String pub,
1593
            String catalogid, int serverCode) throws SQLException, Exception
1594
    {
1595
        String sysdate = dbAdapter.getDateTimeFunction();
1596

    
1597
        try {
1598
            PreparedStatement pstmt = null;
1599

    
1600
            if (action.equals("INSERT")) {
1601
                //AccessionNumber ac = new AccessionNumber();
1602
                //this.docid = ac.generate(docid, "INSERT");
1603

    
1604
                pstmt = connection
1605
                        .prepareStatement("INSERT INTO xml_documents "
1606
                        + "(docid, rootnodeid, docname, doctype, user_owner, "
1607
                        + "user_updated, date_created, date_updated, "
1608
                        + "public_access, catalog_id, server_location, rev) "
1609
                        + "VALUES (?, ?, ?, ?, ?, ?, " + sysdate + ", "
1610
                        + sysdate + ", ?, ?, ?, ?)");
1611
                // Increase dbconnection usage count
1612
                connection.increaseUsageCount(1);
1613

    
1614
                //note that the server_location is set to 1.
1615
                //this means that "localhost" in the xml_replication table must
1616
                //always be the first entry!!!!!
1617

    
1618
                // Bind the values to the query
1619
                pstmt.setString(1, this.docid);
1620
                pstmt.setLong(2, rootnodeid);
1621
                pstmt.setString(3, docname);
1622
                pstmt.setString(4, doctype);
1623
                pstmt.setString(5, user);
1624
                pstmt.setString(6, user);
1625
                //public access is usefulless, so set it to null
1626
                pstmt.setString(7, null);
1627
                /*
1628
                 * if ( pub == null ) { pstmt.setString(7, null); } else if (
1629
                 * pub.toUpperCase().equals("YES") || pub.equals("1") ) {
1630
                 * pstmt.setInt(7, 1); } else if (
1631
                 * pub.toUpperCase().equals("NO") || pub.equals("0") ) {
1632
                 * pstmt.setInt(7, 0); }
1633
                 */
1634
                pstmt.setString(8, catalogid);
1635
                pstmt.setInt(9, serverCode);
1636
                pstmt.setInt(10, Integer.parseInt(updatedVersion));
1637
            } else if (action.equals("UPDATE")) {
1638

    
1639
                // Save the old document publicaccessentry in a backup table
1640
                DocumentImpl.archiveDocRevision(connection, docid, user);
1641
                MetaCatUtil.debugMessage("after archiveDoc", 40);
1642
                DocumentImpl thisdoc = new DocumentImpl(docid, false);
1643
                int thisrev = thisdoc.getRev();
1644
                MetaCatUtil.debugMessage("this revsion is: " + thisrev, 40);
1645
                //if the updated vesion is not greater than current one,
1646
                //throw it into a exception
1647
                if (Integer.parseInt(updatedVersion) <= thisrev) {
1648
                    throw new Exception("Next revision number couldn't be less"
1649
                            + " than or equal " + thisrev);
1650
                } else {
1651
                    //set the user specified revision
1652
                    thisrev = Integer.parseInt(updatedVersion);
1653
                }
1654
                MetaCatUtil.debugMessage("final revsion is: " + thisrev, 40);
1655
                boolean useXMLIndex = (new Boolean(MetaCatUtil
1656
                        .getOption("usexmlindex"))).booleanValue();
1657
                if (useXMLIndex) {
1658
                    MetaCatUtil.debugMessage("before delete", 40);
1659
                    // Delete index for the old version of docid
1660
                    // The new index is inserting on the next calls to DBSAXNode
1661
                    pstmt = connection
1662
                            .prepareStatement("DELETE FROM xml_index WHERE docid='"
1663
                                    + this.docid + "'");
1664
                    MetaCatUtil.debugMessage("after delete", 40);
1665
                    // Increase dbconnection usage count
1666
                    connection.increaseUsageCount(1);
1667

    
1668
                    pstmt.execute();
1669
                    pstmt.close();
1670
                }
1671

    
1672
                // Update the new document to reflect the new node tree
1673
                pstmt = connection
1674
                        .prepareStatement("UPDATE xml_documents "
1675
                        + "SET rootnodeid = ?, docname = ?, doctype = ?, "
1676
                        + "user_updated = ?, date_updated = "
1677
                        + sysdate
1678
                        + ", "
1679
                        + "server_location = ?, rev = ?, public_access = ?, "
1680
                        + "catalog_id = ? "
1681
                        + "WHERE docid = ?");
1682
                // Increase dbconnection usage count
1683
                connection.increaseUsageCount(1);
1684
                // Bind the values to the query
1685
                pstmt.setLong(1, rootnodeid);
1686
                pstmt.setString(2, docname);
1687
                pstmt.setString(3, doctype);
1688
                pstmt.setString(4, user);
1689
                pstmt.setInt(5, serverCode);
1690
                pstmt.setInt(6, thisrev);
1691
                pstmt.setString(7, null);
1692
                /*
1693
                 * if ( pub == null ) { pstmt.setString(7, null); } else if (
1694
                 * pub.toUpperCase().equals("YES") || pub.equals("1") ) { pstmt
1695
                 * .setInt(7, 1); } else if ( pub.toUpperCase().equals("NO") ||
1696
                 * pub.equals("0") ) { pstmt.setInt(7, 0); }
1697
                 */
1698
                pstmt.setString(8, catalogid);
1699
                pstmt.setString(9, this.docid);
1700

    
1701
            } else {
1702
                System.err.println("Action not supported: " + action);
1703
            }
1704

    
1705
            // Do the insertion
1706
            pstmt.execute();
1707

    
1708
            pstmt.close();
1709

    
1710
        } catch (SQLException sqle) {
1711
            throw sqle;
1712
        } catch (Exception e) {
1713
            throw e;
1714
        }
1715
    }
1716

    
1717
    /**
1718
     * Write an XML file to the database, given a filename
1719
     *
1720
     * @param conn
1721
     *            the JDBC connection to the database
1722
     * @param filename
1723
     *            the filename to be loaded into the database
1724
     * @param pub
1725
     *            flag for public "read" access on document
1726
     * @param dtdfilename
1727
     *            the dtd to be uploaded on server's file system
1728
     * @param action
1729
     *            the action to be performed (INSERT OR UPDATE)
1730
     * @param docid
1731
     *            the docid to use for the INSERT OR UPDATE
1732
     * @param user
1733
     *            the user that owns the document
1734
     * @param groups
1735
     *            the groups to which user belongs
1736
     */
1737
    /*
1738
     * public static String write(DBConnection conn,String filename, String pub,
1739
     * String dtdfilename, String action, String docid, String user, String[]
1740
     * groups ) throws Exception {
1741
     *
1742
     * Reader dtd = null; if ( dtdfilename != null ) { dtd = new FileReader(new
1743
     * File(dtdfilename).toString()); } return write ( conn, new FileReader(new
1744
     * File(filename).toString()), pub, dtd, action, docid, user, groups,
1745
     * false); }
1746
     */
1747

    
1748
    public static String write(DBConnection conn, Reader xml, String pub,
1749
            Reader dtd, String action, String docid, String user,
1750
            String[] groups, String ruleBase, boolean needValidation)
1751
            throws Exception
1752
    {
1753
        //this method will be called in handleUpdateOrInsert method
1754
        //in MetacatServlet class and now is wrapper into documentImple
1755
        // get server location for this doc
1756
        int serverLocation = getServerLocationNumber(docid);
1757
        return write(conn, xml, pub, dtd, action, docid, user, groups,
1758
                serverLocation, false, ruleBase, needValidation);
1759
    }
1760

    
1761
    /**
1762
     * Write an XML file to the database, given a Reader
1763
     *
1764
     * @param conn
1765
     *            the JDBC connection to the database
1766
     * @param xml
1767
     *            the xml stream to be loaded into the database
1768
     * @param pub
1769
     *            flag for public "read" access on xml document
1770
     * @param dtd
1771
     *            the dtd to be uploaded on server's file system
1772
     * @param action
1773
     *            the action to be performed (INSERT or UPDATE)
1774
     * @param accnum
1775
     *            the docid + rev# to use on INSERT or UPDATE
1776
     * @param user
1777
     *            the user that owns the document
1778
     * @param groups
1779
     *            the groups to which user belongs
1780
     * @param serverCode
1781
     *            the serverid from xml_replication on which this document
1782
     *            resides.
1783
     * @param override
1784
     *            flag to stop insert replication checking. if override = true
1785
     *            then a document not belonging to the local server will not be
1786
     *            checked upon update for a file lock. if override = false then
1787
     *            a document not from this server, upon update will be locked
1788
     *            and version checked.
1789
     */
1790

    
1791
    public static String write(DBConnection conn, Reader xml, String pub,
1792
            Reader dtd, String action, String accnum, String user,
1793
            String[] groups, int serverCode, boolean override, String ruleBase,
1794
            boolean needValidation) throws Exception
1795
    {
1796
        // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV IN IT
1797
        //MetaCatUtil util = new MetaCatUtil();
1798
        MetaCatUtil.debugMessage("conn usage count before writting: "
1799
                + conn.getUsageCount(), 50);
1800
        AccessionNumber ac = new AccessionNumber(accnum, action);
1801
        String docid = ac.getDocid();
1802
        String rev = ac.getRev();
1803
        MetaCatUtil.debugMessage("action: " + action + " servercode: "
1804
                + serverCode + " override: " + override, 10);
1805

    
1806
        if ((serverCode != 1 && action.equals("UPDATE")) && !override) {
1807
            // if this document being written is not a resident of this server
1808
            // then we need to try to get a lock from it's resident server. If
1809
            // the resident server will not give a lock then we send the user
1810
            // a  message saying that he/she needs to download a new copy of
1811
            // the file and merge the differences manually.
1812
            int istreamInt;
1813
            char istreamChar;
1814

    
1815
            // check for 'write' permission for 'user' to update this document
1816
            if (!hasWritePermission(user, groups, accnum)) {
1817
                throw new Exception(
1818
                    "User " + user
1819
                    + " does not have permission to update XML Document #"
1820
                    + accnum);
1821
            }
1822

    
1823
            DocumentIdentifier id = new DocumentIdentifier(accnum);
1824
            String updaterev = id.getRev();
1825
            String server = MetacatReplication
1826
                    .getServerNameForServerCode(serverCode);
1827
            MetacatReplication.replLog("attempting to lock " + accnum);
1828
            URL u = new URL("https://" + server + "?server="
1829
                    + MetaCatUtil.getLocalReplicationServerName()
1830
                    + "&action=getlock&updaterev=" + updaterev + "&docid="
1831
                    + docid);
1832
            //System.out.println("sending message: " + u.toString());
1833
            String serverResStr = MetacatReplication.getURLContent(u);
1834
            String openingtag = serverResStr.substring(0, serverResStr
1835
                    .indexOf(">") + 1);
1836
            if (openingtag.equals("<lockgranted>")) {//the lock was granted go
1837
                                                     // ahead with the insert
1838
                XMLReader parser = null;
1839
                try {
1840
                    //System.out.println("In lockgranted");
1841
                    MetacatReplication.replLog("lock granted for " + accnum
1842
                            + " from " + server);
1843
                    /*
1844
                     * XMLReader parser = initializeParser(conn, action, docid,
1845
                     * updaterev, validate, user, groups, pub, serverCode, dtd);
1846
                     */
1847
                    parser = initializeParser(conn, action, docid, updaterev,
1848
                            user, groups, pub, serverCode, dtd, ruleBase,
1849
                            needValidation);
1850
                    conn.setAutoCommit(false);
1851
                    parser.parse(new InputSource(xml));
1852
                    conn.commit();
1853
                    conn.setAutoCommit(true);
1854
                } catch (Exception e) {
1855
                    conn.rollback();
1856
                    conn.setAutoCommit(true);
1857
                    //if it is a eml2 document, we need delete online data
1858
                    if (parser != null) {
1859
                        ContentHandler handler = parser.getContentHandler();
1860
                        if (handler instanceof Eml200SAXHandler) {
1861
                            Eml200SAXHandler eml = (Eml200SAXHandler) handler;
1862
                            eml.deleteInlineFiles();
1863
                        }
1864
                    }
1865
                    throw e;
1866
                }
1867
                // run write into access db base one relation table and access
1868
                // object
1869
                runRelationAndAccessHandler(accnum, user, groups, serverCode);
1870

    
1871
                // Force replication the docid
1872
                ForceReplicationHandler frh = new ForceReplicationHandler(
1873
                        accnum, true, null);
1874
                return (accnum);
1875

    
1876
            }
1877

    
1878
            else if (openingtag.equals("<filelocked>")) {
1879
                // the file is currently locked by another user notify our
1880
                // user to wait a few minutes, check out a new copy and try
1881
                // again.
1882
                MetacatReplication.replLog("lock denied for " + accnum + " on "
1883
                        + server + " reason: file already locked");
1884
                throw new Exception(
1885
                        "The file specified is already locked by another "
1886
                                + "user.  Please wait 30 seconds, checkout the "
1887
                                + "newer document, merge your changes and try "
1888
                                + "again.");
1889
            } else if (openingtag.equals("<outdatedfile>")) {
1890
                // our file is outdated. notify our user to check out a new
1891
                // copy of the file and merge his version with the new version.
1892
                //System.out.println("outdated file");
1893
                MetacatReplication.replLog("lock denied for " + accnum + " on "
1894
                        + server + " reason: local file outdated");
1895
                throw new Exception(
1896
                        "The file you are trying to update is an outdated"
1897
                        + " version.  Please checkout the newest document, "
1898
                        + "merge your changes and try again.");
1899
            }
1900
        }
1901

    
1902
        if (action.equals("UPDATE")) {
1903
            // check for 'write' permission for 'user' to update this document
1904
            if (!hasWritePermission(user, groups, accnum)) {
1905
                throw new Exception(
1906
                    "User " + user
1907
                    + " does not have permission to update XML Document #"
1908
                    + accnum); }
1909
        }
1910
        XMLReader parser = null;
1911
        try {
1912
            parser = initializeParser(conn, action, docid, rev, user, groups,
1913
                    pub, serverCode, dtd, ruleBase, needValidation);
1914

    
1915
            conn.setAutoCommit(false);
1916
            parser.parse(new InputSource(xml));
1917
            conn.commit();
1918
            conn.setAutoCommit(true);
1919
        } catch (Exception e) {
1920
            conn.rollback();
1921
            conn.setAutoCommit(true);
1922
            //if it is a eml2 document, we need delete online data
1923
            if (parser != null) {
1924
                ContentHandler handler = parser.getContentHandler();
1925
                if (handler instanceof Eml200SAXHandler) {
1926
                    Eml200SAXHandler eml = (Eml200SAXHandler) handler;
1927
                    eml.deleteInlineFiles();
1928
                }
1929
            }
1930
            throw e;
1931
        }
1932

    
1933
        // run access db base on relation table and access object
1934
        runRelationAndAccessHandler(accnum, user, groups, serverCode);
1935

    
1936
        // Force replicate out the new document to each server in our server
1937
        // list. Start the thread to replicate this new document out to the
1938
        // other servers true mean it is xml document null is because no
1939
        // metacat notify the force replication.
1940
        ForceReplicationHandler frh = new ForceReplicationHandler(accnum,
1941
                action, true, null);
1942

    
1943
        MetaCatUtil.debugMessage("Conn Usage count after writting: "
1944
                + conn.getUsageCount(), 50);
1945
        return (accnum);
1946
    }
1947

    
1948
    /**
1949
     * Write an XML file to the database during replication
1950
     *
1951
     * @param conn
1952
     *            the JDBC connection to the database
1953
     * @param xml
1954
     *            the xml stream to be loaded into the database
1955
     * @param pub
1956
     *            flag for public "read" access on xml document
1957
     * @param dtd
1958
     *            the dtd to be uploaded on server's file system
1959
     * @param action
1960
     *            the action to be performed (INSERT or UPDATE)
1961
     * @param accnum
1962
     *            the docid + rev# to use on INSERT or UPDATE
1963
     * @param user
1964
     *            the user that owns the document
1965
     * @param groups
1966
     *            the groups to which user belongs
1967
     * @param homeServer
1968
     *            the name of server which the document origanlly create
1969
     * @param validate,
1970
     *            if the xml document is valid or not
1971
     * @param notifyServer,
1972
     *            the server which notify local server the force replication
1973
     *            command
1974
     */
1975
    public static String writeReplication(DBConnection conn, Reader xml,
1976
            String pub, Reader dtd, String action, String accnum, String user,
1977
            String[] groups, String homeServer, String notifyServer,
1978
            String ruleBase, boolean needValidation) throws Exception
1979
    {
1980
        MetaCatUtil.debugMessage("user in replication" + user, 30);
1981
        // Docid without revision
1982
        String docid = MetaCatUtil.getDocIdFromAccessionNumber(accnum);
1983
        // Revision specified by user (int)
1984
        int userSpecifyRev = MetaCatUtil.getRevisionFromAccessionNumber(accnum);
1985
        MetaCatUtil.debugMessage("The user specifyRev: " + userSpecifyRev, 30);
1986
        // Revision for this docid in current database
1987
        int revInDataBase = getLatestRevisionNumber(docid);
1988
        MetaCatUtil.debugMessage("The rev in data base: " + revInDataBase, 30);
1989
        // String to store the revision
1990
        String rev = null;
1991

    
1992
        //revIndataBase=-1, there is no record in xml_documents table
1993
        //the document is a new one for local server, inert it into table
1994
        //user specified rev should be great than 0
1995
        if (revInDataBase == -1 && userSpecifyRev >= 0) {
1996
            // rev equals user specified
1997
            rev = (new Integer(userSpecifyRev)).toString();
1998
            // action should be INSERT
1999
            action = "INSERT";
2000
        }
2001
        //rev is greater the last revsion number and revInDataBase isn't -1
2002
        // it is a updated file
2003
        else if (userSpecifyRev > revInDataBase && revInDataBase >= 0) {
2004
            // rev equals user specified
2005
            rev = (new Integer(userSpecifyRev)).toString();
2006
            // action should be update
2007
            action = "UPDATE";
2008
        }
2009
        // local server has newer version, then notify the remote server
2010
        else if (userSpecifyRev < revInDataBase && revInDataBase > 0) {
2011
            throw new Exception("Local server: "
2012
                    + MetaCatUtil.getOption("server")
2013
                    + " has newer revision of doc: " + docid + "."
2014
                    + revInDataBase + ". Please notify it.");
2015
        }
2016
        //other situation
2017
        else {
2018

    
2019
            throw new Exception("The docid" + docid
2020
                    + "'s revision number couldn't be " + userSpecifyRev);
2021
        }
2022
        // Variable to store homeserver code
2023
        int serverCode = -2;
2024

    
2025
        // If server is not int the xml replication talbe, insert it into
2026
        // xml_replication table
2027
        //serverList.addToServerListIfItIsNot(homeServer);
2028
        insertServerIntoReplicationTable(homeServer);
2029
        // Get server code again
2030
        serverCode = getServerCode(homeServer);
2031

    
2032
        MetaCatUtil
2033
                .debugMessage("Document " + docid + "." + rev + " " + action
2034
                        + " into local" + " metacat with servercode: "
2035
                        + serverCode, 10);
2036

    
2037
        // insert into xml_nodes table
2038
        XMLReader parser = null;
2039
        try {
2040

    
2041
            parser = initializeParser(conn, action, docid, rev, user, groups,
2042
                    pub, serverCode, dtd, ruleBase, needValidation);
2043
            conn.setAutoCommit(false);
2044
            parser.parse(new InputSource(xml));
2045
            conn.commit();
2046
            conn.setAutoCommit(true);
2047
        } catch (Exception e) {
2048
            conn.rollback();
2049
            conn.setAutoCommit(true);
2050
            if (parser != null) {
2051
                ContentHandler handler = parser.getContentHandler();
2052
                if (handler instanceof Eml200SAXHandler) {
2053
                    Eml200SAXHandler eml = (Eml200SAXHandler) handler;
2054
                    eml.deleteInlineFiles();
2055
                }
2056
            }
2057
            throw e;
2058
        }
2059

    
2060
        // run write into access db base on relation table and access rule
2061
        try {
2062
            runRelationAndAccessHandler(accnum, user, groups, serverCode);
2063
        } catch (Exception ee) {
2064
            MetacatReplication.replErrorLog("Failed to " + "create access "
2065
                    + "rule for package: " + accnum + " because "
2066
                    + ee.getMessage());
2067
            MetaCatUtil.debugMessage("Failed to  " + "create access "
2068
                    + "rule for package: " + accnum + " because "
2069
                    + ee.getMessage(), 30);
2070
        }
2071
        //Force replication to other server
2072
        ForceReplicationHandler forceReplication = new ForceReplicationHandler(
2073
                accnum, action, true, notifyServer);
2074

    
2075
        return (accnum);
2076
    }
2077

    
2078
    /* Running write record to xml_relation and xml_access */
2079
    private static void runRelationAndAccessHandler(String accnumber,
2080
            String userName, String[] group, int servercode) throws Exception
2081
    {
2082
        DBConnection dbconn = null;
2083
        int serialNumber = -1;
2084
        PreparedStatement pstmt = null;
2085
        String documenttype = getDocTypeFromDBForCurrentDocument(accnumber);
2086
        try {
2087
            String packagedoctype = MetaCatUtil.getOption("packagedoctype");
2088
            Vector packagedoctypes = new Vector();
2089
            packagedoctypes = MetaCatUtil.getOptionList(packagedoctype);
2090
            String docIdWithoutRev = MetaCatUtil.getDocIdFromString(accnumber);
2091
            if (documenttype != null &&
2092
                    packagedoctypes.contains(documenttype)) {
2093
                dbconn = DBConnectionPool.getDBConnection(
2094
                        "DocumentImpl.runRelationAndAccessHandeler");
2095
                serialNumber = dbconn.getCheckOutSerialNumber();
2096
                dbconn.setAutoCommit(false);
2097
                // from the relations get the access file id for that package
2098
                String aclid = RelationHandler.getAccessFileID(docIdWithoutRev);
2099
                // if there are access file, write ACL for that package
2100
                if (aclid != null) {
2101
                    runAccessControlList(dbconn, aclid, userName, group,
2102
                            servercode);
2103
                }
2104
                dbconn.commit();
2105
                dbconn.setAutoCommit(true);
2106
            }
2107
            // if it is an access file
2108
            else if (documenttype != null
2109
                    && MetaCatUtil.getOptionList(
2110
                            MetaCatUtil.getOption("accessdoctype")).contains(
2111
                            documenttype)) {
2112
                dbconn = DBConnectionPool.getDBConnection(
2113
                        "DocumentImpl.runRelationAndAccessHandeler");
2114
                serialNumber = dbconn.getCheckOutSerialNumber();
2115
                dbconn.setAutoCommit(false);
2116
                // write ACL for the package
2117
                runAccessControlList(dbconn, docIdWithoutRev, userName, group,
2118
                        servercode);
2119
                dbconn.commit();
2120
                dbconn.setAutoCommit(true);
2121

    
2122
            }
2123

    
2124
        } catch (Exception e) {
2125
            if (dbconn != null) {
2126
                dbconn.rollback();
2127
                dbconn.setAutoCommit(true);
2128
            }
2129
            MetaCatUtil.debugMessage(
2130
                    "Error in DocumentImple.runRelationAndAccessHandler "
2131
                            + e.getMessage(), 30);
2132
            throw e;
2133
        } finally {
2134
            if (dbconn != null) {
2135
                DBConnectionPool.returnDBConnection(dbconn, serialNumber);
2136
            }
2137
        }
2138
    }
2139

    
2140
    // It runs in xmlIndex thread. It writes ACL for a package.
2141
    private static void runAccessControlList(DBConnection conn, String aclid,
2142
            String users, String[] group, int servercode) throws Exception
2143
    {
2144
        // read the access file from xml_nodes
2145
        // parse the access file and store the access info into xml_access
2146
        AccessControlList aclobj = new AccessControlList(conn, aclid, users,
2147
                group, servercode);
2148

    
2149
    }
2150

    
2151
    /* Method get document type from db */
2152
    private static String getDocTypeFromDBForCurrentDocument(String accnumber)
2153
            throws SQLException
2154
    {
2155
        String docoumentType = null;
2156
        String docid = null;
2157
        PreparedStatement pstate = null;
2158
        ResultSet rs = null;
2159
        String sql = "SELECT doctype FROM xml_documents where docid = ?";
2160
        DBConnection dbConnection = null;
2161
        int serialNumber = -1;
2162
        try {
2163
            //get rid of revision number
2164
            docid = MetaCatUtil.getDocIdFromString(accnumber);
2165
            dbConnection = DBConnectionPool.getDBConnection(
2166
                    "DocumentImpl.getDocTypeFromDBForCurrentDoc");
2167
            serialNumber = dbConnection.getCheckOutSerialNumber();
2168
            pstate = dbConnection.prepareStatement(sql);
2169
            //bind variable
2170
            pstate.setString(1, docid);
2171
            //excute query
2172
            pstate.execute();
2173
            //handle resultset
2174
            rs = pstate.getResultSet();
2175
            if (rs.next()) {
2176
                docoumentType = rs.getString(1);
2177
            }
2178
            rs.close();
2179
            pstate.close();
2180
        }//try
2181
        catch (SQLException e) {
2182
            MetaCatUtil.debugMessage("error in DocumentImpl."
2183
                    + "getDocTypeFromDBForCurrentDocument " + e.getMessage(),
2184
                    30);
2185
            throw e;
2186
        }//catch
2187
        finally {
2188
            pstate.close();
2189
            DBConnectionPool.returnDBConnection(dbConnection, serialNumber);
2190
        }//
2191
        MetaCatUtil.debugMessage("The current doctype from db is: "
2192
                + docoumentType, 35);
2193
        return docoumentType;
2194
    }
2195

    
2196
    /**
2197
     * Delete an XML file from the database (actually, just make it a revision
2198
     * in the xml_revisions table)
2199
     *
2200
     * @param docid
2201
     *            the ID of the document to be deleted from the database
2202
     */
2203
    public static void delete(String accnum, String user, String[] groups)
2204
            throws Exception
2205
    {
2206
        // OLD
2207
        //DocumentIdentifier id = new DocumentIdentifier(accnum);
2208
        //String docid = id.getIdentifier();
2209
        //String rev = id.getRev();
2210

    
2211
        // OLD
2212
        // Determine if the docid,rev are OK for DELETE
2213
        //AccessionNumber ac = new AccessionNumber(conn);
2214
        //docid = ac.generate(docid, rev, "DELETE");
2215
        DBConnection conn = null;
2216
        int serialNumber = -1;
2217
        PreparedStatement pstmt = null;
2218
        try {
2219
            //check out DBConnection
2220
            conn = DBConnectionPool.getDBConnection("DocumentImpl.delete");
2221
            serialNumber = conn.getCheckOutSerialNumber();
2222

    
2223
            // NEW - WHEN CLIENT ALWAYS PROVIDE ACCESSION NUMBER INCLUDING REV
2224
            // IN IT
2225
            AccessionNumber ac = new AccessionNumber(accnum, "DELETE");
2226
            String docid = ac.getDocid();
2227
            String rev = ac.getRev();
2228

    
2229
            MetaCatUtil.debugMessage("Start deleting doc " + docid + "...", 20);
2230
            // check for 'write' permission for 'user' to delete this document
2231
            if (!hasAllPermission(user, groups, accnum)) { throw new Exception(
2232
                    "User " + user
2233
                    + " does not have permission to delete XML Document #"
2234
                    + accnum); }
2235

    
2236
            conn.setAutoCommit(false);
2237
            // Copy the record to the xml_revisions table
2238
            DocumentImpl.archiveDocRevision(conn, docid, user);
2239

    
2240
            // Now delete it from the xml_index table
2241
            boolean useXMLIndex = (new Boolean(MetaCatUtil
2242
                    .getOption("usexmlindex"))).booleanValue();
2243
            //if (useXMLIndex) {
2244
            pstmt = conn
2245
                    .prepareStatement("DELETE FROM xml_index WHERE docid = ?");
2246
            pstmt.setString(1, docid);
2247
            pstmt.execute();
2248
            pstmt.close();
2249
            conn.increaseUsageCount(1);
2250
            //}
2251

    
2252
            //stmt.execute("DELETE FROM xml_access WHERE docid = '" + docid +
2253
            // "'");
2254
            // Now delete it from xml_access table
2255
            pstmt = conn.prepareStatement(
2256
                    "DELETE FROM xml_access WHERE accessfileid = ?");
2257
            pstmt.setString(1, docid);
2258
            pstmt.execute();
2259
            pstmt.close();
2260
            conn.increaseUsageCount(1);
2261

    
2262
            pstmt = conn.prepareStatement(
2263
                    "DELETE FROM xml_access WHERE docid = ?");
2264
            pstmt.setString(1, docid);
2265
            pstmt.execute();
2266
            pstmt.close();
2267
            conn.increaseUsageCount(1);
2268

    
2269
            // Delete it from relation table
2270
            pstmt = conn.prepareStatement(
2271
                    "DELETE FROM xml_relation WHERE docid = ?");
2272
            //increase usage count
2273
            conn.increaseUsageCount(1);
2274
            pstmt.setString(1, docid);
2275
            pstmt.execute();
2276
            pstmt.close();
2277

    
2278
            // Delete it from xml_accesssubtree table
2279
            pstmt = conn.prepareStatement(
2280
                    "DELETE FROM xml_accesssubtree WHERE docid = ?");
2281
            //increase usage count
2282
            conn.increaseUsageCount(1);
2283
            pstmt.setString(1, docid);
2284
            pstmt.execute();
2285
            pstmt.close();
2286

    
2287
            // Delete it from xml_documents table
2288
            pstmt = conn.prepareStatement(
2289
                    "DELETE FROM xml_documents WHERE docid = ?");
2290
            pstmt.setString(1, docid);
2291
            pstmt.execute();
2292
            pstmt.close();
2293
            //Usaga count increase 1
2294
            conn.increaseUsageCount(1);
2295

    
2296
            conn.commit();
2297
            conn.setAutoCommit(true);
2298
        } catch (Exception e) {
2299
            MetaCatUtil.debugMessage("error in DocumentImpl.delete: "
2300
                    + e.getMessage(), 30);
2301
            throw e;
2302
        } finally {
2303

    
2304
            try {
2305
                // close preparedStatement
2306
                if (pstmt != null) {
2307
                    pstmt.close();
2308
                }
2309
            }
2310
            finally {
2311
                //check in DBonnection
2312
                DBConnectionPool.returnDBConnection(conn, serialNumber);
2313
            }
2314
        }
2315
        //IF this is a package document:
2316
        //delete all of the relations that this document created.
2317
        //if the deleted document is a package document its relations should
2318
        //no longer be active if it has been deleted from the system.
2319
    }
2320

    
2321
    /**
2322
     * Check for "WRITE" permission on @docid for @user and/or @groups
2323
     * from DB connection
2324
     */
2325
    private static boolean hasWritePermission(String user, String[] groups,
2326
            String docid) throws SQLException, Exception
2327
    {
2328
        // Check for WRITE permission on @docid for @user and/or @groups
2329
        PermissionController controller = new PermissionController(docid);
2330
        return controller.hasPermission(user, groups,
2331
                AccessControlInterface.WRITESTRING);
2332
    }
2333

    
2334
    /**
2335
     * Check for "READ" permission base on docid, user and group
2336
     *
2337
     * @param docid, the document
2338
     * @param user, user name
2339
     * @param groups, user's group
2340
     */
2341
    public static boolean hasReadPermission(String user, String[] groups,
2342
            String docId) throws SQLException, Exception
2343
    {
2344
        // Check for READ permission on @docid for @user and/or @groups
2345
        PermissionController controller = new PermissionController(docId);
2346
        return controller.hasPermission(user, groups,
2347
                AccessControlInterface.READSTRING);
2348
    }
2349

    
2350
    /**
2351
     * Check for "WRITE" permission on @docid for @user and/or @groups
2352
     * from DB connection
2353
     */
2354
    private static boolean hasAllPermission(String user, String[] groups,
2355
            String docid) throws SQLException, Exception
2356
    {
2357
        // Check for WRITE permission on @docid for @user and/or @groups
2358
        PermissionController controller = new PermissionController(docid);
2359
        return controller.hasPermission(user, groups,
2360
                AccessControlInterface.ALLSTRING);
2361
    }
2362

    
2363
    /**
2364
     * Set up the parser handlers for writing the document to the database
2365
     */
2366
    private static XMLReader initializeParser(DBConnection dbconn,
2367
            String action, String docid, String rev, String user,
2368
            String[] groups, String pub, int serverCode, Reader dtd,
2369
            String ruleBase, boolean needValidation) throws Exception
2370
    {
2371
        XMLReader parser = null;
2372
        try {
2373
            // handler
2374
            ContentHandler chandler;
2375
            EntityResolver eresolver;
2376
            DTDHandler dtdhandler;
2377
            // Get an instance of the parser
2378
            String parserName = MetaCatUtil.getOption("saxparser");
2379
            parser = XMLReaderFactory.createXMLReader(parserName);
2380
            if (ruleBase != null && ruleBase.equals(EML200)) {
2381
                MetaCatUtil.debugMessage("eml 2.0.0 parser", 20);
2382
                chandler = new Eml200SAXHandler(dbconn, action, docid, rev,
2383
                        user, groups, pub, serverCode);
2384
                parser.setContentHandler((ContentHandler) chandler);
2385
                parser.setErrorHandler((ErrorHandler) chandler);
2386
                parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2387
                parser.setProperty(LEXICALPROPERTY, chandler);
2388
                // turn on schema validation feature
2389
                parser.setFeature(VALIDATIONFEATURE, true);
2390
                parser.setFeature(NAMESPACEFEATURE, true);
2391
                //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2392
                parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2393
                // From DB to find the register external schema location
2394
                String externalSchemaLocation = null;
2395
                SchemaLocationResolver resolver = new SchemaLocationResolver();
2396
                externalSchemaLocation = resolver
2397
                        .getNameSpaceAndLocationString();
2398
                // Set external schemalocation.
2399
                if (externalSchemaLocation != null
2400
                        && !(externalSchemaLocation.trim()).equals("")) {
2401
                    parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2402
                            externalSchemaLocation);
2403
                }
2404
            } else if (ruleBase != null && ruleBase.equals(EML210)) {
2405
                MetaCatUtil.debugMessage("eml 2.1.0 parser", 20);
2406
                chandler = new Eml210SAXHandler(dbconn, action, docid, rev,
2407
                        user, groups, pub, serverCode);
2408
                parser.setContentHandler((ContentHandler) chandler);
2409
                parser.setErrorHandler((ErrorHandler) chandler);
2410
                parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2411
                parser.setProperty(LEXICALPROPERTY, chandler);
2412
                // turn on schema validation feature
2413
                parser.setFeature(VALIDATIONFEATURE, true);
2414
                parser.setFeature(NAMESPACEFEATURE, true);
2415
                //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2416
                parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2417
                // From DB to find the register external schema location
2418
                String externalSchemaLocation = null;
2419
                SchemaLocationResolver resolver = new SchemaLocationResolver();
2420
                externalSchemaLocation = resolver
2421
                        .getNameSpaceAndLocationString();
2422
                // Set external schemalocation.
2423
                if (externalSchemaLocation != null
2424
                        && !(externalSchemaLocation.trim()).equals("")) {
2425
                    parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2426
                            externalSchemaLocation);
2427
                }
2428
            } else {
2429
                //create a DBSAXHandler object which has the revision
2430
                // specification
2431
                chandler = new DBSAXHandler(dbconn, action, docid, rev, user,
2432
                        groups, pub, serverCode);
2433
                parser.setContentHandler((ContentHandler) chandler);
2434
                parser.setErrorHandler((ErrorHandler) chandler);
2435
                parser.setProperty(DECLARATIONHANDLERPROPERTY, chandler);
2436
                parser.setProperty(LEXICALPROPERTY, chandler);
2437

    
2438
                if (ruleBase != null && ruleBase.equals(SCHEMA)
2439
                        && needValidation) {
2440
                    MetaCatUtil.debugMessage("General schema parser", 20);
2441
                    // turn on schema validation feature
2442
                    parser.setFeature(VALIDATIONFEATURE, true);
2443
                    parser.setFeature(NAMESPACEFEATURE, true);
2444
                    //parser.setFeature(NAMESPACEPREFIXESFEATURE, true);
2445
                    parser.setFeature(SCHEMAVALIDATIONFEATURE, true);
2446
                    // From DB to find the register external schema location
2447
                    String externalSchemaLocation = null;
2448
                    SchemaLocationResolver resolver =
2449
                        new SchemaLocationResolver();
2450
                    externalSchemaLocation = resolver
2451
                            .getNameSpaceAndLocationString();
2452
                    // Set external schemalocation.
2453
                    if (externalSchemaLocation != null
2454
                            && !(externalSchemaLocation.trim()).equals("")) {
2455
                        parser.setProperty(EXTERNALSCHEMALOCATIONPROPERTY,
2456
                                externalSchemaLocation);
2457
                    }
2458

    
2459
                } else if (ruleBase != null && ruleBase.equals(DTD)
2460
                        && needValidation) {
2461
                    MetaCatUtil.debugMessage("dtd parser", 20);
2462
                    // turn on dtd validaton feature
2463
                    parser.setFeature(VALIDATIONFEATURE, true);
2464
                    eresolver = new DBEntityResolver(dbconn,
2465
                            (DBSAXHandler) chandler, dtd);
2466
                    dtdhandler = new DBDTDHandler(dbconn);
2467
                    parser.setEntityResolver((EntityResolver) eresolver);
2468
                    parser.setDTDHandler((DTDHandler) dtdhandler);
2469
                } else {
2470
                    MetaCatUtil.debugMessage("other parser", 20);
2471
                    // non validation
2472
                    parser.setFeature(VALIDATIONFEATURE, false);
2473
                    eresolver = new DBEntityResolver(dbconn,
2474
                            (DBSAXHandler) chandler, dtd);
2475
                    dtdhandler = new DBDTDHandler(dbconn);
2476
                    parser.setEntityResolver((EntityResolver) eresolver);
2477
                    parser.setDTDHandler((DTDHandler) dtdhandler);
2478
                }
2479
            }//else
2480
        } catch (Exception e) {
2481
            throw e;
2482
        }
2483
        return parser;
2484
    }
2485

    
2486
    /**
2487
     * Save a document entry in the xml_revisions table Connection use as a
2488
     * paramter is in order to rollback feature
2489
     */
2490
    private static void archiveDocRevision(DBConnection dbconn, String docid,
2491
            String user)
2492
    {
2493
        String sysdate = dbAdapter.getDateTimeFunction();
2494
        //DBConnection conn = null;
2495
        //int serialNumber = -1;
2496
        PreparedStatement pstmt = null;
2497

    
2498
        // create a record in xml_revisions table
2499
        // for that document as selected from xml_documents
2500

    
2501
        try {
2502
            //check out DBConnection
2503
            /*
2504
             * conn=DBConnectionPool.
2505
             * getDBConnection("DocumentImpl.archiveDocRevision");
2506
             * serialNumber=conn.getCheckOutSerialNumber();
2507
             */
2508
            pstmt = dbconn.prepareStatement("INSERT INTO xml_revisions "
2509
                    + "(docid, rootnodeid, docname, doctype, "
2510
                    + "user_owner, user_updated, date_created, date_updated, "
2511
                    + "server_location, rev, public_access, catalog_id) "
2512
                    + "SELECT ?, rootnodeid, docname, doctype, "
2513
                    + "user_owner, ?, " + sysdate + ", " + sysdate + ", "
2514
                    + "server_location, rev, public_access, catalog_id "
2515
                    + "FROM xml_documents " + "WHERE docid = ?");
2516
            // Increase dbconnection usage count
2517
            dbconn.increaseUsageCount(1);
2518
            // Bind the values to the query and execute it
2519
            pstmt.setString(1, docid);
2520
            pstmt.setString(2, user);
2521
            pstmt.setString(3, docid);
2522
            pstmt.execute();
2523
            pstmt.close();
2524
        } catch (SQLException e) {
2525
            MetaCatUtil.debugMessage(
2526
                    "Error in DocumentImpl.archiveDocRevision : "
2527
                            + e.getMessage(), 30);
2528
        } finally {
2529
            try {
2530
                pstmt.close();
2531
            } catch (SQLException ee) {
2532
                MetaCatUtil.debugMessage(
2533
                        "Error in DocumnetImpl.archiveDocRevision: "
2534
                                + ee.getMessage(), 50);
2535
            }
2536
        }
2537
    }
2538

    
2539
    /** Save a document entry in the xml_revisions table */
2540
    private static void archiveDocRevision(String docid, String user)
2541
    {
2542
        String sysdate = dbAdapter.getDateTimeFunction();
2543
        DBConnection conn = null;
2544
        int serialNumber = -1;
2545
        PreparedStatement pstmt = null;
2546

    
2547
        // create a record in xml_revisions table
2548
        // for that document as selected from xml_documents
2549

    
2550
        try {
2551
            //check out DBConnection
2552
            conn = DBConnectionPool
2553
                    .getDBConnection("DocumentImpl.archiveDocRevision");
2554
            serialNumber = conn.getCheckOutSerialNumber();
2555
            pstmt = conn.prepareStatement("INSERT INTO xml_revisions "
2556
                    + "(docid, rootnodeid, docname, doctype, "
2557
                    + "user_owner, user_updated, date_created, date_updated, "
2558
                    + "server_location, rev, public_access, catalog_id) "
2559
                    + "SELECT ?, rootnodeid, docname, doctype, "
2560
                    + "user_owner, ?, " + sysdate + ", " + sysdate + ", "
2561
                    + "server_location, rev, public_access, catalog_id "
2562
                    + "FROM xml_documents " + "WHERE docid = ?");
2563
            // Bind the values to the query and execute it
2564
            pstmt.setString(1, docid);
2565
            pstmt.setString(2, user);
2566
            pstmt.setString(3, docid);
2567
            pstmt.execute();
2568
            pstmt.close();
2569
        } catch (SQLException e) {
2570
            MetaCatUtil.debugMessage(
2571
                    "Error in DocumentImpl.archiveDocRevision : "
2572
                            + e.getMessage(), 30);
2573
        } finally {
2574
            try {
2575
                pstmt.close();
2576
            } catch (SQLException ee) {
2577
                MetaCatUtil.debugMessage(
2578
                        "Error in DocumnetImpl.archiveDocRevision: "
2579
                                + ee.getMessage(), 50);
2580
            } finally {
2581
                //check in DBConnection
2582
                DBConnectionPool.returnDBConnection(conn, serialNumber);
2583
            }
2584
        }
2585
    }
2586

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

    
2621
    /**
2622
     * Get last revision number from database for a docid If couldn't find an
2623
     * entry, -1 will return The return value is integer because we want compare
2624
     * it to there new one
2625
     *
2626
     * @param docid
2627
     *            <sitecode>. <uniqueid>part of Accession Number
2628
     */
2629
    public static int getLatestRevisionNumber(String docId) throws SQLException
2630
    {
2631
        int rev = 1;
2632
        PreparedStatement pStmt = null;
2633
        DBConnection dbConn = null;
2634
        int serialNumber = -1;
2635
        // get rid of rev
2636
        docId = MetaCatUtil.getDocIdFromString(docId);
2637
        try {
2638
            //check out DBConnection
2639
            dbConn = DBConnectionPool
2640
                    .getDBConnection("DocumentImpl.getLatestRevisionNumber");
2641
            serialNumber = dbConn.getCheckOutSerialNumber();
2642

    
2643
            pStmt = dbConn
2644
                    .prepareStatement("SELECT rev FROM xml_documents WHERE docid='"
2645
                            + docId + "'");
2646
            pStmt.execute();
2647

    
2648
            ResultSet rs = pStmt.getResultSet();
2649
            boolean hasRow = rs.next();
2650
            if (hasRow) {
2651
                rev = rs.getInt(1);
2652
                pStmt.close();
2653
            } else {
2654
                rev = -1;
2655
                pStmt.close();
2656
            }
2657
        }//try
2658
        finally {
2659
            try {
2660
                pStmt.close();
2661
            } catch (Exception ee) {
2662
                MetaCatUtil.debugMessage("Error in DocumentImpl."
2663
                        + "getLatestRevisionNumber: " + ee.getMessage(), 50);
2664
            } finally {
2665
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2666
            }
2667
        }//finally
2668

    
2669
        return rev;
2670
    }//getLatestRevisionNumber
2671

    
2672
    /**
2673
     * Get server location form database for a accNum
2674
     *
2675
     * @param accum
2676
     *            <sitecode>. <uniqueid>. <rev>
2677
     */
2678
    private static int getServerLocationNumber(String accNum)
2679
            throws SQLException
2680
    {
2681
        //get rid of revNum part
2682
        String docId = MetaCatUtil.getDocIdFromString(accNum);
2683
        PreparedStatement pStmt = null;
2684
        int serverLocation = 1;
2685
        DBConnection conn = null;
2686
        int serialNumber = -1;
2687

    
2688
        try {
2689
            //check out DBConnection
2690
            conn = DBConnectionPool
2691
                    .getDBConnection("DocumentImpl.getServerLocationNumber");
2692
            serialNumber = conn.getCheckOutSerialNumber();
2693

    
2694
            pStmt = conn
2695
                    .prepareStatement("SELECT server_location FROM xml_documents WHERE docid='"
2696
                            + docId + "'");
2697
            pStmt.execute();
2698

    
2699
            ResultSet rs = pStmt.getResultSet();
2700
            boolean hasRow = rs.next();
2701
            //if there is entry in xml_documents, get the serverlocation
2702
            if (hasRow) {
2703
                serverLocation = rs.getInt(1);
2704
                pStmt.close();
2705
            } else {
2706
                //if htere is no entry in xml_documents, we consider it is new
2707
                // document
2708
                //the server location is local host and value is 1
2709
                serverLocation = 1;
2710
                pStmt.close();
2711
            }
2712
        }//try
2713
        finally {
2714
            try {
2715
                pStmt.close();
2716
            }//try
2717
            catch (Exception ee) {
2718
                MetaCatUtil.debugMessage(
2719
                        "Error in DocumentImpl.getServerLocationNu(): "
2720
                                + ee.getMessage(), 50);
2721
            }//catch
2722
            finally {
2723
                DBConnectionPool.returnDBConnection(conn, serialNumber);
2724
            }//finally
2725
        }//finally
2726

    
2727
        return serverLocation;
2728
    }
2729

    
2730
    /**
2731
     * Given a server name, return its servercode in xml_replication table. If
2732
     * no server is found, -1 will return
2733
     *
2734
     * @param serverName,
2735
     */
2736
    private static int getServerCode(String serverName)
2737
    {
2738
        PreparedStatement pStmt = null;
2739
        int serverLocation = -2;
2740
        DBConnection dbConn = null;
2741
        int serialNumber = -1;
2742
        //MetaCatUtil util = new MetaCatUtil();
2743

    
2744
        //we should consider about local host too
2745
        if (serverName.equals(MetaCatUtil.getLocalReplicationServerName())) {
2746
            serverLocation = 1;
2747
            return serverLocation;
2748
        }
2749

    
2750
        try {
2751
            //check xml_replication table
2752
            //dbConn=util.openDBConnection();
2753
            //check out DBConnection
2754
            dbConn = DBConnectionPool
2755
                    .getDBConnection("DocumentImpl.getServerCode");
2756
            serialNumber = dbConn.getCheckOutSerialNumber();
2757
            pStmt = dbConn
2758
                    .prepareStatement("SELECT serverid FROM xml_replication WHERE server='"
2759
                            + serverName + "'");
2760
            pStmt.execute();
2761

    
2762
            ResultSet rs = pStmt.getResultSet();
2763
            boolean hasRow = rs.next();
2764
            //if there is entry in xml_replication, get the serverid
2765
            if (hasRow) {
2766
                serverLocation = rs.getInt(1);
2767
                pStmt.close();
2768
            } else {
2769
                // if htere is no entry in xml_replication, -1 will return
2770
                serverLocation = -1;
2771
                pStmt.close();
2772
            }
2773
        } catch (Exception e) {
2774
            MetaCatUtil.debugMessage("Error in DocumentImpl.getServerCode(): "
2775
                    + e.getMessage(), 30);
2776
        } finally {
2777
            try {
2778
                pStmt.close();
2779
            } catch (Exception ee) {
2780
                MetaCatUtil.debugMessage(
2781
                        "Error in DocumentImpl.getServerCode(): "
2782
                                + ee.getMessage(), 50);
2783
            } finally {
2784
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2785
            }
2786
        }
2787

    
2788
        return serverLocation;
2789
    }
2790

    
2791
    /**
2792
     * Insert a server into xml_replcation table
2793
     *
2794
     * @param server,
2795
     *            the name of server
2796
     */
2797
    private static synchronized void insertServerIntoReplicationTable(
2798
            String server)
2799
    {
2800
        PreparedStatement pStmt = null;
2801
        DBConnection dbConn = null;
2802
        int serialNumber = -1;
2803

    
2804
        // Initial value for the server
2805
        int replicate = 0;
2806
        int dataReplicate = 0;
2807
        int hub = 0;
2808

    
2809
        try {
2810
            // Get DBConnection
2811
            dbConn = DBConnectionPool
2812
                    .getDBConnection("DocumentImpl.insertServIntoReplicationTable");
2813
            serialNumber = dbConn.getCheckOutSerialNumber();
2814

    
2815
            // Compare the server to dabase
2816
            pStmt = dbConn
2817
                    .prepareStatement("SELECT serverid FROM xml_replication WHERE server='"
2818
                            + server + "'");
2819
            pStmt.execute();
2820
            ResultSet rs = pStmt.getResultSet();
2821
            boolean hasRow = rs.next();
2822
            // Close preparedstatement and result set
2823
            pStmt.close();
2824
            rs.close();
2825

    
2826
            // If the server is not in the table, and server is not local host,
2827
            // insert it
2828
            if (!hasRow
2829
                    && !server.equals(MetaCatUtil
2830
                            .getLocalReplicationServerName())) {
2831
                // Set auto commit false
2832
                dbConn.setAutoCommit(false);
2833
                /*
2834
                 * pStmt = dbConn.prepareStatement("INSERT INTO xml_replication " +
2835
                 * "(server, last_checked, replicate, datareplicate, hub) " +
2836
                 * "VALUES ('" + server + "', to_date(" + "'01/01/00',
2837
                 * 'MM/DD/YY'), '" + replicate +"', '"+dataReplicate+"','"+ hub +
2838
                 * "')");
2839
                 */
2840
                pStmt = dbConn
2841
                        .prepareStatement("INSERT INTO xml_replication "
2842
                                + "(server, last_checked, replicate, datareplicate, hub) "
2843
                                + "VALUES ('" + server + "', "
2844
                                + dbAdapter.toDate("01/01/1980", "MM/DD/YYYY")
2845
                                + ", '" + replicate + "', '" + dataReplicate
2846
                                + "','" + hub + "')");
2847

    
2848
                pStmt.execute();
2849
                dbConn.commit();
2850
                // Increase usage number
2851
                dbConn.increaseUsageCount(1);
2852
                pStmt.close();
2853

    
2854
            }
2855
        }//try
2856
        catch (Exception e) {
2857
            MetaCatUtil.debugMessage(
2858
                    "Error in DocumentImpl.insertServerIntoRepli(): "
2859
                            + e.getMessage(), 30);
2860
        }//catch
2861
        finally {
2862

    
2863
            try {
2864
                // Set auto commit true
2865
                dbConn.setAutoCommit(true);
2866
                pStmt.close();
2867

    
2868
            }//try
2869
            catch (Exception ee) {
2870
                MetaCatUtil.debugMessage(
2871
                        "Error in DocumentImpl.insetServerIntoRepl(): "
2872
                                + ee.getMessage(), 50);
2873
            }//catch
2874
            finally {
2875
                DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2876
            }
2877

    
2878
        }//finally
2879

    
2880
    }
2881

    
2882
    /**
2883
     * the main routine used to test the DBWriter utility.
2884
     * <p>
2885
     * Usage: java DocumentImpl <-f filename -a action -d docid>
2886
     *
2887
     * @param filename
2888
     *            the filename to be loaded into the database
2889
     * @param action
2890
     *            the action to perform (READ, INSERT, UPDATE, DELETE)
2891
     * @param docid
2892
     *            the id of the document to process
2893
     */
2894
    static public void main(String[] args)
2895
    {
2896
        DBConnection dbconn = null;
2897
        int serialNumber = -1;
2898
        try {
2899
            String filename = null;
2900
            String dtdfilename = null;
2901
            String action = null;
2902
            String docid = null;
2903
            boolean showRuntime = false;
2904
            boolean useOldReadAlgorithm = false;
2905

    
2906
            // Parse the command line arguments
2907
            for (int i = 0; i < args.length; ++i) {
2908
                if (args[i].equals("-f")) {
2909
                    filename = args[++i];
2910
                } else if (args[i].equals("-r")) {
2911
                    dtdfilename = args[++i];
2912
                } else if (args[i].equals("-a")) {
2913
                    action = args[++i];
2914
                } else if (args[i].equals("-d")) {
2915
                    docid = args[++i];
2916
                } else if (args[i].equals("-t")) {
2917
                    showRuntime = true;
2918
                } else if (args[i].equals("-old")) {
2919
                    useOldReadAlgorithm = true;
2920
                } else {
2921
                    System.err.println("   args[" + i + "] '" + args[i]
2922
                            + "' ignored.");
2923
                }
2924
            }
2925

    
2926
            // Check if the required arguments are provided
2927
            boolean argsAreValid = false;
2928
            if (action != null) {
2929
                if (action.equals("INSERT")) {
2930
                    if (filename != null) {
2931
                        argsAreValid = true;
2932
                    }
2933
                } else if (action.equals("UPDATE")) {
2934
                    if ((filename != null) && (docid != null)) {
2935
                        argsAreValid = true;
2936
                    }
2937
                } else if (action.equals("DELETE")) {
2938
                    if (docid != null) {
2939
                        argsAreValid = true;
2940
                    }
2941
                } else if (action.equals("READ")) {
2942
                    if (docid != null) {
2943
                        argsAreValid = true;
2944
                    }
2945
                }
2946
            }
2947

    
2948
            // Print usage message if the arguments are not valid
2949
            if (!argsAreValid) {
2950
                System.err.println("Wrong number of arguments!!!");
2951
                System.err
2952
                        .println("USAGE: java DocumentImpl [-t] <-a INSERT> [-d docid] <-f filename> "
2953
                                + "[-r dtdfilename]");
2954
                System.err
2955
                        .println("   OR: java DocumentImpl [-t] <-a UPDATE -d docid -f filename> "
2956
                                + "[-r dtdfilename]");
2957
                System.err
2958
                        .println("   OR: java DocumentImpl [-t] <-a DELETE -d docid>");
2959
                System.err
2960
                        .println("   OR: java DocumentImpl [-t] [-old] <-a READ -d docid>");
2961
                return;
2962
            }
2963

    
2964
            // Time the request if asked for
2965
            double startTime = System.currentTimeMillis();
2966

    
2967
            // Open a connection to the database
2968
            MetaCatUtil util = new MetaCatUtil();
2969

    
2970
            dbconn = DBConnectionPool.getDBConnection("DocumentImpl.main");
2971
            serialNumber = dbconn.getCheckOutSerialNumber();
2972

    
2973
            double connTime = System.currentTimeMillis();
2974
            // Execute the action requested (READ, INSERT, UPDATE, DELETE)
2975
            if (action.equals("READ")) {
2976
                DocumentImpl xmldoc = new DocumentImpl(docid);
2977
                if (useOldReadAlgorithm) {
2978
                    System.out.println(xmldoc.readUsingSlowAlgorithm());
2979
                } else {
2980
                    xmldoc.toXml(new PrintWriter(System.out), null, null, true);
2981
                }
2982
            } else if (action.equals("DELETE")) {
2983
                DocumentImpl.delete(docid, null, null);
2984
                System.out.println("Document deleted: " + docid);
2985
            } else {
2986
                /*
2987
                 * String newdocid = DocumentImpl.write(dbconn, filename, null,
2988
                 * dtdfilename, action, docid, null, null); if ((docid != null) &&
2989
                 * (!docid.equals(newdocid))) { if (action.equals("INSERT")) {
2990
                 * System.out.println("New document ID generated!!! "); } else
2991
                 * if (action.equals("UPDATE")) { System.out.println("ERROR:
2992
                 * Couldn't update document!!! "); } } else if ((docid == null) &&
2993
                 * (action.equals("UPDATE"))) { System.out.println("ERROR:
2994
                 * Couldn't update document!!! "); }
2995
                 * System.out.println("Document processing finished for: " +
2996
                 * filename + " (" + newdocid + ")");
2997
                 */
2998
            }
2999

    
3000
            double stopTime = System.currentTimeMillis();
3001
            double dbOpenTime = (connTime - startTime) / 1000;
3002
            double insertTime = (stopTime - connTime) / 1000;
3003
            double executionTime = (stopTime - startTime) / 1000;
3004
            if (showRuntime) {
3005
                System.out.println("\n\nTotal Execution time was: "
3006
                        + executionTime + " seconds.");
3007
                System.out.println("Time to open DB connection was: "
3008
                        + dbOpenTime + " seconds.");
3009
                System.out.println("Time to insert document was: " + insertTime
3010
                        + " seconds.");
3011
            }
3012
            dbconn.close();
3013
        } catch (McdbException me) {
3014
            me.toXml(new PrintWriter(System.err));
3015
        } catch (AccessionNumberException ane) {
3016
            System.out.println(ane.getMessage());
3017
        } catch (Exception e) {
3018
            System.err.println("EXCEPTION HANDLING REQUIRED");
3019
            System.err.println(e.getMessage());
3020
            e.printStackTrace(System.err);
3021
        } finally {
3022
            // Return db connection
3023
            DBConnectionPool.returnDBConnection(dbconn, serialNumber);
3024
        }
3025
    }
3026
}
(31-31/62)