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