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, NotAuthorized, ServiceFailure, InvalidRequest {
124

    
125
		return false;
126
	}
127

    
128
	/**
129
	 * Set the replication status for an object given the object identifier
130
	 * 
131
	 * @param session - the Session object containing the credentials for the Subject
132
	 * @param pid - the object identifier for the given object
133
	 * @param status - the replication status to be applied
134
	 * 
135
	 * @return true or false
136
	 * 
137
	 * @throws NotImplemented
138
	 * @throws NotAuthorized
139
	 * @throws ServiceFailure
140
	 * @throws InvalidRequest
141
	 * @throws InvalidToken
142
	 * @throws NotFound
143
	 * 
144
	 */
145
	@Override
146
	public boolean setReplicationStatus(Session session, Identifier pid,
147
	  ReplicationStatus status) 
148
	  throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
149
	  InvalidRequest, NotFound {
150

    
151
		return false;
152
	}
153

    
154
	/**
155
	 * Add capabilities to the node description
156
	 * 
157
	 * @param session - the Session object containing the credentials for the Subject
158
	 * @param nodeid - the node identifier for the given node be modified
159
	 * @param node - the node information to be added
160
	 * 
161
	 * @return true or false
162
	 * 
163
	 * @throws NotImplemented
164
	 * @throws NotAuthorized
165
	 * @throws ServiceFailure
166
	 * @throws InvalidRequest
167
	 * @throws NotFound
168
	 */
169
	@Override
170
	/*public boolean updateNodeCapabilities(Session session, NodeReference nodeid, Node node) */
171
	public boolean addNodeCapabilities(Session session, NodeReference nodeid, Node node)
172
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
173
	  NotFound {
174

    
175
		return false;
176
	}
177

    
178
	/**
179
	 * Register a Coordinating Node
180
	 * 
181
	 * @param session - the Session object containing the credentials for the Subject
182
	 * @param node - the node information for the given node be modified
183
	 * 
184
	 * @return nodeid - the identifier of the node to be registered
185
	 * 
186
	 * @throws NotImplemented
187
	 * @throws NotAuthorized
188
	 * @throws ServiceFailure
189
	 * @throws InvalidRequest
190
	 * @throws IdentifierNotUnique
191
	 */
192
	@Override
193
	public Identifier register(Session session, Node node) 
194
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
195
	  IdentifierNotUnique {
196

    
197
		return null;
198
	}
199

    
200
	/**
201
	 * Test that the specified relationship between pidOfSubject and pidOfObject exists
202
	 * 
203
	 * @param session - the Session object containing the credentials for the Subject
204
	 * @param node - the node information for the given node be modified
205
	 * 
206
	 * @return true if the relationship exists
207
	 * 
208
	 * @throws InvalidToken
209
	 * @throws ServiceFailure
210
	 * @throws NotAuthorized
211
	 * @throws NotFound
212
	 * @throws InvalidRequest
213
	 * @throws NotImplemented
214
	 */
215
	@Override
216
	public boolean assertRelation(Session session, Identifier pidofsubject, 
217
		String relationship, Identifier pidOfObject) 
218
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
219
	  InvalidRequest, NotImplemented {
220
		
221
		return false;
222
	}
223
	
224
	/**
225
	 * Return the checksum of the object given the identifier 
226
	 * 
227
	 * @param session - the Session object containing the credentials for the Subject
228
	 * @param pid - the object identifier for the given object
229
	 * 
230
	 * @return checksum - the checksum of the object
231
	 * 
232
	 * @throws InvalidToken
233
	 * @throws ServiceFailure
234
	 * @throws NotAuthorized
235
	 * @throws NotFound
236
	 * @throws InvalidRequest
237
	 * @throws NotImplemented
238
	 */
239
	@Override
240
	public Checksum getChecksum(Session session, Identifier pid)
241
	  throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
242
	  InvalidRequest, NotImplemented {
243
		
244
		if (!isAuthorized(session, pid, Permission.READ)) {
245
			throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());	
246
		}
247
		SystemMetadata systemMetadata = null;
248
		try {
249
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
250
		} catch (McdbDocNotFoundException e) {
251
			throw new NotFound("1420", "No record found for: " + pid.getValue());
252
		}
253
		Checksum checksum = systemMetadata.getChecksum();
254
		
255
		return checksum;
256
	}
257

    
258
	/**
259
	 * Resolve the location of a given object
260
	 * 
261
	 * @param session - the Session object containing the credentials for the Subject
262
	 * @param pid - the object identifier for the given object
263
	 * 
264
	 * @return objectLocationList - the list of nodes known to contain the object
265
	 * 
266
	 * @throws InvalidRequest
267
	 * @throws InvalidToken
268
	 * @throws ServiceFailure
269
	 * @throws NotAuthorized
270
	 * @throws NotFound
271
	 * @throws NotImplemented
272
	 */
273
	@Override
274
	public ObjectLocationList resolve(Session session, Identifier pid)
275
	  throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
276
	  NotFound, NotImplemented {
277

    
278
		return null;
279
	}
280

    
281
	/**
282
	 * Search the metadata catalog for identifiers that match the criteria
283
	 * 
284
	 * @param session - the Session object containing the credentials for the Subject
285
	 * @param queryType - An identifier for the type of query expression 
286
	 *                    provided in the query
287
	 * @param query -  The criteria for matching the characteristics of the 
288
	 *                 metadata objects of interest
289
	 * 
290
	 * @return objectList - the list of objects matching the criteria
291
	 * 
292
	 * @throws InvalidToken
293
	 * @throws ServiceFailure
294
	 * @throws NotAuthorized
295
	 * @throws InvalidRequest
296
	 * @throws NotImplemented
297
	 */
298
	@Override
299
	public ObjectList search(Session session, QueryType queryType, String query)
300
	  throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
301
	  NotImplemented {
302

    
303
		return null;
304
	}
305

    
306
	/**
307
	 * Used internally within a Coordinating Node to add a new object 
308
	 * to the object store
309
	 * 
310
	 * @param session - the Session object containing the credentials for the Subject
311
	 * @param pid - The object identifier to be created
312
	 * @param object - the object bytes
313
	 * @param sysmeta - the system metadata that describes the object  
314
	 * 
315
	 * @return pid - the object identifier created
316
	 * 
317
	 * @throws InvalidToken
318
	 * @throws ServiceFailure
319
	 * @throws NotAuthorized
320
	 * @throws IdentifierNotUnique
321
	 * @throws UnsupportedType
322
	 * @throws InsufficientResources
323
	 * @throws InvalidSystemMetadata
324
	 * @throws NotImplemented
325
	 * @throws InvalidRequest
326
	 */
327
	@Override
328
	public Identifier create(Session session, Identifier pid, InputStream object,
329
	  SystemMetadata sysmeta) 
330
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
331
	  UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
332
	  NotImplemented, InvalidRequest {
333

    
334
		return null;
335
	}
336
	
337
	/**
338
	 * Returns the object format registered in the DataONE Object Format 
339
	 * Vocabulary for the given format identifier
340
	 * 
341
	 * @param fmtid - the identifier of the format requested
342
	 * 
343
	 * @return objectFormat - the object format requested
344
	 * 
345
	 * @throws InvalidRequest
346
	 * @throws ServiceFailure
347
	 * @throws NotFound
348
	 * @throws InsufficientResources
349
	 * @throws NotImplemented
350
	 */
351
	@Override
352
	public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
353
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
354
	  NotImplemented {
355
		 
356
	  	return ObjectFormatService.getInstance().getFormat(fmtid);
357
			
358
	}
359

    
360
	/**
361
   * Returns a list of all object formats registered in the DataONE Object 
362
   * Format Vocabulary
363
 	 * 
364
	 * @return objectFormatList - The list of object formats registered in 
365
	 *                            the DataONE Object Format Vocabulary
366
	 * 
367
	 * @throws InvalidRequest
368
	 * @throws ServiceFailure
369
	 * @throws NotImplemented
370
	 * @throws NotFound
371
	 * @throws InsufficientResources
372
	 */
373
	@Override
374
	public ObjectFormatList listFormats() 
375
	  throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
376
	  NotImplemented {
377

    
378
		return ObjectFormatService.getInstance().listFormats();
379
	}
380

    
381
	/**
382
   * Returns a list of nodes that have been registered with the DataONE infrastructure
383
 	 * 
384
	 * @return nodeList - List of nodes from the registry
385
	 * 
386
	 * @throws ServiceFailure
387
	 * @throws NotImplemented
388
	 */
389
	@Override
390
	public NodeList listNodes() 
391
	  throws NotImplemented, ServiceFailure {
392

    
393
		return null;
394
	}
395

    
396
	/**
397
   * Provides a mechanism for adding system metadata independently of its 
398
   * associated object, such as when adding system metadata for data objects.
399
 	 * 
400
	 * @param session - the Session object containing the credentials for the Subject
401
	 * @param pid - The identifier of the object to register the system metadata against
402
	 * @param sysmeta - The system metadata to be registered
403
	 * 
404
	 * @return true if the registration succeeds
405
	 * 
406
	 * @throws NotImplemented
407
	 * @throws NotAuthorized
408
	 * @throws ServiceFailure
409
	 * @throws InvalidRequest
410
	 * @throws InvalidSystemMetadata
411
	 */
412
	@Override
413
	public boolean registerSystemMetadata(Session session, Identifier guid,
414
	  SystemMetadata sysmeta) 
415
	  throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
416
	  InvalidSystemMetadata {
417

    
418
		// TODO: control who can call this?
419
	      if (session == null) {
420
	          //TODO: many of the thrown exceptions do not use the correct error codes
421
	          //check these against the docs and correct them
422
	          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
423
	                  "  If you are not logged in, please do so and retry the request.");
424
	      }
425
	      
426
	      // verify that guid == SystemMetadata.getIdentifier()
427
	      logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
428
	      if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
429
	          throw new InvalidRequest("4863", 
430
	              "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
431
	              sysmeta.getIdentifier().getValue() + ").");
432
	      }
433

    
434
	      logMetacat.debug("Checking if identifier exists...");
435
	      // Check that the identifier does not already exist
436
	      IdentifierManager im = IdentifierManager.getInstance();
437
	      if (im.identifierExists(guid.getValue())) {
438
	          throw new InvalidRequest("4863", 
439
	              "GUID is already in use by an existing object.");
440
	      }
441

    
442
	      // insert the system metadata into the object store
443
	      logMetacat.debug("Starting to insert SystemMetadata...");
444
	      sysmeta.setDateSysMetadataModified(new Date());
445
	      try {
446
			    IdentifierManager.getInstance().createSystemMetadata(sysmeta);
447
			    IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
448
			    // force replication of this record
449
			    ForceReplicationSystemMetadataHandler forceReplication = 
450
			    	new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
451
		    } catch (Exception e) {
452
	          throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
453
		    }
454
	      
455
	      logMetacat.debug("Returning from registerSystemMetadata");
456
	      EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
457
	      return true;
458
	}
459

    
460
	/**
461
   * Given an optional scope and format, reserves and returns an identifier 
462
   * within that scope and format that is unique and will not be 
463
   * used by any other sessions. 
464
 	 * 
465
	 * @param session - the Session object containing the credentials for the Subject
466
	 * @param pid - The identifier of the object to register the system metadata against
467
	 * @param scope - An optional string to be used to qualify the scope of 
468
	 *                the identifier namespace, which is applied differently 
469
	 *                depending on the format requested. If scope is not 
470
	 *                supplied, a default scope will be used.
471
	 * @param format - The optional name of the identifier format to be used, 
472
	 * 								 drawn from a DataONE-specific vocabulary of identifier 
473
	 *                 format names, including several common syntaxes such 
474
	 *                 as DOI, LSID, UUID, and LSRN, among others. If the 
475
	 *                 format is not supplied by the caller, the CN service 
476
	 *                 will use a default identifier format, which may change 
477
	 *                 over time.
478
	 * 
479
	 * @return true if the registration succeeds
480
	 * 
481
	 * @throws InvalidToken
482
	 * @throws ServiceFailure
483
	 * @throws NotAuthorized
484
	 * @throws IdentifierNotUnique
485
	 * @throws NotImplemented
486
	 */
487
	@Override
488
	public Identifier reserveIdentifier(Session session, Identifier pid,
489
	  String scope, String format) 
490
	  throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
491
	  NotImplemented, InvalidRequest {
492

    
493
		return null;
494
	}
495

    
496
	/**
497
   * Changes ownership (RightsHolder) of the specified object to the 
498
   * subject specified by userId
499
 	 * 
500
	 * @param session - the Session object containing the credentials for the Subject
501
	 * @param pid - Identifier of the object to be modified
502
	 * @param userId - The subject that will be taking ownership of the specified object.
503
	 *
504
	 * @return pid - the identifier of the modified object
505
	 * 
506
	 * @throws ServiceFailure
507
	 * @throws InvalidToken
508
	 * @throws NotFound
509
	 * @throws NotAuthorized
510
	 * @throws NotImplemented
511
	 * @throws InvalidRequest
512
	 */	
513
	@Override
514
	public Identifier setOwner(Session session, Identifier pid, Subject userId)
515
	  throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
516
	  NotImplemented, InvalidRequest {
517
		
518
		// get the subject
519
		Subject subject = session.getSubject();
520
		// get the system metadata
521
		String guid = pid.getValue();
522
		
523
		// are we allowed to do this?
524
		if (!hasPermission(session, pid, Permission.CHANGE_PERMISSION)) {
525
			throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);	
526
		}
527
		
528
		SystemMetadata systemMetadata = null;
529
		try {
530
			systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
531
		} catch (McdbDocNotFoundException e) {
532
			throw new NotFound("4460", "No record found for: " + guid);
533
		}
534
				
535
		// set the new rights holder
536
		systemMetadata.setRightsHolder(userId);
537
		
538
		// update the metadata
539
		try {
540
			IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
541
		} catch (McdbDocNotFoundException e) {
542
			throw new ServiceFailure("4490", e.getMessage());
543
		}
544

    
545
		return pid;
546
	}
547

    
548
}
(1-1/8)