Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000-2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author:  $'
7
 *     '$Date:  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23

    
24
package edu.ucsb.nceas.metacat.dataone;
25

    
26
import java.io.InputStream;
27
import java.math.BigInteger;
28
import java.util.ArrayList;
29
import java.util.Calendar;
30
import java.util.Date;
31
import java.util.List;
32
import java.util.concurrent.locks.Lock;
33

    
34
import javax.servlet.http.HttpServletRequest;
35

    
36
import org.apache.log4j.Logger;
37
import org.dataone.client.CNode;
38
import org.dataone.client.D1Client;
39
import org.dataone.client.MNode;
40
import org.dataone.service.cn.v1.CNAuthorization;
41
import org.dataone.service.cn.v1.CNCore;
42
import org.dataone.service.cn.v1.CNRead;
43
import org.dataone.service.cn.v1.CNReplication;
44
import org.dataone.service.exceptions.BaseException;
45
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotFound;
52
import org.dataone.service.exceptions.NotImplemented;
53
import org.dataone.service.exceptions.ServiceFailure;
54
import org.dataone.service.exceptions.UnsupportedType;
55
import org.dataone.service.exceptions.VersionMismatch;
56
import org.dataone.service.types.v1.AccessPolicy;
57
import org.dataone.service.types.v1.Checksum;
58
import org.dataone.service.types.v1.ChecksumAlgorithmList;
59
import org.dataone.service.types.v1.DescribeResponse;
60
import org.dataone.service.types.v1.Event;
61
import org.dataone.service.types.v1.Identifier;
62
import org.dataone.service.types.v1.Log;
63
import org.dataone.service.types.v1.Node;
64
import org.dataone.service.types.v1.NodeList;
65
import org.dataone.service.types.v1.NodeReference;
66
import org.dataone.service.types.v1.NodeType;
67
import org.dataone.service.types.v1.ObjectFormat;
68
import org.dataone.service.types.v1.ObjectFormatIdentifier;
69
import org.dataone.service.types.v1.ObjectFormatList;
70
import org.dataone.service.types.v1.ObjectList;
71
import org.dataone.service.types.v1.ObjectLocationList;
72
import org.dataone.service.types.v1.Permission;
73
import org.dataone.service.types.v1.Replica;
74
import org.dataone.service.types.v1.ReplicationPolicy;
75
import org.dataone.service.types.v1.ReplicationStatus;
76
import org.dataone.service.types.v1.Session;
77
import org.dataone.service.types.v1.Subject;
78
import org.dataone.service.types.v1.SystemMetadata;
79
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
80

    
81
import edu.ucsb.nceas.metacat.EventLog;
82
import edu.ucsb.nceas.metacat.IdentifierManager;
83
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
84

    
85
/**
86
 * Represents Metacat's implementation of the DataONE Coordinating Node 
87
 * service API. Methods implement the various CN* interfaces, and methods common
88
 * to both Member Node and Coordinating Node interfaces are found in the
89
 * D1NodeService super class.
90
 *
91
 */
92
public class CNodeService extends D1NodeService implements CNAuthorization,
93
    CNCore, CNRead, CNReplication {
94

    
95
  /* the logger instance */
96
  private Logger logMetacat = null;
97

    
98
  /**
99
   * singleton accessor
100
   */
101
  public static CNodeService getInstance(HttpServletRequest request) { 
102
    return new CNodeService(request);
103
  }
104
  
105
  /**
106
   * Constructor, private for singleton access
107
   */
108
  private CNodeService(HttpServletRequest request) {
109
    super(request);
110
    logMetacat = Logger.getLogger(CNodeService.class);
111
        
112
  }
113
    
114
  /**
115
   * Set the replication policy for an object given the object identifier
116
   * 
117
   * @param session - the Session object containing the credentials for the Subject
118
   * @param pid - the object identifier for the given object
119
   * @param policy - the replication policy to be applied
120
   * 
121
   * @return true or false
122
   * 
123
   * @throws NotImplemented
124
   * @throws NotAuthorized
125
   * @throws ServiceFailure
126
   * @throws InvalidRequest
127
   * @throws VersionMismatch
128
   * 
129
   */
130
  @Override
131
  public boolean setReplicationPolicy(Session session, Identifier pid,
132
      ReplicationPolicy policy, long serialVersion) 
133
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
134
      InvalidRequest, InvalidToken, VersionMismatch {
135
      
136
      // The lock to be used for this identifier
137
      Lock lock = null;
138
      
139
      // get the subject
140
      Subject subject = session.getSubject();
141
      
142
      // are we allowed to do this?
143
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
144
          throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION
145
                  + " not allowed by " + subject.getValue() + " on "
146
                  + pid.getValue());
147
          
148
      }
149
      
150
      SystemMetadata systemMetadata = null;
151
      try {
152
          lock = HazelcastService.getInstance().getLock(pid.getValue());
153
          lock.lock();
154
          logMetacat.debug("Locked identifier " + pid.getValue());
155

    
156
          try {
157
              if ( HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid) ) {
158
                  systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
159
                  
160
              }
161
              
162
              // did we get it correctly?
163
              if ( systemMetadata == null ) {
164
                  throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
165
                  
166
              }
167

    
168
              // does the request have the most current system metadata?
169
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
170
                 String msg = "The requested system metadata version number " + 
171
                     serialVersion + " differs from the current version at " +
172
                     systemMetadata.getSerialVersion().longValue() +
173
                     ". Please get the latest copy in order to modify it.";
174
                 throw new VersionMismatch("4886", msg);
175
                 
176
              }
177
              
178
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
179
              throw new NotFound("4884", "No record found for: " + pid.getValue());
180
            
181
          }
182
          
183
          // set the new policy
184
          systemMetadata.setReplicationPolicy(policy);
185
          
186
          // update the metadata
187
          try {
188
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
189
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
190
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
191
              notifyReplicaNodes(systemMetadata);
192
              
193
          } catch (RuntimeException e) {
194
              throw new ServiceFailure("4882", e.getMessage());
195
          
196
          }
197
          
198
      } catch (RuntimeException e) {
199
          throw new ServiceFailure("4882", e.getMessage());
200
          
201
      } finally {
202
          lock.unlock();
203
          logMetacat.debug("Unlocked identifier " + pid.getValue());
204
          
205
      }
206
    
207
      return true;
208
  }
209

    
210
  /**
211
   * Deletes the replica from the given Member Node
212
   * NOTE: MN.delete() may be an "archive" operation. TBD.
213
   * @param session
214
   * @param pid
215
   * @param nodeId
216
   * @param serialVersion
217
   * @return
218
   * @throws InvalidToken
219
   * @throws ServiceFailure
220
   * @throws NotAuthorized
221
   * @throws NotFound
222
   * @throws NotImplemented
223
   * @throws VersionMismatch
224
   */
225
  @Override
226
  public boolean deleteReplicationMetadata(Session session, Identifier pid, NodeReference nodeId, long serialVersion) 
227
  	throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, VersionMismatch {
228
	  
229
	  	// The lock to be used for this identifier
230
		Lock lock = null;
231

    
232
		// get the subject
233
		Subject subject = session.getSubject();
234

    
235
		// are we allowed to do this?
236
		boolean isAuthorized = false;
237
		try {
238
			isAuthorized = isAuthorized(session, pid, Permission.WRITE);
239
		} catch (InvalidRequest e) {
240
			throw new ServiceFailure("4882", e.getDescription());
241
		}
242
		if (!isAuthorized) {
243
			throw new NotAuthorized("4881", Permission.WRITE
244
					+ " not allowed by " + subject.getValue() + " on "
245
					+ pid.getValue());
246

    
247
		}
248

    
249
		SystemMetadata systemMetadata = null;
250
		try {
251
			lock = HazelcastService.getInstance().getLock(pid.getValue());
252
			lock.lock();
253
			logMetacat.debug("Locked identifier " + pid.getValue());
254

    
255
			try {
256
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
257
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
258
				}
259

    
260
				// did we get it correctly?
261
				if (systemMetadata == null) {
262
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
263
				}
264

    
265
				// does the request have the most current system metadata?
266
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
267
					String msg = "The requested system metadata version number "
268
							+ serialVersion
269
							+ " differs from the current version at "
270
							+ systemMetadata.getSerialVersion().longValue()
271
							+ ". Please get the latest copy in order to modify it.";
272
					throw new VersionMismatch("4886", msg);
273

    
274
				}
275

    
276
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
277
				throw new NotFound("4884", "No record found for: " + pid.getValue());
278

    
279
			}
280
			  
281
			// check permissions
282
			// TODO: is this necessary?
283
			List<Node> nodeList = D1Client.getCN().listNodes().getNodeList();
284
			boolean isAllowed = ServiceMethodRestrictionUtil.isMethodAllowed(session.getSubject(), nodeList, "CNReplication", "deleteReplicationMetadata");
285
			if (isAllowed) {
286
				throw new NotAuthorized("4881", "Caller is not authorized to deleteReplicationMetadata");
287
			}
288
			  
289
			// delete the replica from the given node
290
			D1Client.getMN(nodeId).delete(session, pid);
291
			  
292
			// reflect that change in the system metadata
293
			List<Replica> updatedReplicas = new ArrayList<Replica>(systemMetadata.getReplicaList());
294
			for (Replica r: systemMetadata.getReplicaList()) {
295
				  if (r.getReplicaMemberNode().equals(nodeId)) {
296
					  updatedReplicas.remove(r);
297
					  break;
298
				  }
299
			}
300
			systemMetadata.setReplicaList(updatedReplicas);
301

    
302
			// update the metadata
303
			try {
304
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
305
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
306
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
307
			} catch (RuntimeException e) {
308
				throw new ServiceFailure("4882", e.getMessage());
309
			}
310

    
311
		} catch (RuntimeException e) {
312
			throw new ServiceFailure("4882", e.getMessage());
313
		} finally {
314
			lock.unlock();
315
			logMetacat.debug("Unlocked identifier " + pid.getValue());
316
		}
317

    
318
		return true;	  
319
	  
320
  }
321
  
322
  /**
323
   * Deletes an object from the Coordinating Node, where the object is a 
324
   * a science metadata object.
325
   * 
326
   * @param session - the Session object containing the credentials for the Subject
327
   * @param pid - The object identifier to be deleted
328
   * 
329
   * @return pid - the identifier of the object used for the deletion
330
   * 
331
   * @throws InvalidToken
332
   * @throws ServiceFailure
333
   * @throws NotAuthorized
334
   * @throws NotFound
335
   * @throws NotImplemented
336
   * @throws InvalidRequest
337
   */
338
  @Override
339
  public Identifier delete(Session session, Identifier pid) 
340
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
341

    
342
	  // check that it is CN/admin
343
	  boolean allowed = isAdminAuthorized(session);
344
	  
345
	  if (!allowed) {
346
		  String msg = "The subject is not allowed to call delete() on a Coordinating Node.";
347
		  logMetacat.info(msg);
348
		  throw new NotAuthorized("1320", msg);
349
	  }
350
	  
351
	  // defer to superclass implementation
352
	  Identifier retId =  super.delete(session, pid);
353
      
354
	  // notify the replicas
355
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
356
	  if (systemMetadata.getReplicaList() != null) {
357
		  for (Replica replica: systemMetadata.getReplicaList()) {
358
			  NodeReference replicaNode = replica.getReplicaMemberNode();
359
			  try {
360
				  Identifier mnRetId = D1Client.getMN(replicaNode).delete(null, pid);
361
			  } catch (Exception e) {
362
				  // all we can really do is log errors and carry on with life
363
				  logMetacat.error("Error deleting pid: " +  pid.getValue() + " from replica MN: " + replicaNode.getValue(), e);
364
			}
365
			  
366
		  }
367
	  }
368
	  
369
	  return retId;
370
      
371
  }
372
  
373
  /**
374
   * Deletes an object from the Coordinating Node, where the object is a 
375
   * a science metadata object.
376
   * 
377
   * @param session - the Session object containing the credentials for the Subject
378
   * @param pid - The object identifier to be deleted
379
   * 
380
   * @return pid - the identifier of the object used for the deletion
381
   * 
382
   * @throws InvalidToken
383
   * @throws ServiceFailure
384
   * @throws NotAuthorized
385
   * @throws NotFound
386
   * @throws NotImplemented
387
   * @throws InvalidRequest
388
   */
389
  @Override
390
  public Identifier archive(Session session, Identifier pid) 
391
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
392

    
393
	  // check that it is CN/admin
394
	  boolean allowed = isAdminAuthorized(session);
395
	  
396
	  if (!allowed) {
397
		  String msg = "The subject is not allowed to call delete() on a Coordinating Node.";
398
		  logMetacat.info(msg);
399
		  throw new NotAuthorized("1320", msg);
400
	  }
401
	  
402
	  // defer to superclass implementation
403
	  Identifier retId =  super.archive(session, pid);
404
      
405
	  // notify the replicas
406
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
407
	  if (systemMetadata.getReplicaList() != null) {
408
		  for (Replica replica: systemMetadata.getReplicaList()) {
409
			  NodeReference replicaNode = replica.getReplicaMemberNode();
410
			  try {
411
				  // TODO: implement in the clients
412
				  //Identifier mnRetId = D1Client.getMN(replicaNode).archive(null, pid);
413
			  } catch (Exception e) {
414
				  // all we can really do is log errors and carry on with life
415
				  logMetacat.error("Error archiving pid: " +  pid.getValue() + " from replica MN: " + replicaNode.getValue(), e);
416
			}
417
			  
418
		  }
419
	  }
420
	  
421
	  return retId;
422
      
423
  }
424
  
425
  /**
426
   * Set the obsoletedBy attribute in System Metadata
427
   * @param session
428
   * @param pid
429
   * @param obsoletedByPid
430
   * @param serialVersion
431
   * @return
432
   * @throws NotImplemented
433
   * @throws NotFound
434
   * @throws NotAuthorized
435
   * @throws ServiceFailure
436
   * @throws InvalidRequest
437
   * @throws InvalidToken
438
   * @throws VersionMismatch
439
   */
440
  @Override
441
  public boolean setObsoletedBy(Session session, Identifier pid,
442
			Identifier obsoletedByPid, long serialVersion)
443
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
444
			InvalidRequest, InvalidToken, VersionMismatch {
445

    
446
		// The lock to be used for this identifier
447
		Lock lock = null;
448

    
449
		// get the subject
450
		Subject subject = session.getSubject();
451

    
452
		// are we allowed to do this?
453
		if (!isAuthorized(session, pid, Permission.WRITE)) {
454
			throw new NotAuthorized("4881", Permission.WRITE
455
					+ " not allowed by " + subject.getValue() + " on "
456
					+ pid.getValue());
457

    
458
		}
459

    
460

    
461
		SystemMetadata systemMetadata = null;
462
		try {
463
			lock = HazelcastService.getInstance().getLock(pid.getValue());
464
			lock.lock();
465
			logMetacat.debug("Locked identifier " + pid.getValue());
466

    
467
			try {
468
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
469
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
470
				}
471

    
472
				// did we get it correctly?
473
				if (systemMetadata == null) {
474
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
475
				}
476

    
477
				// does the request have the most current system metadata?
478
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
479
					String msg = "The requested system metadata version number "
480
							+ serialVersion
481
							+ " differs from the current version at "
482
							+ systemMetadata.getSerialVersion().longValue()
483
							+ ". Please get the latest copy in order to modify it.";
484
					throw new VersionMismatch("4886", msg);
485

    
486
				}
487

    
488
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
489
				throw new NotFound("4884", "No record found for: " + pid.getValue());
490

    
491
			}
492

    
493
			// set the new policy
494
			systemMetadata.setObsoletedBy(obsoletedByPid);
495

    
496
			// update the metadata
497
			try {
498
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
499
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
500
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
501
			} catch (RuntimeException e) {
502
				throw new ServiceFailure("4882", e.getMessage());
503
			}
504

    
505
		} catch (RuntimeException e) {
506
			throw new ServiceFailure("4882", e.getMessage());
507
		} finally {
508
			lock.unlock();
509
			logMetacat.debug("Unlocked identifier " + pid.getValue());
510
		}
511

    
512
		return true;
513
	}
514
  
515
  
516
  /**
517
   * Set the replication status for an object given the object identifier
518
   * 
519
   * @param session - the Session object containing the credentials for the Subject
520
   * @param pid - the object identifier for the given object
521
   * @param status - the replication status to be applied
522
   * 
523
   * @return true or false
524
   * 
525
   * @throws NotImplemented
526
   * @throws NotAuthorized
527
   * @throws ServiceFailure
528
   * @throws InvalidRequest
529
   * @throws InvalidToken
530
   * @throws NotFound
531
   * 
532
   */
533
  @Override
534
  public boolean setReplicationStatus(Session session, Identifier pid,
535
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
536
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
537
      InvalidRequest, NotFound {
538
	  
539
	  // cannot be called by public
540
	  if (session == null) {
541
		  throw new NotAuthorized("4720", "Session cannot be null");
542
	  }
543
      
544
      // The lock to be used for this identifier
545
      Lock lock = null;
546
      
547
      boolean allowed = false;
548
      int replicaEntryIndex = -1;
549
      List<Replica> replicas = null;
550
      // get the subject
551
      Subject subject = session.getSubject();
552
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
553
          " is " + status.toString());
554
      
555
      SystemMetadata systemMetadata = null;
556

    
557
      try {
558
          lock = HazelcastService.getInstance().getLock(pid.getValue());
559
          lock.lock();
560
          logMetacat.debug("Locked identifier " + pid.getValue());
561

    
562
          try {      
563
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
564

    
565
              // did we get it correctly?
566
              if ( systemMetadata == null ) {
567
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
568
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
569
                  
570
              }
571
              replicas = systemMetadata.getReplicaList();
572
              int count = 0;
573
              
574
              // was there a failure? log it
575
              if ( failure != null && status == ReplicationStatus.FAILED ) {
576
                 String msg = "The replication request of the object identified by " + 
577
                     pid.getValue() + " failed.  The error message was " +
578
                     failure.getMessage() + ".";
579
              }
580
              
581
              if (replicas.size() > 0 && replicas != null) {
582
                  // find the target replica index in the replica list
583
                  for (Replica replica : replicas) {
584
                      String replicaNodeStr = replica.getReplicaMemberNode()
585
                              .getValue();
586
                      String targetNodeStr = targetNode.getValue();
587
                      logMetacat.debug("Comparing " + replicaNodeStr + " to "
588
                              + targetNodeStr);
589
                  
590
                      if (replicaNodeStr.equals(targetNodeStr)) {
591
                          replicaEntryIndex = count;
592
                          logMetacat.debug("replica entry index is: "
593
                                  + replicaEntryIndex);
594
                          break;
595
                      }
596
                      count++;
597
                  
598
                  }
599
              }
600
              // are we allowed to do this? only CNs and target MNs are allowed
601
              CNode cn = D1Client.getCN();
602
              List<Node> nodes = cn.listNodes().getNodeList();
603
              
604
              // find the node in the node list
605
              for ( Node node : nodes ) {
606
                  
607
                  NodeReference nodeReference = node.getIdentifier();
608
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
609
                  
610
                  // allow target MN certs
611
                  if (targetNode.getValue().equals(nodeReference.getValue()) &&
612
                      node.getType() == NodeType.MN) {
613
                      List<Subject> nodeSubjects = node.getSubjectList();
614
                      
615
                      // check if the session subject is in the node subject list
616
                      for (Subject nodeSubject : nodeSubjects) {
617
                          logMetacat.debug("In setReplicationStatus(), comparing subjects: " +
618
                                  nodeSubject.getValue() + " and " + subject.getValue());
619
                          if ( nodeSubject.equals(subject) ) { // subject of session == target node subject
620
                              
621
                              // lastly limit to COMPLETED and FAILED status updates from MNs only
622
                              if ( status == ReplicationStatus.COMPLETED ||
623
                                   status == ReplicationStatus.FAILED) {
624
                                  allowed = true;
625
                                  break;
626
                                  
627
                              }                              
628
                          }
629
                      }                 
630
                  }
631
              }
632

    
633
              if ( !allowed ) {
634
                  //check for CN admin access
635
                  allowed = isAuthorized(session, pid, Permission.WRITE);
636
                  
637
              }              
638
              
639
              if ( !allowed ) {
640
                  String msg = "The subject identified by "
641
                          + subject.getValue()
642
                          + " does not have permission to set the replication status for "
643
                          + "the replica identified by "
644
                          + targetNode.getValue() + ".";
645
                  logMetacat.info(msg);
646
                  throw new NotAuthorized("4720", msg);
647
                  
648
              }
649

    
650
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
651
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
652
                " : " + e.getMessage());
653
            
654
          }
655
          
656
          Replica targetReplica = new Replica();
657
          // set the status for the replica
658
          if ( replicaEntryIndex != -1 ) {
659
              targetReplica = replicas.get(replicaEntryIndex);
660
              targetReplica.setReplicationStatus(status);
661
              logMetacat.debug("Set the replication status for " + 
662
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
663
                  targetReplica.getReplicationStatus());
664
              
665
          } else {
666
              // this is a new entry, create it
667
              targetReplica.setReplicaMemberNode(targetNode);
668
              targetReplica.setReplicationStatus(status);
669
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
670
              replicas.add(targetReplica);
671
              
672
          }
673
          
674
          systemMetadata.setReplicaList(replicas);
675
                
676
          // update the metadata
677
          try {
678
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
679
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
680
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
681
              
682
              if ( status == ReplicationStatus.FAILED && failure != null ) {
683
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
684
                      " on target node " + targetNode + ". The exception was: " +
685
                      failure.getMessage());
686
              }
687
          } catch (RuntimeException e) {
688
              throw new ServiceFailure("4700", e.getMessage());
689
          
690
          }
691
          
692
    } catch (RuntimeException e) {
693
        String msg = "There was a RuntimeException getting the lock for " +
694
            pid.getValue();
695
        logMetacat.info(msg);
696
        
697
    } finally {
698
        lock.unlock();
699
        logMetacat.debug("Unlocked identifier " + pid.getValue());
700
        
701
    }
702
      
703
      return true;
704
  }
705
  
706
  /**
707
   * Return the checksum of the object given the identifier 
708
   * 
709
   * @param session - the Session object containing the credentials for the Subject
710
   * @param pid - the object identifier for the given object
711
   * 
712
   * @return checksum - the checksum of the object
713
   * 
714
   * @throws InvalidToken
715
   * @throws ServiceFailure
716
   * @throws NotAuthorized
717
   * @throws NotFound
718
   * @throws NotImplemented
719
   */
720
  @Override
721
  public Checksum getChecksum(Session session, Identifier pid)
722
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
723
    NotImplemented {
724
    
725
	boolean isAuthorized = false;
726
	try {
727
		isAuthorized = isAuthorized(session, pid, Permission.READ);
728
	} catch (InvalidRequest e) {
729
		throw new ServiceFailure("1410", e.getDescription());
730
	}  
731
    if (!isAuthorized) {
732
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
733
    }
734
    
735
    SystemMetadata systemMetadata = null;
736
    Checksum checksum = null;
737
    
738
    try {
739
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
740

    
741
        if (systemMetadata == null ) {
742
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
743
        }
744
        checksum = systemMetadata.getChecksum();
745
        
746
    } catch (RuntimeException e) {
747
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
748
            pid.getValue() + ". The error message was: " + e.getMessage());
749
      
750
    }
751
    
752
    return checksum;
753
  }
754

    
755
  /**
756
   * Resolve the location of a given object
757
   * 
758
   * @param session - the Session object containing the credentials for the Subject
759
   * @param pid - the object identifier for the given object
760
   * 
761
   * @return objectLocationList - the list of nodes known to contain the object
762
   * 
763
   * @throws InvalidToken
764
   * @throws ServiceFailure
765
   * @throws NotAuthorized
766
   * @throws NotFound
767
   * @throws NotImplemented
768
   */
769
  @Override
770
  public ObjectLocationList resolve(Session session, Identifier pid)
771
    throws InvalidToken, ServiceFailure, NotAuthorized,
772
    NotFound, NotImplemented {
773

    
774
    throw new NotImplemented("4131", "resolve not implemented");
775

    
776
  }
777

    
778
  /**
779
   * Search the metadata catalog for identifiers that match the criteria
780
   * 
781
   * @param session - the Session object containing the credentials for the Subject
782
   * @param queryType - An identifier for the type of query expression 
783
   *                    provided in the query
784
   * @param query -  The criteria for matching the characteristics of the 
785
   *                 metadata objects of interest
786
   * 
787
   * @return objectList - the list of objects matching the criteria
788
   * 
789
   * @throws InvalidToken
790
   * @throws ServiceFailure
791
   * @throws NotAuthorized
792
   * @throws InvalidRequest
793
   * @throws NotImplemented
794
   */
795
  @Override
796
  public ObjectList search(Session session, String queryType, String query)
797
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
798
    NotImplemented {
799

    
800
    ObjectList objectList = null;
801
    try {
802
        objectList = 
803
          IdentifierManager.getInstance().querySystemMetadata(
804
              null, //startTime, 
805
              null, //endTime,
806
              null, //objectFormat, 
807
              false, //replicaStatus, 
808
              0, //start, 
809
              -1 //count
810
              );
811
        
812
    } catch (Exception e) {
813
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
814
    }
815

    
816
      return objectList;
817
      
818
    //throw new NotImplemented("4281", "search not implemented");
819
    
820
    // the code block below is from an older implementation
821
    
822
    /*  This block commented out because of the EcoGrid circular dependency.
823
         *  For now, query will not be supported until the circularity can be
824
         *  resolved, probably by moving the ecogrid query syntax transformers
825
         *  directly into the Metacat codebase.  MBJ 2010-02-03
826
         
827
        try {
828
            EcogridQueryParser parser = new EcogridQueryParser(request
829
                    .getReader());
830
            parser.parseXML();
831
            QueryType queryType = parser.getEcogridQuery();
832
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
833
                new EcogridJavaToMetacatJavaQueryTransformer();
834
            QuerySpecification metacatQuery = queryTransformer
835
                    .transform(queryType);
836

    
837
            DBQuery metacat = new DBQuery();
838

    
839
            boolean useXMLIndex = (new Boolean(PropertyService
840
                    .getProperty("database.usexmlindex"))).booleanValue();
841
            String xmlquery = "query"; // we don't care the query in resultset,
842
            // the query can be anything
843
            PrintWriter out = null; // we don't want metacat result, so set out null
844

    
845
            // parameter: queryspecification, user, group, usingIndexOrNot
846
            StringBuffer result = metacat.createResultDocument(xmlquery,
847
                    metacatQuery, out, username, groupNames, useXMLIndex);
848

    
849
            // create result set transfer       
850
            String saxparser = PropertyService.getProperty("xml.saxparser");
851
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
852
                    new StringReader(result.toString()), saxparser, queryType
853
                            .getNamespace().get_value());
854
            ResultsetType records = metacatResultsetParser.getEcogridResult();
855

    
856
            System.out
857
                    .println(EcogridResultsetTransformer.toXMLString(records));
858
            response.setContentType("text/xml");
859
            out = response.getWriter();
860
            out.print(EcogridResultsetTransformer.toXMLString(records));
861

    
862
        } catch (Exception e) {
863
            e.printStackTrace();
864
        }*/
865
    
866

    
867
  }
868
  
869
  /**
870
   * Returns the object format registered in the DataONE Object Format 
871
   * Vocabulary for the given format identifier
872
   * 
873
   * @param fmtid - the identifier of the format requested
874
   * 
875
   * @return objectFormat - the object format requested
876
   * 
877
   * @throws ServiceFailure
878
   * @throws NotFound
879
   * @throws InsufficientResources
880
   * @throws NotImplemented
881
   */
882
  @Override
883
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
884
    throws ServiceFailure, NotFound, NotImplemented {
885
     
886
      return ObjectFormatService.getInstance().getFormat(fmtid);
887
      
888
  }
889

    
890
  /**
891
   * Returns a list of all object formats registered in the DataONE Object 
892
   * Format Vocabulary
893
    * 
894
   * @return objectFormatList - The list of object formats registered in 
895
   *                            the DataONE Object Format Vocabulary
896
   * 
897
   * @throws ServiceFailure
898
   * @throws NotImplemented
899
   * @throws InsufficientResources
900
   */
901
  @Override
902
  public ObjectFormatList listFormats() 
903
    throws ServiceFailure, NotImplemented {
904

    
905
    return ObjectFormatService.getInstance().listFormats();
906
  }
907

    
908
  /**
909
   * Returns a list of nodes that have been registered with the DataONE infrastructure
910
    * 
911
   * @return nodeList - List of nodes from the registry
912
   * 
913
   * @throws ServiceFailure
914
   * @throws NotImplemented
915
   */
916
  @Override
917
  public NodeList listNodes() 
918
    throws NotImplemented, ServiceFailure {
919

    
920
    throw new NotImplemented("4800", "listNodes not implemented");
921
  }
922

    
923
  /**
924
   * Provides a mechanism for adding system metadata independently of its 
925
   * associated object, such as when adding system metadata for data objects.
926
    * 
927
   * @param session - the Session object containing the credentials for the Subject
928
   * @param pid - The identifier of the object to register the system metadata against
929
   * @param sysmeta - The system metadata to be registered
930
   * 
931
   * @return true if the registration succeeds
932
   * 
933
   * @throws NotImplemented
934
   * @throws NotAuthorized
935
   * @throws ServiceFailure
936
   * @throws InvalidRequest
937
   * @throws InvalidSystemMetadata
938
   */
939
  @Override
940
  public Identifier registerSystemMetadata(Session session, Identifier pid,
941
      SystemMetadata sysmeta) 
942
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
943
      InvalidSystemMetadata {
944

    
945
      // The lock to be used for this identifier
946
      Lock lock = null;
947

    
948
      // TODO: control who can call this?
949
      if (session == null) {
950
          //TODO: many of the thrown exceptions do not use the correct error codes
951
          //check these against the docs and correct them
952
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
953
                  "  If you are not logged in, please do so and retry the request.");
954
      }
955
      
956
      // verify that guid == SystemMetadata.getIdentifier()
957
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
958
          "|" + sysmeta.getIdentifier().getValue());
959
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
960
          throw new InvalidRequest("4863", 
961
              "The identifier in method call (" + pid.getValue() + 
962
              ") does not match identifier in system metadata (" +
963
              sysmeta.getIdentifier().getValue() + ").");
964
      }
965

    
966
      try {
967
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
968
          lock.lock();
969
          logMetacat.debug("Locked identifier " + pid.getValue());
970
          logMetacat.debug("Checking if identifier exists...");
971
          // Check that the identifier does not already exist
972
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
973
              throw new InvalidRequest("4863", 
974
                  "The identifier is already in use by an existing object.");
975
          
976
          }
977
          
978
          // insert the system metadata into the object store
979
          logMetacat.debug("Starting to insert SystemMetadata...");
980
          try {
981
              sysmeta.setSerialVersion(BigInteger.ONE);
982
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
983
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
984
              
985
          } catch (RuntimeException e) {
986
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
987
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
988
                  e.getClass() + ": " + e.getMessage());
989
              
990
          }
991
          
992
      } catch (RuntimeException e) {
993
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
994
                  e.getClass() + ": " + e.getMessage());
995
          
996
      }  finally {
997
          lock.unlock();
998
          logMetacat.debug("Unlocked identifier " + pid.getValue());
999
          
1000
      }
1001

    
1002
      
1003
      logMetacat.debug("Returning from registerSystemMetadata");
1004
      EventLog.getInstance().log(request.getRemoteAddr(), 
1005
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
1006
          pid.getValue(), "registerSystemMetadata");
1007
      return pid;
1008
  }
1009
  
1010
  /**
1011
   * Given an optional scope and format, reserves and returns an identifier 
1012
   * within that scope and format that is unique and will not be 
1013
   * used by any other sessions. 
1014
    * 
1015
   * @param session - the Session object containing the credentials for the Subject
1016
   * @param pid - The identifier of the object to register the system metadata against
1017
   * @param scope - An optional string to be used to qualify the scope of 
1018
   *                the identifier namespace, which is applied differently 
1019
   *                depending on the format requested. If scope is not 
1020
   *                supplied, a default scope will be used.
1021
   * @param format - The optional name of the identifier format to be used, 
1022
   *                  drawn from a DataONE-specific vocabulary of identifier 
1023
   *                 format names, including several common syntaxes such 
1024
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
1025
   *                 format is not supplied by the caller, the CN service 
1026
   *                 will use a default identifier format, which may change 
1027
   *                 over time.
1028
   * 
1029
   * @return true if the registration succeeds
1030
   * 
1031
   * @throws InvalidToken
1032
   * @throws ServiceFailure
1033
   * @throws NotAuthorized
1034
   * @throws IdentifierNotUnique
1035
   * @throws NotImplemented
1036
   */
1037
  @Override
1038
  public Identifier reserveIdentifier(Session session, Identifier pid)
1039
  throws InvalidToken, ServiceFailure,
1040
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
1041

    
1042
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
1043
  }
1044
  
1045
  @Override
1046
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
1047
  throws InvalidToken, ServiceFailure,
1048
        NotAuthorized, NotImplemented, InvalidRequest {
1049
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
1050
  }
1051
  
1052
  /**
1053
    * Checks whether the pid is reserved by the subject in the session param
1054
    * If the reservation is held on the pid by the subject, we return true.
1055
    * 
1056
   * @param session - the Session object containing the Subject
1057
   * @param pid - The identifier to check
1058
   * 
1059
   * @return true if the reservation exists for the subject/pid
1060
   * 
1061
   * @throws InvalidToken
1062
   * @throws ServiceFailure
1063
   * @throws NotFound - when the pid is not found (in use or in reservation)
1064
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
1065
   * @throws IdentifierNotUnique - when the pid is in use
1066
   * @throws NotImplemented
1067
   */
1068

    
1069
  @Override
1070
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
1071
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
1072
      NotImplemented, InvalidRequest {
1073
  
1074
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1075
  }
1076

    
1077
  /**
1078
   * Changes ownership (RightsHolder) of the specified object to the 
1079
   * subject specified by userId
1080
    * 
1081
   * @param session - the Session object containing the credentials for the Subject
1082
   * @param pid - Identifier of the object to be modified
1083
   * @param userId - The subject that will be taking ownership of the specified object.
1084
   *
1085
   * @return pid - the identifier of the modified object
1086
   * 
1087
   * @throws ServiceFailure
1088
   * @throws InvalidToken
1089
   * @throws NotFound
1090
   * @throws NotAuthorized
1091
   * @throws NotImplemented
1092
   * @throws InvalidRequest
1093
   */  
1094
  @Override
1095
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1096
      long serialVersion)
1097
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1098
      NotImplemented, InvalidRequest, VersionMismatch {
1099
      
1100
      // The lock to be used for this identifier
1101
      Lock lock = null;
1102

    
1103
      // get the subject
1104
      Subject subject = session.getSubject();
1105
      
1106
      // are we allowed to do this?
1107
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1108
          throw new NotAuthorized("4440", "not allowed by "
1109
                  + subject.getValue() + " on " + pid.getValue());
1110
          
1111
      }
1112
      
1113
      SystemMetadata systemMetadata = null;
1114
      try {
1115
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1116
          logMetacat.debug("Locked identifier " + pid.getValue());
1117

    
1118
          try {
1119
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1120
              
1121
              // does the request have the most current system metadata?
1122
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1123
                 String msg = "The requested system metadata version number " + 
1124
                     serialVersion + " differs from the current version at " +
1125
                     systemMetadata.getSerialVersion().longValue() +
1126
                     ". Please get the latest copy in order to modify it.";
1127
                 throw new VersionMismatch("4443", msg);
1128
              }
1129
              
1130
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1131
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1132
              
1133
          }
1134
              
1135
          // set the new rights holder
1136
          systemMetadata.setRightsHolder(userId);
1137
          
1138
          // update the metadata
1139
          try {
1140
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1141
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1142
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1143
              notifyReplicaNodes(systemMetadata);
1144
              
1145
          } catch (RuntimeException e) {
1146
              throw new ServiceFailure("4490", e.getMessage());
1147
          
1148
          }
1149
          
1150
      } catch (RuntimeException e) {
1151
          throw new ServiceFailure("4490", e.getMessage());
1152
          
1153
      } finally {
1154
          lock.unlock();
1155
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1156
      
1157
      }
1158
      
1159
      return pid;
1160
  }
1161

    
1162
  /**
1163
   * Verify that a replication task is authorized by comparing the target node's
1164
   * Subject (from the X.509 certificate-derived Session) with the list of 
1165
   * subjects in the known, pending replication tasks map.
1166
   * 
1167
   * @param originatingNodeSession - Session information that contains the 
1168
   *                                 identity of the calling user
1169
   * @param targetNodeSubject - Subject identifying the target node
1170
   * @param pid - the identifier of the object to be replicated
1171
   * @param replicatePermission - the execute permission to be granted
1172
   * 
1173
   * @throws ServiceFailure
1174
   * @throws NotImplemented
1175
   * @throws InvalidToken
1176
   * @throws NotAuthorized
1177
   * @throws InvalidRequest
1178
   * @throws NotFound
1179
   */
1180
  @Override
1181
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1182
    Subject targetNodeSubject, Identifier pid) 
1183
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1184
    NotFound, InvalidRequest {
1185
    
1186
    boolean isAllowed = false;
1187
    SystemMetadata sysmeta = null;
1188
    NodeReference targetNode = null;
1189
    
1190
    try {
1191
      // get the target node reference from the nodes list
1192
      CNode cn = D1Client.getCN();
1193
      List<Node> nodes = cn.listNodes().getNodeList();
1194
      
1195
      if ( nodes != null ) {
1196
        for (Node node : nodes) {
1197
            
1198
        	if (node.getSubjectList() != null) {
1199
        		
1200
	            for (Subject nodeSubject : node.getSubjectList()) {
1201
	            	
1202
	                if ( nodeSubject.equals(targetNodeSubject) ) {
1203
	                    targetNode = node.getIdentifier();
1204
	                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1205
	                    break;
1206
	                }
1207
	            }
1208
        	}
1209
            
1210
            if ( targetNode != null) { break; }
1211
        }
1212
        
1213
      } else {
1214
          String msg = "Couldn't get the node list from the CN";
1215
          logMetacat.debug(msg);
1216
          throw new ServiceFailure("4872", msg);
1217
          
1218
      }
1219
      
1220
      // can't find a node listed with the given subject
1221
      if ( targetNode == null ) {
1222
          String msg = "There is no Member Node registered with a node subject " +
1223
              "matching " + targetNodeSubject.getValue();
1224
          logMetacat.info(msg);
1225
          throw new NotAuthorized("4871", msg);
1226
          
1227
      }
1228
      
1229
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1230
      
1231
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1232

    
1233
      if ( sysmeta != null ) {
1234
          
1235
          List<Replica> replicaList = sysmeta.getReplicaList();
1236
          
1237
          if ( replicaList != null ) {
1238
              
1239
              // find the replica with the status set to 'requested'
1240
              for (Replica replica : replicaList) {
1241
                  ReplicationStatus status = replica.getReplicationStatus();
1242
                  NodeReference listedNode = replica.getReplicaMemberNode();
1243
                  if ( listedNode != null && targetNode != null ) {
1244
                      logMetacat.debug("Comparing " + listedNode.getValue()
1245
                              + " to " + targetNode.getValue());
1246
                      
1247
                      if (listedNode.getValue().equals(targetNode.getValue())
1248
                              && status.equals(ReplicationStatus.REQUESTED)) {
1249
                          isAllowed = true;
1250
                          break;
1251

    
1252
                      }
1253
                  }
1254
              }
1255
          }
1256
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1257
              "to replicate: " + isAllowed + " for " + pid.getValue());
1258

    
1259
          
1260
      } else {
1261
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1262
          " is null.");          
1263
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1264
          
1265
      }
1266

    
1267
    } catch (RuntimeException e) {
1268
    	  ServiceFailure sf = new ServiceFailure("4872", 
1269
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1270
                e.getMessage());
1271
    	  sf.initCause(e);
1272
        throw sf;
1273
        
1274
    }
1275
      
1276
    return isAllowed;
1277
    
1278
  }
1279

    
1280
  /**
1281
   * Adds a new object to the Node, where the object is a science metadata object.
1282
   * 
1283
   * @param session - the Session object containing the credentials for the Subject
1284
   * @param pid - The object identifier to be created
1285
   * @param object - the object bytes
1286
   * @param sysmeta - the system metadata that describes the object  
1287
   * 
1288
   * @return pid - the object identifier created
1289
   * 
1290
   * @throws InvalidToken
1291
   * @throws ServiceFailure
1292
   * @throws NotAuthorized
1293
   * @throws IdentifierNotUnique
1294
   * @throws UnsupportedType
1295
   * @throws InsufficientResources
1296
   * @throws InvalidSystemMetadata
1297
   * @throws NotImplemented
1298
   * @throws InvalidRequest
1299
   */
1300
  public Identifier create(Session session, Identifier pid, InputStream object,
1301
    SystemMetadata sysmeta) 
1302
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1303
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1304
    NotImplemented, InvalidRequest {
1305
                  
1306
      // The lock to be used for this identifier
1307
      Lock lock = null;
1308

    
1309
      try {
1310
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1311
          // are we allowed?
1312
          boolean isAllowed = false;
1313
          isAllowed = isAdminAuthorized(session);
1314

    
1315
          // proceed if we're called by a CN
1316
          if ( isAllowed ) {
1317
              // create the coordinating node version of the document      
1318
              lock.lock();
1319
              logMetacat.debug("Locked identifier " + pid.getValue());
1320
              sysmeta.setSerialVersion(BigInteger.ONE);
1321
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1322
              sysmeta.setArchived(false); // this is a create op, not update
1323
              
1324
              // the CN should have set the origin and authoritative member node fields
1325
              try {
1326
                  sysmeta.getOriginMemberNode().getValue();
1327
                  sysmeta.getAuthoritativeMemberNode().getValue();
1328
                  
1329
              } catch (NullPointerException npe) {
1330
                  throw new InvalidSystemMetadata("4896", 
1331
                      "Both the origin and authoritative member node identifiers need to be set.");
1332
                  
1333
              }
1334
              pid = super.create(session, pid, object, sysmeta);
1335

    
1336
          } else {
1337
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1338
                  " isn't allowed to call create() on a Coordinating Node.";
1339
              logMetacat.info(msg);
1340
              throw new NotAuthorized("1100", msg);
1341
          }
1342
          
1343
      } catch (RuntimeException e) {
1344
          // Convert Hazelcast runtime exceptions to service failures
1345
          String msg = "There was a problem creating the object identified by " +
1346
              pid.getValue() + ". There error message was: " + e.getMessage();
1347
          throw new ServiceFailure("4893", msg);
1348
          
1349
      } finally {
1350
    	  if (lock != null) {
1351
	          lock.unlock();
1352
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1353
    	  }
1354
      }
1355
      
1356
      return pid;
1357

    
1358
  }
1359

    
1360
  /**
1361
   * Set access for a given object using the object identifier and a Subject
1362
   * under a given Session.
1363
   * 
1364
   * @param session - the Session object containing the credentials for the Subject
1365
   * @param pid - the object identifier for the given object to apply the policy
1366
   * @param policy - the access policy to be applied
1367
   * 
1368
   * @return true if the application of the policy succeeds
1369
   * @throws InvalidToken
1370
   * @throws ServiceFailure
1371
   * @throws NotFound
1372
   * @throws NotAuthorized
1373
   * @throws NotImplemented
1374
   * @throws InvalidRequest
1375
   */
1376
  public boolean setAccessPolicy(Session session, Identifier pid, 
1377
      AccessPolicy accessPolicy, long serialVersion) 
1378
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1379
      NotImplemented, InvalidRequest, VersionMismatch {
1380
      
1381
      // The lock to be used for this identifier
1382
      Lock lock = null;
1383
      SystemMetadata systemMetadata = null;
1384
      
1385
      boolean success = false;
1386
      
1387
      // get the subject
1388
      Subject subject = session.getSubject();
1389
      
1390
      // are we allowed to do this?
1391
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1392
          throw new NotAuthorized("4420", "not allowed by "
1393
                  + subject.getValue() + " on " + pid.getValue());
1394
      }
1395
      
1396
      try {
1397
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1398
          lock.lock();
1399
          logMetacat.debug("Locked identifier " + pid.getValue());
1400

    
1401
          try {
1402
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1403

    
1404
              if ( systemMetadata == null ) {
1405
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1406
                  
1407
              }
1408
              // does the request have the most current system metadata?
1409
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1410
                 String msg = "The requested system metadata version number " + 
1411
                     serialVersion + " differs from the current version at " +
1412
                     systemMetadata.getSerialVersion().longValue() +
1413
                     ". Please get the latest copy in order to modify it.";
1414
                 throw new VersionMismatch("4402", msg);
1415
                 
1416
              }
1417
              
1418
          } catch (RuntimeException e) {
1419
              // convert Hazelcast RuntimeException to NotFound
1420
              throw new NotFound("4400", "No record found for: " + pid);
1421
            
1422
          }
1423
              
1424
          // set the access policy
1425
          systemMetadata.setAccessPolicy(accessPolicy);
1426
          
1427
          // update the system metadata
1428
          try {
1429
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1430
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1431
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1432
              notifyReplicaNodes(systemMetadata);
1433
              
1434
          } catch (RuntimeException e) {
1435
              // convert Hazelcast RuntimeException to ServiceFailure
1436
              throw new ServiceFailure("4430", e.getMessage());
1437
            
1438
          }
1439
          
1440
      } catch (RuntimeException e) {
1441
          throw new ServiceFailure("4430", e.getMessage());
1442
          
1443
      } finally {
1444
          lock.unlock();
1445
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1446
        
1447
      }
1448

    
1449
    
1450
    // TODO: how do we know if the map was persisted?
1451
    success = true;
1452
    
1453
    return success;
1454
  }
1455

    
1456
  /**
1457
   * Full replacement of replication metadata in the system metadata for the 
1458
   * specified object, changes date system metadata modified
1459
   * 
1460
   * @param session - the Session object containing the credentials for the Subject
1461
   * @param pid - the object identifier for the given object to apply the policy
1462
   * @param replica - the replica to be updated
1463
   * @return
1464
   * @throws NotImplemented
1465
   * @throws NotAuthorized
1466
   * @throws ServiceFailure
1467
   * @throws InvalidRequest
1468
   * @throws NotFound
1469
   * @throws VersionMismatch
1470
   */
1471
  @Override
1472
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1473
      Replica replica, long serialVersion) 
1474
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1475
      NotFound, VersionMismatch {
1476
      
1477
      // The lock to be used for this identifier
1478
      Lock lock = null;
1479
      
1480
      // get the subject
1481
      Subject subject = session.getSubject();
1482
      
1483
      // are we allowed to do this?
1484
      try {
1485

    
1486
          // what is the controlling permission?
1487
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1488
              throw new NotAuthorized("4851", "not allowed by "
1489
                      + subject.getValue() + " on " + pid.getValue());
1490
          }
1491

    
1492
        
1493
      } catch (InvalidToken e) {
1494
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1495
                  " on " + pid.getValue());  
1496
          
1497
      }
1498

    
1499
      SystemMetadata systemMetadata = null;
1500
      try {
1501
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1502
          lock.lock();
1503
          logMetacat.debug("Locked identifier " + pid.getValue());
1504

    
1505
          try {      
1506
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1507

    
1508
              // does the request have the most current system metadata?
1509
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1510
                 String msg = "The requested system metadata version number " + 
1511
                     serialVersion + " differs from the current version at " +
1512
                     systemMetadata.getSerialVersion().longValue() +
1513
                     ". Please get the latest copy in order to modify it.";
1514
                 throw new VersionMismatch("4855", msg);
1515
              }
1516
              
1517
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1518
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1519
                  " : " + e.getMessage());
1520
            
1521
          }
1522
              
1523
          // set the status for the replica
1524
          List<Replica> replicas = systemMetadata.getReplicaList();
1525
          NodeReference replicaNode = replica.getReplicaMemberNode();
1526
          int index = 0;
1527
          for (Replica listedReplica: replicas) {
1528
              
1529
              // remove the replica that we are replacing
1530
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1531
                  replicas.remove(index);
1532
                  break;
1533
                  
1534
              }
1535
              index++;
1536
          }
1537
          
1538
          // add the new replica item
1539
          replicas.add(replica);
1540
          systemMetadata.setReplicaList(replicas);
1541
          
1542
          // update the metadata
1543
          try {
1544
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1545
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1546
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1547
            
1548
          } catch (RuntimeException e) {
1549
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1550
              throw new ServiceFailure("4852", e.getMessage());
1551
          
1552
          }
1553
          
1554
      } catch (RuntimeException e) {
1555
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1556
          throw new ServiceFailure("4852", e.getMessage());
1557
      
1558
      } finally {
1559
          lock.unlock();
1560
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1561
          
1562
      }
1563
    
1564
      return true;
1565
      
1566
  }
1567
  
1568
  /**
1569
   * 
1570
   */
1571
  @Override
1572
  public ObjectList listObjects(Session session, Date startTime, 
1573
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1574
      Integer start, Integer count)
1575
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1576
      ServiceFailure {
1577
      
1578
      ObjectList objectList = null;
1579
        try {
1580
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1581
        } catch (Exception e) {
1582
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1583
        }
1584

    
1585
        return objectList;
1586
  }
1587

    
1588
  
1589
 	/**
1590
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1591
 	 * @return cal  the list of checksum algorithms
1592
 	 * 
1593
 	 * @throws ServiceFailure
1594
 	 * @throws NotImplemented
1595
 	 */
1596
  @Override
1597
  public ChecksumAlgorithmList listChecksumAlgorithms()
1598
			throws ServiceFailure, NotImplemented {
1599
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1600
		cal.addAlgorithm("MD5");
1601
		cal.addAlgorithm("SHA-1");
1602
		return cal;
1603
		
1604
	}
1605

    
1606
  /**
1607
   * Notify replica Member Nodes of system metadata changes for a given pid
1608
   * 
1609
   * @param currentSystemMetadata - the up to date system metadata
1610
   */
1611
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1612
      
1613
      Session session = null;
1614
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1615
      MNode mn = null;
1616
      NodeReference replicaNodeRef = null;
1617
      CNode cn = null;
1618
      NodeType nodeType = null;
1619
      List<Node> nodeList = null;
1620
      
1621
      try {
1622
          cn = D1Client.getCN();
1623
          nodeList = cn.listNodes().getNodeList();
1624
          
1625
      } catch (Exception e) { // handle BaseException and other I/O issues
1626
          
1627
          // swallow errors since the call is not critical
1628
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1629
              "to communication issues with the CN: " + e.getMessage());
1630
          
1631
      }
1632
      
1633
      if ( replicaList != null ) {
1634
          
1635
          // iterate through the replicas and inform  MN replica nodes
1636
          for (Replica replica : replicaList) {
1637
              
1638
              replicaNodeRef = replica.getReplicaMemberNode();
1639
              try {
1640
                  if (nodeList != null) {
1641
                      // find the node type
1642
                      for (Node node : nodeList) {
1643
                          if (node.getIdentifier().getValue() == 
1644
                              replicaNodeRef.getValue()) {
1645
                              nodeType = node.getType();
1646
                              break;
1647
              
1648
                          }
1649
                      }
1650
                  }
1651
              
1652
                  // notify only MNs
1653
                  if (nodeType != null && nodeType == NodeType.MN) {
1654
                      mn = D1Client.getMN(replicaNodeRef);
1655
                      mn.systemMetadataChanged(session, 
1656
                          currentSystemMetadata.getIdentifier(), 
1657
                          currentSystemMetadata.getSerialVersion().longValue(),
1658
                          currentSystemMetadata.getDateSysMetadataModified());
1659
                  }
1660
              
1661
              } catch (Exception e) { // handle BaseException and other I/O issues
1662
              
1663
                  // swallow errors since the call is not critical
1664
                  logMetacat.error("Can't inform "
1665
                          + replicaNodeRef.getValue()
1666
                          + " of system metadata changes due "
1667
                          + "to communication issues with the CN: "
1668
                          + e.getMessage());
1669
              
1670
              }
1671
          }
1672
      }
1673
  }
1674

    
1675
	@Override
1676
	public boolean isAuthorized(Identifier pid, Permission permission)
1677
			throws ServiceFailure, InvalidToken, NotFound, NotAuthorized,
1678
			NotImplemented, InvalidRequest {
1679
		
1680
		return isAuthorized(null, pid, permission);
1681
	}
1682
	
1683
	@Override
1684
	public boolean setAccessPolicy(Identifier pid, AccessPolicy accessPolicy, long serialVersion)
1685
			throws InvalidToken, NotFound, NotImplemented, NotAuthorized,
1686
			ServiceFailure, InvalidRequest, VersionMismatch {
1687
		
1688
		return setAccessPolicy(null, pid, accessPolicy, serialVersion);
1689
	}
1690
	
1691
	@Override
1692
	public Identifier setRightsHolder(Identifier pid, Subject userId, long serialVersion)
1693
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1694
			NotImplemented, InvalidRequest, VersionMismatch {
1695
		
1696
		return setRightsHolder(null, pid, userId, serialVersion);
1697
	}
1698
	
1699
	@Override
1700
	public Identifier create(Identifier pid, InputStream object, SystemMetadata sysmeta)
1701
			throws InvalidToken, ServiceFailure, NotAuthorized,
1702
			IdentifierNotUnique, UnsupportedType, InsufficientResources,
1703
			InvalidSystemMetadata, NotImplemented, InvalidRequest {
1704

    
1705
		return create(null, pid, object, sysmeta);
1706
	}
1707
	
1708
	@Override
1709
	public Identifier delete(Identifier pid) throws InvalidToken, ServiceFailure,
1710
			InvalidRequest, NotAuthorized, NotFound, NotImplemented {
1711

    
1712
		return delete(null, pid);
1713
	}
1714
	
1715
	@Override
1716
	public Identifier generateIdentifier(String scheme, String fragment)
1717
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1718
			InvalidRequest {
1719

    
1720
		return generateIdentifier(null, scheme, fragment);
1721
	}
1722
	
1723
	@Override
1724
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1725
			Integer start, Integer count) throws InvalidToken, InvalidRequest,
1726
			ServiceFailure, NotAuthorized, NotImplemented, InsufficientResources {
1727

    
1728
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1729
	}
1730
	
1731
	@Override
1732
	public boolean hasReservation(Subject subject, Identifier pid)
1733
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1734
			NotImplemented, InvalidRequest, IdentifierNotUnique {
1735

    
1736
		return hasReservation(null, subject, pid);
1737
	}
1738
	
1739
	@Override
1740
	public Identifier registerSystemMetadata(Identifier pid, SystemMetadata sysmeta)
1741
			throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1742
			InvalidSystemMetadata, InvalidToken {
1743

    
1744
		return registerSystemMetadata(null, pid, sysmeta);
1745
	}
1746
	
1747
	@Override
1748
	public Identifier reserveIdentifier(Identifier pid) throws InvalidToken,
1749
			ServiceFailure, NotAuthorized, IdentifierNotUnique, NotImplemented,
1750
			InvalidRequest {
1751

    
1752
		return reserveIdentifier(null, pid);
1753
	}
1754
	
1755
	@Override
1756
	public boolean setObsoletedBy(Identifier pid, Identifier obsoletedByPid, long serialVersion)
1757
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
1758
			InvalidRequest, InvalidToken, VersionMismatch {
1759

    
1760
		return setObsoletedBy(null, pid, obsoletedByPid, serialVersion);
1761
	}
1762
	
1763
	@Override
1764
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1765
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1766

    
1767
		return describe(null, pid);
1768
	}
1769
	
1770
	@Override
1771
	public InputStream get(Identifier pid) throws InvalidToken, ServiceFailure,
1772
			NotAuthorized, NotFound, NotImplemented {
1773

    
1774
		return get(null, pid);
1775
	}
1776
	
1777
	@Override
1778
	public Checksum getChecksum(Identifier pid) throws InvalidToken,
1779
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1780

    
1781
		return getChecksum(null, pid);
1782
	}
1783
	
1784
	@Override
1785
	public SystemMetadata getSystemMetadata(Identifier pid) throws InvalidToken,
1786
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1787

    
1788
		return getSystemMetadata(null, pid);
1789
	}
1790
	
1791
	@Override
1792
	public ObjectList listObjects(Date startTime, Date endTime,
1793
			ObjectFormatIdentifier formatid, Boolean replicaStatus, Integer start, Integer count)
1794
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1795
			ServiceFailure {
1796

    
1797
		return listObjects(null, startTime, endTime, formatid, replicaStatus, start, count);
1798
	}
1799
	
1800
	@Override
1801
	public ObjectLocationList resolve(Identifier pid) throws InvalidToken,
1802
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1803

    
1804
		return resolve(null, pid);
1805
	}
1806
	
1807
	@Override
1808
	public ObjectList search(String queryType, String query) throws InvalidToken,
1809
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
1810

    
1811
		return search(null, queryType, query);
1812
	}
1813
	
1814
	@Override
1815
	public boolean deleteReplicationMetadata(Identifier pid, NodeReference nodeId,
1816
			long serialVersion) throws InvalidToken, InvalidRequest, ServiceFailure,
1817
			NotAuthorized, NotFound, NotImplemented, VersionMismatch {
1818

    
1819
		return deleteReplicationMetadata(null, pid, nodeId, serialVersion);
1820
	}
1821
	
1822
	@Override
1823
	public boolean isNodeAuthorized(Subject targetNodeSubject, Identifier pid)
1824
			throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
1825
			NotFound, InvalidRequest {
1826

    
1827
		return isNodeAuthorized(null, targetNodeSubject, pid);
1828
	}
1829
	
1830
	@Override
1831
	public boolean setReplicationPolicy(Identifier pid, ReplicationPolicy policy,
1832
			long serialVersion) throws NotImplemented, NotFound, NotAuthorized,
1833
			ServiceFailure, InvalidRequest, InvalidToken, VersionMismatch {
1834

    
1835
		return setReplicationPolicy(null, pid, policy, serialVersion);
1836
	}
1837
	
1838
	@Override
1839
	public boolean setReplicationStatus(Identifier pid, NodeReference targetNode,
1840
			ReplicationStatus status, BaseException failure) throws ServiceFailure,
1841
			NotImplemented, InvalidToken, NotAuthorized, InvalidRequest, NotFound {
1842

    
1843
		return setReplicationStatus(null, pid, targetNode, status, failure);
1844
	}
1845
	
1846
	@Override
1847
	public boolean updateReplicationMetadata(Identifier pid, Replica replica,
1848
			long serialVersion) throws NotImplemented, NotAuthorized, ServiceFailure,
1849
			NotFound, InvalidRequest, InvalidToken, VersionMismatch {
1850

    
1851
		return updateReplicationMetadata(null, pid, replica, serialVersion);
1852
	}
1853
}
(1-1/5)