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: cjones $'
10
 *     '$Date: 2016-05-18 09:01:29 -0700 (Wed, 18 May 2016) $'
11
 * '$Revision: 9780 $'
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.File;
31
import java.io.FileOutputStream;
32
import java.io.IOException;
33
import java.io.OutputStreamWriter;
34
import java.io.Writer;
35
import java.sql.PreparedStatement;
36
import java.sql.ResultSet;
37
import java.sql.SQLException;
38
import java.util.Date;
39
import java.util.EmptyStackException;
40
import java.util.Enumeration;
41
import java.util.Hashtable;
42
import java.util.Stack;
43
import java.util.Vector;
44

    
45
import org.apache.log4j.Logger;
46
import org.xml.sax.Attributes;
47
import org.xml.sax.SAXException;
48

    
49
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
50
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
51
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
52
import edu.ucsb.nceas.metacat.database.DBConnection;
53
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
54
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
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 processingTopLevelAccess = false;
67

    
68
	private boolean processingAdditionalAccess = false;
69

    
70
	private boolean processingOtherAccess = false;
71

    
72
	private AccessSection accessObject = null;
73

    
74
	private AccessRule accessRule = null;
75

    
76
	// all access rules
77
	private Vector<AccessSection> accessObjectList = new Vector<AccessSection>(); 
78

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

    
81
	// subtree access for additionalmetadata
82
	private Hashtable<String, AccessSection> additionalAccessControlMap = new Hashtable<String, AccessSection>();
83
	
84
	
85
	private Vector<Hashtable<String, AccessSection>> additionalAccessMapList = new Vector<Hashtable<String, AccessSection>>();
86
	
87
	// ids from additionalmetadata/describes
88
	private Vector<String> describesId = new Vector<String>(); 
89
	
90
	private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
91

    
92
	private Vector<SubTree> subTreeList = new Vector<SubTree>();
93
	
94
	private boolean needToCheckAccessModule = false;
95

    
96
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
97

    
98
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
99

    
100
	private AccessSection topAccessSection;
101

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

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

    
109
	// vector stored the data file id which will be write top access rules to
110
	// access table
111
	private Vector<String> onlineDataFileIdInTopAccessVector = new Vector<String>();
112

    
113
	// Indicator of inline data
114
	private boolean handleInlineData = false;
115

    
116
	private Hashtable<String, String> inlineDataNameSpace = null;
117

    
118
	private Writer inlineDataFileWriter = null;
119

    
120
	private String inlineDataFileName = null;
121

    
122
	DistributionSection currentDistributionSection = null;
123

    
124
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
125
    private Vector<String> guidsToSync;
126

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

    
133
	// private String distributionOnlineFileName = null;
134

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

    
138
	// Constant
139
	private static final String EML = "eml";
140

    
141
	private static final String DISTRIBUTION = "distribution";
142

    
143
	private static final String ORDER = "order";
144

    
145
	private static final String ID = "id";
146

    
147
	private static final String REFERENCES = "references";
148

    
149
	public static final String INLINE = "inline";
150

    
151
	private static final String ONLINE = "online";
152

    
153
	private static final String URL = "url";
154

    
155
	// private static final String PERMISSIONERROR = "User tried to update a
156
	// subtree "
157
	// + "when they don't have write permission!";
158

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

    
162
	private static final String TOPLEVEL = "top";
163

    
164
	private static final String SUBTREELEVEL = "subtree";
165

    
166
	private static final String RELATION = "Provides info for";
167

    
168
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
169

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

    
203
			if (action.equals("UPDATE")) {
204
				// If the action is update and user doesn't have "ALL" permission
205
				// we need to check if user can update access subtree			
206
				int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docid);
207
				String previousDocid = 
208
					docid + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
209
				
210
				PermissionController control = new PermissionController(previousDocid );
211
				if (!control.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
212
						&& !control.hasPermission(user, groups, AccessControlInterface.CHMODSTRING)
213
						&& action != null) {
214
					needToCheckAccessModule = true;
215
					unChangeableAccessSubTreeVector = getAccessSubTreeListFromDB();
216
				}
217
			}
218

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

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

    
235
		try {
236

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

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

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

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

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

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

    
330
			}
331

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

    
336
				// compare top level access module
337
				if (processingTopLevelAccess && needToCheckAccessModule) {
338
					compareAccessTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer);
339
				}
340

    
341
				if (needToCheckAccessModule
342
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
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
						String sql = "SELECT catalog_id FROM xml_catalog "
399
							+ "WHERE entry_type = 'Schema' "
400
							+ "AND public_id = ?";
401
						PreparedStatement pstmt = dbConn.prepareStatement(sql);
402
						pstmt.setString(1, doctype);
403
						ResultSet rs = pstmt.executeQuery();
404
						boolean hasRow = rs.next();
405
						if (hasRow) {
406
							catalogid = rs.getString(1);
407
						}
408
						pstmt.close();
409
					}// try
410
					finally {
411
						// Return dbconnection
412
						DBConnectionPool.returnDBConnection(dbConn, serialNumber);
413
					}// finally
414

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

    
424
				} catch (McdbDocNotFoundException mdnfe) {
425
					Vector<Integer> revList = null;
426
					
427
					try {
428
						revList = DBUtil.getRevListFromRevisionTable(docid);
429
					} catch (SQLException sqle) {
430
						logMetacat.error("SQL error when trying to get rev list for doc " + docid + " : " + sqle.getMessage());
431
						throw (new SAXException("Doc ID " + docid + " was not found and cannot be updated.")); 
432
					}
433
					
434
					if (revList.size() > 0) {
435
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was deleted and cannot be updated."));
436
					} else {
437
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was not found and cannot be updated.")); 
438
					}
439
				} catch (Exception e) {
440
                    throw (new SAXException("EML210SaxHandler.startElement - error with action " + 
441
                    		action + " : " + e.getMessage()));
442
				}
443
			}
444

    
445
			// Create the current node representation
446
			currentNode = new DBSAXNode(connection, qName, localName, parentNode,
447
					rootNode.getNodeID(), docid, doctype);
448
			// Use a local variable to store the element node id
449
			// If this element is a start point of subtree(section), it will be
450
			// stored otherwise, it will be discarded
451
			long startNodeId = currentNode.getNodeID();
452
			// Add all of the namespaces
453
			String prefix = null;
454
			String nsuri = null;
455
			Enumeration<String> prefixes = namespaces.keys();
456
			while (prefixes.hasMoreElements()) {
457
				prefix = prefixes.nextElement();
458
				nsuri = namespaces.get(prefix);
459
				endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
460
			}
461

    
462
			// Add all of the attributes
463
			for (int i = 0; i < atts.getLength(); i++) {
464
				String attributeName = atts.getQName(i);
465
				String attributeValue = atts.getValue(i);
466
				endNodeId = currentNode
467
						.setAttribute(attributeName, attributeValue, docid);
468

    
469
				// To handle name space and schema location if the attribute
470
				// name is xsi:schemaLocation. If the name space is in not
471
				// in catalog table it will be registered.
472
				if (attributeName != null
473
						&& attributeName.indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
474
					SchemaLocationResolver resolver = new SchemaLocationResolver(
475
							attributeValue);
476
					resolver.resolveNameSpace();
477

    
478
				} else if (attributeName != null && attributeName.equals(ID)) {
479

    
480
				}
481
			}// for
482

    
483
			// handle access stuff
484
			if (localName.equals(ACCESS)) {
485
				if (parentNode.getTagName().equals(EML)) {
486
					processingTopLevelAccess = true;
487
				} else if (parentNode.getTagName() == DISTRIBUTION) {
488
					processingAdditionalAccess = true;
489
				} else {
490
					// process other access embedded into resource level
491
					// module
492
					processingOtherAccess = true;
493
				}
494
				// create access object
495
				accessObject = new AccessSection();
496
				// set permission order
497
				String permOrder = currentNode.getAttribute(ORDER);
498
				accessObject.setPermissionOrder(permOrder);
499
				// set access id
500
				String accessId = currentNode.getAttribute(ID);
501
				accessObject.setSubTreeId(accessId);
502
				accessObject.setStartNodeId(startNodeId);
503
				accessObject.setDocId(docid);
504
				if (processingAdditionalAccess) {
505
					accessObject.setDataFileName(inlineDataFileName);
506
				}
507

    
508
				// load top level node stack to
509
				// currentUnchangableAccessModuleNodeStack
510
				if (processingTopLevelAccess && needToCheckAccessModule) {
511
					// get the node stack for
512
					currentUnchangeableAccessModuleNodeStack = topAccessSection
513
							.getSubTreeNodeStack();
514
				}
515
			} else if (localName.equals(DISTRIBUTION)) {
516
				distributionIndex++;
517
				currentDistributionSection = new DistributionSection(distributionIndex);
518

    
519
				// handle subtree info
520
				SubTree subTree = new SubTree();
521
				// set sub tree id
522
				subTree.setSubTreeId(String.valueOf(distributionIndex));
523
				// set sub tree start element name
524
				subTree.setStartElementName(currentNode.getTagName());
525
				// set start node number
526
				subTree.setStartNodeId(startNodeId);
527
				// add to stack, but it didn't get end node id yet
528
				subTreeInfoStack.push(subTree);
529
			}
530
			// Set up a access rule for allow
531
			else if (parentNode.getTagName() != null
532
					&& (parentNode.getTagName()).equals(ACCESS)
533
					&& localName.equals(ALLOW)) {
534

    
535
				accessRule = new AccessRule();
536

    
537
				// set permission type "allow"
538
				accessRule.setPermissionType(ALLOW);
539

    
540
			}
541
			// set up an access rule for deny
542
			else if (parentNode.getTagName() != null
543
					&& (parentNode.getTagName()).equals(ACCESS) && localName.equals(DENY)) {
544
				accessRule = new AccessRule();
545
				// set permission type "allow"
546
				accessRule.setPermissionType(DENY);
547
			}
548

    
549
			// Add the node to the stack, so that any text data can be
550
			// added as it is encountered
551
			nodeStack.push(currentNode);
552
			// Add the node to the vector used by thread for writing XML Index
553
			nodeIndex.addElement(currentNode);
554

    
555
			// compare top access level module
556
			if (processingTopLevelAccess && needToCheckAccessModule) {
557
				compareElementNameSpaceAttributes(
558
						currentUnchangeableAccessModuleNodeStack, namespaces, atts,
559
						localName, UPDATEACCESSERROR);
560

    
561
			}
562

    
563
			// store access module element and attributes into stored stack
564
			if (needToCheckAccessModule
565
					&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
566
				// stored the pull out nodes into storedNode stack
567
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT", localName,
568
						prefix, MetacatUtil.normalize(null));
569
				storedAccessNodeStack.push(nodeElement);
570
				for (int i = 0; i < atts.getLength(); i++) {
571
					String attributeName = atts.getQName(i);
572
					String attributeValue = atts.getValue(i);
573
					NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2, "ATTRIBUTE",
574
							attributeName, null, MetacatUtil.normalize(attributeValue));
575
					storedAccessNodeStack.push(nodeAttribute);
576
				}
577

    
578
			}
579

    
580
			// reset name space
581
			namespaces = null;
582
			namespaces = new Hashtable<String, String>();
583
		}// not inline data
584
		else {
585
			// we don't buffer the inline data in characters() method
586
			// so start character don't need to hand text node.
587

    
588
			// inline data may be the xml format.
589
			StringBuffer inlineElements = new StringBuffer();
590
			inlineElements.append("<").append(qName);
591
			// append attributes
592
			for (int i = 0; i < atts.getLength(); i++) {
593
				String attributeName = atts.getQName(i);
594
				String attributeValue = atts.getValue(i);
595
				inlineElements.append(" ");
596
				inlineElements.append(attributeName);
597
				inlineElements.append("=\"");
598
				inlineElements.append(attributeValue);
599
				inlineElements.append("\"");
600
			}
601
			// append namespace
602
			String prefix = null;
603
			String nsuri = null;
604
			Enumeration<String> prefixes = inlineDataNameSpace.keys();
605
			while (prefixes.hasMoreElements()) {
606
				prefix = prefixes.nextElement();
607
				nsuri = namespaces.get(prefix);
608
				inlineElements.append(" ");
609
				inlineElements.append("xmlns:");
610
				inlineElements.append(prefix);
611
				inlineElements.append("=\"");
612
				inlineElements.append(nsuri);
613
				inlineElements.append("\"");
614
			}
615
			inlineElements.append(">");
616
			// reset inline data name space
617
			inlineDataNameSpace = null;
618
			inlineDataNameSpace = new Hashtable<String, String>();
619
			// write inline data into file
620
			logMetacat.debug("the inline element data is: " + inlineElements.toString());
621
			writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
622
		}// else
623

    
624
	}
625

    
626
	private void compareElementNameSpaceAttributes(
627
			Stack<NodeRecord> unchangeableNodeStack,
628
			Hashtable<String, String> nameSpaces, Attributes attributes,
629
			String localName, String error) throws SAXException {
630
		// Get element subtree node stack (element node)
631
		NodeRecord elementNode = null;
632
		try {
633
			elementNode = unchangeableNodeStack.pop();
634
		} catch (EmptyStackException ee) {
635
			logMetacat.error("Node stack is empty for element data");
636
			throw new SAXException(error);
637
		}
638
		logMetacat.debug("current node type from xml is ELEMENT");
639
		logMetacat.debug("node type from stack: " + elementNode.getNodeType());
640
		logMetacat.debug("node name from xml document: " + localName);
641
		logMetacat.debug("node name from stack: " + elementNode.getNodeName());
642
		logMetacat.debug("node data from stack: " + elementNode.getNodeData());
643
		logMetacat.debug("node id is: " + elementNode.getNodeId());
644
		// if this node is not element or local name not equal or name space
645
		// not equals, throw an exception
646
		if (!elementNode.getNodeType().equals("ELEMENT")
647
				|| !localName.equals(elementNode.getNodeName()))
648
		// (uri != null && !uri.equals(elementNode.getNodePrefix())))
649
		{
650
			logMetacat.error("Inconsistence happened: ");
651
			logMetacat.error("current node type from xml is ELEMENT");
652
			logMetacat.error("node type from stack: " + elementNode.getNodeType());
653
			logMetacat.error("node name from xml document: " + localName);
654
			logMetacat.error("node name from stack: " + elementNode.getNodeName());
655
			logMetacat.error("node data from stack: " + elementNode.getNodeData());
656
			logMetacat.error("node id is: " + elementNode.getNodeId());
657
			throw new SAXException(error);
658
		}
659

    
660
		// compare namespace
661
		Enumeration<String> nameEn = nameSpaces.keys();
662
		while (nameEn.hasMoreElements()) {
663
			// Get namespacke node stack (element node)
664
			NodeRecord nameNode = null;
665
			try {
666
				nameNode = unchangeableNodeStack.pop();
667
			} catch (EmptyStackException ee) {
668
				logMetacat.error("Node stack is empty for namespace data");
669
				throw new SAXException(error);
670
			}
671

    
672
			String prefixName = nameEn.nextElement();
673
			String nameSpaceUri = nameSpaces.get(prefixName);
674
			if (!nameNode.getNodeType().equals("NAMESPACE")
675
					|| !prefixName.equals(nameNode.getNodeName())
676
					|| !nameSpaceUri.equals(nameNode.getNodeData())) {
677
				logMetacat.error("Inconsistence happened: ");
678
				logMetacat.error("current node type from xml is NAMESPACE");
679
				logMetacat.error("node type from stack: " + nameNode.getNodeType());
680
				logMetacat.error("current node name from xml is: " + prefixName);
681
				logMetacat.error("node name from stack: " + nameNode.getNodeName());
682
				logMetacat.error("current node data from xml is: " + nameSpaceUri);
683
				logMetacat.error("node data from stack: " + nameNode.getNodeData());
684
				logMetacat.error("node id is: " + nameNode.getNodeId());
685
				throw new SAXException(error);
686
			}
687

    
688
		}// while
689

    
690
		// compare attributes
691
		for (int i = 0; i < attributes.getLength(); i++) {
692
			NodeRecord attriNode = null;
693
			try {
694
				attriNode = unchangeableNodeStack.pop();
695

    
696
			} catch (EmptyStackException ee) {
697
				logMetacat.error("Node stack is empty for attribute data");
698
				throw new SAXException(error);
699
			}
700
			String attributeName = attributes.getQName(i);
701
			String attributeValue = attributes.getValue(i);
702
			logMetacat.debug("current node type from xml is ATTRIBUTE ");
703
			logMetacat.debug("node type from stack: " + attriNode.getNodeType());
704
			logMetacat.debug("current node name from xml is: " + attributeName);
705
			logMetacat.debug("node name from stack: " + attriNode.getNodeName());
706
			logMetacat.debug("current node data from xml is: " + attributeValue);
707
			logMetacat.debug("node data from stack: " + attriNode.getNodeData());
708
			logMetacat.debug("node id  is: " + attriNode.getNodeId());
709

    
710
			if (!attriNode.getNodeType().equals("ATTRIBUTE")
711
					|| !attributeName.equals(attriNode.getNodeName())
712
					|| !attributeValue.equals(attriNode.getNodeData())) {
713
				logMetacat.error("Inconsistence happened: ");
714
				logMetacat.error("current node type from xml is ATTRIBUTE ");
715
				logMetacat.error("node type from stack: " + attriNode.getNodeType());
716
				logMetacat.error("current node name from xml is: " + attributeName);
717
				logMetacat.error("node name from stack: " + attriNode.getNodeName());
718
				logMetacat.error("current node data from xml is: " + attributeValue);
719
				logMetacat.error("node data from stack: " + attriNode.getNodeData());
720
				logMetacat.error("node is: " + attriNode.getNodeId());
721
				throw new SAXException(error);
722
			}
723
		}// for
724

    
725
	}
726

    
727
	/* method to compare current text node and node in db */
728
	private void compareAccessTextNode(Stack<NodeRecord> nodeStack, StringBuffer text) throws SAXException {
729
		NodeRecord node = null;
730
		// get node from current stack
731
		try {
732
			node = nodeStack.pop();
733
		} catch (EmptyStackException ee) {
734
			logMetacat.error("Node stack is empty for text data in startElement for doc id " + docid);
735
			throw new SAXException("Access rules could not be found in database.");
736
		}
737

    
738
		String dbAccessData = node.getNodeData();
739
		String docAccessData = text.toString().trim();
740
		
741
		logMetacat.debug("Eml210SAXHandler.compareAccessTextNode - \n" +
742
					"\t access node type from db:       " + node.getNodeType() + "\n" +
743
					"\t access node data from db:       " + node.getNodeData() + "\n" +
744
					"\t access node data from document: " + text.toString());
745
		
746
		if (!node.getNodeType().equals("TEXT")
747
				|| !docAccessData.equals(dbAccessData)) {
748
			logMetacat.warn("Eml210SAXHandler.compareAccessTextNode - Access record mismatch: \n" +
749
					"\t access node type from db:       " + node.getNodeType() + "\n" +
750
					"\t access node data from db:       " + dbAccessData + "\n" +
751
					"\t access node data from document: " + docAccessData);
752
			
753
			throw new SAXException(UPDATEACCESSERROR + " [Eml210SAXHandler.compareAccessTextNode]");
754
		}// if
755
	}
756

    
757
	/** SAX Handler that is called for each XML text node */
758
	public void characters(char[] cbuf, int start, int len) throws SAXException {
759
		logMetacat.debug("CHARACTERS");
760
		if (!handleInlineData) {
761
			// buffer all text nodes for same element. This is for if text was
762
			// split into different nodes
763
			textBuffer.append(new String(cbuf, start, len));
764
			// set hittextnode true
765
			hitTextNode = true;
766
			// if text buffer .size is greater than max, write it to db.
767
			// so we can save memory
768
			if (textBuffer.length() > MAXDATACHARS) {
769
				logMetacat.debug("Write text into DB in charaters"
770
						+ " when text buffer size is greater than maxmum number");
771
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
772
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
773
				textBuffer = null;
774
				textBuffer = new StringBuffer();
775
			}
776
		} else {
777
			// this is inline data and write file system directly
778
			// we don't need to buffer it.
779
			StringBuffer inlineText = new StringBuffer();
780
			inlineText.append(new String(cbuf, start, len));
781
			logMetacat.debug("The inline text data write into file system: "
782
					+ inlineText.toString());
783
			writeInlineDataIntoFile(inlineDataFileWriter, inlineText);
784
		}
785
	}
786

    
787
	/** SAX Handler that is called at the end of each XML element */
788
	public void endElement(String uri, String localName, String qName)
789
			throws SAXException {
790
		logMetacat.debug("End ELEMENT " + qName);
791

    
792
		if (localName.equals(INLINE) && handleInlineData) {
793
			// Get the node from the stack
794
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
795
			logMetacat.debug("End of inline data");
796
			// close file writer
797
			try {
798
				inlineDataFileWriter.close();
799
				handleInlineData = false;
800
			} catch (IOException ioe) {
801
				throw new SAXException(ioe.getMessage());
802
			}
803

    
804
			// write put inline data file name into text buffer (without path)
805
			textBuffer = new StringBuffer(inlineDataFileName);
806
			// write file name into db
807
			endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
808
			// reset textbuff
809
			textBuffer = null;
810
			textBuffer = new StringBuffer();
811
			return;
812
		}
813

    
814
		if (!handleInlineData) {
815
			// Get the node from the stack
816
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
817
			String currentTag = currentNode.getTagName();
818

    
819
			// If before the end element, the parser hit text nodes and store
820
			// them into the buffer, write the buffer to data base. The reason
821
			// we put write database here is for xerces some time split text
822
			// node
823
			if (hitTextNode) {
824
				// get access value
825
				String data = null;
826
				// add principal
827
				if (currentTag.equals(PRINCIPAL) && accessRule != null) {
828
					data = (textBuffer.toString()).trim();
829
					accessRule.addPrincipal(data);
830

    
831
				} else if (currentTag.equals(PERMISSION) && accessRule != null) {
832
					data = (textBuffer.toString()).trim();
833
					// we combine different a permission into one value
834
					int permission = accessRule.getPermission();
835
					// add permission
836
					if (data.toUpperCase().equals(READSTRING)) {
837
						permission = permission | READ;
838
					} else if (data.toUpperCase().equals(WRITESTRING)) {
839
						permission = permission | WRITE;
840
					} else if (data.toUpperCase().equals(CHMODSTRING)) {
841
						permission = permission | CHMOD;
842
					} else if (data.toUpperCase().equals(ALLSTRING)) {
843
						permission = permission | ALL;
844
					}
845
					accessRule.setPermission(permission);
846
				} else if (currentTag.equals(REFERENCES)
847
						&& (processingTopLevelAccess || processingAdditionalAccess || processingOtherAccess)) {
848
					// get reference
849
					data = (textBuffer.toString()).trim();
850
					// put reference id into accessSection
851
					accessObject.setReferences(data);
852

    
853
				} else if (currentTag.equals(URL)) {
854
					// handle online data, make sure its'parent is online
855
					DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
856
					if (parentNode != null && parentNode.getTagName() != null
857
							&& parentNode.getTagName().equals(ONLINE)) {
858
						// if online data is in local metacat, add it to the
859
						// vector
860
						data = (textBuffer.toString()).trim();
861
						handleOnlineUrlDataFile(data);
862

    
863
					}// if
864
				}// else if
865
				
866
				// write text to db if it is not inline data
867
				logMetacat.debug("Write text into DB in End Element");
868

    
869
				// compare top level access module
870
				if (processingTopLevelAccess && needToCheckAccessModule) {
871
					compareAccessTextNode(currentUnchangeableAccessModuleNodeStack,
872
							textBuffer);
873
				}
874
				// write text node into db
875
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
876
			}
877
			
878
			if (needToCheckAccessModule
879
					&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
880
				// stored the pull out nodes into storedNode stack
881
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
882
						null, MetacatUtil.normalize(textBuffer.toString()));
883
				storedAccessNodeStack.push(nodeElement);
884

    
885
			}
886

    
887
			// set hitText false
888
			hitTextNode = false;
889
			// reset textbuff
890
			textBuffer = null;
891
			textBuffer = new StringBuffer();
892

    
893
			// hand sub stree stuff
894
			if (!subTreeInfoStack.empty()) {
895
				SubTree tree = subTreeInfoStack.peek();// get last
896
				// subtree
897
				if (tree != null && tree.getStartElementName() != null
898
						&& (tree.getStartElementName()).equals(currentTag)) {
899
					// find the end of sub tree and set the end node id
900
					tree.setEndNodeId(endNodeId);
901
					// add the subtree into the final store palace
902
					subTreeList.add(tree);
903
					// get rid of it from stack
904
					subTreeInfoStack.pop();
905
				}// if
906
			}// if
907

    
908
			// access stuff
909
			if (currentTag.equals(ALLOW) || currentTag.equals(DENY)) {
910
				// finish parser access rule and assign it to new one
911
				AccessRule newRule = accessRule;
912
				// add the new rule to access section object
913
				accessObject.addAccessRule(newRule);
914
				// reset access rule
915
				accessRule = null;
916
			} else if (currentTag.equals(ACCESS)) {
917
				// finish parsing an access section and assign it to new one
918
				DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
919

    
920
				accessObject.setEndNodeId(endNodeId);
921

    
922
				if (parentNode != null && parentNode.getTagName() != null
923
						&& parentNode.getTagName().equals(DISTRIBUTION)) {
924
					describesId.add(String.valueOf(distributionIndex));
925
					currentDistributionSection.setAccessSection(accessObject);
926
				}
927

    
928
				AccessSection newAccessObject = accessObject;
929

    
930
				if (newAccessObject != null) {
931

    
932
					// add the accessSection into a vector to store it
933
					// if it is not a reference, need to store it
934
					if (newAccessObject.getReferences() == null) {
935

    
936
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
937
						accessObjectList.add(newAccessObject);
938
					}
939
					if (processingTopLevelAccess) {
940

    
941
						// top level access control will handle whole document
942
						// -docid
943
						topLevelAccessControlMap.put(docid, newAccessObject);
944
						// reset processtopleveraccess tag
945

    
946
					}// if
947
					else if (processingAdditionalAccess) {
948
						// for additional control put everything in describes
949
						// value
950
						// and access object into hash
951
						for (int i = 0; i < describesId.size(); i++) {
952

    
953
							String subId = describesId.elementAt(i);
954
							if (subId != null) {
955
								additionalAccessControlMap.put(subId, newAccessObject);
956
							}// if
957
						}// for
958
						// add this hashtable in to vector
959

    
960
						additionalAccessMapList.add(additionalAccessControlMap);
961
						// reset this hashtable in order to store another
962
						// additional
963
						// accesscontrol
964
						additionalAccessControlMap = null;
965
						additionalAccessControlMap = new Hashtable<String, AccessSection>();
966
					}// if
967

    
968
				}// if
969
				// check if access node stack is empty after parsing top access
970
				// module
971

    
972
				if (needToCheckAccessModule && processingTopLevelAccess
973
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
974

    
975
					logMetacat.error("Access node stack is not empty after "
976
							+ "parsing access subtree");
977
					throw new SAXException(UPDATEACCESSERROR);
978

    
979
				}
980
				// reset access section object
981

    
982
				accessObject = null;
983

    
984
				// reset tmp stored node stack
985
				storedAccessNodeStack = null;
986
				storedAccessNodeStack = new Stack<NodeRecord>();
987

    
988
				// reset flag
989
				processingAdditionalAccess = false;
990
				processingTopLevelAccess = false;
991
				processingOtherAccess = false;
992

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

    
1030
	/**
1031
	 * SAX Handler that receives notification of comments in the DTD
1032
	 */
1033
	public void comment(char[] ch, int start, int length) throws SAXException {
1034
		logMetacat.debug("COMMENT");
1035
		if (!handleInlineData) {
1036
			if (!processingDTD) {
1037
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1038
				String str = new String(ch, start, length);
1039

    
1040
				// compare top level access module
1041
				if (processingTopLevelAccess && needToCheckAccessModule) {
1042
					compareCommentNode(currentUnchangeableAccessModuleNodeStack, str,
1043
							UPDATEACCESSERROR);
1044
				}
1045
				endNodeId = currentNode.writeChildNodeToDB("COMMENT", null, str, docid);
1046
				if (needToCheckAccessModule
1047
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
1048
					// stored the pull out nodes into storedNode stack
1049
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "COMMENT", null,
1050
							null, MetacatUtil.normalize(str));
1051
					storedAccessNodeStack.push(nodeElement);
1052

    
1053
				}
1054
			}
1055
		} else {
1056
			// inline data comment
1057
			StringBuffer inlineComment = new StringBuffer();
1058
			inlineComment.append("<!--");
1059
			inlineComment.append(new String(ch, start, length));
1060
			inlineComment.append("-->");
1061
			logMetacat.debug("inline data comment: " + inlineComment.toString());
1062
			writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1063
		}
1064
	}
1065

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

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

    
1114
	/** SAX Handler that is called at the start of Namespace */
1115
	public void startPrefixMapping(String prefix, String uri) throws SAXException {
1116
		logMetacat.debug("NAMESPACE");
1117
		if (!handleInlineData) {
1118
			namespaces.put(prefix, uri);
1119
		} else {
1120
			inlineDataNameSpace.put(prefix, uri);
1121
		}
1122
	}
1123

    
1124
	/**
1125
	 * SAX Handler that is called for each XML text node that is Ignorable white
1126
	 * space
1127
	 */
1128
	public void ignorableWhitespace(char[] cbuf, int start, int len) throws SAXException {
1129
		// When validation is turned "on", white spaces are reported here
1130
		// When validation is turned "off" white spaces are not reported here,
1131
		// but through characters() callback
1132
		logMetacat.debug("IGNORABLEWHITESPACE");
1133
		if (!handleInlineData) {
1134
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1135
				String data = new String(cbuf, start, len);
1136
				// compare whitespace in access top module
1137
				if (processingTopLevelAccess && needToCheckAccessModule) {
1138
					compareWhiteSpace(currentUnchangeableAccessModuleNodeStack, data,
1139
							UPDATEACCESSERROR);
1140
				}
1141
				// Write the content of the node to the database
1142
				if (needToCheckAccessModule
1143
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
1144
					// stored the pull out nodes into storedNode stack
1145
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
1146
							null, MetacatUtil.normalize(data));
1147
					storedAccessNodeStack.push(nodeElement);
1148

    
1149
				}
1150
				endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data, docid);
1151
		} else {
1152
			// This is inline data write to file directly
1153
			StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf, start, len));
1154
			writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1155
		}
1156

    
1157
	}
1158

    
1159
	/* Compare whitespace from xml and db */
1160
	private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string,
1161
			String error) throws SAXException {
1162
		NodeRecord node = null;
1163
		try {
1164
			node = nodeStack.pop();
1165
		} catch (EmptyStackException ee) {
1166
			logMetacat.error("the stack is empty for whitespace data");
1167
			throw new SAXException(error);
1168
		}
1169
		if (!node.getNodeType().equals("TEXT") || !string.equals(node.getNodeData())) {
1170
			logMetacat.error("Inconsistence happened: ");
1171
			logMetacat.error("current node type from xml is WHITESPACE TEXT");
1172
			logMetacat.error("node type from stack: " + node.getNodeType());
1173
			logMetacat.error("current node data from xml is: " + string);
1174
			logMetacat.error("node data from stack: " + node.getNodeData());
1175
			logMetacat.error("node is from stack: " + node.getNodeId());
1176
			throw new SAXException(error);
1177
		}// if
1178
	}
1179

    
1180
	/** SAX Handler that receives notification of end of the document */
1181
	public void endDocument() throws SAXException {
1182
		logMetacat.debug("end Document");
1183
		// There are some unchangable subtree didn't be compare
1184
		// This maybe cause user change the subtree id
1185
		if (!super.getIsRevisionDoc()) {
1186
			// write access rule to db
1187
			if (writeAccessRules) {
1188
				writeAccessRuleToDB();
1189
			}
1190
			// delete relation table
1191
			deleteRelations();
1192
			// write relations
1193
			for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1194
				String id = onlineDataFileIdInRelationVector.elementAt(i);
1195
				writeOnlineDataFileIdIntoRelationTable(id);
1196
			}
1197
		}
1198
	}
1199

    
1200
	/* The method to write all access rule into db */
1201
	private void writeAccessRuleToDB() throws SAXException {
1202
		// Delete old permssion
1203
		deletePermissionsInAccessTable();
1204
		// write top leve access rule
1205
		writeTopLevelAccessRuleToDB();
1206
		// write additional access rule
1207
		// writeAdditionalAccessRuleToDB();
1208
		writeAdditionalAccessRulesToDB();
1209
	}// writeAccessRuleToDB
1210

    
1211
	/* The method to write top level access rule into db. */
1212
	private void writeTopLevelAccessRuleToDB() throws SAXException {
1213
		// for top document level
1214
		AccessSection accessSection = topLevelAccessControlMap.get(docid);
1215
		boolean top = true;
1216
		String subSectionId = null;
1217
		if (accessSection != null) {
1218
			AccessSection accessSectionObj = accessSection;
1219

    
1220
			// if accessSection is not null and is not reference
1221
			if (accessSectionObj.getReferences() == null) {
1222
				// check for denyFirst permOrder
1223
				String permOrder = accessSectionObj.getPermissionOrder();
1224
				if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1225
					logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block");
1226
			    	return;
1227
			    }
1228
				// write the top level access module into xml_accesssubtree to
1229
				// store info and then when update to check if the user can
1230
				// update it or not
1231
				deleteAccessSubTreeRecord(docid);
1232
				writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1233

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

    
1245
			} else {
1246

    
1247
				// this is a reference and go trough the vector which contains
1248
				// all access object
1249
				String referenceId = accessSectionObj.getReferences();
1250
				boolean findAccessObject = false;
1251
				logMetacat.debug("referered id for top access: " + referenceId);
1252
				for (int i = 0; i < accessObjectList.size(); i++) {
1253
					AccessSection accessObj = accessObjectList.elementAt(i);
1254
					String accessObjId = accessObj.getSubTreeId();
1255
					// check for denyFirst permOrder
1256
					String permOrder = accessObj.getPermissionOrder();
1257
					if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1258
						logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block, subtree id: " + accessObjId);
1259
				    	continue;
1260
				    }
1261
					if (referenceId != null && accessObj != null
1262
							&& referenceId.equals(accessObjId)) {
1263
						// make sure the user didn't change any thing in this
1264
						// access moduel
1265
						// too if user doesn't have all permission
1266
						if (needToCheckAccessModule) {
1267

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

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

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

    
1325
	}// writeTopLevelAccessRuletoDB
1326

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

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

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

    
1379
	/* The method to write additional access rule into db. */
1380
	private void writeAdditionalAccessRulesToDB() throws SAXException {
1381
		
1382
		// Iterate through every distribution and write access sections for data and inline
1383
		// types to the database
1384
		for (DistributionSection distributionSection : allDistributionSections) {			
1385
			// we're only interested in data and inline distributions
1386
			int distributionType = distributionSection.getDistributionType();
1387
			if (distributionType == DistributionSection.DATA_DISTRIBUTION
1388
					|| distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION) {
1389
				AccessSection accessSection = distributionSection.getAccessSection();
1390
				
1391
				// If the distribution doesn't have an access section, we continue.
1392
				if (accessSection == null) {
1393
					continue;		
1394
				}
1395
				
1396
				// check for denyFirst permOrder
1397
				String permOrder = accessSection.getPermissionOrder();
1398
				if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1399
					logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block: " + distributionSection.getDataFileName());
1400
			    	continue;
1401
			    }
1402
				
1403
				// We want to check file permissions for all online data updates and inserts, or for 
1404
				// inline updates.
1405
//				if (distributionType == DistributionSection.DATA_DISTRIBUTION
1406
//						|| (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE")) {
1407

    
1408
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1409
					try {
1410
						// check for the previous version for permissions on update
1411
						// we need to look up the docid from the guid, if we have it
1412
						String dataDocid = distributionSection.getDataFileName();
1413
						try {
1414
							dataDocid = IdentifierManager.getInstance().getLocalId(dataDocid);
1415
						} catch (McdbDocNotFoundException mcdbnfe) {
1416
							// ignore
1417
							logMetacat.warn("Could not find guid/docid mapping for " + dataDocid);
1418
						}
1419
						String previousDocid = dataDocid;
1420
						if (action == "UPDATE") {
1421
							String docidWithoutRev = DocumentUtil.getDocIdFromString(dataDocid);
1422
							int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docidWithoutRev);
1423
							if (latestRevision > 0) {
1424
								previousDocid = docidWithoutRev + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
1425
							}
1426
						}
1427
						
1428
						// check both the previous and current data permissions
1429
						// see: https://projects.ecoinformatics.org/ecoinfo/issues/5647
1430
						PermissionController controller = new PermissionController(previousDocid);
1431
						PermissionController currentController = new PermissionController(dataDocid);
1432

    
1433
						if (AccessionNumber.accNumberUsed(docid)
1434
								&& 
1435
								!(controller.hasPermission(user, groups, "WRITE") 
1436
										|| currentController.hasPermission(user, groups, "WRITE")
1437
										)
1438
								) {
1439
							throw new SAXException(UPDATEACCESSERROR + " id: " + dataDocid);
1440
						}
1441
					} catch (SQLException sqle) {
1442
						throw new SAXException(
1443
								"Database error checking user permissions: "
1444
										+ sqle.getMessage());
1445
					} catch (Exception e) {
1446
						throw new SAXException(
1447
								"General error checking user permissions: "
1448
										+ e.getMessage());
1449
					}
1450
				} else if (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE") {
1451
					try {
1452
						
1453
						// check for the previous version for permissions
1454
						int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docid);
1455
						String previousDocid = 
1456
							docid + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
1457
						PermissionController controller = new PermissionController(previousDocid);
1458

    
1459
						if (!controller.hasPermission(user, groups, "WRITE")) {
1460
							throw new SAXException(UPDATEACCESSERROR);
1461
						}
1462
					} catch (SQLException sqle) {
1463
						throw new SAXException(
1464
								"Database error checking user permissions: "
1465
										+ sqle.getMessage());
1466
					} catch (Exception e) {
1467
						throw new SAXException(
1468
								"General error checking user permissions: "
1469
										+ e.getMessage());
1470
					}
1471
				}
1472
				
1473
				// clear previous versions first
1474
				deleteAccessRule(accessSection, false);
1475
				
1476
				// now write the new ones
1477
				String subSectionId = Integer.toString(distributionSection.getDistributionId());
1478
				writeGivenAccessRuleIntoDB(accessSection, false, subSectionId);
1479
			}
1480

    
1481
		}
1482
		
1483
		
1484
	}
1485
	
1486
	/**
1487
	 *  delete existing access for given access rule
1488
	 *  
1489
	 */
1490
	private void deleteAccessRule(AccessSection accessSection, boolean topLevel) throws SAXException {
1491
		
1492
		if (accessSection == null) {
1493
			throw new SAXException("The access object is null");
1494
		}
1495
		
1496
		PreparedStatement pstmt = null;
1497

    
1498
		String sql = null;
1499
		sql = "DELETE FROM xml_access WHERE guid = ?";
1500
			
1501
		try {
1502

    
1503
			pstmt = connection.prepareStatement(sql);
1504
			// Increase DBConnection usage count
1505
			connection.increaseUsageCount(1);
1506
			// Bind the values to the query
1507
			String guid = null;
1508
			if (topLevel) {
1509
				try {
1510
					guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
1511
				} catch (NumberFormatException e) {
1512
					throw new SAXException(e.getMessage(), e);
1513
				} catch (McdbDocNotFoundException e) {
1514
					// register the default mapping now
1515
					guid = docid + "." + revision;
1516
					IdentifierManager.getInstance().createMapping(guid, guid);
1517
				}
1518
			} else {
1519
				guid = accessSection.getDataFileName();
1520
				
1521
			}
1522
			pstmt.setString(1, guid);
1523
			logMetacat.debug("guid in accesstable: " + guid);
1524
			
1525
			logMetacat.debug("running sql: " + pstmt.toString());
1526
			pstmt.execute();
1527
			
1528
			pstmt.close();
1529
		}// try
1530
		catch (SQLException e) {
1531
			throw new SAXException("EMLSAXHandler.deleteAccessRule(): "
1532
					+ e.getMessage());
1533
		}// catch
1534
		finally {
1535
			try {
1536
				pstmt.close();
1537
			} catch (SQLException ee) {
1538
				throw new SAXException("EMLSAXHandler.deleteAccessRule(): "
1539
						+ ee.getMessage());
1540
			}
1541
		}// finally
1542

    
1543
	}
1544

    
1545
	/* Write a given access rule into db */
1546
	private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1547
			boolean topLevel, String subSectionId) throws SAXException {
1548
		if (accessSection == null) {
1549
			throw new SAXException("The access object is null");
1550
		}
1551

    
1552
		String guid = null;
1553
		String referencedGuid = accessSection.getDataFileName();
1554

    
1555
		try {
1556
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
1557
		} catch (NumberFormatException e) {
1558
			throw new SAXException(e.getMessage(), e);
1559
		} catch (McdbDocNotFoundException e) {
1560
			// register the default mapping now
1561
			guid = docid + "." + revision;
1562
			IdentifierManager.getInstance().createMapping(guid, guid);
1563
		}
1564
		
1565
		String permOrder = accessSection.getPermissionOrder();
1566
		String sql = null;
1567
		PreparedStatement pstmt = null;
1568
		if (topLevel) {
1569
			sql = "INSERT INTO xml_access (guid, principal_name, permission, "
1570
					+ "perm_type, perm_order, accessfileid) VALUES "
1571
					+ " (?, ?, ?, ?, ?, ?)";
1572
		} else {
1573
			sql = "INSERT INTO xml_access (guid,principal_name, "
1574
					+ "permission, perm_type, perm_order, accessfileid, subtreeid"
1575
					+ ") VALUES" + " (?, ?, ?, ?, ?, ?, ?)";
1576
		}
1577
		try {
1578

    
1579
			pstmt = connection.prepareStatement(sql);
1580
			// Increase DBConnection usage count
1581
			connection.increaseUsageCount(1);
1582
			// Bind the values to the query
1583
			pstmt.setString(6, guid);
1584
			logMetacat.debug("Accessfileid in accesstable: " + guid);
1585
			pstmt.setString(5, permOrder);
1586
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1587
			// if it is not top level, set subsection id
1588
			if (topLevel) {
1589
				pstmt.setString(1, guid);
1590
				logMetacat.debug("Guid in accesstable: " + guid);
1591
			}
1592
			if (!topLevel) {
1593
				// use the referenced guid
1594
				pstmt.setString(1, referencedGuid );
1595
				logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1596

    
1597
				// for subtree should specify the
1598
				if (subSectionId == null) {
1599
					throw new SAXException("The subsection is null");
1600
				}
1601

    
1602
				pstmt.setString(7, subSectionId);
1603
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1604
				
1605
				// Save guid of data object for syncing of access policy with CN after parsing
1606
				// is successful (see DocumentImpl.write)
1607
				guidsToSync.add(referencedGuid);
1608
			}
1609

    
1610
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1611
			// go through every rule
1612
			for (int i = 0; i < accessRules.size(); i++) {
1613
				AccessRule rule = accessRules.elementAt(i);
1614
				String permType = rule.getPermissionType();
1615
				int permission = rule.getPermission();
1616
				pstmt.setInt(3, permission);
1617
				logMetacat.debug("permission in accesstable: " + permission);
1618
				pstmt.setString(4, permType);
1619
				logMetacat.debug("Permtype in accesstable: " + permType);
1620
				// go through every principle in rule
1621
				Vector<String> nameVector = rule.getPrincipal();
1622
				for (int j = 0; j < nameVector.size(); j++) {
1623
					String prName = nameVector.elementAt(j);
1624
					pstmt.setString(2, prName);
1625
					logMetacat.debug("Principal in accesstable: " + prName);
1626
					logMetacat.debug("running sql: " + pstmt.toString());
1627
					pstmt.execute();
1628
				}// for
1629
			}// for
1630
			pstmt.close();
1631
		}// try
1632
		catch (SQLException e) {
1633
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1634
					+ e.getMessage());
1635
		}// catch
1636
		finally {
1637
			try {
1638
				pstmt.close();
1639
			} catch (SQLException ee) {
1640
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1641
						+ ee.getMessage());
1642
			}
1643
		}// finally
1644
		
1645
		// for D1, refresh the entries
1646
		HazelcastService.getInstance().refreshSystemMetadataEntry(guid);
1647
		HazelcastService.getInstance().refreshSystemMetadataEntry(referencedGuid);
1648

    
1649
	}// writeGivenAccessRuleIntoDB
1650

    
1651
	/* Delete from db all permission for resources related to the document, if any */
1652
	private void deletePermissionsInAccessTable() throws SAXException {
1653
		PreparedStatement pstmt = null;
1654
		try {
1655
			
1656
			String sql = "DELETE FROM xml_access " +
1657
					// the file defines access for another file
1658
					"WHERE accessfileid IN " +
1659
						"(SELECT guid from identifier where docid = ? and rev = ?) " +
1660
					// the described file has other versions describing it	
1661
					"OR guid IN " +
1662
						"(SELECT xa.guid from xml_access xa, identifier id" +
1663
						" WHERE xa.accessfileid = id.guid " +
1664
						" AND id.docid = ?" +
1665
						" AND id.rev = ?)";
1666
			// delete all acl records for resources related to @aclid if any
1667
			pstmt = connection.prepareStatement(sql);
1668
			pstmt.setString(1, docid);
1669
			pstmt.setInt(2, Integer.valueOf(revision));
1670
			// second part of query
1671
			pstmt.setString(3, docid);
1672
			pstmt.setInt(4, Integer.valueOf(revision));
1673
			// Increase DBConnection usage count
1674
			connection.increaseUsageCount(1);
1675
			logMetacat.debug("running sql: " + sql);
1676
			pstmt.execute();
1677

    
1678
		} catch (SQLException e) {
1679
			throw new SAXException(e.getMessage());
1680
		} finally {
1681
			try {
1682
				pstmt.close();
1683
			} catch (SQLException ee) {
1684
				throw new SAXException(ee.getMessage());
1685
			}
1686
		}
1687
	}// deletePermissionsInAccessTable
1688

    
1689
	/*
1690
	 * In order to make sure only usr has "all" permission can update access
1691
	 * subtree in eml document we need to keep access subtree info in
1692
	 * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1693
	 */
1694
	private void writeAccessSubTreeIntoDB(AccessSection accessSection, String level)
1695
			throws SAXException {
1696
		if (accessSection == null) {
1697
			throw new SAXException("The access object is null");
1698
		}
1699

    
1700
		String sql = null;
1701
		PreparedStatement pstmt = null;
1702
		sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1703
				+ "subtreeid, startnodeid, endnodeid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1704
		try {
1705

    
1706
			pstmt = connection.prepareStatement(sql);
1707
			// Increase DBConnection usage count
1708
			connection.increaseUsageCount(1);
1709
			long startNodeId = accessSection.getStartNodeId();
1710
			long endNodeId = accessSection.getEndNodeId();
1711
			String sectionId = accessSection.getSubTreeId();
1712
			// Bind the values to the query
1713
			pstmt.setString(1, docid);
1714
			logMetacat.debug("Docid in access-subtreetable: " + docid);
1715
			pstmt.setLong(2, (new Long(revision)).longValue());
1716
			logMetacat.debug("rev in accesssubtreetable: " + revision);
1717
			pstmt.setString(3, level);
1718
			logMetacat.debug("contorl level in access-subtree table: " + level);
1719
			pstmt.setString(4, sectionId);
1720
			logMetacat.debug("Subtree id in access-subtree table: " + sectionId);
1721
			pstmt.setLong(5, startNodeId);
1722
			logMetacat.debug("Start node id is: " + startNodeId);
1723
			pstmt.setLong(6, endNodeId);
1724
			logMetacat.debug("End node id is: " + endNodeId);
1725
			logMetacat.debug("running sql: " + pstmt.toString());
1726
			pstmt.execute();
1727
			pstmt.close();
1728
		}// try
1729
		catch (SQLException e) {
1730
			throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1731
					+ e.getMessage());
1732
		}// catch
1733
		finally {
1734
			try {
1735
				pstmt.close();
1736
			} catch (SQLException ee) {
1737
				throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1738
						+ ee.getMessage());
1739
			}
1740
		}// finally
1741

    
1742
	}// writeAccessSubtreeIntoDB
1743

    
1744
	/* Delete every access subtree record from xml_accesssubtree. */
1745
	private void deleteAccessSubTreeRecord(String docId) throws SAXException {
1746
		PreparedStatement pstmt = null;
1747
		try {
1748
			String sql = "DELETE FROM xml_accesssubtree WHERE docid = ?";
1749
			// delete all acl records for resources related to @aclid if any
1750
			pstmt = connection.prepareStatement(sql);
1751
			pstmt.setString(1, docId);
1752
			// Increase DBConnection usage count
1753
			connection.increaseUsageCount(1);
1754
			logMetacat.debug("running sql: " + sql);
1755
			pstmt.execute();
1756

    
1757
		} catch (SQLException e) {
1758
			throw new SAXException(e.getMessage());
1759
		} finally {
1760
			try {
1761
				pstmt.close();
1762
			} catch (SQLException ee) {
1763
				throw new SAXException(ee.getMessage());
1764
			}
1765
		}
1766
	}// deleteAccessSubTreeRecord
1767

    
1768
	// open a file writer for writing inline data to file
1769
	private Writer createInlineDataFileWriter(String fileName, String encoding) throws SAXException {
1770
		Writer writer = null;
1771
		String path;
1772
		try {
1773
			path = PropertyService.getProperty("application.inlinedatafilepath");
1774
		} catch (PropertyNotFoundException pnfe) {
1775
			throw new SAXException(pnfe.getMessage());
1776
		}
1777
		/*
1778
		 * File inlineDataDirectory = new File(path);
1779
		 */
1780
		String newFile = path + "/" + fileName;
1781
		logMetacat.debug("inline file name: " + newFile);
1782
		try {
1783
			// true means append
1784
			writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
1785
		} catch (IOException ioe) {
1786
			throw new SAXException(ioe.getMessage());
1787
		}
1788
		return writer;
1789
	}
1790

    
1791
	// write inline data into file system and return file name(without path)
1792
	private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
1793
			throws SAXException {
1794
		try {
1795
			writer.write(data.toString());
1796
			writer.flush();
1797
		} catch (Exception e) {
1798
			throw new SAXException(e.getMessage());
1799
		}
1800
	}
1801

    
1802

    
1803

    
1804
	// if xml file failed to upload, we need to call this method to delete
1805
	// the inline data already in file system
1806
	public void deleteInlineFiles() throws SAXException {
1807
		if (!inlineFileIdList.isEmpty()) {
1808
			for (int i = 0; i < inlineFileIdList.size(); i++) {
1809
				String fileName = inlineFileIdList.elementAt(i);
1810
				deleteInlineDataFile(fileName);
1811
			}
1812
		}
1813
	}
1814

    
1815
	/* delete the inline data file */
1816
	private void deleteInlineDataFile(String fileName) throws SAXException {
1817
		String path;
1818
		try {
1819
			path = PropertyService.getProperty("application.inlinedatafilepath");
1820
		} catch (PropertyNotFoundException pnfe) {
1821
			throw new SAXException("Could not find inline data file path: "
1822
					+ pnfe.getMessage());
1823
		}
1824
		File inlineDataDirectory = new File(path);
1825
		File newFile = new File(inlineDataDirectory, fileName);
1826
		newFile.delete();
1827

    
1828
	}
1829

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

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

    
1893
	}// writeOnlineDataFileIdIntoRelationTable
1894

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

    
1910
		if (currentDistributionSection == null) {
1911
			throw new SAXException("Trying to set the online file name for a null"
1912
					+ " distribution section");
1913
		}
1914

    
1915
		// if the url is not in ecogrid protocol, null will be returned
1916
		String accessionNumber = DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
1917
		
1918
		// check he accession number and get docid.rev if we can
1919
		String docid = null;
1920
		int rev = 0;
1921
		if (accessionNumber != null) {
1922
			// get rid of revision number to get the docid.
1923
			try {
1924
				docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
1925
				rev = DocumentUtil.getRevisionFromAccessionNumber(accessionNumber);
1926
			} catch (Exception e) {
1927
				logMetacat.warn(e.getClass().getName() + " - Problem parsing accession number for: " + accessionNumber + ". Message: " + e.getMessage());
1928
				accessionNumber = null;
1929
			}
1930
		}
1931
		
1932
		if (accessionNumber == null) {
1933
			// the accession number is null if the url does not references a
1934
			// local data file (url would start with "ecogrid://"
1935
			currentDistributionSection
1936
					.setDistributionType(DistributionSection.ONLINE_DATA_DISTRIBUTION);
1937
		} else {
1938
			// look up the guid using docid/rev
1939
			String guid = null;
1940
			try {
1941
				guid = IdentifierManager.getInstance().getGUID(docid, rev);
1942
			} catch (McdbDocNotFoundException e1) {
1943
				// no need to do this if we are not writing access rules for the data
1944
				if (!writeAccessRules) {
1945
					logMetacat.warn("Not configured to write access rules for data referenced by: " + url);
1946
					return;
1947
				}
1948
				guid = docid + "." + rev;
1949
				IdentifierManager.getInstance().createMapping(guid, guid);
1950
			}
1951

    
1952
			currentDistributionSection
1953
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
1954
			currentDistributionSection.setDataFileName(guid);
1955

    
1956
			// distributionOnlineFileName = docid;
1957
			onlineDataFileIdInRelationVector.add(guid);
1958
			try {				
1959
				if (!AccessionNumber.accNumberUsed(docid)) {
1960
					onlineDataFileIdInTopAccessVector.add(guid);
1961
				} else {
1962
					// check the previous revision if we have it
1963
					int previousRevision = rev;
1964
					Vector<Integer> revisions = DBUtil.getRevListFromRevisionTable(docid);
1965
					if (revisions != null && revisions.size() > 0) {
1966
						previousRevision = revisions.get(revisions.size() - 1);
1967
					}
1968
					String previousDocid = 
1969
						docid + PropertyService.getProperty("document.accNumSeparator") + previousRevision;
1970

    
1971
					// check EITHER previous or current id for access rules
1972
					// see: https://projects.ecoinformatics.org/ecoinfo/issues/5647
1973
					PermissionController previousController = new PermissionController(previousDocid);
1974
					PermissionController currentController = new PermissionController(accessionNumber);				
1975
					if (previousController.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
1976
							|| previousController.hasPermission(user, groups, AccessControlInterface.CHMODSTRING)
1977
							|| currentController.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
1978
							|| currentController.hasPermission(user, groups, AccessControlInterface.CHMODSTRING)
1979
							) {
1980
						onlineDataFileIdInTopAccessVector.add(guid);
1981
					} else {
1982
						throw new SAXException(UPDATEACCESSERROR);
1983
					}
1984
				} 
1985
			}// try
1986
			catch (Exception e) {
1987
				logMetacat.error("Eorr in "
1988
								+ "Eml210SAXHanlder.handleOnlineUrlDataFile is "
1989
								+ e.getMessage());
1990
				throw new SAXException(e.getMessage());
1991
			}
1992
		}
1993
	}
1994
}
(32-32/64)