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-02-21 13:11:27 -0800 (Tue, 21 Feb 2012) $'
10
 * '$Revision: 7022 $'
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.File;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.math.BigInteger;
32
import java.net.URL;
33
import java.net.URLConnection;
34
import java.security.NoSuchAlgorithmException;
35
import java.sql.SQLException;
36
import java.util.ArrayList;
37
import java.util.Collections;
38
import java.util.Date;
39
import java.util.HashMap;
40
import java.util.Hashtable;
41
import java.util.List;
42
import java.util.Map;
43
import java.util.Vector;
44

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

    
48
import org.apache.commons.beanutils.BeanUtils;
49
import org.apache.commons.io.IOUtils;
50
import org.apache.log4j.Logger;
51
import org.apache.wicket.protocol.http.MockHttpServletRequest;
52
import org.dataone.client.ObjectFormatCache;
53
import org.dataone.eml.DataoneEMLParser;
54
import org.dataone.eml.EMLDocument;
55
import org.dataone.eml.EMLDocument.DistributionMetadata;
56
import org.dataone.ore.ResourceMapFactory;
57
import org.dataone.service.exceptions.BaseException;
58
import org.dataone.service.exceptions.NotFound;
59
import org.dataone.service.types.v1.AccessPolicy;
60
import org.dataone.service.types.v1.Checksum;
61
import org.dataone.service.types.v1.Identifier;
62
import org.dataone.service.types.v1.NodeReference;
63
import org.dataone.service.types.v1.ObjectFormatIdentifier;
64
import org.dataone.service.types.v1.ReplicationPolicy;
65
import org.dataone.service.types.v1.Session;
66
import org.dataone.service.types.v1.Subject;
67
import org.dataone.service.types.v1.SystemMetadata;
68
import org.dataone.service.types.v1.util.ChecksumUtil;
69
import org.dataone.service.util.DateTimeMarshaller;
70
import org.dspace.foresite.ResourceMap;
71
import org.jibx.runtime.JiBXException;
72
import org.xml.sax.SAXException;
73

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

    
94
public class SystemMetadataFactory {
95

    
96
	private static final String resourceMapPrefix = "resourceMap_";
97
	private static Logger logMetacat = Logger.getLogger(SystemMetadataFactory.class);
98
	/**
99
	 * use this flag if you want to update any existing system metadata values with generated content
100
	 */
101
	private static boolean updateExisting = true;
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("createSystemMetadata() called for localId " + localId);
127

    
128
		// check for system metadata
129
		SystemMetadata sysMeta = null;
130
		
131
		AccessionNumber accNum = new AccessionNumber(localId, "NONE");
132
		int rev = Integer.valueOf(accNum.getRev());
133
		
134
		// get/make the guid
135
		String guid = null;
136
		try {
137
			// get the guid if it exists
138
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);
139
		} catch (McdbDocNotFoundException dnfe) {
140
			// otherwise create the mapping
141
			logMetacat.debug("No guid found in the identifier table.  Creating mapping for " + localId);
142
			IdentifierManager.getInstance().createMapping(localId, localId);
143
			guid = IdentifierManager.getInstance().getGUID(accNum.getDocid(), rev);			
144
		}
145
		
146
		// look up existing system metadata if it exists
147
		Identifier identifier = new Identifier();
148
		identifier.setValue(guid);
149
		try {
150
			logMetacat.debug("Using hazelcast to get system metadata");
151
			sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(identifier);
152
			// TODO: if this is the case, we could return here -- what else do we gain?
153
			if (!updateExisting ) {
154
				return sysMeta;
155
			}
156
		} catch (Exception e) {
157
			logMetacat.debug("No system metadata found in hz: " + e.getMessage());
158

    
159
		}
160

    
161
		if (sysMeta == null) {
162
			// create system metadata
163
			sysMeta = new SystemMetadata();
164
			sysMeta.setIdentifier(identifier);
165
			sysMeta.setSerialVersion(BigInteger.valueOf(1));
166
			sysMeta.setArchived(false);
167
		}
168
		
169
		// get additional docinfo
170
		Hashtable<String, String> docInfo = ReplicationService.getDocumentInfoMap(localId);
171
		// set the default object format
172
		String doctype = docInfo.get("doctype");
173
		ObjectFormatIdentifier fmtid = null;
174

    
175
		// set the object format, fall back to defaults
176
		if (doctype.trim().equals("BIN")) {
177
			// we don't know much about this file (yet)
178
			fmtid = ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFormatId();
179
		} else {
180
			try {
181
				// do we know the given format?
182
				fmtid = ObjectFormatCache.getInstance().getFormat(doctype).getFormatId();
183
			} catch (NotFound nfe) {
184
				// format is not registered, use default
185
				fmtid = ObjectFormatCache.getInstance().getFormat("text/plain").getFormatId();
186
			}
187
		}
188

    
189
		sysMeta.setFormatId(fmtid);
190
		logMetacat.debug("The ObjectFormat for " + localId + " is " + fmtid.getValue());
191

    
192
		// for retrieving the actual object
193
		InputStream inputStream = null;
194
		inputStream = MetacatHandler.read(localId);
195

    
196
		// create the checksum
197
		String algorithm = "MD5";
198
		Checksum checksum = ChecksumUtil.checksum(inputStream, algorithm);
199
		sysMeta.setChecksum(checksum);
200
		
201
		// set the size from file on disk, don't read bytes again
202
		File fileOnDisk = getFileOnDisk(localId);
203
		long fileSize = 0;
204
		if (fileOnDisk.exists()) {
205
			fileSize = fileOnDisk.length();
206
		}
207
		sysMeta.setSize(BigInteger.valueOf(fileSize));
208
		
209
		// submitter
210
		Subject submitter = new Subject();
211
		submitter.setValue(docInfo.get("user_updated"));
212
		sysMeta.setSubmitter(submitter);
213
		
214
		// rights holder
215
		Subject owner = new Subject();
216
		owner.setValue(docInfo.get("user_owner"));
217
		sysMeta.setRightsHolder(owner);
218

    
219
		// dates
220
		String createdDateString = docInfo.get("date_created");
221
		String updatedDateString = docInfo.get("date_updated");
222
		Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
223
		Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);  
224
		sysMeta.setDateUploaded(createdDate);
225
		sysMeta.setDateSysMetadataModified(updatedDate);
226
		
227
		// set the revision history
228
		String docidWithoutRev = accNum.getDocid();
229
		Identifier obsoletedBy = null;
230
		Identifier obsoletes = null;
231
		Vector<Integer> revisions = DBUtil.getRevListFromRevisionTable(docidWithoutRev);
232
		// ensure this ordering since processing depends on it
233
		Collections.sort(revisions);
234
		for (int existingRev: revisions) {
235
			// use the docid+rev as the guid
236
			String existingPid = docidWithoutRev + "." + existingRev;
237
			try {
238
				existingPid = IdentifierManager.getInstance().getGUID(docidWithoutRev, existingRev);
239
			} catch (McdbDocNotFoundException mdfe) {
240
				// we'll be defaulting to the local id
241
				logMetacat.warn("could not locate guid when processing revision history for localId: " + localId);
242
			}
243
			if (existingRev < rev) {
244
				// it's the old docid, until it's not
245
				obsoletes = new Identifier();
246
				obsoletes.setValue(existingPid);
247
			}
248
			if (existingRev > rev) {
249
				// it's the newer docid
250
				obsoletedBy = new Identifier();
251
				obsoletedBy.setValue(existingPid);
252
				// only want the version just after it
253
				break;
254
			}
255
		}
256
		// set them on our object
257
		sysMeta.setObsoletedBy(obsoletedBy);
258
		sysMeta.setObsoletes(obsoletes);
259
		
260
		// update the system metadata for the object[s] we are revising
261
		if (obsoletedBy != null) {
262
			SystemMetadata obsoletedBySysMeta = null;
263
			try {
264
				//obsoletedBySysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletedBy);
265
				obsoletedBySysMeta = IdentifierManager.getInstance().getSystemMetadata(obsoletedBy.getValue());
266
			} catch (McdbDocNotFoundException e) {
267
				// ignore
268
			}
269
			if (obsoletedBySysMeta != null) {
270
				obsoletedBySysMeta.setObsoletes(identifier);
271
				HazelcastService.getInstance().getSystemMetadataMap().put(obsoletedBy, obsoletedBySysMeta);
272
			}
273
		}
274
		if (obsoletes != null) {
275
			SystemMetadata obsoletesSysMeta = null;
276
			try {
277
				//obsoletesSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(obsoletes);
278
				obsoletesSysMeta = IdentifierManager.getInstance().getSystemMetadata(obsoletes.getValue());
279
			} catch (McdbDocNotFoundException e) {
280
				// ignore
281
			}
282
			if (obsoletesSysMeta != null) {
283
				obsoletesSysMeta.setObsoletedBy(identifier);
284
				obsoletesSysMeta.setArchived(true);
285
				HazelcastService.getInstance().getSystemMetadataMap().put(obsoletes, obsoletesSysMeta);
286
			}
287
		}
288
		
289
		// look up the access control policy we have in metacat
290
		AccessPolicy accessPolicy = IdentifierManager.getInstance().getAccessPolicy(guid);
291
		sysMeta.setAccessPolicy(accessPolicy);
292
		
293
		// authoritative node
294
		NodeReference nr = new NodeReference();
295
		nr.setValue(PropertyService.getProperty("dataone.memberNodeId"));
296
		sysMeta.setOriginMemberNode(nr);
297
		sysMeta.setAuthoritativeMemberNode(nr);
298
		
299
		// Set a default replication policy
300
        ReplicationPolicy rp = getDefaultReplicationPolicy();
301
        if (rp != null) {
302
            sysMeta.setReplicationPolicy(rp);
303
        }
304
		
305
		// further parse EML documents to get data object format,
306
		// describes and describedBy information
307
		if (fmtid == ObjectFormatCache.getInstance().getFormat(
308
				"eml://ecoinformatics.org/eml-2.0.0").getFormatId()
309
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
310
						"eml://ecoinformatics.org/eml-2.0.1").getFormatId()
311
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
312
						"eml://ecoinformatics.org/eml-2.1.0").getFormatId()
313
				|| fmtid == ObjectFormatCache.getInstance().getFormat(
314
						"eml://ecoinformatics.org/eml-2.1.1").getFormatId()) {
315

    
316
			try {
317
				
318
				// get it again to parse the document
319
				logMetacat.debug("Re-reading document inputStream");
320
				inputStream = MetacatHandler.read(localId);
321
				
322
				DataoneEMLParser emlParser = DataoneEMLParser.getInstance();
323
		        EMLDocument emlDocument = emlParser.parseDocument(inputStream);
324
				
325
				// iterate through the data objects in the EML doc and add sysmeta
326
				logMetacat.debug("In createSystemMetadata() the number of data "
327
								+ "entities is: "
328
								+ emlDocument.distributionMetadata);
329

    
330
				// for generating the ORE map
331
	            Map<Identifier, List<Identifier>> idMap = new HashMap<Identifier, List<Identifier>>();
332
	            List<Identifier> dataIds = new ArrayList<Identifier>();
333
				
334
				// iterate through data objects described by the EML
335
	            if (emlDocument.distributionMetadata != null) {
336
					for (int j = 0; j < emlDocument.distributionMetadata.size(); j++) {
337
	
338
						DistributionMetadata distMetadata = emlDocument.distributionMetadata.elementAt(j);
339
				        String dataDocUrl = distMetadata.url;
340
				        String dataDocMimeType = distMetadata.mimeType;
341
						// default to binary
342
						if (dataDocMimeType == null) {
343
							dataDocMimeType = "application/octet-stream";
344
						}
345

    
346
						// process the data
347
						boolean remoteData = false;
348
						String dataDocLocalId = null;
349
						Identifier dataGuid = new Identifier();
350

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

    
367
									String detectedContentType = dataConnection.getContentType();
368
									logMetacat.info("Detected content type: " + detectedContentType);
369

    
370
									if (detectedContentType != null) {
371
										// seems to be HTML from the remote location
372
										if (detectedContentType.contains("html")) {
373
											// if we are not expecting it, we skip it
374
											if (!dataDocMimeType.contains("html")) {
375
												// set to null so we don't download it
376
												dataObject = null;
377
												logMetacat.warn("Skipping remote resource, unexpected HTML content type at: " + dataDocUrl);
378
											}
379
										}
380
										
381
									} else {
382
										// if we don't know what it is, should we skip it?
383
										dataObject = null;
384
										logMetacat.warn("Skipping remote resource, unknown content type at: " + dataDocUrl);
385
									}
386
									
387
								} catch (Exception e) {
388
									// error with the download
389
									logMetacat.warn("Error downloading remote data. " + e.getMessage());
390
								}
391
								
392
								if (dataObject != null) {
393
									// create the local version of it
394
									dataDocLocalId = DocumentUtil.generateDocumentId(1);
395
									IdentifierManager.getInstance().createMapping(dataDocLocalId, dataDocLocalId);
396
									dataGuid.setValue(dataDocLocalId);
397
									
398
									// save it locally
399
									Session session = new Session();
400
									session.setSubject(submitter);
401
									MockHttpServletRequest request = new MockHttpServletRequest(null, null, null);
402
									MNodeService.getInstance(request).insertDataObject(dataObject, dataGuid, session);
403
									
404
									remoteData = true;
405
								}
406
							}
407
							
408
						}
409
						
410
						logMetacat.debug("Data local ID: " + dataDocLocalId);
411
						logMetacat.debug("Data URL     : " + dataDocUrl);
412
						logMetacat.debug("Data mime    : " + dataDocMimeType);
413
						
414
						// now we have a local id for the data
415
						if (dataDocLocalId != null) {
416
							
417
							// look up the guid for the data
418
							String dataDocid = DocumentUtil.getSmartDocId(dataDocLocalId);
419
							int dataRev = DocumentUtil.getRevisionFromAccessionNumber(dataDocLocalId);
420
	
421
							// check if data system metadata exists already
422
							SystemMetadata dataSysMeta = null;
423
							String dataGuidString = null;
424
							try {
425
								// look for the identifier
426
								dataGuidString = IdentifierManager.getInstance().getGUID(dataDocid, dataRev);
427
								// set it
428
								dataGuid.setValue(dataGuidString);
429
								// look up the system metadata
430
								try {
431
									dataSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(dataGuid);
432
								} catch (Exception e) {
433
									// probably not in the system
434
									dataSysMeta = null;
435
								}
436
								//dataSysMeta = IdentifierManager.getInstance().getSystemMetadata(dataGuidString);
437
							} catch (McdbDocNotFoundException nf) {
438
								// we didn't find it
439
								dataSysMeta = null;
440
							}
441
								
442
							// we'll have to generate it	
443
							if (dataSysMeta == null) {
444
								// System metadata for data doesn't exist yet, so create it
445
								logMetacat.debug("No exisiting SystemMetdata found, creating for: " + dataDocLocalId);
446
								dataSysMeta = createSystemMetadata(dataDocLocalId, includeORE, false);
447

    
448
								// now look it up again
449
								dataGuidString = IdentifierManager.getInstance().getGUID(dataDocid, dataRev);
450

    
451
								// set the guid
452
								dataGuid.setValue(dataGuidString);
453
								
454
								// inherit access rules from metadata, if we don't have our own
455
								if (remoteData) {
456
									dataSysMeta.setAccessPolicy(sysMeta.getAccessPolicy());
457
									// TODO: use access rules defined in EML, per data file
458
								}
459
	
460
							}
461
							
462
							// set object format for the data file
463
							logMetacat.debug("Updating system metadata for " + dataGuid.getValue() + " to " + dataDocMimeType);
464
							ObjectFormatIdentifier fmt = null;
465
							try {
466
								fmt = ObjectFormatCache.getInstance().getFormat(dataDocMimeType).getFormatId();
467
							} catch (NotFound nfe) {
468
								logMetacat.debug("Couldn't find format identifier for: "
469
												+ dataDocMimeType
470
												+ ". Setting it to application/octet-stream.");
471
								fmt = new ObjectFormatIdentifier();
472
								fmt.setValue("application/octet-stream");
473
							}
474
							dataSysMeta.setFormatId(fmt);
475

    
476
							// update the values
477
							HazelcastService.getInstance().getSystemMetadataMap().put(dataSysMeta.getIdentifier(), dataSysMeta);
478
							
479
							// include as part of the ORE package
480
							dataIds.add(dataGuid);
481
	
482
						} // end if (EML package)
483
	
484
					} // end for (data entities)
485
					
486
	            } // data entities not null
487
	            
488
				// ORE map
489
				if (includeORE) {
490
					// can we generate them?
491
			        if (!dataIds.isEmpty()) {
492
			        	// it doesn't exist in the system?
493
			        	if (!oreExistsFor(sysMeta.getIdentifier())) {
494
			        	
495
				            // generate the ORE map for this datapackage
496
				            Identifier resourceMapId = new Identifier();
497
				            // use the local id, not the guid in case we have DOIs for them already
498
				            resourceMapId.setValue(resourceMapPrefix + localId);
499
				            idMap.put(sysMeta.getIdentifier(), dataIds);
500
				            ResourceMap rm = ResourceMapFactory.getInstance().createResourceMap(resourceMapId, idMap);
501
				            String resourceMapXML = ResourceMapFactory.getInstance().serializeResourceMap(rm);
502
				            // copy most of the same system metadata as the packaging metadata
503
				            SystemMetadata resourceMapSysMeta = new SystemMetadata();
504
				            BeanUtils.copyProperties(resourceMapSysMeta, sysMeta);
505
				            resourceMapSysMeta.setIdentifier(resourceMapId);
506
				            Checksum oreChecksum = ChecksumUtil.checksum(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING), "MD5");
507
							resourceMapSysMeta.setChecksum(oreChecksum);
508
				            ObjectFormatIdentifier formatId = ObjectFormatCache.getInstance().getFormat("http://www.openarchives.org/ore/terms").getFormatId();
509
							resourceMapSysMeta.setFormatId(formatId);
510
							resourceMapSysMeta.setSize(BigInteger.valueOf(sizeOfStream(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING))));
511
							
512
							// set the revision graph
513
							resourceMapSysMeta.setObsoletes(null);
514
							resourceMapSysMeta.setObsoletedBy(null);
515
							// look up the resource map that this one obsoletes
516
							if (sysMeta.getObsoletes() != null) {
517
								Identifier resourceMapObsoletes = new Identifier();
518
								resourceMapObsoletes.setValue(resourceMapPrefix + sysMeta.getObsoletes().getValue());
519
								resourceMapSysMeta.setObsoletes(resourceMapObsoletes);
520
								SystemMetadata resourceMapObsoletesSystemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(resourceMapObsoletes);
521
								if (resourceMapObsoletesSystemMetadata != null) {
522
									resourceMapObsoletesSystemMetadata.setObsoletedBy(resourceMapId);
523
									HazelcastService.getInstance().getSystemMetadataMap().put(resourceMapObsoletes, resourceMapObsoletesSystemMetadata);
524
								}
525
							}
526
							// look up the resource map that this one is obsoletedBy
527
							if (sysMeta.getObsoletedBy() != null) {
528
								Identifier resourceMapObsoletedBy = new Identifier();
529
								resourceMapObsoletedBy.setValue(resourceMapPrefix + sysMeta.getObsoletedBy().getValue());
530
								resourceMapSysMeta.setObsoletedBy(resourceMapObsoletedBy);
531
								SystemMetadata resourceMapObsoletedBySystemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(resourceMapObsoletedBy);
532
								if (resourceMapObsoletedBySystemMetadata != null) {
533
									resourceMapObsoletedBySystemMetadata.setObsoletes(resourceMapId);
534
									HazelcastService.getInstance().getSystemMetadataMap().put(resourceMapObsoletedBy, resourceMapObsoletedBySystemMetadata);
535
								}
536
							}
537
				            
538
							// save it locally, if it doesn't already exist
539
							if (!IdentifierManager.getInstance().identifierExists(resourceMapId.getValue())) {
540
								Session session = new Session();
541
								session.setSubject(submitter);
542
								MockHttpServletRequest request = new MockHttpServletRequest(null, null, null);
543
								MNodeService.getInstance(request).insertDataObject(IOUtils.toInputStream(resourceMapXML, MetaCatServlet.DEFAULT_ENCODING), resourceMapId, session);
544
								MNodeService.getInstance(request).insertSystemMetadata(resourceMapSysMeta);
545
								logMetacat.info("Inserted ORE package: " + resourceMapId.getValue());
546
							}
547
			        	}
548
			        }
549
				}
550

    
551
			} catch (ParserConfigurationException pce) {
552
				logMetacat.debug("There was a problem parsing the EML document. "
553
								+ "The error message was: " + pce.getMessage());
554

    
555
			} catch (SAXException saxe) {
556
				logMetacat.debug("There was a problem traversing the EML document. "
557
								+ "The error message was: " + saxe.getMessage());
558

    
559
			} catch (XPathExpressionException xpee) {
560
				logMetacat.debug("There was a problem searching the EML document. "
561
								+ "The error message was: " + xpee.getMessage());
562
			} catch (Exception e) {
563
				logMetacat.debug("There was a problem creating System Metadata. "
564
								+ "The error message was: " + e.getMessage());
565
				e.printStackTrace();
566
			} // end try()
567

    
568
		} // end if()
569

    
570
		return sysMeta;
571
	}
572

    
573
    /**
574
     * Generate SystemMetadata for any object in the object store that does
575
     * not already have it.  SystemMetadata documents themselves, are, of course,
576
     * exempt.  This is a utility method for migration of existing object 
577
     * stores to DataONE where SystemMetadata is required for all objects.
578
     * @param idList
579
     * @param includeOre
580
     * @param downloadData
581
     * @throws PropertyNotFoundException
582
     * @throws NoSuchAlgorithmException
583
     * @throws AccessionNumberException
584
     * @throws SQLException
585
	 * @throws SAXException 
586
	 * @throws HandlerException 
587
	 * @throws JiBXException 
588
	 * @throws BaseException 
589
	 * @throws ParseLSIDException 
590
	 * @throws InsufficientKarmaException 
591
	 * @throws ClassNotFoundException 
592
	 * @throws IOException 
593
	 * @throws McdbException 
594
	 * @throws AccessException 
595
	 * @throws AccessControlException 
596
     */
597
    public static void generateSystemMetadata(List<String> idList, boolean includeOre, boolean downloadData) 
598
    throws PropertyNotFoundException, NoSuchAlgorithmException, AccessionNumberException, SQLException, AccessControlException, AccessException, McdbException, IOException, ClassNotFoundException, InsufficientKarmaException, ParseLSIDException, BaseException, JiBXException, HandlerException, SAXException 
599
    {
600
        
601
        for (String localId : idList) { 
602
        	logMetacat.debug("Creating SystemMetadata for localId " + localId);
603
            SystemMetadata sm = null;
604

    
605
            //generate required system metadata fields from the document
606
        	sm = SystemMetadataFactory.createSystemMetadata(localId, includeOre, downloadData);
607
        	
608
            //insert the systemmetadata object or just update it as needed
609
            boolean exists = IdentifierManager.getInstance().systemMetadataExists(sm.getIdentifier().getValue());
610
            if (!exists) {
611
            	IdentifierManager.getInstance().insertSystemMetadata(sm);
612
            	logMetacat.info("Generated SystemMetadata for " + localId);
613
            } else {
614
            	IdentifierManager.getInstance().updateSystemMetadata(sm);
615
            	logMetacat.info("Updated SystemMetadata for " + localId);
616
            }
617
        }
618
        logMetacat.info("done generating system metadata for given list");
619
    }
620
    
621
	/**
622
	 * Determines if we already have registered an ORE map for this package
623
	 * @param guid of the EML/packaging object
624
	 * @return true if there is an ORE map for the given package
625
	 */
626
	private static boolean oreExistsFor(Identifier guid) {
627
		// TODO: implement call to CN.search()
628
		return false;
629
	}
630

    
631
	/**
632
	 * Find the size (in bytes) of a stream. Note: This needs to refactored out
633
	 * of MetacatHandler and into a utility when stream i/o in Metacat is
634
	 * evaluated.
635
	 * 
636
	 * @param is The InputStream of bytes
637
	 * 
638
	 * @return size The size in bytes of the input stream as a long
639
	 * 
640
	 * @throws IOException
641
	 */
642
	private static long sizeOfStream(InputStream is) throws IOException {
643

    
644
		long size = 0;
645
		byte[] b = new byte[1024];
646
		int numread = is.read(b, 0, 1024);
647
		while (numread != -1) {
648
			size += numread;
649
			numread = is.read(b, 0, 1024);
650
		}
651
		return size;
652

    
653
	}
654
	
655
	private static File getFileOnDisk(String docid) throws McdbException, PropertyNotFoundException {
656
		
657
		DocumentImpl doc = new DocumentImpl(docid, false);
658
		String filepath = null;
659
		String filename = null;
660

    
661
		// deal with data or metadata cases
662
		if (doc.getRootNodeID() == 0) {
663
			// this is a data file
664
			filepath = PropertyService.getProperty("application.datafilepath");
665
		} else {
666
			filepath = PropertyService.getProperty("application.documentfilepath");
667
		}
668
		// ensure it is a directory path
669
		if (!(filepath.endsWith("/"))) {
670
			filepath += "/";
671
		}
672
		filename = filepath + docid;
673
		File documentFile = new File(filename);
674
		
675
		return documentFile;
676
	}
677

    
678
	/**
679
	 * Create a default ReplicationPolicy by reading properties from metacat's configuration
680
	 * and using those defaults. If the numReplicas property is not found, malformed, or less
681
	 * than or equal to zero, no policy needs to be set, so return null.
682
	 * @return ReplicationPolicy, or null if no replication policy is needed
683
	 */
684
    private static ReplicationPolicy getDefaultReplicationPolicy() {
685
        ReplicationPolicy rp = null;
686
        int numReplicas = -1;
687
        try {
688
            numReplicas = new Integer(PropertyService.getProperty("dataone.replicationpolicy.default.numreplicas"));
689
        } catch (NumberFormatException e) {
690
            // The property is not a valid integer, so return a null policy
691
            return null;
692
        } catch (PropertyNotFoundException e) {
693
            // The property is not found, so return a null policy
694
            return null;
695
        }
696
        
697
        if (numReplicas > 0) {
698
            rp = new ReplicationPolicy();
699
            rp.setReplicationAllowed(true);
700
            rp.setNumberReplicas(numReplicas);
701
            try {
702
                String preferredNodeList = PropertyService.getProperty("dataone.replicationpolicy.default.preferredNodeList");
703
                if (preferredNodeList != null) {
704
                    List<NodeReference> pNodes = extractNodeReferences(preferredNodeList);
705
                    if (pNodes != null && !pNodes.isEmpty()) {
706
                        rp.setPreferredMemberNodeList(pNodes);
707
                    }
708
                }
709
            } catch (PropertyNotFoundException e) {
710
                // No preferred list found in properties, so just ignore it; no action needed
711
            }
712
            try {
713
                String blockedNodeList = PropertyService.getProperty("dataone.replicationpolicy.default.blockedNodeList");
714
                if (blockedNodeList != null) {
715
                    List<NodeReference> bNodes = extractNodeReferences(blockedNodeList);
716
                    if (bNodes != null && !bNodes.isEmpty()) {
717
                        rp.setBlockedMemberNodeList(bNodes);
718
                    }
719
                }
720
            } catch (PropertyNotFoundException e) {
721
                // No blocked list found in properties, so just ignore it; no action needed
722
            }
723
        }
724
        return rp;
725
    }
726

    
727
    /**
728
     * Extract a List of NodeReferences froma String listing the node identifiers where
729
     * each identifier is separated by whitespace, comma, or semicolon characters.
730
     * @param nodeString the string containing the list of nodes
731
     * @return the List of NodeReference objects parsed fromt he input string
732
     */
733
    private static List<NodeReference> extractNodeReferences(String nodeString) {
734
        List<NodeReference> nodeList = new ArrayList<NodeReference>();
735
        String[] result = nodeString.split("[,;\\s]");
736
        for (String r : result) {
737
            NodeReference noderef = new NodeReference();
738
            noderef.setValue(r);
739
            nodeList.add(noderef);
740
        }
741
        return nodeList;
742
    }
743
}
(5-5/5)