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-04 14:32:58 -0700 (Tue, 04 Aug 2009) $'
11
 * '$Revision: 5015 $'
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.service.PropertyService;
53
import edu.ucsb.nceas.metacat.util.MetacatUtil;
54
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
55

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

    
62
	private boolean processTopLevelAccess = false;
63

    
64
	private boolean processAdditionalAccess = false;
65

    
66
	private boolean processOtherAccess = false;
67

    
68
	private AccessSection accessObject = null;
69

    
70
	private AccessRule accessRule = null;
71

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

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

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

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

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

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

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

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

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

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

    
113
	private AccessSection topAccessSection;
114

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

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

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

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

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

    
131
	private FileWriter inlineDataFileWriter = null;
132

    
133
	private String inlineDataFileName = null;
134

    
135
	DistributionSection currentDistributionSection = null;
136

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

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

    
145
	// private String distributionOnlineFileName = null;
146

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
283
		try {
284

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

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

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

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

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

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

    
378
			}
379

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

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

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

    
405
				}
406

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

    
415
			}
416

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

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

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

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

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

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

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

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

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

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

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

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

    
593
				accessRule = new AccessRule();
594

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

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

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

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

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

    
637
			}
638

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

    
654
			}
655

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

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

    
700
	}
701

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

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

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

    
764
		}// while
765

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

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

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

    
801
	}
802

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

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

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

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

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

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

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

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

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

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

    
1008
				}
1009
			}// if
1010

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

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

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

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

    
1054
				accessObject.setEndNodeId(endNodeId);
1055

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

    
1062
				AccessSection newAccessObject = accessObject;
1063

    
1064
				if (newAccessObject != null) {
1065

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

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

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

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

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

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

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

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

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

    
1113
				}
1114
				// reset access section object
1115

    
1116
				accessObject = null;
1117

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1329
	}
1330

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

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

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

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

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

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

    
1415
			} else {
1416

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

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

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

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

    
1489
	}// writeTopLevelAccessRuletoDB
1490

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

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

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

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

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

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

    
1605
		}
1606
		
1607
		
1608
	}
1609

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

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

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

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

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

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

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

    
1790
	}// writeGivenAccessRuleIntoDB
1791

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

    
1806
		try {
1807

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

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

    
1855
	}// writeAccessRuleForRalatedDataFileIntoDB
1856

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

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

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

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

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

    
1933
	}// writeAccessSubtreeIntoDB
1934

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

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

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

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

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

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

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

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

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

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

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

    
2065
	}
2066

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

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

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

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

    
2153
	}// writeOnlineDataFileIdIntoRelationTable
2154

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

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

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

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

    
2191
			// distributionOnlineFileName = docid;
2192
			onlineDataFileIdInRelationVector.add(docid);
2193
			try {				
2194
				if (!AccessionNumber.accNumberUsed(docid)) {
2195
					onlineDataFileIdInTopAccessVector.add(docid);
2196
				} else {
2197
					PermissionController controller = new PermissionController(accessionNumber);				
2198
					if (controller.hasPermission(user, groups,AccessControlInterface.ALLSTRING)) {
2199
						onlineDataFileIdInTopAccessVector.add(docid);
2200
					} else {
2201
						throw new SAXException(UPDATEACCESSERROR);
2202
					}
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
}
(37-37/63)