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

    
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.CNRegister;
34
import org.dataone.service.cn.CNReplication;
35
import org.dataone.service.exceptions.IdentifierNotUnique;
36
import org.dataone.service.exceptions.InsufficientResources;
37
import org.dataone.service.exceptions.InvalidRequest;
38
import org.dataone.service.exceptions.InvalidSystemMetadata;
39
import org.dataone.service.exceptions.InvalidToken;
40
import org.dataone.service.exceptions.NotAuthorized;
41
import org.dataone.service.exceptions.NotFound;
42
import org.dataone.service.exceptions.NotImplemented;
43
import org.dataone.service.exceptions.ServiceFailure;
44
import org.dataone.service.exceptions.UnsupportedType;
45
import org.dataone.service.types.Checksum;
46
import org.dataone.service.types.Identifier;
47
import org.dataone.service.types.Node;
48
import org.dataone.service.types.NodeList;
49
import org.dataone.service.types.NodeReference;
50
import org.dataone.service.types.ObjectFormat;
51
import org.dataone.service.types.ObjectFormatIdentifier;
52
import org.dataone.service.types.ObjectFormatList;
53
import org.dataone.service.types.ObjectList;
54
import org.dataone.service.types.ObjectLocationList;
55
import org.dataone.service.types.Permission;
56
import org.dataone.service.types.QueryType;
57
import org.dataone.service.types.ReplicationPolicy;
58
import org.dataone.service.types.ReplicationStatus;
59
import org.dataone.service.types.Session;
60
import org.dataone.service.types.Subject;
61
import org.dataone.service.types.SystemMetadata;
62

    
63
import edu.ucsb.nceas.metacat.EventLog;
64
import edu.ucsb.nceas.metacat.IdentifierManager;
65
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
66
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
67

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

    
78
	/* the instance of the CNodeService object */
79
  private static CNodeService instance = null;
80
  
81
  /* the logger instance */
82
  private Logger logMetacat = null;
83

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

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

    
152
		return true;
153
	}
154

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

    
178
		return false;
179
	}
180

    
181
	/**
182
	 * Add capabilities to the node description
183
	 * 
184
	 * @param session - the Session object containing the credentials for the Subject
185
	 * @param nodeid - the node identifier for the given node be modified
186
	 * @param node - the node information to be added
187
	 * 
188
	 * @return true or false
189
	 * 
190
	 * @throws NotImplemented
191
	 * @throws NotAuthorized
192
	 * @throws ServiceFailure
193
	 * @throws InvalidRequest
194
	 * @throws NotFound
195
	 */
196
	@Override
197
	/*public boolean updateNodeCapabilities(Session session, NodeReference nodeid, Node node) */
198
	public boolean addNodeCapabilities(Session session, NodeReference nodeid, Node node)
199
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
200
	  NotFound {
201

    
202
		return false;
203
	}
204

    
205
	/**
206
	 * Register a Coordinating Node
207
	 * 
208
	 * @param session - the Session object containing the credentials for the Subject
209
	 * @param node - the node information for the given node be modified
210
	 * 
211
	 * @return nodeid - the identifier of the node to be registered
212
	 * 
213
	 * @throws NotImplemented
214
	 * @throws NotAuthorized
215
	 * @throws ServiceFailure
216
	 * @throws InvalidRequest
217
	 * @throws IdentifierNotUnique
218
	 */
219
	@Override
220
	public Identifier register(Session session, Node node) 
221
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
222
	  IdentifierNotUnique {
223

    
224
		return null;
225
	}
226

    
227
	/**
228
	 * Test that the specified relationship between pidOfSubject and pidOfObject exists
229
	 * 
230
	 * @param session - the Session object containing the credentials for the Subject
231
	 * @param node - the node information for the given node be modified
232
	 * 
233
	 * @return true if the relationship exists
234
	 * 
235
	 * @throws InvalidToken
236
	 * @throws ServiceFailure
237
	 * @throws NotAuthorized
238
	 * @throws NotFound
239
	 * @throws InvalidRequest
240
	 * @throws NotImplemented
241
	 */
242
	@Override
243
	public boolean assertRelation(Session session, Identifier pidofsubject, 
244
		String relationship, Identifier pidOfObject) 
245
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
246
	  InvalidRequest, NotImplemented {
247
		
248
		return false;
249
	}
250
	
251
	/**
252
	 * Return the checksum of the object given the identifier 
253
	 * 
254
	 * @param session - the Session object containing the credentials for the Subject
255
	 * @param pid - the object identifier for the given object
256
	 * 
257
	 * @return checksum - the checksum of the object
258
	 * 
259
	 * @throws InvalidToken
260
	 * @throws ServiceFailure
261
	 * @throws NotAuthorized
262
	 * @throws NotFound
263
	 * @throws InvalidRequest
264
	 * @throws NotImplemented
265
	 */
266
	@Override
267
	public Checksum getChecksum(Session session, Identifier pid)
268
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
269
	  InvalidRequest, NotImplemented {
270
		
271
		if (!isAuthorized(session, pid, Permission.READ)) {
272
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
273
		}
274
		SystemMetadata systemMetadata = null;
275
		try {
276
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
277
		} catch (McdbDocNotFoundException e) {
278
			throw new NotFound("1420", "No record found for: " + pid.getValue());
279
		}
280
		Checksum checksum = systemMetadata.getChecksum();
281
		
282
		return checksum;
283
	}
284

    
285
	/**
286
	 * Resolve the location of a given object
287
	 * 
288
	 * @param session - the Session object containing the credentials for the Subject
289
	 * @param pid - the object identifier for the given object
290
	 * 
291
	 * @return objectLocationList - the list of nodes known to contain the object
292
	 * 
293
	 * @throws InvalidRequest
294
	 * @throws InvalidToken
295
	 * @throws ServiceFailure
296
	 * @throws NotAuthorized
297
	 * @throws NotFound
298
	 * @throws NotImplemented
299
	 */
300
	@Override
301
	public ObjectLocationList resolve(Session session, Identifier pid)
302
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
303
	  NotFound, NotImplemented {
304

    
305
		return null;
306
	}
307

    
308
	/**
309
	 * Search the metadata catalog for identifiers that match the criteria
310
	 * 
311
	 * @param session - the Session object containing the credentials for the Subject
312
	 * @param queryType - An identifier for the type of query expression 
313
	 *                    provided in the query
314
	 * @param query -  The criteria for matching the characteristics of the 
315
	 *                 metadata objects of interest
316
	 * 
317
	 * @return objectList - the list of objects matching the criteria
318
	 * 
319
	 * @throws InvalidToken
320
	 * @throws ServiceFailure
321
	 * @throws NotAuthorized
322
	 * @throws InvalidRequest
323
	 * @throws NotImplemented
324
	 */
325
	@Override
326
	public ObjectList search(Session session, QueryType queryType, String query)
327
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
328
	  NotImplemented {
329

    
330
		return null;
331
	}
332

    
333
	/**
334
	 * Used internally within a Coordinating Node to add a new object 
335
	 * to the object store
336
	 * 
337
	 * @param session - the Session object containing the credentials for the Subject
338
	 * @param pid - The object identifier to be created
339
	 * @param object - the object bytes
340
	 * @param sysmeta - the system metadata that describes the object  
341
	 * 
342
	 * @return pid - the object identifier created
343
	 * 
344
	 * @throws InvalidToken
345
	 * @throws ServiceFailure
346
	 * @throws NotAuthorized
347
	 * @throws IdentifierNotUnique
348
	 * @throws UnsupportedType
349
	 * @throws InsufficientResources
350
	 * @throws InvalidSystemMetadata
351
	 * @throws NotImplemented
352
	 * @throws InvalidRequest
353
	 */
354
	@Override
355
	public Identifier create(Session session, Identifier pid, InputStream object,
356
	  SystemMetadata sysmeta) 
357
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
358
	  UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
359
	  NotImplemented, InvalidRequest {
360

    
361
		return null;
362
	}
363
	
364
	/**
365
	 * Returns the object format registered in the DataONE Object Format 
366
	 * Vocabulary for the given format identifier
367
	 * 
368
	 * @param fmtid - the identifier of the format requested
369
	 * 
370
	 * @return objectFormat - the object format requested
371
	 * 
372
	 * @throws InvalidRequest
373
	 * @throws ServiceFailure
374
	 * @throws NotFound
375
	 * @throws InsufficientResources
376
	 * @throws NotImplemented
377
	 */
378
	@Override
379
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
380
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
381
	  NotImplemented {
382
		 
383
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
384
			
385
	}
386

    
387
	/**
388
   * Returns a list of all object formats registered in the DataONE Object 
389
   * Format Vocabulary
390
 	 * 
391
	 * @return objectFormatList - The list of object formats registered in 
392
	 *                            the DataONE Object Format Vocabulary
393
	 * 
394
	 * @throws InvalidRequest
395
	 * @throws ServiceFailure
396
	 * @throws NotImplemented
397
	 * @throws NotFound
398
	 * @throws InsufficientResources
399
	 */
400
	@Override
401
	public ObjectFormatList listFormats() 
402
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
403
	  NotImplemented {
404

    
405
		return ObjectFormatService.getInstance().listFormats();
406
	}
407

    
408
	/**
409
   * Returns a list of nodes that have been registered with the DataONE infrastructure
410
 	 * 
411
	 * @return nodeList - List of nodes from the registry
412
	 * 
413
	 * @throws ServiceFailure
414
	 * @throws NotImplemented
415
	 */
416
	@Override
417
	public NodeList listNodes() 
418
	  throws NotImplemented, ServiceFailure {
419

    
420
		return null;
421
	}
422

    
423
	/**
424
   * Provides a mechanism for adding system metadata independently of its 
425
   * associated object, such as when adding system metadata for data objects.
426
 	 * 
427
	 * @param session - the Session object containing the credentials for the Subject
428
	 * @param pid - The identifier of the object to register the system metadata against
429
	 * @param sysmeta - The system metadata to be registered
430
	 * 
431
	 * @return true if the registration succeeds
432
	 * 
433
	 * @throws NotImplemented
434
	 * @throws NotAuthorized
435
	 * @throws ServiceFailure
436
	 * @throws InvalidRequest
437
	 * @throws InvalidSystemMetadata
438
	 */
439
	@Override
440
	public boolean registerSystemMetadata(Session session, Identifier guid,
441
	  SystemMetadata sysmeta) 
442
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
443
	  InvalidSystemMetadata {
444

    
445
		// TODO: control who can call this?
446
	      if (session == null) {
447
	          //TODO: many of the thrown exceptions do not use the correct error codes
448
	          //check these against the docs and correct them
449
	          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
450
	                  "  If you are not logged in, please do so and retry the request.");
451
	      }
452
	      
453
	      // verify that guid == SystemMetadata.getIdentifier()
454
	      logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
455
	      if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
456
	          throw new InvalidRequest("4863", 
457
	              "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
458
	              sysmeta.getIdentifier().getValue() + ").");
459
	      }
460

    
461
	      logMetacat.debug("Checking if identifier exists...");
462
	      // Check that the identifier does not already exist
463
	      IdentifierManager im = IdentifierManager.getInstance();
464
	      if (im.identifierExists(guid.getValue())) {
465
	          throw new InvalidRequest("4863", 
466
	              "GUID is already in use by an existing object.");
467
	      }
468

    
469
	      // insert the system metadata into the object store
470
	      logMetacat.debug("Starting to insert SystemMetadata...");
471
	      sysmeta.setDateSysMetadataModified(new Date());
472
	      try {
473
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
474
			    IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
475
			    // force replication of this record
476
			    ForceReplicationSystemMetadataHandler forceReplication = 
477
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
478
		    } catch (Exception e) {
479
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
480
		    }
481
	      
482
	      logMetacat.debug("Returning from registerSystemMetadata");
483
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
484
	      return true;
485
	}
486

    
487
	/**
488
   * Given an optional scope and format, reserves and returns an identifier 
489
   * within that scope and format that is unique and will not be 
490
   * used by any other sessions. 
491
 	 * 
492
	 * @param session - the Session object containing the credentials for the Subject
493
	 * @param pid - The identifier of the object to register the system metadata against
494
	 * @param scope - An optional string to be used to qualify the scope of 
495
	 *                the identifier namespace, which is applied differently 
496
	 *                depending on the format requested. If scope is not 
497
	 *                supplied, a default scope will be used.
498
	 * @param format - The optional name of the identifier format to be used, 
499
	 * 								 drawn from a DataONE-specific vocabulary of identifier 
500
	 *                 format names, including several common syntaxes such 
501
	 *                 as DOI, LSID, UUID, and LSRN, among others. If the 
502
	 *                 format is not supplied by the caller, the CN service 
503
	 *                 will use a default identifier format, which may change 
504
	 *                 over time.
505
	 * 
506
	 * @return true if the registration succeeds
507
	 * 
508
	 * @throws InvalidToken
509
	 * @throws ServiceFailure
510
	 * @throws NotAuthorized
511
	 * @throws IdentifierNotUnique
512
	 * @throws NotImplemented
513
	 */
514
	@Override
515
	public Identifier reserveIdentifier(Session session, Identifier pid,
516
	  String scope, String format) 
517
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
518
	  NotImplemented, InvalidRequest {
519

    
520
		return null;
521
	}
522

    
523
	/**
524
   * Changes ownership (RightsHolder) of the specified object to the 
525
   * subject specified by userId
526
 	 * 
527
	 * @param session - the Session object containing the credentials for the Subject
528
	 * @param pid - Identifier of the object to be modified
529
	 * @param userId - The subject that will be taking ownership of the specified object.
530
	 *
531
	 * @return pid - the identifier of the modified object
532
	 * 
533
	 * @throws ServiceFailure
534
	 * @throws InvalidToken
535
	 * @throws NotFound
536
	 * @throws NotAuthorized
537
	 * @throws NotImplemented
538
	 * @throws InvalidRequest
539
	 */	
540
	@Override
541
	public Identifier setOwner(Session session, Identifier pid, Subject userId)
542
	  throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
543
	  NotImplemented, InvalidRequest {
544
		
545
		// get the subject
546
		Subject subject = session.getSubject();
547
		// get the system metadata
548
		String guid = pid.getValue();
549
		
550
		// are we allowed to do this?
551
		if (!hasPermission(session, pid, Permission.CHANGE_PERMISSION)) {
552
			throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);	
553
		}
554
		
555
		SystemMetadata systemMetadata = null;
556
		try {
557
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
558
		} catch (McdbDocNotFoundException e) {
559
			throw new NotFound("4460", "No record found for: " + guid);
560
		}
561
				
562
		// set the new rights holder
563
		systemMetadata.setRightsHolder(userId);
564
		
565
		// update the metadata
566
		try {
567
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
568
		} catch (McdbDocNotFoundException e) {
569
			throw new ServiceFailure("4490", e.getMessage());
570
		}
571

    
572
		return pid;
573
	}
574

    
575
}
(1-1/8)