Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that handles the SAX XML events as they
4
 *             are generated from XML documents
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Matt Jones, Jivka Bojilova
8
 *
9
 *   '$Author: daigle $'
10
 *     '$Date: 2008-10-21 15:29:13 -0700 (Tue, 21 Oct 2008) $'
11
 * '$Revision: 4472 $'
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.BufferedReader;
31
import java.io.File;
32
import java.io.FileReader;
33
import java.io.FileWriter;
34
import java.io.IOException;
35
import java.io.Reader;
36
import java.sql.PreparedStatement;
37
import java.sql.ResultSet;
38
import java.sql.SQLException;
39
import java.sql.Statement;
40
import java.util.EmptyStackException;
41
import java.util.Enumeration;
42
import java.util.Hashtable;
43
import java.util.Stack;
44
import java.util.Vector;
45

    
46
import org.apache.log4j.Logger;
47
import org.xml.sax.Attributes;
48
import org.xml.sax.SAXException;
49

    
50
import edu.ucsb.nceas.metacat.service.PropertyService;
51
import edu.ucsb.nceas.metacat.util.MetaCatUtil;
52
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
53

    
54
/**
55
 * A database aware Class implementing callback methods for the SAX parser to
56
 * call when processing the XML stream and generating events
57
 */
58
public class Eml210SAXHandler extends DBSAXHandler implements
59
        AccessControlInterface
60
{
61

    
62
    private boolean processTopLevelAccess = false;
63

    
64
    private boolean processAdditionalAccess = false;
65

    
66
    private boolean processOtherAccess = false;
67

    
68
    private AccessSection accessObject = null;
69

    
70
    private AccessRule accessRule = null;
71

    
72
    private Vector<AccessSection> accessObjectList = new Vector<AccessSection>(); // store every access rule
73

    
74
    private Hashtable<String,AccessSection> topLevelAccessControlMap = new Hashtable<String,AccessSection>();
75

    
76
    private Hashtable<String,AccessSection> additionalAccessControlMap = new Hashtable<String,AccessSection>();// store
77

    
78
    //subtree access for single additionalmetacat
79
    private Vector<Hashtable<String,AccessSection>> additionalAccessMapList = new Vector<Hashtable<String,AccessSection>>();// store maps for
80

    
81
    // every additionalmetadata
82
    private Vector<String> describesId = new Vector<String>(); // store the ids in
83

    
84
    //additionalmetadata/describes
85
    private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
86

    
87
    private Vector<SubTree> subTreeList = new Vector<SubTree>();// store the final subtree
88

    
89
    // MCD - Removed the following lines.  They are used for node level access control
90
    // which is not yet implemented in EML 2.1.0
91
	// private Hashtable<String,SubTree> unChangeableSubTreeHash = new Hashtable<String,SubTree>();
92
	// private Stack<NodeRecord> currentUnChangeableSubtreeNodeStack = new Stack<NodeRecord>();
93
	// private boolean startCriticalSubTree = false;
94
    // private boolean firstElementForCriticalSubTree = false;
95
    // private String firstElementNameForCriticalSubTree;
96

    
97
    private boolean needToCheckAccessModule = false;
98

    
99
    private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
100

    
101
    private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
102

    
103
    private AccessSection topAccessSection;
104

    
105
    // we need an another stack to store the access node which we pull out just
106
    // from xml. If a reference happened, we can use the stack the compare nodes
107
    private Stack<NodeRecord> storedAccessNodeStack = new Stack<NodeRecord>();
108

    
109
    // vector stored the data file id which will be write into relation table
110
    private Vector<String> onlineDataFileIdInRelationVector = new Vector<String>();
111

    
112
    // vector stored the data file id which will be write top access rules to
113
   // access table
114
    private Vector<String> onlineDataFileIdInTopAccessVector = new Vector<String>();
115

    
116
    // Indicator of inline data
117
    private boolean handleInlineData = false;
118

    
119
    private Hashtable<String,String> inlineDataNameSpace = null;
120

    
121
    private FileWriter inlineDataFileWriter = null;
122

    
123
    private String inlineDataFileName = null;
124

    
125
    // This variable keeps a counter of each distribution element.  This index will
126
    // be used to name the inline data file that gets written to disk, and to strip
127
    // inline data from the metadata document on file if the user does not have read
128
    // access.
129
	private int distributionIndex = 0;
130

    
131
	// This is used to delete inline files if the xml does not parse correctly.
132
    private Vector<String> inlineFileIdList = new Vector<String>();
133

    
134
    // Constant
135
    private static final String EML = "eml";
136
    
137
    private static final String DISTRIBUTION = "distribution";
138

    
139
    private static final String ORDER = "order";
140

    
141
    private static final String ID = "id";
142

    
143
    private static final String REFERENCES = "references";
144

    
145
    public static final String INLINE = "inline";
146

    
147
    private static final String ONLINE = "online";
148

    
149
    private static final String URL = "url";
150

    
151
    // private static final String PERMISSIONERROR = "User tried to update a subtree "
152
    //        + "when they don't have write permission!";
153

    
154
    private static final String UPDATEACCESSERROR = "User tried to update an "
155
            + "access module when they don't have \"ALL\" permission!";
156

    
157
    private static final String TOPLEVEL = "top";
158

    
159
    private static final String SUBTREELEVEL = "subtree";
160

    
161
    private static final String RELATION = "Provides info for";
162

    
163
    private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);   	   	
164

    
165
    /**
166
     * Construct an instance of the handler class In this constructor, user can
167
     * specify the version need to update
168
     *
169
     * @param conn the JDBC connection to which information is written
170
     * @param action - "INSERT" or "UPDATE"
171
     * @param docid to be inserted or updated into JDBC connection
172
     * @param revision, the user specified the revision need to be update
173
     * @param user the user connected to MetaCat servlet and owns the document
174
     * @param groups the groups to which user belongs
175
     * @param pub flag for public "read" access on document
176
     * @param serverCode the serverid from xml_replication on which this
177
     *            document resides.
178
     *
179
     */
180
    public Eml210SAXHandler(DBConnection conn, String action, String docid,
181
            String revision, String user, String[] groups, String pub,
182
            int serverCode, String createDate, String updateDate) throws SAXException
183
    {
184
        super(conn, action, docid, revision, user, groups, pub, 
185
                serverCode, createDate, updateDate);
186
        // Get the unchangeable subtrees (user doesn't have write permission)
187
        try {
188
            PermissionController control = new PermissionController(docid
189
                    + PropertyService.getProperty("document.accNumSeparator") + revision);
190
            //unChangableSubTreeHash = getUnchangableSubTree(control, user,
191
            // groups);
192

    
193
            //If the action is update and user doesn't have "ALL" permission
194
            // we need to check if user can update access subtree
195
            if (action.equals("UPDATE")
196
                    && !control.hasPermission(user, groups,
197
                            AccessControlInterface.ALLSTRING)) {
198
                needToCheckAccessModule = true;
199
                unChangeableAccessSubTreeVector = getAccessSubTreeListFromDB();
200
            }
201
        } catch (Exception e) {
202
            throw new SAXException(e.getMessage());
203
        }
204
    }
205

    
206
    // MCD - Removed the following method.  It is used for node level access control
207
    // which is not yet implemented in EML 2.1.0
208
    //    /* Pass a permission control and get the list of unchangable subtree */
209
    //    private Hashtable getUnchangableSubTree(PermissionController controller,
210
    //            String user, String[] groups) throws Exception
211
    //    {
212
    //        Hashtable list = null;
213
    //        Hashtable result = new Hashtable();
214
    //        // get unwritable sutree from controller
215
    //        list = null;
216
    //        // changed after old code removal
217
    //        //controller.hasUnaccessableSubTree(user, groups,
218
    //        //        AccessControlInterface.WRITESTRING);
219
    //        if (list != null) {
220
	//
221
    //            Enumeration en = list.elements();
222
    //            while (en.hasMoreElements()) {
223
    //                // Get a subtree without node record list
224
    //                SubTree treeWithoutStack = (SubTree) en.nextElement();
225
    //                String subTreeId = treeWithoutStack.getSubTreeId();
226
    //                logMetacat.info(
227
    //                        "unchangable subtree id: " + subTreeId);
228
    //                long startNodeId = treeWithoutStack.getStartNodeId();
229
    //                logMetacat.info("unchangable subtree startnodeid: "
230
    //                        + startNodeId);
231
    //                long endNodeId = treeWithoutStack.getEndNodeId();
232
    //                logMetacat.info("unchangable subtree endnodeid: "
233
    //                        + endNodeId);
234
    //                // Get a subtree has the nodelist
235
    //                SubTree tree = new SubTree(docid, subTreeId, startNodeId,
236
    //                        endNodeId);
237
    //                // add this tree to the result
238
    //                result.put(subTreeId, tree);
239
    //
240
    //            }//while
241
    //
242
    //        }//if
243
    //
244
    //        return result;
245
    //    }
246

    
247
    /*
248
     * Get the subtree node info from xml_accesssubtree table
249
     */
250
    private Vector<AccessSection> getAccessSubTreeListFromDB() throws Exception
251
    {
252
        Vector<AccessSection> result = new Vector<AccessSection>();
253
        PreparedStatement pstmt = null;
254
        ResultSet rs = null;
255
        String sql = "SELECT controllevel, subtreeid, startnodeid, endnodeid "
256
                + "FROM xml_accesssubtree WHERE docid like ? "
257
                + "ORDER BY startnodeid ASC";
258

    
259
        try {
260

    
261
            pstmt = connection.prepareStatement(sql);
262
            // Increase DBConnection usage count
263
            connection.increaseUsageCount(1);
264
            // Bind the values to the query
265
            pstmt.setString(1, docid);
266
            pstmt.execute();
267

    
268
            // Get result set
269
            rs = pstmt.getResultSet();
270
            while (rs.next()) {
271
                String level = rs.getString(1);
272
                String sectionId = rs.getString(2);
273
                long startNodeId = rs.getLong(3);
274
                long endNodeId = rs.getLong(4);
275
                // create a new access section
276
                AccessSection accessObj = new AccessSection();
277
                accessObj.setControlLevel(level);
278
                accessObj.setDocId(docid);
279
                accessObj.setSubTreeId(sectionId);
280
                accessObj.setStartNodeId(startNodeId);
281
                accessObj.setEndNodeId(endNodeId);
282
                Stack<NodeRecord> nodeStack = accessObj.getSubTreeNodeStack();
283
                accessObj.setSubTreeNodeStack(nodeStack);
284
                // add this access obj into vector
285
                result.add(accessObj);
286
                // Get the top level access subtree control
287
                if (level != null && level.equals(TOPLEVEL)) {
288
                    topAccessSection = accessObj;
289
                }
290
            }
291
            pstmt.close();
292
        }//try
293
        catch (SQLException e) {
294
            throw new SAXException(
295
                    "EMLSAXHandler.getAccessSubTreeListFromDB(): "
296
                            + e.getMessage());
297
        }//catch
298
        finally {
299
            try {
300
                pstmt.close();
301
            } catch (SQLException ee) {
302
                throw new SAXException(
303
                        "EMLSAXHandler.getAccessSubTreeListFromDB(): "
304
                                + ee.getMessage());
305
            }
306
        }//finally
307
        return result;
308
    }
309

    
310
    /** SAX Handler that is called at the start of each XML element */
311
    public void startElement(String uri, String localName, String qName,
312
            Attributes atts) throws SAXException
313
    {
314
        // for element <eml:eml...> qname is "eml:eml", local name is "eml"
315
        // for element <acl....> both qname and local name is "eml"
316
        // uri is namesapce
317
        logMetacat.debug("Start ELEMENT(qName) " + qName);
318
        logMetacat.debug("Start ELEMENT(localName) " + localName);
319
        logMetacat.debug("Start ELEMENT(uri) " + uri);
320

    
321
        DBSAXNode parentNode = null;
322
        DBSAXNode currentNode = null;
323

    
324
        if (!handleInlineData) {
325
            // Get a reference to the parent node for the id
326
            try {
327
                parentNode = (DBSAXNode) nodeStack.peek();
328
            } catch (EmptyStackException e) {
329
                parentNode = null;
330
            }
331

    
332
            //start handle inline data
333
            if (localName.equals(INLINE)) {
334
                handleInlineData = true;
335
                //initialize namespace hash for in line data
336
                inlineDataNameSpace = new Hashtable<String,String>();
337
                //initialize file writer
338
                String docidWithoutRev = MetaCatUtil.getDocIdFromString(docid);
339
                String seperator = ".";
340
                try {
341
                seperator = PropertyService.getProperty("document.accNumSeparator");
342
                } catch (PropertyNotFoundException pnfe) {
343
                	logMetacat.error("Could not fing property 'accNumSeparator'. " 
344
                			+ "Setting separator to '.': " + pnfe.getMessage());
345
                }
346
                // the new file name will look like docid.rev.2
347
                inlineDataFileName = docidWithoutRev + seperator + revision + seperator
348
						+ distributionIndex;
349
                inlineDataFileWriter = createInlineDataFileWriter(inlineDataFileName);
350
                // put the inline file id into a vector. If upload failed,
351
                // metacat will delete the inline data file
352
                inlineFileIdList.add(inlineDataFileName);
353

    
354
            }
355

    
356
            // If hit a text node, we need write this text for current's parent
357
            // node This will happen if the element is mixed
358
            if (hitTextNode && parentNode != null) {
359
                // MCD - Removed the following method.  It is used for node level access control
360
                // which is not yet implemented in EML 2.1.0
361
                // //compare text node data for unchangeablesubtree
362
            	// if (startCriticalSubTree) {
363
            	//     compareTextNode(currentUnChangeableSubtreeNodeStack,
364
            	//             textBuffer, PERMISSIONERROR);
365
            	// }//if
366

    
367
                //compare top level access module
368
                if (processTopLevelAccess && needToCheckAccessModule) {
369
                    compareTextNode(currentUnchangeableAccessModuleNodeStack,
370
                            textBuffer, UPDATEACCESSERROR);
371
                }
372

    
373
                if (needToCheckAccessModule
374
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
375
                    // stored the pull out nodes into storedNode stack
376
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
377
                            null, null, MetaCatUtil.normalize(textBuffer
378
                                    .toString()));
379
                    storedAccessNodeStack.push(nodeElement);
380

    
381
                }
382

    
383
                // write the textbuffer into db for parent node.
384
                endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
385
                        parentNode);
386
                // rest hitTextNode
387
                hitTextNode = false;
388
                // reset textbuffer
389
                textBuffer = null;
390
                textBuffer = new StringBuffer();
391

    
392
            }
393

    
394
            // Document representation that points to the root document node
395
            if (atFirstElement) {
396
                atFirstElement = false;
397
                // If no DOCTYPE declaration: docname = root element
398
                // doctype = root element name or name space
399
                if (docname == null) {
400
                    docname = localName;
401
                    // if uri isn't null doctype = uri(namespace)
402
                    // othewise root element
403
                    if (uri != null && !(uri.trim()).equals("")) {
404
                        doctype = uri;
405
                    } else {
406
                        doctype = docname;
407
                    }
408
                    logMetacat.debug("DOCNAME-a: " + docname);
409
                    logMetacat.debug("DOCTYPE-a: " + doctype);
410
                } else if (doctype == null) {
411
                    // because docname is not null and it is declared in dtd
412
                    // so could not be in schema, no namespace
413
                    doctype = docname;
414
                    logMetacat.debug("DOCTYPE-b: " + doctype);
415
                }
416
                rootNode.writeNodename(docname);
417
                try {
418
                    // for validated XML Documents store a reference to XML DB
419
                    // Catalog. Because this is select statement and it needn't 
420
                	// roll back if insert document action failed. In order to 
421
                	// decrease DBConnection usage count, we get a new
422
                    // dbconnection from pool String catalogid = null;
423
                    DBConnection dbConn = null;
424
                    int serialNumber = -1;
425

    
426
                    try {
427
                        // Get dbconnection
428
                        dbConn = DBConnectionPool
429
                                .getDBConnection("DBSAXHandler.startElement");
430
                        serialNumber = dbConn.getCheckOutSerialNumber();
431

    
432
                        Statement stmt = dbConn.createStatement();
433
                        ResultSet rs = stmt
434
                                .executeQuery("SELECT catalog_id FROM xml_catalog "
435
                                        + "WHERE entry_type = 'Schema' "
436
                                        + "AND public_id = '" + doctype + "'");
437
                        boolean hasRow = rs.next();
438
                        if (hasRow) {
439
                            catalogid = rs.getString(1);
440
                        }
441
                        stmt.close();
442
                    }//try
443
                    finally {
444
                        // Return dbconnection
445
                        DBConnectionPool.returnDBConnection(dbConn,
446
                                serialNumber);
447
                    }//finally
448

    
449
                    //create documentImpl object by the constructor which can
450
                    // specify the revision
451
                    if (!super.getIsRevisionDoc())
452
                    {
453
                       currentDocument = new DocumentImpl(connection, rootNode
454
                            .getNodeID(), docname, doctype, docid, revision,
455
                            action, user, this.pub, catalogid, this.serverCode, 
456
                            createDate, updateDate);
457
                    }
458
                   
459
                } catch (Exception ane) {
460
                    throw (new SAXException(
461
                            "Error in EMLSaxHandler.startElement " + action,
462
                            ane));
463
                }
464
            }
465

    
466
            // Create the current node representation
467
            currentNode = new DBSAXNode(connection, qName, localName,
468
                    parentNode, rootNode.getNodeID(), docid, doctype);
469
            // Use a local variable to store the element node id
470
            // If this element is a start point of subtree(section), it will be
471
            // stored otherwise, it will be discarded
472
            long startNodeId = currentNode.getNodeID();
473
            // Add all of the namespaces
474
            String prefix = null;
475
            String nsuri = null;
476
            Enumeration<String> prefixes = namespaces.keys();
477
            while (prefixes.hasMoreElements()) {
478
                prefix = prefixes.nextElement();
479
                nsuri = namespaces.get(prefix);
480
                endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
481
            }
482

    
483
            // Add all of the attributes
484
            for (int i = 0; i < atts.getLength(); i++) {
485
                String attributeName = atts.getQName(i);
486
                String attributeValue = atts.getValue(i);
487
                endNodeId = currentNode.setAttribute(attributeName,
488
                        attributeValue, docid);
489

    
490
                // To handle name space and schema location if the attribute
491
                // name is xsi:schemaLocation. If the name space is in not 
492
                // in catalog table it will be registered.
493
                if (attributeName != null
494
                        && attributeName
495
                                .indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
496
                    SchemaLocationResolver resolver = new SchemaLocationResolver(
497
                            attributeValue);
498
                    resolver.resolveNameSpace();
499

    
500
                } else if (attributeName != null && attributeName.equals(ID)) {
501
                    // MCD - Removed the following code.  It is used for node level access 
502
                	// control which is not yet implemented in EML 2.1.0
503
                    // // check unchangeable subtree hash if contains this
504
                	// // subtree id
505
                	// if (unChangeableSubTreeHash.containsKey(attributeValue)) {
506
                	//     // this subtree couldn't be changed by the user and
507
                	//     // move it from hash
508
                	//     SubTree currentUnChangedableSubtree = unChangeableSubTreeHash
509
                	//             .remove(attributeValue);
510
                	//     currentUnChangeableSubtreeNodeStack = currentUnChangedableSubtree
511
                	//             .getSubTreeNodeStack();
512
                	//     startCriticalSubTree = true;
513
                	//     firstElementForCriticalSubTree = true;
514
                	// }
515
                }
516
            }//for
517

    
518
            // handle access stuff
519
            if (localName.equals(ACCESS)) {
520
            	if (parentNode.getTagName().equals(EML)) {
521
					processTopLevelAccess = true;
522
				} else if (parentNode.getTagName() == DISTRIBUTION) {
523
					processAdditionalAccess = true;
524
				} else {
525
					// process other access embedded into resource level
526
					// module
527
					processOtherAccess = true;
528
				}
529
                // create access object
530
                accessObject = new AccessSection();
531
                // set permission order
532
                String permOrder = currentNode.getAttribute(ORDER);
533
                accessObject.setPermissionOrder(permOrder);
534
                // set access id
535
                String accessId = currentNode.getAttribute(ID);
536
                accessObject.setSubTreeId(accessId);
537
                accessObject.setStartNodeId(startNodeId);
538
                accessObject.setDocId(docid);
539
                if (processAdditionalAccess) {
540
                	accessObject.setInlineDataFileName(inlineDataFileName);
541
                }
542

    
543
                // load top level node stack to
544
                // currentUnchangableAccessModuleNodeStack
545
                if (processTopLevelAccess && needToCheckAccessModule) {
546
                    // get the node stack for
547
                    currentUnchangeableAccessModuleNodeStack = topAccessSection
548
                            .getSubTreeNodeStack();
549
                }
550
            }
551
            else if (localName.equals(DISTRIBUTION)) {
552
            	distributionIndex++;    
553
            	
554
              // handle subtree info
555
              SubTree subTree = new SubTree();
556
              // set sub tree id
557
              subTree.setSubTreeId(String.valueOf(distributionIndex));
558
              // set sub tree start element name
559
              subTree.setStartElementName(currentNode.getTagName());
560
              // set start node number
561
              subTree.setStartNodeId(startNodeId);
562
              // add to stack, but it didn't get end node id yet
563
              subTreeInfoStack.push(subTree);
564
            }
565
            // Set up a access rule for allow
566
            else if (parentNode.getTagName() != null
567
                    && (parentNode.getTagName()).equals(ACCESS)
568
                    && localName.equals(ALLOW)) {
569

    
570
                accessRule = new AccessRule();
571

    
572
                //set permission type "allow"
573
                accessRule.setPermissionType(ALLOW);
574

    
575
            }
576
            // set up an access rule for deny
577
            else if (parentNode.getTagName() != null
578
                    && (parentNode.getTagName()).equals(ACCESS)
579
                    && localName.equals(DENY)) {
580
                accessRule = new AccessRule();
581
                //set permission type "allow"
582
                accessRule.setPermissionType(DENY);
583
            }
584

    
585
            // Add the node to the stack, so that any text data can be
586
            // added as it is encountered
587
            nodeStack.push(currentNode);
588
            // Add the node to the vector used by thread for writing XML Index
589
            nodeIndex.addElement(currentNode);
590
            
591
            // MCD - Removed the following code.  It is used for node level access 
592
        	// control which is not yet implemented in EML 2.1.0
593
            // // handle critical subtree
594
            // if (startCriticalSubTree && firstElementForCriticalSubTree) {
595
            //     //store the element name
596
            //     firstElementNameForCriticalSubTree = qName;
597
            //     firstElementForCriticalSubTree = false;
598
            // }//for first element
599
            //
600
            // // handle critical subtree
601
            // if (startCriticalSubTree) {
602
            //     compareElementNameSpaceAttributes(
603
            //             currentUnChangeableSubtreeNodeStack, namespaces, atts,
604
            //             localName, PERMISSIONERROR);
605
            // 
606
            // }
607
            
608
            //compare top access level module
609
            if (processTopLevelAccess && needToCheckAccessModule) {
610
                compareElementNameSpaceAttributes(
611
                        currentUnchangeableAccessModuleNodeStack, namespaces,
612
                        atts, localName, UPDATEACCESSERROR);
613

    
614
            }
615

    
616
            // store access module element and attributes into stored stack
617
            if (needToCheckAccessModule
618
                    && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
619
                // stored the pull out nodes into storedNode stack
620
                NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT",
621
                        localName, prefix, MetaCatUtil.normalize(null));
622
                storedAccessNodeStack.push(nodeElement);
623
                for (int i = 0; i < atts.getLength(); i++) {
624
                    String attributeName = atts.getQName(i);
625
                    String attributeValue = atts.getValue(i);
626
                    NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2,
627
                            "ATTRIBUTE", attributeName, null, MetaCatUtil
628
                                    .normalize(attributeValue));
629
                    storedAccessNodeStack.push(nodeAttribute);
630
                }
631

    
632
            }
633

    
634
            // reset name space
635
            namespaces = null;
636
            namespaces = new Hashtable<String, String>();
637
        }//not inline data
638
        else {
639
            // we don't buffer the inline data in characters() method
640
            // so start character don't need to hand text node.
641

    
642
            // inline data may be the xml format.
643
            StringBuffer inlineElements = new StringBuffer();
644
            inlineElements.append("<").append(qName);
645
            // append attributes
646
            for (int i = 0; i < atts.getLength(); i++) {
647
                String attributeName = atts.getQName(i);
648
                String attributeValue = atts.getValue(i);
649
                inlineElements.append(" ");
650
                inlineElements.append(attributeName);
651
                inlineElements.append("=\"");
652
                inlineElements.append(attributeValue);
653
                inlineElements.append("\"");
654
            }
655
            // append namespace
656
            String prefix = null;
657
            String nsuri = null;
658
            Enumeration<String> prefixes = inlineDataNameSpace.keys();
659
            while (prefixes.hasMoreElements()) {
660
                prefix = prefixes.nextElement();
661
                nsuri = namespaces.get(prefix);
662
                inlineElements.append(" ");
663
                inlineElements.append("xmlns:");
664
                inlineElements.append(prefix);
665
                inlineElements.append("=\"");
666
                inlineElements.append(nsuri);
667
                inlineElements.append("\"");
668
            }
669
            inlineElements.append(">");
670
            //reset inline data name space
671
            inlineDataNameSpace = null;
672
            inlineDataNameSpace = new Hashtable<String, String>();
673
            //write inline data into file
674
            logMetacat.debug("the inline element data is: "
675
                    + inlineElements.toString());
676
            writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
677
        }//else
678

    
679
    }
680

    
681
    private void compareElementNameSpaceAttributes(Stack<NodeRecord> unchangeableNodeStack,
682
            Hashtable<String,String> nameSpaces, Attributes attributes, String localName,
683
            String error) throws SAXException
684
    {
685
        //Get element subtree node stack (element node)
686
        NodeRecord elementNode = null;
687
        try {
688
            elementNode = unchangeableNodeStack.pop();
689
        } catch (EmptyStackException ee) {
690
            logMetacat.error("Node stack is empty for element data");
691
            throw new SAXException(error);
692
        }
693
        logMetacat.debug("current node type from xml is ELEMENT");
694
        logMetacat.debug("node type from stack: "
695
                + elementNode.getNodeType());
696
        logMetacat.debug("node name from xml document: " + localName);
697
        logMetacat.debug("node name from stack: "
698
                + elementNode.getNodeName());
699
        logMetacat.debug("node data from stack: "
700
                + elementNode.getNodeData());
701
        logMetacat.debug("node id is: " + elementNode.getNodeId());
702
        // if this node is not element or local name not equal or name space
703
        // not equals, throw an exception
704
        if (!elementNode.getNodeType().equals("ELEMENT")
705
                || !localName.equals(elementNode.getNodeName()))
706
        //  (uri != null && !uri.equals(elementNode.getNodePrefix())))
707
        {
708
            logMetacat.error("Inconsistence happened: ");
709
            logMetacat.error("current node type from xml is ELEMENT");
710
            logMetacat.error("node type from stack: "
711
                    + elementNode.getNodeType());
712
            logMetacat.error("node name from xml document: "
713
                    + localName);
714
            logMetacat.error("node name from stack: "
715
                    + elementNode.getNodeName());
716
            logMetacat.error("node data from stack: "
717
                    + elementNode.getNodeData());
718
            logMetacat.error("node id is: " + elementNode.getNodeId());
719
            throw new SAXException(error);
720
        }
721

    
722
        //compare namespace
723
        Enumeration<String> nameEn = nameSpaces.keys();
724
        while (nameEn.hasMoreElements()) {
725
            //Get namespacke node stack (element node)
726
            NodeRecord nameNode = null;
727
            try {
728
                nameNode = unchangeableNodeStack.pop();
729
            } catch (EmptyStackException ee) {
730
                logMetacat.error(
731
                        "Node stack is empty for namespace data");
732
                throw new SAXException(error);
733
            }
734

    
735
            String prefixName = nameEn.nextElement();
736
            String nameSpaceUri = nameSpaces.get(prefixName);
737
            if (!nameNode.getNodeType().equals("NAMESPACE")
738
                    || !prefixName.equals(nameNode.getNodeName())
739
                    || !nameSpaceUri.equals(nameNode.getNodeData())) {
740
                logMetacat.error("Inconsistence happened: ");
741
                logMetacat.error(
742
                        "current node type from xml is NAMESPACE");
743
                logMetacat.error("node type from stack: "
744
                        + nameNode.getNodeType());
745
                logMetacat.error("current node name from xml is: "
746
                        + prefixName);
747
                logMetacat.error("node name from stack: "
748
                        + nameNode.getNodeName());
749
                logMetacat.error("current node data from xml is: "
750
                        + nameSpaceUri);
751
                logMetacat.error("node data from stack: "
752
                        + nameNode.getNodeData());
753
                logMetacat.error("node id is: " + nameNode.getNodeId());
754
                throw new SAXException(error);
755
            }
756

    
757
        }//while
758

    
759
        //compare attributes
760
        for (int i = 0; i < attributes.getLength(); i++) {
761
            NodeRecord attriNode = null;
762
            try {
763
                attriNode = unchangeableNodeStack.pop();
764

    
765
            } catch (EmptyStackException ee) {
766
                logMetacat.error("Node stack is empty for attribute data");
767
                throw new SAXException(error);
768
            }
769
            String attributeName = attributes.getQName(i);
770
            String attributeValue = attributes.getValue(i);
771
            logMetacat.debug(
772
                    "current node type from xml is ATTRIBUTE ");
773
            logMetacat.debug("node type from stack: "
774
                    + attriNode.getNodeType());
775
            logMetacat.debug("current node name from xml is: "
776
                    + attributeName);
777
            logMetacat.debug("node name from stack: "
778
                    + attriNode.getNodeName());
779
            logMetacat.debug("current node data from xml is: "
780
                    + attributeValue);
781
            logMetacat.debug("node data from stack: "
782
                    + attriNode.getNodeData());
783
            logMetacat.debug("node id  is: " + attriNode.getNodeId());
784

    
785
            if (!attriNode.getNodeType().equals("ATTRIBUTE")
786
                    || !attributeName.equals(attriNode.getNodeName())
787
                    || !attributeValue.equals(attriNode.getNodeData())) {
788
                logMetacat.error("Inconsistence happened: ");
789
                logMetacat.error(
790
                        "current node type from xml is ATTRIBUTE ");
791
                logMetacat.error("node type from stack: "
792
                        + attriNode.getNodeType());
793
                logMetacat.error("current node name from xml is: "
794
                        + attributeName);
795
                logMetacat.error("node name from stack: "
796
                        + attriNode.getNodeName());
797
                logMetacat.error("current node data from xml is: "
798
                        + attributeValue);
799
                logMetacat.error("node data from stack: "
800
                        + attriNode.getNodeData());
801
                logMetacat.error("node is: " + attriNode.getNodeId());
802
                throw new SAXException(error);
803
            }
804
        }//for
805

    
806
    }
807

    
808
    /* method to compare current text node and node in db */
809
    private void compareTextNode(Stack<NodeRecord> nodeStack, StringBuffer text,
810
            String error) throws SAXException
811
    {
812
        NodeRecord node = null;
813
        //get node from current stack
814
        try {
815
            node = nodeStack.pop();
816
        } catch (EmptyStackException ee) {
817
            logMetacat.error(
818
                    "Node stack is empty for text data in startElement");
819
            throw new SAXException(error);
820
        }
821
        logMetacat.debug(
822
                "current node type from xml is TEXT in start element");
823
        logMetacat.debug("node type from stack: " + node.getNodeType());
824
        logMetacat.debug("current node data from xml is: "
825
                + text.toString());
826
        logMetacat.debug("node data from stack: " + node.getNodeData());
827
        logMetacat.debug("node name from stack: " + node.getNodeName());
828
        logMetacat.debug("node is: " + node.getNodeId());
829
        if (!node.getNodeType().equals("TEXT")
830
                || !(text.toString()).equals(node.getNodeData())) {
831
            logMetacat.error("Inconsistence happened: ");
832
            logMetacat.error(
833
                    "current node type from xml is TEXT in start element");
834
            logMetacat.error("node type from stack: "
835
                    + node.getNodeType());
836
            logMetacat.error("current node data from xml is: "
837
                    + text.toString());
838
            logMetacat.error("node data from stack: "
839
                    + node.getNodeData());
840
            logMetacat.error("node name from stack: "
841
                    + node.getNodeName());
842
            logMetacat.error("node is: " + node.getNodeId());
843
            throw new SAXException(error);
844
        }//if
845
    }
846

    
847
    /** SAX Handler that is called for each XML text node */
848
    public void characters(char[] cbuf, int start, int len) throws SAXException
849
    {
850
        logMetacat.debug("CHARACTERS");
851
        if (!handleInlineData) {
852
            // buffer all text nodes for same element. This is for if text was
853
            // split into different nodes
854
            textBuffer.append(new String(cbuf, start, len));
855
            // set hittextnode true
856
            hitTextNode = true;
857
            // if text buffer .size is greater than max, write it to db.
858
            // so we can save memory
859
            if (textBuffer.length() > MAXDATACHARS) {
860
                logMetacat.debug("Write text into DB in charaters"
861
                       + " when text buffer size is greater than maxmum number");
862
                DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
863
                endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
864
                        currentNode);
865
                textBuffer = null;
866
                textBuffer = new StringBuffer();
867
            }
868
        } else {
869
            // this is inline data and write file system directly
870
            // we don't need to buffer it.
871
            StringBuffer inlineText = new StringBuffer();
872
            inlineText.append(new String(cbuf, start, len));
873
            logMetacat.debug(
874
                    "The inline text data write into file system: "
875
                            + inlineText.toString());
876
            writeInlineDataIntoFile(inlineDataFileWriter, inlineText);
877
        }
878
    }
879

    
880
    /** SAX Handler that is called at the end of each XML element */
881
    public void endElement(String uri, String localName, String qName)
882
            throws SAXException
883
    {
884
        logMetacat.debug("End ELEMENT " + qName);
885

    
886
        if (localName.equals(INLINE) && handleInlineData) {
887
            // Get the node from the stack
888
            DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
889
            logMetacat.debug("End of inline data");
890
            // close file writer
891
            try {
892
                inlineDataFileWriter.close();
893
                handleInlineData = false;
894
            } catch (IOException ioe) {
895
                throw new SAXException(ioe.getMessage());
896
            }
897
            // MCD - Removed the following code.  It is used for node level access 
898
        	// control which is not yet implemented in EML 2.1.0
899
            // //check if user changed inline data or not if user doesn't have
900
            // // write permission
901
            // if (startCriticalSubTree) {
902
            //    NodeRecord node = null;
903
            //    try {
904
            //        node = currentUnChangeableSubtreeNodeStack.pop();
905
            //        // get file name from db
906
            //        String fileName = node.getNodeData();
907
            //            logMetacat.info("in handle inline data");
908
            //      logMetacat.info(
909
            //             "the inline data file name from node is: "
910
            //                    + fileName);
911
            //    if (!compareInlineDataFiles(fileName, inlineDataFileName)) {
912
            //       logMetacat.info(
913
            //              "inline data was changed by a user"
914
            //                     + " who doesn't have permission");
915
            //      throw new SAXException(PERMISSIONERROR);
916
            //    }
917
            //   } catch (EmptyStackException ee) {
918
            //    logMetacat.error(
919
            //            "the stack is empty for text data");
920
            //     throw new SAXException(PERMISSIONERROR);
921
            //  } catch (McdbException eee) {
922
            //     throw new SAXException(eee.getMessage());
923
            //  } finally {
924
            //     // delete the inline data file already in file system
925
            //      deleteInlineDataFile(inlineDataFileName);
926
            //   }
927
            //   }//if
928
            
929
            // write put inline data file name into text buffer (without path)
930
            textBuffer = new StringBuffer(inlineDataFileName);
931
            // write file name into db
932
            endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
933
                    currentNode);
934
            // reset textbuff
935
            textBuffer = null;
936
            textBuffer = new StringBuffer();
937
            return;
938
        }
939

    
940
        if (!handleInlineData) {
941
            // Get the node from the stack
942
            DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
943
            String currentTag = currentNode.getTagName();
944

    
945
            // If before the end element, the parser hit text nodes and store
946
            // them into the buffer, write the buffer to data base. The reason
947
            // we put write database here is for xerces some time split text node
948
            if (hitTextNode) {
949
                // get access value
950
                String data = null;
951
                // add principal
952
                if (currentTag.equals(PRINCIPAL) && accessRule != null) {
953
                    data = (textBuffer.toString()).trim();
954
                    accessRule.addPrincipal(data);
955

    
956
                } else if (currentTag.equals(PERMISSION) && accessRule != null) {
957
                    data = (textBuffer.toString()).trim();
958
                    // we combine different a permission into one value
959
                    int permission = accessRule.getPermission();
960
                    // add permission
961
                    if (data.toUpperCase().equals(READSTRING)) {
962
                        permission = permission | READ;
963
                    } else if (data.toUpperCase().equals(WRITESTRING)) {
964
                        permission = permission | WRITE;
965
                    } else if (data.toUpperCase().equals(CHMODSTRING)) {
966
                        permission = permission | CHMOD;
967
                    } else if (data.toUpperCase().equals(ALLSTRING)) {
968
                        permission = permission | ALL;
969
                    }
970
                    accessRule.setPermission(permission);
971
                } else if (currentTag.equals(REFERENCES)
972
                        && (processTopLevelAccess || processAdditionalAccess || processOtherAccess)) {
973
                    // get reference
974
                    data = (textBuffer.toString()).trim();
975
                    // put reference id into accessSection
976
                    accessObject.setReferences(data);
977

    
978
                } else if (currentTag.equals(URL)) {
979
                    //handle online data, make sure its'parent is online
980
                    DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
981
                    if (parentNode != null && parentNode.getTagName() != null
982
                            && parentNode.getTagName().equals(ONLINE)) {
983
                        // if online data is in local metacat, add it to the
984
                        // vector
985
                        data = (textBuffer.toString()).trim();
986
                        handleOnlineUrlDataFile(data);
987
                        /*if (data != null
988
                                && (data.indexOf(MetaCatUtil
989
                                        .getProperty("httpserver")) != -1 || data
990
                                        .indexOf(MetaCatUtil
991
                                                .getProperty("server")) != -1)) {
992
                            // Get docid from url
993
                            String dataId = MetaCatUtil
994
                                    .getDocIdWithRevFromOnlineURL(data);
995
                            // add to vector
996
                            onlineDataFileIdVector.add(dataId);
997
                        }//if*/
998
                    }//if
999
                }//else if
1000
                // write text to db if it is not inline data
1001
                //if (!localName.equals(INLINE))
1002
                {
1003
                    logMetacat.debug(
1004
                            "Write text into DB in End Element");
1005
                    // MCD - Removed the following code.  It is used for node level access 
1006
                	// control which is not yet implemented in EML 2.1.0
1007
                    // //compare text node if need
1008
                    // if (startCriticalSubTree) {
1009
                    //     compareTextNode(currentUnChangeableSubtreeNodeStack,
1010
                    //             textBuffer, PERMISSIONERROR);
1011
                 	// }//if
1012
                    
1013
                    //compare top level access module
1014
                    if (processTopLevelAccess && needToCheckAccessModule) {
1015
                        compareTextNode(
1016
                                currentUnchangeableAccessModuleNodeStack,
1017
                                textBuffer, UPDATEACCESSERROR);
1018
                    }
1019
                    // write text node into db
1020
                    endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer,
1021
                            currentNode);
1022
                }
1023
                if (needToCheckAccessModule
1024
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1025
                    // stored the pull out nodes into storedNode stack
1026
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
1027
                            null, null, MetaCatUtil.normalize(textBuffer
1028
                                    .toString()));
1029
                    storedAccessNodeStack.push(nodeElement);
1030

    
1031
                }
1032
            }//if
1033
            
1034
            // MCD - Removed the following code.  It is used for node level access 
1035
        	// control which is not yet implemented in EML 2.1.0
1036
            // //end critical subtree(user doesn't have permission to write)
1037
            // //When reach the first element and stack is empty
1038
        	// if (localName.equals(firstElementNameForCriticalSubTree)
1039
            //         && currentUnChangeableSubtreeNodeStack.isEmpty()) {
1040
            //     startCriticalSubTree = false;
1041
            // }
1042

    
1043
            //set hitText false
1044
            hitTextNode = false;
1045
            // reset textbuff
1046
            textBuffer = null;
1047
            textBuffer = new StringBuffer();
1048

    
1049
            // hand sub stree stuff
1050
            if (!subTreeInfoStack.empty()) {
1051
                SubTree tree = subTreeInfoStack.peek();// get last
1052
                                                                 // subtree
1053
                if (tree != null && tree.getStartElementName() != null
1054
                        && (tree.getStartElementName()).equals(currentTag)) {
1055
                    // find the end of sub tree and set the end node id
1056
                    tree.setEndNodeId(endNodeId);
1057
                    // add the subtree into the final store palace
1058
                    subTreeList.add(tree);
1059
                    // get rid of it from stack
1060
                    subTreeInfoStack.pop();
1061
                }//if
1062
            }//if
1063

    
1064
            // access stuff
1065
            if (currentTag.equals(ALLOW) || currentTag.equals(DENY)) {
1066
                // finish parser access rule and assign it to new one
1067
                AccessRule newRule = accessRule;
1068
                //add the new rule to access section object
1069
                accessObject.addAccessRule(newRule);
1070
                // reset access rule
1071
                accessRule = null;
1072
            } else if (currentTag.equals(ACCESS)) {
1073
                // finish parsing an access section and assign it to new one
1074
                DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
1075
                if (parentNode != null && parentNode.getTagName() != null
1076
                        && parentNode.getTagName().equals(DISTRIBUTION)) {
1077
                	describesId.add(String.valueOf(distributionIndex));
1078
                }
1079
                accessObject.setEndNodeId(endNodeId);
1080
                AccessSection newAccessObject = accessObject;
1081

    
1082
                if (newAccessObject != null) {
1083

    
1084
                    // add the accessSection into a vector to store it
1085
                    // if it is not a reference, need to store it
1086
                    if (newAccessObject.getReferences() == null) {
1087

    
1088
                        newAccessObject
1089
                                .setStoredTmpNodeStack(storedAccessNodeStack);
1090
                        accessObjectList.add(newAccessObject);
1091
                    }
1092
                    if (processTopLevelAccess) {
1093

    
1094
                        // top level access control will handle whole document
1095
                        // -docid
1096
                        topLevelAccessControlMap.put(docid, newAccessObject);
1097
                        // reset processtopleveraccess tag
1098

    
1099
                    }//if
1100
                    else if (processAdditionalAccess) {
1101
                        // for additional control put everything in describes value 
1102
                    	// and access object into hash
1103
                        for (int i = 0; i < describesId.size(); i++) {
1104

    
1105
                            String subId = describesId.elementAt(i);
1106
                            if (subId != null) {
1107
                                additionalAccessControlMap.put(subId,
1108
                                        newAccessObject);
1109
                            }//if
1110
                        }//for
1111
                        // add this hashtable in to vector
1112

    
1113
                        additionalAccessMapList.add(additionalAccessControlMap);
1114
                        // reset this hashtable in order to store another
1115
                        // additional
1116
                        //accesscontrol
1117
                        additionalAccessControlMap = null;
1118
                        additionalAccessControlMap = new Hashtable<String, AccessSection>();
1119
                    }//if
1120

    
1121
                }//if
1122
                //check if access node stack is empty after parsing top access
1123
                //module
1124

    
1125
                if (needToCheckAccessModule && processTopLevelAccess
1126
                        && !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
1127

    
1128
                    logMetacat.error(
1129
                            "Access node stack is not empty after "
1130
                                    + "parsing access subtree");
1131
                    throw new SAXException(UPDATEACCESSERROR);
1132

    
1133
                }
1134
                //reset access section object
1135

    
1136
                accessObject = null;
1137

    
1138
                // reset tmp stored node stack
1139
                storedAccessNodeStack = null;
1140
                storedAccessNodeStack = new Stack<NodeRecord>();
1141

    
1142
                // reset flag
1143
                processAdditionalAccess = false;
1144
                processTopLevelAccess = false;
1145
                processOtherAccess = false;
1146

    
1147
              } else if (currentTag.equals(DISTRIBUTION)) {
1148
                //reset describesId
1149
                describesId = null;
1150
                describesId = new Vector<String>();
1151
            }
1152
        } else {
1153
            // this is in inline part
1154
            StringBuffer endElement = new StringBuffer();
1155
            endElement.append("</");
1156
            endElement.append(qName);
1157
            endElement.append(">");
1158
            logMetacat.debug("inline endElement: "
1159
                    + endElement.toString());
1160
            writeInlineDataIntoFile(inlineDataFileWriter, endElement);
1161
        }
1162
    }
1163

    
1164
    /**
1165
     * SAX Handler that receives notification of comments in the DTD
1166
     */
1167
    public void comment(char[] ch, int start, int length) throws SAXException
1168
    {
1169
        logMetacat.debug("COMMENT");
1170
        if (!handleInlineData) {
1171
            if (!processingDTD) {
1172
                DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1173
                String str = new String(ch, start, length);
1174

    
1175
                // MCD - Removed the following code.  It is used for node level access 
1176
            	// control which is not yet implemented in EML 2.1.0
1177
                // //compare comment if need
1178
                // if (startCriticalSubTree) {
1179
                //     compareCommentNode(currentUnChangeableSubtreeNodeStack,
1180
                //             str, PERMISSIONERROR);
1181
                // }//if
1182
                
1183
                //compare top level access module
1184
                if (processTopLevelAccess && needToCheckAccessModule) {
1185
                    compareCommentNode(currentUnchangeableAccessModuleNodeStack,
1186
                            str, UPDATEACCESSERROR);
1187
                }
1188
                endNodeId = currentNode.writeChildNodeToDB("COMMENT", null,
1189
                        str, docid);
1190
                if (needToCheckAccessModule
1191
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1192
                    // stored the pull out nodes into storedNode stack
1193
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2,
1194
                            "COMMENT", null, null, MetaCatUtil.normalize(str));
1195
                    storedAccessNodeStack.push(nodeElement);
1196

    
1197
                }
1198
            }
1199
        } else {
1200
            // inline data comment
1201
            StringBuffer inlineComment = new StringBuffer();
1202
            inlineComment.append("<!--");
1203
            inlineComment.append(new String(ch, start, length));
1204
            inlineComment.append("-->");
1205
            logMetacat.debug("inline data comment: "
1206
                    + inlineComment.toString());
1207
            writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1208
        }
1209
    }
1210

    
1211
    /* Compare comment from xml and db */
1212
    private void compareCommentNode(Stack<NodeRecord> nodeStack, String string, String error)
1213
            throws SAXException
1214
    {
1215
        NodeRecord node = null;
1216
        try {
1217
            node = nodeStack.pop();
1218
        } catch (EmptyStackException ee) {
1219
            logMetacat.error("the stack is empty for comment data");
1220
            throw new SAXException(error);
1221
        }
1222
        logMetacat.debug("current node type from xml is COMMENT");
1223
        logMetacat.debug("node type from stack: " + node.getNodeType());
1224
        logMetacat.debug("current node data from xml is: " + string);
1225
        logMetacat.debug("node data from stack: " + node.getNodeData());
1226
        logMetacat.debug("node is from stack: " + node.getNodeId());
1227
        // if not consistent terminate program and throw a exception
1228
        if (!node.getNodeType().equals("COMMENT")
1229
                || !string.equals(node.getNodeData())) {
1230
            logMetacat.error("Inconsistence happened: ");
1231
            logMetacat.error("current node type from xml is COMMENT");
1232
            logMetacat.error("node type from stack: "
1233
                    + node.getNodeType());
1234
            logMetacat.error(
1235
                    "current node data from xml is: " + string);
1236
            logMetacat.error("node data from stack: "
1237
                    + node.getNodeData());
1238
            logMetacat.error("node is from stack: " + node.getNodeId());
1239
            throw new SAXException(error);
1240
        }//if
1241
    }
1242

    
1243
    /**
1244
     * SAX Handler called once for each processing instruction found: node that
1245
     * PI may occur before or after the root element.
1246
     */
1247
    public void processingInstruction(String target, String data)
1248
            throws SAXException
1249
    {
1250
        logMetacat.debug("PI");
1251
        if (!handleInlineData) {
1252
            DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1253
            endNodeId = currentNode.writeChildNodeToDB("PI", target, data,
1254
                    docid);
1255
        } else {
1256
            StringBuffer inlinePI = new StringBuffer();
1257
            inlinePI.append("<?");
1258
            inlinePI.append(target);
1259
            inlinePI.append(" ");
1260
            inlinePI.append(data);
1261
            inlinePI.append("?>");
1262
            logMetacat.debug("inline data pi is: "
1263
                    + inlinePI.toString());
1264
            writeInlineDataIntoFile(inlineDataFileWriter, inlinePI);
1265
        }
1266
    }
1267

    
1268
    /** SAX Handler that is called at the start of Namespace */
1269
    public void startPrefixMapping(String prefix, String uri)
1270
            throws SAXException
1271
    {
1272
        logMetacat.debug("NAMESPACE");
1273
        if (!handleInlineData) {
1274
            namespaces.put(prefix, uri);
1275
        } else {
1276
            inlineDataNameSpace.put(prefix, uri);
1277
        }
1278
    }
1279

    
1280
    /**
1281
     * SAX Handler that is called for each XML text node that is Ignorable
1282
     * white space
1283
     */
1284
    public void ignorableWhitespace(char[] cbuf, int start, int len)
1285
            throws SAXException
1286
    {
1287
        // When validation is turned "on", white spaces are reported here
1288
        // When validation is turned "off" white spaces are not reported here,
1289
        // but through characters() callback
1290
        logMetacat.debug("IGNORABLEWHITESPACE");
1291
        if (!handleInlineData) {
1292
            DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1293
            String data = null;
1294
            int leftover = len;
1295
            int offset = start;
1296
            boolean moredata = true;
1297

    
1298
            // This loop deals with the case where there are more characters
1299
            // than can fit in a single database text field (limit is
1300
            // MAXDATACHARS). If the text to be inserted exceeds MAXDATACHARS,
1301
            // write a series of nodes that are MAXDATACHARS long, and then the
1302
            // final node contains the remainder
1303
            while (moredata) {
1304
                if (leftover > MAXDATACHARS) {
1305
                    data = new String(cbuf, offset, MAXDATACHARS);
1306
                    leftover -= MAXDATACHARS;
1307
                    offset += MAXDATACHARS;
1308
                } else {
1309
                    data = new String(cbuf, offset, leftover);
1310
                    moredata = false;
1311
                }
1312

    
1313
                // MCD - Removed the following code.  It is used for node level access 
1314
            	// control which is not yet implemented in EML 2.1.0
1315
                // //compare whitespace if need
1316
	            // if (startCriticalSubTree) {
1317
	            //     compareWhiteSpace(currentUnChangeableSubtreeNodeStack,
1318
	            //             data, PERMISSIONERROR);
1319
	            // }//if
1320

    
1321
                //compare whitespace in access top module
1322
                if (processTopLevelAccess && needToCheckAccessModule) {
1323
                    compareWhiteSpace(currentUnchangeableAccessModuleNodeStack,
1324
                            data, UPDATEACCESSERROR);
1325
                }
1326
                // Write the content of the node to the database
1327
                if (needToCheckAccessModule
1328
                        && (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1329
                    // stored the pull out nodes into storedNode stack
1330
                    NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT",
1331
                            null, null, MetaCatUtil.normalize(data));
1332
                    storedAccessNodeStack.push(nodeElement);
1333

    
1334
                }
1335
                endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data,
1336
                        docid);
1337
            }
1338
        } else {
1339
            //This is inline data write to file directly
1340
            StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf,
1341
                    start, len));
1342
            writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1343
        }
1344

    
1345
    }
1346

    
1347
    /* Compare whitespace from xml and db */
1348
    private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string, String error)
1349
            throws SAXException
1350
    {
1351
        NodeRecord node = null;
1352
        try {
1353
            node = nodeStack.pop();
1354
        } catch (EmptyStackException ee) {
1355
            logMetacat.error("the stack is empty for whitespace data");
1356
            throw new SAXException(error);
1357
        }
1358
        if (!node.getNodeType().equals("TEXT")
1359
                || !string.equals(node.getNodeData())) {
1360
            logMetacat.error("Inconsistence happened: ");
1361
            logMetacat.error(
1362
                    "current node type from xml is WHITESPACE TEXT");
1363
            logMetacat.error("node type from stack: "
1364
                    + node.getNodeType());
1365
            logMetacat.error(
1366
                    "current node data from xml is: " + string);
1367
            logMetacat.error("node data from stack: "
1368
                    + node.getNodeData());
1369
            logMetacat.error("node is from stack: " + node.getNodeId());
1370
            throw new SAXException(error);
1371
        }//if
1372
    }
1373

    
1374
    /** SAX Handler that receives notification of end of the document */
1375
    public void endDocument() throws SAXException
1376
    {
1377
        logMetacat.debug("end Document");
1378
        // There are some unchangable subtree didn't be compare
1379
        // This maybe cause user change the subtree id
1380
        // MCD - Removed the following code.  It is used for node level access 
1381
    	// control which is not yet implemented in EML 2.1.0
1382
        // if (!unChangeableSubTreeHash.isEmpty()) {
1383
        // 		logMetacat.info("The unChangealbe subtree is not empty");
1384
        // 		throw new SAXException(PERMISSIONERROR);
1385
        // }
1386
        if (!super.getIsRevisionDoc())
1387
        {
1388
            // write access rule to db
1389
            writeAccessRuleToDB();
1390
            //delete relation table
1391
            deleteRelations();
1392
            //write relations
1393
            for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1394
                String id = onlineDataFileIdInRelationVector.elementAt(i);
1395
                writeOnlineDataFileIdIntoRelationTable(id);
1396
            }
1397
        }
1398
    }
1399

    
1400
    /* The method to write all access rule into db */
1401
    private void writeAccessRuleToDB() throws SAXException
1402
    {
1403
        //Delete old permssion
1404
        deletePermissionsInAccessTable(docid);
1405
        //write top leve access rule
1406
        writeTopLevelAccessRuleToDB();
1407
        //write additional access rule
1408
        writeAddtionalAccessRuleToDB();
1409
    }//writeAccessRuleToDB
1410

    
1411
    /* The method to write top level access rule into db. */
1412
    private void writeTopLevelAccessRuleToDB() throws SAXException
1413
    {
1414
        // for top document level
1415
    	AccessSection accessSection = topLevelAccessControlMap.get(docid);
1416
        boolean top = true;
1417
        String subSectionId = null;
1418
        if (accessSection != null) {
1419
            AccessSection accessSectionObj = accessSection;
1420

    
1421
            // if accessSection is not null and is not reference
1422
            if (accessSectionObj.getReferences() == null) {
1423
                // write the top level access module into xml_accesssubtree to
1424
                // store info and then when update to check if the user can 
1425
            	// update it or not
1426
                deleteAccessSubTreeRecord(docid);
1427
                writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1428

    
1429
                //write access section into xml_access table
1430
                writeGivenAccessRuleIntoDB(accessSectionObj, top, subSectionId);
1431
                // write online data file into xml_access too.
1432
                for (int i= 0; i <onlineDataFileIdInTopAccessVector.size(); i++)
1433
                {
1434
                  String id = onlineDataFileIdInTopAccessVector.elementAt(i);
1435
                  writeAccessRuleForRalatedDataFileIntoDB(accessSectionObj, id);
1436
                }
1437

    
1438

    
1439
            } else {
1440

    
1441
                // this is a reference and go trough the vector which contains
1442
                // all access object
1443
                String referenceId = accessSectionObj.getReferences();
1444
                boolean findAccessObject = false;
1445
                logMetacat.debug("referered id for top access: "
1446
                        + referenceId);
1447
                for (int i = 0; i < accessObjectList.size(); i++) {
1448
                    AccessSection accessObj = accessObjectList.elementAt(i);
1449
                    String accessObjId = accessObj.getSubTreeId();
1450
                    if (referenceId != null && accessObj != null
1451
                            && referenceId.equals(accessObjId)) {
1452
                        // make sure the user didn't change any thing in this
1453
                        // access moduel
1454
                        // too if user doesn't have all permission
1455
                        if (needToCheckAccessModule) {
1456

    
1457
                            Stack<NodeRecord> newStack = accessObj.getStoredTmpNodeStack();
1458
                            //revise order
1459
                            newStack = MetaCatUtil.reviseStack(newStack);
1460
                            // go throught the vector of unChangeableAccessSubtreevector
1461
                            // and find the one whose id is as same as referenceid
1462
                            AccessSection oldAccessObj = getAccessSectionFromUnchangableAccessVector(referenceId);
1463
                            //if oldAccessObj is null something is wrong
1464
                            if (oldAccessObj == null) {
1465
                                throw new SAXException(UPDATEACCESSERROR);
1466
                            }//if
1467
                            else {
1468
                                // Get the node stack from old access obj
1469
                                Stack<NodeRecord> oldStack = oldAccessObj
1470
                                        .getSubTreeNodeStack();
1471
                                compareNodeStacks(newStack, oldStack);
1472
                            }//else
1473
                        }//if
1474
                        // write accessobject into db
1475
                        writeGivenAccessRuleIntoDB(accessObj, top, subSectionId);
1476
                        // write online data file into xml_access too.
1477
                        for (int j= 0; j <onlineDataFileIdInTopAccessVector.size(); j++)
1478
                        {
1479
                          String id =
1480
                              onlineDataFileIdInTopAccessVector.elementAt(j);
1481
                           writeAccessRuleForRalatedDataFileIntoDB(accessSectionObj, id);
1482
                        }
1483

    
1484
                        //write the reference access into xml_accesssubtree
1485
                        // too write the top level access module into
1486
                        // xml_accesssubtree to store info and then when update 
1487
                        // to check if the user can update it or not
1488
                        deleteAccessSubTreeRecord(docid);
1489
                        writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1490
                        writeAccessSubTreeIntoDB(accessObj, SUBTREELEVEL);
1491
                        findAccessObject = true;
1492
                        break;
1493
                    }
1494
                }//for
1495
                // if we couldn't find an access subtree id for this reference
1496
                // id
1497
                if (!findAccessObject) { throw new SAXException(
1498
                        "The referenceid: " + referenceId
1499
                                + " is not access subtree"); }//if
1500
            }//else
1501

    
1502
        }//if
1503
        else {
1504
            // couldn't find a access section object
1505
            logMetacat.warn(
1506
                    "couldn't find access control for document: " + docid);
1507
        }
1508

    
1509
    }//writeTopLevelAccessRuletoDB
1510

    
1511
    /* Given a subtree id and find the responding access section */
1512
    private AccessSection getAccessSectionFromUnchangableAccessVector(String id)
1513
    {
1514
        AccessSection result = null;
1515
        // Makse sure the id
1516
        if (id == null || id.equals("")) { return result; }
1517
        // go throught vector and find the list
1518
        for (int i = 0; i < unChangeableAccessSubTreeVector.size(); i++) {
1519
            AccessSection accessObj = unChangeableAccessSubTreeVector
1520
                    .elementAt(i);
1521
            if (accessObj.getSubTreeId() != null
1522
                    && (accessObj.getSubTreeId()).equals(id)) {
1523
                result = accessObj;
1524
            }//if
1525
        }//for
1526
        return result;
1527
    }//getAccessSectionFromUnchangableAccessVector
1528

    
1529
    /* Compare two node stacks to see if they are same */
1530
    private void compareNodeStacks(Stack<NodeRecord> stack1, Stack<NodeRecord> stack2)
1531
            throws SAXException
1532
    {
1533
        // make sure stack1 and stack2 are not empty
1534
        if (stack1.isEmpty() || stack2.isEmpty()) {
1535
            logMetacat.error("Because stack is empty!");
1536
            throw new SAXException(UPDATEACCESSERROR);
1537
        }
1538
        // go throw two stacks and compare every element
1539
        while (!stack1.isEmpty()) {
1540
            // Pop an element from stack1
1541
            NodeRecord record1 = stack1.pop();
1542
            // Pop an element from stack2(stack 2 maybe empty)
1543
            NodeRecord record2 = null;
1544
            try {
1545
                record2 = stack2.pop();
1546
            } catch (EmptyStackException ee) {
1547
                logMetacat.error(
1548
                        "Node stack2 is empty but stack1 isn't!");
1549
                throw new SAXException(UPDATEACCESSERROR);
1550
            }
1551
            // if two records are not same throw a exception
1552
            if (!record1.contentEquals(record2)) {
1553
                logMetacat.error("Two records from new and old stack are not "
1554
                                        + "same!");
1555
                throw new SAXException(UPDATEACCESSERROR);
1556
            }//if
1557
        }//while
1558

    
1559
        // now stack1 is empty and we should make sure stack2 is empty too
1560
        if (!stack2.isEmpty()) {
1561
            logMetacat.error(
1562
                    "stack2 still has some elements while stack "
1563
                            + "is empty! ");
1564
            throw new SAXException(UPDATEACCESSERROR);
1565
        }//if
1566
    }//comparingNodeStacks
1567

    
1568
    /* The method to write addtional access rule into db. */
1569
    private void writeAddtionalAccessRuleToDB() throws SAXException
1570
    {
1571

    
1572
        boolean topLevel = false;
1573
        // go through the vector which contains the additional access control
1574
        // hashtable. Each hashtable has info for one additonalmetadata
1575
        // container
1576
        for (Hashtable<String, AccessSection> accessControlMap : additionalAccessMapList) {
1577
            // additional access rule
1578
            Enumeration<String> en = accessControlMap.keys();
1579

    
1580
            while (en.hasMoreElements()) {
1581
                try {
1582
                    // Get subsection id
1583
                    String subSectionId = en.nextElement();
1584
                    logMetacat.debug("sub section id in additional access mapping"
1585
                                    + "(go through): " + subSectionId);
1586

    
1587
                    if (subSectionId == null) {
1588
                    // if id is null, terminate the program
1589
                    throw new SAXException("subtree id is null"); }
1590
                    // Get AccessSection Object
1591
                    AccessSection accessSectionObj = 
1592
                    	accessControlMap.get(subSectionId);
1593
                    if (accessSectionObj == null) {
1594
                        // if accesssection is null, terminate the program
1595
                        throw new SAXException("access subtree is null");
1596
                    } else {
1597
                        AccessSection accessControlObj = accessSectionObj;
1598
                        // if the access section is not references, write it to
1599
                        // db
1600
                        if (accessControlObj.getReferences() == null) {
1601
                            writeGivenAccessRuleIntoDB(accessControlObj,
1602
                                    topLevel, subSectionId);
1603
                        } else {
1604
                            // this is a reference and go trough the vector
1605
                            // which contains all
1606
                            // access object
1607
                            String referenceId = accessControlObj
1608
                                    .getReferences();
1609

    
1610
                            boolean findAccessObject = false;
1611
                            logMetacat.debug("referered id for additional access "
1612
                            		+ "mapping(go through): " + referenceId);
1613
                            for (AccessSection accessObj : accessObjectList) {
1614
                                String accessObjId = accessObj.getSubTreeId();
1615
                                logMetacat.debug("access obj id in the list(go through): "
1616
                                                + accessObjId);
1617
                                if (referenceId != null && accessObj != null
1618
                                        && referenceId.equals(accessObjId)) {
1619
                                    writeGivenAccessRuleIntoDB(accessObj,
1620
                                            topLevel, subSectionId);
1621
                                    findAccessObject = true;
1622
                                }//if
1623
                            }//for
1624
                            // if we couldn't find an access subtree id for
1625
                            // this reference id
1626
                            if (!findAccessObject) { 
1627
                            	throw new SAXException("The referenceid: " + referenceId
1628
                                            + " is not access subtree"); 
1629
                            }//if
1630
                        }//else
1631
                    }//else
1632
                }//try
1633
                catch (Exception e) {
1634

    
1635
                    logMetacat.error(
1636
                            "error in EmlSAXHandler.writeAddtionalAccess"
1637
                                    + ": " + e.getMessage());
1638
                    throw new SAXException(e.getMessage());
1639
                }
1640
            }//while
1641
        }//for
1642
    }//writeAccessRuletoDB
1643

    
1644
    /* Write a gaven access rule into db */
1645
    private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1646
            boolean topLevel, String subSectionId) throws SAXException
1647
    {
1648
        if (accessSection == null) { throw new SAXException(
1649
                "The access object is null"); }
1650

    
1651
        String permOrder = accessSection.getPermissionOrder();
1652
        String sql = null;
1653
        PreparedStatement pstmt = null;
1654
        if (topLevel) {
1655
            sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1656
                    + "perm_type, perm_order, accessfileid) VALUES "
1657
                    + " (?, ?, ?, ?, ?, ?)";
1658
        } else {
1659
            sql = "INSERT INTO xml_access (docid,principal_name, "
1660
                    + "permission, perm_type, perm_order, accessfileid, subtreeid"
1661
                    + ") VALUES"
1662
                    + " (?, ?, ?, ?, ?, ?, ?)";
1663
        }
1664
        try {
1665

    
1666
            pstmt = connection.prepareStatement(sql);
1667
            // Increase DBConnection usage count
1668
            connection.increaseUsageCount(1);
1669
            // Bind the values to the query
1670
            pstmt.setString(6, docid);
1671
            logMetacat.debug("Accessfileid in accesstable: " + docid);
1672
            pstmt.setString(5, permOrder);
1673
            logMetacat.debug("PermOder in accesstable: " + permOrder);
1674
            // if it is not top level, set subsection id
1675
            if (topLevel) {
1676
            	pstmt.setString(1, docid);
1677
            	logMetacat.debug("Docid in accesstable: " + docid);
1678
            }
1679
            if (!topLevel) {
1680
            	pstmt.setString(1, accessSection.getInlineDataFileName());
1681
            	logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1682

    
1683
                // for subtree should specify the
1684
                if (subSectionId == null) { throw new SAXException(
1685
                        "The subsection is null"); }
1686
                	 // MCD - Removed the following code.  It is used for node level access 
1687
            		 // control which is not yet implemented in EML 2.1.0
1688
                	 // // go through the substree list vector and found the start node
1689
		             // // id and stop node id for this subtree id
1690
		             // for (int i = 0; i < subTreeList.size(); i++) {
1691
		             //     SubTree tree = subTreeList.elementAt(i);
1692
		             //     String subTreeId = tree.getSubTreeId();
1693
		             //     if (subSectionId.equals(subTreeId)) {
1694
		             //         startNodeId = tree.getStartNodeId();
1695
		             //         endNodeId = tree.getEndNodeId();
1696
		             //     }//if
1697
		             // }//for
1698
		             // if (startNodeId == 0 || endNodeId == 0) { 
1699
		             // 	throw new SAXException("Could not find the subtree for this id: " 
1700
		             // 			+ subSectionId); 
1701
		             // }
1702
                
1703
                	pstmt.setString(7, subSectionId);
1704
					logMetacat.debug("SubSectionId in accesstable: "
1705
                        + subSectionId);
1706
            }
1707

    
1708
            Vector<AccessRule> accessRules = accessSection.getAccessRules();
1709
            // go through every rule
1710
            for (int i = 0; i < accessRules.size(); i++) {
1711
                AccessRule rule = accessRules.elementAt(i);
1712
                String permType = rule.getPermissionType();
1713
                int permission = rule.getPermission();
1714
                pstmt.setInt(3, permission);
1715
                logMetacat.debug("permission in accesstable: "
1716
                        + permission);
1717
                pstmt.setString(4, permType);
1718
                logMetacat.debug(
1719
                        "Permtype in accesstable: " + permType);
1720
                // go through every principle in rule
1721
                Vector<String> nameVector = rule.getPrincipal();
1722
                for (int j = 0; j < nameVector.size(); j++) {
1723
                    String prName = nameVector.elementAt(j);
1724
                    pstmt.setString(2, prName);
1725
                    logMetacat.debug("Principal in accesstable: "
1726
                            + prName);
1727
                    logMetacat.debug("running sql: " + pstmt.toString());
1728
                    pstmt.execute();
1729
                }//for
1730
            }//for
1731
            pstmt.close();
1732
        }//try
1733
        catch (SQLException e) {
1734
            throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1735
                    + e.getMessage());
1736
        }//catch
1737
        finally {
1738
            try {
1739
                pstmt.close();
1740
            } catch (SQLException ee) {
1741
                throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1742
                        + ee.getMessage());
1743
            }
1744
        }//finally
1745

    
1746
    }//writeGivenAccessRuleIntoDB
1747

    
1748
    /* Write a gaven access rule into db */
1749
    private void writeAccessRuleForRalatedDataFileIntoDB(
1750
            AccessSection accessSection, String dataId) throws SAXException
1751
    {
1752
        if (accessSection == null) { throw new SAXException(
1753
                "The access object is null"); }
1754
        // get rid of rev from dataId
1755
        //dataId = MetaCatUtil.getDocIdFromString(dataId);
1756
        String permOrder = accessSection.getPermissionOrder();
1757
        String sql = null;
1758
        PreparedStatement pstmt = null;
1759
        sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1760
                + "perm_type, perm_order, accessfileid) VALUES "
1761
                + " (?, ?, ?, ?, ?, ?)";
1762

    
1763
        try {
1764

    
1765
            pstmt = connection.prepareStatement(sql);
1766
            // Increase DBConnection usage count
1767
            connection.increaseUsageCount(1);
1768
            // Bind the values to the query
1769
            pstmt.setString(1, dataId);
1770
            logMetacat.debug("Docid in accesstable: " + docid);
1771
            pstmt.setString(6, docid);
1772
            logMetacat.debug("Accessfileid in accesstable: " + docid);
1773
            pstmt.setString(5, permOrder);
1774
            logMetacat.debug("PermOder in accesstable: " + permOrder);
1775
            // if it is not top level, set subsection id
1776

    
1777
            Vector<AccessRule> accessRules = accessSection.getAccessRules();
1778
            // go through every rule
1779
            for (int i = 0; i < accessRules.size(); i++) {
1780
                AccessRule rule = accessRules.elementAt(i);
1781
                String permType = rule.getPermissionType();
1782
                int permission = rule.getPermission();
1783
                pstmt.setInt(3, permission);
1784
                logMetacat.debug("permission in accesstable: "
1785
                        + permission);
1786
                pstmt.setString(4, permType);
1787
                logMetacat.debug(
1788
                        "Permtype in accesstable: " + permType);
1789
                // go through every principle in rule
1790
                Vector<String> nameVector = rule.getPrincipal();
1791
                for (int j = 0; j < nameVector.size(); j++) {
1792
                    String prName = nameVector.elementAt(j);
1793
                    pstmt.setString(2, prName);
1794
                    logMetacat.debug("Principal in accesstable: "
1795
                            + prName);
1796
                    logMetacat.debug("running sql: " + pstmt.toString());
1797
                    pstmt.execute();
1798
                }//for
1799
            }//for
1800
            pstmt.close();
1801
        }//try
1802
        catch (SQLException e) {
1803
            throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1804
                    + e.getMessage());
1805
        }//catch
1806
        finally {
1807
            try {
1808
                pstmt.close();
1809
            } catch (SQLException ee) {
1810
                throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1811
                        + ee.getMessage());
1812
            }
1813
        }//finally
1814

    
1815
    }//writeAccessRuleForRalatedDataFileIntoDB
1816

    
1817
    /* Delete from db all permission for resources related to @aclid if any. */
1818
    private void deletePermissionsInAccessTable(String aclid)
1819
            throws SAXException
1820
    {
1821
        Statement stmt = null;
1822
        try {
1823
            // delete all acl records for resources related to @aclid if any
1824
            stmt = connection.createStatement();
1825
            // Increase DBConnection usage count
1826
            connection.increaseUsageCount(1);
1827
            logMetacat.debug("running sql: DELETE FROM xml_access WHERE accessfileid = '"
1828
                    + aclid + "'");
1829
            stmt.execute("DELETE FROM xml_access WHERE accessfileid = '"
1830
                    + aclid + "'");
1831

    
1832
        } catch (SQLException e) {
1833
            throw new SAXException(e.getMessage());
1834
        } finally {
1835
            try {
1836
                stmt.close();
1837
            } catch (SQLException ee) {
1838
                throw new SAXException(ee.getMessage());
1839
            }
1840
        }
1841
    }//deletePermissionsInAccessTable
1842

    
1843
    /*
1844
     * In order to make sure only usr has "all" permission can update access
1845
     * subtree in eml document we need to keep access subtree info in
1846
     * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1847
     */
1848
    private void writeAccessSubTreeIntoDB(AccessSection accessSection,
1849
            String level) throws SAXException
1850
    {
1851
        if (accessSection == null) { throw new SAXException(
1852
                "The access object is null"); }
1853

    
1854
        String sql = null;
1855
        PreparedStatement pstmt = null;
1856
        sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1857
                + "subtreeid, startnodeid, endnodeid) VALUES "
1858
                + " (?, ?, ?, ?, ?, ?)";
1859
        try {
1860

    
1861
            pstmt = connection.prepareStatement(sql);
1862
            // Increase DBConnection usage count
1863
            connection.increaseUsageCount(1);
1864
            long startNodeId = accessSection.getStartNodeId();
1865
            long endNodeId = accessSection.getEndNodeId();
1866
            String sectionId = accessSection.getSubTreeId();
1867
            // Bind the values to the query
1868
            pstmt.setString(1, docid);
1869
            logMetacat.debug("Docid in access-subtreetable: " + docid);
1870
            pstmt.setLong(2, (new Long(revision)).longValue());
1871
            logMetacat.debug("rev in accesssubtreetable: " + revision);
1872
            pstmt.setString(3, level);
1873
            logMetacat.debug("contorl level in access-subtree table: "
1874
                    + level);
1875
            pstmt.setString(4, sectionId);
1876
            logMetacat.debug("Subtree id in access-subtree table: "
1877
                    + sectionId);
1878
            pstmt.setLong(5, startNodeId);
1879
            logMetacat.debug("Start node id is: " + startNodeId);
1880
            pstmt.setLong(6, endNodeId);
1881
            logMetacat.debug("End node id is: " + endNodeId);
1882
            logMetacat.debug("running sql: " + pstmt.toString());
1883
            pstmt.execute();
1884
            pstmt.close();
1885
        }//try
1886
        catch (SQLException e) {
1887
            throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1888
                    + e.getMessage());
1889
        }//catch
1890
        finally {
1891
            try {
1892
                pstmt.close();
1893
            } catch (SQLException ee) {
1894
                throw new SAXException(
1895
                        "EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1896
                                + ee.getMessage());
1897
            }
1898
        }//finally
1899

    
1900
    }//writeAccessSubtreeIntoDB
1901

    
1902
    /* Delete every access subtree record from xml_accesssubtree. */
1903
    private void deleteAccessSubTreeRecord(String docId) throws SAXException
1904
    {
1905
        Statement stmt = null;
1906
        try {
1907
            // delete all acl records for resources related to @aclid if any
1908
            stmt = connection.createStatement();
1909
            // Increase DBConnection usage count
1910
            connection.increaseUsageCount(1);
1911
            logMetacat.debug("running sql: DELETE FROM xml_accesssubtree WHERE docid = '"
1912
                    + docId + "'");
1913
            stmt.execute("DELETE FROM xml_accesssubtree WHERE docid = '"
1914
                    + docId + "'");
1915

    
1916
        } catch (SQLException e) {
1917
            throw new SAXException(e.getMessage());
1918
        } finally {
1919
            try {
1920
                stmt.close();
1921
            } catch (SQLException ee) {
1922
                throw new SAXException(ee.getMessage());
1923
            }
1924
        }
1925
    }//deleteAccessSubTreeRecord
1926

    
1927
    // open a file writer for writing inline data to file
1928
    private FileWriter createInlineDataFileWriter(String fileName)
1929
            throws SAXException
1930
    {
1931
        FileWriter writer = null;
1932
        String path;
1933
        try {
1934
        	path = PropertyService.getProperty("application.inlinedatafilepath");
1935
        } catch (PropertyNotFoundException pnfe) {
1936
        	throw new SAXException(pnfe.getMessage());
1937
        }
1938
        /*
1939
         * File inlineDataDirectory = new File(path);
1940
         */
1941
        String newFile = path + "/" + fileName;
1942
        logMetacat.debug("inline file name: " + newFile);
1943
        try {
1944
            // true means append
1945
            writer = new FileWriter(newFile, true);
1946
        } catch (IOException ioe) {
1947
            throw new SAXException(ioe.getMessage());
1948
        }
1949
        return writer;
1950
    }
1951

    
1952
    // write inline data into file system and return file name(without path)
1953
    private void writeInlineDataIntoFile(FileWriter writer, StringBuffer data)
1954
            throws SAXException
1955
    {
1956
        try {
1957
            writer.write(data.toString());
1958
            writer.flush();
1959
        } catch (Exception e) {
1960
            throw new SAXException(e.getMessage());
1961
        }
1962
    }
1963

    
1964
    /*
1965
     * In eml2, the inline data wouldn't store in db, it store in file system
1966
     * The db stores file name(without path). We got the old file name from db
1967
     * and compare to the new in line data file
1968
     */
1969
    public boolean compareInlineDataFiles(String oldFileName, String newFileName)
1970
            throws McdbException
1971
    {
1972
        // this method need to be testing
1973
        boolean same = true;
1974
        String data = null;
1975
        try {
1976
        	String path = PropertyService.getProperty("application.inlinedatafilepath");
1977
			// the new file name will look like path/docid.rev.2
1978
			File inlineDataDirectory = new File(path);
1979
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
1980
			File newDataFile = new File(inlineDataDirectory, newFileName);
1981

    
1982
            FileReader oldFileReader = new FileReader(oldDataFile);
1983
            BufferedReader oldStringReader = new BufferedReader(oldFileReader);
1984
            FileReader newFileReader = new FileReader(newDataFile);
1985
            BufferedReader newStringReader = new BufferedReader(newFileReader);
1986
            // read first line of data
1987
            String oldString = oldStringReader.readLine();
1988
            String newString = newStringReader.readLine();
1989

    
1990
            // at the end oldstring will be null
1991
            while (oldString != null) {
1992
                oldString = oldStringReader.readLine();
1993
                newString = newStringReader.readLine();
1994
                if (!oldString.equals(newString)) {
1995
                    same = false;
1996
                    break;
1997
                }
1998
            }
1999

    
2000
            // if oldString is null but newString is not null, they are same
2001
            if (same) {
2002
                if (newString != null) {
2003
                    same = false;
2004
                }
2005
            }
2006

    
2007
        } catch (Exception e) {
2008
            throw new McdbException(e.getMessage());
2009
        }
2010
        logMetacat.debug("the inline data retrieved from file: " + data);
2011
        return same;
2012
    }
2013

    
2014
    // if xml file failed to upload, we need to call this method to delete
2015
    // the inline data already in file system
2016
    public void deleteInlineFiles() throws SAXException
2017
    {
2018
        if (!inlineFileIdList.isEmpty()) {
2019
            for (int i = 0; i < inlineFileIdList.size(); i++) {
2020
                String fileName = inlineFileIdList.elementAt(i);
2021
                deleteInlineDataFile(fileName);
2022
            }
2023
        }
2024
    }
2025

    
2026
    /* delete the inline data file */
2027
    private void deleteInlineDataFile(String fileName) throws SAXException
2028
    {
2029
    	String path;
2030
    	try {
2031
        	path = PropertyService.getProperty("application.inlinedatafilepath");
2032
    	} catch (PropertyNotFoundException pnfe) {
2033
    		throw new SAXException ("Could not find inline data file path: " 
2034
    				+ pnfe.getMessage());
2035
    	}
2036
        File inlineDataDirectory = new File(path);
2037
        File newFile = new File(inlineDataDirectory, fileName);
2038
        newFile.delete();
2039

    
2040
    }
2041

    
2042
    /*
2043
	 * In eml2, the inline data wouldn't store in db, it store in file system
2044
	 * The db stores file name(without path).
2045
	 */
2046
	public static Reader readInlineDataFromFileSystem(String fileName)
2047
			throws McdbException {
2048
		// BufferedReader stringReader = null;
2049
		FileReader fileReader = null;
2050
		try {
2051
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2052
			// the new file name will look like path/docid.rev.2
2053
			File inlineDataDirectory = new File(path);
2054
			File dataFile = new File(inlineDataDirectory, fileName);
2055

    
2056
			fileReader = new FileReader(dataFile);
2057
			// stringReader = new BufferedReader(fileReader);
2058
		} catch (Exception e) {
2059
			throw new McdbException(e.getMessage());
2060
		}
2061
		// return stringReader;
2062
		return fileReader;
2063
	}
2064

    
2065
    /* Delete relations */
2066
    private void deleteRelations() throws SAXException
2067
    {
2068
        PreparedStatement pStmt = null;
2069
        String sql = "DELETE FROM xml_relation where docid =?";
2070
        try {
2071
            pStmt = connection.prepareStatement(sql);
2072
            //bind variable
2073
            pStmt.setString(1, docid);
2074
            //execute query
2075
            pStmt.execute();
2076
            pStmt.close();
2077
        }//try
2078
        catch (SQLException e) {
2079
            throw new SAXException("EMLSAXHandler.deleteRelations(): "
2080
                    + e.getMessage());
2081
        }//catch
2082
        finally {
2083
            try {
2084
                pStmt.close();
2085
            }//try
2086
            catch (SQLException ee) {
2087
                throw new SAXException("EMLSAXHandler.deleteRelations: "
2088
                        + ee.getMessage());
2089
            }//catch
2090
        }//finally
2091
    }
2092

    
2093
    /* Write an online data file id into xml_relation table. The dataId shouldnot
2094
     * have the revision
2095
     */
2096
    private void writeOnlineDataFileIdIntoRelationTable(String dataId)
2097
            throws SAXException
2098
    {
2099
        PreparedStatement pStmt = null;
2100
        String sql = "INSERT into xml_relation (docid, packagetype, subject, "
2101
                + "relationship, object) values (?, ?, ?, ?, ?)";
2102
        try {
2103
            pStmt = connection.prepareStatement(sql);
2104
            //bind variable
2105
            pStmt.setString(1, docid);
2106
            pStmt.setString(2, DocumentImpl.EML2_1_0NAMESPACE);
2107
            pStmt.setString(3, docid);
2108
            pStmt.setString(4, RELATION);
2109
            pStmt.setString(5, dataId);
2110
            //execute query
2111
            pStmt.execute();
2112
            pStmt.close();
2113
        }//try
2114
        catch (SQLException e) {
2115
            throw new SAXException(
2116
                    "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2117
                            + e.getMessage());
2118
        }//catch
2119
        finally {
2120
            try {
2121
                pStmt.close();
2122
            }//try
2123
            catch (SQLException ee) {
2124
                throw new SAXException(
2125
                        "EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2126
                                + ee.getMessage());
2127
            }//catch
2128
        }//finally
2129

    
2130
    }//writeOnlineDataFileIdIntoRelationTable
2131

    
2132
    /*
2133
     * This method will handle data file in online url. If the data file is in
2134
     * ecogrid protocol, then the datafile identifier(without rev)should be put
2135
     * into onlineDataFileRelationVector. The  docid in this vector will be
2136
     * insert into xml_relation table in endDocument().
2137
     * If the data file doesn't exsit in xml_documents or
2138
     * xml_revision table, or the user has all permission to the data file if
2139
     * the docid already existed, the data file id (without rev)will be put into
2140
     * onlineDataFileTopAccessVector. The top access rules specified in this eml
2141
     * document will apply to the data file.
2142
     * NEED to do:
2143
     * We should also need to implement http and ftp. Those
2144
     * external files should be download and assign a data file id to it.
2145
     */
2146
    private void handleOnlineUrlDataFile(String url) throws SAXException
2147
    {
2148
      logMetacat.warn("The url is "+ url);
2149
      // if the url is not a ecogrid protocol, null will be getten
2150
      String accessionNumber =
2151
                 MetaCatUtil.getAccessionNumberFromEcogridIdentifier(url);
2152
      if (accessionNumber != null)
2153
      {
2154
        // handle ecogrid protocol
2155
        // get rid of revision number to get the docid.
2156
        String docid = MetaCatUtil.getDocIdFromAccessionNumber(accessionNumber);
2157
        onlineDataFileIdInRelationVector.add(docid);
2158
        try
2159
        {
2160

    
2161
          if (!AccessionNumber.accNumberUsed(docid))
2162
          {
2163
            onlineDataFileIdInTopAccessVector.add(docid);
2164
          }
2165
          PermissionController controller = new
2166
              PermissionController(accessionNumber);
2167
          if (controller.hasPermission(
2168
              user, groups, AccessControlInterface.ALLSTRING))
2169
          {
2170
            onlineDataFileIdInTopAccessVector.add(docid);
2171
          }
2172
        }//try
2173
        catch(Exception e)
2174
        {
2175
          logMetacat.error("Eorr in " +
2176
                                "Eml210SAXHanlder.handleOnlineUrlDataFile is " +
2177
                                 e.getMessage());
2178
          throw new SAXException(e.getMessage());
2179
        }
2180
      }
2181
    }
2182
}
(37-37/68)