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: 2009-08-24 14:34:17 -0700 (Mon, 24 Aug 2009) $'
11
 * '$Revision: 5030 $'
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.database.DBConnection;
51
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
52
import edu.ucsb.nceas.metacat.properties.PropertyService;
53
import edu.ucsb.nceas.metacat.util.DocumentUtil;
54
import edu.ucsb.nceas.metacat.util.MetacatUtil;
55
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
56

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

    
63
	private boolean processTopLevelAccess = false;
64

    
65
	private boolean processAdditionalAccess = false;
66

    
67
	private boolean processOtherAccess = false;
68

    
69
	private AccessSection accessObject = null;
70

    
71
	private AccessRule accessRule = null;
72

    
73
	private Vector<AccessSection> accessObjectList = new Vector<AccessSection>(); // store
74
																					// every
75
																					// access
76
																					// rule
77

    
78
	private Hashtable<String, AccessSection> topLevelAccessControlMap = new Hashtable<String, AccessSection>();
79

    
80
	private Hashtable<String, AccessSection> additionalAccessControlMap = new Hashtable<String, AccessSection>();// store
81

    
82
	// subtree access for single additionalmetacat
83
	private Vector<Hashtable<String, AccessSection>> additionalAccessMapList = new Vector<Hashtable<String, AccessSection>>();// store
84
																																// maps
85
																																// for
86

    
87
	// every additionalmetadata
88
	private Vector<String> describesId = new Vector<String>(); // store the ids
89
																// in
90

    
91
	// additionalmetadata/describes
92
	private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
93

    
94
	private Vector<SubTree> subTreeList = new Vector<SubTree>();// store the
95
																// final subtree
96

    
97
	// MCD - Removed the following lines. They are used for node level access
98
	// control
99
	// which is not yet implemented in EML 2.1.0
100
	// private Hashtable<String,SubTree> unChangeableSubTreeHash = new
101
	// Hashtable<String,SubTree>();
102
	// private Stack<NodeRecord> currentUnChangeableSubtreeNodeStack = new
103
	// Stack<NodeRecord>();
104
	// private boolean startCriticalSubTree = false;
105
	// private boolean firstElementForCriticalSubTree = false;
106
	// private String firstElementNameForCriticalSubTree;
107
	
108
	private boolean needToCheckAccessModule = false;
109

    
110
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
111

    
112
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
113

    
114
	private AccessSection topAccessSection;
115

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

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

    
123
	// vector stored the data file id which will be write top access rules to
124
	// access table
125
	private Vector<String> onlineDataFileIdInTopAccessVector = new Vector<String>();
126

    
127
	// Indicator of inline data
128
	private boolean handleInlineData = false;
129

    
130
	private Hashtable<String, String> inlineDataNameSpace = null;
131

    
132
	private FileWriter inlineDataFileWriter = null;
133

    
134
	private String inlineDataFileName = null;
135

    
136
	DistributionSection currentDistributionSection = null;
137

    
138
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
139

    
140
	// This variable keeps a counter of each distribution element. This index
141
	// will be used to name the inline data file that gets written to disk, and to
142
	// strip inline data from the metadata document on file if the user does not have
143
	// read access.
144
	private int distributionIndex = 0;
145

    
146
	// private String distributionOnlineFileName = null;
147

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

    
151
	// Constant
152
	private static final String EML = "eml";
153

    
154
	private static final String DISTRIBUTION = "distribution";
155

    
156
	private static final String ORDER = "order";
157

    
158
	private static final String ID = "id";
159

    
160
	private static final String REFERENCES = "references";
161

    
162
	public static final String INLINE = "inline";
163

    
164
	private static final String ONLINE = "online";
165

    
166
	private static final String URL = "url";
167

    
168
	// private static final String PERMISSIONERROR = "User tried to update a
169
	// subtree "
170
	// + "when they don't have write permission!";
171

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

    
175
	private static final String TOPLEVEL = "top";
176

    
177
	private static final String SUBTREELEVEL = "subtree";
178

    
179
	private static final String RELATION = "Provides info for";
180

    
181
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
182

    
183
	/**
184
	 * Construct an instance of the handler class In this constructor, user can
185
	 * specify the version need to update
186
	 * 
187
	 * @param conn
188
	 *            the JDBC connection to which information is written
189
	 * @param action -
190
	 *            "INSERT" or "UPDATE"
191
	 * @param docid
192
	 *            to be inserted or updated into JDBC connection
193
	 * @param revision,
194
	 *            the user specified the revision need to be update
195
	 * @param user
196
	 *            the user connected to MetaCat servlet and owns the document
197
	 * @param groups
198
	 *            the groups to which user belongs
199
	 * @param pub
200
	 *            flag for public "read" access on document
201
	 * @param serverCode
202
	 *            the serverid from xml_replication on which this document
203
	 *            resides.
204
	 * 
205
	 */
206
	public Eml210SAXHandler(DBConnection conn, String action, String docid,
207
			String revision, String user, String[] groups, String pub, int serverCode,
208
			String createDate, String updateDate) throws SAXException {
209
		super(conn, action, docid, revision, user, groups, pub, serverCode, createDate,
210
				updateDate);
211
		// Get the unchangeable subtrees (user doesn't have write permission)
212
		try {
213
			PermissionController control = new PermissionController(docid
214
					+ PropertyService.getProperty("document.accNumSeparator") + revision);
215
			// unChangableSubTreeHash = getUnchangableSubTree(control, user,
216
			// groups);
217

    
218
			// If the action is update and user doesn't have "ALL" permission
219
			// we need to check if user can update access subtree			
220
			if (!control.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
221
					&& action != null && action.equals("UPDATE")) {
222
				needToCheckAccessModule = true;
223
				unChangeableAccessSubTreeVector = getAccessSubTreeListFromDB();
224
			}
225

    
226
		} catch (Exception e) {
227
			throw new SAXException(e.getMessage());
228
		}
229
	}
230

    
231
	// MCD - Removed the following method. It is used for node level access
232
	// control
233
	// which is not yet implemented in EML 2.1.0
234
	// /* Pass a permission control and get the list of unchangable subtree */
235
	// private Hashtable getUnchangableSubTree(PermissionController controller,
236
	// String user, String[] groups) throws Exception
237
	// {
238
	// Hashtable list = null;
239
	// Hashtable result = new Hashtable();
240
	// // get unwritable sutree from controller
241
	// list = null;
242
	// // changed after old code removal
243
	// //controller.hasUnaccessableSubTree(user, groups,
244
	// // AccessControlInterface.WRITESTRING);
245
	// if (list != null) {
246
	//
247
	// Enumeration en = list.elements();
248
	// while (en.hasMoreElements()) {
249
	// // Get a subtree without node record list
250
	// SubTree treeWithoutStack = (SubTree) en.nextElement();
251
	// String subTreeId = treeWithoutStack.getSubTreeId();
252
	// logMetacat.info(
253
	// "unchangable subtree id: " + subTreeId);
254
	// long startNodeId = treeWithoutStack.getStartNodeId();
255
	// logMetacat.info("unchangable subtree startnodeid: "
256
	// + startNodeId);
257
	// long endNodeId = treeWithoutStack.getEndNodeId();
258
	// logMetacat.info("unchangable subtree endnodeid: "
259
	// + endNodeId);
260
	// // Get a subtree has the nodelist
261
	// SubTree tree = new SubTree(docid, subTreeId, startNodeId,
262
	// endNodeId);
263
	// // add this tree to the result
264
	// result.put(subTreeId, tree);
265
	//
266
	// }//while
267
	//
268
	// }//if
269
	//
270
	// return result;
271
	// }
272

    
273
	/*
274
	 * Get the subtree node info from xml_accesssubtree table
275
	 */
276
	private Vector<AccessSection> getAccessSubTreeListFromDB() throws Exception {
277
		Vector<AccessSection> result = new Vector<AccessSection>();
278
		PreparedStatement pstmt = null;
279
		ResultSet rs = null;
280
		String sql = "SELECT controllevel, subtreeid, startnodeid, endnodeid "
281
				+ "FROM xml_accesssubtree WHERE docid like ? "
282
				+ "ORDER BY startnodeid ASC";
283

    
284
		try {
285

    
286
			pstmt = connection.prepareStatement(sql);
287
			// Increase DBConnection usage count
288
			connection.increaseUsageCount(1);
289
			// Bind the values to the query
290
			pstmt.setString(1, docid);
291
			pstmt.execute();
292

    
293
			// Get result set
294
			rs = pstmt.getResultSet();
295
			while (rs.next()) {
296
				String level = rs.getString(1);
297
				String sectionId = rs.getString(2);
298
				long startNodeId = rs.getLong(3);
299
				long endNodeId = rs.getLong(4);
300
				// create a new access section
301
				AccessSection accessObj = new AccessSection();
302
				accessObj.setControlLevel(level);
303
				accessObj.setDocId(docid);
304
				accessObj.setSubTreeId(sectionId);
305
				accessObj.setStartNodeId(startNodeId);
306
				accessObj.setEndNodeId(endNodeId);
307
				Stack<NodeRecord> nodeStack = accessObj.getSubTreeNodeStack();
308
				accessObj.setSubTreeNodeStack(nodeStack);
309
				// add this access obj into vector
310
				result.add(accessObj);
311
				// Get the top level access subtree control
312
				if (level != null && level.equals(TOPLEVEL)) {
313
					topAccessSection = accessObj;
314
				}
315
			}
316
			pstmt.close();
317
		}// try
318
		catch (SQLException e) {
319
			throw new SAXException("EMLSAXHandler.getAccessSubTreeListFromDB(): "
320
					+ e.getMessage());
321
		}// catch
322
		finally {
323
			try {
324
				pstmt.close();
325
			} catch (SQLException ee) {
326
				throw new SAXException("EMLSAXHandler.getAccessSubTreeListFromDB(): "
327
						+ ee.getMessage());
328
			}
329
		}// finally
330
		return result;
331
	}
332

    
333
	/** SAX Handler that is called at the start of each XML element */
334
	public void startElement(String uri, String localName, String qName, Attributes atts)
335
			throws SAXException {
336
		// for element <eml:eml...> qname is "eml:eml", local name is "eml"
337
		// for element <acl....> both qname and local name is "eml"
338
		// uri is namesapce
339
		logMetacat.debug("Start ELEMENT(qName) " + qName);
340
		logMetacat.debug("Start ELEMENT(localName) " + localName);
341
		logMetacat.debug("Start ELEMENT(uri) " + uri);
342

    
343
		DBSAXNode parentNode = null;
344
		DBSAXNode currentNode = null;
345

    
346
		if (!handleInlineData) {
347
			// Get a reference to the parent node for the id
348
			try {
349
				parentNode = (DBSAXNode) nodeStack.peek();
350
			} catch (EmptyStackException e) {
351
				parentNode = null;
352
			}
353

    
354
			// start handle inline data
355
			if (localName.equals(INLINE)) {
356
				handleInlineData = true;
357
				// initialize namespace hash for in line data
358
				inlineDataNameSpace = new Hashtable<String, String>();
359
				// initialize file writer
360
				String docidWithoutRev = DocumentUtil.getDocIdFromString(docid);
361
				String seperator = ".";
362
				try {
363
					seperator = PropertyService.getProperty("document.accNumSeparator");
364
				} catch (PropertyNotFoundException pnfe) {
365
					logMetacat.error("Could not fing property 'accNumSeparator'. "
366
							+ "Setting separator to '.': " + pnfe.getMessage());
367
				}
368
				// the new file name will look like docid.rev.2
369
				inlineDataFileName = docidWithoutRev + seperator + revision + seperator
370
						+ distributionIndex;
371
				inlineDataFileWriter = createInlineDataFileWriter(inlineDataFileName);
372
				// put the inline file id into a vector. If upload failed,
373
				// metacat will delete the inline data file
374
				inlineFileIdList.add(inlineDataFileName);
375
				
376
				currentDistributionSection.setDistributionType(DistributionSection.INLINE_DATA_DISTRIBUTION);
377
				currentDistributionSection.setDataFileName(inlineDataFileName);
378

    
379
			}
380

    
381
			// If hit a text node, we need write this text for current's parent
382
			// node This will happen if the element is mixed
383
			if (hitTextNode && parentNode != null) {
384
				// MCD - Removed the following method. It is used for node level
385
				// access control
386
				// which is not yet implemented in EML 2.1.0
387
				// //compare text node data for unchangeablesubtree
388
				// if (startCriticalSubTree) {
389
				// compareTextNode(currentUnChangeableSubtreeNodeStack,
390
				// textBuffer, PERMISSIONERROR);
391
				// }//if
392

    
393
				// compare top level access module
394
				if (processTopLevelAccess && needToCheckAccessModule) {
395
					compareTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer,
396
							UPDATEACCESSERROR);
397
				}
398

    
399
				if (needToCheckAccessModule
400
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
401
					// stored the pull out nodes into storedNode stack
402
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
403
							null, MetacatUtil.normalize(textBuffer.toString()));
404
					storedAccessNodeStack.push(nodeElement);
405

    
406
				}
407

    
408
				// write the textbuffer into db for parent node.
409
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, parentNode);
410
				// rest hitTextNode
411
				hitTextNode = false;
412
				// reset textbuffer
413
				textBuffer = null;
414
				textBuffer = new StringBuffer();
415

    
416
			}
417

    
418
			// Document representation that points to the root document node
419
			if (atFirstElement) {
420
				atFirstElement = false;
421
				// If no DOCTYPE declaration: docname = root element
422
				// doctype = root element name or name space
423
				if (docname == null) {
424
					docname = localName;
425
					// if uri isn't null doctype = uri(namespace)
426
					// othewise root element
427
					if (uri != null && !(uri.trim()).equals("")) {
428
						doctype = uri;
429
					} else {
430
						doctype = docname;
431
					}
432
					logMetacat.debug("DOCNAME-a: " + docname);
433
					logMetacat.debug("DOCTYPE-a: " + doctype);
434
				} else if (doctype == null) {
435
					// because docname is not null and it is declared in dtd
436
					// so could not be in schema, no namespace
437
					doctype = docname;
438
					logMetacat.debug("DOCTYPE-b: " + doctype);
439
				}
440
				rootNode.writeNodename(docname);
441
				try {
442
					// for validated XML Documents store a reference to XML DB
443
					// Catalog. Because this is select statement and it needn't
444
					// roll back if insert document action failed. In order to
445
					// decrease DBConnection usage count, we get a new
446
					// dbconnection from pool String catalogid = null;
447
					DBConnection dbConn = null;
448
					int serialNumber = -1;
449

    
450
					try {
451
						// Get dbconnection
452
						dbConn = DBConnectionPool
453
								.getDBConnection("DBSAXHandler.startElement");
454
						serialNumber = dbConn.getCheckOutSerialNumber();
455

    
456
						Statement stmt = dbConn.createStatement();
457
						ResultSet rs = stmt
458
								.executeQuery("SELECT catalog_id FROM xml_catalog "
459
										+ "WHERE entry_type = 'Schema' "
460
										+ "AND public_id = '" + doctype + "'");
461
						boolean hasRow = rs.next();
462
						if (hasRow) {
463
							catalogid = rs.getString(1);
464
						}
465
						stmt.close();
466
					}// try
467
					finally {
468
						// Return dbconnection
469
						DBConnectionPool.returnDBConnection(dbConn, serialNumber);
470
					}// finally
471

    
472
					// create documentImpl object by the constructor which can
473
					// specify the revision
474
					if (!super.getIsRevisionDoc()) {
475
						currentDocument = new DocumentImpl(connection, rootNode
476
								.getNodeID(), docname, doctype, docid, revision, action,
477
								user, this.pub, catalogid, this.serverCode, createDate,
478
								updateDate);
479
					}
480

    
481
				} catch (Exception ane) {
482
					throw (new SAXException("Error in EMLSaxHandler.startElement "
483
							+ action, ane));
484
				}
485
			}
486

    
487
			// Create the current node representation
488
			currentNode = new DBSAXNode(connection, qName, localName, parentNode,
489
					rootNode.getNodeID(), docid, doctype);
490
			// Use a local variable to store the element node id
491
			// If this element is a start point of subtree(section), it will be
492
			// stored otherwise, it will be discarded
493
			long startNodeId = currentNode.getNodeID();
494
			// Add all of the namespaces
495
			String prefix = null;
496
			String nsuri = null;
497
			Enumeration<String> prefixes = namespaces.keys();
498
			while (prefixes.hasMoreElements()) {
499
				prefix = prefixes.nextElement();
500
				nsuri = namespaces.get(prefix);
501
				endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
502
			}
503

    
504
			// Add all of the attributes
505
			for (int i = 0; i < atts.getLength(); i++) {
506
				String attributeName = atts.getQName(i);
507
				String attributeValue = atts.getValue(i);
508
				endNodeId = currentNode
509
						.setAttribute(attributeName, attributeValue, docid);
510

    
511
				// To handle name space and schema location if the attribute
512
				// name is xsi:schemaLocation. If the name space is in not
513
				// in catalog table it will be registered.
514
				if (attributeName != null
515
						&& attributeName.indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
516
					SchemaLocationResolver resolver = new SchemaLocationResolver(
517
							attributeValue);
518
					resolver.resolveNameSpace();
519

    
520
				} else if (attributeName != null && attributeName.equals(ID)) {
521
					// MCD - Removed the following code. It is used for node
522
					// level access
523
					// control which is not yet implemented in EML 2.1.0
524
					// // check unchangeable subtree hash if contains this
525
					// // subtree id
526
					// if (unChangeableSubTreeHash.containsKey(attributeValue))
527
					// {
528
					// // this subtree couldn't be changed by the user and
529
					// // move it from hash
530
					// SubTree currentUnChangedableSubtree =
531
					// unChangeableSubTreeHash
532
					// .remove(attributeValue);
533
					// currentUnChangeableSubtreeNodeStack =
534
					// currentUnChangedableSubtree
535
					// .getSubTreeNodeStack();
536
					// startCriticalSubTree = true;
537
					// firstElementForCriticalSubTree = true;
538
					// }
539
				}
540
			}// for
541

    
542
			// handle access stuff
543
			if (localName.equals(ACCESS)) {
544
				if (parentNode.getTagName().equals(EML)) {
545
					processTopLevelAccess = true;
546
				} else if (parentNode.getTagName() == DISTRIBUTION) {
547
					processAdditionalAccess = true;
548
				} else {
549
					// process other access embedded into resource level
550
					// module
551
					processOtherAccess = true;
552
				}
553
				// create access object
554
				accessObject = new AccessSection();
555
				// set permission order
556
				String permOrder = currentNode.getAttribute(ORDER);
557
				accessObject.setPermissionOrder(permOrder);
558
				// set access id
559
				String accessId = currentNode.getAttribute(ID);
560
				accessObject.setSubTreeId(accessId);
561
				accessObject.setStartNodeId(startNodeId);
562
				accessObject.setDocId(docid);
563
				if (processAdditionalAccess) {
564
					accessObject.setDataFileName(inlineDataFileName);
565
				}
566

    
567
				// load top level node stack to
568
				// currentUnchangableAccessModuleNodeStack
569
				if (processTopLevelAccess && needToCheckAccessModule) {
570
					// get the node stack for
571
					currentUnchangeableAccessModuleNodeStack = topAccessSection
572
							.getSubTreeNodeStack();
573
				}
574
			} else if (localName.equals(DISTRIBUTION)) {
575
				distributionIndex++;
576
				currentDistributionSection = new DistributionSection(distributionIndex);
577

    
578
				// handle subtree info
579
				SubTree subTree = new SubTree();
580
				// set sub tree id
581
				subTree.setSubTreeId(String.valueOf(distributionIndex));
582
				// set sub tree start element name
583
				subTree.setStartElementName(currentNode.getTagName());
584
				// set start node number
585
				subTree.setStartNodeId(startNodeId);
586
				// add to stack, but it didn't get end node id yet
587
				subTreeInfoStack.push(subTree);
588
			}
589
			// Set up a access rule for allow
590
			else if (parentNode.getTagName() != null
591
					&& (parentNode.getTagName()).equals(ACCESS)
592
					&& localName.equals(ALLOW)) {
593

    
594
				accessRule = new AccessRule();
595

    
596
				// set permission type "allow"
597
				accessRule.setPermissionType(ALLOW);
598

    
599
			}
600
			// set up an access rule for deny
601
			else if (parentNode.getTagName() != null
602
					&& (parentNode.getTagName()).equals(ACCESS) && localName.equals(DENY)) {
603
				accessRule = new AccessRule();
604
				// set permission type "allow"
605
				accessRule.setPermissionType(DENY);
606
			}
607

    
608
			// Add the node to the stack, so that any text data can be
609
			// added as it is encountered
610
			nodeStack.push(currentNode);
611
			// Add the node to the vector used by thread for writing XML Index
612
			nodeIndex.addElement(currentNode);
613

    
614
			// MCD - Removed the following code. It is used for node level
615
			// access
616
			// control which is not yet implemented in EML 2.1.0
617
			// // handle critical subtree
618
			// if (startCriticalSubTree && firstElementForCriticalSubTree) {
619
			// //store the element name
620
			// firstElementNameForCriticalSubTree = qName;
621
			// firstElementForCriticalSubTree = false;
622
			// }//for first element
623
			//
624
			// // handle critical subtree
625
			// if (startCriticalSubTree) {
626
			// compareElementNameSpaceAttributes(
627
			// currentUnChangeableSubtreeNodeStack, namespaces, atts,
628
			// localName, PERMISSIONERROR);
629
			// 
630
			// }
631

    
632
			// compare top access level module
633
			if (processTopLevelAccess && needToCheckAccessModule) {
634
				compareElementNameSpaceAttributes(
635
						currentUnchangeableAccessModuleNodeStack, namespaces, atts,
636
						localName, UPDATEACCESSERROR);
637

    
638
			}
639

    
640
			// store access module element and attributes into stored stack
641
			if (needToCheckAccessModule
642
					&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
643
				// stored the pull out nodes into storedNode stack
644
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT", localName,
645
						prefix, MetacatUtil.normalize(null));
646
				storedAccessNodeStack.push(nodeElement);
647
				for (int i = 0; i < atts.getLength(); i++) {
648
					String attributeName = atts.getQName(i);
649
					String attributeValue = atts.getValue(i);
650
					NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2, "ATTRIBUTE",
651
							attributeName, null, MetacatUtil.normalize(attributeValue));
652
					storedAccessNodeStack.push(nodeAttribute);
653
				}
654

    
655
			}
656

    
657
			// reset name space
658
			namespaces = null;
659
			namespaces = new Hashtable<String, String>();
660
		}// not inline data
661
		else {
662
			// we don't buffer the inline data in characters() method
663
			// so start character don't need to hand text node.
664

    
665
			// inline data may be the xml format.
666
			StringBuffer inlineElements = new StringBuffer();
667
			inlineElements.append("<").append(qName);
668
			// append attributes
669
			for (int i = 0; i < atts.getLength(); i++) {
670
				String attributeName = atts.getQName(i);
671
				String attributeValue = atts.getValue(i);
672
				inlineElements.append(" ");
673
				inlineElements.append(attributeName);
674
				inlineElements.append("=\"");
675
				inlineElements.append(attributeValue);
676
				inlineElements.append("\"");
677
			}
678
			// append namespace
679
			String prefix = null;
680
			String nsuri = null;
681
			Enumeration<String> prefixes = inlineDataNameSpace.keys();
682
			while (prefixes.hasMoreElements()) {
683
				prefix = prefixes.nextElement();
684
				nsuri = namespaces.get(prefix);
685
				inlineElements.append(" ");
686
				inlineElements.append("xmlns:");
687
				inlineElements.append(prefix);
688
				inlineElements.append("=\"");
689
				inlineElements.append(nsuri);
690
				inlineElements.append("\"");
691
			}
692
			inlineElements.append(">");
693
			// reset inline data name space
694
			inlineDataNameSpace = null;
695
			inlineDataNameSpace = new Hashtable<String, String>();
696
			// write inline data into file
697
			logMetacat.debug("the inline element data is: " + inlineElements.toString());
698
			writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
699
		}// else
700

    
701
	}
702

    
703
	private void compareElementNameSpaceAttributes(
704
			Stack<NodeRecord> unchangeableNodeStack,
705
			Hashtable<String, String> nameSpaces, Attributes attributes,
706
			String localName, String error) throws SAXException {
707
		// Get element subtree node stack (element node)
708
		NodeRecord elementNode = null;
709
		try {
710
			elementNode = unchangeableNodeStack.pop();
711
		} catch (EmptyStackException ee) {
712
			logMetacat.error("Node stack is empty for element data");
713
			throw new SAXException(error);
714
		}
715
		logMetacat.debug("current node type from xml is ELEMENT");
716
		logMetacat.debug("node type from stack: " + elementNode.getNodeType());
717
		logMetacat.debug("node name from xml document: " + localName);
718
		logMetacat.debug("node name from stack: " + elementNode.getNodeName());
719
		logMetacat.debug("node data from stack: " + elementNode.getNodeData());
720
		logMetacat.debug("node id is: " + elementNode.getNodeId());
721
		// if this node is not element or local name not equal or name space
722
		// not equals, throw an exception
723
		if (!elementNode.getNodeType().equals("ELEMENT")
724
				|| !localName.equals(elementNode.getNodeName()))
725
		// (uri != null && !uri.equals(elementNode.getNodePrefix())))
726
		{
727
			logMetacat.error("Inconsistence happened: ");
728
			logMetacat.error("current node type from xml is ELEMENT");
729
			logMetacat.error("node type from stack: " + elementNode.getNodeType());
730
			logMetacat.error("node name from xml document: " + localName);
731
			logMetacat.error("node name from stack: " + elementNode.getNodeName());
732
			logMetacat.error("node data from stack: " + elementNode.getNodeData());
733
			logMetacat.error("node id is: " + elementNode.getNodeId());
734
			throw new SAXException(error);
735
		}
736

    
737
		// compare namespace
738
		Enumeration<String> nameEn = nameSpaces.keys();
739
		while (nameEn.hasMoreElements()) {
740
			// Get namespacke node stack (element node)
741
			NodeRecord nameNode = null;
742
			try {
743
				nameNode = unchangeableNodeStack.pop();
744
			} catch (EmptyStackException ee) {
745
				logMetacat.error("Node stack is empty for namespace data");
746
				throw new SAXException(error);
747
			}
748

    
749
			String prefixName = nameEn.nextElement();
750
			String nameSpaceUri = nameSpaces.get(prefixName);
751
			if (!nameNode.getNodeType().equals("NAMESPACE")
752
					|| !prefixName.equals(nameNode.getNodeName())
753
					|| !nameSpaceUri.equals(nameNode.getNodeData())) {
754
				logMetacat.error("Inconsistence happened: ");
755
				logMetacat.error("current node type from xml is NAMESPACE");
756
				logMetacat.error("node type from stack: " + nameNode.getNodeType());
757
				logMetacat.error("current node name from xml is: " + prefixName);
758
				logMetacat.error("node name from stack: " + nameNode.getNodeName());
759
				logMetacat.error("current node data from xml is: " + nameSpaceUri);
760
				logMetacat.error("node data from stack: " + nameNode.getNodeData());
761
				logMetacat.error("node id is: " + nameNode.getNodeId());
762
				throw new SAXException(error);
763
			}
764

    
765
		}// while
766

    
767
		// compare attributes
768
		for (int i = 0; i < attributes.getLength(); i++) {
769
			NodeRecord attriNode = null;
770
			try {
771
				attriNode = unchangeableNodeStack.pop();
772

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

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

    
802
	}
803

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

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

    
864
	/** SAX Handler that is called at the end of each XML element */
865
	public void endElement(String uri, String localName, String qName)
866
			throws SAXException {
867
		logMetacat.debug("End ELEMENT " + qName);
868

    
869
		if (localName.equals(INLINE) && handleInlineData) {
870
			// Get the node from the stack
871
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
872
			logMetacat.debug("End of inline data");
873
			// close file writer
874
			try {
875
				inlineDataFileWriter.close();
876
				handleInlineData = false;
877
			} catch (IOException ioe) {
878
				throw new SAXException(ioe.getMessage());
879
			}
880
			// MCD - Removed the following code. It is used for node level
881
			// access
882
			// control which is not yet implemented in EML 2.1.0
883
			// //check if user changed inline data or not if user doesn't have
884
			// // write permission
885
			// if (startCriticalSubTree) {
886
			// NodeRecord node = null;
887
			// try {
888
			// node = currentUnChangeableSubtreeNodeStack.pop();
889
			// // get file name from db
890
			// String fileName = node.getNodeData();
891
			// logMetacat.info("in handle inline data");
892
			// logMetacat.info(
893
			// "the inline data file name from node is: "
894
			// + fileName);
895
			// if (!compareInlineDataFiles(fileName, inlineDataFileName)) {
896
			// logMetacat.info(
897
			// "inline data was changed by a user"
898
			// + " who doesn't have permission");
899
			// throw new SAXException(PERMISSIONERROR);
900
			// }
901
			// } catch (EmptyStackException ee) {
902
			// logMetacat.error(
903
			// "the stack is empty for text data");
904
			// throw new SAXException(PERMISSIONERROR);
905
			// } catch (McdbException eee) {
906
			// throw new SAXException(eee.getMessage());
907
			// } finally {
908
			// // delete the inline data file already in file system
909
			// deleteInlineDataFile(inlineDataFileName);
910
			// }
911
			// }//if
912

    
913
			// write put inline data file name into text buffer (without path)
914
			textBuffer = new StringBuffer(inlineDataFileName);
915
			// write file name into db
916
			endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
917
			// reset textbuff
918
			textBuffer = null;
919
			textBuffer = new StringBuffer();
920
			return;
921
		}
922

    
923
		if (!handleInlineData) {
924
			// Get the node from the stack
925
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
926
			String currentTag = currentNode.getTagName();
927

    
928
			// If before the end element, the parser hit text nodes and store
929
			// them into the buffer, write the buffer to data base. The reason
930
			// we put write database here is for xerces some time split text
931
			// node
932
			if (hitTextNode) {
933
				// get access value
934
				String data = null;
935
				// add principal
936
				if (currentTag.equals(PRINCIPAL) && accessRule != null) {
937
					data = (textBuffer.toString()).trim();
938
					accessRule.addPrincipal(data);
939

    
940
				} else if (currentTag.equals(PERMISSION) && accessRule != null) {
941
					data = (textBuffer.toString()).trim();
942
					// we combine different a permission into one value
943
					int permission = accessRule.getPermission();
944
					// add permission
945
					if (data.toUpperCase().equals(READSTRING)) {
946
						permission = permission | READ;
947
					} else if (data.toUpperCase().equals(WRITESTRING)) {
948
						permission = permission | WRITE;
949
					} else if (data.toUpperCase().equals(CHMODSTRING)) {
950
						permission = permission | CHMOD;
951
					} else if (data.toUpperCase().equals(ALLSTRING)) {
952
						permission = permission | ALL;
953
					}
954
					accessRule.setPermission(permission);
955
				} else if (currentTag.equals(REFERENCES)
956
						&& (processTopLevelAccess || processAdditionalAccess || processOtherAccess)) {
957
					// get reference
958
					data = (textBuffer.toString()).trim();
959
					// put reference id into accessSection
960
					accessObject.setReferences(data);
961

    
962
				} else if (currentTag.equals(URL)) {
963
					// handle online data, make sure its'parent is online
964
					DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
965
					if (parentNode != null && parentNode.getTagName() != null
966
							&& parentNode.getTagName().equals(ONLINE)) {
967
						// if online data is in local metacat, add it to the
968
						// vector
969
						data = (textBuffer.toString()).trim();
970
						handleOnlineUrlDataFile(data);
971
						/*
972
						 * if (data != null && (data.indexOf(MetacatUtil
973
						 * .getProperty("httpserver")) != -1 || data
974
						 * .indexOf(MetacatUtil .getProperty("server")) != -1)) { //
975
						 * Get docid from url String dataId = MetacatUtil
976
						 * .getDocIdWithRevFromOnlineURL(data); // add to vector
977
						 * onlineDataFileIdVector.add(dataId); }//if
978
						 */
979
					}// if
980
				}// else if
981
				// write text to db if it is not inline data
982
				// if (!localName.equals(INLINE))
983
				{
984
					logMetacat.debug("Write text into DB in End Element");
985
					// MCD - Removed the following code. It is used for node
986
					// level access
987
					// control which is not yet implemented in EML 2.1.0
988
					// //compare text node if need
989
					// if (startCriticalSubTree) {
990
					// compareTextNode(currentUnChangeableSubtreeNodeStack,
991
					// textBuffer, PERMISSIONERROR);
992
					// }//if
993

    
994
					// compare top level access module
995
					if (processTopLevelAccess && needToCheckAccessModule) {
996
						compareTextNode(currentUnchangeableAccessModuleNodeStack,
997
								textBuffer, UPDATEACCESSERROR);
998
					}
999
					// write text node into db
1000
					endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
1001
				}
1002
				if (needToCheckAccessModule
1003
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1004
					// stored the pull out nodes into storedNode stack
1005
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
1006
							null, MetacatUtil.normalize(textBuffer.toString()));
1007
					storedAccessNodeStack.push(nodeElement);
1008

    
1009
				}
1010
			}// if
1011

    
1012
			// MCD - Removed the following code. It is used for node level
1013
			// access
1014
			// control which is not yet implemented in EML 2.1.0
1015
			// //end critical subtree(user doesn't have permission to write)
1016
			// //When reach the first element and stack is empty
1017
			// if (localName.equals(firstElementNameForCriticalSubTree)
1018
			// && currentUnChangeableSubtreeNodeStack.isEmpty()) {
1019
			// startCriticalSubTree = false;
1020
			// }
1021

    
1022
			// set hitText false
1023
			hitTextNode = false;
1024
			// reset textbuff
1025
			textBuffer = null;
1026
			textBuffer = new StringBuffer();
1027

    
1028
			// hand sub stree stuff
1029
			if (!subTreeInfoStack.empty()) {
1030
				SubTree tree = subTreeInfoStack.peek();// get last
1031
				// subtree
1032
				if (tree != null && tree.getStartElementName() != null
1033
						&& (tree.getStartElementName()).equals(currentTag)) {
1034
					// find the end of sub tree and set the end node id
1035
					tree.setEndNodeId(endNodeId);
1036
					// add the subtree into the final store palace
1037
					subTreeList.add(tree);
1038
					// get rid of it from stack
1039
					subTreeInfoStack.pop();
1040
				}// if
1041
			}// if
1042

    
1043
			// access stuff
1044
			if (currentTag.equals(ALLOW) || currentTag.equals(DENY)) {
1045
				// finish parser access rule and assign it to new one
1046
				AccessRule newRule = accessRule;
1047
				// add the new rule to access section object
1048
				accessObject.addAccessRule(newRule);
1049
				// reset access rule
1050
				accessRule = null;
1051
			} else if (currentTag.equals(ACCESS)) {
1052
				// finish parsing an access section and assign it to new one
1053
				DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
1054

    
1055
				accessObject.setEndNodeId(endNodeId);
1056

    
1057
				if (parentNode != null && parentNode.getTagName() != null
1058
						&& parentNode.getTagName().equals(DISTRIBUTION)) {
1059
					describesId.add(String.valueOf(distributionIndex));
1060
					currentDistributionSection.setAccessSection(accessObject);
1061
				}
1062

    
1063
				AccessSection newAccessObject = accessObject;
1064

    
1065
				if (newAccessObject != null) {
1066

    
1067
					// add the accessSection into a vector to store it
1068
					// if it is not a reference, need to store it
1069
					if (newAccessObject.getReferences() == null) {
1070

    
1071
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
1072
						accessObjectList.add(newAccessObject);
1073
					}
1074
					if (processTopLevelAccess) {
1075

    
1076
						// top level access control will handle whole document
1077
						// -docid
1078
						topLevelAccessControlMap.put(docid, newAccessObject);
1079
						// reset processtopleveraccess tag
1080

    
1081
					}// if
1082
					else if (processAdditionalAccess) {
1083
						// for additional control put everything in describes
1084
						// value
1085
						// and access object into hash
1086
						for (int i = 0; i < describesId.size(); i++) {
1087

    
1088
							String subId = describesId.elementAt(i);
1089
							if (subId != null) {
1090
								additionalAccessControlMap.put(subId, newAccessObject);
1091
							}// if
1092
						}// for
1093
						// add this hashtable in to vector
1094

    
1095
						additionalAccessMapList.add(additionalAccessControlMap);
1096
						// reset this hashtable in order to store another
1097
						// additional
1098
						// accesscontrol
1099
						additionalAccessControlMap = null;
1100
						additionalAccessControlMap = new Hashtable<String, AccessSection>();
1101
					}// if
1102

    
1103
				}// if
1104
				// check if access node stack is empty after parsing top access
1105
				// module
1106

    
1107
				if (needToCheckAccessModule && processTopLevelAccess
1108
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
1109

    
1110
					logMetacat.error("Access node stack is not empty after "
1111
							+ "parsing access subtree");
1112
					throw new SAXException(UPDATEACCESSERROR);
1113

    
1114
				}
1115
				// reset access section object
1116

    
1117
				accessObject = null;
1118

    
1119
				// reset tmp stored node stack
1120
				storedAccessNodeStack = null;
1121
				storedAccessNodeStack = new Stack<NodeRecord>();
1122

    
1123
				// reset flag
1124
				processAdditionalAccess = false;
1125
				processTopLevelAccess = false;
1126
				processOtherAccess = false;
1127

    
1128
			} else if (currentTag.equals(DISTRIBUTION)) {
1129
				// If the current Distribution is inline or data and it doesn't have an access section
1130
				// we use the top level access section (if it exists)
1131
				if ((currentDistributionSection.getDistributionType() == DistributionSection.DATA_DISTRIBUTION 
1132
						|| currentDistributionSection.getDistributionType() == DistributionSection.INLINE_DATA_DISTRIBUTION)
1133
						&& currentDistributionSection.getAccessSection() == null
1134
						&& topLevelAccessControlMap.size() > 0) {
1135
					
1136
					AccessSection accessSection = new AccessSection();
1137
					accessSection.setDocId(docid);	
1138
					AccessSection topLevelAccess = topLevelAccessControlMap.get(docid);
1139
					accessSection.setPermissionOrder(topLevelAccess.getPermissionOrder());
1140
					Vector<AccessRule> accessRuleList = topLevelAccess.getAccessRules();
1141
					for (AccessRule accessRule : accessRuleList) {
1142
						accessSection.addAccessRule(accessRule);
1143
					}
1144
					currentDistributionSection.setAccessSection(accessSection);
1145
				} 
1146
				if (currentDistributionSection.getAccessSection() != null) {
1147
					currentDistributionSection.getAccessSection().setDataFileName(currentDistributionSection.getDataFileName());
1148
				}
1149
				allDistributionSections.add(currentDistributionSection);
1150
				currentDistributionSection = null;
1151
				describesId = null;
1152
				describesId = new Vector<String>();
1153
			}
1154
		} else {
1155
			// this is in inline part
1156
			StringBuffer endElement = new StringBuffer();
1157
			endElement.append("</");
1158
			endElement.append(qName);
1159
			endElement.append(">");
1160
			logMetacat.debug("inline endElement: " + endElement.toString());
1161
			writeInlineDataIntoFile(inlineDataFileWriter, endElement);
1162
		}
1163
	}
1164

    
1165
	/**
1166
	 * SAX Handler that receives notification of comments in the DTD
1167
	 */
1168
	public void comment(char[] ch, int start, int length) throws SAXException {
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
1176
				// access
1177
				// control which is not yet implemented in EML 2.1.0
1178
				// //compare comment if need
1179
				// if (startCriticalSubTree) {
1180
				// compareCommentNode(currentUnChangeableSubtreeNodeStack,
1181
				// str, PERMISSIONERROR);
1182
				// }//if
1183

    
1184
				// compare top level access module
1185
				if (processTopLevelAccess && needToCheckAccessModule) {
1186
					compareCommentNode(currentUnchangeableAccessModuleNodeStack, str,
1187
							UPDATEACCESSERROR);
1188
				}
1189
				endNodeId = currentNode.writeChildNodeToDB("COMMENT", null, 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, "COMMENT", null,
1194
							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: " + inlineComment.toString());
1206
			writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1207
		}
1208
	}
1209

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

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

    
1258
	/** SAX Handler that is called at the start of Namespace */
1259
	public void startPrefixMapping(String prefix, String uri) throws SAXException {
1260
		logMetacat.debug("NAMESPACE");
1261
		if (!handleInlineData) {
1262
			namespaces.put(prefix, uri);
1263
		} else {
1264
			inlineDataNameSpace.put(prefix, uri);
1265
		}
1266
	}
1267

    
1268
	/**
1269
	 * SAX Handler that is called for each XML text node that is Ignorable white
1270
	 * space
1271
	 */
1272
	public void ignorableWhitespace(char[] cbuf, int start, int len) throws SAXException {
1273
		// When validation is turned "on", white spaces are reported here
1274
		// When validation is turned "off" white spaces are not reported here,
1275
		// but through characters() callback
1276
		logMetacat.debug("IGNORABLEWHITESPACE");
1277
		if (!handleInlineData) {
1278
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1279
			String data = null;
1280
			int leftover = len;
1281
			int offset = start;
1282
			boolean moredata = true;
1283

    
1284
			// This loop deals with the case where there are more characters
1285
			// than can fit in a single database text field (limit is
1286
			// MAXDATACHARS). If the text to be inserted exceeds MAXDATACHARS,
1287
			// write a series of nodes that are MAXDATACHARS long, and then the
1288
			// final node contains the remainder
1289
			while (moredata) {
1290
				if (leftover > MAXDATACHARS) {
1291
					data = new String(cbuf, offset, MAXDATACHARS);
1292
					leftover -= MAXDATACHARS;
1293
					offset += MAXDATACHARS;
1294
				} else {
1295
					data = new String(cbuf, offset, leftover);
1296
					moredata = false;
1297
				}
1298

    
1299
				// MCD - Removed the following code. It is used for node level
1300
				// access
1301
				// control which is not yet implemented in EML 2.1.0
1302
				// //compare whitespace if need
1303
				// if (startCriticalSubTree) {
1304
				// compareWhiteSpace(currentUnChangeableSubtreeNodeStack,
1305
				// data, PERMISSIONERROR);
1306
				// }//if
1307

    
1308
				// compare whitespace in access top module
1309
				if (processTopLevelAccess && needToCheckAccessModule) {
1310
					compareWhiteSpace(currentUnchangeableAccessModuleNodeStack, data,
1311
							UPDATEACCESSERROR);
1312
				}
1313
				// Write the content of the node to the database
1314
				if (needToCheckAccessModule
1315
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1316
					// stored the pull out nodes into storedNode stack
1317
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
1318
							null, MetacatUtil.normalize(data));
1319
					storedAccessNodeStack.push(nodeElement);
1320

    
1321
				}
1322
				endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data, docid);
1323
			}
1324
		} else {
1325
			// This is inline data write to file directly
1326
			StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf, start, len));
1327
			writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1328
		}
1329

    
1330
	}
1331

    
1332
	/* Compare whitespace from xml and db */
1333
	private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string,
1334
			String error) throws SAXException {
1335
		NodeRecord node = null;
1336
		try {
1337
			node = nodeStack.pop();
1338
		} catch (EmptyStackException ee) {
1339
			logMetacat.error("the stack is empty for whitespace data");
1340
			throw new SAXException(error);
1341
		}
1342
		if (!node.getNodeType().equals("TEXT") || !string.equals(node.getNodeData())) {
1343
			logMetacat.error("Inconsistence happened: ");
1344
			logMetacat.error("current node type from xml is WHITESPACE TEXT");
1345
			logMetacat.error("node type from stack: " + node.getNodeType());
1346
			logMetacat.error("current node data from xml is: " + string);
1347
			logMetacat.error("node data from stack: " + node.getNodeData());
1348
			logMetacat.error("node is from stack: " + node.getNodeId());
1349
			throw new SAXException(error);
1350
		}// if
1351
	}
1352

    
1353
	/** SAX Handler that receives notification of end of the document */
1354
	public void endDocument() throws SAXException {
1355
		logMetacat.debug("end Document");
1356
		// There are some unchangable subtree didn't be compare
1357
		// This maybe cause user change the subtree id
1358
		// MCD - Removed the following code. It is used for node level access
1359
		// control which is not yet implemented in EML 2.1.0
1360
		// if (!unChangeableSubTreeHash.isEmpty()) {
1361
		// logMetacat.info("The unChangealbe subtree is not empty");
1362
		// throw new SAXException(PERMISSIONERROR);
1363
		// }
1364
		if (!super.getIsRevisionDoc()) {
1365
			// write access rule to db
1366
			writeAccessRuleToDB();
1367
			// delete relation table
1368
			deleteRelations();
1369
			// write relations
1370
			for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1371
				String id = onlineDataFileIdInRelationVector.elementAt(i);
1372
				writeOnlineDataFileIdIntoRelationTable(id);
1373
			}
1374
		}
1375
	}
1376

    
1377
	/* The method to write all access rule into db */
1378
	private void writeAccessRuleToDB() throws SAXException {
1379
		// Delete old permssion
1380
		deletePermissionsInAccessTable(docid);
1381
		// write top leve access rule
1382
		writeTopLevelAccessRuleToDB();
1383
		// write additional access rule
1384
		// writeAdditionalAccessRuleToDB();
1385
		writeAdditionalAccessRulesToDB();
1386
	}// writeAccessRuleToDB
1387

    
1388
	/* The method to write top level access rule into db. */
1389
	private void writeTopLevelAccessRuleToDB() throws SAXException {
1390
		// for top document level
1391
		AccessSection accessSection = topLevelAccessControlMap.get(docid);
1392
		boolean top = true;
1393
		String subSectionId = null;
1394
		if (accessSection != null) {
1395
			AccessSection accessSectionObj = accessSection;
1396

    
1397
			// if accessSection is not null and is not reference
1398
			if (accessSectionObj.getReferences() == null) {
1399
				// write the top level access module into xml_accesssubtree to
1400
				// store info and then when update to check if the user can
1401
				// update it or not
1402
				deleteAccessSubTreeRecord(docid);
1403
				writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1404

    
1405
				// write access section into xml_access table
1406
				writeGivenAccessRuleIntoDB(accessSectionObj, top, subSectionId);
1407
				// write online data file into xml_access too.
1408
				// for (int i= 0; i <onlineDataFileIdInTopAccessVector.size();
1409
				// i++)
1410
				// {
1411
				// String id = onlineDataFileIdInTopAccessVector.elementAt(i);
1412
				// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1413
				// id);
1414
				// }
1415

    
1416
			} else {
1417

    
1418
				// this is a reference and go trough the vector which contains
1419
				// all access object
1420
				String referenceId = accessSectionObj.getReferences();
1421
				boolean findAccessObject = false;
1422
				logMetacat.debug("referered id for top access: " + referenceId);
1423
				for (int i = 0; i < accessObjectList.size(); i++) {
1424
					AccessSection accessObj = accessObjectList.elementAt(i);
1425
					String accessObjId = accessObj.getSubTreeId();
1426
					if (referenceId != null && accessObj != null
1427
							&& referenceId.equals(accessObjId)) {
1428
						// make sure the user didn't change any thing in this
1429
						// access moduel
1430
						// too if user doesn't have all permission
1431
						if (needToCheckAccessModule) {
1432

    
1433
							Stack<NodeRecord> newStack = accessObj
1434
									.getStoredTmpNodeStack();
1435
							// revise order
1436
							newStack = DocumentUtil.reviseStack(newStack);
1437
							// go throught the vector of
1438
							// unChangeableAccessSubtreevector
1439
							// and find the one whose id is as same as
1440
							// referenceid
1441
							AccessSection oldAccessObj = getAccessSectionFromUnchangableAccessVector(referenceId);
1442
							// if oldAccessObj is null something is wrong
1443
							if (oldAccessObj == null) {
1444
								throw new SAXException(UPDATEACCESSERROR);
1445
							}// if
1446
							else {
1447
								// Get the node stack from old access obj
1448
								Stack<NodeRecord> oldStack = oldAccessObj
1449
										.getSubTreeNodeStack();
1450
								compareNodeStacks(newStack, oldStack);
1451
							}// else
1452
						}// if
1453
						// write accessobject into db
1454
						writeGivenAccessRuleIntoDB(accessObj, top, subSectionId);
1455
						// write online data file into xml_access too.
1456
						// for (int j= 0; j
1457
						// <onlineDataFileIdInTopAccessVector.size(); j++)
1458
						// {
1459
						// String id =
1460
						// onlineDataFileIdInTopAccessVector.elementAt(j);
1461
						// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1462
						// id);
1463
						// }
1464

    
1465
						// write the reference access into xml_accesssubtree
1466
						// too write the top level access module into
1467
						// xml_accesssubtree to store info and then when update
1468
						// to check if the user can update it or not
1469
						deleteAccessSubTreeRecord(docid);
1470
						writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1471
						writeAccessSubTreeIntoDB(accessObj, SUBTREELEVEL);
1472
						findAccessObject = true;
1473
						break;
1474
					}
1475
				}// for
1476
				// if we couldn't find an access subtree id for this reference
1477
				// id
1478
				if (!findAccessObject) {
1479
					throw new SAXException("The referenceid: " + referenceId
1480
							+ " is not access subtree");
1481
				}// if
1482
			}// else
1483

    
1484
		}// if
1485
		else {
1486
			// couldn't find a access section object
1487
			logMetacat.warn("couldn't find access control for document: " + docid);
1488
		}
1489

    
1490
	}// writeTopLevelAccessRuletoDB
1491

    
1492
	/* Given a subtree id and find the responding access section */
1493
	private AccessSection getAccessSectionFromUnchangableAccessVector(String id) {
1494
		AccessSection result = null;
1495
		// Makse sure the id
1496
		if (id == null || id.equals("")) {
1497
			return result;
1498
		}
1499
		// go throught vector and find the list
1500
		for (int i = 0; i < unChangeableAccessSubTreeVector.size(); i++) {
1501
			AccessSection accessObj = unChangeableAccessSubTreeVector.elementAt(i);
1502
			if (accessObj.getSubTreeId() != null && (accessObj.getSubTreeId()).equals(id)) {
1503
				result = accessObj;
1504
			}// if
1505
		}// for
1506
		return result;
1507
	}// getAccessSectionFromUnchangableAccessVector
1508

    
1509
	/* Compare two node stacks to see if they are same */
1510
	private void compareNodeStacks(Stack<NodeRecord> stack1, Stack<NodeRecord> stack2)
1511
			throws SAXException {
1512
		// make sure stack1 and stack2 are not empty
1513
		if (stack1.isEmpty() || stack2.isEmpty()) {
1514
			logMetacat.error("Because stack is empty!");
1515
			throw new SAXException(UPDATEACCESSERROR);
1516
		}
1517
		// go throw two stacks and compare every element
1518
		while (!stack1.isEmpty()) {
1519
			// Pop an element from stack1
1520
			NodeRecord record1 = stack1.pop();
1521
			// Pop an element from stack2(stack 2 maybe empty)
1522
			NodeRecord record2 = null;
1523
			try {
1524
				record2 = stack2.pop();
1525
			} catch (EmptyStackException ee) {
1526
				logMetacat.error("Node stack2 is empty but stack1 isn't!");
1527
				throw new SAXException(UPDATEACCESSERROR);
1528
			}
1529
			// if two records are not same throw a exception
1530
			if (!record1.contentEquals(record2)) {
1531
				logMetacat.error("Two records from new and old stack are not " + "same!");
1532
				throw new SAXException(UPDATEACCESSERROR);
1533
			}// if
1534
		}// while
1535

    
1536
		// now stack1 is empty and we should make sure stack2 is empty too
1537
		if (!stack2.isEmpty()) {
1538
			logMetacat
1539
					.error("stack2 still has some elements while stack " + "is empty! ");
1540
			throw new SAXException(UPDATEACCESSERROR);
1541
		}// if
1542
	}// comparingNodeStacks
1543

    
1544
	/* The method to write addtional access rule into db. */
1545
	private void writeAdditionalAccessRulesToDB() throws SAXException {
1546
		
1547
		// Iterate through every distribution and write access sections for data and inline
1548
		// types to the database
1549
		for (DistributionSection distributionSection : allDistributionSections) {			
1550
			// we're only interested in data and inline distributions
1551
			int distributionType = distributionSection.getDistributionType();
1552
			if (distributionType == DistributionSection.DATA_DISTRIBUTION
1553
					|| distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION) {
1554
				AccessSection accessSection = distributionSection.getAccessSection();
1555
				
1556
				// If the distribution doesn't have an access section, we continue.
1557
				if (accessSection == null) {
1558
					continue;		
1559
				} 
1560
				
1561
				// We want to check file permissions for all online data updates and inserts, or for 
1562
				// inline updates.
1563
//				if (distributionType == DistributionSection.DATA_DISTRIBUTION
1564
//						|| (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE")) {
1565

    
1566
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1567
					try {
1568
						PermissionController controller = new PermissionController(
1569
								distributionSection.getDataFileName(), false);
1570
						if (AccessionNumber.accNumberUsed(docid)
1571
								&& !controller.hasPermission(user, groups, "WRITE")) {
1572
							throw new SAXException(UPDATEACCESSERROR);
1573
						}
1574
					} catch (SQLException sqle) {
1575
						throw new SAXException(
1576
								"Database error checking user permissions: "
1577
										+ sqle.getMessage());
1578
					} catch (Exception e) {
1579
						throw new SAXException(
1580
								"General error checking user permissions: "
1581
										+ e.getMessage());
1582
					}
1583
				} else if (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE") {
1584
					try {
1585
						PermissionController controller = new PermissionController(
1586
								docid, false);
1587

    
1588
						if (!controller.hasPermission(user, groups, "WRITE")) {
1589
							throw new SAXException(UPDATEACCESSERROR);
1590
						}
1591
					} catch (SQLException sqle) {
1592
						throw new SAXException(
1593
								"Database error checking user permissions: "
1594
										+ sqle.getMessage());
1595
					} catch (Exception e) {
1596
						throw new SAXException(
1597
								"General error checking user permissions: "
1598
										+ e.getMessage());
1599
					}
1600
				}
1601
				
1602
				String subSectionId = Integer.toString(distributionSection.getDistributionId());
1603
				writeGivenAccessRuleIntoDB(accessSection, false, subSectionId);
1604
			}
1605

    
1606
		}
1607
		
1608
		
1609
	}
1610

    
1611
	// /* The method to write addtional access rule into db. */
1612
	// private void writeAdditionalAccessRuleToDB() throws SAXException
1613
	// {
1614
	//
1615
	// boolean topLevel = false;
1616
	// // go through the vector which contains the additional access control
1617
	// // hashtable. Each hashtable has info for one additonalmetadata
1618
	// // container
1619
	// for (Hashtable<String, AccessSection> accessControlMap :
1620
	// additionalAccessMapList) {
1621
	// // additional access rule
1622
	// Enumeration<String> en = accessControlMap.keys();
1623
	//
1624
	// while (en.hasMoreElements()) {
1625
	// try {
1626
	// // Get subsection id
1627
	// String subSectionId = en.nextElement();
1628
	// logMetacat.debug("sub section id in additional access mapping"
1629
	// + "(go through): " + subSectionId);
1630
	//
1631
	// if (subSectionId == null) {
1632
	// // if id is null, terminate the program
1633
	// throw new SAXException("subtree id is null"); }
1634
	// // Get AccessSection Object
1635
	// AccessSection accessSectionObj =
1636
	// accessControlMap.get(subSectionId);
1637
	// if (accessSectionObj == null) {
1638
	// // if accesssection is null, terminate the program
1639
	// throw new SAXException("access subtree is null");
1640
	// } else if (accessSectionObj.getDataFileName() == null) {
1641
	// // if there is no data file name, continue with the next record
1642
	// continue;
1643
	// } else {
1644
	// AccessSection accessControlObj = accessSectionObj;
1645
	// // if the access section is not references, write it to
1646
	// // db
1647
	// if (accessControlObj.getReferences() == null) {
1648
	// writeGivenAccessRuleIntoDB(accessControlObj,
1649
	// topLevel, subSectionId);
1650
	// } else {
1651
	// // this is a reference and go trough the vector
1652
	// // which contains all access object
1653
	// String referenceId = accessControlObj
1654
	// .getReferences();
1655
	//
1656
	// boolean findAccessObject = false;
1657
	// logMetacat.debug("referered id for additional access "
1658
	// + "mapping(go through): " + referenceId);
1659
	// for (AccessSection accessObj : accessObjectList) {
1660
	// String accessObjId = accessObj.getSubTreeId();
1661
	// logMetacat.debug("access obj id in the list(go through): "
1662
	// + accessObjId);
1663
	// if (referenceId != null && accessObj != null
1664
	// && referenceId.equals(accessObjId)) {
1665
	// writeGivenAccessRuleIntoDB(accessObj,
1666
	// topLevel, subSectionId);
1667
	// findAccessObject = true;
1668
	// }//if
1669
	// }//for
1670
	// // if we couldn't find an access subtree id for
1671
	// // this reference id
1672
	// if (!findAccessObject) {
1673
	// throw new SAXException("The referenceid: " + referenceId
1674
	// + " is not access subtree");
1675
	// }//if
1676
	// }//else
1677
	// }//else
1678
	// }//try
1679
	// catch (Exception e) {
1680
	//
1681
	// logMetacat.error(
1682
	// "error in EmlSAXHandler.writeAddtionalAccess"
1683
	// + ": " + e.getMessage());
1684
	// throw new SAXException(e.getMessage());
1685
	// }
1686
	// }//while
1687
	// }//for
1688
	// }//writeAccessRuletoDB
1689

    
1690
	/* Write a given access rule into db */
1691
	private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1692
			boolean topLevel, String subSectionId) throws SAXException {
1693
		if (accessSection == null) {
1694
			throw new SAXException("The access object is null");
1695
		}
1696

    
1697
		String permOrder = accessSection.getPermissionOrder();
1698
		String sql = null;
1699
		PreparedStatement pstmt = null;
1700
		if (topLevel) {
1701
			sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1702
					+ "perm_type, perm_order, accessfileid) VALUES "
1703
					+ " (?, ?, ?, ?, ?, ?)";
1704
		} else {
1705
			sql = "INSERT INTO xml_access (docid,principal_name, "
1706
					+ "permission, perm_type, perm_order, accessfileid, subtreeid"
1707
					+ ") VALUES" + " (?, ?, ?, ?, ?, ?, ?)";
1708
		}
1709
		try {
1710

    
1711
			pstmt = connection.prepareStatement(sql);
1712
			// Increase DBConnection usage count
1713
			connection.increaseUsageCount(1);
1714
			// Bind the values to the query
1715
			pstmt.setString(6, docid);
1716
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1717
			pstmt.setString(5, permOrder);
1718
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1719
			// if it is not top level, set subsection id
1720
			if (topLevel) {
1721
				pstmt.setString(1, docid);
1722
				logMetacat.debug("Docid in accesstable: " + docid);
1723
			}
1724
			if (!topLevel) {
1725
				pstmt.setString(1, accessSection.getDataFileName());
1726
				logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1727

    
1728
				// for subtree should specify the
1729
				if (subSectionId == null) {
1730
					throw new SAXException("The subsection is null");
1731
				}
1732
				// MCD - Removed the following code. It is used for node level
1733
				// access
1734
				// control which is not yet implemented in EML 2.1.0
1735
				// // go through the substree list vector and found the start
1736
				// node
1737
				// // id and stop node id for this subtree id
1738
				// for (int i = 0; i < subTreeList.size(); i++) {
1739
				// SubTree tree = subTreeList.elementAt(i);
1740
				// String subTreeId = tree.getSubTreeId();
1741
				// if (subSectionId.equals(subTreeId)) {
1742
				// startNodeId = tree.getStartNodeId();
1743
				// endNodeId = tree.getEndNodeId();
1744
				// }//if
1745
				// }//for
1746
				// if (startNodeId == 0 || endNodeId == 0) {
1747
				// throw new SAXException("Could not find the subtree for this
1748
				// id: "
1749
				// + subSectionId);
1750
				// }
1751

    
1752
				pstmt.setString(7, subSectionId);
1753
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1754
			}
1755

    
1756
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1757
			// go through every rule
1758
			for (int i = 0; i < accessRules.size(); i++) {
1759
				AccessRule rule = accessRules.elementAt(i);
1760
				String permType = rule.getPermissionType();
1761
				int permission = rule.getPermission();
1762
				pstmt.setInt(3, permission);
1763
				logMetacat.debug("permission in accesstable: " + permission);
1764
				pstmt.setString(4, permType);
1765
				logMetacat.debug("Permtype in accesstable: " + permType);
1766
				// go through every principle in rule
1767
				Vector<String> nameVector = rule.getPrincipal();
1768
				for (int j = 0; j < nameVector.size(); j++) {
1769
					String prName = nameVector.elementAt(j);
1770
					pstmt.setString(2, prName);
1771
					logMetacat.debug("Principal in accesstable: " + prName);
1772
					logMetacat.debug("running sql: " + pstmt.toString());
1773
					pstmt.execute();
1774
				}// for
1775
			}// for
1776
			pstmt.close();
1777
		}// try
1778
		catch (SQLException e) {
1779
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1780
					+ e.getMessage());
1781
		}// catch
1782
		finally {
1783
			try {
1784
				pstmt.close();
1785
			} catch (SQLException ee) {
1786
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1787
						+ ee.getMessage());
1788
			}
1789
		}// finally
1790

    
1791
	}// writeGivenAccessRuleIntoDB
1792

    
1793
	/* Write a gaven access rule into db */
1794
	private void writeAccessRuleForRelatedDataFileIntoDB(AccessSection accessSection,
1795
			String dataId) throws SAXException {
1796
		if (accessSection == null) {
1797
			throw new SAXException("The access object is null");
1798
		}
1799
		// get rid of rev from dataId
1800
		// dataId = MetacatUtil.getDocIdFromString(dataId);
1801
		String permOrder = accessSection.getPermissionOrder();
1802
		String sql = null;
1803
		PreparedStatement pstmt = null;
1804
		sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1805
				+ "perm_type, perm_order, accessfileid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1806

    
1807
		try {
1808

    
1809
			pstmt = connection.prepareStatement(sql);
1810
			// Increase DBConnection usage count
1811
			connection.increaseUsageCount(1);
1812
			// Bind the values to the query
1813
			pstmt.setString(1, dataId);
1814
			logMetacat.debug("Docid in accesstable: " + docid);
1815
			pstmt.setString(6, docid);
1816
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1817
			pstmt.setString(5, permOrder);
1818
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1819
			// if it is not top level, set subsection id
1820

    
1821
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1822
			// go through every rule
1823
			for (int i = 0; i < accessRules.size(); i++) {
1824
				AccessRule rule = accessRules.elementAt(i);
1825
				String permType = rule.getPermissionType();
1826
				int permission = rule.getPermission();
1827
				pstmt.setInt(3, permission);
1828
				logMetacat.debug("permission in accesstable: " + permission);
1829
				pstmt.setString(4, permType);
1830
				logMetacat.debug("Permtype in accesstable: " + permType);
1831
				// go through every principle in rule
1832
				Vector<String> nameVector = rule.getPrincipal();
1833
				for (int j = 0; j < nameVector.size(); j++) {
1834
					String prName = nameVector.elementAt(j);
1835
					pstmt.setString(2, prName);
1836
					logMetacat.debug("Principal in accesstable: " + prName);
1837
					logMetacat.debug("running sql: " + pstmt.toString());
1838
					pstmt.execute();
1839
				}// for
1840
			}// for
1841
			pstmt.close();
1842
		}// try
1843
		catch (SQLException e) {
1844
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1845
					+ e.getMessage());
1846
		}// catch
1847
		finally {
1848
			try {
1849
				pstmt.close();
1850
			} catch (SQLException ee) {
1851
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1852
						+ ee.getMessage());
1853
			}
1854
		}// finally
1855

    
1856
	}// writeAccessRuleForRalatedDataFileIntoDB
1857

    
1858
	/* Delete from db all permission for resources related to @aclid if any. */
1859
	private void deletePermissionsInAccessTable(String aclid) throws SAXException {
1860
		Statement stmt = null;
1861
		try {
1862
			// delete all acl records for resources related to @aclid if any
1863
			stmt = connection.createStatement();
1864
			// Increase DBConnection usage count
1865
			connection.increaseUsageCount(1);
1866
			logMetacat.debug("running sql: DELETE FROM xml_access WHERE accessfileid = '"
1867
					+ aclid + "'");
1868
			stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
1869

    
1870
		} catch (SQLException e) {
1871
			throw new SAXException(e.getMessage());
1872
		} finally {
1873
			try {
1874
				stmt.close();
1875
			} catch (SQLException ee) {
1876
				throw new SAXException(ee.getMessage());
1877
			}
1878
		}
1879
	}// deletePermissionsInAccessTable
1880

    
1881
	/*
1882
	 * In order to make sure only usr has "all" permission can update access
1883
	 * subtree in eml document we need to keep access subtree info in
1884
	 * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1885
	 */
1886
	private void writeAccessSubTreeIntoDB(AccessSection accessSection, String level)
1887
			throws SAXException {
1888
		if (accessSection == null) {
1889
			throw new SAXException("The access object is null");
1890
		}
1891

    
1892
		String sql = null;
1893
		PreparedStatement pstmt = null;
1894
		sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1895
				+ "subtreeid, startnodeid, endnodeid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1896
		try {
1897

    
1898
			pstmt = connection.prepareStatement(sql);
1899
			// Increase DBConnection usage count
1900
			connection.increaseUsageCount(1);
1901
			long startNodeId = accessSection.getStartNodeId();
1902
			long endNodeId = accessSection.getEndNodeId();
1903
			String sectionId = accessSection.getSubTreeId();
1904
			// Bind the values to the query
1905
			pstmt.setString(1, docid);
1906
			logMetacat.debug("Docid in access-subtreetable: " + docid);
1907
			pstmt.setLong(2, (new Long(revision)).longValue());
1908
			logMetacat.debug("rev in accesssubtreetable: " + revision);
1909
			pstmt.setString(3, level);
1910
			logMetacat.debug("contorl level in access-subtree table: " + level);
1911
			pstmt.setString(4, sectionId);
1912
			logMetacat.debug("Subtree id in access-subtree table: " + sectionId);
1913
			pstmt.setLong(5, startNodeId);
1914
			logMetacat.debug("Start node id is: " + startNodeId);
1915
			pstmt.setLong(6, endNodeId);
1916
			logMetacat.debug("End node id is: " + endNodeId);
1917
			logMetacat.debug("running sql: " + pstmt.toString());
1918
			pstmt.execute();
1919
			pstmt.close();
1920
		}// try
1921
		catch (SQLException e) {
1922
			throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1923
					+ e.getMessage());
1924
		}// catch
1925
		finally {
1926
			try {
1927
				pstmt.close();
1928
			} catch (SQLException ee) {
1929
				throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1930
						+ ee.getMessage());
1931
			}
1932
		}// finally
1933

    
1934
	}// writeAccessSubtreeIntoDB
1935

    
1936
	/* Delete every access subtree record from xml_accesssubtree. */
1937
	private void deleteAccessSubTreeRecord(String docId) throws SAXException {
1938
		Statement stmt = null;
1939
		try {
1940
			// delete all acl records for resources related to @aclid if any
1941
			stmt = connection.createStatement();
1942
			// Increase DBConnection usage count
1943
			connection.increaseUsageCount(1);
1944
			logMetacat.debug("running sql: DELETE FROM xml_accesssubtree WHERE docid = '"
1945
					+ docId + "'");
1946
			stmt.execute("DELETE FROM xml_accesssubtree WHERE docid = '" + docId + "'");
1947

    
1948
		} catch (SQLException e) {
1949
			throw new SAXException(e.getMessage());
1950
		} finally {
1951
			try {
1952
				stmt.close();
1953
			} catch (SQLException ee) {
1954
				throw new SAXException(ee.getMessage());
1955
			}
1956
		}
1957
	}// deleteAccessSubTreeRecord
1958

    
1959
	// open a file writer for writing inline data to file
1960
	private FileWriter createInlineDataFileWriter(String fileName) throws SAXException {
1961
		FileWriter writer = null;
1962
		String path;
1963
		try {
1964
			path = PropertyService.getProperty("application.inlinedatafilepath");
1965
		} catch (PropertyNotFoundException pnfe) {
1966
			throw new SAXException(pnfe.getMessage());
1967
		}
1968
		/*
1969
		 * File inlineDataDirectory = new File(path);
1970
		 */
1971
		String newFile = path + "/" + fileName;
1972
		logMetacat.debug("inline file name: " + newFile);
1973
		try {
1974
			// true means append
1975
			writer = new FileWriter(newFile, true);
1976
		} catch (IOException ioe) {
1977
			throw new SAXException(ioe.getMessage());
1978
		}
1979
		return writer;
1980
	}
1981

    
1982
	// write inline data into file system and return file name(without path)
1983
	private void writeInlineDataIntoFile(FileWriter writer, StringBuffer data)
1984
			throws SAXException {
1985
		try {
1986
			writer.write(data.toString());
1987
			writer.flush();
1988
		} catch (Exception e) {
1989
			throw new SAXException(e.getMessage());
1990
		}
1991
	}
1992

    
1993
	/*
1994
	 * In eml2, the inline data wouldn't store in db, it store in file system
1995
	 * The db stores file name(without path). We got the old file name from db
1996
	 * and compare to the new in line data file
1997
	 */
1998
	public boolean compareInlineDataFiles(String oldFileName, String newFileName)
1999
			throws McdbException {
2000
		// this method need to be testing
2001
		boolean same = true;
2002
		String data = null;
2003
		try {
2004
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2005
			// the new file name will look like path/docid.rev.2
2006
			File inlineDataDirectory = new File(path);
2007
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
2008
			File newDataFile = new File(inlineDataDirectory, newFileName);
2009

    
2010
			FileReader oldFileReader = new FileReader(oldDataFile);
2011
			BufferedReader oldStringReader = new BufferedReader(oldFileReader);
2012
			FileReader newFileReader = new FileReader(newDataFile);
2013
			BufferedReader newStringReader = new BufferedReader(newFileReader);
2014
			// read first line of data
2015
			String oldString = oldStringReader.readLine();
2016
			String newString = newStringReader.readLine();
2017

    
2018
			// at the end oldstring will be null
2019
			while (oldString != null) {
2020
				oldString = oldStringReader.readLine();
2021
				newString = newStringReader.readLine();
2022
				if (!oldString.equals(newString)) {
2023
					same = false;
2024
					break;
2025
				}
2026
			}
2027

    
2028
			// if oldString is null but newString is not null, they are same
2029
			if (same) {
2030
				if (newString != null) {
2031
					same = false;
2032
				}
2033
			}
2034

    
2035
		} catch (Exception e) {
2036
			throw new McdbException(e.getMessage());
2037
		}
2038
		logMetacat.debug("the inline data retrieved from file: " + data);
2039
		return same;
2040
	}
2041

    
2042
	// if xml file failed to upload, we need to call this method to delete
2043
	// the inline data already in file system
2044
	public void deleteInlineFiles() throws SAXException {
2045
		if (!inlineFileIdList.isEmpty()) {
2046
			for (int i = 0; i < inlineFileIdList.size(); i++) {
2047
				String fileName = inlineFileIdList.elementAt(i);
2048
				deleteInlineDataFile(fileName);
2049
			}
2050
		}
2051
	}
2052

    
2053
	/* delete the inline data file */
2054
	private void deleteInlineDataFile(String fileName) throws SAXException {
2055
		String path;
2056
		try {
2057
			path = PropertyService.getProperty("application.inlinedatafilepath");
2058
		} catch (PropertyNotFoundException pnfe) {
2059
			throw new SAXException("Could not find inline data file path: "
2060
					+ pnfe.getMessage());
2061
		}
2062
		File inlineDataDirectory = new File(path);
2063
		File newFile = new File(inlineDataDirectory, fileName);
2064
		newFile.delete();
2065

    
2066
	}
2067

    
2068
	/*
2069
	 * In eml2, the inline data wouldn't store in db, it store in file system
2070
	 * The db stores file name(without path).
2071
	 */
2072
	public static Reader readInlineDataFromFileSystem(String fileName)
2073
			throws McdbException {
2074
		// BufferedReader stringReader = null;
2075
		FileReader fileReader = null;
2076
		try {
2077
			String path = PropertyService.getProperty("application.inlinedatafilepath");
2078
			// the new file name will look like path/docid.rev.2
2079
			File inlineDataDirectory = new File(path);
2080
			File dataFile = new File(inlineDataDirectory, fileName);
2081

    
2082
			fileReader = new FileReader(dataFile);
2083
			// stringReader = new BufferedReader(fileReader);
2084
		} catch (Exception e) {
2085
			throw new McdbException(e.getMessage());
2086
		}
2087
		// return stringReader;
2088
		return fileReader;
2089
	}
2090

    
2091
	/* Delete relations */
2092
	private void deleteRelations() throws SAXException {
2093
		PreparedStatement pStmt = null;
2094
		String sql = "DELETE FROM xml_relation where docid =?";
2095
		try {
2096
			pStmt = connection.prepareStatement(sql);
2097
			// bind variable
2098
			pStmt.setString(1, docid);
2099
			// execute query
2100
			pStmt.execute();
2101
			pStmt.close();
2102
		}// try
2103
		catch (SQLException e) {
2104
			throw new SAXException("EMLSAXHandler.deleteRelations(): " + e.getMessage());
2105
		}// catch
2106
		finally {
2107
			try {
2108
				pStmt.close();
2109
			}// try
2110
			catch (SQLException ee) {
2111
				throw new SAXException("EMLSAXHandler.deleteRelations: "
2112
						+ ee.getMessage());
2113
			}// catch
2114
		}// finally
2115
	}
2116

    
2117
	/*
2118
	 * Write an online data file id into xml_relation table. The dataId
2119
	 * shouldnot have the revision
2120
	 */
2121
	private void writeOnlineDataFileIdIntoRelationTable(String dataId)
2122
			throws SAXException {
2123
		PreparedStatement pStmt = null;
2124
		String sql = "INSERT into xml_relation (docid, packagetype, subject, "
2125
				+ "relationship, object) values (?, ?, ?, ?, ?)";
2126
		try {
2127
			pStmt = connection.prepareStatement(sql);
2128
			// bind variable
2129
			pStmt.setString(1, docid);
2130
			pStmt.setString(2, DocumentImpl.EML2_1_0NAMESPACE);
2131
			pStmt.setString(3, docid);
2132
			pStmt.setString(4, RELATION);
2133
			pStmt.setString(5, dataId);
2134
			// execute query
2135
			pStmt.execute();
2136
			pStmt.close();
2137
		}// try
2138
		catch (SQLException e) {
2139
			throw new SAXException(
2140
					"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2141
							+ e.getMessage());
2142
		}// catch
2143
		finally {
2144
			try {
2145
				pStmt.close();
2146
			}// try
2147
			catch (SQLException ee) {
2148
				throw new SAXException(
2149
						"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
2150
								+ ee.getMessage());
2151
			}// catch
2152
		}// finally
2153

    
2154
	}// writeOnlineDataFileIdIntoRelationTable
2155

    
2156
	/*
2157
	 * This method will handle data file in online url. If the data file is in
2158
	 * ecogrid protocol, then the datafile identifier(without rev)should be put
2159
	 * into onlineDataFileRelationVector. The docid in this vector will be
2160
	 * insert into xml_relation table in endDocument(). If the data file doesn't
2161
	 * exsit in xml_documents or xml_revision table, or the user has all
2162
	 * permission to the data file if the docid already existed, the data file
2163
	 * id (without rev)will be put into onlineDataFileTopAccessVector. The top
2164
	 * access rules specified in this eml document will apply to the data file.
2165
	 * NEED to do: We should also need to implement http and ftp. Those external
2166
	 * files should be download and assign a data file id to it.
2167
	 */
2168
	private void handleOnlineUrlDataFile(String url) throws SAXException {
2169
		logMetacat.warn("The url is " + url);
2170

    
2171
		if (currentDistributionSection == null) {
2172
			throw new SAXException("Trying to set the online file name for a null"
2173
					+ " distribution section");
2174
		}
2175

    
2176
		// if the url is not in ecogrid protocol, null will be returned
2177
		String accessionNumber = DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
2178
		if (accessionNumber == null) {
2179
			// the accession number is null if the url does not references a
2180
			// local data file (url would start with "ecogrid://"
2181
			currentDistributionSection
2182
					.setDistributionType(DistributionSection.ONLINE_DATA_DISTRIBUTION);
2183
		} else {
2184
			// handle ecogrid protocol
2185
			// get rid of revision number to get the docid.
2186
			String docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
2187

    
2188
			currentDistributionSection
2189
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
2190
			currentDistributionSection.setDataFileName(docid);
2191

    
2192
			// distributionOnlineFileName = docid;
2193
			onlineDataFileIdInRelationVector.add(docid);
2194
			try {				
2195
				if (!AccessionNumber.accNumberUsed(docid)) {
2196
					onlineDataFileIdInTopAccessVector.add(docid);
2197
				} else {
2198
					PermissionController controller = new PermissionController(accessionNumber);				
2199
					if (controller.hasPermission(user, groups,AccessControlInterface.ALLSTRING)) {
2200
						onlineDataFileIdInTopAccessVector.add(docid);
2201
					} else {
2202
						throw new SAXException(UPDATEACCESSERROR);
2203
					}
2204
				} 
2205
			}// try
2206
			catch (Exception e) {
2207
				logMetacat.error("Eorr in "
2208
								+ "Eml210SAXHanlder.handleOnlineUrlDataFile is "
2209
								+ e.getMessage());
2210
				throw new SAXException(e.getMessage());
2211
			}
2212
		}
2213
	}
2214
}
(37-37/63)