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.replication.ForceReplicationSystemMetadataHandler;
70

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

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

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

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

    
155
		return true;
156
	}
157

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

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

    
215
		return true;
216
	}
217

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

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

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

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

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

    
397
		throw new NotImplemented("4281", "search not implemented");
398

    
399
	}
400

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

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

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

    
473
		return ObjectFormatService.getInstance().listFormats();
474
	}
475

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

    
488
		throw new NotImplemented("4800", "listNodes not implemented");
489
	}
490

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

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

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

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

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

    
588
		throw new NotImplemented("4191", "reserveIdentifier not implemented");
589

    
590
	}
591

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

    
641
		return pid;
642
	}
643

    
644
}
(1-1/8)