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

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

    
60
import edu.ucsb.nceas.metacat.EventLog;
61
import edu.ucsb.nceas.metacat.IdentifierManager;
62
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
63
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
64

    
65
/**
66
 * Represents Metacat's implementation of the DataONE Coordinating Node 
67
 * service API. Methods implement the various CN* interfaces, and methods common
68
 * to both Member Node and Coordinating Node interfaces are found in the
69
 * D1NodeService super class.
70
 *
71
 */
72
public class CNodeService extends D1NodeService implements CNAuthorization,
73
    CNCore, CNRead, CNReplication {
74

    
75
	/* the instance of the CNodeService object */
76
  private static CNodeService instance = null;
77
  
78
  /* the logger instance */
79
  private Logger logMetacat = null;
80

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

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

    
150
		return true;
151
	}
152

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

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

    
210
		return true;
211
	}
212

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

    
292
		return false;
293
	}
294
	
295
	/**
296
	 * Return the checksum of the object given the identifier 
297
	 * 
298
	 * @param session - the Session object containing the credentials for the Subject
299
	 * @param pid - the object identifier for the given object
300
	 * 
301
	 * @return checksum - the checksum of the object
302
	 * 
303
	 * @throws InvalidToken
304
	 * @throws ServiceFailure
305
	 * @throws NotAuthorized
306
	 * @throws NotFound
307
	 * @throws InvalidRequest
308
	 * @throws NotImplemented
309
	 */
310
	@Override
311
	public Checksum getChecksum(Session session, Identifier pid)
312
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
313
	  InvalidRequest, NotImplemented {
314
		
315
		if (!isAuthorized(session, pid, Permission.READ)) {
316
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
317
		}
318
		SystemMetadata systemMetadata = null;
319
		try {
320
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
321
		} catch (McdbDocNotFoundException e) {
322
			throw new NotFound("1420", "No record found for: " + pid.getValue());
323
		}
324
		Checksum checksum = systemMetadata.getChecksum();
325
		
326
		return checksum;
327
	}
328

    
329
	/**
330
	 * Resolve the location of a given object
331
	 * 
332
	 * @param session - the Session object containing the credentials for the Subject
333
	 * @param pid - the object identifier for the given object
334
	 * 
335
	 * @return objectLocationList - the list of nodes known to contain the object
336
	 * 
337
	 * @throws InvalidRequest
338
	 * @throws InvalidToken
339
	 * @throws ServiceFailure
340
	 * @throws NotAuthorized
341
	 * @throws NotFound
342
	 * @throws NotImplemented
343
	 */
344
	@Override
345
	public ObjectLocationList resolve(Session session, Identifier pid)
346
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
347
	  NotFound, NotImplemented {
348

    
349
		throw new NotImplemented("4131", "resolve not implemented");
350

    
351
	}
352

    
353
	/**
354
	 * Search the metadata catalog for identifiers that match the criteria
355
	 * 
356
	 * @param session - the Session object containing the credentials for the Subject
357
	 * @param queryType - An identifier for the type of query expression 
358
	 *                    provided in the query
359
	 * @param query -  The criteria for matching the characteristics of the 
360
	 *                 metadata objects of interest
361
	 * 
362
	 * @return objectList - the list of objects matching the criteria
363
	 * 
364
	 * @throws InvalidToken
365
	 * @throws ServiceFailure
366
	 * @throws NotAuthorized
367
	 * @throws InvalidRequest
368
	 * @throws NotImplemented
369
	 */
370
	@Override
371
	public ObjectList search(Session session, QueryType queryType, String query)
372
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
373
	  NotImplemented {
374

    
375
		ObjectList objectList = null;
376
		try {
377
		    objectList = 
378
		    	IdentifierManager.getInstance().querySystemMetadata(
379
		    			null, //startTime, 
380
		    			null, //endTime,
381
		    			null, //objectFormat, 
382
		    			false, //replicaStatus, 
383
		    			0, //start, 
384
		    			-1 //count
385
		    			);
386
		    
387
		} catch (Exception e) {
388
			throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
389
		}
390

    
391
	    return objectList;
392
	    
393
		//throw new NotImplemented("4281", "search not implemented");
394
		
395
		// the code block below is from an older implementation
396
		
397
		/*  This block commented out because of the EcoGrid circular dependency.
398
         *  For now, query will not be supported until the circularity can be
399
         *  resolved, probably by moving the ecogrid query syntax transformers
400
         *  directly into the Metacat codebase.  MBJ 2010-02-03
401
         
402
        try {
403
            EcogridQueryParser parser = new EcogridQueryParser(request
404
                    .getReader());
405
            parser.parseXML();
406
            QueryType queryType = parser.getEcogridQuery();
407
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
408
                new EcogridJavaToMetacatJavaQueryTransformer();
409
            QuerySpecification metacatQuery = queryTransformer
410
                    .transform(queryType);
411

    
412
            DBQuery metacat = new DBQuery();
413

    
414
            boolean useXMLIndex = (new Boolean(PropertyService
415
                    .getProperty("database.usexmlindex"))).booleanValue();
416
            String xmlquery = "query"; // we don't care the query in resultset,
417
            // the query can be anything
418
            PrintWriter out = null; // we don't want metacat result, so set out null
419

    
420
            // parameter: queryspecification, user, group, usingIndexOrNot
421
            StringBuffer result = metacat.createResultDocument(xmlquery,
422
                    metacatQuery, out, username, groupNames, useXMLIndex);
423

    
424
            // create result set transfer       
425
            String saxparser = PropertyService.getProperty("xml.saxparser");
426
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
427
                    new StringReader(result.toString()), saxparser, queryType
428
                            .getNamespace().get_value());
429
            ResultsetType records = metacatResultsetParser.getEcogridResult();
430

    
431
            System.out
432
                    .println(EcogridResultsetTransformer.toXMLString(records));
433
            response.setContentType("text/xml");
434
            out = response.getWriter();
435
            out.print(EcogridResultsetTransformer.toXMLString(records));
436

    
437
        } catch (Exception e) {
438
            e.printStackTrace();
439
        }*/
440
		
441

    
442
	}
443
	
444
	/**
445
	 * Returns the object format registered in the DataONE Object Format 
446
	 * Vocabulary for the given format identifier
447
	 * 
448
	 * @param fmtid - the identifier of the format requested
449
	 * 
450
	 * @return objectFormat - the object format requested
451
	 * 
452
	 * @throws InvalidRequest
453
	 * @throws ServiceFailure
454
	 * @throws NotFound
455
	 * @throws InsufficientResources
456
	 * @throws NotImplemented
457
	 */
458
	@Override
459
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
460
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
461
	  NotImplemented {
462
		 
463
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
464
			
465
	}
466

    
467
	/**
468
   * Returns a list of all object formats registered in the DataONE Object 
469
   * Format Vocabulary
470
 	 * 
471
	 * @return objectFormatList - The list of object formats registered in 
472
	 *                            the DataONE Object Format Vocabulary
473
	 * 
474
	 * @throws InvalidRequest
475
	 * @throws ServiceFailure
476
	 * @throws NotImplemented
477
	 * @throws NotFound
478
	 * @throws InsufficientResources
479
	 */
480
	@Override
481
	public ObjectFormatList listFormats() 
482
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
483
	  NotImplemented {
484

    
485
		return ObjectFormatService.getInstance().listFormats();
486
	}
487

    
488
	/**
489
   * Returns a list of nodes that have been registered with the DataONE infrastructure
490
 	 * 
491
	 * @return nodeList - List of nodes from the registry
492
	 * 
493
	 * @throws ServiceFailure
494
	 * @throws NotImplemented
495
	 */
496
	@Override
497
	public NodeList listNodes() 
498
	  throws NotImplemented, ServiceFailure {
499

    
500
		throw new NotImplemented("4800", "listNodes not implemented");
501
	}
502

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

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

    
541
	      logMetacat.debug("Checking if identifier exists...");
542
	      // Check that the identifier does not already exist
543
	      if (IdentifierManager.getInstance().identifierExists(guid.getValue())) {
544
			// check if we reserved the id
545
			boolean reserved = IdentifierManager.getInstance().hasReservation(guid, session);
546
			// throw an exception if not
547
			if (!reserved) {
548
	          throw new InvalidRequest("4863", 
549
	              "GUID is already in use by an existing object.");
550
			}
551
	      }
552

    
553
	      // insert the system metadata into the object store
554
	      logMetacat.debug("Starting to insert SystemMetadata...");
555
	      sysmeta.setDateSysMetadataModified(new Date());
556
	      try {
557
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
558
			    // force replication of this record
559
			    ForceReplicationSystemMetadataHandler forceReplication = 
560
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
561
		    } catch (Exception e) {
562
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
563
		    }
564
	      
565
	      logMetacat.debug("Returning from registerSystemMetadata");
566
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
567
	      return true;
568
	}
569

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

    
603
		// generate a pid if not provided
604
		if (pid == null) {
605
			pid = new Identifier();
606
			// TODO: go to EZID
607
			StringBuffer sb = new StringBuffer();
608
			if (scope != null) {
609
				sb.append(scope);
610
				sb.append(".");
611
			}
612
			sb.append(System.currentTimeMillis());
613
			pid.setValue(sb.toString());
614
		}
615
		// does this identifier exist?
616
		if (IdentifierManager.getInstance().identifierExists(pid.getValue())) {
617
			// check if it was reserved already
618
			if (IdentifierManager.getInstance().hasReservation(pid, session)) {
619
				throw new IdentifierNotUnique("4210", "Identifier already reserved: " + pid.getValue());
620
			}
621
			throw new IdentifierNotUnique("4210", "Identifier already exists: " + pid.getValue());
622
		}
623
		
624
		// if we got this far, save it
625
		SystemMetadata provisoinalSystemMetadata = new SystemMetadata();
626
		provisoinalSystemMetadata.setIdentifier(pid);
627
		provisoinalSystemMetadata.setRightsHolder(session.getSubject());
628
		try {
629
			IdentifierManager.getInstance().createSystemMetadata(provisoinalSystemMetadata);
630
		} catch (McdbDocNotFoundException e) {
631
			throw new ServiceFailure("4210", e.getMessage());
632
		}
633
		
634
		return pid;
635
	}
636

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

    
686
		return pid;
687
	}
688

    
689
}
(1-1/8)