Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that handles the SAX XML events as they
4
 *             are generated from XML documents
5
 *  Copyright: 2000 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Matt Jones, Jivka Bojilova
8
 *
9
 *   '$Author: leinfelder $'
10
 *     '$Date: 2011-03-23 15:39:35 -0700 (Wed, 23 Mar 2011) $'
11
 * '$Revision: 6019 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat;
29

    
30
import java.io.File;
31
import java.io.FileOutputStream;
32
import java.io.IOException;
33
import java.io.OutputStreamWriter;
34
import java.io.Writer;
35
import java.sql.PreparedStatement;
36
import java.sql.ResultSet;
37
import java.sql.SQLException;
38
import java.sql.Statement;
39
import java.util.EmptyStackException;
40
import java.util.Enumeration;
41
import java.util.Hashtable;
42
import java.util.Stack;
43
import java.util.Vector;
44

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

    
49
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlInterface;
50
import edu.ucsb.nceas.metacat.accesscontrol.AccessRule;
51
import edu.ucsb.nceas.metacat.accesscontrol.AccessSection;
52
import edu.ucsb.nceas.metacat.database.DBConnection;
53
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
54
import edu.ucsb.nceas.metacat.properties.PropertyService;
55
import edu.ucsb.nceas.metacat.util.DocumentUtil;
56
import edu.ucsb.nceas.metacat.util.MetacatUtil;
57
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
58

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

    
65
	private boolean processingTopLevelAccess = false;
66

    
67
	private boolean processingAdditionalAccess = false;
68

    
69
	private boolean processingOtherAccess = false;
70

    
71
	private AccessSection accessObject = null;
72

    
73
	private AccessRule accessRule = null;
74

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

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

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

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

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

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

    
99
	private AccessSection topAccessSection;
100

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

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

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

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

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

    
117
	private Writer inlineDataFileWriter = null;
118

    
119
	private String inlineDataFileName = null;
120

    
121
	DistributionSection currentDistributionSection = null;
122

    
123
	Vector<DistributionSection> allDistributionSections = new Vector<DistributionSection>();
124

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

    
131
	// private String distributionOnlineFileName = null;
132

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

    
136
	// Constant
137
	private static final String EML = "eml";
138

    
139
	private static final String DISTRIBUTION = "distribution";
140

    
141
	private static final String ORDER = "order";
142

    
143
	private static final String ID = "id";
144

    
145
	private static final String REFERENCES = "references";
146

    
147
	public static final String INLINE = "inline";
148

    
149
	private static final String ONLINE = "online";
150

    
151
	private static final String URL = "url";
152

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

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

    
160
	private static final String TOPLEVEL = "top";
161

    
162
	private static final String SUBTREELEVEL = "subtree";
163

    
164
	private static final String RELATION = "Provides info for";
165

    
166
	private Logger logMetacat = Logger.getLogger(Eml210SAXHandler.class);
167

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

    
201
			// If the action is update and user doesn't have "ALL" permission
202
			// we need to check if user can update access subtree			
203
			if (!control.hasPermission(user, groups, AccessControlInterface.ALLSTRING)
204
					&& action != null && action.equals("UPDATE")) {
205
				needToCheckAccessModule = true;
206
				unChangeableAccessSubTreeVector = getAccessSubTreeListFromDB();
207
			}
208

    
209
		} catch (Exception e) {
210
			throw new SAXException(e.getMessage());
211
		}
212
	}
213

    
214
	/*
215
	 * Get the subtree node info from xml_accesssubtree table
216
	 */
217
	private Vector<AccessSection> getAccessSubTreeListFromDB() throws Exception {
218
		Vector<AccessSection> result = new Vector<AccessSection>();
219
		PreparedStatement pstmt = null;
220
		ResultSet rs = null;
221
		String sql = "SELECT controllevel, subtreeid, startnodeid, endnodeid "
222
				+ "FROM xml_accesssubtree WHERE docid like ? "
223
				+ "ORDER BY startnodeid ASC";
224

    
225
		try {
226

    
227
			pstmt = connection.prepareStatement(sql);
228
			// Increase DBConnection usage count
229
			connection.increaseUsageCount(1);
230
			// Bind the values to the query
231
			pstmt.setString(1, docid);
232
			pstmt.execute();
233

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

    
274
	/** SAX Handler that is called at the start of each XML element */
275
	public void startElement(String uri, String localName, String qName, Attributes atts)
276
			throws SAXException {
277
		// for element <eml:eml...> qname is "eml:eml", local name is "eml"
278
		// for element <acl....> both qname and local name is "eml"
279
		// uri is namesapce
280
		logMetacat.debug("Start ELEMENT(qName) " + qName);
281
		logMetacat.debug("Start ELEMENT(localName) " + localName);
282
		logMetacat.debug("Start ELEMENT(uri) " + uri);
283

    
284
		DBSAXNode parentNode = null;
285
		DBSAXNode currentNode = null;
286

    
287
		if (!handleInlineData) {
288
			// Get a reference to the parent node for the id
289
			try {
290
				parentNode = (DBSAXNode) nodeStack.peek();
291
			} catch (EmptyStackException e) {
292
				parentNode = null;
293
			}
294

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

    
320
			}
321

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

    
326
				// compare top level access module
327
				if (processingTopLevelAccess && needToCheckAccessModule) {
328
					compareAccessTextNode(currentUnchangeableAccessModuleNodeStack, textBuffer);
329
				}
330

    
331
				if (needToCheckAccessModule
332
						&& (processingAdditionalAccess || processingOtherAccess || processingTopLevelAccess)) {
333
					// stored the pull out nodes into storedNode stack
334
					NodeRecord nodeElement = new NodeRecord(-2, -2, -2, "TEXT", null,
335
							null, MetacatUtil.normalize(textBuffer.toString()));
336
					storedAccessNodeStack.push(nodeElement);
337

    
338
				}
339

    
340
				// write the textbuffer into db for parent node.
341
				endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, parentNode);
342
				// rest hitTextNode
343
				hitTextNode = false;
344
				// reset textbuffer
345
				textBuffer = null;
346
				textBuffer = new StringBuffer();
347

    
348
			}
349

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

    
382
					try {
383
						// Get dbconnection
384
						dbConn = DBConnectionPool
385
								.getDBConnection("DBSAXHandler.startElement");
386
						serialNumber = dbConn.getCheckOutSerialNumber();
387

    
388
						Statement stmt = dbConn.createStatement();
389
						ResultSet rs = stmt
390
								.executeQuery("SELECT catalog_id FROM xml_catalog "
391
										+ "WHERE entry_type = 'Schema' "
392
										+ "AND public_id = '" + doctype + "'");
393
						boolean hasRow = rs.next();
394
						if (hasRow) {
395
							catalogid = rs.getString(1);
396
						}
397
						stmt.close();
398
					}// try
399
					finally {
400
						// Return dbconnection
401
						DBConnectionPool.returnDBConnection(dbConn, serialNumber);
402
					}// finally
403

    
404
					// create documentImpl object by the constructor which can
405
					// specify the revision
406
					if (!super.getIsRevisionDoc()) {
407
						currentDocument = new DocumentImpl(connection, rootNode
408
								.getNodeID(), docname, doctype, docid, revision, action,
409
								user, this.pub, catalogid, this.serverCode, createDate,
410
								updateDate);
411
					}
412

    
413
				} catch (McdbDocNotFoundException mdnfe) {
414
					Vector<Integer> revList = null;
415
					
416
					try {
417
						revList = DBUtil.getRevListFromRevisionTable(docid);
418
					} catch (SQLException sqle) {
419
						logMetacat.error("SQL error when trying to get rev list for doc " + docid + " : " + sqle.getMessage());
420
						throw (new SAXException("Doc ID " + docid + " was not found and cannot be updated.")); 
421
					}
422
					
423
					if (revList.size() > 0) {
424
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was deleted and cannot be updated."));
425
					} else {
426
						throw (new SAXException("EML210SaxHandler.startElement - Doc ID " + docid + " was not found and cannot be updated.")); 
427
					}
428
				} catch (Exception e) {
429
                    throw (new SAXException("EML210SaxHandler.startElement - error with action " + 
430
                    		action + " : " + e.getMessage()));
431
				}
432
			}
433

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

    
451
			// Add all of the attributes
452
			for (int i = 0; i < atts.getLength(); i++) {
453
				String attributeName = atts.getQName(i);
454
				String attributeValue = atts.getValue(i);
455
				endNodeId = currentNode
456
						.setAttribute(attributeName, attributeValue, docid);
457

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

    
467
				} else if (attributeName != null && attributeName.equals(ID)) {
468

    
469
				}
470
			}// for
471

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

    
497
				// load top level node stack to
498
				// currentUnchangableAccessModuleNodeStack
499
				if (processingTopLevelAccess && needToCheckAccessModule) {
500
					// get the node stack for
501
					currentUnchangeableAccessModuleNodeStack = topAccessSection
502
							.getSubTreeNodeStack();
503
				}
504
			} else if (localName.equals(DISTRIBUTION)) {
505
				distributionIndex++;
506
				currentDistributionSection = new DistributionSection(distributionIndex);
507

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

    
524
				accessRule = new AccessRule();
525

    
526
				// set permission type "allow"
527
				accessRule.setPermissionType(ALLOW);
528

    
529
			}
530
			// set up an access rule for deny
531
			else if (parentNode.getTagName() != null
532
					&& (parentNode.getTagName()).equals(ACCESS) && localName.equals(DENY)) {
533
				accessRule = new AccessRule();
534
				// set permission type "allow"
535
				accessRule.setPermissionType(DENY);
536
			}
537

    
538
			// Add the node to the stack, so that any text data can be
539
			// added as it is encountered
540
			nodeStack.push(currentNode);
541
			// Add the node to the vector used by thread for writing XML Index
542
			nodeIndex.addElement(currentNode);
543

    
544
			// compare top access level module
545
			if (processingTopLevelAccess && needToCheckAccessModule) {
546
				compareElementNameSpaceAttributes(
547
						currentUnchangeableAccessModuleNodeStack, namespaces, atts,
548
						localName, UPDATEACCESSERROR);
549

    
550
			}
551

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

    
567
			}
568

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

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

    
613
	}
614

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

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

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

    
677
		}// while
678

    
679
		// compare attributes
680
		for (int i = 0; i < attributes.getLength(); i++) {
681
			NodeRecord attriNode = null;
682
			try {
683
				attriNode = unchangeableNodeStack.pop();
684

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

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

    
714
	}
715

    
716
	/* method to compare current text node and node in db */
717
	private void compareAccessTextNode(Stack<NodeRecord> nodeStack, StringBuffer text) throws SAXException {
718
		NodeRecord node = null;
719
		// get node from current stack
720
		try {
721
			node = nodeStack.pop();
722
		} catch (EmptyStackException ee) {
723
			logMetacat.error("Node stack is empty for text data in startElement for doc id " + docid);
724
			throw new SAXException("Access rules could not be found in database.");
725
		}
726

    
727
		String dbAccessData = node.getNodeData();
728
		String docAccessData = text.toString().trim();
729
		
730
		logMetacat.debug("Eml210SAXHandler.compareAccessTextNode - \n" +
731
					"\t access node type from db:       " + node.getNodeType() + "\n" +
732
					"\t access node data from db:       " + node.getNodeData() + "\n" +
733
					"\t access node data from document: " + text.toString());
734
		
735
		if (!node.getNodeType().equals("TEXT")
736
				|| !docAccessData.equals(dbAccessData)) {
737
			logMetacat.warn("Eml210SAXHandler.compareAccessTextNode - Access record mismatch: \n" +
738
					"\t access node type from db:       " + node.getNodeType() + "\n" +
739
					"\t access node data from db:       " + dbAccessData + "\n" +
740
					"\t access node data from document: " + docAccessData);
741
			
742
			throw new SAXException(UPDATEACCESSERROR + " [Eml210SAXHandler.compareAccessTextNode]");
743
		}// if
744
	}
745

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

    
776
	/** SAX Handler that is called at the end of each XML element */
777
	public void endElement(String uri, String localName, String qName)
778
			throws SAXException {
779
		logMetacat.debug("End ELEMENT " + qName);
780

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

    
793
			// write put inline data file name into text buffer (without path)
794
			textBuffer = new StringBuffer(inlineDataFileName);
795
			// write file name into db
796
			endNodeId = writeTextForDBSAXNode(endNodeId, textBuffer, currentNode);
797
			// reset textbuff
798
			textBuffer = null;
799
			textBuffer = new StringBuffer();
800
			return;
801
		}
802

    
803
		if (!handleInlineData) {
804
			// Get the node from the stack
805
			DBSAXNode currentNode = (DBSAXNode) nodeStack.pop();
806
			String currentTag = currentNode.getTagName();
807

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

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

    
842
				} else if (currentTag.equals(URL)) {
843
					// handle online data, make sure its'parent is online
844
					DBSAXNode parentNode = (DBSAXNode) nodeStack.peek();
845
					if (parentNode != null && parentNode.getTagName() != null
846
							&& parentNode.getTagName().equals(ONLINE)) {
847
						// if online data is in local metacat, add it to the
848
						// vector
849
						data = (textBuffer.toString()).trim();
850
						handleOnlineUrlDataFile(data);
851

    
852
					}// if
853
				}// else if
854
				
855
				// write text to db if it is not inline data
856
				logMetacat.debug("Write text into DB in End Element");
857

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

    
874
			}
875

    
876
			// set hitText false
877
			hitTextNode = false;
878
			// reset textbuff
879
			textBuffer = null;
880
			textBuffer = new StringBuffer();
881

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

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

    
909
				accessObject.setEndNodeId(endNodeId);
910

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

    
917
				AccessSection newAccessObject = accessObject;
918

    
919
				if (newAccessObject != null) {
920

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

    
925
						newAccessObject.setStoredTmpNodeStack(storedAccessNodeStack);
926
						accessObjectList.add(newAccessObject);
927
					}
928
					if (processingTopLevelAccess) {
929

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

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

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

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

    
957
				}// if
958
				// check if access node stack is empty after parsing top access
959
				// module
960

    
961
				if (needToCheckAccessModule && processingTopLevelAccess
962
						&& !currentUnchangeableAccessModuleNodeStack.isEmpty()) {
963

    
964
					logMetacat.error("Access node stack is not empty after "
965
							+ "parsing access subtree");
966
					throw new SAXException(UPDATEACCESSERROR);
967

    
968
				}
969
				// reset access section object
970

    
971
				accessObject = null;
972

    
973
				// reset tmp stored node stack
974
				storedAccessNodeStack = null;
975
				storedAccessNodeStack = new Stack<NodeRecord>();
976

    
977
				// reset flag
978
				processingAdditionalAccess = false;
979
				processingTopLevelAccess = false;
980
				processingOtherAccess = false;
981

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

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

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

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

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

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

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

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

    
1138
				}
1139
				endNodeId = currentNode.writeChildNodeToDB("TEXT", null, data, docid);
1140
		} else {
1141
			// This is inline data write to file directly
1142
			StringBuffer inlineWhiteSpace = new StringBuffer(new String(cbuf, start, len));
1143
			writeInlineDataIntoFile(inlineDataFileWriter, inlineWhiteSpace);
1144
		}
1145

    
1146
	}
1147

    
1148
	/* Compare whitespace from xml and db */
1149
	private void compareWhiteSpace(Stack<NodeRecord> nodeStack, String string,
1150
			String error) throws SAXException {
1151
		NodeRecord node = null;
1152
		try {
1153
			node = nodeStack.pop();
1154
		} catch (EmptyStackException ee) {
1155
			logMetacat.error("the stack is empty for whitespace data");
1156
			throw new SAXException(error);
1157
		}
1158
		if (!node.getNodeType().equals("TEXT") || !string.equals(node.getNodeData())) {
1159
			logMetacat.error("Inconsistence happened: ");
1160
			logMetacat.error("current node type from xml is WHITESPACE TEXT");
1161
			logMetacat.error("node type from stack: " + node.getNodeType());
1162
			logMetacat.error("current node data from xml is: " + string);
1163
			logMetacat.error("node data from stack: " + node.getNodeData());
1164
			logMetacat.error("node is from stack: " + node.getNodeId());
1165
			throw new SAXException(error);
1166
		}// if
1167
	}
1168

    
1169
	/** SAX Handler that receives notification of end of the document */
1170
	public void endDocument() throws SAXException {
1171
		logMetacat.debug("end Document");
1172
		// There are some unchangable subtree didn't be compare
1173
		// This maybe cause user change the subtree id
1174
		if (!super.getIsRevisionDoc()) {
1175
			// write access rule to db
1176
			writeAccessRuleToDB();
1177
			// delete relation table
1178
			deleteRelations();
1179
			// write relations
1180
			for (int i = 0; i < onlineDataFileIdInRelationVector.size(); i++) {
1181
				String id = onlineDataFileIdInRelationVector.elementAt(i);
1182
				writeOnlineDataFileIdIntoRelationTable(id);
1183
			}
1184
		}
1185
	}
1186

    
1187
	/* The method to write all access rule into db */
1188
	private void writeAccessRuleToDB() throws SAXException {
1189
		// Delete old permssion
1190
		deletePermissionsInAccessTable(docid);
1191
		// write top leve access rule
1192
		writeTopLevelAccessRuleToDB();
1193
		// write additional access rule
1194
		// writeAdditionalAccessRuleToDB();
1195
		writeAdditionalAccessRulesToDB();
1196
	}// writeAccessRuleToDB
1197

    
1198
	/* The method to write top level access rule into db. */
1199
	private void writeTopLevelAccessRuleToDB() throws SAXException {
1200
		// for top document level
1201
		AccessSection accessSection = topLevelAccessControlMap.get(docid);
1202
		boolean top = true;
1203
		String subSectionId = null;
1204
		if (accessSection != null) {
1205
			AccessSection accessSectionObj = accessSection;
1206

    
1207
			// if accessSection is not null and is not reference
1208
			if (accessSectionObj.getReferences() == null) {
1209
				// write the top level access module into xml_accesssubtree to
1210
				// store info and then when update to check if the user can
1211
				// update it or not
1212
				deleteAccessSubTreeRecord(docid);
1213
				writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1214

    
1215
				// write access section into xml_access table
1216
				writeGivenAccessRuleIntoDB(accessSectionObj, top, subSectionId);
1217
				// write online data file into xml_access too.
1218
				// for (int i= 0; i <onlineDataFileIdInTopAccessVector.size();
1219
				// i++)
1220
				// {
1221
				// String id = onlineDataFileIdInTopAccessVector.elementAt(i);
1222
				// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1223
				// id);
1224
				// }
1225

    
1226
			} else {
1227

    
1228
				// this is a reference and go trough the vector which contains
1229
				// all access object
1230
				String referenceId = accessSectionObj.getReferences();
1231
				boolean findAccessObject = false;
1232
				logMetacat.debug("referered id for top access: " + referenceId);
1233
				for (int i = 0; i < accessObjectList.size(); i++) {
1234
					AccessSection accessObj = accessObjectList.elementAt(i);
1235
					String accessObjId = accessObj.getSubTreeId();
1236
					if (referenceId != null && accessObj != null
1237
							&& referenceId.equals(accessObjId)) {
1238
						// make sure the user didn't change any thing in this
1239
						// access moduel
1240
						// too if user doesn't have all permission
1241
						if (needToCheckAccessModule) {
1242

    
1243
							Stack<NodeRecord> newStack = accessObj
1244
									.getStoredTmpNodeStack();
1245
							// revise order
1246
							newStack = DocumentUtil.reviseStack(newStack);
1247
							// go throught the vector of
1248
							// unChangeableAccessSubtreevector
1249
							// and find the one whose id is as same as
1250
							// referenceid
1251
							AccessSection oldAccessObj = getAccessSectionFromUnchangableAccessVector(referenceId);
1252
							// if oldAccessObj is null something is wrong
1253
							if (oldAccessObj == null) {
1254
								throw new SAXException(UPDATEACCESSERROR);
1255
							}// if
1256
							else {
1257
								// Get the node stack from old access obj
1258
								Stack<NodeRecord> oldStack = oldAccessObj
1259
										.getSubTreeNodeStack();
1260
								compareNodeStacks(newStack, oldStack);
1261
							}// else
1262
						}// if
1263
						// write accessobject into db
1264
						writeGivenAccessRuleIntoDB(accessObj, top, subSectionId);
1265
						// write online data file into xml_access too.
1266
						// for (int j= 0; j
1267
						// <onlineDataFileIdInTopAccessVector.size(); j++)
1268
						// {
1269
						// String id =
1270
						// onlineDataFileIdInTopAccessVector.elementAt(j);
1271
						// writeAccessRuleForRelatedDataFileIntoDB(accessSectionObj,
1272
						// id);
1273
						// }
1274

    
1275
						// write the reference access into xml_accesssubtree
1276
						// too write the top level access module into
1277
						// xml_accesssubtree to store info and then when update
1278
						// to check if the user can update it or not
1279
						deleteAccessSubTreeRecord(docid);
1280
						writeAccessSubTreeIntoDB(accessSectionObj, TOPLEVEL);
1281
						writeAccessSubTreeIntoDB(accessObj, SUBTREELEVEL);
1282
						findAccessObject = true;
1283
						break;
1284
					}
1285
				}// for
1286
				// if we couldn't find an access subtree id for this reference
1287
				// id
1288
				if (!findAccessObject) {
1289
					throw new SAXException("The referenceid: " + referenceId
1290
							+ " is not access subtree");
1291
				}// if
1292
			}// else
1293

    
1294
		}// if
1295
		else {
1296
			// couldn't find a access section object
1297
			logMetacat.warn("couldn't find access control for document: " + docid);
1298
		}
1299

    
1300
	}// writeTopLevelAccessRuletoDB
1301

    
1302
	/* Given a subtree id and find the responding access section */
1303
	private AccessSection getAccessSectionFromUnchangableAccessVector(String id) {
1304
		AccessSection result = null;
1305
		// Makse sure the id
1306
		if (id == null || id.equals("")) {
1307
			return result;
1308
		}
1309
		// go throught vector and find the list
1310
		for (int i = 0; i < unChangeableAccessSubTreeVector.size(); i++) {
1311
			AccessSection accessObj = unChangeableAccessSubTreeVector.elementAt(i);
1312
			if (accessObj.getSubTreeId() != null && (accessObj.getSubTreeId()).equals(id)) {
1313
				result = accessObj;
1314
			}// if
1315
		}// for
1316
		return result;
1317
	}// getAccessSectionFromUnchangableAccessVector
1318

    
1319
	/* Compare two node stacks to see if they are same */
1320
	private void compareNodeStacks(Stack<NodeRecord> stack1, Stack<NodeRecord> stack2)
1321
			throws SAXException {
1322
		// make sure stack1 and stack2 are not empty
1323
		if (stack1.isEmpty() || stack2.isEmpty()) {
1324
			logMetacat.error("Because stack is empty!");
1325
			throw new SAXException(UPDATEACCESSERROR);
1326
		}
1327
		// go throw two stacks and compare every element
1328
		while (!stack1.isEmpty()) {
1329
			// Pop an element from stack1
1330
			NodeRecord record1 = stack1.pop();
1331
			// Pop an element from stack2(stack 2 maybe empty)
1332
			NodeRecord record2 = null;
1333
			try {
1334
				record2 = stack2.pop();
1335
			} catch (EmptyStackException ee) {
1336
				logMetacat.error("Node stack2 is empty but stack1 isn't!");
1337
				throw new SAXException(UPDATEACCESSERROR);
1338
			}
1339
			// if two records are not same throw a exception
1340
			if (!record1.contentEquals(record2)) {
1341
				logMetacat.error("Two records from new and old stack are not " + "same!");
1342
				throw new SAXException(UPDATEACCESSERROR);
1343
			}// if
1344
		}// while
1345

    
1346
		// now stack1 is empty and we should make sure stack2 is empty too
1347
		if (!stack2.isEmpty()) {
1348
			logMetacat
1349
					.error("stack2 still has some elements while stack " + "is empty! ");
1350
			throw new SAXException(UPDATEACCESSERROR);
1351
		}// if
1352
	}// comparingNodeStacks
1353

    
1354
	/* The method to write additional access rule into db. */
1355
	private void writeAdditionalAccessRulesToDB() throws SAXException {
1356
		
1357
		// Iterate through every distribution and write access sections for data and inline
1358
		// types to the database
1359
		for (DistributionSection distributionSection : allDistributionSections) {			
1360
			// we're only interested in data and inline distributions
1361
			int distributionType = distributionSection.getDistributionType();
1362
			if (distributionType == DistributionSection.DATA_DISTRIBUTION
1363
					|| distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION) {
1364
				AccessSection accessSection = distributionSection.getAccessSection();
1365
				
1366
				// If the distribution doesn't have an access section, we continue.
1367
				if (accessSection == null) {
1368
					continue;		
1369
				} 
1370
				
1371
				// We want to check file permissions for all online data updates and inserts, or for 
1372
				// inline updates.
1373
//				if (distributionType == DistributionSection.DATA_DISTRIBUTION
1374
//						|| (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE")) {
1375

    
1376
				if (distributionType == DistributionSection.DATA_DISTRIBUTION) {
1377
					try {
1378
						PermissionController controller = new PermissionController(
1379
								distributionSection.getDataFileName(), false);
1380
						if (AccessionNumber.accNumberUsed(docid)
1381
								&& !controller.hasPermission(user, groups, "WRITE")) {
1382
							throw new SAXException(UPDATEACCESSERROR);
1383
						}
1384
					} catch (SQLException sqle) {
1385
						throw new SAXException(
1386
								"Database error checking user permissions: "
1387
										+ sqle.getMessage());
1388
					} catch (Exception e) {
1389
						throw new SAXException(
1390
								"General error checking user permissions: "
1391
										+ e.getMessage());
1392
					}
1393
				} else if (distributionType == DistributionSection.INLINE_DATA_DISTRIBUTION && action == "UPDATE") {
1394
					try {
1395
						PermissionController controller = new PermissionController(
1396
								docid, false);
1397

    
1398
						if (!controller.hasPermission(user, groups, "WRITE")) {
1399
							throw new SAXException(UPDATEACCESSERROR);
1400
						}
1401
					} catch (SQLException sqle) {
1402
						throw new SAXException(
1403
								"Database error checking user permissions: "
1404
										+ sqle.getMessage());
1405
					} catch (Exception e) {
1406
						throw new SAXException(
1407
								"General error checking user permissions: "
1408
										+ e.getMessage());
1409
					}
1410
				}
1411
				
1412
				String subSectionId = Integer.toString(distributionSection.getDistributionId());
1413
				writeGivenAccessRuleIntoDB(accessSection, false, subSectionId);
1414
			}
1415

    
1416
		}
1417
		
1418
		
1419
	}
1420

    
1421
	/* Write a given access rule into db */
1422
	private void writeGivenAccessRuleIntoDB(AccessSection accessSection,
1423
			boolean topLevel, String subSectionId) throws SAXException {
1424
		if (accessSection == null) {
1425
			throw new SAXException("The access object is null");
1426
		}
1427

    
1428
		String permOrder = accessSection.getPermissionOrder();
1429
		String sql = null;
1430
		PreparedStatement pstmt = null;
1431
		if (topLevel) {
1432
			sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1433
					+ "perm_type, perm_order, accessfileid) VALUES "
1434
					+ " (?, ?, ?, ?, ?, ?)";
1435
		} else {
1436
			sql = "INSERT INTO xml_access (docid,principal_name, "
1437
					+ "permission, perm_type, perm_order, accessfileid, subtreeid"
1438
					+ ") VALUES" + " (?, ?, ?, ?, ?, ?, ?)";
1439
		}
1440
		try {
1441

    
1442
			pstmt = connection.prepareStatement(sql);
1443
			// Increase DBConnection usage count
1444
			connection.increaseUsageCount(1);
1445
			// Bind the values to the query
1446
			pstmt.setString(6, docid);
1447
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1448
			pstmt.setString(5, permOrder);
1449
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1450
			// if it is not top level, set subsection id
1451
			if (topLevel) {
1452
				pstmt.setString(1, docid);
1453
				logMetacat.debug("Docid in accesstable: " + docid);
1454
			}
1455
			if (!topLevel) {
1456
				pstmt.setString(1, accessSection.getDataFileName());
1457
				logMetacat.debug("Docid in accesstable: " + inlineDataFileName);
1458

    
1459
				// for subtree should specify the
1460
				if (subSectionId == null) {
1461
					throw new SAXException("The subsection is null");
1462
				}
1463

    
1464
				pstmt.setString(7, subSectionId);
1465
				logMetacat.debug("SubSectionId in accesstable: " + subSectionId);
1466
			}
1467

    
1468
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1469
			// go through every rule
1470
			for (int i = 0; i < accessRules.size(); i++) {
1471
				AccessRule rule = accessRules.elementAt(i);
1472
				String permType = rule.getPermissionType();
1473
				int permission = rule.getPermission();
1474
				pstmt.setInt(3, permission);
1475
				logMetacat.debug("permission in accesstable: " + permission);
1476
				pstmt.setString(4, permType);
1477
				logMetacat.debug("Permtype in accesstable: " + permType);
1478
				// go through every principle in rule
1479
				Vector<String> nameVector = rule.getPrincipal();
1480
				for (int j = 0; j < nameVector.size(); j++) {
1481
					String prName = nameVector.elementAt(j);
1482
					pstmt.setString(2, prName);
1483
					logMetacat.debug("Principal in accesstable: " + prName);
1484
					logMetacat.debug("running sql: " + pstmt.toString());
1485
					pstmt.execute();
1486
				}// for
1487
			}// for
1488
			pstmt.close();
1489
		}// try
1490
		catch (SQLException e) {
1491
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1492
					+ e.getMessage());
1493
		}// catch
1494
		finally {
1495
			try {
1496
				pstmt.close();
1497
			} catch (SQLException ee) {
1498
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1499
						+ ee.getMessage());
1500
			}
1501
		}// finally
1502

    
1503
	}// writeGivenAccessRuleIntoDB
1504

    
1505
	/* Write a gaven access rule into db */
1506
	private void writeAccessRuleForRelatedDataFileIntoDB(AccessSection accessSection,
1507
			String dataId) throws SAXException {
1508
		if (accessSection == null) {
1509
			throw new SAXException("The access object is null");
1510
		}
1511
		// get rid of rev from dataId
1512
		// dataId = MetacatUtil.getDocIdFromString(dataId);
1513
		String permOrder = accessSection.getPermissionOrder();
1514
		String sql = null;
1515
		PreparedStatement pstmt = null;
1516
		sql = "INSERT INTO xml_access (docid, principal_name, permission, "
1517
				+ "perm_type, perm_order, accessfileid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1518

    
1519
		try {
1520

    
1521
			pstmt = connection.prepareStatement(sql);
1522
			// Increase DBConnection usage count
1523
			connection.increaseUsageCount(1);
1524
			// Bind the values to the query
1525
			pstmt.setString(1, dataId);
1526
			logMetacat.debug("Docid in accesstable: " + docid);
1527
			pstmt.setString(6, docid);
1528
			logMetacat.debug("Accessfileid in accesstable: " + docid);
1529
			pstmt.setString(5, permOrder);
1530
			logMetacat.debug("PermOder in accesstable: " + permOrder);
1531
			// if it is not top level, set subsection id
1532

    
1533
			Vector<AccessRule> accessRules = accessSection.getAccessRules();
1534
			// go through every rule
1535
			for (int i = 0; i < accessRules.size(); i++) {
1536
				AccessRule rule = accessRules.elementAt(i);
1537
				String permType = rule.getPermissionType();
1538
				int permission = rule.getPermission();
1539
				pstmt.setInt(3, permission);
1540
				logMetacat.debug("permission in accesstable: " + permission);
1541
				pstmt.setString(4, permType);
1542
				logMetacat.debug("Permtype in accesstable: " + permType);
1543
				// go through every principle in rule
1544
				Vector<String> nameVector = rule.getPrincipal();
1545
				for (int j = 0; j < nameVector.size(); j++) {
1546
					String prName = nameVector.elementAt(j);
1547
					pstmt.setString(2, prName);
1548
					logMetacat.debug("Principal in accesstable: " + prName);
1549
					logMetacat.debug("running sql: " + pstmt.toString());
1550
					pstmt.execute();
1551
				}// for
1552
			}// for
1553
			pstmt.close();
1554
		}// try
1555
		catch (SQLException e) {
1556
			throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1557
					+ e.getMessage());
1558
		}// catch
1559
		finally {
1560
			try {
1561
				pstmt.close();
1562
			} catch (SQLException ee) {
1563
				throw new SAXException("EMLSAXHandler.writeAccessRuletoDB(): "
1564
						+ ee.getMessage());
1565
			}
1566
		}// finally
1567

    
1568
	}// writeAccessRuleForRalatedDataFileIntoDB
1569

    
1570
	/* Delete from db all permission for resources related to @aclid if any. */
1571
	private void deletePermissionsInAccessTable(String aclid) throws SAXException {
1572
		Statement stmt = null;
1573
		try {
1574
			// delete all acl records for resources related to @aclid if any
1575
			stmt = connection.createStatement();
1576
			// Increase DBConnection usage count
1577
			connection.increaseUsageCount(1);
1578
			logMetacat.debug("running sql: DELETE FROM xml_access WHERE accessfileid = '"
1579
					+ aclid + "'");
1580
			stmt.execute("DELETE FROM xml_access WHERE accessfileid = '" + aclid + "'");
1581

    
1582
		} catch (SQLException e) {
1583
			throw new SAXException(e.getMessage());
1584
		} finally {
1585
			try {
1586
				stmt.close();
1587
			} catch (SQLException ee) {
1588
				throw new SAXException(ee.getMessage());
1589
			}
1590
		}
1591
	}// deletePermissionsInAccessTable
1592

    
1593
	/*
1594
	 * In order to make sure only usr has "all" permission can update access
1595
	 * subtree in eml document we need to keep access subtree info in
1596
	 * xml_accesssubtree table, such as docid, version, startnodeid, endnodeid
1597
	 */
1598
	private void writeAccessSubTreeIntoDB(AccessSection accessSection, String level)
1599
			throws SAXException {
1600
		if (accessSection == null) {
1601
			throw new SAXException("The access object is null");
1602
		}
1603

    
1604
		String sql = null;
1605
		PreparedStatement pstmt = null;
1606
		sql = "INSERT INTO xml_accesssubtree (docid, rev, controllevel, "
1607
				+ "subtreeid, startnodeid, endnodeid) VALUES " + " (?, ?, ?, ?, ?, ?)";
1608
		try {
1609

    
1610
			pstmt = connection.prepareStatement(sql);
1611
			// Increase DBConnection usage count
1612
			connection.increaseUsageCount(1);
1613
			long startNodeId = accessSection.getStartNodeId();
1614
			long endNodeId = accessSection.getEndNodeId();
1615
			String sectionId = accessSection.getSubTreeId();
1616
			// Bind the values to the query
1617
			pstmt.setString(1, docid);
1618
			logMetacat.debug("Docid in access-subtreetable: " + docid);
1619
			pstmt.setLong(2, (new Long(revision)).longValue());
1620
			logMetacat.debug("rev in accesssubtreetable: " + revision);
1621
			pstmt.setString(3, level);
1622
			logMetacat.debug("contorl level in access-subtree table: " + level);
1623
			pstmt.setString(4, sectionId);
1624
			logMetacat.debug("Subtree id in access-subtree table: " + sectionId);
1625
			pstmt.setLong(5, startNodeId);
1626
			logMetacat.debug("Start node id is: " + startNodeId);
1627
			pstmt.setLong(6, endNodeId);
1628
			logMetacat.debug("End node id is: " + endNodeId);
1629
			logMetacat.debug("running sql: " + pstmt.toString());
1630
			pstmt.execute();
1631
			pstmt.close();
1632
		}// try
1633
		catch (SQLException e) {
1634
			throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1635
					+ e.getMessage());
1636
		}// catch
1637
		finally {
1638
			try {
1639
				pstmt.close();
1640
			} catch (SQLException ee) {
1641
				throw new SAXException("EMLSAXHandler.writeAccessSubTreeIntoDB(): "
1642
						+ ee.getMessage());
1643
			}
1644
		}// finally
1645

    
1646
	}// writeAccessSubtreeIntoDB
1647

    
1648
	/* Delete every access subtree record from xml_accesssubtree. */
1649
	private void deleteAccessSubTreeRecord(String docId) throws SAXException {
1650
		Statement stmt = null;
1651
		try {
1652
			// delete all acl records for resources related to @aclid if any
1653
			stmt = connection.createStatement();
1654
			// Increase DBConnection usage count
1655
			connection.increaseUsageCount(1);
1656
			logMetacat.debug("running sql: DELETE FROM xml_accesssubtree WHERE docid = '"
1657
					+ docId + "'");
1658
			stmt.execute("DELETE FROM xml_accesssubtree WHERE docid = '" + docId + "'");
1659

    
1660
		} catch (SQLException e) {
1661
			throw new SAXException(e.getMessage());
1662
		} finally {
1663
			try {
1664
				stmt.close();
1665
			} catch (SQLException ee) {
1666
				throw new SAXException(ee.getMessage());
1667
			}
1668
		}
1669
	}// deleteAccessSubTreeRecord
1670

    
1671
	// open a file writer for writing inline data to file
1672
	private Writer createInlineDataFileWriter(String fileName, String encoding) throws SAXException {
1673
		Writer writer = null;
1674
		String path;
1675
		try {
1676
			path = PropertyService.getProperty("application.inlinedatafilepath");
1677
		} catch (PropertyNotFoundException pnfe) {
1678
			throw new SAXException(pnfe.getMessage());
1679
		}
1680
		/*
1681
		 * File inlineDataDirectory = new File(path);
1682
		 */
1683
		String newFile = path + "/" + fileName;
1684
		logMetacat.debug("inline file name: " + newFile);
1685
		try {
1686
			// true means append
1687
			writer = new OutputStreamWriter(new FileOutputStream(newFile, true), encoding);
1688
		} catch (IOException ioe) {
1689
			throw new SAXException(ioe.getMessage());
1690
		}
1691
		return writer;
1692
	}
1693

    
1694
	// write inline data into file system and return file name(without path)
1695
	private void writeInlineDataIntoFile(Writer writer, StringBuffer data)
1696
			throws SAXException {
1697
		try {
1698
			writer.write(data.toString());
1699
			writer.flush();
1700
		} catch (Exception e) {
1701
			throw new SAXException(e.getMessage());
1702
		}
1703
	}
1704

    
1705

    
1706

    
1707
	// if xml file failed to upload, we need to call this method to delete
1708
	// the inline data already in file system
1709
	public void deleteInlineFiles() throws SAXException {
1710
		if (!inlineFileIdList.isEmpty()) {
1711
			for (int i = 0; i < inlineFileIdList.size(); i++) {
1712
				String fileName = inlineFileIdList.elementAt(i);
1713
				deleteInlineDataFile(fileName);
1714
			}
1715
		}
1716
	}
1717

    
1718
	/* delete the inline data file */
1719
	private void deleteInlineDataFile(String fileName) throws SAXException {
1720
		String path;
1721
		try {
1722
			path = PropertyService.getProperty("application.inlinedatafilepath");
1723
		} catch (PropertyNotFoundException pnfe) {
1724
			throw new SAXException("Could not find inline data file path: "
1725
					+ pnfe.getMessage());
1726
		}
1727
		File inlineDataDirectory = new File(path);
1728
		File newFile = new File(inlineDataDirectory, fileName);
1729
		newFile.delete();
1730

    
1731
	}
1732

    
1733
	/* Delete relations */
1734
	private void deleteRelations() throws SAXException {
1735
		PreparedStatement pStmt = null;
1736
		String sql = "DELETE FROM xml_relation where docid =?";
1737
		try {
1738
			pStmt = connection.prepareStatement(sql);
1739
			// bind variable
1740
			pStmt.setString(1, docid);
1741
			// execute query
1742
			pStmt.execute();
1743
			pStmt.close();
1744
		}// try
1745
		catch (SQLException e) {
1746
			throw new SAXException("EMLSAXHandler.deleteRelations(): " + e.getMessage());
1747
		}// catch
1748
		finally {
1749
			try {
1750
				pStmt.close();
1751
			}// try
1752
			catch (SQLException ee) {
1753
				throw new SAXException("EMLSAXHandler.deleteRelations: "
1754
						+ ee.getMessage());
1755
			}// catch
1756
		}// finally
1757
	}
1758

    
1759
	/*
1760
	 * Write an online data file id into xml_relation table. The dataId
1761
	 * shouldnot have the revision
1762
	 */
1763
	private void writeOnlineDataFileIdIntoRelationTable(String dataId)
1764
			throws SAXException {
1765
		PreparedStatement pStmt = null;
1766
		String sql = "INSERT into xml_relation (docid, packagetype, subject, "
1767
				+ "relationship, object) values (?, ?, ?, ?, ?)";
1768
		try {
1769
			pStmt = connection.prepareStatement(sql);
1770
			// bind variable
1771
			pStmt.setString(1, docid);
1772
			pStmt.setString(2, doctype); //DocumentImpl.EML2_1_0NAMESPACE);
1773
			pStmt.setString(3, docid);
1774
			pStmt.setString(4, RELATION);
1775
			pStmt.setString(5, dataId);
1776
			// execute query
1777
			pStmt.execute();
1778
			pStmt.close();
1779
		}// try
1780
		catch (SQLException e) {
1781
			throw new SAXException(
1782
					"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1783
							+ e.getMessage());
1784
		}// catch
1785
		finally {
1786
			try {
1787
				pStmt.close();
1788
			}// try
1789
			catch (SQLException ee) {
1790
				throw new SAXException(
1791
						"EMLSAXHandler.writeOnlineDataFileIdIntoRelationTable(): "
1792
								+ ee.getMessage());
1793
			}// catch
1794
		}// finally
1795

    
1796
	}// writeOnlineDataFileIdIntoRelationTable
1797

    
1798
	/*
1799
	 * This method will handle data file in online url. If the data file is in
1800
	 * ecogrid protocol, then the datafile identifier(without rev)should be put
1801
	 * into onlineDataFileRelationVector. The docid in this vector will be
1802
	 * insert into xml_relation table in endDocument(). If the data file doesn't
1803
	 * exsit in xml_documents or xml_revision table, or the user has all
1804
	 * permission to the data file if the docid already existed, the data file
1805
	 * id (without rev)will be put into onlineDataFileTopAccessVector. The top
1806
	 * access rules specified in this eml document will apply to the data file.
1807
	 * NEED to do: We should also need to implement http and ftp. Those external
1808
	 * files should be download and assign a data file id to it.
1809
	 */
1810
	private void handleOnlineUrlDataFile(String url) throws SAXException {
1811
		logMetacat.warn("The url is " + url);
1812

    
1813
		if (currentDistributionSection == null) {
1814
			throw new SAXException("Trying to set the online file name for a null"
1815
					+ " distribution section");
1816
		}
1817

    
1818
		// if the url is not in ecogrid protocol, null will be returned
1819
		String accessionNumber = DocumentUtil.getAccessionNumberFromEcogridIdentifier(url);
1820
		if (accessionNumber == null) {
1821
			// the accession number is null if the url does not references a
1822
			// local data file (url would start with "ecogrid://"
1823
			currentDistributionSection
1824
					.setDistributionType(DistributionSection.ONLINE_DATA_DISTRIBUTION);
1825
		} else {
1826
			// handle ecogrid protocol
1827
			// get rid of revision number to get the docid.
1828
			String docid = DocumentUtil.getDocIdFromAccessionNumber(accessionNumber);
1829

    
1830
			currentDistributionSection
1831
					.setDistributionType(DistributionSection.DATA_DISTRIBUTION);
1832
			currentDistributionSection.setDataFileName(docid);
1833

    
1834
			// distributionOnlineFileName = docid;
1835
			onlineDataFileIdInRelationVector.add(docid);
1836
			try {				
1837
				if (!AccessionNumber.accNumberUsed(docid)) {
1838
					onlineDataFileIdInTopAccessVector.add(docid);
1839
				} else {
1840
					PermissionController controller = new PermissionController(accessionNumber);				
1841
					if (controller.hasPermission(user, groups,AccessControlInterface.ALLSTRING)) {
1842
						onlineDataFileIdInTopAccessVector.add(docid);
1843
					} else {
1844
						throw new SAXException(UPDATEACCESSERROR);
1845
					}
1846
				} 
1847
			}// try
1848
			catch (Exception e) {
1849
				logMetacat.error("Eorr in "
1850
								+ "Eml210SAXHanlder.handleOnlineUrlDataFile is "
1851
								+ e.getMessage());
1852
				throw new SAXException(e.getMessage());
1853
			}
1854
		}
1855
	}
1856
}
(33-33/65)