Project

General

Profile

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

    
24
package edu.ucsb.nceas.metacat.dataone;
25

    
26
import java.io.InputStream;
27
import java.math.BigInteger;
28
import java.util.Date;
29
import java.util.List;
30

    
31
import org.apache.log4j.Logger;
32
import org.dataone.service.cn.CNAuthorization;
33
import org.dataone.service.cn.CNCore;
34
import org.dataone.service.cn.CNRead;
35
import org.dataone.service.cn.CNReplication;
36
import org.dataone.service.exceptions.IdentifierNotUnique;
37
import org.dataone.service.exceptions.InsufficientResources;
38
import org.dataone.service.exceptions.InvalidRequest;
39
import org.dataone.service.exceptions.InvalidSystemMetadata;
40
import org.dataone.service.exceptions.InvalidToken;
41
import org.dataone.service.exceptions.NotAuthorized;
42
import org.dataone.service.exceptions.NotFound;
43
import org.dataone.service.exceptions.NotImplemented;
44
import org.dataone.service.exceptions.ServiceFailure;
45
import org.dataone.service.exceptions.UnsupportedType;
46
import org.dataone.service.types.Checksum;
47
import org.dataone.service.types.Identifier;
48
import org.dataone.service.types.Node;
49
import org.dataone.service.types.NodeList;
50
import org.dataone.service.types.NodeReference;
51
import org.dataone.service.types.ObjectFormat;
52
import org.dataone.service.types.ObjectFormatIdentifier;
53
import org.dataone.service.types.ObjectFormatList;
54
import org.dataone.service.types.ObjectList;
55
import org.dataone.service.types.ObjectLocation;
56
import org.dataone.service.types.ObjectLocationList;
57
import org.dataone.service.types.Permission;
58
import org.dataone.service.types.QueryType;
59
import org.dataone.service.types.Replica;
60
import org.dataone.service.types.ReplicationPolicy;
61
import org.dataone.service.types.ReplicationStatus;
62
import org.dataone.service.types.Session;
63
import org.dataone.service.types.Subject;
64
import org.dataone.service.types.SystemMetadata;
65

    
66
import edu.ucsb.nceas.metacat.EventLog;
67
import edu.ucsb.nceas.metacat.IdentifierManager;
68
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
69
import edu.ucsb.nceas.metacat.properties.PropertyService;
70
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
71
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
72

    
73
/**
74
 * Represents Metacat's implementation of the DataONE Coordinating Node 
75
 * service API. Methods implement the various CN* interfaces, and methods common
76
 * to both Member Node and Coordinating Node interfaces are found in the
77
 * D1NodeService super class.
78
 *
79
 */
80
public class CNodeService extends D1NodeService implements CNAuthorization,
81
    CNCore, CNRead, CNReplication {
82

    
83
	/* the instance of the CNodeService object */
84
  private static CNodeService instance = null;
85
  
86
  /* the logger instance */
87
  private Logger logMetacat = null;
88

    
89
  /**
90
   * singleton accessor
91
   */
92
  public static CNodeService getInstance() { 
93
    if (instance == null) {
94
    
95
      instance = new CNodeService();
96
      
97
    }
98
  
99
    return instance;
100
  }
101
  
102
  /**
103
   * Constructor, private for singleton access
104
   */
105
  private CNodeService() {
106
  	super();
107
    logMetacat = Logger.getLogger(CNodeService.class);
108
        
109
  }
110
    
111
	/**
112
	 * Set the replication policy for an object given the object identifier
113
	 * 
114
	 * @param session - the Session object containing the credentials for the Subject
115
	 * @param pid - the object identifier for the given object
116
	 * @param policy - the replication policy to be applied
117
	 * 
118
	 * @return true or false
119
	 * 
120
	 * @throws NotImplemented
121
	 * @throws NotAuthorized
122
	 * @throws ServiceFailure
123
	 * @throws InvalidRequest
124
	 * 
125
	 */
126
	@Override
127
	public boolean setReplicationPolicy(Session session, Identifier pid,
128
	  ReplicationPolicy policy) 
129
	  throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, InvalidRequest, InvalidToken {
130

    
131
		// get the subject
132
		Subject subject = session.getSubject();
133
		// get the system metadata
134
		String guid = pid.getValue();
135
		
136
		// are we allowed to do this?
137
		if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
138
			throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + " not allowed by " + subject.getValue() + " on " + guid);	
139
		}
140
		
141
		SystemMetadata systemMetadata = null;
142
		try {
143
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
144
		} catch (McdbDocNotFoundException e) {
145
			throw new NotFound("4884", "No record found for: " + guid);
146
		}
147
				
148
		// set the new policy
149
		systemMetadata.setReplicationPolicy(policy);
150
		
151
		// update the metadata
152
		try {
153
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
154
		} catch (McdbDocNotFoundException e) {
155
			throw new ServiceFailure("4882", e.getMessage());
156
		}
157

    
158
		return true;
159
	}
160

    
161
	/**
162
	 * Set the replication status for an object given the object identifier
163
	 * 
164
	 * @param session - the Session object containing the credentials for the Subject
165
	 * @param pid - the object identifier for the given object
166
	 * @param status - the replication status to be applied
167
	 * 
168
	 * @return true or false
169
	 * 
170
	 * @throws NotImplemented
171
	 * @throws NotAuthorized
172
	 * @throws ServiceFailure
173
	 * @throws InvalidRequest
174
	 * @throws InvalidToken
175
	 * @throws NotFound
176
	 * 
177
	 */
178
	@Override
179
	public boolean setReplicationStatus(Session session, Identifier pid,
180
	  ReplicationStatus status) 
181
	  throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
182
	  InvalidRequest, NotFound {
183

    
184
		// get the subject
185
		Subject subject = session.getSubject();
186
		// get the system metadata
187
		String guid = pid.getValue();
188
		
189
		// are we allowed to do this?
190
		if (!isAuthorized(session, pid, Permission.WRITE)) {
191
			throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + subject.getValue() + " on " + guid);	
192
		}
193
		
194
		SystemMetadata systemMetadata = null;
195
		try {
196
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
197
		} catch (McdbDocNotFoundException e) {
198
			throw new NotFound("4740", "No record found for: " + guid);
199
		}
200
				
201
		// set the status for each replica
202
		// TODO: should this method select a certain replica?
203
		List<Replica> replicas = systemMetadata.getReplicaList();
204
		for (Replica replica: replicas) {
205
			replica.setReplicationStatus(status);
206
		}
207
		
208
		// [re]set the list -- redundant?
209
		systemMetadata.setReplicaList(replicas);
210
		
211
		// update the metadata
212
		try {
213
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
214
		} catch (McdbDocNotFoundException e) {
215
			throw new ServiceFailure("4700", e.getMessage());
216
		}
217

    
218
		return true;
219
	}
220

    
221
	/**
222
	 * Test that the specified relationship between pidOfSubject and pidOfObject exists
223
	 * 
224
	 * @param session - the Session object containing the credentials for the Subject
225
	 * @param node - the node information for the given node be modified
226
	 * 
227
	 * @return true if the relationship exists
228
	 * 
229
	 * @throws InvalidToken
230
	 * @throws ServiceFailure
231
	 * @throws NotAuthorized
232
	 * @throws NotFound
233
	 * @throws InvalidRequest
234
	 * @throws NotImplemented
235
	 */
236
	@Override
237
	public boolean assertRelation(Session session, Identifier pidOfSubject, 
238
		String relationship, Identifier pidOfObject) 
239
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
240
	  InvalidRequest, NotImplemented {
241
		
242
		
243
		// get the system metadata
244
		String guid1 = pidOfSubject.getValue();
245
		// are we allowed to do this?
246
		if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
247
			throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);	
248
		}
249
		
250
		SystemMetadata systemMetadata = null;
251
		try {
252
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
253
		} catch (McdbDocNotFoundException e) {
254
			throw new NotFound("4884", "No record found for: " + guid1);
255
		}
256
				
257
		// check relationships
258
		// TODO: make this more strongly typed?
259
		if (relationship.equalsIgnoreCase("describes")) {
260
			return systemMetadata.getDescribeList().contains(pidOfObject);
261
		}
262
		if (relationship.equalsIgnoreCase("describedBy")) {
263
			return systemMetadata.getDescribedByList().contains(pidOfObject);
264
		}
265
		if (relationship.equalsIgnoreCase("derivedFrom")) {
266
			return systemMetadata.getDerivedFromList().contains(pidOfObject);
267
		}
268
		if (relationship.equalsIgnoreCase("obsoletes")) {
269
			return systemMetadata.getObsoleteList().contains(pidOfObject);
270
		}
271
		if (relationship.equalsIgnoreCase("obsoletedBy")) {
272
			return systemMetadata.getObsoletedByList().contains(pidOfObject);
273
		}
274

    
275
		return false;
276
	}
277
	
278
	/**
279
	 * Return the checksum of the object given the identifier 
280
	 * 
281
	 * @param session - the Session object containing the credentials for the Subject
282
	 * @param pid - the object identifier for the given object
283
	 * 
284
	 * @return checksum - the checksum of the object
285
	 * 
286
	 * @throws InvalidToken
287
	 * @throws ServiceFailure
288
	 * @throws NotAuthorized
289
	 * @throws NotFound
290
	 * @throws InvalidRequest
291
	 * @throws NotImplemented
292
	 */
293
	@Override
294
	public Checksum getChecksum(Session session, Identifier pid)
295
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
296
	  InvalidRequest, NotImplemented {
297
		
298
		if (!isAuthorized(session, pid, Permission.READ)) {
299
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
300
		}
301
		SystemMetadata systemMetadata = null;
302
		try {
303
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
304
		} catch (McdbDocNotFoundException e) {
305
			throw new NotFound("1420", "No record found for: " + pid.getValue());
306
		}
307
		Checksum checksum = systemMetadata.getChecksum();
308
		
309
		return checksum;
310
	}
311

    
312
	/**
313
	 * Resolve the location of a given object
314
	 * 
315
	 * @param session - the Session object containing the credentials for the Subject
316
	 * @param pid - the object identifier for the given object
317
	 * 
318
	 * @return objectLocationList - the list of nodes known to contain the object
319
	 * 
320
	 * @throws InvalidRequest
321
	 * @throws InvalidToken
322
	 * @throws ServiceFailure
323
	 * @throws NotAuthorized
324
	 * @throws NotFound
325
	 * @throws NotImplemented
326
	 */
327
	@Override
328
	public ObjectLocationList resolve(Session session, Identifier pid)
329
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
330
	  NotFound, NotImplemented {
331

    
332
		ObjectLocationList objLocList = new ObjectLocationList();
333
		objLocList.setIdentifier(pid);
334
		
335
		// get the system metadata
336
		String guid = pid.getValue();
337
		
338
		// are we allowed to do this?
339
		if (!isAuthorized(session, pid, Permission.READ)) {
340
			throw new NotAuthorized("4720", Permission.READ + " not allowed on " + guid);	
341
		}
342
		
343
		SystemMetadata systemMetadata = null;
344
		try {
345
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
346
		} catch (McdbDocNotFoundException e) {
347
			throw new NotFound("4740", "No record found for: " + guid);
348
		}
349
			
350
		NodeList nodes = listNodes();
351
		
352
		// get the locations
353
		List<Replica> replicas = systemMetadata.getReplicaList();
354
		for (Replica replica: replicas) {
355
			NodeReference nodeReference = replica.getReplicaMemberNode();
356
			ObjectLocation objectLocation = new ObjectLocation();
357
			objectLocation.setNodeIdentifier(nodeReference);
358
			// get the node
359
			Node node = null;
360
			for (Node n: nodes.getNodeList()) {
361
				if (n.getIdentifier().equals(nodeReference)) {
362
					node = n;
363
					break;
364
				}
365
			}
366
			// set base URL
367
			objectLocation.setBaseURL(node.getBaseURL());
368
			// set URL
369
			objectLocation.setUrl(node.getBaseURL() + "/object/" + pid.getValue());
370
			// TODO set meaningful preference
371
			objectLocation.setPreference(BigInteger.ONE);
372
			objLocList.addObjectLocation(objectLocation);
373
		}
374
		
375
		return objLocList;
376
	}
377

    
378
	/**
379
	 * Search the metadata catalog for identifiers that match the criteria
380
	 * 
381
	 * @param session - the Session object containing the credentials for the Subject
382
	 * @param queryType - An identifier for the type of query expression 
383
	 *                    provided in the query
384
	 * @param query -  The criteria for matching the characteristics of the 
385
	 *                 metadata objects of interest
386
	 * 
387
	 * @return objectList - the list of objects matching the criteria
388
	 * 
389
	 * @throws InvalidToken
390
	 * @throws ServiceFailure
391
	 * @throws NotAuthorized
392
	 * @throws InvalidRequest
393
	 * @throws NotImplemented
394
	 */
395
	@Override
396
	public ObjectList search(Session session, QueryType queryType, String query)
397
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
398
	  NotImplemented {
399

    
400
		throw new NotImplemented("4281", "search not implemented");
401

    
402
	}
403

    
404
	/**
405
	 * Used internally within a Coordinating Node to add a new object 
406
	 * to the object store
407
	 * 
408
	 * @param session - the Session object containing the credentials for the Subject
409
	 * @param pid - The object identifier to be created
410
	 * @param object - the object bytes
411
	 * @param sysmeta - the system metadata that describes the object  
412
	 * 
413
	 * @return pid - the object identifier created
414
	 * 
415
	 * @throws InvalidToken
416
	 * @throws ServiceFailure
417
	 * @throws NotAuthorized
418
	 * @throws IdentifierNotUnique
419
	 * @throws UnsupportedType
420
	 * @throws InsufficientResources
421
	 * @throws InvalidSystemMetadata
422
	 * @throws NotImplemented
423
	 * @throws InvalidRequest
424
	 */
425
	@Override
426
	public Identifier create(Session session, Identifier pid, InputStream object,
427
	  SystemMetadata sysmeta) 
428
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
429
	  UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
430
	  NotImplemented, InvalidRequest {
431

    
432
		throw new NotImplemented("4890", "create not implemented");
433
	}
434
	
435
	/**
436
	 * Returns the object format registered in the DataONE Object Format 
437
	 * Vocabulary for the given format identifier
438
	 * 
439
	 * @param fmtid - the identifier of the format requested
440
	 * 
441
	 * @return objectFormat - the object format requested
442
	 * 
443
	 * @throws InvalidRequest
444
	 * @throws ServiceFailure
445
	 * @throws NotFound
446
	 * @throws InsufficientResources
447
	 * @throws NotImplemented
448
	 */
449
	@Override
450
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
451
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
452
	  NotImplemented {
453
		 
454
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
455
			
456
	}
457

    
458
	/**
459
   * Returns a list of all object formats registered in the DataONE Object 
460
   * Format Vocabulary
461
 	 * 
462
	 * @return objectFormatList - The list of object formats registered in 
463
	 *                            the DataONE Object Format Vocabulary
464
	 * 
465
	 * @throws InvalidRequest
466
	 * @throws ServiceFailure
467
	 * @throws NotImplemented
468
	 * @throws NotFound
469
	 * @throws InsufficientResources
470
	 */
471
	@Override
472
	public ObjectFormatList listFormats() 
473
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
474
	  NotImplemented {
475

    
476
		return ObjectFormatService.getInstance().listFormats();
477
	}
478

    
479
	/**
480
   * Returns a list of nodes that have been registered with the DataONE infrastructure
481
 	 * 
482
	 * @return nodeList - List of nodes from the registry
483
	 * 
484
	 * @throws ServiceFailure
485
	 * @throws NotImplemented
486
	 */
487
	@Override
488
	public NodeList listNodes() 
489
	  throws NotImplemented, ServiceFailure {
490

    
491
		throw new NotImplemented("4800", "listNodes not implemented");
492
	}
493

    
494
	/**
495
   * Provides a mechanism for adding system metadata independently of its 
496
   * associated object, such as when adding system metadata for data objects.
497
 	 * 
498
	 * @param session - the Session object containing the credentials for the Subject
499
	 * @param pid - The identifier of the object to register the system metadata against
500
	 * @param sysmeta - The system metadata to be registered
501
	 * 
502
	 * @return true if the registration succeeds
503
	 * 
504
	 * @throws NotImplemented
505
	 * @throws NotAuthorized
506
	 * @throws ServiceFailure
507
	 * @throws InvalidRequest
508
	 * @throws InvalidSystemMetadata
509
	 */
510
	@Override
511
	public boolean registerSystemMetadata(Session session, Identifier guid,
512
	  SystemMetadata sysmeta) 
513
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
514
	  InvalidSystemMetadata {
515

    
516
		// TODO: control who can call this?
517
	      if (session == null) {
518
	          //TODO: many of the thrown exceptions do not use the correct error codes
519
	          //check these against the docs and correct them
520
	          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
521
	                  "  If you are not logged in, please do so and retry the request.");
522
	      }
523
	      
524
	      // verify that guid == SystemMetadata.getIdentifier()
525
	      logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
526
	      if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
527
	          throw new InvalidRequest("4863", 
528
	              "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
529
	              sysmeta.getIdentifier().getValue() + ").");
530
	      }
531

    
532
	      logMetacat.debug("Checking if identifier exists...");
533
	      // Check that the identifier does not already exist
534
	      IdentifierManager im = IdentifierManager.getInstance();
535
	      if (im.identifierExists(guid.getValue())) {
536
	          throw new InvalidRequest("4863", 
537
	              "GUID is already in use by an existing object.");
538
	      }
539

    
540
	      // insert the system metadata into the object store
541
	      logMetacat.debug("Starting to insert SystemMetadata...");
542
	      sysmeta.setDateSysMetadataModified(new Date());
543
	      try {
544
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
545
			    IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
546
			    // force replication of this record
547
			    ForceReplicationSystemMetadataHandler forceReplication = 
548
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
549
		    } catch (Exception e) {
550
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
551
		    }
552
	      
553
	      logMetacat.debug("Returning from registerSystemMetadata");
554
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
555
	      return true;
556
	}
557

    
558
	/**
559
   * Given an optional scope and format, reserves and returns an identifier 
560
   * within that scope and format that is unique and will not be 
561
   * used by any other sessions. 
562
 	 * 
563
	 * @param session - the Session object containing the credentials for the Subject
564
	 * @param pid - The identifier of the object to register the system metadata against
565
	 * @param scope - An optional string to be used to qualify the scope of 
566
	 *                the identifier namespace, which is applied differently 
567
	 *                depending on the format requested. If scope is not 
568
	 *                supplied, a default scope will be used.
569
	 * @param format - The optional name of the identifier format to be used, 
570
	 * 								 drawn from a DataONE-specific vocabulary of identifier 
571
	 *                 format names, including several common syntaxes such 
572
	 *                 as DOI, LSID, UUID, and LSRN, among others. If the 
573
	 *                 format is not supplied by the caller, the CN service 
574
	 *                 will use a default identifier format, which may change 
575
	 *                 over time.
576
	 * 
577
	 * @return true if the registration succeeds
578
	 * 
579
	 * @throws InvalidToken
580
	 * @throws ServiceFailure
581
	 * @throws NotAuthorized
582
	 * @throws IdentifierNotUnique
583
	 * @throws NotImplemented
584
	 */
585
	@Override
586
	public Identifier reserveIdentifier(Session session, Identifier pid,
587
	  String scope, String format) 
588
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
589
	  NotImplemented, InvalidRequest {
590

    
591
		throw new NotImplemented("4191", "reserveIdentifier not implemented");
592

    
593
	}
594

    
595
	/**
596
   * Changes ownership (RightsHolder) of the specified object to the 
597
   * subject specified by userId
598
 	 * 
599
	 * @param session - the Session object containing the credentials for the Subject
600
	 * @param pid - Identifier of the object to be modified
601
	 * @param userId - The subject that will be taking ownership of the specified object.
602
	 *
603
	 * @return pid - the identifier of the modified object
604
	 * 
605
	 * @throws ServiceFailure
606
	 * @throws InvalidToken
607
	 * @throws NotFound
608
	 * @throws NotAuthorized
609
	 * @throws NotImplemented
610
	 * @throws InvalidRequest
611
	 */	
612
	@Override
613
	public Identifier setOwner(Session session, Identifier pid, Subject userId)
614
	  throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
615
	  NotImplemented, InvalidRequest {
616
		
617
		// get the subject
618
		Subject subject = session.getSubject();
619
		// get the system metadata
620
		String guid = pid.getValue();
621
		
622
		// are we allowed to do this?
623
		if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
624
			throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);	
625
		}
626
		
627
		SystemMetadata systemMetadata = null;
628
		try {
629
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
630
		} catch (McdbDocNotFoundException e) {
631
			throw new NotFound("4460", "No record found for: " + guid);
632
		}
633
				
634
		// set the new rights holder
635
		systemMetadata.setRightsHolder(userId);
636
		
637
		// update the metadata
638
		try {
639
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
640
		} catch (McdbDocNotFoundException e) {
641
			throw new ServiceFailure("4490", e.getMessage());
642
		}
643

    
644
		return pid;
645
	}
646

    
647
}
(1-1/8)