Project

General

Profile

1 2169 sgarg
/**
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$'
10
 *     '$Date$'
11
 * '$Revision$'
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 5752 leinfelder
import java.io.FileOutputStream;
32 2169 sgarg
import java.io.IOException;
33 5752 leinfelder
import java.io.OutputStreamWriter;
34
import java.io.Writer;
35 2169 sgarg
import java.sql.PreparedStatement;
36
import java.sql.ResultSet;
37
import java.sql.SQLException;
38 6595 leinfelder
import java.util.Date;
39 2169 sgarg
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 2663 sgarg
import org.apache.log4j.Logger;
46 2169 sgarg
import org.xml.sax.Attributes;
47
import org.xml.sax.SAXException;
48
49 7475 leinfelder
import edu.ucsb.nceas.utilities.access.AccessControlInterface;
50 5090 daigle
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
51
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
52 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
53
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
54 6802 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
55 5030 daigle
import edu.ucsb.nceas.metacat.properties.PropertyService;
56 5025 daigle
import edu.ucsb.nceas.metacat.util.DocumentUtil;
57 4698 daigle
import edu.ucsb.nceas.metacat.util.MetacatUtil;
58 4080 daigle
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
59
60 2169 sgarg
/**
61 4472 daigle
 * A database aware Class implementing callback methods for the SAX parser to
62 2169 sgarg
 * call when processing the XML stream and generating events
63
 */
64 4678 daigle
public class Eml210SAXHandler extends DBSAXHandler implements AccessControlInterface {
65 2169 sgarg
66 5311 daigle
	private boolean processingTopLevelAccess = false;
67 2169 sgarg
68 5311 daigle
	private boolean processingAdditionalAccess = false;
69 2169 sgarg
70 5311 daigle
	private boolean processingOtherAccess = false;
71 2169 sgarg
72 4678 daigle
	private AccessSection accessObject = null;
73 2169 sgarg
74 4678 daigle
	private AccessRule accessRule = null;
75 2169 sgarg
76 5311 daigle
	// all access rules
77
	private Vector<AccessSection> accessObjectList = new Vector<AccessSection>();
78 2169 sgarg
79 4678 daigle
	private Hashtable<String, AccessSection> topLevelAccessControlMap = new Hashtable<String, AccessSection>();
80 2169 sgarg
81 5311 daigle
	// 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 4678 daigle
	private Stack<SubTree> subTreeInfoStack = new Stack<SubTree>();
91 2169 sgarg
92 5311 daigle
	private Vector<SubTree> subTreeList = new Vector<SubTree>();
93 4678 daigle
94
	private boolean needToCheckAccessModule = false;
95 2169 sgarg
96 4678 daigle
	private Vector<AccessSection> unChangeableAccessSubTreeVector = new Vector<AccessSection>();
97 2169 sgarg
98 4678 daigle
	private Stack<NodeRecord> currentUnchangeableAccessModuleNodeStack = new Stack<NodeRecord>();
99 2169 sgarg
100 4678 daigle
	private AccessSection topAccessSection;
101 2169 sgarg
102 4678 daigle
	// 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 2169 sgarg
106 4678 daigle
	// vector stored the data file id which will be write into relation table
107
	private Vector<String> onlineDataFileIdInRelationVector = new Vector<String>();
108 2169 sgarg
109 4678 daigle
	// 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 2169 sgarg
113 4678 daigle
	// Indicator of inline data
114
	private boolean handleInlineData = false;
115 2169 sgarg
116 4678 daigle
	private Hashtable<String, String> inlineDataNameSpace = null;
117 2169 sgarg
118 5752 leinfelder
	private Writer inlineDataFileWriter = null;
119 2169 sgarg
120 4678 daigle
	private String inlineDataFileName = null;
121 2169 sgarg
122 4678 daigle
	DistributionSection currentDistributionSection = null;
123 2169 sgarg
124 4678 daigle
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
125 8560 slaughter
    private Vector<String> guidsToSync;
126 4678 daigle
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 4472 daigle
	private int distributionIndex = 0;
132 2169 sgarg
133 4678 daigle
	// private String distributionOnlineFileName = null;
134
135 4472 daigle
	// This is used to delete inline files if the xml does not parse correctly.
136 4678 daigle
	private Vector<String> inlineFileIdList = new Vector<String>();
137 2169 sgarg
138 4678 daigle
	// Constant
139
	private static final String EML = "eml";
140 2169 sgarg
141 4678 daigle
	private static final String DISTRIBUTION = "distribution";
142 2169 sgarg
143 4678 daigle
	private static final String ORDER = "order";
144 2169 sgarg
145 4678 daigle
	private static final String ID = "id";
146 2169 sgarg
147 4678 daigle
	private static final String REFERENCES = "references";
148 2169 sgarg
149 4678 daigle
	public static final String INLINE = "inline";
150 2169 sgarg
151 4678 daigle
	private static final String ONLINE = "online";
152 2169 sgarg
153 4678 daigle
	private static final String URL = "url";
154 2169 sgarg
155 4678 daigle
	// private static final String PERMISSIONERROR = "User tried to update a
156
	// subtree "
157
	// + "when they don't have write permission!";
158 2169 sgarg
159 4678 daigle
	private static final String UPDATEACCESSERROR = "User tried to update an "
160
			+ "access module when they don't have \"ALL\" permission!";
161 2169 sgarg
162 4678 daigle
	private static final String TOPLEVEL = "top";
163 2169 sgarg
164 4678 daigle
	private static final String SUBTREELEVEL = "subtree";
165 2169 sgarg
166 4678 daigle
	private static final String RELATION = "Provides info for";
167 2663 sgarg
168 4678 daigle
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
169 2169 sgarg
170 4678 daigle
	/**
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 8560 slaughter
			Date createDate, Date updateDate, boolean writeAccessRules, Vector<String> guidsToSync) throws SAXException {
196 4678 daigle
		super(conn, action, docid, revision, user, groups, pub, serverCode, createDate,
197 7128 leinfelder
				updateDate, writeAccessRules);
198 8560 slaughter
199
		this.guidsToSync = guidsToSync;
200 4678 daigle
		// Get the unchangeable subtrees (user doesn't have write permission)
201
		try {
202 2169 sgarg
203 6744 leinfelder
			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
						&& action != null) {
213
					needToCheckAccessModule = true;
214
					unChangeableAccessSubTreeVector = getAccessSubTreeListFromDB();
215
				}
216 4678 daigle
			}
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 2169 sgarg
234 4678 daigle
		try {
235 2169 sgarg
236 4678 daigle
			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 2169 sgarg
243 4678 daigle
			// 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 2169 sgarg
283 4678 daigle
	/** 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 2169 sgarg
293 4678 daigle
		DBSAXNode parentNode = null;
294
		DBSAXNode currentNode = null;
295 2169 sgarg
296 4678 daigle
		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 2169 sgarg
304 4678 daigle
			// 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 5025 daigle
				String docidWithoutRev = DocumentUtil.getDocIdFromString(docid);
311 4678 daigle
				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 4472 daigle
						+ distributionIndex;
321 5752 leinfelder
				inlineDataFileWriter = createInlineDataFileWriter(inlineDataFileName, encoding);
322 4678 daigle
				// 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 2169 sgarg
329 4678 daigle
			}
330 2169 sgarg
331 4678 daigle
			// 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 2169 sgarg
335 4678 daigle
				// compare top level access module
336 5311 daigle
				if (processingTopLevelAccess && needToCheckAccessModule) {
337
					compareAccessTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer);
338 4678 daigle
				}
339 2169 sgarg
340 4678 daigle
				if (needToCheckAccessModule
341 5311 daigle
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
342 4678 daigle
					// stored the pull out nodes into storedNode stack
343
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
344 4698 daigle
							null, MetacatUtil.normalize(textBuffer.toString()));
345 4678 daigle
					storedAccessNodeStack.push(nodeElement);
346 2169 sgarg
347 4678 daigle
				}
348 2169 sgarg
349 4678 daigle
				// write the textbuffer into db for parent node.
350
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, parentNode);
351
				// rest hitTextNode
352
				hitTextNode = false;
353
				// reset textbuffer
354
				textBuffer = null;
355
				textBuffer = new StringBuffer();
356 2169 sgarg
357 4678 daigle
			}
358 2169 sgarg
359 4678 daigle
			// Document representation that points to the root document node
360
			if (atFirstElement) {
361
				atFirstElement = false;
362
				// If no DOCTYPE declaration: docname = root element
363
				// doctype = root element name or name space
364
				if (docname == null) {
365
					docname = localName;
366
					// if uri isn't null doctype = uri(namespace)
367
					// othewise root element
368
					if (uri != null && !(uri.trim()).equals("")) {
369
						doctype = uri;
370
					} else {
371
						doctype = docname;
372
					}
373
					logMetacat.debug("DOCNAME-a: " + docname);
374
					logMetacat.debug("DOCTYPE-a: " + doctype);
375
				} else if (doctype == null) {
376
					// because docname is not null and it is declared in dtd
377
					// so could not be in schema, no namespace
378
					doctype = docname;
379
					logMetacat.debug("DOCTYPE-b: " + doctype);
380
				}
381
				rootNode.writeNodename(docname);
382
				try {
383
					// for validated XML Documents store a reference to XML DB
384
					// Catalog. Because this is select statement and it needn't
385
					// roll back if insert document action failed. In order to
386
					// decrease DBConnection usage count, we get a new
387
					// dbconnection from pool String catalogid = null;
388
					DBConnection dbConn = null;
389
					int serialNumber = -1;
390 2169 sgarg
391 4678 daigle
					try {
392
						// Get dbconnection
393
						dbConn = DBConnectionPool
394
								.getDBConnection("DBSAXHandler.startElement");
395
						serialNumber = dbConn.getCheckOutSerialNumber();
396 6606 leinfelder
397
						String sql = "SELECT catalog_id FROM xml_catalog "
398
							+ "WHERE entry_type = 'Schema' "
399
							+ "AND public_id = ?";
400
						PreparedStatement pstmt = dbConn.prepareStatement(sql);
401
						pstmt.setString(1, doctype);
402
						ResultSet rs = pstmt.executeQuery();
403 4678 daigle
						boolean hasRow = rs.next();
404
						if (hasRow) {
405
							catalogid = rs.getString(1);
406
						}
407 6606 leinfelder
						pstmt.close();
408 4678 daigle
					}// try
409
					finally {
410
						// Return dbconnection
411
						DBConnectionPool.returnDBConnection(dbConn, serialNumber);
412
					}// finally
413 2169 sgarg
414 4678 daigle
					// 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 2169 sgarg
423 5200 daigle
				} catch (McdbDocNotFoundException mdnfe) {
424
					Vector<Integer> revList = null;
425
426
					try {
427
						revList = DBUtil.getRevListFromRevisionTable(docid);
428
					} catch (SQLException sqle) {
429
						logMetacat.error("SQL error when trying to get rev list for doc " + docid + " : " + sqle.getMessage());
430
						throw (new SAXException("Doc ID " + docid + " was not found and cannot be updated."));
431
					}
432
433
					if (revList.size() > 0) {
434 5208 daigle
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was deleted and cannot be updated."));
435 5200 daigle
					} else {
436 5208 daigle
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was not found and cannot be updated."));
437 5200 daigle
					}
438
				} catch (Exception e) {
439 5208 daigle
                    throw (new SAXException("EML210SaxHandler.startElement - error with action " +
440
                    		action + " : " + e.getMessage()));
441 4678 daigle
				}
442
			}
443 2169 sgarg
444 4678 daigle
			// Create the current node representation
445
			currentNode = new DBSAXNode(connection, qName, localName, parentNode,
446
					rootNode.getNodeID(), docid, doctype);
447
			// Use a local variable to store the element node id
448
			// If this element is a start point of subtree(section), it will be
449
			// stored otherwise, it will be discarded
450
			long startNodeId = currentNode.getNodeID();
451
			// Add all of the namespaces
452
			String prefix = null;
453
			String nsuri = null;
454
			Enumeration<String> prefixes = namespaces.keys();
455
			while (prefixes.hasMoreElements()) {
456
				prefix = prefixes.nextElement();
457
				nsuri = namespaces.get(prefix);
458
				endNodeId = currentNode.setNamespace(prefix, nsuri, docid);
459
			}
460 2169 sgarg
461 4678 daigle
			// Add all of the attributes
462
			for (int i = 0; i < atts.getLength(); i++) {
463
				String attributeName = atts.getQName(i);
464
				String attributeValue = atts.getValue(i);
465
				endNodeId = currentNode
466
						.setAttribute(attributeName, attributeValue, docid);
467 2169 sgarg
468 4678 daigle
				// To handle name space and schema location if the attribute
469
				// name is xsi:schemaLocation. If the name space is in not
470
				// in catalog table it will be registered.
471
				if (attributeName != null
472
						&& attributeName.indexOf(MetaCatServlet.SCHEMALOCATIONKEYWORD) != -1) {
473
					SchemaLocationResolver resolver = new SchemaLocationResolver(
474
							attributeValue);
475
					resolver.resolveNameSpace();
476 2169 sgarg
477 4678 daigle
				} else if (attributeName != null && attributeName.equals(ID)) {
478 5096 daigle
479 4678 daigle
				}
480
			}// for
481
482
			// handle access stuff
483
			if (localName.equals(ACCESS)) {
484
				if (parentNode.getTagName().equals(EML)) {
485 5311 daigle
					processingTopLevelAccess = true;
486 4472 daigle
				} else if (parentNode.getTagName() == DISTRIBUTION) {
487 5311 daigle
					processingAdditionalAccess = true;
488 4472 daigle
				} else {
489
					// process other access embedded into resource level
490
					// module
491 5311 daigle
					processingOtherAccess = true;
492 4472 daigle
				}
493 4678 daigle
				// create access object
494
				accessObject = new AccessSection();
495
				// set permission order
496
				String permOrder = currentNode.getAttribute(ORDER);
497
				accessObject.setPermissionOrder(permOrder);
498
				// set access id
499
				String accessId = currentNode.getAttribute(ID);
500
				accessObject.setSubTreeId(accessId);
501
				accessObject.setStartNodeId(startNodeId);
502
				accessObject.setDocId(docid);
503 5311 daigle
				if (processingAdditionalAccess) {
504 4678 daigle
					accessObject.setDataFileName(inlineDataFileName);
505
				}
506 2169 sgarg
507 4678 daigle
				// load top level node stack to
508
				// currentUnchangableAccessModuleNodeStack
509 5311 daigle
				if (processingTopLevelAccess && needToCheckAccessModule) {
510 4678 daigle
					// get the node stack for
511
					currentUnchangeableAccessModuleNodeStack = topAccessSection
512
							.getSubTreeNodeStack();
513
				}
514
			} else if (localName.equals(DISTRIBUTION)) {
515
				distributionIndex++;
516
				currentDistributionSection = new DistributionSection(distributionIndex);
517 2169 sgarg
518 4678 daigle
				// handle subtree info
519
				SubTree subTree = new SubTree();
520
				// set sub tree id
521
				subTree.setSubTreeId(String.valueOf(distributionIndex));
522
				// set sub tree start element name
523
				subTree.setStartElementName(currentNode.getTagName());
524
				// set start node number
525
				subTree.setStartNodeId(startNodeId);
526
				// add to stack, but it didn't get end node id yet
527
				subTreeInfoStack.push(subTree);
528
			}
529
			// Set up a access rule for allow
530
			else if (parentNode.getTagName() != null
531
					&& (parentNode.getTagName()).equals(ACCESS)
532
					&& localName.equals(ALLOW)) {
533 2169 sgarg
534 4678 daigle
				accessRule = new AccessRule();
535 2169 sgarg
536 4678 daigle
				// set permission type "allow"
537
				accessRule.setPermissionType(ALLOW);
538 2169 sgarg
539 4678 daigle
			}
540
			// set up an access rule for deny
541
			else if (parentNode.getTagName() != null
542
					&& (parentNode.getTagName()).equals(ACCESS) && localName.equals(DENY)) {
543
				accessRule = new AccessRule();
544
				// set permission type "allow"
545
				accessRule.setPermissionType(DENY);
546
			}
547 2169 sgarg
548 4678 daigle
			// Add the node to the stack, so that any text data can be
549
			// added as it is encountered
550
			nodeStack.push(currentNode);
551
			// Add the node to the vector used by thread for writing XML Index
552
			nodeIndex.addElement(currentNode);
553 2169 sgarg
554 4678 daigle
			// compare top access level module
555 5311 daigle
			if (processingTopLevelAccess && needToCheckAccessModule) {
556 4678 daigle
				compareElementNameSpaceAttributes(
557
						currentUnchangeableAccessModuleNodeStack, namespaces, atts,
558
						localName, UPDATEACCESSERROR);
559 2169 sgarg
560 4678 daigle
			}
561 2169 sgarg
562 4678 daigle
			// store access module element and attributes into stored stack
563
			if (needToCheckAccessModule
564 5311 daigle
					&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
565 4678 daigle
				// stored the pull out nodes into storedNode stack
566
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "ELEMENT", localName,
567 4698 daigle
						prefix, MetacatUtil.normalize(null));
568 4678 daigle
				storedAccessNodeStack.push(nodeElement);
569
				for (int i = 0; i < atts.getLength(); i++) {
570
					String attributeName = atts.getQName(i);
571
					String attributeValue = atts.getValue(i);
572
					NodeRecord nodeAttribute = new NodeRecord(-2, -2, -2, "ATTRIBUTE",
573 4698 daigle
							attributeName, null, MetacatUtil.normalize(attributeValue));
574 4678 daigle
					storedAccessNodeStack.push(nodeAttribute);
575
				}
576 2169 sgarg
577 4678 daigle
			}
578 2169 sgarg
579 4678 daigle
			// reset name space
580
			namespaces = null;
581
			namespaces = new Hashtable<String, String>();
582
		}// not inline data
583
		else {
584
			// we don't buffer the inline data in characters() method
585
			// so start character don't need to hand text node.
586 2169 sgarg
587 4678 daigle
			// inline data may be the xml format.
588
			StringBuffer inlineElements = new StringBuffer();
589
			inlineElements.append("<").append(qName);
590
			// append attributes
591
			for (int i = 0; i < atts.getLength(); i++) {
592
				String attributeName = atts.getQName(i);
593
				String attributeValue = atts.getValue(i);
594
				inlineElements.append(" ");
595
				inlineElements.append(attributeName);
596
				inlineElements.append("=\"");
597
				inlineElements.append(attributeValue);
598
				inlineElements.append("\"");
599
			}
600
			// append namespace
601
			String prefix = null;
602
			String nsuri = null;
603
			Enumeration<String> prefixes = inlineDataNameSpace.keys();
604
			while (prefixes.hasMoreElements()) {
605
				prefix = prefixes.nextElement();
606
				nsuri = namespaces.get(prefix);
607
				inlineElements.append(" ");
608
				inlineElements.append("xmlns:");
609
				inlineElements.append(prefix);
610
				inlineElements.append("=\"");
611
				inlineElements.append(nsuri);
612
				inlineElements.append("\"");
613
			}
614
			inlineElements.append(">");
615
			// reset inline data name space
616
			inlineDataNameSpace = null;
617
			inlineDataNameSpace = new Hashtable<String, String>();
618
			// write inline data into file
619
			logMetacat.debug("the inline element data is: " + inlineElements.toString());
620
			writeInlineDataIntoFile(inlineDataFileWriter, inlineElements);
621
		}// else
622 2169 sgarg
623 4678 daigle
	}
624 2169 sgarg
625 4678 daigle
	private void compareElementNameSpaceAttributes(
626
			Stack<NodeRecord> unchangeableNodeStack,
627
			Hashtable<String, String> nameSpaces, Attributes attributes,
628
			String localName, String error) throws SAXException {
629
		// Get element subtree node stack (element node)
630
		NodeRecord elementNode = null;
631
		try {
632
			elementNode = unchangeableNodeStack.pop();
633
		} catch (EmptyStackException ee) {
634
			logMetacat.error("Node stack is empty for element data");
635
			throw new SAXException(error);
636
		}
637
		logMetacat.debug("current node type from xml is ELEMENT");
638
		logMetacat.debug("node type from stack: " + elementNode.getNodeType());
639
		logMetacat.debug("node name from xml document: " + localName);
640
		logMetacat.debug("node name from stack: " + elementNode.getNodeName());
641
		logMetacat.debug("node data from stack: " + elementNode.getNodeData());
642
		logMetacat.debug("node id is: " + elementNode.getNodeId());
643
		// if this node is not element or local name not equal or name space
644
		// not equals, throw an exception
645
		if (!elementNode.getNodeType().equals("ELEMENT")
646
				|| !localName.equals(elementNode.getNodeName()))
647
		// (uri != null && !uri.equals(elementNode.getNodePrefix())))
648
		{
649
			logMetacat.error("Inconsistence happened: ");
650
			logMetacat.error("current node type from xml is ELEMENT");
651
			logMetacat.error("node type from stack: " + elementNode.getNodeType());
652
			logMetacat.error("node name from xml document: " + localName);
653
			logMetacat.error("node name from stack: " + elementNode.getNodeName());
654
			logMetacat.error("node data from stack: " + elementNode.getNodeData());
655
			logMetacat.error("node id is: " + elementNode.getNodeId());
656
			throw new SAXException(error);
657
		}
658 2169 sgarg
659 4678 daigle
		// compare namespace
660
		Enumeration<String> nameEn = nameSpaces.keys();
661
		while (nameEn.hasMoreElements()) {
662
			// Get namespacke node stack (element node)
663
			NodeRecord nameNode = null;
664
			try {
665
				nameNode = unchangeableNodeStack.pop();
666
			} catch (EmptyStackException ee) {
667
				logMetacat.error("Node stack is empty for namespace data");
668
				throw new SAXException(error);
669
			}
670 2169 sgarg
671 4678 daigle
			String prefixName = nameEn.nextElement();
672
			String nameSpaceUri = nameSpaces.get(prefixName);
673
			if (!nameNode.getNodeType().equals("NAMESPACE")
674
					|| !prefixName.equals(nameNode.getNodeName())
675
					|| !nameSpaceUri.equals(nameNode.getNodeData())) {
676
				logMetacat.error("Inconsistence happened: ");
677
				logMetacat.error("current node type from xml is NAMESPACE");
678
				logMetacat.error("node type from stack: " + nameNode.getNodeType());
679
				logMetacat.error("current node name from xml is: " + prefixName);
680
				logMetacat.error("node name from stack: " + nameNode.getNodeName());
681
				logMetacat.error("current node data from xml is: " + nameSpaceUri);
682
				logMetacat.error("node data from stack: " + nameNode.getNodeData());
683
				logMetacat.error("node id is: " + nameNode.getNodeId());
684
				throw new SAXException(error);
685
			}
686 2169 sgarg
687 4678 daigle
		}// while
688 2169 sgarg
689 4678 daigle
		// compare attributes
690
		for (int i = 0; i < attributes.getLength(); i++) {
691
			NodeRecord attriNode = null;
692
			try {
693
				attriNode = unchangeableNodeStack.pop();
694 2169 sgarg
695 4678 daigle
			} catch (EmptyStackException ee) {
696
				logMetacat.error("Node stack is empty for attribute data");
697
				throw new SAXException(error);
698
			}
699
			String attributeName = attributes.getQName(i);
700
			String attributeValue = attributes.getValue(i);
701
			logMetacat.debug("current node type from xml is ATTRIBUTE ");
702
			logMetacat.debug("node type from stack: " + attriNode.getNodeType());
703
			logMetacat.debug("current node name from xml is: " + attributeName);
704
			logMetacat.debug("node name from stack: " + attriNode.getNodeName());
705
			logMetacat.debug("current node data from xml is: " + attributeValue);
706
			logMetacat.debug("node data from stack: " + attriNode.getNodeData());
707
			logMetacat.debug("node id  is: " + attriNode.getNodeId());
708 2169 sgarg
709 4678 daigle
			if (!attriNode.getNodeType().equals("ATTRIBUTE")
710
					|| !attributeName.equals(attriNode.getNodeName())
711
					|| !attributeValue.equals(attriNode.getNodeData())) {
712
				logMetacat.error("Inconsistence happened: ");
713
				logMetacat.error("current node type from xml is ATTRIBUTE ");
714
				logMetacat.error("node type from stack: " + attriNode.getNodeType());
715
				logMetacat.error("current node name from xml is: " + attributeName);
716
				logMetacat.error("node name from stack: " + attriNode.getNodeName());
717
				logMetacat.error("current node data from xml is: " + attributeValue);
718
				logMetacat.error("node data from stack: " + attriNode.getNodeData());
719
				logMetacat.error("node is: " + attriNode.getNodeId());
720
				throw new SAXException(error);
721
			}
722
		}// for
723 2169 sgarg
724 4678 daigle
	}
725 2169 sgarg
726 4678 daigle
	/* method to compare current text node and node in db */
727 5311 daigle
	private void compareAccessTextNode(Stack<NodeRecord> nodeStack, StringBuffer text) throws SAXException {
728 4678 daigle
		NodeRecord node = null;
729
		// get node from current stack
730
		try {
731
			node = nodeStack.pop();
732
		} catch (EmptyStackException ee) {
733 5311 daigle
			logMetacat.error("Node stack is empty for text data in startElement for doc id " + docid);
734
			throw new SAXException("Access rules could not be found in database.");
735 4678 daigle
		}
736 5311 daigle
737
		String dbAccessData = node.getNodeData();
738
		String docAccessData = text.toString().trim();
739
740
		logMetacat.debug("Eml210SAXHandler.compareAccessTextNode - \n" +
741
					"\t access node type from db:       " + node.getNodeType() + "\n" +
742
					"\t access node data from db:       " + node.getNodeData() + "\n" +
743
					"\t access node data from document: " + text.toString());
744
745 4678 daigle
		if (!node.getNodeType().equals("TEXT")
746 5311 daigle
				|| !docAccessData.equals(dbAccessData)) {
747
			logMetacat.warn("Eml210SAXHandler.compareAccessTextNode - Access record mismatch: \n" +
748
					"\t access node type from db:       " + node.getNodeType() + "\n" +
749
					"\t access node data from db:       " + dbAccessData + "\n" +
750
					"\t access node data from document: " + docAccessData);
751
752
			throw new SAXException(UPDATEACCESSERROR + " [Eml210SAXHandler.compareAccessTextNode]");
753 4678 daigle
		}// if
754
	}
755 2169 sgarg
756 4678 daigle
	/** SAX Handler that is called for each XML text node */
757
	public void characters(char[] cbuf, int start, int len) throws SAXException {
758
		logMetacat.debug("CHARACTERS");
759
		if (!handleInlineData) {
760
			// buffer all text nodes for same element. This is for if text was
761
			// split into different nodes
762
			textBuffer.append(new String(cbuf, start, len));
763
			// set hittextnode true
764
			hitTextNode = true;
765
			// if text buffer .size is greater than max, write it to db.
766
			// so we can save memory
767
			if (textBuffer.length() > MAXDATACHARS) {
768
				logMetacat.debug("Write text into DB in charaters"
769
						+ " when text buffer size is greater than maxmum number");
770
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
771
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
772
				textBuffer = null;
773
				textBuffer = new StringBuffer();
774
			}
775
		} else {
776
			// this is inline data and write file system directly
777
			// we don't need to buffer it.
778
			StringBuffer inlineText = new StringBuffer();
779
			inlineText.append(new String(cbuf, start, len));
780
			logMetacat.debug("The inline text data write into file system: "
781
					+ inlineText.toString());
782
			writeInlineDataIntoFile(inlineDataFileWriter, inlineText);
783
		}
784
	}
785 2169 sgarg
786 4678 daigle
	/** SAX Handler that is called at the end of each XML element */
787
	public void endElement(String uri, String localName, String qName)
788
			throws SAXException {
789
		logMetacat.debug("End ELEMENT " + qName);
790 2169 sgarg
791 4678 daigle
		if (localName.equals(INLINE) && handleInlineData) {
792
			// Get the node from the stack
793
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
794
			logMetacat.debug("End of inline data");
795
			// close file writer
796
			try {
797
				inlineDataFileWriter.close();
798
				handleInlineData = false;
799
			} catch (IOException ioe) {
800
				throw new SAXException(ioe.getMessage());
801
			}
802 2169 sgarg
803 4678 daigle
			// write put inline data file name into text buffer (without path)
804
			textBuffer = new StringBuffer(inlineDataFileName);
805
			// write file name into db
806
			endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
807
			// reset textbuff
808
			textBuffer = null;
809
			textBuffer = new StringBuffer();
810
			return;
811
		}
812 2169 sgarg
813 4678 daigle
		if (!handleInlineData) {
814
			// Get the node from the stack
815
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
816
			String currentTag = currentNode.getTagName();
817 2169 sgarg
818 4678 daigle
			// If before the end element, the parser hit text nodes and store
819
			// them into the buffer, write the buffer to data base. The reason
820
			// we put write database here is for xerces some time split text
821
			// node
822
			if (hitTextNode) {
823
				// get access value
824
				String data = null;
825
				// add principal
826
				if (currentTag.equals(PRINCIPAL) && accessRule != null) {
827
					data = (textBuffer.toString()).trim();
828
					accessRule.addPrincipal(data);
829 2169 sgarg
830 4678 daigle
				} else if (currentTag.equals(PERMISSION) && accessRule != null) {
831
					data = (textBuffer.toString()).trim();
832
					// we combine different a permission into one value
833
					int permission = accessRule.getPermission();
834
					// add permission
835
					if (data.toUpperCase().equals(READSTRING)) {
836
						permission = permission | READ;
837
					} else if (data.toUpperCase().equals(WRITESTRING)) {
838
						permission = permission | WRITE;
839
					} else if (data.toUpperCase().equals(CHMODSTRING)) {
840
						permission = permission | CHMOD;
841
					} else if (data.toUpperCase().equals(ALLSTRING)) {
842
						permission = permission | ALL;
843
					}
844
					accessRule.setPermission(permission);
845
				} else if (currentTag.equals(REFERENCES)
846 5311 daigle
						&& (processingTopLevelAccess || processingAdditionalAccess || processingOtherAccess)) {
847 4678 daigle
					// get reference
848
					data = (textBuffer.toString()).trim();
849
					// put reference id into accessSection
850
					accessObject.setReferences(data);
851 2169 sgarg
852 4678 daigle
				} else if (currentTag.equals(URL)) {
853
					// handle online data, make sure its'parent is online
854
					DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
855
					if (parentNode != null && parentNode.getTagName() != null
856
							&& parentNode.getTagName().equals(ONLINE)) {
857
						// if online data is in local metacat, add it to the
858
						// vector
859
						data = (textBuffer.toString()).trim();
860
						handleOnlineUrlDataFile(data);
861 5311 daigle
862 4678 daigle
					}// if
863
				}// else if
864 5311 daigle
865 4678 daigle
				// write text to db if it is not inline data
866 5311 daigle
				logMetacat.debug("Write text into DB in End Element");
867 2169 sgarg
868 5311 daigle
				// compare top level access module
869
				if (processingTopLevelAccess && needToCheckAccessModule) {
870
					compareAccessTextNode(currentUnchangeableAccessModuleNodeStack,
871
							textBuffer);
872 4678 daigle
				}
873 5311 daigle
				// write text node into db
874
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
875
			}
876
877
			if (needToCheckAccessModule
878
					&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
879
				// stored the pull out nodes into storedNode stack
880
				NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
881
						null, MetacatUtil.normalize(textBuffer.toString()));
882
				storedAccessNodeStack.push(nodeElement);
883 2169 sgarg
884 5311 daigle
			}
885 2169 sgarg
886 4678 daigle
			// set hitText false
887
			hitTextNode = false;
888
			// reset textbuff
889
			textBuffer = null;
890
			textBuffer = new StringBuffer();
891 2169 sgarg
892 4678 daigle
			// hand sub stree stuff
893
			if (!subTreeInfoStack.empty()) {
894
				SubTree tree = subTreeInfoStack.peek();// get last
895
				// subtree
896
				if (tree != null && tree.getStartElementName() != null
897
						&& (tree.getStartElementName()).equals(currentTag)) {
898
					// find the end of sub tree and set the end node id
899
					tree.setEndNodeId(endNodeId);
900
					// add the subtree into the final store palace
901
					subTreeList.add(tree);
902
					// get rid of it from stack
903
					subTreeInfoStack.pop();
904
				}// if
905
			}// if
906 2169 sgarg
907 4678 daigle
			// access stuff
908
			if (currentTag.equals(ALLOW) || currentTag.equals(DENY)) {
909
				// finish parser access rule and assign it to new one
910
				AccessRule newRule = accessRule;
911
				// add the new rule to access section object
912
				accessObject.addAccessRule(newRule);
913
				// reset access rule
914
				accessRule = null;
915
			} else if (currentTag.equals(ACCESS)) {
916
				// finish parsing an access section and assign it to new one
917
				DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
918 2169 sgarg
919 4678 daigle
				accessObject.setEndNodeId(endNodeId);
920 2169 sgarg
921 4678 daigle
				if (parentNode != null && parentNode.getTagName() != null
922
						&& parentNode.getTagName().equals(DISTRIBUTION)) {
923
					describesId.add(String.valueOf(distributionIndex));
924
					currentDistributionSection.setAccessSection(accessObject);
925
				}
926 2169 sgarg
927 4678 daigle
				AccessSection newAccessObject = accessObject;
928 2169 sgarg
929 4678 daigle
				if (newAccessObject != null) {
930 2169 sgarg
931 4678 daigle
					// add the accessSection into a vector to store it
932
					// if it is not a reference, need to store it
933
					if (newAccessObject.getReferences() == null) {
934 2169 sgarg
935 4678 daigle
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
936
						accessObjectList.add(newAccessObject);
937
					}
938 5311 daigle
					if (processingTopLevelAccess) {
939 2169 sgarg
940 4678 daigle
						// top level access control will handle whole document
941
						// -docid
942
						topLevelAccessControlMap.put(docid, newAccessObject);
943
						// reset processtopleveraccess tag
944 2169 sgarg
945 4678 daigle
					}// if
946 5311 daigle
					else if (processingAdditionalAccess) {
947 4678 daigle
						// for additional control put everything in describes
948
						// value
949
						// and access object into hash
950
						for (int i = 0; i < describesId.size(); i++) {
951 2169 sgarg
952 4678 daigle
							String subId = describesId.elementAt(i);
953
							if (subId != null) {
954
								additionalAccessControlMap.put(subId, newAccessObject);
955
							}// if
956
						}// for
957
						// add this hashtable in to vector
958 2169 sgarg
959 4678 daigle
						additionalAccessMapList.add(additionalAccessControlMap);
960
						// reset this hashtable in order to store another
961
						// additional
962
						// accesscontrol
963
						additionalAccessControlMap = null;
964
						additionalAccessControlMap = new Hashtable<String, AccessSection>();
965
					}// if
966 2169 sgarg
967 4678 daigle
				}// if
968
				// check if access node stack is empty after parsing top access
969
				// module
970 2169 sgarg
971 5311 daigle
				if (needToCheckAccessModule && processingTopLevelAccess
972 4678 daigle
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
973 2169 sgarg
974 4678 daigle
					logMetacat.error("Access node stack is not empty after "
975
							+ "parsing access subtree");
976
					throw new SAXException(UPDATEACCESSERROR);
977 2169 sgarg
978 4678 daigle
				}
979
				// reset access section object
980 2169 sgarg
981 4678 daigle
				accessObject = null;
982 2169 sgarg
983 4678 daigle
				// reset tmp stored node stack
984
				storedAccessNodeStack = null;
985
				storedAccessNodeStack = new Stack<NodeRecord>();
986 2169 sgarg
987 4678 daigle
				// reset flag
988 5311 daigle
				processingAdditionalAccess = false;
989
				processingTopLevelAccess = false;
990
				processingOtherAccess = false;
991 2169 sgarg
992 4678 daigle
			} else if (currentTag.equals(DISTRIBUTION)) {
993
				// If the current Distribution is inline or data and it doesn't have an access section
994
				// we use the top level access section (if it exists)
995
				if ((currentDistributionSection.getDistributionType() == DistributionSection.DATA_DISTRIBUTION
996
						|| currentDistributionSection.getDistributionType() == DistributionSection.INLINE_DATA_DISTRIBUTION)
997
						&& currentDistributionSection.getAccessSection() == null
998
						&& topLevelAccessControlMap.size() > 0) {
999
1000
					AccessSection accessSection = new AccessSection();
1001
					accessSection.setDocId(docid);
1002
					AccessSection topLevelAccess = topLevelAccessControlMap.get(docid);
1003
					accessSection.setPermissionOrder(topLevelAccess.getPermissionOrder());
1004
					Vector<AccessRule> accessRuleList = topLevelAccess.getAccessRules();
1005
					for (AccessRule accessRule : accessRuleList) {
1006
						accessSection.addAccessRule(accessRule);
1007
					}
1008
					currentDistributionSection.setAccessSection(accessSection);
1009
				}
1010
				if (currentDistributionSection.getAccessSection() != null) {
1011
					currentDistributionSection.getAccessSection().setDataFileName(currentDistributionSection.getDataFileName());
1012
				}
1013
				allDistributionSections.add(currentDistributionSection);
1014
				currentDistributionSection = null;
1015
				describesId = null;
1016
				describesId = new Vector<String>();
1017
			}
1018
		} else {
1019
			// this is in inline part
1020
			StringBuffer endElement = new StringBuffer();
1021
			endElement.append("</");
1022
			endElement.append(qName);
1023
			endElement.append(">");
1024
			logMetacat.debug("inline endElement: " + endElement.toString());
1025
			writeInlineDataIntoFile(inlineDataFileWriter, endElement);
1026
		}
1027
	}
1028 2169 sgarg
1029 4678 daigle
	/**
1030
	 * SAX Handler that receives notification of comments in the DTD
1031
	 */
1032
	public void comment(char[] ch, int start, int length) throws SAXException {
1033
		logMetacat.debug("COMMENT");
1034
		if (!handleInlineData) {
1035
			if (!processingDTD) {
1036
				DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1037
				String str = new String(ch, start, length);
1038 2169 sgarg
1039 4678 daigle
				// compare top level access module
1040 5311 daigle
				if (processingTopLevelAccess && needToCheckAccessModule) {
1041 4678 daigle
					compareCommentNode(currentUnchangeableAccessModuleNodeStack, str,
1042
							UPDATEACCESSERROR);
1043
				}
1044
				endNodeId = currentNode.writeChildNodeToDB("COMMENT", null, str, docid);
1045
				if (needToCheckAccessModule
1046 5311 daigle
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
1047 4678 daigle
					// stored the pull out nodes into storedNode stack
1048
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "COMMENT", null,
1049 4698 daigle
							null, MetacatUtil.normalize(str));
1050 4678 daigle
					storedAccessNodeStack.push(nodeElement);
1051 2169 sgarg
1052 4678 daigle
				}
1053
			}
1054
		} else {
1055
			// inline data comment
1056
			StringBuffer inlineComment = new StringBuffer();
1057
			inlineComment.append("<!--");
1058
			inlineComment.append(new String(ch, start, length));
1059
			inlineComment.append("-->");
1060
			logMetacat.debug("inline data comment: " + inlineComment.toString());
1061
			writeInlineDataIntoFile(inlineDataFileWriter, inlineComment);
1062
		}
1063
	}
1064 2169 sgarg
1065 4678 daigle
	/* Compare comment from xml and db */
1066
	private void compareCommentNode(Stack<NodeRecord> nodeStack, String string,
1067
			String error) throws SAXException {
1068
		NodeRecord node = null;
1069
		try {
1070
			node = nodeStack.pop();
1071
		} catch (EmptyStackException ee) {
1072
			logMetacat.error("the stack is empty for comment data");
1073
			throw new SAXException(error);
1074
		}
1075
		logMetacat.debug("current node type from xml is COMMENT");
1076
		logMetacat.debug("node type from stack: " + node.getNodeType());
1077
		logMetacat.debug("current node data from xml is: " + string);
1078
		logMetacat.debug("node data from stack: " + node.getNodeData());
1079
		logMetacat.debug("node is from stack: " + node.getNodeId());
1080
		// if not consistent terminate program and throw a exception
1081
		if (!node.getNodeType().equals("COMMENT") || !string.equals(node.getNodeData())) {
1082
			logMetacat.error("Inconsistence happened: ");
1083
			logMetacat.error("current node type from xml is COMMENT");
1084
			logMetacat.error("node type from stack: " + node.getNodeType());
1085
			logMetacat.error("current node data from xml is: " + string);
1086
			logMetacat.error("node data from stack: " + node.getNodeData());
1087
			logMetacat.error("node is from stack: " + node.getNodeId());
1088
			throw new SAXException(error);
1089
		}// if
1090
	}
1091 2169 sgarg
1092 4678 daigle
	/**
1093
	 * SAX Handler called once for each processing instruction found: node that
1094
	 * PI may occur before or after the root element.
1095
	 */
1096
	public void processingInstruction(String target, String data) throws SAXException {
1097
		logMetacat.debug("PI");
1098
		if (!handleInlineData) {
1099
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1100
			endNodeId = currentNode.writeChildNodeToDB("PI", target, data, docid);
1101
		} else {
1102
			StringBuffer inlinePI = new StringBuffer();
1103
			inlinePI.append("<?");
1104
			inlinePI.append(target);
1105
			inlinePI.append(" ");
1106
			inlinePI.append(data);
1107
			inlinePI.append("?>");
1108
			logMetacat.debug("inline data pi is: " + inlinePI.toString());
1109
			writeInlineDataIntoFile(inlineDataFileWriter, inlinePI);
1110
		}
1111
	}
1112 2169 sgarg
1113 4678 daigle
	/** SAX Handler that is called at the start of Namespace */
1114
	public void startPrefixMapping(String prefix, String uri) throws SAXException {
1115
		logMetacat.debug("NAMESPACE");
1116
		if (!handleInlineData) {
1117
			namespaces.put(prefix, uri);
1118
		} else {
1119
			inlineDataNameSpace.put(prefix, uri);
1120
		}
1121
	}
1122 2169 sgarg
1123 4678 daigle
	/**
1124
	 * SAX Handler that is called for each XML text node that is Ignorable white
1125
	 * space
1126
	 */
1127
	public void ignorableWhitespace(char[] cbuf, int start, int len) throws SAXException {
1128
		// When validation is turned "on", white spaces are reported here
1129
		// When validation is turned "off" white spaces are not reported here,
1130
		// but through characters() callback
1131
		logMetacat.debug("IGNORABLEWHITESPACE");
1132
		if (!handleInlineData) {
1133
			DBSAXNode currentNode = (DBSAXNode) nodeStack.peek();
1134 5208 daigle
				String data = new String(cbuf, start, len);
1135 4678 daigle
				// compare whitespace in access top module
1136 5311 daigle
				if (processingTopLevelAccess && needToCheckAccessModule) {
1137 4678 daigle
					compareWhiteSpace(currentUnchangeableAccessModuleNodeStack, data,
1138
							UPDATEACCESSERROR);
1139
				}
1140
				// Write the content of the node to the database
1141
				if (needToCheckAccessModule
1142 5311 daigle
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
1143 4678 daigle
					// stored the pull out nodes into storedNode stack
1144
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
1145 4698 daigle
							null, MetacatUtil.normalize(data));
1146 4678 daigle
					storedAccessNodeStack.push(nodeElement);
1147 2169 sgarg
1148 4678 daigle
				}
1149
				endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data, docid);
1150
		} else {
1151
			// This is inline data write to file directly
1152
			StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf, start, len));
1153
			writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1154
		}
1155 2169 sgarg
1156 4678 daigle
	}
1157 2169 sgarg
1158 4678 daigle
	/* Compare whitespace from xml and db */
1159
	private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string,
1160
			String error) throws SAXException {
1161
		NodeRecord node = null;
1162
		try {
1163
			node = nodeStack.pop();
1164
		} catch (EmptyStackException ee) {
1165
			logMetacat.error("the stack is empty for whitespace data");
1166
			throw new SAXException(error);
1167
		}
1168
		if (!node.getNodeType().equals("TEXT") || !string.equals(node.getNodeData())) {
1169
			logMetacat.error("Inconsistence happened: ");
1170
			logMetacat.error("current node type from xml is WHITESPACE TEXT");
1171
			logMetacat.error("node type from stack: " + node.getNodeType());
1172
			logMetacat.error("current node data from xml is: " + string);
1173
			logMetacat.error("node data from stack: " + node.getNodeData());
1174
			logMetacat.error("node is from stack: " + node.getNodeId());
1175
			throw new SAXException(error);
1176
		}// if
1177
	}
1178 2169 sgarg
1179 4678 daigle
	/** SAX Handler that receives notification of end of the document */
1180
	public void endDocument() throws SAXException {
1181
		logMetacat.debug("end Document");
1182
		// There are some unchangable subtree didn't be compare
1183
		// This maybe cause user change the subtree id
1184
		if (!super.getIsRevisionDoc()) {
1185
			// write access rule to db
1186 7128 leinfelder
			if (writeAccessRules) {
1187
				writeAccessRuleToDB();
1188
			}
1189 4678 daigle
			// delete relation table
1190
			deleteRelations();
1191
			// write relations
1192
			for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1193
				String id = onlineDataFileIdInRelationVector.elementAt(i);
1194
				writeOnlineDataFileIdIntoRelationTable(id);
1195
			}
1196
		}
1197
	}
1198 2169 sgarg
1199 4678 daigle
	/* The method to write all access rule into db */
1200
	private void writeAccessRuleToDB() throws SAXException {
1201
		// Delete old permssion
1202 6744 leinfelder
		deletePermissionsInAccessTable();
1203 4678 daigle
		// write top leve access rule
1204
		writeTopLevelAccessRuleToDB();
1205
		// write additional access rule
1206
		// writeAdditionalAccessRuleToDB();
1207
		writeAdditionalAccessRulesToDB();
1208
	}// writeAccessRuleToDB
1209 2169 sgarg
1210 4678 daigle
	/* The method to write top level access rule into db. */
1211
	private void writeTopLevelAccessRuleToDB() throws SAXException {
1212
		// for top document level
1213
		AccessSection accessSection = topLevelAccessControlMap.get(docid);
1214
		boolean top = true;
1215
		String subSectionId = null;
1216
		if (accessSection != null) {
1217
			AccessSection accessSectionObj = accessSection;
1218 2169 sgarg
1219 4678 daigle
			// if accessSection is not null and is not reference
1220
			if (accessSectionObj.getReferences() == null) {
1221 7137 leinfelder
				// check for denyFirst permOrder
1222
				String permOrder = accessSectionObj.getPermissionOrder();
1223
				if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1224
					logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block");
1225
			    	return;
1226
			    }
1227 4678 daigle
				// write the top level access module into xml_accesssubtree to
1228
				// store info and then when update to check if the user can
1229
				// update it or not
1230
				deleteAccessSubTreeRecord(docid);
1231
				writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1232 2169 sgarg
1233 4678 daigle
				// write access section into xml_access table
1234
				writeGivenAccessRuleIntoDB(accessSectionObj, top, subSectionId);
1235
				// write online data file into xml_access too.
1236
				// for (int i= 0; i <onlineDataFileIdInTopAccessVector.size();
1237
				// i++)
1238
				// {
1239
				// String id = onlineDataFileIdInTopAccessVector.elementAt(i);
1240
				// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1241
				// id);
1242
				// }
1243 2169 sgarg
1244 4678 daigle
			} else {
1245 2169 sgarg
1246 4678 daigle
				// this is a reference and go trough the vector which contains
1247
				// all access object
1248
				String referenceId = accessSectionObj.getReferences();
1249
				boolean findAccessObject = false;
1250
				logMetacat.debug("referered id for top access: " + referenceId);
1251
				for (int i = 0; i < accessObjectList.size(); i++) {
1252
					AccessSection accessObj = accessObjectList.elementAt(i);
1253
					String accessObjId = accessObj.getSubTreeId();
1254 7137 leinfelder
					// check for denyFirst permOrder
1255
					String permOrder = accessObj.getPermissionOrder();
1256
					if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1257
						logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block, subtree id: " + accessObjId);
1258
				    	continue;
1259
				    }
1260 4678 daigle
					if (referenceId != null && accessObj != null
1261
							&& referenceId.equals(accessObjId)) {
1262
						// make sure the user didn't change any thing in this
1263
						// access moduel
1264
						// too if user doesn't have all permission
1265
						if (needToCheckAccessModule) {
1266 2169 sgarg
1267 4678 daigle
							Stack<NodeRecord> newStack = accessObj
1268
									.getStoredTmpNodeStack();
1269
							// revise order
1270 5025 daigle
							newStack = DocumentUtil.reviseStack(newStack);
1271 4678 daigle
							// go throught the vector of
1272
							// unChangeableAccessSubtreevector
1273
							// and find the one whose id is as same as
1274
							// referenceid
1275
							AccessSection oldAccessObj = getAccessSectionFromUnchangableAccessVector(referenceId);
1276
							// if oldAccessObj is null something is wrong
1277
							if (oldAccessObj == null) {
1278
								throw new SAXException(UPDATEACCESSERROR);
1279
							}// if
1280
							else {
1281
								// Get the node stack from old access obj
1282
								Stack<NodeRecord> oldStack = oldAccessObj
1283
										.getSubTreeNodeStack();
1284
								compareNodeStacks(newStack, oldStack);
1285
							}// else
1286
						}// if
1287
						// write accessobject into db
1288
						writeGivenAccessRuleIntoDB(accessObj, top, subSectionId);
1289
						// write online data file into xml_access too.
1290
						// for (int j= 0; j
1291
						// <onlineDataFileIdInTopAccessVector.size(); j++)
1292
						// {
1293
						// String id =
1294
						// onlineDataFileIdInTopAccessVector.elementAt(j);
1295
						// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1296
						// id);
1297
						// }
1298 2169 sgarg
1299 4678 daigle
						// write the reference access into xml_accesssubtree
1300
						// too write the top level access module into
1301
						// xml_accesssubtree to store info and then when update
1302
						// to check if the user can update it or not
1303
						deleteAccessSubTreeRecord(docid);
1304
						writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1305
						writeAccessSubTreeIntoDB(accessObj, SUBTREELEVEL);
1306
						findAccessObject = true;
1307
						break;
1308
					}
1309
				}// for
1310
				// if we couldn't find an access subtree id for this reference
1311
				// id
1312
				if (!findAccessObject) {
1313
					throw new SAXException("The referenceid: " + referenceId
1314
							+ " is not access subtree");
1315
				}// if
1316
			}// else
1317 2169 sgarg
1318 4678 daigle
		}// if
1319
		else {
1320
			// couldn't find a access section object
1321
			logMetacat.warn("couldn't find access control for document: " + docid);
1322
		}
1323 2169 sgarg
1324 4678 daigle
	}// writeTopLevelAccessRuletoDB
1325 2169 sgarg
1326 4678 daigle
	/* Given a subtree id and find the responding access section */
1327
	private AccessSection getAccessSectionFromUnchangableAccessVector(String id) {
1328
		AccessSection result = null;
1329
		// Makse sure the id
1330
		if (id == null || id.equals("")) {
1331
			return result;
1332
		}
1333
		// go throught vector and find the list
1334
		for (int i = 0; i < unChangeableAccessSubTreeVector.size(); i++) {
1335
			AccessSection accessObj = unChangeableAccessSubTreeVector.elementAt(i);
1336
			if (accessObj.getSubTreeId() != null && (accessObj.getSubTreeId()).equals(id)) {
1337
				result = accessObj;
1338
			}// if
1339
		}// for
1340
		return result;
1341
	}// getAccessSectionFromUnchangableAccessVector
1342 2169 sgarg
1343 4678 daigle
	/* Compare two node stacks to see if they are same */
1344
	private void compareNodeStacks(Stack<NodeRecord> stack1, Stack<NodeRecord> stack2)
1345
			throws SAXException {
1346
		// make sure stack1 and stack2 are not empty
1347
		if (stack1.isEmpty() || stack2.isEmpty()) {
1348
			logMetacat.error("Because stack is empty!");
1349
			throw new SAXException(UPDATEACCESSERROR);
1350
		}
1351
		// go throw two stacks and compare every element
1352
		while (!stack1.isEmpty()) {
1353
			// Pop an element from stack1
1354
			NodeRecord record1 = stack1.pop();
1355
			// Pop an element from stack2(stack 2 maybe empty)
1356
			NodeRecord record2 = null;
1357
			try {
1358
				record2 = stack2.pop();
1359
			} catch (EmptyStackException ee) {
1360
				logMetacat.error("Node stack2 is empty but stack1 isn't!");
1361
				throw new SAXException(UPDATEACCESSERROR);
1362
			}
1363
			// if two records are not same throw a exception
1364
			if (!record1.contentEquals(record2)) {
1365
				logMetacat.error("Two records from new and old stack are not " + "same!");
1366
				throw new SAXException(UPDATEACCESSERROR);
1367
			}// if
1368
		}// while
1369 2169 sgarg
1370 4678 daigle
		// now stack1 is empty and we should make sure stack2 is empty too
1371
		if (!stack2.isEmpty()) {
1372
			logMetacat
1373
					.error("stack2 still has some elements while stack " + "is empty! ");
1374
			throw new SAXException(UPDATEACCESSERROR);
1375
		}// if
1376
	}// comparingNodeStacks
1377 4472 daigle
1378 5166 daigle
	/* The method to write additional access rule into db. */
1379 4678 daigle
	private void writeAdditionalAccessRulesToDB() throws SAXException {
1380
1381
		// Iterate through every distribution and write access sections for data and inline
1382
		// types to the database
1383
		for (DistributionSection distributionSection : allDistributionSections) {
1384
			// we're only interested in data and inline distributions
1385
			int distributionType = distributionSection.getDistributionType();
1386
			if (distributionType == DistributionSection.DATA_DISTRIBUTION
1387
					|| distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION) {
1388
				AccessSection accessSection = distributionSection.getAccessSection();
1389
1390
				// If the distribution doesn't have an access section, we continue.
1391
				if (accessSection == null) {
1392
					continue;
1393 7137 leinfelder
				}
1394 4678 daigle
1395 7137 leinfelder
				// check for denyFirst permOrder
1396
				String permOrder = accessSection.getPermissionOrder();
1397
				if (permOrder.equals(AccessControlInterface.DENYFIRST) && ignoreDenyFirst) {
1398
					logMetacat.warn("Metacat no longer supports EML 'denyFirst' access rules - ignoring this access block: " + distributionSection.getDataFileName());
1399
			    	continue;
1400
			    }
1401
1402 4678 daigle
				// We want to check file permissions for all online data updates and inserts, or for
1403
				// inline updates.
1404
//				if (distributionType == DistributionSection.DATA_DISTRIBUTION
1405
//						|| (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE")) {
1406 2169 sgarg
1407 4678 daigle
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1408
					try {
1409 6746 leinfelder
						// check for the previous version for permissions on update
1410 7293 leinfelder
						// we need to look up the docid from the guid, if we have it
1411 6746 leinfelder
						String dataDocid = distributionSection.getDataFileName();
1412 7293 leinfelder
						try {
1413
							dataDocid = IdentifierManager.getInstance().getLocalId(dataDocid);
1414
						} catch (McdbDocNotFoundException mcdbnfe) {
1415
							// ignore
1416
							logMetacat.warn("Could not find guid/docid mapping for " + dataDocid);
1417
						}
1418 6746 leinfelder
						String previousDocid = dataDocid;
1419
						if (action == "UPDATE") {
1420
							String docidWithoutRev = DocumentUtil.getDocIdFromString(dataDocid);
1421
							int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docidWithoutRev);
1422
							if (latestRevision > 0) {
1423 6748 leinfelder
								previousDocid = docidWithoutRev + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
1424 6746 leinfelder
							}
1425
						}
1426
1427 8178 leinfelder
						// check both the previous and current data permissions
1428
						// see: https://projects.ecoinformatics.org/ecoinfo/issues/5647
1429 6744 leinfelder
						PermissionController controller = new PermissionController(previousDocid);
1430 8178 leinfelder
						PermissionController currentController = new PermissionController(dataDocid);
1431
1432 4769 daigle
						if (AccessionNumber.accNumberUsed(docid)
1433 8178 leinfelder
								&&
1434
								!(controller.hasPermission(user, groups, "WRITE")
1435
										|| currentController.hasPermission(user, groups, "WRITE")
1436
										)
1437
								) {
1438
							throw new SAXException(UPDATEACCESSERROR + " id: " + dataDocid);
1439 4678 daigle
						}
1440
					} catch (SQLException sqle) {
1441
						throw new SAXException(
1442
								"Database error checking user permissions: "
1443
										+ sqle.getMessage());
1444
					} catch (Exception e) {
1445
						throw new SAXException(
1446
								"General error checking user permissions: "
1447
										+ e.getMessage());
1448
					}
1449
				} else if (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE") {
1450
					try {
1451 6744 leinfelder
1452
						// check for the previous version for permissions
1453
						int latestRevision = DBUtil.getLatestRevisionInDocumentTable(docid);
1454
						String previousDocid =
1455
							docid + PropertyService.getProperty("document.accNumSeparator") + latestRevision;
1456
						PermissionController controller = new PermissionController(previousDocid);
1457 2169 sgarg
1458 4678 daigle
						if (!controller.hasPermission(user, groups, "WRITE")) {
1459
							throw new SAXException(UPDATEACCESSERROR);
1460
						}
1461
					} catch (SQLException sqle) {
1462
						throw new SAXException(
1463
								"Database error checking user permissions: "
1464
										+ sqle.getMessage());
1465
					} catch (Exception e) {
1466
						throw new SAXException(
1467
								"General error checking user permissions: "
1468
										+ e.getMessage());
1469
					}
1470
				}
1471
1472 6749 leinfelder
				// clear previous versions first
1473
				deleteAccessRule(accessSection, false);
1474
1475
				// now write the new ones
1476 4678 daigle
				String subSectionId = Integer.toString(distributionSection.getDistributionId());
1477
				writeGivenAccessRuleIntoDB(accessSection, false, subSectionId);
1478
			}
1479 2169 sgarg
1480 4678 daigle
		}
1481
1482
1483
	}
1484 6749 leinfelder
1485
	/**
1486
	 *  delete existing access for given access rule
1487
	 *
1488
	 */
1489
	private void deleteAccessRule(AccessSection accessSection, boolean topLevel) throws SAXException {
1490
1491
		if (accessSection == null) {
1492
			throw new SAXException("The access object is null");
1493
		}
1494
1495
		PreparedStatement pstmt = null;
1496 2169 sgarg
1497 6749 leinfelder
		String sql = null;
1498
		sql = "DELETE FROM xml_access WHERE guid = ?";
1499
1500
		try {
1501
1502
			pstmt = connection.prepareStatement(sql);
1503
			// Increase DBConnection usage count
1504
			connection.increaseUsageCount(1);
1505
			// Bind the values to the query
1506
			String guid = null;
1507
			if (topLevel) {
1508
				try {
1509
					guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
1510
				} catch (NumberFormatException e) {
1511
					throw new SAXException(e.getMessage(), e);
1512
				} catch (McdbDocNotFoundException e) {
1513
					// register the default mapping now
1514
					guid = docid + "." + revision;
1515
					IdentifierManager.getInstance().createMapping(guid, guid);
1516
				}
1517
			} else {
1518
				guid = accessSection.getDataFileName();
1519
1520
			}
1521
			pstmt.setString(1, guid);
1522
			logMetacat.debug("guid in accesstable: " + guid);
1523
1524
			logMetacat.debug("running sql: " + pstmt.toString());
1525
			pstmt.execute();
1526
1527
			pstmt.close();
1528
		}// try
1529
		catch (SQLException e) {
1530
			throw new SAXException("EMLSAXHandler.deleteAccessRule(): "
1531
					+ e.getMessage());
1532
		}// catch
1533
		finally {
1534
			try {
1535
				pstmt.close();
1536
			} catch (SQLException ee) {
1537
				throw new SAXException("EMLSAXHandler.deleteAccessRule(): "
1538
						+ ee.getMessage());
1539
			}
1540
		}// finally
1541
1542
	}
1543
1544 4678 daigle
	/* Write a given access rule into db */
1545
	private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1546
			boolean topLevel, String subSectionId) throws SAXException {
1547
		if (accessSection == null) {
1548
			throw new SAXException("The access object is null");
1549
		}
1550 2169 sgarg
1551 6744 leinfelder
		String guid = null;
1552 6802 leinfelder
		String referencedGuid = accessSection.getDataFileName();
1553
1554 6744 leinfelder
		try {
1555
			guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(revision));
1556
		} catch (NumberFormatException e) {
1557
			throw new SAXException(e.getMessage(), e);
1558
		} catch (McdbDocNotFoundException e) {
1559
			// register the default mapping now
1560
			guid = docid + "." + revision;
1561
			IdentifierManager.getInstance().createMapping(guid, guid);
1562
		}
1563
1564 4678 daigle
		String permOrder = accessSection.getPermissionOrder();
1565 6019 leinfelder
		String sql = null;
1566
		PreparedStatement pstmt = null;
1567 4678 daigle
		if (topLevel) {
1568 6744 leinfelder
			sql = "INSERT INTO xml_access (guid, principal_name, permission, "
1569 6019 leinfelder
					+ "perm_type, perm_order, accessfileid) VALUES "
1570
					+ " (?, ?, ?, ?, ?, ?)";
1571 4678 daigle
		} else {
1572 6744 leinfelder
			sql = "INSERT INTO xml_access (guid,principal_name, "
1573 6019 leinfelder
					+ "permission, perm_type, perm_order, accessfileid, subtreeid"
1574
					+ ") VALUES" + " (?, ?, ?, ?, ?, ?, ?)";
1575 4678 daigle
		}
1576
		try {
1577 2169 sgarg
1578 6019 leinfelder
			pstmt = connection.prepareStatement(sql);
1579
			// Increase DBConnection usage count
1580
			connection.increaseUsageCount(1);
1581
			// Bind the values to the query
1582 6744 leinfelder
			pstmt.setString(6, guid);
1583
			logMetacat.debug("Accessfileid in accesstable: " + guid);
1584 6019 leinfelder
			pstmt.setString(5, permOrder);
1585
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1586
			// if it is not top level, set subsection id
1587
			if (topLevel) {
1588 6744 leinfelder
				pstmt.setString(1, guid);
1589
				logMetacat.debug("Guid in accesstable: " + guid);
1590 6019 leinfelder
			}
1591
			if (!topLevel) {
1592 6802 leinfelder
				// use the referenced guid
1593
				pstmt.setString(1, referencedGuid );
1594 6019 leinfelder
				logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1595
1596
				// for subtree should specify the
1597
				if (subSectionId == null) {
1598
					throw new SAXException("The subsection is null");
1599
				}
1600
1601
				pstmt.setString(7, subSectionId);
1602
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1603 8560 slaughter
1604
				// Save guid of data object for syncing of access policy with CN after parsing
1605
				// is successful (see DocumentImpl.write)
1606
				guidsToSync.add(referencedGuid);
1607 6019 leinfelder
			}
1608
1609 4678 daigle
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1610
			// go through every rule
1611
			for (int i = 0; i < accessRules.size(); i++) {
1612
				AccessRule rule = accessRules.elementAt(i);
1613
				String permType = rule.getPermissionType();
1614
				int permission = rule.getPermission();
1615 6019 leinfelder
				pstmt.setInt(3, permission);
1616 4678 daigle
				logMetacat.debug("permission in accesstable: " + permission);
1617 6019 leinfelder
				pstmt.setString(4, permType);
1618 4678 daigle
				logMetacat.debug("Permtype in accesstable: " + permType);
1619
				// go through every principle in rule
1620
				Vector<String> nameVector = rule.getPrincipal();
1621
				for (int j = 0; j < nameVector.size(); j++) {
1622
					String prName = nameVector.elementAt(j);
1623 6019 leinfelder
					pstmt.setString(2, prName);
1624 4678 daigle
					logMetacat.debug("Principal in accesstable: " + prName);
1625 6019 leinfelder
					logMetacat.debug("running sql: " + pstmt.toString());
1626
					pstmt.execute();
1627 4678 daigle
				}// for
1628
			}// for
1629 6019 leinfelder
			pstmt.close();
1630 4678 daigle
		}// try
1631 6019 leinfelder
		catch (SQLException e) {
1632 4678 daigle
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1633
					+ e.getMessage());
1634
		}// catch
1635 6019 leinfelder
		finally {
1636
			try {
1637
				pstmt.close();
1638
			} catch (SQLException ee) {
1639
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1640
						+ ee.getMessage());
1641
			}
1642
		}// finally
1643 6802 leinfelder
1644
		// for D1, refresh the entries
1645
		HazelcastService.getInstance().refreshSystemMetadataEntry(guid);
1646
		HazelcastService.getInstance().refreshSystemMetadataEntry(referencedGuid);
1647 2169 sgarg
1648 4678 daigle
	}// writeGivenAccessRuleIntoDB
1649 2169 sgarg
1650 6744 leinfelder
	/* Delete from db all permission for resources related to the document, if any */
1651
	private void deletePermissionsInAccessTable() throws SAXException {
1652 6606 leinfelder
		PreparedStatement pstmt = null;
1653 4678 daigle
		try {
1654 6749 leinfelder
1655 6744 leinfelder
			String sql = "DELETE FROM xml_access " +
1656 6749 leinfelder
					// the file defines access for another file
1657
					"WHERE accessfileid IN " +
1658
						"(SELECT guid from identifier where docid = ? and rev = ?) " +
1659
					// the described file has other versions describing it
1660
					"OR guid IN " +
1661
						"(SELECT xa.guid from xml_access xa, identifier id" +
1662
						" WHERE xa.accessfileid = id.guid " +
1663
						" AND id.docid = ?" +
1664
						" AND id.rev = ?)";
1665 4678 daigle
			// delete all acl records for resources related to @aclid if any
1666 6606 leinfelder
			pstmt = connection.prepareStatement(sql);
1667 6744 leinfelder
			pstmt.setString(1, docid);
1668
			pstmt.setInt(2, Integer.valueOf(revision));
1669 6749 leinfelder
			// second part of query
1670
			pstmt.setString(3, docid);
1671
			pstmt.setInt(4, Integer.valueOf(revision));
1672 4678 daigle
			// Increase DBConnection usage count
1673
			connection.increaseUsageCount(1);
1674 6606 leinfelder
			logMetacat.debug("running sql: " + sql);
1675
			pstmt.execute();
1676 4678 daigle
1677
		} catch (SQLException e) {
1678
			throw new SAXException(e.getMessage());
1679
		} finally {
1680
			try {
1681 6606 leinfelder
				pstmt.close();
1682 4678 daigle
			} catch (SQLException ee) {
1683
				throw new SAXException(ee.getMessage());
1684
			}
1685
		}
1686
	}// deletePermissionsInAccessTable
1687
1688
	/*
1689
	 * In order to make sure only usr has "all" permission can update access
1690
	 * subtree in eml document we need to keep access subtree info in
1691
	 * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1692
	 */
1693
	private void writeAccessSubTreeIntoDB(AccessSection accessSection, String level)
1694
			throws SAXException {
1695
		if (accessSection == null) {
1696
			throw new SAXException("The access object is null");
1697
		}
1698
1699
		String sql = null;
1700
		PreparedStatement pstmt = null;
1701
		sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1702
				+ "subtreeid, startnodeid, endnodeid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1703
		try {
1704
1705
			pstmt = connection.prepareStatement(sql);
1706
			// Increase DBConnection usage count
1707
			connection.increaseUsageCount(1);
1708
			long startNodeId = accessSection.getStartNodeId();
1709
			long endNodeId = accessSection.getEndNodeId();
1710
			String sectionId = accessSection.getSubTreeId();
1711
			// Bind the values to the query
1712
			pstmt.setString(1, docid);
1713
			logMetacat.debug("Docid in access-subtreetable: " + docid);
1714
			pstmt.setLong(2, (new Long(revision)).longValue());
1715
			logMetacat.debug("rev in accesssubtreetable: " + revision);
1716
			pstmt.setString(3, level);
1717
			logMetacat.debug("contorl level in access-subtree table: " + level);
1718
			pstmt.setString(4, sectionId);
1719
			logMetacat.debug("Subtree id in access-subtree table: " + sectionId);
1720
			pstmt.setLong(5, startNodeId);
1721
			logMetacat.debug("Start node id is: " + startNodeId);
1722
			pstmt.setLong(6, endNodeId);
1723
			logMetacat.debug("End node id is: " + endNodeId);
1724
			logMetacat.debug("running sql: " + pstmt.toString());
1725
			pstmt.execute();
1726
			pstmt.close();
1727
		}// try
1728
		catch (SQLException e) {
1729
			throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1730
					+ e.getMessage());
1731
		}// catch
1732
		finally {
1733
			try {
1734
				pstmt.close();
1735
			} catch (SQLException ee) {
1736
				throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1737
						+ ee.getMessage());
1738
			}
1739
		}// finally
1740
1741
	}// writeAccessSubtreeIntoDB
1742
1743
	/* Delete every access subtree record from xml_accesssubtree. */
1744
	private void deleteAccessSubTreeRecord(String docId) throws SAXException {
1745 6606 leinfelder
		PreparedStatement pstmt = null;
1746 4678 daigle
		try {
1747 6606 leinfelder
			String sql = "DELETE FROM xml_accesssubtree WHERE docid = ?";
1748 4678 daigle
			// delete all acl records for resources related to @aclid if any
1749 6606 leinfelder
			pstmt = connection.prepareStatement(sql);
1750
			pstmt.setString(1, docId);
1751 4678 daigle
			// Increase DBConnection usage count
1752
			connection.increaseUsageCount(1);
1753 6606 leinfelder
			logMetacat.debug("running sql: " + sql);
1754
			pstmt.execute();
1755 4678 daigle
1756
		} catch (SQLException e) {
1757
			throw new SAXException(e.getMessage());
1758
		} finally {
1759
			try {
1760 6606 leinfelder
				pstmt.close();
1761 4678 daigle
			} catch (SQLException ee) {
1762
				throw new SAXException(ee.getMessage());
1763
			}
1764
		}
1765
	}// deleteAccessSubTreeRecord
1766
1767
	// open a file writer for writing inline data to file
1768 5752 leinfelder
	private Writer createInlineDataFileWriter(String fileName, String encoding) throws SAXException {
1769
		Writer writer = null;
1770 4678 daigle
		String path;
1771
		try {
1772
			path = PropertyService.getProperty("application.inlinedatafilepath");
1773
		} catch (PropertyNotFoundException pnfe) {
1774
			throw new SAXException(pnfe.getMessage());
1775
		}
1776
		/*
1777
		 * File inlineDataDirectory = new File(path);
1778
		 */
1779
		String newFile = path + "/" + fileName;
1780
		logMetacat.debug("inline file name: " + newFile);
1781
		try {
1782
			// true means append
1783 5752 leinfelder
			writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
1784 4678 daigle
		} catch (IOException ioe) {
1785
			throw new SAXException(ioe.getMessage());
1786
		}
1787
		return writer;
1788
	}
1789
1790
	// write inline data into file system and return file name(without path)
1791 5752 leinfelder
	private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
1792 4678 daigle
			throws SAXException {
1793
		try {
1794
			writer.write(data.toString());
1795
			writer.flush();
1796
		} catch (Exception e) {
1797
			throw new SAXException(e.getMessage());
1798
		}
1799
	}
1800
1801 4080 daigle
1802 2169 sgarg
1803 4678 daigle
	// if xml file failed to upload, we need to call this method to delete
1804
	// the inline data already in file system
1805
	public void deleteInlineFiles() throws SAXException {
1806
		if (!inlineFileIdList.isEmpty()) {
1807
			for (int i = 0; i < inlineFileIdList.size(); i++) {
1808
				String fileName = inlineFileIdList.elementAt(i);
1809
				deleteInlineDataFile(fileName);
1810
			}
1811
		}
1812
	}
1813 2169 sgarg
1814 4678 daigle
	/* delete the inline data file */
1815
	private void deleteInlineDataFile(String fileName) throws SAXException {
1816
		String path;
1817
		try {
1818
			path = PropertyService.getProperty("application.inlinedatafilepath");
1819
		} catch (PropertyNotFoundException pnfe) {
1820
			throw new SAXException("Could not find inline data file path: "
1821
					+ pnfe.getMessage());
1822
		}
1823
		File inlineDataDirectory = new File(path);
1824
		File newFile = new File(inlineDataDirectory, fileName);
1825
		newFile.delete();
1826 2169 sgarg
1827 4678 daigle
	}
1828 2169 sgarg
1829 4678 daigle
	/* Delete relations */
1830
	private void deleteRelations() throws SAXException {
1831
		PreparedStatement pStmt = null;
1832
		String sql = "DELETE FROM xml_relation where docid =?";
1833
		try {
1834
			pStmt = connection.prepareStatement(sql);
1835
			// bind variable
1836
			pStmt.setString(1, docid);
1837
			// execute query
1838
			pStmt.execute();
1839
			pStmt.close();
1840
		}// try
1841
		catch (SQLException e) {
1842
			throw new SAXException("EMLSAXHandler.deleteRelations(): " + e.getMessage());
1843
		}// catch
1844
		finally {
1845
			try {
1846
				pStmt.close();
1847
			}// try
1848
			catch (SQLException ee) {
1849
				throw new SAXException("EMLSAXHandler.deleteRelations: "
1850
						+ ee.getMessage());
1851
			}// catch
1852
		}// finally
1853
	}
1854 2169 sgarg
1855 4678 daigle
	/*
1856
	 * Write an online data file id into xml_relation table. The dataId
1857
	 * shouldnot have the revision
1858
	 */
1859
	private void writeOnlineDataFileIdIntoRelationTable(String dataId)
1860
			throws SAXException {
1861
		PreparedStatement pStmt = null;
1862
		String sql = "INSERT into xml_relation (docid, packagetype, subject, "
1863
				+ "relationship, object) values (?, ?, ?, ?, ?)";
1864
		try {
1865
			pStmt = connection.prepareStatement(sql);
1866
			// bind variable
1867
			pStmt.setString(1, docid);
1868 5709 leinfelder
			pStmt.setString(2, doctype); //DocumentImpl.EML2_1_0NAMESPACE);
1869 4678 daigle
			pStmt.setString(3, docid);
1870
			pStmt.setString(4, RELATION);
1871
			pStmt.setString(5, dataId);
1872
			// execute query
1873
			pStmt.execute();
1874
			pStmt.close();
1875
		}// try
1876
		catch (SQLException e) {
1877
			throw new SAXException(
1878
					"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1879
							+ e.getMessage());
1880
		}// catch
1881
		finally {
1882
			try {
1883
				pStmt.close();
1884
			}// try
1885
			catch (SQLException ee) {
1886
				throw new SAXException(
1887
						"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1888
								+ ee.getMessage());
1889
			}// catch
1890
		}// finally
1891 2169 sgarg
1892 4678 daigle
	}// writeOnlineDataFileIdIntoRelationTable
1893 2169 sgarg
1894 4678 daigle
	/*
1895
	 * This method will handle data file in online url. If the data file is in
1896
	 * ecogrid protocol, then the datafile identifier(without rev)should be put
1897
	 * into onlineDataFileRelationVector. The docid in this vector will be
1898
	 * insert into xml_relation table in endDocument(). If the data file doesn't
1899
	 * exsit in xml_documents or xml_revision table, or the user has all
1900
	 * permission to the data file if the docid already existed, the data file
1901
	 * id (without rev)will be put into onlineDataFileTopAccessVector. The top
1902
	 * access rules specified in this eml document will apply to the data file.
1903
	 * NEED to do: We should also need to implement http and ftp. Those external
1904
	 * files should be download and assign a data file id to it.
1905
	 */
1906
	private void handleOnlineUrlDataFile(String url) throws SAXException {
1907
		logMetacat.warn("The url is " + url);
1908 2169 sgarg
1909 4678 daigle
		if (currentDistributionSection == null) {
1910
			throw new SAXException("Trying to set the online file name for a null"
1911
					+ " distribution section");
1912
		}
1913
1914
		// if the url is not in ecogrid protocol, null will be returned
1915 5025 daigle
		String accessionNumber = DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
1916 7112 leinfelder
1917
		// check he accession number and get docid.rev if we can
1918
		String docid = null;
1919
		int rev = 0;
1920
		if (accessionNumber != null) {
1921
			// get rid of revision number to get the docid.
1922
			try {
1923
				docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
1924
				rev = DocumentUtil.getRevisionFromAccessionNumber(accessionNumber);
1925
			} catch (Exception e) {
1926
				logMetacat.warn(e.getClass().getName() + " - Problem parsing accession number for: " + accessionNumber + ". Message: " + e.getMessage());
1927
				accessionNumber = null;
1928
			}
1929
		}
1930
1931 4678 daigle
		if (accessionNumber == null) {
1932
			// the accession number is null if the url does not references a
1933
			// local data file (url would start with "ecogrid://"
1934
			currentDistributionSection
1935
					.setDistributionType(DistributionSection.ONLINE_DATA_DISTRIBUTION);
1936
		} else {
1937 7112 leinfelder
			// look up the guid using docid/rev
1938 6744 leinfelder
			String guid = null;
1939
			try {
1940
				guid = IdentifierManager.getInstance().getGUID(docid, rev);
1941
			} catch (McdbDocNotFoundException e1) {
1942 7135 leinfelder
				// no need to do this if we are not writing access rules for the data
1943
				if (!writeAccessRules) {
1944
					logMetacat.warn("Not configured to write access rules for data referenced by: " + url);
1945
					return;
1946
				}
1947 6744 leinfelder
				guid = docid + "." + rev;
1948
				IdentifierManager.getInstance().createMapping(guid, guid);
1949
			}
1950 4678 daigle
1951
			currentDistributionSection
1952
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
1953 6744 leinfelder
			currentDistributionSection.setDataFileName(guid);
1954 4678 daigle
1955
			// distributionOnlineFileName = docid;
1956 6746 leinfelder
			onlineDataFileIdInRelationVector.add(guid);
1957 4733 daigle
			try {
1958 4678 daigle
				if (!AccessionNumber.accNumberUsed(docid)) {
1959 6746 leinfelder
					onlineDataFileIdInTopAccessVector.add(guid);
1960 4678 daigle
				} else {
1961 6746 leinfelder
					// check the previous revision if we have it
1962
					int previousRevision = rev;
1963
					Vector<Integer> revisions = DBUtil.getRevListFromRevisionTable(docid);
1964
					if (revisions != null && revisions.size() > 0) {
1965
						previousRevision = revisions.get(revisions.size() - 1);
1966
					}
1967
					String previousDocid =
1968
						docid + PropertyService.getProperty("document.accNumSeparator") + previousRevision;
1969 8178 leinfelder
1970
					// check EITHER previous or current id for access rules
1971
					// see: https://projects.ecoinformatics.org/ecoinfo/issues/5647
1972
					PermissionController previousController = new PermissionController(previousDocid);
1973
					PermissionController currentController = new PermissionController(accessionNumber);
1974
					if (previousController.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
1975
							|| currentController.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
1976
							) {
1977 6746 leinfelder
						onlineDataFileIdInTopAccessVector.add(guid);
1978 4733 daigle
					} else {
1979
						throw new SAXException(UPDATEACCESSERROR);
1980
					}
1981
				}
1982 4678 daigle
			}// try
1983
			catch (Exception e) {
1984
				logMetacat.error("Eorr in "
1985
								+ "Eml210SAXHanlder.handleOnlineUrlDataFile is "
1986
								+ e.getMessage());
1987
				throw new SAXException(e.getMessage());
1988
			}
1989
		}
1990
	}
1991 2169 sgarg
}