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-10-16 11:10:37 -0700 (Fri, 16 Oct 2009) $'
11
 * '$Revision: 5090 $'
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.accesscontrol.AccessControlInterface;
51
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
52
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
53
import edu.ucsb.nceas.metacat.database.DBConnection;
54
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
55
import edu.ucsb.nceas.metacat.properties.PropertyService;
56
import edu.ucsb.nceas.metacat.util.DocumentUtil;
57
import edu.ucsb.nceas.metacat.util.MetacatUtil;
58
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
59

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

    
66
	private boolean processTopLevelAccess = false;
67

    
68
	private boolean processAdditionalAccess = false;
69

    
70
	private boolean processOtherAccess = false;
71

    
72
	private AccessSection accessObject = null;
73

    
74
	private AccessRule accessRule = null;
75

    
76
	private Vector<AccessSection> accessObjectList = new Vector<AccessSection>(); // store
77
																					// every
78
																					// access
79
																					// rule
80

    
81
	private Hashtable<String, AccessSection> topLevelAccessControlMap = new Hashtable<String, AccessSection>();
82

    
83
	private Hashtable<String, AccessSection> additionalAccessControlMap = new Hashtable<String, AccessSection>();// store
84

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

    
90
	// every additionalmetadata
91
	private Vector<String> describesId = new Vector<String>(); // store the ids
92
																// in
93

    
94
	// additionalmetadata/describes
95
	private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
96

    
97
	private Vector<SubTree> subTreeList = new Vector<SubTree>();// store the
98
																// final subtree
99

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

    
113
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
114

    
115
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
116

    
117
	private AccessSection topAccessSection;
118

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

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

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

    
130
	// Indicator of inline data
131
	private boolean handleInlineData = false;
132

    
133
	private Hashtable<String, String> inlineDataNameSpace = null;
134

    
135
	private FileWriter inlineDataFileWriter = null;
136

    
137
	private String inlineDataFileName = null;
138

    
139
	DistributionSection currentDistributionSection = null;
140

    
141
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
142

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

    
149
	// private String distributionOnlineFileName = null;
150

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

    
154
	// Constant
155
	private static final String EML = "eml";
156

    
157
	private static final String DISTRIBUTION = "distribution";
158

    
159
	private static final String ORDER = "order";
160

    
161
	private static final String ID = "id";
162

    
163
	private static final String REFERENCES = "references";
164

    
165
	public static final String INLINE = "inline";
166

    
167
	private static final String ONLINE = "online";
168

    
169
	private static final String URL = "url";
170

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

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

    
178
	private static final String TOPLEVEL = "top";
179

    
180
	private static final String SUBTREELEVEL = "subtree";
181

    
182
	private static final String RELATION = "Provides info for";
183

    
184
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
185

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

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

    
229
		} catch (Exception e) {
230
			throw new SAXException(e.getMessage());
231
		}
232
	}
233

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

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

    
287
		try {
288

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

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

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

    
346
		DBSAXNode parentNode = null;
347
		DBSAXNode currentNode = null;
348

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

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

    
382
			}
383

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

    
396
				// compare top level access module
397
				if (processTopLevelAccess && needToCheckAccessModule) {
398
					compareTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer,
399
							UPDATEACCESSERROR);
400
				}
401

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

    
409
				}
410

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

    
419
			}
420

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

    
453
					try {
454
						// Get dbconnection
455
						dbConn = DBConnectionPool
456
								.getDBConnection("DBSAXHandler.startElement");
457
						serialNumber = dbConn.getCheckOutSerialNumber();
458

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

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

    
484
				} catch (Exception ane) {
485
					throw (new SAXException("Error in EMLSaxHandler.startElement "
486
							+ action, ane));
487
				}
488
			}
489

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

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

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

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

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

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

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

    
597
				accessRule = new AccessRule();
598

    
599
				// set permission type "allow"
600
				accessRule.setPermissionType(ALLOW);
601

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

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

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

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

    
641
			}
642

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

    
658
			}
659

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

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

    
704
	}
705

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

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

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

    
768
		}// while
769

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

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

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

    
805
	}
806

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

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

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

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

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

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

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

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

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

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

    
1012
				}
1013
			}// if
1014

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

    
1025
			// set hitText false
1026
			hitTextNode = false;
1027
			// reset textbuff
1028
			textBuffer = null;
1029
			textBuffer = new StringBuffer();
1030

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

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

    
1058
				accessObject.setEndNodeId(endNodeId);
1059

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

    
1066
				AccessSection newAccessObject = accessObject;
1067

    
1068
				if (newAccessObject != null) {
1069

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

    
1074
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
1075
						accessObjectList.add(newAccessObject);
1076
					}
1077
					if (processTopLevelAccess) {
1078

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

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

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

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

    
1106
				}// if
1107
				// check if access node stack is empty after parsing top access
1108
				// module
1109

    
1110
				if (needToCheckAccessModule && processTopLevelAccess
1111
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
1112

    
1113
					logMetacat.error("Access node stack is not empty after "
1114
							+ "parsing access subtree");
1115
					throw new SAXException(UPDATEACCESSERROR);
1116

    
1117
				}
1118
				// reset access section object
1119

    
1120
				accessObject = null;
1121

    
1122
				// reset tmp stored node stack
1123
				storedAccessNodeStack = null;
1124
				storedAccessNodeStack = new Stack<NodeRecord>();
1125

    
1126
				// reset flag
1127
				processAdditionalAccess = false;
1128
				processTopLevelAccess = false;
1129
				processOtherAccess = false;
1130

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1333
	}
1334

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

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

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

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

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

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

    
1419
			} else {
1420

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

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

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

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

    
1493
	}// writeTopLevelAccessRuletoDB
1494

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

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

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

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

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

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

    
1609
		}
1610
		
1611
		
1612
	}
1613

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

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

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

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

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

    
1755
				pstmt.setString(7, subSectionId);
1756
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1757
			}
1758

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

    
1794
	}// writeGivenAccessRuleIntoDB
1795

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

    
1810
		try {
1811

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

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

    
1859
	}// writeAccessRuleForRalatedDataFileIntoDB
1860

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

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

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

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

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

    
1937
	}// writeAccessSubtreeIntoDB
1938

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

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

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

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

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

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

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

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

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

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

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

    
2069
	}
2070

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

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

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

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

    
2157
	}// writeOnlineDataFileIdIntoRelationTable
2158

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

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

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

    
2191
			currentDistributionSection
2192
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
2193
			currentDistributionSection.setDataFileName(docid);
2194

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