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-12-26 13:07:40 -0800 (Fri, 26 Dec 2008) $'
11
 * '$Revision: 4698 $'
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 AccessControlInterface {
59

    
60
	private boolean processTopLevelAccess = false;
61

    
62
	private boolean processAdditionalAccess = false;
63

    
64
	private boolean processOtherAccess = false;
65

    
66
	private AccessSection accessObject = null;
67

    
68
	private AccessRule accessRule = null;
69

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

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

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

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

    
84
	// every additionalmetadata
85
	private Vector<String> describesId = new Vector<String>(); // store the ids
86
																// in
87

    
88
	// additionalmetadata/describes
89
	private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
90

    
91
	private Vector<SubTree> subTreeList = new Vector<SubTree>();// store the
92
																// final subtree
93

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

    
107
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
108

    
109
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
110

    
111
	private AccessSection topAccessSection;
112

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

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

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

    
124
	// Indicator of inline data
125
	private boolean handleInlineData = false;
126

    
127
	private Hashtable<String, String> inlineDataNameSpace = null;
128

    
129
	private FileWriter inlineDataFileWriter = null;
130

    
131
	private String inlineDataFileName = null;
132

    
133
	DistributionSection currentDistributionSection = null;
134

    
135
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
136

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

    
143
	// private String distributionOnlineFileName = null;
144

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

    
148
	// Constant
149
	private static final String EML = "eml";
150

    
151
	private static final String DISTRIBUTION = "distribution";
152

    
153
	private static final String ORDER = "order";
154

    
155
	private static final String ID = "id";
156

    
157
	private static final String REFERENCES = "references";
158

    
159
	public static final String INLINE = "inline";
160

    
161
	private static final String ONLINE = "online";
162

    
163
	private static final String URL = "url";
164

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

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

    
172
	private static final String TOPLEVEL = "top";
173

    
174
	private static final String SUBTREELEVEL = "subtree";
175

    
176
	private static final String RELATION = "Provides info for";
177

    
178
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
179

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

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

    
223
		} catch (Exception e) {
224
			throw new SAXException(e.getMessage());
225
		}
226
	}
227

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

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

    
281
		try {
282

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

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

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

    
340
		DBSAXNode parentNode = null;
341
		DBSAXNode currentNode = null;
342

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

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

    
376
			}
377

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

    
390
				// compare top level access module
391
				if (processTopLevelAccess && needToCheckAccessModule) {
392
					compareTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer,
393
							UPDATEACCESSERROR);
394
				}
395

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

    
403
				}
404

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

    
413
			}
414

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

    
447
					try {
448
						// Get dbconnection
449
						dbConn = DBConnectionPool
450
								.getDBConnection("DBSAXHandler.startElement");
451
						serialNumber = dbConn.getCheckOutSerialNumber();
452

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

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

    
478
				} catch (Exception ane) {
479
					throw (new SAXException("Error in EMLSaxHandler.startElement "
480
							+ action, ane));
481
				}
482
			}
483

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

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

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

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

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

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

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

    
591
				accessRule = new AccessRule();
592

    
593
				// set permission type "allow"
594
				accessRule.setPermissionType(ALLOW);
595

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

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

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

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

    
635
			}
636

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

    
652
			}
653

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

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

    
698
	}
699

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

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

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

    
762
		}// while
763

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

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

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

    
799
	}
800

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

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

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

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

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

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

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

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

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

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

    
1006
				}
1007
			}// if
1008

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

    
1019
			// set hitText false
1020
			hitTextNode = false;
1021
			// reset textbuff
1022
			textBuffer = null;
1023
			textBuffer = new StringBuffer();
1024

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

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

    
1052
				accessObject.setEndNodeId(endNodeId);
1053

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

    
1060
				AccessSection newAccessObject = accessObject;
1061

    
1062
				if (newAccessObject != null) {
1063

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

    
1068
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
1069
						accessObjectList.add(newAccessObject);
1070
					}
1071
					if (processTopLevelAccess) {
1072

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

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

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

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

    
1100
				}// if
1101
				// check if access node stack is empty after parsing top access
1102
				// module
1103

    
1104
				if (needToCheckAccessModule && processTopLevelAccess
1105
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
1106

    
1107
					logMetacat.error("Access node stack is not empty after "
1108
							+ "parsing access subtree");
1109
					throw new SAXException(UPDATEACCESSERROR);
1110

    
1111
				}
1112
				// reset access section object
1113

    
1114
				accessObject = null;
1115

    
1116
				// reset tmp stored node stack
1117
				storedAccessNodeStack = null;
1118
				storedAccessNodeStack = new Stack<NodeRecord>();
1119

    
1120
				// reset flag
1121
				processAdditionalAccess = false;
1122
				processTopLevelAccess = false;
1123
				processOtherAccess = false;
1124

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

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

    
1172
				// MCD - Removed the following code. It is used for node level
1173
				// access
1174
				// control which is not yet implemented in EML 2.1.0
1175
				// //compare comment if need
1176
				// if (startCriticalSubTree) {
1177
				// compareCommentNode(currentUnChangeableSubtreeNodeStack,
1178
				// str, PERMISSIONERROR);
1179
				// }//if
1180

    
1181
				// compare top level access module
1182
				if (processTopLevelAccess && needToCheckAccessModule) {
1183
					compareCommentNode(currentUnchangeableAccessModuleNodeStack, str,
1184
							UPDATEACCESSERROR);
1185
				}
1186
				endNodeId = currentNode.writeChildNodeToDB("COMMENT", null, str, docid);
1187
				if (needToCheckAccessModule
1188
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1189
					// stored the pull out nodes into storedNode stack
1190
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "COMMENT", null,
1191
							null, MetacatUtil.normalize(str));
1192
					storedAccessNodeStack.push(nodeElement);
1193

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

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

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

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

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

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

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

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

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

    
1327
	}
1328

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

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

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

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

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

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

    
1413
			} else {
1414

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

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

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

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

    
1487
	}// writeTopLevelAccessRuletoDB
1488

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

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

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

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

    
1563
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1564
					try {
1565
						PermissionController controller = new PermissionController(
1566
								distributionSection.getDataFileName(), false);
1567

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

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

    
1603
		}
1604
		
1605
		
1606
	}
1607

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

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

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

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

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

    
1749
				pstmt.setString(7, subSectionId);
1750
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1751
			}
1752

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

    
1788
	}// writeGivenAccessRuleIntoDB
1789

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

    
1804
		try {
1805

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

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

    
1853
	}// writeAccessRuleForRalatedDataFileIntoDB
1854

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

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

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

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

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

    
1931
	}// writeAccessSubtreeIntoDB
1932

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

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

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

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

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

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

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

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

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

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

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

    
2063
	}
2064

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

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

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

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

    
2151
	}// writeOnlineDataFileIdIntoRelationTable
2152

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

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

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

    
2185
			currentDistributionSection
2186
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
2187
			currentDistributionSection.setDataFileName(docid);
2188

    
2189
			// distributionOnlineFileName = docid;
2190
			onlineDataFileIdInRelationVector.add(docid);
2191
			try {
2192

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