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.util.Date;
28
import java.util.List;
29

    
30
import org.apache.log4j.Logger;
31
import org.dataone.service.cn.CNAuthorization;
32
import org.dataone.service.cn.CNCore;
33
import org.dataone.service.cn.CNRead;
34
import org.dataone.service.cn.CNRegister;
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, CNRegister, 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
	 * Add capabilities to the node description
220
	 * 
221
	 * @param session - the Session object containing the credentials for the Subject
222
	 * @param nodeid - the node identifier for the given node be modified
223
	 * @param node - the node information to be added
224
	 * 
225
	 * @return true or false
226
	 * 
227
	 * @throws NotImplemented
228
	 * @throws NotAuthorized
229
	 * @throws ServiceFailure
230
	 * @throws InvalidRequest
231
	 * @throws NotFound
232
	 */
233
	@Override
234
	public boolean addNodeCapabilities(Session session, NodeReference nodeid, Node node)
235
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
236
	  NotFound {
237

    
238
		throw new NotImplemented("4820", "addNodeCapabilities not implemented");
239
	}
240

    
241
	/**
242
	 * Register a Coordinating Node
243
	 * 
244
	 * @param session - the Session object containing the credentials for the Subject
245
	 * @param node - the node information for the given node be modified
246
	 * 
247
	 * @return nodeid - the identifier of the node to be registered
248
	 * 
249
	 * @throws NotImplemented
250
	 * @throws NotAuthorized
251
	 * @throws ServiceFailure
252
	 * @throws InvalidRequest
253
	 * @throws IdentifierNotUnique
254
	 */
255
	@Override
256
	public Identifier register(Session session, Node node) 
257
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
258
	  IdentifierNotUnique {
259

    
260
		return null;
261
	}
262

    
263
	/**
264
	 * Test that the specified relationship between pidOfSubject and pidOfObject exists
265
	 * 
266
	 * @param session - the Session object containing the credentials for the Subject
267
	 * @param node - the node information for the given node be modified
268
	 * 
269
	 * @return true if the relationship exists
270
	 * 
271
	 * @throws InvalidToken
272
	 * @throws ServiceFailure
273
	 * @throws NotAuthorized
274
	 * @throws NotFound
275
	 * @throws InvalidRequest
276
	 * @throws NotImplemented
277
	 */
278
	@Override
279
	public boolean assertRelation(Session session, Identifier pidOfSubject, 
280
		String relationship, Identifier pidOfObject) 
281
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
282
	  InvalidRequest, NotImplemented {
283
		
284
		
285
		// get the system metadata
286
		String guid1 = pidOfSubject.getValue();
287
		// are we allowed to do this?
288
		if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
289
			throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);	
290
		}
291
		
292
		SystemMetadata systemMetadata = null;
293
		try {
294
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
295
		} catch (McdbDocNotFoundException e) {
296
			throw new NotFound("4884", "No record found for: " + guid1);
297
		}
298
				
299
		// check relationships
300
		// TODO: make this more strongly typed?
301
		if (relationship.equalsIgnoreCase("describes")) {
302
			return systemMetadata.getDescribeList().contains(pidOfObject);
303
		}
304
		if (relationship.equalsIgnoreCase("describedBy")) {
305
			return systemMetadata.getDescribedByList().contains(pidOfObject);
306
		}
307
		if (relationship.equalsIgnoreCase("derivedFrom")) {
308
			return systemMetadata.getDerivedFromList().contains(pidOfObject);
309
		}
310
		if (relationship.equalsIgnoreCase("obsoletes")) {
311
			return systemMetadata.getObsoleteList().contains(pidOfObject);
312
		}
313
		if (relationship.equalsIgnoreCase("obsoletedBy")) {
314
			return systemMetadata.getObsoletedByList().contains(pidOfObject);
315
		}
316

    
317
		return false;
318
	}
319
	
320
	/**
321
	 * Return the checksum of the object given the identifier 
322
	 * 
323
	 * @param session - the Session object containing the credentials for the Subject
324
	 * @param pid - the object identifier for the given object
325
	 * 
326
	 * @return checksum - the checksum of the object
327
	 * 
328
	 * @throws InvalidToken
329
	 * @throws ServiceFailure
330
	 * @throws NotAuthorized
331
	 * @throws NotFound
332
	 * @throws InvalidRequest
333
	 * @throws NotImplemented
334
	 */
335
	@Override
336
	public Checksum getChecksum(Session session, Identifier pid)
337
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
338
	  InvalidRequest, NotImplemented {
339
		
340
		if (!isAuthorized(session, pid, Permission.READ)) {
341
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
342
		}
343
		SystemMetadata systemMetadata = null;
344
		try {
345
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
346
		} catch (McdbDocNotFoundException e) {
347
			throw new NotFound("1420", "No record found for: " + pid.getValue());
348
		}
349
		Checksum checksum = systemMetadata.getChecksum();
350
		
351
		return checksum;
352
	}
353

    
354
	/**
355
	 * Resolve the location of a given object
356
	 * 
357
	 * @param session - the Session object containing the credentials for the Subject
358
	 * @param pid - the object identifier for the given object
359
	 * 
360
	 * @return objectLocationList - the list of nodes known to contain the object
361
	 * 
362
	 * @throws InvalidRequest
363
	 * @throws InvalidToken
364
	 * @throws ServiceFailure
365
	 * @throws NotAuthorized
366
	 * @throws NotFound
367
	 * @throws NotImplemented
368
	 */
369
	@Override
370
	public ObjectLocationList resolve(Session session, Identifier pid)
371
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
372
	  NotFound, NotImplemented {
373

    
374
		ObjectLocationList objLocList = new ObjectLocationList();
375
		objLocList.setIdentifier(pid);
376
		
377
		// get the system metadata
378
		String guid = pid.getValue();
379
		
380
		// are we allowed to do this?
381
		if (!isAuthorized(session, pid, Permission.READ)) {
382
			throw new NotAuthorized("4720", Permission.READ + " not allowed on " + guid);	
383
		}
384
		
385
		SystemMetadata systemMetadata = null;
386
		try {
387
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
388
		} catch (McdbDocNotFoundException e) {
389
			throw new NotFound("4740", "No record found for: " + guid);
390
		}
391
			
392
		NodeList nodes = listNodes();
393
		
394
		// get the locations
395
		List<Replica> replicas = systemMetadata.getReplicaList();
396
		for (Replica replica: replicas) {
397
			NodeReference nodeReference = replica.getReplicaMemberNode();
398
			ObjectLocation objectLocation = new ObjectLocation();
399
			objectLocation.setNodeIdentifier(nodeReference);
400
			// get the node
401
			Node node = null;
402
			for (Node n: nodes.getNodeList()) {
403
				if (n.getIdentifier().equals(nodeReference)) {
404
					node = n;
405
					break;
406
				}
407
			}
408
			objectLocation.setBaseURL(node.getBaseURL());
409
			// TODO set URL
410
			// TODO set preference
411
			objLocList.addObjectLocation(objectLocation);
412
		}
413
		
414
		return objLocList;
415
	}
416

    
417
	/**
418
	 * Search the metadata catalog for identifiers that match the criteria
419
	 * 
420
	 * @param session - the Session object containing the credentials for the Subject
421
	 * @param queryType - An identifier for the type of query expression 
422
	 *                    provided in the query
423
	 * @param query -  The criteria for matching the characteristics of the 
424
	 *                 metadata objects of interest
425
	 * 
426
	 * @return objectList - the list of objects matching the criteria
427
	 * 
428
	 * @throws InvalidToken
429
	 * @throws ServiceFailure
430
	 * @throws NotAuthorized
431
	 * @throws InvalidRequest
432
	 * @throws NotImplemented
433
	 */
434
	@Override
435
	public ObjectList search(Session session, QueryType queryType, String query)
436
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
437
	  NotImplemented {
438

    
439
		throw new NotImplemented("4281", "search not implemented");
440

    
441
	}
442

    
443
	/**
444
	 * Used internally within a Coordinating Node to add a new object 
445
	 * to the object store
446
	 * 
447
	 * @param session - the Session object containing the credentials for the Subject
448
	 * @param pid - The object identifier to be created
449
	 * @param object - the object bytes
450
	 * @param sysmeta - the system metadata that describes the object  
451
	 * 
452
	 * @return pid - the object identifier created
453
	 * 
454
	 * @throws InvalidToken
455
	 * @throws ServiceFailure
456
	 * @throws NotAuthorized
457
	 * @throws IdentifierNotUnique
458
	 * @throws UnsupportedType
459
	 * @throws InsufficientResources
460
	 * @throws InvalidSystemMetadata
461
	 * @throws NotImplemented
462
	 * @throws InvalidRequest
463
	 */
464
	@Override
465
	public Identifier create(Session session, Identifier pid, InputStream object,
466
	  SystemMetadata sysmeta) 
467
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
468
	  UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
469
	  NotImplemented, InvalidRequest {
470

    
471
		throw new NotImplemented("4890", "create not implemented");
472
	}
473
	
474
	/**
475
	 * Returns the object format registered in the DataONE Object Format 
476
	 * Vocabulary for the given format identifier
477
	 * 
478
	 * @param fmtid - the identifier of the format requested
479
	 * 
480
	 * @return objectFormat - the object format requested
481
	 * 
482
	 * @throws InvalidRequest
483
	 * @throws ServiceFailure
484
	 * @throws NotFound
485
	 * @throws InsufficientResources
486
	 * @throws NotImplemented
487
	 */
488
	@Override
489
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
490
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
491
	  NotImplemented {
492
		 
493
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
494
			
495
	}
496

    
497
	/**
498
   * Returns a list of all object formats registered in the DataONE Object 
499
   * Format Vocabulary
500
 	 * 
501
	 * @return objectFormatList - The list of object formats registered in 
502
	 *                            the DataONE Object Format Vocabulary
503
	 * 
504
	 * @throws InvalidRequest
505
	 * @throws ServiceFailure
506
	 * @throws NotImplemented
507
	 * @throws NotFound
508
	 * @throws InsufficientResources
509
	 */
510
	@Override
511
	public ObjectFormatList listFormats() 
512
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
513
	  NotImplemented {
514

    
515
		return ObjectFormatService.getInstance().listFormats();
516
	}
517

    
518
	/**
519
   * Returns a list of nodes that have been registered with the DataONE infrastructure
520
 	 * 
521
	 * @return nodeList - List of nodes from the registry
522
	 * 
523
	 * @throws ServiceFailure
524
	 * @throws NotImplemented
525
	 */
526
	@Override
527
	public NodeList listNodes() 
528
	  throws NotImplemented, ServiceFailure {
529

    
530
		throw new NotImplemented("4800", "listNodes not implemented");
531
	}
532

    
533
	/**
534
   * Provides a mechanism for adding system metadata independently of its 
535
   * associated object, such as when adding system metadata for data objects.
536
 	 * 
537
	 * @param session - the Session object containing the credentials for the Subject
538
	 * @param pid - The identifier of the object to register the system metadata against
539
	 * @param sysmeta - The system metadata to be registered
540
	 * 
541
	 * @return true if the registration succeeds
542
	 * 
543
	 * @throws NotImplemented
544
	 * @throws NotAuthorized
545
	 * @throws ServiceFailure
546
	 * @throws InvalidRequest
547
	 * @throws InvalidSystemMetadata
548
	 */
549
	@Override
550
	public boolean registerSystemMetadata(Session session, Identifier guid,
551
	  SystemMetadata sysmeta) 
552
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
553
	  InvalidSystemMetadata {
554

    
555
		// TODO: control who can call this?
556
	      if (session == null) {
557
	          //TODO: many of the thrown exceptions do not use the correct error codes
558
	          //check these against the docs and correct them
559
	          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
560
	                  "  If you are not logged in, please do so and retry the request.");
561
	      }
562
	      
563
	      // verify that guid == SystemMetadata.getIdentifier()
564
	      logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
565
	      if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
566
	          throw new InvalidRequest("4863", 
567
	              "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
568
	              sysmeta.getIdentifier().getValue() + ").");
569
	      }
570

    
571
	      logMetacat.debug("Checking if identifier exists...");
572
	      // Check that the identifier does not already exist
573
	      IdentifierManager im = IdentifierManager.getInstance();
574
	      if (im.identifierExists(guid.getValue())) {
575
	          throw new InvalidRequest("4863", 
576
	              "GUID is already in use by an existing object.");
577
	      }
578

    
579
	      // insert the system metadata into the object store
580
	      logMetacat.debug("Starting to insert SystemMetadata...");
581
	      sysmeta.setDateSysMetadataModified(new Date());
582
	      try {
583
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
584
			    IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
585
			    // force replication of this record
586
			    ForceReplicationSystemMetadataHandler forceReplication = 
587
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
588
		    } catch (Exception e) {
589
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
590
		    }
591
	      
592
	      logMetacat.debug("Returning from registerSystemMetadata");
593
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
594
	      return true;
595
	}
596

    
597
	/**
598
   * Given an optional scope and format, reserves and returns an identifier 
599
   * within that scope and format that is unique and will not be 
600
   * used by any other sessions. 
601
 	 * 
602
	 * @param session - the Session object containing the credentials for the Subject
603
	 * @param pid - The identifier of the object to register the system metadata against
604
	 * @param scope - An optional string to be used to qualify the scope of 
605
	 *                the identifier namespace, which is applied differently 
606
	 *                depending on the format requested. If scope is not 
607
	 *                supplied, a default scope will be used.
608
	 * @param format - The optional name of the identifier format to be used, 
609
	 * 								 drawn from a DataONE-specific vocabulary of identifier 
610
	 *                 format names, including several common syntaxes such 
611
	 *                 as DOI, LSID, UUID, and LSRN, among others. If the 
612
	 *                 format is not supplied by the caller, the CN service 
613
	 *                 will use a default identifier format, which may change 
614
	 *                 over time.
615
	 * 
616
	 * @return true if the registration succeeds
617
	 * 
618
	 * @throws InvalidToken
619
	 * @throws ServiceFailure
620
	 * @throws NotAuthorized
621
	 * @throws IdentifierNotUnique
622
	 * @throws NotImplemented
623
	 */
624
	@Override
625
	public Identifier reserveIdentifier(Session session, Identifier pid,
626
	  String scope, String format) 
627
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
628
	  NotImplemented, InvalidRequest {
629

    
630
		throw new NotImplemented("4191", "reserveIdentifier not implemented");
631

    
632
	}
633

    
634
	/**
635
   * Changes ownership (RightsHolder) of the specified object to the 
636
   * subject specified by userId
637
 	 * 
638
	 * @param session - the Session object containing the credentials for the Subject
639
	 * @param pid - Identifier of the object to be modified
640
	 * @param userId - The subject that will be taking ownership of the specified object.
641
	 *
642
	 * @return pid - the identifier of the modified object
643
	 * 
644
	 * @throws ServiceFailure
645
	 * @throws InvalidToken
646
	 * @throws NotFound
647
	 * @throws NotAuthorized
648
	 * @throws NotImplemented
649
	 * @throws InvalidRequest
650
	 */	
651
	@Override
652
	public Identifier setOwner(Session session, Identifier pid, Subject userId)
653
	  throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
654
	  NotImplemented, InvalidRequest {
655
		
656
		// get the subject
657
		Subject subject = session.getSubject();
658
		// get the system metadata
659
		String guid = pid.getValue();
660
		
661
		// are we allowed to do this?
662
		if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
663
			throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);	
664
		}
665
		
666
		SystemMetadata systemMetadata = null;
667
		try {
668
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
669
		} catch (McdbDocNotFoundException e) {
670
			throw new NotFound("4460", "No record found for: " + guid);
671
		}
672
				
673
		// set the new rights holder
674
		systemMetadata.setRightsHolder(userId);
675
		
676
		// update the metadata
677
		try {
678
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
679
		} catch (McdbDocNotFoundException e) {
680
			throw new ServiceFailure("4490", e.getMessage());
681
		}
682

    
683
		return pid;
684
	}
685

    
686
}
(1-1/8)