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-12-18 13:36:43 -0800 (Fri, 18 Dec 2009) $'
11
 * '$Revision: 5166 $'
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
	private boolean needToCheckAccessModule = false;
101

    
102
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
103

    
104
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
105

    
106
	private AccessSection topAccessSection;
107

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

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

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

    
119
	// Indicator of inline data
120
	private boolean handleInlineData = false;
121

    
122
	private Hashtable<String, String> inlineDataNameSpace = null;
123

    
124
	private FileWriter inlineDataFileWriter = null;
125

    
126
	private String inlineDataFileName = null;
127

    
128
	DistributionSection currentDistributionSection = null;
129

    
130
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
131

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

    
138
	// private String distributionOnlineFileName = null;
139

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

    
143
	// Constant
144
	private static final String EML = "eml";
145

    
146
	private static final String DISTRIBUTION = "distribution";
147

    
148
	private static final String ORDER = "order";
149

    
150
	private static final String ID = "id";
151

    
152
	private static final String REFERENCES = "references";
153

    
154
	public static final String INLINE = "inline";
155

    
156
	private static final String ONLINE = "online";
157

    
158
	private static final String URL = "url";
159

    
160
	// private static final String PERMISSIONERROR = "User tried to update a
161
	// subtree "
162
	// + "when they don't have write permission!";
163

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

    
167
	private static final String TOPLEVEL = "top";
168

    
169
	private static final String SUBTREELEVEL = "subtree";
170

    
171
	private static final String RELATION = "Provides info for";
172

    
173
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
174

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

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

    
218
		} catch (Exception e) {
219
			throw new SAXException(e.getMessage());
220
		}
221
	}
222

    
223
	/*
224
	 * Get the subtree node info from xml_accesssubtree table
225
	 */
226
	private Vector<AccessSection> getAccessSubTreeListFromDB() throws Exception {
227
		Vector<AccessSection> result = new Vector<AccessSection>();
228
		PreparedStatement pstmt = null;
229
		ResultSet rs = null;
230
		String sql = "SELECT controllevel, subtreeid, startnodeid, endnodeid "
231
				+ "FROM xml_accesssubtree WHERE docid like ? "
232
				+ "ORDER BY startnodeid ASC";
233

    
234
		try {
235

    
236
			pstmt = connection.prepareStatement(sql);
237
			// Increase DBConnection usage count
238
			connection.increaseUsageCount(1);
239
			// Bind the values to the query
240
			pstmt.setString(1, docid);
241
			pstmt.execute();
242

    
243
			// Get result set
244
			rs = pstmt.getResultSet();
245
			while (rs.next()) {
246
				String level = rs.getString(1);
247
				String sectionId = rs.getString(2);
248
				long startNodeId = rs.getLong(3);
249
				long endNodeId = rs.getLong(4);
250
				// create a new access section
251
				AccessSection accessObj = new AccessSection();
252
				accessObj.setControlLevel(level);
253
				accessObj.setDocId(docid);
254
				accessObj.setSubTreeId(sectionId);
255
				accessObj.setStartNodeId(startNodeId);
256
				accessObj.setEndNodeId(endNodeId);
257
				Stack<NodeRecord> nodeStack = accessObj.getSubTreeNodeStack();
258
				accessObj.setSubTreeNodeStack(nodeStack);
259
				// add this access obj into vector
260
				result.add(accessObj);
261
				// Get the top level access subtree control
262
				if (level != null && level.equals(TOPLEVEL)) {
263
					topAccessSection = accessObj;
264
				}
265
			}
266
			pstmt.close();
267
		}// try
268
		catch (SQLException e) {
269
			throw new SAXException("EMLSAXHandler.getAccessSubTreeListFromDB(): "
270
					+ e.getMessage());
271
		}// catch
272
		finally {
273
			try {
274
				pstmt.close();
275
			} catch (SQLException ee) {
276
				throw new SAXException("EMLSAXHandler.getAccessSubTreeListFromDB(): "
277
						+ ee.getMessage());
278
			}
279
		}// finally
280
		return result;
281
	}
282

    
283
	/** SAX Handler that is called at the start of each XML element */
284
	public void startElement(String uri, String localName, String qName, Attributes atts)
285
			throws SAXException {
286
		// for element <eml:eml...> qname is "eml:eml", local name is "eml"
287
		// for element <acl....> both qname and local name is "eml"
288
		// uri is namesapce
289
		logMetacat.debug("Start ELEMENT(qName) " + qName);
290
		logMetacat.debug("Start ELEMENT(localName) " + localName);
291
		logMetacat.debug("Start ELEMENT(uri) " + uri);
292

    
293
		DBSAXNode parentNode = null;
294
		DBSAXNode currentNode = null;
295

    
296
		if (!handleInlineData) {
297
			// Get a reference to the parent node for the id
298
			try {
299
				parentNode = (DBSAXNode) nodeStack.peek();
300
			} catch (EmptyStackException e) {
301
				parentNode = null;
302
			}
303

    
304
			// start handle inline data
305
			if (localName.equals(INLINE)) {
306
				handleInlineData = true;
307
				// initialize namespace hash for in line data
308
				inlineDataNameSpace = new Hashtable<String, String>();
309
				// initialize file writer
310
				String docidWithoutRev = DocumentUtil.getDocIdFromString(docid);
311
				String seperator = ".";
312
				try {
313
					seperator = PropertyService.getProperty("document.accNumSeparator");
314
				} catch (PropertyNotFoundException pnfe) {
315
					logMetacat.error("Could not fing property 'accNumSeparator'. "
316
							+ "Setting separator to '.': " + pnfe.getMessage());
317
				}
318
				// the new file name will look like docid.rev.2
319
				inlineDataFileName = docidWithoutRev + seperator + revision + seperator
320
						+ distributionIndex;
321
				inlineDataFileWriter = createInlineDataFileWriter(inlineDataFileName);
322
				// put the inline file id into a vector. If upload failed,
323
				// metacat will delete the inline data file
324
				inlineFileIdList.add(inlineDataFileName);
325
				
326
				currentDistributionSection.setDistributionType(DistributionSection.INLINE_DATA_DISTRIBUTION);
327
				currentDistributionSection.setDataFileName(inlineDataFileName);
328

    
329
			}
330

    
331
			// If hit a text node, we need write this text for current's parent
332
			// node This will happen if the element is mixed
333
			if (hitTextNode && parentNode != null) {
334

    
335
				// compare top level access module
336
				if (processTopLevelAccess && needToCheckAccessModule) {
337
					compareTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer,
338
							UPDATEACCESSERROR);
339
				}
340

    
341
				if (needToCheckAccessModule
342
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
343
					// stored the pull out nodes into storedNode stack
344
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
345
							null, MetacatUtil.normalize(textBuffer.toString()));
346
					storedAccessNodeStack.push(nodeElement);
347

    
348
				}
349

    
350
				// write the textbuffer into db for parent node.
351
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, parentNode);
352
				// rest hitTextNode
353
				hitTextNode = false;
354
				// reset textbuffer
355
				textBuffer = null;
356
				textBuffer = new StringBuffer();
357

    
358
			}
359

    
360
			// Document representation that points to the root document node
361
			if (atFirstElement) {
362
				atFirstElement = false;
363
				// If no DOCTYPE declaration: docname = root element
364
				// doctype = root element name or name space
365
				if (docname == null) {
366
					docname = localName;
367
					// if uri isn't null doctype = uri(namespace)
368
					// othewise root element
369
					if (uri != null && !(uri.trim()).equals("")) {
370
						doctype = uri;
371
					} else {
372
						doctype = docname;
373
					}
374
					logMetacat.debug("DOCNAME-a: " + docname);
375
					logMetacat.debug("DOCTYPE-a: " + doctype);
376
				} else if (doctype == null) {
377
					// because docname is not null and it is declared in dtd
378
					// so could not be in schema, no namespace
379
					doctype = docname;
380
					logMetacat.debug("DOCTYPE-b: " + doctype);
381
				}
382
				rootNode.writeNodename(docname);
383
				try {
384
					// for validated XML Documents store a reference to XML DB
385
					// Catalog. Because this is select statement and it needn't
386
					// roll back if insert document action failed. In order to
387
					// decrease DBConnection usage count, we get a new
388
					// dbconnection from pool String catalogid = null;
389
					DBConnection dbConn = null;
390
					int serialNumber = -1;
391

    
392
					try {
393
						// Get dbconnection
394
						dbConn = DBConnectionPool
395
								.getDBConnection("DBSAXHandler.startElement");
396
						serialNumber = dbConn.getCheckOutSerialNumber();
397

    
398
						Statement stmt = dbConn.createStatement();
399
						ResultSet rs = stmt
400
								.executeQuery("SELECT catalog_id FROM xml_catalog "
401
										+ "WHERE entry_type = 'Schema' "
402
										+ "AND public_id = '" + doctype + "'");
403
						boolean hasRow = rs.next();
404
						if (hasRow) {
405
							catalogid = rs.getString(1);
406
						}
407
						stmt.close();
408
					}// try
409
					finally {
410
						// Return dbconnection
411
						DBConnectionPool.returnDBConnection(dbConn, serialNumber);
412
					}// finally
413

    
414
					// create documentImpl object by the constructor which can
415
					// specify the revision
416
					if (!super.getIsRevisionDoc()) {
417
						currentDocument = new DocumentImpl(connection, rootNode
418
								.getNodeID(), docname, doctype, docid, revision, action,
419
								user, this.pub, catalogid, this.serverCode, createDate,
420
								updateDate);
421
					}
422

    
423
				} catch (Exception ane) {
424
					throw (new SAXException("Error in EMLSaxHandler.startElement "
425
							+ action, ane));
426
				}
427
			}
428

    
429
			// Create the current node representation
430
			currentNode = new DBSAXNode(connection, qName, localName, parentNode,
431
					rootNode.getNodeID(), docid, doctype);
432
			// Use a local variable to store the element node id
433
			// If this element is a start point of subtree(section), it will be
434
			// stored otherwise, it will be discarded
435
			long startNodeId = currentNode.getNodeID();
436
			// Add all of the namespaces
437
			String prefix = null;
438
			String nsuri = null;
439
			Enumeration<String> prefixes = namespaces.keys();
440
			while (prefixes.hasMoreElements()) {
441
				prefix = prefixes.nextElement();
442
				nsuri = namespaces.get(prefix);
443
				endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
444
			}
445

    
446
			// Add all of the attributes
447
			for (int i = 0; i < atts.getLength(); i++) {
448
				String attributeName = atts.getQName(i);
449
				String attributeValue = atts.getValue(i);
450
				endNodeId = currentNode
451
						.setAttribute(attributeName, attributeValue, docid);
452

    
453
				// To handle name space and schema location if the attribute
454
				// name is xsi:schemaLocation. If the name space is in not
455
				// in catalog table it will be registered.
456
				if (attributeName != null
457
						&& attributeName.indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
458
					SchemaLocationResolver resolver = new SchemaLocationResolver(
459
							attributeValue);
460
					resolver.resolveNameSpace();
461

    
462
				} else if (attributeName != null && attributeName.equals(ID)) {
463

    
464
				}
465
			}// for
466

    
467
			// handle access stuff
468
			if (localName.equals(ACCESS)) {
469
				if (parentNode.getTagName().equals(EML)) {
470
					processTopLevelAccess = true;
471
				} else if (parentNode.getTagName() == DISTRIBUTION) {
472
					processAdditionalAccess = true;
473
				} else {
474
					// process other access embedded into resource level
475
					// module
476
					processOtherAccess = true;
477
				}
478
				// create access object
479
				accessObject = new AccessSection();
480
				// set permission order
481
				String permOrder = currentNode.getAttribute(ORDER);
482
				accessObject.setPermissionOrder(permOrder);
483
				// set access id
484
				String accessId = currentNode.getAttribute(ID);
485
				accessObject.setSubTreeId(accessId);
486
				accessObject.setStartNodeId(startNodeId);
487
				accessObject.setDocId(docid);
488
				if (processAdditionalAccess) {
489
					accessObject.setDataFileName(inlineDataFileName);
490
				}
491

    
492
				// load top level node stack to
493
				// currentUnchangableAccessModuleNodeStack
494
				if (processTopLevelAccess && needToCheckAccessModule) {
495
					// get the node stack for
496
					currentUnchangeableAccessModuleNodeStack = topAccessSection
497
							.getSubTreeNodeStack();
498
				}
499
			} else if (localName.equals(DISTRIBUTION)) {
500
				distributionIndex++;
501
				currentDistributionSection = new DistributionSection(distributionIndex);
502

    
503
				// handle subtree info
504
				SubTree subTree = new SubTree();
505
				// set sub tree id
506
				subTree.setSubTreeId(String.valueOf(distributionIndex));
507
				// set sub tree start element name
508
				subTree.setStartElementName(currentNode.getTagName());
509
				// set start node number
510
				subTree.setStartNodeId(startNodeId);
511
				// add to stack, but it didn't get end node id yet
512
				subTreeInfoStack.push(subTree);
513
			}
514
			// Set up a access rule for allow
515
			else if (parentNode.getTagName() != null
516
					&& (parentNode.getTagName()).equals(ACCESS)
517
					&& localName.equals(ALLOW)) {
518

    
519
				accessRule = new AccessRule();
520

    
521
				// set permission type "allow"
522
				accessRule.setPermissionType(ALLOW);
523

    
524
			}
525
			// set up an access rule for deny
526
			else if (parentNode.getTagName() != null
527
					&& (parentNode.getTagName()).equals(ACCESS) && localName.equals(DENY)) {
528
				accessRule = new AccessRule();
529
				// set permission type "allow"
530
				accessRule.setPermissionType(DENY);
531
			}
532

    
533
			// Add the node to the stack, so that any text data can be
534
			// added as it is encountered
535
			nodeStack.push(currentNode);
536
			// Add the node to the vector used by thread for writing XML Index
537
			nodeIndex.addElement(currentNode);
538

    
539
			// compare top access level module
540
			if (processTopLevelAccess && needToCheckAccessModule) {
541
				compareElementNameSpaceAttributes(
542
						currentUnchangeableAccessModuleNodeStack, namespaces, atts,
543
						localName, UPDATEACCESSERROR);
544

    
545
			}
546

    
547
			// store access module element and attributes into stored stack
548
			if (needToCheckAccessModule
549
					&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
550
				// stored the pull out nodes into storedNode stack
551
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT", localName,
552
						prefix, MetacatUtil.normalize(null));
553
				storedAccessNodeStack.push(nodeElement);
554
				for (int i = 0; i < atts.getLength(); i++) {
555
					String attributeName = atts.getQName(i);
556
					String attributeValue = atts.getValue(i);
557
					NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2, "ATTRIBUTE",
558
							attributeName, null, MetacatUtil.normalize(attributeValue));
559
					storedAccessNodeStack.push(nodeAttribute);
560
				}
561

    
562
			}
563

    
564
			// reset name space
565
			namespaces = null;
566
			namespaces = new Hashtable<String, String>();
567
		}// not inline data
568
		else {
569
			// we don't buffer the inline data in characters() method
570
			// so start character don't need to hand text node.
571

    
572
			// inline data may be the xml format.
573
			StringBuffer inlineElements = new StringBuffer();
574
			inlineElements.append("<").append(qName);
575
			// append attributes
576
			for (int i = 0; i < atts.getLength(); i++) {
577
				String attributeName = atts.getQName(i);
578
				String attributeValue = atts.getValue(i);
579
				inlineElements.append(" ");
580
				inlineElements.append(attributeName);
581
				inlineElements.append("=\"");
582
				inlineElements.append(attributeValue);
583
				inlineElements.append("\"");
584
			}
585
			// append namespace
586
			String prefix = null;
587
			String nsuri = null;
588
			Enumeration<String> prefixes = inlineDataNameSpace.keys();
589
			while (prefixes.hasMoreElements()) {
590
				prefix = prefixes.nextElement();
591
				nsuri = namespaces.get(prefix);
592
				inlineElements.append(" ");
593
				inlineElements.append("xmlns:");
594
				inlineElements.append(prefix);
595
				inlineElements.append("=\"");
596
				inlineElements.append(nsuri);
597
				inlineElements.append("\"");
598
			}
599
			inlineElements.append(">");
600
			// reset inline data name space
601
			inlineDataNameSpace = null;
602
			inlineDataNameSpace = new Hashtable<String, String>();
603
			// write inline data into file
604
			logMetacat.debug("the inline element data is: " + inlineElements.toString());
605
			writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
606
		}// else
607

    
608
	}
609

    
610
	private void compareElementNameSpaceAttributes(
611
			Stack<NodeRecord> unchangeableNodeStack,
612
			Hashtable<String, String> nameSpaces, Attributes attributes,
613
			String localName, String error) throws SAXException {
614
		// Get element subtree node stack (element node)
615
		NodeRecord elementNode = null;
616
		try {
617
			elementNode = unchangeableNodeStack.pop();
618
		} catch (EmptyStackException ee) {
619
			logMetacat.error("Node stack is empty for element data");
620
			throw new SAXException(error);
621
		}
622
		logMetacat.debug("current node type from xml is ELEMENT");
623
		logMetacat.debug("node type from stack: " + elementNode.getNodeType());
624
		logMetacat.debug("node name from xml document: " + localName);
625
		logMetacat.debug("node name from stack: " + elementNode.getNodeName());
626
		logMetacat.debug("node data from stack: " + elementNode.getNodeData());
627
		logMetacat.debug("node id is: " + elementNode.getNodeId());
628
		// if this node is not element or local name not equal or name space
629
		// not equals, throw an exception
630
		if (!elementNode.getNodeType().equals("ELEMENT")
631
				|| !localName.equals(elementNode.getNodeName()))
632
		// (uri != null && !uri.equals(elementNode.getNodePrefix())))
633
		{
634
			logMetacat.error("Inconsistence happened: ");
635
			logMetacat.error("current node type from xml is ELEMENT");
636
			logMetacat.error("node type from stack: " + elementNode.getNodeType());
637
			logMetacat.error("node name from xml document: " + localName);
638
			logMetacat.error("node name from stack: " + elementNode.getNodeName());
639
			logMetacat.error("node data from stack: " + elementNode.getNodeData());
640
			logMetacat.error("node id is: " + elementNode.getNodeId());
641
			throw new SAXException(error);
642
		}
643

    
644
		// compare namespace
645
		Enumeration<String> nameEn = nameSpaces.keys();
646
		while (nameEn.hasMoreElements()) {
647
			// Get namespacke node stack (element node)
648
			NodeRecord nameNode = null;
649
			try {
650
				nameNode = unchangeableNodeStack.pop();
651
			} catch (EmptyStackException ee) {
652
				logMetacat.error("Node stack is empty for namespace data");
653
				throw new SAXException(error);
654
			}
655

    
656
			String prefixName = nameEn.nextElement();
657
			String nameSpaceUri = nameSpaces.get(prefixName);
658
			if (!nameNode.getNodeType().equals("NAMESPACE")
659
					|| !prefixName.equals(nameNode.getNodeName())
660
					|| !nameSpaceUri.equals(nameNode.getNodeData())) {
661
				logMetacat.error("Inconsistence happened: ");
662
				logMetacat.error("current node type from xml is NAMESPACE");
663
				logMetacat.error("node type from stack: " + nameNode.getNodeType());
664
				logMetacat.error("current node name from xml is: " + prefixName);
665
				logMetacat.error("node name from stack: " + nameNode.getNodeName());
666
				logMetacat.error("current node data from xml is: " + nameSpaceUri);
667
				logMetacat.error("node data from stack: " + nameNode.getNodeData());
668
				logMetacat.error("node id is: " + nameNode.getNodeId());
669
				throw new SAXException(error);
670
			}
671

    
672
		}// while
673

    
674
		// compare attributes
675
		for (int i = 0; i < attributes.getLength(); i++) {
676
			NodeRecord attriNode = null;
677
			try {
678
				attriNode = unchangeableNodeStack.pop();
679

    
680
			} catch (EmptyStackException ee) {
681
				logMetacat.error("Node stack is empty for attribute data");
682
				throw new SAXException(error);
683
			}
684
			String attributeName = attributes.getQName(i);
685
			String attributeValue = attributes.getValue(i);
686
			logMetacat.debug("current node type from xml is ATTRIBUTE ");
687
			logMetacat.debug("node type from stack: " + attriNode.getNodeType());
688
			logMetacat.debug("current node name from xml is: " + attributeName);
689
			logMetacat.debug("node name from stack: " + attriNode.getNodeName());
690
			logMetacat.debug("current node data from xml is: " + attributeValue);
691
			logMetacat.debug("node data from stack: " + attriNode.getNodeData());
692
			logMetacat.debug("node id  is: " + attriNode.getNodeId());
693

    
694
			if (!attriNode.getNodeType().equals("ATTRIBUTE")
695
					|| !attributeName.equals(attriNode.getNodeName())
696
					|| !attributeValue.equals(attriNode.getNodeData())) {
697
				logMetacat.error("Inconsistence happened: ");
698
				logMetacat.error("current node type from xml is ATTRIBUTE ");
699
				logMetacat.error("node type from stack: " + attriNode.getNodeType());
700
				logMetacat.error("current node name from xml is: " + attributeName);
701
				logMetacat.error("node name from stack: " + attriNode.getNodeName());
702
				logMetacat.error("current node data from xml is: " + attributeValue);
703
				logMetacat.error("node data from stack: " + attriNode.getNodeData());
704
				logMetacat.error("node is: " + attriNode.getNodeId());
705
				throw new SAXException(error);
706
			}
707
		}// for
708

    
709
	}
710

    
711
	/* method to compare current text node and node in db */
712
	private void compareTextNode(Stack<NodeRecord> nodeStack, StringBuffer text,
713
			String error) throws SAXException {
714
		NodeRecord node = null;
715
		// get node from current stack
716
		try {
717
			node = nodeStack.pop();
718
		} catch (EmptyStackException ee) {
719
			logMetacat.error("Node stack is empty for text data in startElement");
720
			throw new SAXException(error);
721
		}
722
		logMetacat.debug("current node type from xml is TEXT in start element");
723
		logMetacat.debug("node type from stack: " + node.getNodeType());
724
		logMetacat.debug("current node data from xml is: " + text.toString());
725
		logMetacat.debug("node data from stack: " + node.getNodeData());
726
		logMetacat.debug("node name from stack: " + node.getNodeName());
727
		logMetacat.debug("node is: " + node.getNodeId());
728
		if (!node.getNodeType().equals("TEXT")
729
				|| !(text.toString()).equals(node.getNodeData())) {
730
			logMetacat.error("Inconsistence happened: ");
731
			logMetacat.error("current node type from xml is TEXT in start element");
732
			logMetacat.error("node type from stack: " + node.getNodeType());
733
			logMetacat.error("current node data from xml is: " + text.toString());
734
			logMetacat.error("node data from stack: " + node.getNodeData());
735
			logMetacat.error("node name from stack: " + node.getNodeName());
736
			logMetacat.error("node is: " + node.getNodeId());
737
			throw new SAXException(error);
738
		}// if
739
	}
740

    
741
	/** SAX Handler that is called for each XML text node */
742
	public void characters(char[] cbuf, int start, int len) throws SAXException {
743
		logMetacat.debug("CHARACTERS");
744
		if (!handleInlineData) {
745
			// buffer all text nodes for same element. This is for if text was
746
			// split into different nodes
747
			textBuffer.append(new String(cbuf, start, len));
748
			// set hittextnode true
749
			hitTextNode = true;
750
			// if text buffer .size is greater than max, write it to db.
751
			// so we can save memory
752
			if (textBuffer.length() > MAXDATACHARS) {
753
				logMetacat.debug("Write text into DB in charaters"
754
						+ " when text buffer size is greater than maxmum number");
755
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
756
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
757
				textBuffer = null;
758
				textBuffer = new StringBuffer();
759
			}
760
		} else {
761
			// this is inline data and write file system directly
762
			// we don't need to buffer it.
763
			StringBuffer inlineText = new StringBuffer();
764
			inlineText.append(new String(cbuf, start, len));
765
			logMetacat.debug("The inline text data write into file system: "
766
					+ inlineText.toString());
767
			writeInlineDataIntoFile(inlineDataFileWriter, inlineText);
768
		}
769
	}
770

    
771
	/** SAX Handler that is called at the end of each XML element */
772
	public void endElement(String uri, String localName, String qName)
773
			throws SAXException {
774
		logMetacat.debug("End ELEMENT " + qName);
775

    
776
		if (localName.equals(INLINE) && handleInlineData) {
777
			// Get the node from the stack
778
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
779
			logMetacat.debug("End of inline data");
780
			// close file writer
781
			try {
782
				inlineDataFileWriter.close();
783
				handleInlineData = false;
784
			} catch (IOException ioe) {
785
				throw new SAXException(ioe.getMessage());
786
			}
787

    
788
			// write put inline data file name into text buffer (without path)
789
			textBuffer = new StringBuffer(inlineDataFileName);
790
			// write file name into db
791
			endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
792
			// reset textbuff
793
			textBuffer = null;
794
			textBuffer = new StringBuffer();
795
			return;
796
		}
797

    
798
		if (!handleInlineData) {
799
			// Get the node from the stack
800
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
801
			String currentTag = currentNode.getTagName();
802

    
803
			// If before the end element, the parser hit text nodes and store
804
			// them into the buffer, write the buffer to data base. The reason
805
			// we put write database here is for xerces some time split text
806
			// node
807
			if (hitTextNode) {
808
				// get access value
809
				String data = null;
810
				// add principal
811
				if (currentTag.equals(PRINCIPAL) && accessRule != null) {
812
					data = (textBuffer.toString()).trim();
813
					accessRule.addPrincipal(data);
814

    
815
				} else if (currentTag.equals(PERMISSION) && accessRule != null) {
816
					data = (textBuffer.toString()).trim();
817
					// we combine different a permission into one value
818
					int permission = accessRule.getPermission();
819
					// add permission
820
					if (data.toUpperCase().equals(READSTRING)) {
821
						permission = permission | READ;
822
					} else if (data.toUpperCase().equals(WRITESTRING)) {
823
						permission = permission | WRITE;
824
					} else if (data.toUpperCase().equals(CHMODSTRING)) {
825
						permission = permission | CHMOD;
826
					} else if (data.toUpperCase().equals(ALLSTRING)) {
827
						permission = permission | ALL;
828
					}
829
					accessRule.setPermission(permission);
830
				} else if (currentTag.equals(REFERENCES)
831
						&& (processTopLevelAccess || processAdditionalAccess || processOtherAccess)) {
832
					// get reference
833
					data = (textBuffer.toString()).trim();
834
					// put reference id into accessSection
835
					accessObject.setReferences(data);
836

    
837
				} else if (currentTag.equals(URL)) {
838
					// handle online data, make sure its'parent is online
839
					DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
840
					if (parentNode != null && parentNode.getTagName() != null
841
							&& parentNode.getTagName().equals(ONLINE)) {
842
						// if online data is in local metacat, add it to the
843
						// vector
844
						data = (textBuffer.toString()).trim();
845
						handleOnlineUrlDataFile(data);
846
						/*
847
						 * if (data != null && (data.indexOf(MetacatUtil
848
						 * .getProperty("httpserver")) != -1 || data
849
						 * .indexOf(MetacatUtil .getProperty("server")) != -1)) { //
850
						 * Get docid from url String dataId = MetacatUtil
851
						 * .getDocIdWithRevFromOnlineURL(data); // add to vector
852
						 * onlineDataFileIdVector.add(dataId); }//if
853
						 */
854
					}// if
855
				}// else if
856
				// write text to db if it is not inline data
857
				// if (!localName.equals(INLINE))
858
				{
859
					logMetacat.debug("Write text into DB in End Element");
860

    
861
					// compare top level access module
862
					if (processTopLevelAccess && needToCheckAccessModule) {
863
						compareTextNode(currentUnchangeableAccessModuleNodeStack,
864
								textBuffer, UPDATEACCESSERROR);
865
					}
866
					// write text node into db
867
					endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
868
				}
869
				if (needToCheckAccessModule
870
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
871
					// stored the pull out nodes into storedNode stack
872
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
873
							null, MetacatUtil.normalize(textBuffer.toString()));
874
					storedAccessNodeStack.push(nodeElement);
875

    
876
				}
877
			}// if
878

    
879
			// set hitText false
880
			hitTextNode = false;
881
			// reset textbuff
882
			textBuffer = null;
883
			textBuffer = new StringBuffer();
884

    
885
			// hand sub stree stuff
886
			if (!subTreeInfoStack.empty()) {
887
				SubTree tree = subTreeInfoStack.peek();// get last
888
				// subtree
889
				if (tree != null && tree.getStartElementName() != null
890
						&& (tree.getStartElementName()).equals(currentTag)) {
891
					// find the end of sub tree and set the end node id
892
					tree.setEndNodeId(endNodeId);
893
					// add the subtree into the final store palace
894
					subTreeList.add(tree);
895
					// get rid of it from stack
896
					subTreeInfoStack.pop();
897
				}// if
898
			}// if
899

    
900
			// access stuff
901
			if (currentTag.equals(ALLOW) || currentTag.equals(DENY)) {
902
				// finish parser access rule and assign it to new one
903
				AccessRule newRule = accessRule;
904
				// add the new rule to access section object
905
				accessObject.addAccessRule(newRule);
906
				// reset access rule
907
				accessRule = null;
908
			} else if (currentTag.equals(ACCESS)) {
909
				// finish parsing an access section and assign it to new one
910
				DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
911

    
912
				accessObject.setEndNodeId(endNodeId);
913

    
914
				if (parentNode != null && parentNode.getTagName() != null
915
						&& parentNode.getTagName().equals(DISTRIBUTION)) {
916
					describesId.add(String.valueOf(distributionIndex));
917
					currentDistributionSection.setAccessSection(accessObject);
918
				}
919

    
920
				AccessSection newAccessObject = accessObject;
921

    
922
				if (newAccessObject != null) {
923

    
924
					// add the accessSection into a vector to store it
925
					// if it is not a reference, need to store it
926
					if (newAccessObject.getReferences() == null) {
927

    
928
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
929
						accessObjectList.add(newAccessObject);
930
					}
931
					if (processTopLevelAccess) {
932

    
933
						// top level access control will handle whole document
934
						// -docid
935
						topLevelAccessControlMap.put(docid, newAccessObject);
936
						// reset processtopleveraccess tag
937

    
938
					}// if
939
					else if (processAdditionalAccess) {
940
						// for additional control put everything in describes
941
						// value
942
						// and access object into hash
943
						for (int i = 0; i < describesId.size(); i++) {
944

    
945
							String subId = describesId.elementAt(i);
946
							if (subId != null) {
947
								additionalAccessControlMap.put(subId, newAccessObject);
948
							}// if
949
						}// for
950
						// add this hashtable in to vector
951

    
952
						additionalAccessMapList.add(additionalAccessControlMap);
953
						// reset this hashtable in order to store another
954
						// additional
955
						// accesscontrol
956
						additionalAccessControlMap = null;
957
						additionalAccessControlMap = new Hashtable<String, AccessSection>();
958
					}// if
959

    
960
				}// if
961
				// check if access node stack is empty after parsing top access
962
				// module
963

    
964
				if (needToCheckAccessModule && processTopLevelAccess
965
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
966

    
967
					logMetacat.error("Access node stack is not empty after "
968
							+ "parsing access subtree");
969
					throw new SAXException(UPDATEACCESSERROR);
970

    
971
				}
972
				// reset access section object
973

    
974
				accessObject = null;
975

    
976
				// reset tmp stored node stack
977
				storedAccessNodeStack = null;
978
				storedAccessNodeStack = new Stack<NodeRecord>();
979

    
980
				// reset flag
981
				processAdditionalAccess = false;
982
				processTopLevelAccess = false;
983
				processOtherAccess = false;
984

    
985
			} else if (currentTag.equals(DISTRIBUTION)) {
986
				// If the current Distribution is inline or data and it doesn't have an access section
987
				// we use the top level access section (if it exists)
988
				if ((currentDistributionSection.getDistributionType() == DistributionSection.DATA_DISTRIBUTION 
989
						|| currentDistributionSection.getDistributionType() == DistributionSection.INLINE_DATA_DISTRIBUTION)
990
						&& currentDistributionSection.getAccessSection() == null
991
						&& topLevelAccessControlMap.size() > 0) {
992
					
993
					AccessSection accessSection = new AccessSection();
994
					accessSection.setDocId(docid);	
995
					AccessSection topLevelAccess = topLevelAccessControlMap.get(docid);
996
					accessSection.setPermissionOrder(topLevelAccess.getPermissionOrder());
997
					Vector<AccessRule> accessRuleList = topLevelAccess.getAccessRules();
998
					for (AccessRule accessRule : accessRuleList) {
999
						accessSection.addAccessRule(accessRule);
1000
					}
1001
					currentDistributionSection.setAccessSection(accessSection);
1002
				} 
1003
				if (currentDistributionSection.getAccessSection() != null) {
1004
					currentDistributionSection.getAccessSection().setDataFileName(currentDistributionSection.getDataFileName());
1005
				}
1006
				allDistributionSections.add(currentDistributionSection);
1007
				currentDistributionSection = null;
1008
				describesId = null;
1009
				describesId = new Vector<String>();
1010
			}
1011
		} else {
1012
			// this is in inline part
1013
			StringBuffer endElement = new StringBuffer();
1014
			endElement.append("</");
1015
			endElement.append(qName);
1016
			endElement.append(">");
1017
			logMetacat.debug("inline endElement: " + endElement.toString());
1018
			writeInlineDataIntoFile(inlineDataFileWriter, endElement);
1019
		}
1020
	}
1021

    
1022
	/**
1023
	 * SAX Handler that receives notification of comments in the DTD
1024
	 */
1025
	public void comment(char[] ch, int start, int length) throws SAXException {
1026
		logMetacat.debug("COMMENT");
1027
		if (!handleInlineData) {
1028
			if (!processingDTD) {
1029
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1030
				String str = new String(ch, start, length);
1031

    
1032
				// compare top level access module
1033
				if (processTopLevelAccess && needToCheckAccessModule) {
1034
					compareCommentNode(currentUnchangeableAccessModuleNodeStack, str,
1035
							UPDATEACCESSERROR);
1036
				}
1037
				endNodeId = currentNode.writeChildNodeToDB("COMMENT", null, str, docid);
1038
				if (needToCheckAccessModule
1039
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1040
					// stored the pull out nodes into storedNode stack
1041
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "COMMENT", null,
1042
							null, MetacatUtil.normalize(str));
1043
					storedAccessNodeStack.push(nodeElement);
1044

    
1045
				}
1046
			}
1047
		} else {
1048
			// inline data comment
1049
			StringBuffer inlineComment = new StringBuffer();
1050
			inlineComment.append("<!--");
1051
			inlineComment.append(new String(ch, start, length));
1052
			inlineComment.append("-->");
1053
			logMetacat.debug("inline data comment: " + inlineComment.toString());
1054
			writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1055
		}
1056
	}
1057

    
1058
	/* Compare comment from xml and db */
1059
	private void compareCommentNode(Stack<NodeRecord> nodeStack, String string,
1060
			String error) throws SAXException {
1061
		NodeRecord node = null;
1062
		try {
1063
			node = nodeStack.pop();
1064
		} catch (EmptyStackException ee) {
1065
			logMetacat.error("the stack is empty for comment data");
1066
			throw new SAXException(error);
1067
		}
1068
		logMetacat.debug("current node type from xml is COMMENT");
1069
		logMetacat.debug("node type from stack: " + node.getNodeType());
1070
		logMetacat.debug("current node data from xml is: " + string);
1071
		logMetacat.debug("node data from stack: " + node.getNodeData());
1072
		logMetacat.debug("node is from stack: " + node.getNodeId());
1073
		// if not consistent terminate program and throw a exception
1074
		if (!node.getNodeType().equals("COMMENT") || !string.equals(node.getNodeData())) {
1075
			logMetacat.error("Inconsistence happened: ");
1076
			logMetacat.error("current node type from xml is COMMENT");
1077
			logMetacat.error("node type from stack: " + node.getNodeType());
1078
			logMetacat.error("current node data from xml is: " + string);
1079
			logMetacat.error("node data from stack: " + node.getNodeData());
1080
			logMetacat.error("node is from stack: " + node.getNodeId());
1081
			throw new SAXException(error);
1082
		}// if
1083
	}
1084

    
1085
	/**
1086
	 * SAX Handler called once for each processing instruction found: node that
1087
	 * PI may occur before or after the root element.
1088
	 */
1089
	public void processingInstruction(String target, String data) throws SAXException {
1090
		logMetacat.debug("PI");
1091
		if (!handleInlineData) {
1092
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1093
			endNodeId = currentNode.writeChildNodeToDB("PI", target, data, docid);
1094
		} else {
1095
			StringBuffer inlinePI = new StringBuffer();
1096
			inlinePI.append("<?");
1097
			inlinePI.append(target);
1098
			inlinePI.append(" ");
1099
			inlinePI.append(data);
1100
			inlinePI.append("?>");
1101
			logMetacat.debug("inline data pi is: " + inlinePI.toString());
1102
			writeInlineDataIntoFile(inlineDataFileWriter, inlinePI);
1103
		}
1104
	}
1105

    
1106
	/** SAX Handler that is called at the start of Namespace */
1107
	public void startPrefixMapping(String prefix, String uri) throws SAXException {
1108
		logMetacat.debug("NAMESPACE");
1109
		if (!handleInlineData) {
1110
			namespaces.put(prefix, uri);
1111
		} else {
1112
			inlineDataNameSpace.put(prefix, uri);
1113
		}
1114
	}
1115

    
1116
	/**
1117
	 * SAX Handler that is called for each XML text node that is Ignorable white
1118
	 * space
1119
	 */
1120
	public void ignorableWhitespace(char[] cbuf, int start, int len) throws SAXException {
1121
		// When validation is turned "on", white spaces are reported here
1122
		// When validation is turned "off" white spaces are not reported here,
1123
		// but through characters() callback
1124
		logMetacat.debug("IGNORABLEWHITESPACE");
1125
		if (!handleInlineData) {
1126
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1127
			String data = null;
1128
			int leftover = len;
1129
			int offset = start;
1130
			boolean moredata = true;
1131

    
1132
			// This loop deals with the case where there are more characters
1133
			// than can fit in a single database text field (limit is
1134
			// MAXDATACHARS). If the text to be inserted exceeds MAXDATACHARS,
1135
			// write a series of nodes that are MAXDATACHARS long, and then the
1136
			// final node contains the remainder
1137
			while (moredata) {
1138
				if (leftover > MAXDATACHARS) {
1139
					data = new String(cbuf, offset, MAXDATACHARS);
1140
					leftover -= MAXDATACHARS;
1141
					offset += MAXDATACHARS;
1142
				} else {
1143
					data = new String(cbuf, offset, leftover);
1144
					moredata = false;
1145
				}
1146

    
1147
				// compare whitespace in access top module
1148
				if (processTopLevelAccess && needToCheckAccessModule) {
1149
					compareWhiteSpace(currentUnchangeableAccessModuleNodeStack, data,
1150
							UPDATEACCESSERROR);
1151
				}
1152
				// Write the content of the node to the database
1153
				if (needToCheckAccessModule
1154
						&& (processAdditionalAccess || processOtherAccess || processTopLevelAccess)) {
1155
					// stored the pull out nodes into storedNode stack
1156
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
1157
							null, MetacatUtil.normalize(data));
1158
					storedAccessNodeStack.push(nodeElement);
1159

    
1160
				}
1161
				endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data, docid);
1162
			}
1163
		} else {
1164
			// This is inline data write to file directly
1165
			StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf, start, len));
1166
			writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1167
		}
1168

    
1169
	}
1170

    
1171
	/* Compare whitespace from xml and db */
1172
	private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string,
1173
			String error) throws SAXException {
1174
		NodeRecord node = null;
1175
		try {
1176
			node = nodeStack.pop();
1177
		} catch (EmptyStackException ee) {
1178
			logMetacat.error("the stack is empty for whitespace data");
1179
			throw new SAXException(error);
1180
		}
1181
		if (!node.getNodeType().equals("TEXT") || !string.equals(node.getNodeData())) {
1182
			logMetacat.error("Inconsistence happened: ");
1183
			logMetacat.error("current node type from xml is WHITESPACE TEXT");
1184
			logMetacat.error("node type from stack: " + node.getNodeType());
1185
			logMetacat.error("current node data from xml is: " + string);
1186
			logMetacat.error("node data from stack: " + node.getNodeData());
1187
			logMetacat.error("node is from stack: " + node.getNodeId());
1188
			throw new SAXException(error);
1189
		}// if
1190
	}
1191

    
1192
	/** SAX Handler that receives notification of end of the document */
1193
	public void endDocument() throws SAXException {
1194
		logMetacat.debug("end Document");
1195
		// There are some unchangable subtree didn't be compare
1196
		// This maybe cause user change the subtree id
1197
		if (!super.getIsRevisionDoc()) {
1198
			// write access rule to db
1199
			writeAccessRuleToDB();
1200
			// delete relation table
1201
			deleteRelations();
1202
			// write relations
1203
			for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1204
				String id = onlineDataFileIdInRelationVector.elementAt(i);
1205
				writeOnlineDataFileIdIntoRelationTable(id);
1206
			}
1207
		}
1208
	}
1209

    
1210
	/* The method to write all access rule into db */
1211
	private void writeAccessRuleToDB() throws SAXException {
1212
		// Delete old permssion
1213
		deletePermissionsInAccessTable(docid);
1214
		// write top leve access rule
1215
		writeTopLevelAccessRuleToDB();
1216
		// write additional access rule
1217
		// writeAdditionalAccessRuleToDB();
1218
		writeAdditionalAccessRulesToDB();
1219
	}// writeAccessRuleToDB
1220

    
1221
	/* The method to write top level access rule into db. */
1222
	private void writeTopLevelAccessRuleToDB() throws SAXException {
1223
		// for top document level
1224
		AccessSection accessSection = topLevelAccessControlMap.get(docid);
1225
		boolean top = true;
1226
		String subSectionId = null;
1227
		if (accessSection != null) {
1228
			AccessSection accessSectionObj = accessSection;
1229

    
1230
			// if accessSection is not null and is not reference
1231
			if (accessSectionObj.getReferences() == null) {
1232
				// write the top level access module into xml_accesssubtree to
1233
				// store info and then when update to check if the user can
1234
				// update it or not
1235
				deleteAccessSubTreeRecord(docid);
1236
				writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1237

    
1238
				// write access section into xml_access table
1239
				writeGivenAccessRuleIntoDB(accessSectionObj, top, subSectionId);
1240
				// write online data file into xml_access too.
1241
				// for (int i= 0; i <onlineDataFileIdInTopAccessVector.size();
1242
				// i++)
1243
				// {
1244
				// String id = onlineDataFileIdInTopAccessVector.elementAt(i);
1245
				// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1246
				// id);
1247
				// }
1248

    
1249
			} else {
1250

    
1251
				// this is a reference and go trough the vector which contains
1252
				// all access object
1253
				String referenceId = accessSectionObj.getReferences();
1254
				boolean findAccessObject = false;
1255
				logMetacat.debug("referered id for top access: " + referenceId);
1256
				for (int i = 0; i < accessObjectList.size(); i++) {
1257
					AccessSection accessObj = accessObjectList.elementAt(i);
1258
					String accessObjId = accessObj.getSubTreeId();
1259
					if (referenceId != null && accessObj != null
1260
							&& referenceId.equals(accessObjId)) {
1261
						// make sure the user didn't change any thing in this
1262
						// access moduel
1263
						// too if user doesn't have all permission
1264
						if (needToCheckAccessModule) {
1265

    
1266
							Stack<NodeRecord> newStack = accessObj
1267
									.getStoredTmpNodeStack();
1268
							// revise order
1269
							newStack = DocumentUtil.reviseStack(newStack);
1270
							// go throught the vector of
1271
							// unChangeableAccessSubtreevector
1272
							// and find the one whose id is as same as
1273
							// referenceid
1274
							AccessSection oldAccessObj = getAccessSectionFromUnchangableAccessVector(referenceId);
1275
							// if oldAccessObj is null something is wrong
1276
							if (oldAccessObj == null) {
1277
								throw new SAXException(UPDATEACCESSERROR);
1278
							}// if
1279
							else {
1280
								// Get the node stack from old access obj
1281
								Stack<NodeRecord> oldStack = oldAccessObj
1282
										.getSubTreeNodeStack();
1283
								compareNodeStacks(newStack, oldStack);
1284
							}// else
1285
						}// if
1286
						// write accessobject into db
1287
						writeGivenAccessRuleIntoDB(accessObj, top, subSectionId);
1288
						// write online data file into xml_access too.
1289
						// for (int j= 0; j
1290
						// <onlineDataFileIdInTopAccessVector.size(); j++)
1291
						// {
1292
						// String id =
1293
						// onlineDataFileIdInTopAccessVector.elementAt(j);
1294
						// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1295
						// id);
1296
						// }
1297

    
1298
						// write the reference access into xml_accesssubtree
1299
						// too write the top level access module into
1300
						// xml_accesssubtree to store info and then when update
1301
						// to check if the user can update it or not
1302
						deleteAccessSubTreeRecord(docid);
1303
						writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1304
						writeAccessSubTreeIntoDB(accessObj, SUBTREELEVEL);
1305
						findAccessObject = true;
1306
						break;
1307
					}
1308
				}// for
1309
				// if we couldn't find an access subtree id for this reference
1310
				// id
1311
				if (!findAccessObject) {
1312
					throw new SAXException("The referenceid: " + referenceId
1313
							+ " is not access subtree");
1314
				}// if
1315
			}// else
1316

    
1317
		}// if
1318
		else {
1319
			// couldn't find a access section object
1320
			logMetacat.warn("couldn't find access control for document: " + docid);
1321
		}
1322

    
1323
	}// writeTopLevelAccessRuletoDB
1324

    
1325
	/* Given a subtree id and find the responding access section */
1326
	private AccessSection getAccessSectionFromUnchangableAccessVector(String id) {
1327
		AccessSection result = null;
1328
		// Makse sure the id
1329
		if (id == null || id.equals("")) {
1330
			return result;
1331
		}
1332
		// go throught vector and find the list
1333
		for (int i = 0; i < unChangeableAccessSubTreeVector.size(); i++) {
1334
			AccessSection accessObj = unChangeableAccessSubTreeVector.elementAt(i);
1335
			if (accessObj.getSubTreeId() != null && (accessObj.getSubTreeId()).equals(id)) {
1336
				result = accessObj;
1337
			}// if
1338
		}// for
1339
		return result;
1340
	}// getAccessSectionFromUnchangableAccessVector
1341

    
1342
	/* Compare two node stacks to see if they are same */
1343
	private void compareNodeStacks(Stack<NodeRecord> stack1, Stack<NodeRecord> stack2)
1344
			throws SAXException {
1345
		// make sure stack1 and stack2 are not empty
1346
		if (stack1.isEmpty() || stack2.isEmpty()) {
1347
			logMetacat.error("Because stack is empty!");
1348
			throw new SAXException(UPDATEACCESSERROR);
1349
		}
1350
		// go throw two stacks and compare every element
1351
		while (!stack1.isEmpty()) {
1352
			// Pop an element from stack1
1353
			NodeRecord record1 = stack1.pop();
1354
			// Pop an element from stack2(stack 2 maybe empty)
1355
			NodeRecord record2 = null;
1356
			try {
1357
				record2 = stack2.pop();
1358
			} catch (EmptyStackException ee) {
1359
				logMetacat.error("Node stack2 is empty but stack1 isn't!");
1360
				throw new SAXException(UPDATEACCESSERROR);
1361
			}
1362
			// if two records are not same throw a exception
1363
			if (!record1.contentEquals(record2)) {
1364
				logMetacat.error("Two records from new and old stack are not " + "same!");
1365
				throw new SAXException(UPDATEACCESSERROR);
1366
			}// if
1367
		}// while
1368

    
1369
		// now stack1 is empty and we should make sure stack2 is empty too
1370
		if (!stack2.isEmpty()) {
1371
			logMetacat
1372
					.error("stack2 still has some elements while stack " + "is empty! ");
1373
			throw new SAXException(UPDATEACCESSERROR);
1374
		}// if
1375
	}// comparingNodeStacks
1376

    
1377
	/* The method to write additional access rule into db. */
1378
	private void writeAdditionalAccessRulesToDB() throws SAXException {
1379
		
1380
		// Iterate through every distribution and write access sections for data and inline
1381
		// types to the database
1382
		for (DistributionSection distributionSection : allDistributionSections) {			
1383
			// we're only interested in data and inline distributions
1384
			int distributionType = distributionSection.getDistributionType();
1385
			if (distributionType == DistributionSection.DATA_DISTRIBUTION
1386
					|| distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION) {
1387
				AccessSection accessSection = distributionSection.getAccessSection();
1388
				
1389
				// If the distribution doesn't have an access section, we continue.
1390
				if (accessSection == null) {
1391
					continue;		
1392
				} 
1393
				
1394
				// We want to check file permissions for all online data updates and inserts, or for 
1395
				// inline updates.
1396
//				if (distributionType == DistributionSection.DATA_DISTRIBUTION
1397
//						|| (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE")) {
1398

    
1399
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1400
					try {
1401
						PermissionController controller = new PermissionController(
1402
								distributionSection.getDataFileName(), false);
1403
						if (AccessionNumber.accNumberUsed(docid)
1404
								&& !controller.hasPermission(user, groups, "WRITE")) {
1405
							throw new SAXException(UPDATEACCESSERROR);
1406
						}
1407
					} catch (SQLException sqle) {
1408
						throw new SAXException(
1409
								"Database error checking user permissions: "
1410
										+ sqle.getMessage());
1411
					} catch (Exception e) {
1412
						throw new SAXException(
1413
								"General error checking user permissions: "
1414
										+ e.getMessage());
1415
					}
1416
				} else if (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE") {
1417
					try {
1418
						PermissionController controller = new PermissionController(
1419
								docid, false);
1420

    
1421
						if (!controller.hasPermission(user, groups, "WRITE")) {
1422
							throw new SAXException(UPDATEACCESSERROR);
1423
						}
1424
					} catch (SQLException sqle) {
1425
						throw new SAXException(
1426
								"Database error checking user permissions: "
1427
										+ sqle.getMessage());
1428
					} catch (Exception e) {
1429
						throw new SAXException(
1430
								"General error checking user permissions: "
1431
										+ e.getMessage());
1432
					}
1433
				}
1434
				
1435
				String subSectionId = Integer.toString(distributionSection.getDistributionId());
1436
				writeGivenAccessRuleIntoDB(accessSection, false, subSectionId);
1437
			}
1438

    
1439
		}
1440
		
1441
		
1442
	}
1443

    
1444
	/* Write a given access rule into db */
1445
	private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1446
			boolean topLevel, String subSectionId) throws SAXException {
1447
		if (accessSection == null) {
1448
			throw new SAXException("The access object is null");
1449
		}
1450

    
1451
		String permOrder = accessSection.getPermissionOrder();
1452
		String sql = null;
1453
		PreparedStatement pstmt = null;
1454
		if (topLevel) {
1455
			sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1456
					+ "perm_type, perm_order, accessfileid) VALUES "
1457
					+ " (?, ?, ?, ?, ?, ?)";
1458
		} else {
1459
			sql = "INSERT INTO xml_access (docid,principal_name, "
1460
					+ "permission, perm_type, perm_order, accessfileid, subtreeid"
1461
					+ ") VALUES" + " (?, ?, ?, ?, ?, ?, ?)";
1462
		}
1463
		try {
1464

    
1465
			pstmt = connection.prepareStatement(sql);
1466
			// Increase DBConnection usage count
1467
			connection.increaseUsageCount(1);
1468
			// Bind the values to the query
1469
			pstmt.setString(6, docid);
1470
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1471
			pstmt.setString(5, permOrder);
1472
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1473
			// if it is not top level, set subsection id
1474
			if (topLevel) {
1475
				pstmt.setString(1, docid);
1476
				logMetacat.debug("Docid in accesstable: " + docid);
1477
			}
1478
			if (!topLevel) {
1479
				pstmt.setString(1, accessSection.getDataFileName());
1480
				logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1481

    
1482
				// for subtree should specify the
1483
				if (subSectionId == null) {
1484
					throw new SAXException("The subsection is null");
1485
				}
1486

    
1487
				pstmt.setString(7, subSectionId);
1488
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1489
			}
1490

    
1491
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1492
			// go through every rule
1493
			for (int i = 0; i < accessRules.size(); i++) {
1494
				AccessRule rule = accessRules.elementAt(i);
1495
				String permType = rule.getPermissionType();
1496
				int permission = rule.getPermission();
1497
				pstmt.setInt(3, permission);
1498
				logMetacat.debug("permission in accesstable: " + permission);
1499
				pstmt.setString(4, permType);
1500
				logMetacat.debug("Permtype in accesstable: " + permType);
1501
				// go through every principle in rule
1502
				Vector<String> nameVector = rule.getPrincipal();
1503
				for (int j = 0; j < nameVector.size(); j++) {
1504
					String prName = nameVector.elementAt(j);
1505
					pstmt.setString(2, prName);
1506
					logMetacat.debug("Principal in accesstable: " + prName);
1507
					logMetacat.debug("running sql: " + pstmt.toString());
1508
					pstmt.execute();
1509
				}// for
1510
			}// for
1511
			pstmt.close();
1512
		}// try
1513
		catch (SQLException e) {
1514
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1515
					+ e.getMessage());
1516
		}// catch
1517
		finally {
1518
			try {
1519
				pstmt.close();
1520
			} catch (SQLException ee) {
1521
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1522
						+ ee.getMessage());
1523
			}
1524
		}// finally
1525

    
1526
	}// writeGivenAccessRuleIntoDB
1527

    
1528
	/* Write a gaven access rule into db */
1529
	private void writeAccessRuleForRelatedDataFileIntoDB(AccessSection accessSection,
1530
			String dataId) throws SAXException {
1531
		if (accessSection == null) {
1532
			throw new SAXException("The access object is null");
1533
		}
1534
		// get rid of rev from dataId
1535
		// dataId = MetacatUtil.getDocIdFromString(dataId);
1536
		String permOrder = accessSection.getPermissionOrder();
1537
		String sql = null;
1538
		PreparedStatement pstmt = null;
1539
		sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1540
				+ "perm_type, perm_order, accessfileid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1541

    
1542
		try {
1543

    
1544
			pstmt = connection.prepareStatement(sql);
1545
			// Increase DBConnection usage count
1546
			connection.increaseUsageCount(1);
1547
			// Bind the values to the query
1548
			pstmt.setString(1, dataId);
1549
			logMetacat.debug("Docid in accesstable: " + docid);
1550
			pstmt.setString(6, docid);
1551
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1552
			pstmt.setString(5, permOrder);
1553
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1554
			// if it is not top level, set subsection id
1555

    
1556
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1557
			// go through every rule
1558
			for (int i = 0; i < accessRules.size(); i++) {
1559
				AccessRule rule = accessRules.elementAt(i);
1560
				String permType = rule.getPermissionType();
1561
				int permission = rule.getPermission();
1562
				pstmt.setInt(3, permission);
1563
				logMetacat.debug("permission in accesstable: " + permission);
1564
				pstmt.setString(4, permType);
1565
				logMetacat.debug("Permtype in accesstable: " + permType);
1566
				// go through every principle in rule
1567
				Vector<String> nameVector = rule.getPrincipal();
1568
				for (int j = 0; j < nameVector.size(); j++) {
1569
					String prName = nameVector.elementAt(j);
1570
					pstmt.setString(2, prName);
1571
					logMetacat.debug("Principal in accesstable: " + prName);
1572
					logMetacat.debug("running sql: " + pstmt.toString());
1573
					pstmt.execute();
1574
				}// for
1575
			}// for
1576
			pstmt.close();
1577
		}// try
1578
		catch (SQLException e) {
1579
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1580
					+ e.getMessage());
1581
		}// catch
1582
		finally {
1583
			try {
1584
				pstmt.close();
1585
			} catch (SQLException ee) {
1586
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1587
						+ ee.getMessage());
1588
			}
1589
		}// finally
1590

    
1591
	}// writeAccessRuleForRalatedDataFileIntoDB
1592

    
1593
	/* Delete from db all permission for resources related to @aclid if any. */
1594
	private void deletePermissionsInAccessTable(String aclid) throws SAXException {
1595
		Statement stmt = null;
1596
		try {
1597
			// delete all acl records for resources related to @aclid if any
1598
			stmt = connection.createStatement();
1599
			// Increase DBConnection usage count
1600
			connection.increaseUsageCount(1);
1601
			logMetacat.debug("running sql: DELETE FROM xml_access WHERE accessfileid = '"
1602
					+ aclid + "'");
1603
			stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
1604

    
1605
		} catch (SQLException e) {
1606
			throw new SAXException(e.getMessage());
1607
		} finally {
1608
			try {
1609
				stmt.close();
1610
			} catch (SQLException ee) {
1611
				throw new SAXException(ee.getMessage());
1612
			}
1613
		}
1614
	}// deletePermissionsInAccessTable
1615

    
1616
	/*
1617
	 * In order to make sure only usr has "all" permission can update access
1618
	 * subtree in eml document we need to keep access subtree info in
1619
	 * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1620
	 */
1621
	private void writeAccessSubTreeIntoDB(AccessSection accessSection, String level)
1622
			throws SAXException {
1623
		if (accessSection == null) {
1624
			throw new SAXException("The access object is null");
1625
		}
1626

    
1627
		String sql = null;
1628
		PreparedStatement pstmt = null;
1629
		sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1630
				+ "subtreeid, startnodeid, endnodeid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1631
		try {
1632

    
1633
			pstmt = connection.prepareStatement(sql);
1634
			// Increase DBConnection usage count
1635
			connection.increaseUsageCount(1);
1636
			long startNodeId = accessSection.getStartNodeId();
1637
			long endNodeId = accessSection.getEndNodeId();
1638
			String sectionId = accessSection.getSubTreeId();
1639
			// Bind the values to the query
1640
			pstmt.setString(1, docid);
1641
			logMetacat.debug("Docid in access-subtreetable: " + docid);
1642
			pstmt.setLong(2, (new Long(revision)).longValue());
1643
			logMetacat.debug("rev in accesssubtreetable: " + revision);
1644
			pstmt.setString(3, level);
1645
			logMetacat.debug("contorl level in access-subtree table: " + level);
1646
			pstmt.setString(4, sectionId);
1647
			logMetacat.debug("Subtree id in access-subtree table: " + sectionId);
1648
			pstmt.setLong(5, startNodeId);
1649
			logMetacat.debug("Start node id is: " + startNodeId);
1650
			pstmt.setLong(6, endNodeId);
1651
			logMetacat.debug("End node id is: " + endNodeId);
1652
			logMetacat.debug("running sql: " + pstmt.toString());
1653
			pstmt.execute();
1654
			pstmt.close();
1655
		}// try
1656
		catch (SQLException e) {
1657
			throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1658
					+ e.getMessage());
1659
		}// catch
1660
		finally {
1661
			try {
1662
				pstmt.close();
1663
			} catch (SQLException ee) {
1664
				throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1665
						+ ee.getMessage());
1666
			}
1667
		}// finally
1668

    
1669
	}// writeAccessSubtreeIntoDB
1670

    
1671
	/* Delete every access subtree record from xml_accesssubtree. */
1672
	private void deleteAccessSubTreeRecord(String docId) throws SAXException {
1673
		Statement stmt = null;
1674
		try {
1675
			// delete all acl records for resources related to @aclid if any
1676
			stmt = connection.createStatement();
1677
			// Increase DBConnection usage count
1678
			connection.increaseUsageCount(1);
1679
			logMetacat.debug("running sql: DELETE FROM xml_accesssubtree WHERE docid = '"
1680
					+ docId + "'");
1681
			stmt.execute("DELETE FROM xml_accesssubtree WHERE docid = '" + docId + "'");
1682

    
1683
		} catch (SQLException e) {
1684
			throw new SAXException(e.getMessage());
1685
		} finally {
1686
			try {
1687
				stmt.close();
1688
			} catch (SQLException ee) {
1689
				throw new SAXException(ee.getMessage());
1690
			}
1691
		}
1692
	}// deleteAccessSubTreeRecord
1693

    
1694
	// open a file writer for writing inline data to file
1695
	private FileWriter createInlineDataFileWriter(String fileName) throws SAXException {
1696
		FileWriter writer = null;
1697
		String path;
1698
		try {
1699
			path = PropertyService.getProperty("application.inlinedatafilepath");
1700
		} catch (PropertyNotFoundException pnfe) {
1701
			throw new SAXException(pnfe.getMessage());
1702
		}
1703
		/*
1704
		 * File inlineDataDirectory = new File(path);
1705
		 */
1706
		String newFile = path + "/" + fileName;
1707
		logMetacat.debug("inline file name: " + newFile);
1708
		try {
1709
			// true means append
1710
			writer = new FileWriter(newFile, true);
1711
		} catch (IOException ioe) {
1712
			throw new SAXException(ioe.getMessage());
1713
		}
1714
		return writer;
1715
	}
1716

    
1717
	// write inline data into file system and return file name(without path)
1718
	private void writeInlineDataIntoFile(FileWriter writer, StringBuffer data)
1719
			throws SAXException {
1720
		try {
1721
			writer.write(data.toString());
1722
			writer.flush();
1723
		} catch (Exception e) {
1724
			throw new SAXException(e.getMessage());
1725
		}
1726
	}
1727

    
1728
	/*
1729
	 * In eml2, the inline data wouldn't store in db, it store in file system
1730
	 * The db stores file name(without path). We got the old file name from db
1731
	 * and compare to the new in line data file
1732
	 */
1733
	public boolean compareInlineDataFiles(String oldFileName, String newFileName)
1734
			throws McdbException {
1735
		// this method need to be testing
1736
		boolean same = true;
1737
		String data = null;
1738
		try {
1739
			String path = PropertyService.getProperty("application.inlinedatafilepath");
1740
			// the new file name will look like path/docid.rev.2
1741
			File inlineDataDirectory = new File(path);
1742
			File oldDataFile = new File(inlineDataDirectory, oldFileName);
1743
			File newDataFile = new File(inlineDataDirectory, newFileName);
1744

    
1745
			FileReader oldFileReader = new FileReader(oldDataFile);
1746
			BufferedReader oldStringReader = new BufferedReader(oldFileReader);
1747
			FileReader newFileReader = new FileReader(newDataFile);
1748
			BufferedReader newStringReader = new BufferedReader(newFileReader);
1749
			// read first line of data
1750
			String oldString = oldStringReader.readLine();
1751
			String newString = newStringReader.readLine();
1752

    
1753
			// at the end oldstring will be null
1754
			while (oldString != null) {
1755
				oldString = oldStringReader.readLine();
1756
				newString = newStringReader.readLine();
1757
				if (!oldString.equals(newString)) {
1758
					same = false;
1759
					break;
1760
				}
1761
			}
1762

    
1763
			// if oldString is null but newString is not null, they are same
1764
			if (same) {
1765
				if (newString != null) {
1766
					same = false;
1767
				}
1768
			}
1769

    
1770
		} catch (Exception e) {
1771
			throw new McdbException(e.getMessage());
1772
		}
1773
		logMetacat.debug("the inline data retrieved from file: " + data);
1774
		return same;
1775
	}
1776

    
1777
	// if xml file failed to upload, we need to call this method to delete
1778
	// the inline data already in file system
1779
	public void deleteInlineFiles() throws SAXException {
1780
		if (!inlineFileIdList.isEmpty()) {
1781
			for (int i = 0; i < inlineFileIdList.size(); i++) {
1782
				String fileName = inlineFileIdList.elementAt(i);
1783
				deleteInlineDataFile(fileName);
1784
			}
1785
		}
1786
	}
1787

    
1788
	/* delete the inline data file */
1789
	private void deleteInlineDataFile(String fileName) throws SAXException {
1790
		String path;
1791
		try {
1792
			path = PropertyService.getProperty("application.inlinedatafilepath");
1793
		} catch (PropertyNotFoundException pnfe) {
1794
			throw new SAXException("Could not find inline data file path: "
1795
					+ pnfe.getMessage());
1796
		}
1797
		File inlineDataDirectory = new File(path);
1798
		File newFile = new File(inlineDataDirectory, fileName);
1799
		newFile.delete();
1800

    
1801
	}
1802

    
1803
	/*
1804
	 * In eml2, the inline data wouldn't store in db, it store in file system
1805
	 * The db stores file name(without path).
1806
	 */
1807
	public static Reader readInlineDataFromFileSystem(String fileName)
1808
			throws McdbException {
1809
		// BufferedReader stringReader = null;
1810
		FileReader fileReader = null;
1811
		try {
1812
			String path = PropertyService.getProperty("application.inlinedatafilepath");
1813
			// the new file name will look like path/docid.rev.2
1814
			File inlineDataDirectory = new File(path);
1815
			File dataFile = new File(inlineDataDirectory, fileName);
1816

    
1817
			fileReader = new FileReader(dataFile);
1818
			// stringReader = new BufferedReader(fileReader);
1819
		} catch (Exception e) {
1820
			throw new McdbException(e.getMessage());
1821
		}
1822
		// return stringReader;
1823
		return fileReader;
1824
	}
1825

    
1826
	/* Delete relations */
1827
	private void deleteRelations() throws SAXException {
1828
		PreparedStatement pStmt = null;
1829
		String sql = "DELETE FROM xml_relation where docid =?";
1830
		try {
1831
			pStmt = connection.prepareStatement(sql);
1832
			// bind variable
1833
			pStmt.setString(1, docid);
1834
			// execute query
1835
			pStmt.execute();
1836
			pStmt.close();
1837
		}// try
1838
		catch (SQLException e) {
1839
			throw new SAXException("EMLSAXHandler.deleteRelations(): " + e.getMessage());
1840
		}// catch
1841
		finally {
1842
			try {
1843
				pStmt.close();
1844
			}// try
1845
			catch (SQLException ee) {
1846
				throw new SAXException("EMLSAXHandler.deleteRelations: "
1847
						+ ee.getMessage());
1848
			}// catch
1849
		}// finally
1850
	}
1851

    
1852
	/*
1853
	 * Write an online data file id into xml_relation table. The dataId
1854
	 * shouldnot have the revision
1855
	 */
1856
	private void writeOnlineDataFileIdIntoRelationTable(String dataId)
1857
			throws SAXException {
1858
		PreparedStatement pStmt = null;
1859
		String sql = "INSERT into xml_relation (docid, packagetype, subject, "
1860
				+ "relationship, object) values (?, ?, ?, ?, ?)";
1861
		try {
1862
			pStmt = connection.prepareStatement(sql);
1863
			// bind variable
1864
			pStmt.setString(1, docid);
1865
			pStmt.setString(2, DocumentImpl.EML2_1_0NAMESPACE);
1866
			pStmt.setString(3, docid);
1867
			pStmt.setString(4, RELATION);
1868
			pStmt.setString(5, dataId);
1869
			// execute query
1870
			pStmt.execute();
1871
			pStmt.close();
1872
		}// try
1873
		catch (SQLException e) {
1874
			throw new SAXException(
1875
					"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1876
							+ e.getMessage());
1877
		}// catch
1878
		finally {
1879
			try {
1880
				pStmt.close();
1881
			}// try
1882
			catch (SQLException ee) {
1883
				throw new SAXException(
1884
						"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1885
								+ ee.getMessage());
1886
			}// catch
1887
		}// finally
1888

    
1889
	}// writeOnlineDataFileIdIntoRelationTable
1890

    
1891
	/*
1892
	 * This method will handle data file in online url. If the data file is in
1893
	 * ecogrid protocol, then the datafile identifier(without rev)should be put
1894
	 * into onlineDataFileRelationVector. The docid in this vector will be
1895
	 * insert into xml_relation table in endDocument(). If the data file doesn't
1896
	 * exsit in xml_documents or xml_revision table, or the user has all
1897
	 * permission to the data file if the docid already existed, the data file
1898
	 * id (without rev)will be put into onlineDataFileTopAccessVector. The top
1899
	 * access rules specified in this eml document will apply to the data file.
1900
	 * NEED to do: We should also need to implement http and ftp. Those external
1901
	 * files should be download and assign a data file id to it.
1902
	 */
1903
	private void handleOnlineUrlDataFile(String url) throws SAXException {
1904
		logMetacat.warn("The url is " + url);
1905

    
1906
		if (currentDistributionSection == null) {
1907
			throw new SAXException("Trying to set the online file name for a null"
1908
					+ " distribution section");
1909
		}
1910

    
1911
		// if the url is not in ecogrid protocol, null will be returned
1912
		String accessionNumber = DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
1913
		if (accessionNumber == null) {
1914
			// the accession number is null if the url does not references a
1915
			// local data file (url would start with "ecogrid://"
1916
			currentDistributionSection
1917
					.setDistributionType(DistributionSection.ONLINE_DATA_DISTRIBUTION);
1918
		} else {
1919
			// handle ecogrid protocol
1920
			// get rid of revision number to get the docid.
1921
			String docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
1922

    
1923
			currentDistributionSection
1924
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
1925
			currentDistributionSection.setDataFileName(docid);
1926

    
1927
			// distributionOnlineFileName = docid;
1928
			onlineDataFileIdInRelationVector.add(docid);
1929
			try {				
1930
				if (!AccessionNumber.accNumberUsed(docid)) {
1931
					onlineDataFileIdInTopAccessVector.add(docid);
1932
				} else {
1933
					PermissionController controller = new PermissionController(accessionNumber);				
1934
					if (controller.hasPermission(user, groups,AccessControlInterface.ALLSTRING)) {
1935
						onlineDataFileIdInTopAccessVector.add(docid);
1936
					} else {
1937
						throw new SAXException(UPDATEACCESSERROR);
1938
					}
1939
				} 
1940
			}// try
1941
			catch (Exception e) {
1942
				logMetacat.error("Eorr in "
1943
								+ "Eml210SAXHanlder.handleOnlineUrlDataFile is "
1944
								+ e.getMessage());
1945
				throw new SAXException(e.getMessage());
1946
			}
1947
		}
1948
	}
1949
}
(33-33/59)