Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class for upgrading the database to version 1.5
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Saurabh Garg
7
 *
8
 *   '$Author: leinfelder $'
9
 *     '$Date: 2012-01-17 11:43:27 -0800 (Tue, 17 Jan 2012) $'
10
 * '$Revision: 6911 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26
package edu.ucsb.nceas.metacat.dataone;
27

    
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.math.BigInteger;
31
import java.net.URL;
32
import java.net.URLConnection;
33
import java.security.NoSuchAlgorithmException;
34
import java.sql.SQLException;
35
import java.util.ArrayList;
36
import java.util.Collections;
37
import java.util.Date;
38
import java.util.HashMap;
39
import java.util.Hashtable;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.Vector;
43

    
44
import javax.xml.parsers.ParserConfigurationException;
45
import javax.xml.xpath.XPathExpressionException;
46

    
47
import org.apache.commons.beanutils.BeanUtils;
48
import org.apache.commons.io.IOUtils;
49
import org.apache.log4j.Logger;
50
import org.apache.wicket.protocol.http.MockHttpServletRequest;
51
import org.dataone.client.ObjectFormatCache;
52
import org.dataone.ore.ResourceMapFactory;
53
import org.dataone.service.exceptions.BaseException;
54
import org.dataone.service.exceptions.InvalidRequest;
55
import org.dataone.service.exceptions.InvalidSystemMetadata;
56
import org.dataone.service.exceptions.InvalidToken;
57
import org.dataone.service.exceptions.NotAuthorized;
58
import org.dataone.service.exceptions.NotFound;
59
import org.dataone.service.exceptions.NotImplemented;
60
import org.dataone.service.exceptions.ServiceFailure;
61
import org.dataone.service.types.v1.AccessPolicy;
62
import org.dataone.service.types.v1.Checksum;
63
import org.dataone.service.types.v1.Identifier;
64
import org.dataone.service.types.v1.NodeReference;
65
import org.dataone.service.types.v1.ObjectFormatIdentifier;
66
import org.dataone.service.types.v1.Session;
67
import org.dataone.service.types.v1.Subject;
68
import org.dataone.service.types.v1.SystemMetadata;
69
import org.dataone.service.types.v1.util.ChecksumUtil;
70
import org.dataone.service.util.DateTimeMarshaller;
71
import org.dspace.foresite.ResourceMap;
72
import org.ecoinformatics.datamanager.DataManager;
73
import org.ecoinformatics.datamanager.database.DatabaseConnectionPoolInterface;
74
import org.ecoinformatics.datamanager.parser.DataPackage;
75
import org.jibx.runtime.JiBXException;
76
import org.xml.sax.SAXException;
77

    
78
import edu.ucsb.nceas.metacat.AccessionNumber;
79
import edu.ucsb.nceas.metacat.AccessionNumberException;
80
import edu.ucsb.nceas.metacat.DBUtil;
81
import edu.ucsb.nceas.metacat.IdentifierManager;
82
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
83
import edu.ucsb.nceas.metacat.McdbException;
84
import edu.ucsb.nceas.metacat.MetaCatServlet;
85
import edu.ucsb.nceas.metacat.MetacatHandler;
86
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlException;
87
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
88
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
89
import edu.ucsb.nceas.metacat.dataquery.MetacatDatabaseConnectionPoolFactory;
90
import edu.ucsb.nceas.metacat.properties.PropertyService;
91
import edu.ucsb.nceas.metacat.replication.ReplicationService;
92
import edu.ucsb.nceas.metacat.shared.AccessException;
93
import edu.ucsb.nceas.metacat.shared.HandlerException;
94
import edu.ucsb.nceas.metacat.util.DocumentUtil;
95
import edu.ucsb.nceas.utilities.ParseLSIDException;
96
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
97

    
98
public class SystemMetadataFactory {
99

    
100
	private static final String resourceMapPrefix = "resourceMap_";
101
	private static Logger logMetacat = Logger.getLogger(SystemMetadataFactory.class);
102
	
103
	/**
104
	 * Creates a system metadata object for insertion into metacat
105
	 * 
106
	 * @param localId
107
	 *            The local document identifier
108
	 * @param user
109
	 *            The user submitting the system metadata document
110
	 * @param groups
111
	 *            The groups the user belongs to
112
	 * 
113
	 * @return sysMeta The system metadata object created
114
	 * @throws SAXException 
115
	 * @throws HandlerException 
116
	 * @throws AccessControlException 
117
	 * @throws AccessException 
118
	 */
119
	public static SystemMetadata createSystemMetadata(String localId, boolean includeORE, boolean downloadData)
120
			throws McdbException, McdbDocNotFoundException, SQLException,
121
			IOException, AccessionNumberException, ClassNotFoundException,
122
			InsufficientKarmaException, ParseLSIDException,
123
			PropertyNotFoundException, BaseException, NoSuchAlgorithmException,
124
			JiBXException, AccessControlException, HandlerException, SAXException, AccessException {
125
		
126
		logMetacat.debug("MetacatHandler.createSystemMetadata() called.");
127
		logMetacat.debug("provided localId: " + localId);
128

    
129
		// create system metadata for the document
130
		SystemMetadata sysMeta = new SystemMetadata();
131
		sysMeta.setSerialVersion(BigInteger.valueOf(1));
132
		sysMeta.setArchived(false);
133
		AccessionNumber accNum = new AccessionNumber(localId, "NONE");
134
		int rev = Integer.valueOf(accNum.getRev());
135

    
136
		String guid = null;
137
		try {
138
			// get the guid if it exists
139
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);
140
		} catch (McdbDocNotFoundException dnfe) {
141
			// otherwise create the mapping
142
			logMetacat.debug("There was a problem getting the guid from "
143
							+ "the given localId (docid and revision). The error message was: "
144
							+ dnfe.getMessage());
145
			logMetacat.debug("No guid in the identifier table.  adding it for " + localId);
146
			IdentifierManager.getInstance().createMapping(localId, localId);
147
			logMetacat.debug("Mapping created for " + localId);
148
			logMetacat.debug("accessionNumber: " + accNum);
149
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);
150
		}
151
		Identifier identifier = new Identifier();
152
		identifier.setValue(guid);
153

    
154
		// set the id
155
		sysMeta.setIdentifier(identifier);
156

    
157
		// get the data or metadata object
158
		InputStream inputStream;
159
		try {
160
			inputStream = MetacatHandler.read(localId);
161
		} catch (ParseLSIDException ple) {
162
			logMetacat.debug("There was a problem parsing the LSID from "
163
					+ localId + ". The error message was: " + ple.getMessage());
164
			throw ple;
165

    
166
		} catch (PropertyNotFoundException pnfe) {
167
			logMetacat.debug("There was a problem finding a property. "
168
					+ "The error message was: " + pnfe.getMessage());
169
			throw pnfe;
170

    
171
		} catch (McdbException me) {
172
			logMetacat.debug("There was a Metacat problem. "
173
					+ "The error message was: " + me.getMessage());
174
			throw me;
175

    
176
		} catch (SQLException sqle) {
177
			logMetacat.debug("There was a SQL problem. "
178
					+ "The error message was: " + sqle.getMessage());
179
			throw sqle;
180

    
181
		} catch (ClassNotFoundException cnfe) {
182
			logMetacat.debug("There was a problem finding a class. "
183
					+ "The error message was: " + cnfe.getMessage());
184
			throw cnfe;
185

    
186
		} catch (IOException ioe) {
187
			logMetacat.debug("There was an I/O exception. "
188
					+ "The error message was: " + ioe.getMessage());
189
			throw ioe;
190

    
191
		} // end try()
192

    
193
		// get additional docinfo
194
		Hashtable<String, String> docInfo = ReplicationService.getDocumentInfoMap(localId);
195
		// set the default object format
196
		String doctype = docInfo.get("doctype");
197
		ObjectFormatIdentifier fmtid = null;
198

    
199
		// set the object format, fall back to defaults
200
		try {
201
			fmtid = ObjectFormatCache.getInstance().getFormat(doctype).getFormatId();
202
		} catch (NotFound nfe) {
203

    
204
			try {
205
				// format is not registered, use default
206
				if (doctype.trim().equals("BIN")) {
207
					fmtid = ObjectFormatCache.getInstance().getFormat(
208
							"application/octet-stream").getFormatId();
209

    
210
				} else {
211
					fmtid = ObjectFormatCache.getInstance().getFormat(
212
							"text/plain").getFormatId();
213
				}
214

    
215
			} catch (NotFound nf) {
216
				logMetacat.error("There was a problem getting the default format "
217
								+ "from the ObjectFormatCache: "
218
								+ nf.getMessage());
219
				throw nf;
220
			}
221

    
222
		}
223

    
224
		sysMeta.setFormatId(fmtid);
225
		logMetacat.debug("The ObjectFormat for " + localId + " is " + fmtid.getValue());
226

    
227
		// create the checksum
228
		inputStream = MetacatHandler.read(localId);
229
		String algorithm = "MD5";
230
		Checksum checksum = ChecksumUtil.checksum(inputStream, algorithm);
231
		sysMeta.setChecksum(checksum);
232
		
233
		// set the size
234
		inputStream = MetacatHandler.read(localId);
235
		String sizeStr = new Long(sizeOfStream(inputStream)).toString();
236
		sysMeta.setSize(new BigInteger(sizeStr));
237
		
238
		// submitter
239
		Subject submitter = new Subject();
240
		submitter.setValue(docInfo.get("user_updated"));
241
		sysMeta.setSubmitter(submitter);
242
		
243
		// rights holder
244
		Subject owner = new Subject();
245
		owner.setValue(docInfo.get("user_owner"));
246
		sysMeta.setRightsHolder(owner);
247

    
248
		// dates
249
		String createdDateString = docInfo.get("date_created");
250
		String updatedDateString = docInfo.get("date_updated");
251
		Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
252
		Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);  
253
		sysMeta.setDateUploaded(createdDate);
254
		sysMeta.setDateSysMetadataModified(updatedDate);
255
		
256
		// set the revision history
257
		String docidWithoutRev = accNum.getDocid();
258
		Identifier obsoletedBy = null;
259
		Identifier obsoletes = null;
260
		Vector<Integer> revisions = DBUtil.getRevListFromRevisionTable(docidWithoutRev);
261
		// ensure this ordering since processing depends on it
262
		Collections.sort(revisions);
263
		for (int existingRev: revisions) {
264
			// use the docid+rev as the guid
265
			String existingPid = docidWithoutRev + "." + existingRev;
266
			if (existingRev < rev) {
267
				// it's the old docid, until it's not
268
				obsoletes = new Identifier();
269
				obsoletes.setValue(existingPid);
270
			}
271
			if (existingRev > rev) {
272
				// it's the newer docid
273
				obsoletedBy = new Identifier();
274
				obsoletedBy.setValue(existingPid);
275
				// only want the version just after it
276
				break;
277
			}
278
		}
279
		// set them on our object
280
		sysMeta.setObsoletedBy(obsoletedBy);
281
		sysMeta.setObsoletes(obsoletes);
282
		
283
		// update the system metadata for the object[s] we are revising
284
		if (obsoletedBy != null) {
285
			//SystemMetadata obsoletedBySysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletedBy);
286
			SystemMetadata obsoletedBySysMeta = IdentifierManager.getInstance().getSystemMetadata(obsoletedBy.getValue());
287
			if (obsoletedBySysMeta != null) {
288
				obsoletedBySysMeta.setObsoletes(identifier);
289
				HazelcastService.getInstance().getSystemMetadataMap().put(obsoletedBy, obsoletedBySysMeta);
290
			}
291
		}
292
		if (obsoletes != null) {
293
			//SystemMetadata obsoletesSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletes);
294
			SystemMetadata obsoletesSysMeta = IdentifierManager.getInstance().getSystemMetadata(obsoletes.getValue());
295
			if (obsoletesSysMeta != null) {
296
				obsoletesSysMeta.setObsoletedBy(identifier);
297
				obsoletesSysMeta.setArchived(true);
298
				HazelcastService.getInstance().getSystemMetadataMap().put(obsoletes, obsoletesSysMeta);
299
			}
300
		}
301
		
302
		// look up the access control policy we have in metacat
303
		AccessPolicy accessPolicy = IdentifierManager.getInstance().getAccessPolicy(guid);
304
		sysMeta.setAccessPolicy(accessPolicy);
305
		
306
		// authoritative node
307
		NodeReference nr = new NodeReference();
308
		nr.setValue(PropertyService.getProperty("dataone.memberNodeId"));
309
		sysMeta.setOriginMemberNode(nr);
310
		sysMeta.setAuthoritativeMemberNode(nr);
311
		
312
		// further parse EML documents to get data object format,
313
		// describes and describedBy information
314
		if (fmtid == ObjectFormatCache.getInstance().getFormat(
315
				"eml://ecoinformatics.org/eml-2.0.0").getFormatId()
316
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
317
						"eml://ecoinformatics.org/eml-2.0.1").getFormatId()
318
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
319
						"eml://ecoinformatics.org/eml-2.1.0").getFormatId()
320
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
321
						"eml://ecoinformatics.org/eml-2.1.1").getFormatId()) {
322

    
323
			try {
324
				inputStream = MetacatHandler.read(localId);
325
				DatabaseConnectionPoolInterface connectionPool = 
326
					MetacatDatabaseConnectionPoolFactory.getDatabaseConnectionPoolInterface();
327
				DataManager dataManager = 
328
					DataManager.getInstance(connectionPool, connectionPool.getDBAdapterName());
329
				DataPackage dataPackage = dataManager.parseMetadata(inputStream);
330

    
331
				// iterate through the data objects in the EML doc and add sysmeta
332
				logMetacat.debug("In createSystemMetadata() the number of data "
333
								+ "entities is: "
334
								+ dataPackage.getEntityNumber());
335

    
336
				// for generating the ORE map
337
	            Map<Identifier, List<Identifier>> idMap = new HashMap<Identifier, List<Identifier>>();
338
	            List<Identifier> dataIds = new ArrayList<Identifier>();
339
				
340
				// iterate through data objects described by the EML
341
	            if (dataPackage.getEntityList() != null) {
342
					for (int j = 0; j < dataPackage.getEntityList().length; j++) {
343
	
344
						String dataDocUrl = dataPackage.getEntityList()[j].getURL();
345
						String dataDocMimeType = dataPackage.getEntityList()[j].getDataFormat();
346
						// default to binary
347
						if (dataDocMimeType == null) {
348
							dataDocMimeType = ObjectFormatCache.getInstance()
349
									.getFormat("application/octet-stream")
350
									.getFormatId().getValue();
351
						}
352

    
353
						// process the data
354
						boolean remoteData = false;
355
						String dataDocLocalId = null;
356
						Identifier dataGuid = new Identifier();
357

    
358
						// handle ecogrid, or downloadable data
359
						String ecogridPrefix = "ecogrid://knb/";
360
						if (dataDocUrl.trim().startsWith(ecogridPrefix)) {
361
							dataDocLocalId = dataDocUrl.substring(dataDocUrl.indexOf(ecogridPrefix) + ecogridPrefix.length());
362
						} else {
363
							// should we try downloading the remote data?
364
							if (downloadData) {
365
								InputStream dataObject = null;
366
								try {
367
									// download the data from the URL
368
									URL dataURL = new URL(dataDocUrl);
369
									URLConnection dataConnection = dataURL.openConnection();
370
									
371
									// default is to download the data
372
									dataObject = dataConnection.getInputStream();
373

    
374
									String detectedContentType = dataConnection.getContentType();
375
									logMetacat.info("Detected content type: " + detectedContentType);
376

    
377
									if (detectedContentType != null) {
378
										// seems to be HTML from the remote location
379
										if (detectedContentType.contains("html")) {
380
											// if we are not expecting it, we skip it
381
											if (!dataDocMimeType.contains("html")) {
382
												// set to null so we don't download it
383
												dataObject = null;
384
												logMetacat.warn("Skipping remote resource, unexpected HTML content type at: " + dataDocUrl);
385
											}
386
										}
387
										
388
										// TODO: any other special processing (csv, images, etc)?
389
									} else {
390
										// if we don't know what it is, should we skip it?
391
										dataObject = null;
392
										logMetacat.warn("Skipping remote resource, unknown content type at: " + dataDocUrl);
393
									}
394
									
395
								} catch (Exception e) {
396
									// error with the download
397
									logMetacat.warn("Error downloading remote data. " + e.getMessage());
398
								}
399
								
400
								if (dataObject != null) {
401
									// create the local version of it
402
									dataDocLocalId = DocumentUtil.generateDocumentId(1);
403
									IdentifierManager.getInstance().createMapping(dataDocLocalId, dataDocLocalId);
404
									dataGuid.setValue(dataDocLocalId);
405
									
406
									// save it locally
407
									Session session = new Session();
408
									session.setSubject(submitter);
409
									MockHttpServletRequest request = new MockHttpServletRequest(null, null, null);
410
									MNodeService.getInstance(request).insertDataObject(dataObject, dataGuid, session);
411
									
412
									remoteData = true;
413
								}
414
							}
415
							
416
						}
417
						
418
						logMetacat.debug("Data local ID: " + dataDocLocalId);
419
						logMetacat.debug("Data URL     : " + dataDocUrl);
420
						logMetacat.debug("Data mime    : " + dataDocMimeType);
421
						
422
						// now we have a local id for the data
423
						if (dataDocLocalId != null) {
424
							
425
							// look up the guid for the data
426
							String dataDocid = DocumentUtil.getSmartDocId(dataDocLocalId);
427
							int dataRev = DocumentUtil.getRevisionFromAccessionNumber(dataDocLocalId);
428
	
429
							// check if data system metadata exists already
430
							SystemMetadata dataSysMeta = null;
431
							String dataGuidString = null;
432
							try {
433
								// look for the identifier
434
								dataGuidString = IdentifierManager.getInstance().getGUID(dataDocid, dataRev);
435
								// set it
436
								dataGuid.setValue(dataGuidString);
437
								// look up the system metadata
438
								try {
439
									dataSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(dataGuid);
440
								} catch (Exception e) {
441
									// probably not in the system
442
									dataSysMeta = null;
443
								}
444
								//dataSysMeta = IdentifierManager.getInstance().getSystemMetadata(dataGuidString);
445
							} catch (McdbDocNotFoundException nf) {
446
								// we didn't find it
447
								dataSysMeta = null;
448
							}
449
								
450
							// we'll have to generate it	
451
							if (dataSysMeta == null) {
452
								// System metadata for data doesn't exist yet, so create it
453
								logMetacat.debug("There was not an existing system metadata document for " + dataDocLocalId);
454
								try {
455
									logMetacat.debug("Creating a system metadata " + "document for " + dataDocLocalId);
456
									dataSysMeta = createSystemMetadata(dataDocLocalId, includeORE, false);
457
	
458
									// now look it up again
459
									dataGuidString = IdentifierManager.getInstance().getGUID(dataDocid, dataRev);
460
	
461
									// set the guid
462
									dataGuid.setValue(dataGuidString);
463
	
464
									// set object format
465
									logMetacat.debug("Updating system metadata for "
466
													+ dataGuid.getValue() + " to "
467
													+ dataDocMimeType);
468
									try {
469
										ObjectFormatIdentifier fmt = 
470
											ObjectFormatCache.getInstance().getFormat(dataDocMimeType).getFormatId();
471
										dataSysMeta.setFormatId(fmt);
472
									} catch (NotFound nfe) {
473
										logMetacat.debug("Couldn't find format identifier for: "
474
														+ dataDocMimeType
475
														+ ". Setting it to application/octet-stream.");
476
										ObjectFormatIdentifier newFmtid = new ObjectFormatIdentifier();
477
										newFmtid.setValue("application/octet-stream");
478
									}
479
									
480
									// inherit access rules from metadata, if we don't have our own
481
									if (remoteData) {
482
										dataSysMeta.setAccessPolicy(sysMeta.getAccessPolicy());
483
										// TODO: use access rules defined in EML, per data file
484
									}
485
									
486
									// update the values
487
									HazelcastService.getInstance().getSystemMetadataMap().put(dataSysMeta.getIdentifier(), dataSysMeta);
488
									
489
	
490
								} catch (McdbDocNotFoundException mdnf) {
491
									mdnf.printStackTrace();
492
									throw mdnf;
493
								} catch (NumberFormatException nfe) {
494
									nfe.printStackTrace();
495
									throw nfe;
496
								} catch (AccessionNumberException ane) {
497
									ane.printStackTrace();
498
									throw ane;
499
								} catch (SQLException sqle) {
500
									sqle.printStackTrace();
501
									throw sqle;
502
								} catch (NoSuchAlgorithmException nsae) {
503
									nsae.printStackTrace();
504
									throw nsae;
505
								} catch (IOException ioe) {
506
									ioe.printStackTrace();
507
									throw ioe;
508
								} catch (PropertyNotFoundException pnfe) {
509
									pnfe.printStackTrace();
510
									throw pnfe;
511
								} catch (BaseException be) {
512
									be.printStackTrace();
513
									throw be;
514
								}	
515
								
516
							}
517
							
518
							// part of the ORE package
519
							dataIds.add(dataGuid);
520
	
521
						} // end if (EML package)
522
	
523
					} // end for (data entities)
524
					
525
	            } // data entities not null
526
	            
527
				// ORE map
528
				if (includeORE) {
529
					// can we generate them?
530
			        if (!dataIds.isEmpty()) {
531
			        	// it doesn't exist in the system?
532
			        	if (!oreExistsFor(sysMeta.getIdentifier())) {
533
			        	
534
				            // generate the ORE map for this datapackage
535
				            Identifier resourceMapId = new Identifier();
536
				            // want to be able to run this over and over again for now
537
				            resourceMapId.setValue(resourceMapPrefix + sysMeta.getIdentifier().getValue());
538
				            idMap.put(sysMeta.getIdentifier(), dataIds);
539
				            ResourceMap rm = ResourceMapFactory.getInstance().createResourceMap(resourceMapId, idMap);
540
				            String resourceMapXML = ResourceMapFactory.getInstance().serializeResourceMap(rm);
541
				            // copy most of the same system metadata as the packaging metadata
542
				            SystemMetadata resourceMapSysMeta = new SystemMetadata();
543
				            BeanUtils.copyProperties(resourceMapSysMeta, sysMeta);
544
				            resourceMapSysMeta.setIdentifier(resourceMapId);
545
				            Checksum oreChecksum = ChecksumUtil.checksum(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING), "MD5");
546
							resourceMapSysMeta.setChecksum(oreChecksum);
547
				            ObjectFormatIdentifier formatId = ObjectFormatCache.getInstance().getFormat("http://www.openarchives.org/ore/terms").getFormatId();
548
							resourceMapSysMeta.setFormatId(formatId);
549
							resourceMapSysMeta.setSize(BigInteger.valueOf(sizeOfStream(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING))));
550
							
551
							// set the revision graph
552
							resourceMapSysMeta.setObsoletes(null);
553
							resourceMapSysMeta.setObsoletedBy(null);
554
							// look up the resource map that this one obsoletes
555
							if (sysMeta.getObsoletes() != null) {
556
								Identifier resourceMapObsoletes = new Identifier();
557
								resourceMapObsoletes.setValue(resourceMapPrefix + sysMeta.getObsoletes().getValue());
558
								resourceMapSysMeta.setObsoletes(resourceMapObsoletes);
559
								SystemMetadata resourceMapObsoletesSystemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(resourceMapObsoletes);
560
								if (resourceMapObsoletesSystemMetadata != null) {
561
									resourceMapObsoletesSystemMetadata.setObsoletedBy(resourceMapId);
562
									HazelcastService.getInstance().getSystemMetadataMap().put(resourceMapObsoletes, resourceMapObsoletesSystemMetadata);
563
								}
564
							}
565
							// look up the resource map that this one is obsoletedBy
566
							if (sysMeta.getObsoletedBy() != null) {
567
								Identifier resourceMapObsoletedBy = new Identifier();
568
								resourceMapObsoletedBy.setValue(resourceMapPrefix + sysMeta.getObsoletedBy().getValue());
569
								resourceMapSysMeta.setObsoletedBy(resourceMapObsoletedBy);
570
								SystemMetadata resourceMapObsoletedBySystemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(resourceMapObsoletedBy);
571
								if (resourceMapObsoletedBySystemMetadata != null) {
572
									resourceMapObsoletedBySystemMetadata.setObsoletes(resourceMapId);
573
									HazelcastService.getInstance().getSystemMetadataMap().put(resourceMapObsoletedBy, resourceMapObsoletedBySystemMetadata);
574
								}
575
							}
576
				            
577
							// save it locally, if it doesn't already exist
578
							if (!IdentifierManager.getInstance().identifierExists(resourceMapId.getValue())) {
579
								Session session = new Session();
580
								session.setSubject(submitter);
581
								MockHttpServletRequest request = new MockHttpServletRequest(null, null, null);
582
								MNodeService.getInstance(request).insertDataObject(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING), resourceMapId, session);
583
								MNodeService.getInstance(request).insertSystemMetadata(resourceMapSysMeta);
584
								logMetacat.info("Inserted ORE package: " + resourceMapId.getValue());
585
							}
586
			        	}
587
			        }
588
				}
589

    
590
			} catch (ParserConfigurationException pce) {
591
				logMetacat.debug("There was a problem parsing the EML document. "
592
								+ "The error message was: " + pce.getMessage());
593

    
594
			} catch (SAXException saxe) {
595
				logMetacat.debug("There was a problem traversing the EML document. "
596
								+ "The error message was: " + saxe.getMessage());
597

    
598
			} catch (XPathExpressionException xpee) {
599
				logMetacat.debug("There was a problem searching the EML document. "
600
								+ "The error message was: " + xpee.getMessage());
601
			} catch (Exception e) {
602
				logMetacat.debug("There was a problem creating System Metadata. "
603
								+ "The error message was: " + e.getMessage());
604
				e.printStackTrace();
605
			} // end try()
606

    
607
		} // end if()
608

    
609
		return sysMeta;
610
	}
611
	
612
	/**
613
     * Generate SystemMetadata for any object in the object store that does
614
     * not already have it.  SystemMetadata documents themselves, are, of course,
615
     * exempt.  This is a utility method for migration of existing object 
616
     * stores to DataONE where SystemMetadata is required for all objects.
617
     * @param idList
618
     * @param includeOre
619
     * @param downloadData
620
     * @throws ServiceFailure
621
     * @throws McdbDocNotFoundException
622
     * @throws PropertyNotFoundException
623
     * @throws InvalidToken
624
     * @throws NotAuthorized
625
     * @throws NotFound
626
     * @throws NotImplemented
627
     * @throws InvalidRequest
628
     * @throws NoSuchAlgorithmException
629
     * @throws AccessionNumberException
630
     * @throws SQLException
631
     */
632
    public static void generateSystemMetadata(List<String> idList, boolean includeOre, boolean downloadData) 
633
    throws ServiceFailure, McdbDocNotFoundException, PropertyNotFoundException, InvalidToken, NotAuthorized, 
634
    NotFound, NotImplemented, InvalidRequest, NoSuchAlgorithmException, AccessionNumberException, SQLException 
635
    {
636
        
637
        for (String localId : idList) { 
638
            //for each id, add a system metadata doc
639
        	try {
640
        		logMetacat.debug("generating system metadata for " + localId);
641
        		generateSystemMetadata(localId, includeOre, downloadData);
642
        	} catch (Exception e) {
643
        		logMetacat.error("Error generating system metadata for: " + localId, e);
644
			}
645
        }
646
        logMetacat.info("done generating system metadata");
647
    }
648
    
649

    
650
    /**
651
     * Generate SystemMetadata for a particular object with identifier localId.
652
     * This is a utility method for migration of existing objects 
653
     * to DataONE where SystemMetadata is required for all objects.
654
     * @param localId
655
     * @param includeOre
656
     * @param downloadData
657
     * @throws ServiceFailure
658
     * @throws McdbDocNotFoundException
659
     * @throws PropertyNotFoundException
660
     * @throws InvalidToken
661
     * @throws NotAuthorized
662
     * @throws NotFound
663
     * @throws NotImplemented
664
     * @throws InvalidRequest
665
     * @throws NoSuchAlgorithmException
666
     * @throws AccessionNumberException
667
     * @throws SQLException
668
     * @throws InvalidSystemMetadata
669
     */
670
    protected static void generateSystemMetadata(String localId, boolean includeOre, boolean downloadData) 
671
    throws ServiceFailure, McdbDocNotFoundException, PropertyNotFoundException, InvalidToken, NotAuthorized,
672
    NotFound, NotImplemented, InvalidRequest, NoSuchAlgorithmException, AccessionNumberException, SQLException, InvalidSystemMetadata 
673
    {
674
    	logMetacat.debug("generateSystemMetadata() called.");
675
    	logMetacat.debug("Creating SystemMetadata for localId " + localId);
676
        SystemMetadata sm = null;
677

    
678
        //generate required system metadata fields from the document
679
        try {
680
        	sm = SystemMetadataFactory.createSystemMetadata(localId, includeOre, downloadData);
681
        } catch (Exception e1) {
682
        	e1.printStackTrace();
683
        	ServiceFailure sf = new ServiceFailure("00","Exception in generateSystemMetadata: " +
684
        			e1.getMessage());
685
        	sf.setStackTrace(e1.getStackTrace());
686
        	throw sf;
687
        }
688
        
689
        //insert the systemmetadata object or just update it as needed
690
        boolean exists = IdentifierManager.getInstance().systemMetadataExists(sm.getIdentifier().getValue());
691
        if (!exists) {
692
        	IdentifierManager.getInstance().createSystemMetadata(sm);
693
        	logMetacat.info("Generated SystemMetadata for " + localId);
694
        } else {
695
        	IdentifierManager.getInstance().updateSystemMetadata(sm);
696
        	logMetacat.info("Updated SystemMetadata for " + localId);
697
        }
698
    }
699
	
700
	/**
701
	 * Determines if we already have registered an ORE map for this package
702
	 * @param guid of the EML/packaging object
703
	 * @return true if there is an ORE map for the given package
704
	 */
705
	private static boolean oreExistsFor(Identifier guid) {
706
		// TODO: implement call to CN.search()
707
		return false;
708
	}
709

    
710
	/**
711
	 * Find the size (in bytes) of a stream. Note: This needs to refactored out
712
	 * of MetacatHandler and into a utility when stream i/o in Metacat is
713
	 * evaluated.
714
	 * 
715
	 * @param is The InputStream of bytes
716
	 * 
717
	 * @return size The size in bytes of the input stream as a long
718
	 * 
719
	 * @throws IOException
720
	 */
721
	private static long sizeOfStream(InputStream is) throws IOException {
722

    
723
		long size = 0;
724
		byte[] b = new byte[1024];
725
		int numread = is.read(b, 0, 1024);
726
		while (numread != -1) {
727
			size += numread;
728
			numread = is.read(b, 0, 1024);
729
		}
730
		return size;
731

    
732
	}
733
}
(5-5/5)