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-20 15:45:06 -0800 (Fri, 20 Jan 2012) $'
10
 * '$Revision: 6931 $'
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
		// get the data or metadata object
137
		InputStream inputStream;
138
		try {
139
			inputStream = MetacatHandler.read(localId);
140
		} catch (ParseLSIDException ple) {
141
			logMetacat.debug("There was a problem parsing the LSID from "
142
					+ localId + ". The error message was: " + ple.getMessage());
143
			throw ple;
144

    
145
		} catch (PropertyNotFoundException pnfe) {
146
			logMetacat.debug("There was a problem finding a property. "
147
					+ "The error message was: " + pnfe.getMessage());
148
			throw pnfe;
149

    
150
		} catch (McdbException me) {
151
			logMetacat.debug("There was a Metacat problem. "
152
					+ "The error message was: " + me.getMessage());
153
			throw me;
154

    
155
		} catch (SQLException sqle) {
156
			logMetacat.debug("There was a SQL problem. "
157
					+ "The error message was: " + sqle.getMessage());
158
			throw sqle;
159

    
160
		} catch (ClassNotFoundException cnfe) {
161
			logMetacat.debug("There was a problem finding a class. "
162
					+ "The error message was: " + cnfe.getMessage());
163
			throw cnfe;
164

    
165
		} catch (IOException ioe) {
166
			logMetacat.debug("There was an I/O exception. "
167
					+ "The error message was: " + ioe.getMessage());
168
			throw ioe;
169

    
170
		}
171
		
172
		// get/make the guid
173
		String guid = null;
174
		try {
175
			// get the guid if it exists
176
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);
177
		} catch (McdbDocNotFoundException dnfe) {
178
			// otherwise create the mapping
179
			logMetacat.debug("There was a problem getting the guid from "
180
							+ "the given localId (docid and revision). The error message was: "
181
							+ dnfe.getMessage());
182
			logMetacat.debug("No guid in the identifier table.  adding it for " + localId);
183
			IdentifierManager.getInstance().createMapping(localId, localId);
184
			logMetacat.debug("Mapping created for " + localId);
185
			logMetacat.debug("accessionNumber: " + accNum);
186
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);
187
		}
188
		Identifier identifier = new Identifier();
189
		identifier.setValue(guid);
190

    
191
		// set the id
192
		sysMeta.setIdentifier(identifier);
193

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

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

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

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

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

    
223
		}
224

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

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

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

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

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

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

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

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

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

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

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

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

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

    
608
		} // end if()
609

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

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

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

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

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

    
733
	}
734
}
(5-5/5)