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, INVALIDATED,
622
                              // and FAILED status updates from MNs only
623
                              if ( status == ReplicationStatus.COMPLETED ||
624
                                   status == ReplicationStatus.INVALIDATED ||
625
                                   status == ReplicationStatus.FAILED) {
626
                                  allowed = true;
627
                                  break;
628
                                  
629
                              }                              
630
                          }
631
                      }                 
632
                  }
633
              }
634

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

    
652
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
653
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
654
                " : " + e.getMessage());
655
            
656
          }
657
          
658
          Replica targetReplica = new Replica();
659
          // set the status for the replica
660
          if ( replicaEntryIndex != -1 ) {
661
              targetReplica = replicas.get(replicaEntryIndex);
662
              
663
              // don't allow status to change from COMPLETED to anything other
664
              // than INVALIDATED: prevents overwrites from race conditions
665
              if ( targetReplica.getReplicationStatus() == ReplicationStatus.COMPLETED &&
666
            	   status != ReplicationStatus.INVALIDATED) {
667
            	  throw new InvalidRequest("4730", "Status state change from " +
668
            			  targetReplica.getReplicationStatus() + " to " +
669
            			  status.toString() + "is prohibited for identifier " +
670
            			  pid.getValue() + " and target node " + 
671
            			  targetReplica.getReplicaMemberNode().getValue());
672
              }
673
              
674
              targetReplica.setReplicationStatus(status);
675
              logMetacat.debug("Set the replication status for " + 
676
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
677
                  targetReplica.getReplicationStatus() + " for identifier " +
678
                  pid.getValue());
679
              
680
          } else {
681
              // this is a new entry, create it
682
              targetReplica.setReplicaMemberNode(targetNode);
683
              targetReplica.setReplicationStatus(status);
684
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
685
              replicas.add(targetReplica);
686
              
687
          }
688
          
689
          systemMetadata.setReplicaList(replicas);
690
                
691
          // update the metadata
692
          try {
693
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
694
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
695
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
696

    
697
              if ( status != ReplicationStatus.QUEUED && status != ReplicationStatus.REQUESTED) {
698
                  
699
                logMetacat.trace("METRICS:\tREPLICATION:\tEND REQUEST:\tPID:\t" + pid.getValue() + 
700
                          "\tNODE:\t" + targetNode.getValue() + 
701
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
702
                
703
                logMetacat.trace("METRICS:\tREPLICATION:\t" + status.toString().toUpperCase() +
704
                          "\tPID:\t"  + pid.getValue() + 
705
                          "\tNODE:\t" + targetNode.getValue() + 
706
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
707
              }
708

    
709
              if ( status == ReplicationStatus.FAILED && failure != null ) {
710
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
711
                      " on target node " + targetNode + ". The exception was: " +
712
                      failure.getMessage());
713
              }
714
          } catch (RuntimeException e) {
715
              throw new ServiceFailure("4700", e.getMessage());
716
          
717
          }
718
          
719
    } catch (RuntimeException e) {
720
        String msg = "There was a RuntimeException getting the lock for " +
721
            pid.getValue();
722
        logMetacat.info(msg);
723
        
724
    } finally {
725
        lock.unlock();
726
        logMetacat.debug("Unlocked identifier " + pid.getValue());
727
        
728
    }
729
      
730
      return true;
731
  }
732
  
733
  /**
734
   * Return the checksum of the object given the identifier 
735
   * 
736
   * @param session - the Session object containing the credentials for the Subject
737
   * @param pid - the object identifier for the given object
738
   * 
739
   * @return checksum - the checksum of the object
740
   * 
741
   * @throws InvalidToken
742
   * @throws ServiceFailure
743
   * @throws NotAuthorized
744
   * @throws NotFound
745
   * @throws NotImplemented
746
   */
747
  @Override
748
  public Checksum getChecksum(Session session, Identifier pid)
749
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
750
    NotImplemented {
751
    
752
	boolean isAuthorized = false;
753
	try {
754
		isAuthorized = isAuthorized(session, pid, Permission.READ);
755
	} catch (InvalidRequest e) {
756
		throw new ServiceFailure("1410", e.getDescription());
757
	}  
758
    if (!isAuthorized) {
759
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
760
    }
761
    
762
    SystemMetadata systemMetadata = null;
763
    Checksum checksum = null;
764
    
765
    try {
766
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
767

    
768
        if (systemMetadata == null ) {
769
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
770
        }
771
        checksum = systemMetadata.getChecksum();
772
        
773
    } catch (RuntimeException e) {
774
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
775
            pid.getValue() + ". The error message was: " + e.getMessage());
776
      
777
    }
778
    
779
    return checksum;
780
  }
781

    
782
  /**
783
   * Resolve the location of a given object
784
   * 
785
   * @param session - the Session object containing the credentials for the Subject
786
   * @param pid - the object identifier for the given object
787
   * 
788
   * @return objectLocationList - the list of nodes known to contain the object
789
   * 
790
   * @throws InvalidToken
791
   * @throws ServiceFailure
792
   * @throws NotAuthorized
793
   * @throws NotFound
794
   * @throws NotImplemented
795
   */
796
  @Override
797
  public ObjectLocationList resolve(Session session, Identifier pid)
798
    throws InvalidToken, ServiceFailure, NotAuthorized,
799
    NotFound, NotImplemented {
800

    
801
    throw new NotImplemented("4131", "resolve not implemented");
802

    
803
  }
804

    
805
  /**
806
   * Search the metadata catalog for identifiers that match the criteria
807
   * 
808
   * @param session - the Session object containing the credentials for the Subject
809
   * @param queryType - An identifier for the type of query expression 
810
   *                    provided in the query
811
   * @param query -  The criteria for matching the characteristics of the 
812
   *                 metadata objects of interest
813
   * 
814
   * @return objectList - the list of objects matching the criteria
815
   * 
816
   * @throws InvalidToken
817
   * @throws ServiceFailure
818
   * @throws NotAuthorized
819
   * @throws InvalidRequest
820
   * @throws NotImplemented
821
   */
822
  @Override
823
  public ObjectList search(Session session, String queryType, String query)
824
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
825
    NotImplemented {
826

    
827
    ObjectList objectList = null;
828
    try {
829
        objectList = 
830
          IdentifierManager.getInstance().querySystemMetadata(
831
              null, //startTime, 
832
              null, //endTime,
833
              null, //objectFormat, 
834
              false, //replicaStatus, 
835
              0, //start, 
836
              -1 //count
837
              );
838
        
839
    } catch (Exception e) {
840
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
841
    }
842

    
843
      return objectList;
844
      
845
    //throw new NotImplemented("4281", "search not implemented");
846
    
847
    // the code block below is from an older implementation
848
    
849
    /*  This block commented out because of the EcoGrid circular dependency.
850
         *  For now, query will not be supported until the circularity can be
851
         *  resolved, probably by moving the ecogrid query syntax transformers
852
         *  directly into the Metacat codebase.  MBJ 2010-02-03
853
         
854
        try {
855
            EcogridQueryParser parser = new EcogridQueryParser(request
856
                    .getReader());
857
            parser.parseXML();
858
            QueryType queryType = parser.getEcogridQuery();
859
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
860
                new EcogridJavaToMetacatJavaQueryTransformer();
861
            QuerySpecification metacatQuery = queryTransformer
862
                    .transform(queryType);
863

    
864
            DBQuery metacat = new DBQuery();
865

    
866
            boolean useXMLIndex = (new Boolean(PropertyService
867
                    .getProperty("database.usexmlindex"))).booleanValue();
868
            String xmlquery = "query"; // we don't care the query in resultset,
869
            // the query can be anything
870
            PrintWriter out = null; // we don't want metacat result, so set out null
871

    
872
            // parameter: queryspecification, user, group, usingIndexOrNot
873
            StringBuffer result = metacat.createResultDocument(xmlquery,
874
                    metacatQuery, out, username, groupNames, useXMLIndex);
875

    
876
            // create result set transfer       
877
            String saxparser = PropertyService.getProperty("xml.saxparser");
878
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
879
                    new StringReader(result.toString()), saxparser, queryType
880
                            .getNamespace().get_value());
881
            ResultsetType records = metacatResultsetParser.getEcogridResult();
882

    
883
            System.out
884
                    .println(EcogridResultsetTransformer.toXMLString(records));
885
            response.setContentType("text/xml");
886
            out = response.getWriter();
887
            out.print(EcogridResultsetTransformer.toXMLString(records));
888

    
889
        } catch (Exception e) {
890
            e.printStackTrace();
891
        }*/
892
    
893

    
894
  }
895
  
896
  /**
897
   * Returns the object format registered in the DataONE Object Format 
898
   * Vocabulary for the given format identifier
899
   * 
900
   * @param fmtid - the identifier of the format requested
901
   * 
902
   * @return objectFormat - the object format requested
903
   * 
904
   * @throws ServiceFailure
905
   * @throws NotFound
906
   * @throws InsufficientResources
907
   * @throws NotImplemented
908
   */
909
  @Override
910
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
911
    throws ServiceFailure, NotFound, NotImplemented {
912
     
913
      return ObjectFormatService.getInstance().getFormat(fmtid);
914
      
915
  }
916

    
917
  /**
918
   * Returns a list of all object formats registered in the DataONE Object 
919
   * Format Vocabulary
920
    * 
921
   * @return objectFormatList - The list of object formats registered in 
922
   *                            the DataONE Object Format Vocabulary
923
   * 
924
   * @throws ServiceFailure
925
   * @throws NotImplemented
926
   * @throws InsufficientResources
927
   */
928
  @Override
929
  public ObjectFormatList listFormats() 
930
    throws ServiceFailure, NotImplemented {
931

    
932
    return ObjectFormatService.getInstance().listFormats();
933
  }
934

    
935
  /**
936
   * Returns a list of nodes that have been registered with the DataONE infrastructure
937
    * 
938
   * @return nodeList - List of nodes from the registry
939
   * 
940
   * @throws ServiceFailure
941
   * @throws NotImplemented
942
   */
943
  @Override
944
  public NodeList listNodes() 
945
    throws NotImplemented, ServiceFailure {
946

    
947
    throw new NotImplemented("4800", "listNodes not implemented");
948
  }
949

    
950
  /**
951
   * Provides a mechanism for adding system metadata independently of its 
952
   * associated object, such as when adding system metadata for data objects.
953
    * 
954
   * @param session - the Session object containing the credentials for the Subject
955
   * @param pid - The identifier of the object to register the system metadata against
956
   * @param sysmeta - The system metadata to be registered
957
   * 
958
   * @return true if the registration succeeds
959
   * 
960
   * @throws NotImplemented
961
   * @throws NotAuthorized
962
   * @throws ServiceFailure
963
   * @throws InvalidRequest
964
   * @throws InvalidSystemMetadata
965
   */
966
  @Override
967
  public Identifier registerSystemMetadata(Session session, Identifier pid,
968
      SystemMetadata sysmeta) 
969
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
970
      InvalidSystemMetadata {
971

    
972
      // The lock to be used for this identifier
973
      Lock lock = null;
974

    
975
      // TODO: control who can call this?
976
      if (session == null) {
977
          //TODO: many of the thrown exceptions do not use the correct error codes
978
          //check these against the docs and correct them
979
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
980
                  "  If you are not logged in, please do so and retry the request.");
981
      }
982
      
983
      // verify that guid == SystemMetadata.getIdentifier()
984
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
985
          "|" + sysmeta.getIdentifier().getValue());
986
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
987
          throw new InvalidRequest("4863", 
988
              "The identifier in method call (" + pid.getValue() + 
989
              ") does not match identifier in system metadata (" +
990
              sysmeta.getIdentifier().getValue() + ").");
991
      }
992

    
993
      try {
994
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
995
          lock.lock();
996
          logMetacat.debug("Locked identifier " + pid.getValue());
997
          logMetacat.debug("Checking if identifier exists...");
998
          // Check that the identifier does not already exist
999
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
1000
              throw new InvalidRequest("4863", 
1001
                  "The identifier is already in use by an existing object.");
1002
          
1003
          }
1004
          
1005
          // insert the system metadata into the object store
1006
          logMetacat.debug("Starting to insert SystemMetadata...");
1007
          try {
1008
              sysmeta.setSerialVersion(BigInteger.ONE);
1009
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1010
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
1011
              
1012
          } catch (RuntimeException e) {
1013
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
1014
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
1015
                  e.getClass() + ": " + e.getMessage());
1016
              
1017
          }
1018
          
1019
      } catch (RuntimeException e) {
1020
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
1021
                  e.getClass() + ": " + e.getMessage());
1022
          
1023
      }  finally {
1024
          lock.unlock();
1025
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1026
          
1027
      }
1028

    
1029
      
1030
      logMetacat.debug("Returning from registerSystemMetadata");
1031
      EventLog.getInstance().log(request.getRemoteAddr(), 
1032
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
1033
          pid.getValue(), "registerSystemMetadata");
1034
      return pid;
1035
  }
1036
  
1037
  /**
1038
   * Given an optional scope and format, reserves and returns an identifier 
1039
   * within that scope and format that is unique and will not be 
1040
   * used by any other sessions. 
1041
    * 
1042
   * @param session - the Session object containing the credentials for the Subject
1043
   * @param pid - The identifier of the object to register the system metadata against
1044
   * @param scope - An optional string to be used to qualify the scope of 
1045
   *                the identifier namespace, which is applied differently 
1046
   *                depending on the format requested. If scope is not 
1047
   *                supplied, a default scope will be used.
1048
   * @param format - The optional name of the identifier format to be used, 
1049
   *                  drawn from a DataONE-specific vocabulary of identifier 
1050
   *                 format names, including several common syntaxes such 
1051
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
1052
   *                 format is not supplied by the caller, the CN service 
1053
   *                 will use a default identifier format, which may change 
1054
   *                 over time.
1055
   * 
1056
   * @return true if the registration succeeds
1057
   * 
1058
   * @throws InvalidToken
1059
   * @throws ServiceFailure
1060
   * @throws NotAuthorized
1061
   * @throws IdentifierNotUnique
1062
   * @throws NotImplemented
1063
   */
1064
  @Override
1065
  public Identifier reserveIdentifier(Session session, Identifier pid)
1066
  throws InvalidToken, ServiceFailure,
1067
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
1068

    
1069
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
1070
  }
1071
  
1072
  @Override
1073
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
1074
  throws InvalidToken, ServiceFailure,
1075
        NotAuthorized, NotImplemented, InvalidRequest {
1076
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
1077
  }
1078
  
1079
  /**
1080
    * Checks whether the pid is reserved by the subject in the session param
1081
    * If the reservation is held on the pid by the subject, we return true.
1082
    * 
1083
   * @param session - the Session object containing the Subject
1084
   * @param pid - The identifier to check
1085
   * 
1086
   * @return true if the reservation exists for the subject/pid
1087
   * 
1088
   * @throws InvalidToken
1089
   * @throws ServiceFailure
1090
   * @throws NotFound - when the pid is not found (in use or in reservation)
1091
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
1092
   * @throws IdentifierNotUnique - when the pid is in use
1093
   * @throws NotImplemented
1094
   */
1095

    
1096
  @Override
1097
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
1098
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
1099
      NotImplemented, InvalidRequest {
1100
  
1101
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1102
  }
1103

    
1104
  /**
1105
   * Changes ownership (RightsHolder) of the specified object to the 
1106
   * subject specified by userId
1107
    * 
1108
   * @param session - the Session object containing the credentials for the Subject
1109
   * @param pid - Identifier of the object to be modified
1110
   * @param userId - The subject that will be taking ownership of the specified object.
1111
   *
1112
   * @return pid - the identifier of the modified object
1113
   * 
1114
   * @throws ServiceFailure
1115
   * @throws InvalidToken
1116
   * @throws NotFound
1117
   * @throws NotAuthorized
1118
   * @throws NotImplemented
1119
   * @throws InvalidRequest
1120
   */  
1121
  @Override
1122
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1123
      long serialVersion)
1124
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1125
      NotImplemented, InvalidRequest, VersionMismatch {
1126
      
1127
      // The lock to be used for this identifier
1128
      Lock lock = null;
1129

    
1130
      // get the subject
1131
      Subject subject = session.getSubject();
1132
      
1133
      // are we allowed to do this?
1134
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1135
          throw new NotAuthorized("4440", "not allowed by "
1136
                  + subject.getValue() + " on " + pid.getValue());
1137
          
1138
      }
1139
      
1140
      SystemMetadata systemMetadata = null;
1141
      try {
1142
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1143
          logMetacat.debug("Locked identifier " + pid.getValue());
1144

    
1145
          try {
1146
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1147
              
1148
              // does the request have the most current system metadata?
1149
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1150
                 String msg = "The requested system metadata version number " + 
1151
                     serialVersion + " differs from the current version at " +
1152
                     systemMetadata.getSerialVersion().longValue() +
1153
                     ". Please get the latest copy in order to modify it.";
1154
                 throw new VersionMismatch("4443", msg);
1155
              }
1156
              
1157
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1158
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1159
              
1160
          }
1161
              
1162
          // set the new rights holder
1163
          systemMetadata.setRightsHolder(userId);
1164
          
1165
          // update the metadata
1166
          try {
1167
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1168
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1169
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1170
              notifyReplicaNodes(systemMetadata);
1171
              
1172
          } catch (RuntimeException e) {
1173
              throw new ServiceFailure("4490", e.getMessage());
1174
          
1175
          }
1176
          
1177
      } catch (RuntimeException e) {
1178
          throw new ServiceFailure("4490", e.getMessage());
1179
          
1180
      } finally {
1181
          lock.unlock();
1182
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1183
      
1184
      }
1185
      
1186
      return pid;
1187
  }
1188

    
1189
  /**
1190
   * Verify that a replication task is authorized by comparing the target node's
1191
   * Subject (from the X.509 certificate-derived Session) with the list of 
1192
   * subjects in the known, pending replication tasks map.
1193
   * 
1194
   * @param originatingNodeSession - Session information that contains the 
1195
   *                                 identity of the calling user
1196
   * @param targetNodeSubject - Subject identifying the target node
1197
   * @param pid - the identifier of the object to be replicated
1198
   * @param replicatePermission - the execute permission to be granted
1199
   * 
1200
   * @throws ServiceFailure
1201
   * @throws NotImplemented
1202
   * @throws InvalidToken
1203
   * @throws NotAuthorized
1204
   * @throws InvalidRequest
1205
   * @throws NotFound
1206
   */
1207
  @Override
1208
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1209
    Subject targetNodeSubject, Identifier pid) 
1210
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1211
    NotFound, InvalidRequest {
1212
    
1213
    boolean isAllowed = false;
1214
    SystemMetadata sysmeta = null;
1215
    NodeReference targetNode = null;
1216
    
1217
    try {
1218
      // get the target node reference from the nodes list
1219
      CNode cn = D1Client.getCN();
1220
      List<Node> nodes = cn.listNodes().getNodeList();
1221
      
1222
      if ( nodes != null ) {
1223
        for (Node node : nodes) {
1224
            
1225
        	if (node.getSubjectList() != null) {
1226
        		
1227
	            for (Subject nodeSubject : node.getSubjectList()) {
1228
	            	
1229
	                if ( nodeSubject.equals(targetNodeSubject) ) {
1230
	                    targetNode = node.getIdentifier();
1231
	                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1232
	                    break;
1233
	                }
1234
	            }
1235
        	}
1236
            
1237
            if ( targetNode != null) { break; }
1238
        }
1239
        
1240
      } else {
1241
          String msg = "Couldn't get the node list from the CN";
1242
          logMetacat.debug(msg);
1243
          throw new ServiceFailure("4872", msg);
1244
          
1245
      }
1246
      
1247
      // can't find a node listed with the given subject
1248
      if ( targetNode == null ) {
1249
          String msg = "There is no Member Node registered with a node subject " +
1250
              "matching " + targetNodeSubject.getValue();
1251
          logMetacat.info(msg);
1252
          throw new NotAuthorized("4871", msg);
1253
          
1254
      }
1255
      
1256
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1257
      
1258
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1259

    
1260
      if ( sysmeta != null ) {
1261
          
1262
          List<Replica> replicaList = sysmeta.getReplicaList();
1263
          
1264
          if ( replicaList != null ) {
1265
              
1266
              // find the replica with the status set to 'requested'
1267
              for (Replica replica : replicaList) {
1268
                  ReplicationStatus status = replica.getReplicationStatus();
1269
                  NodeReference listedNode = replica.getReplicaMemberNode();
1270
                  if ( listedNode != null && targetNode != null ) {
1271
                      logMetacat.debug("Comparing " + listedNode.getValue()
1272
                              + " to " + targetNode.getValue());
1273
                      
1274
                      if (listedNode.getValue().equals(targetNode.getValue())
1275
                              && status.equals(ReplicationStatus.REQUESTED)) {
1276
                          isAllowed = true;
1277
                          break;
1278

    
1279
                      }
1280
                  }
1281
              }
1282
          }
1283
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1284
              "to replicate: " + isAllowed + " for " + pid.getValue());
1285

    
1286
          
1287
      } else {
1288
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1289
          " is null.");          
1290
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1291
          
1292
      }
1293

    
1294
    } catch (RuntimeException e) {
1295
    	  ServiceFailure sf = new ServiceFailure("4872", 
1296
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1297
                e.getMessage());
1298
    	  sf.initCause(e);
1299
        throw sf;
1300
        
1301
    }
1302
      
1303
    return isAllowed;
1304
    
1305
  }
1306

    
1307
  /**
1308
   * Adds a new object to the Node, where the object is a science metadata object.
1309
   * 
1310
   * @param session - the Session object containing the credentials for the Subject
1311
   * @param pid - The object identifier to be created
1312
   * @param object - the object bytes
1313
   * @param sysmeta - the system metadata that describes the object  
1314
   * 
1315
   * @return pid - the object identifier created
1316
   * 
1317
   * @throws InvalidToken
1318
   * @throws ServiceFailure
1319
   * @throws NotAuthorized
1320
   * @throws IdentifierNotUnique
1321
   * @throws UnsupportedType
1322
   * @throws InsufficientResources
1323
   * @throws InvalidSystemMetadata
1324
   * @throws NotImplemented
1325
   * @throws InvalidRequest
1326
   */
1327
  public Identifier create(Session session, Identifier pid, InputStream object,
1328
    SystemMetadata sysmeta) 
1329
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1330
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1331
    NotImplemented, InvalidRequest {
1332
                  
1333
      // The lock to be used for this identifier
1334
      Lock lock = null;
1335

    
1336
      try {
1337
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1338
          // are we allowed?
1339
          boolean isAllowed = false;
1340
          isAllowed = isAdminAuthorized(session);
1341

    
1342
          // proceed if we're called by a CN
1343
          if ( isAllowed ) {
1344
              // create the coordinating node version of the document      
1345
              lock.lock();
1346
              logMetacat.debug("Locked identifier " + pid.getValue());
1347
              sysmeta.setSerialVersion(BigInteger.ONE);
1348
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1349
              sysmeta.setArchived(false); // this is a create op, not update
1350
              
1351
              // the CN should have set the origin and authoritative member node fields
1352
              try {
1353
                  sysmeta.getOriginMemberNode().getValue();
1354
                  sysmeta.getAuthoritativeMemberNode().getValue();
1355
                  
1356
              } catch (NullPointerException npe) {
1357
                  throw new InvalidSystemMetadata("4896", 
1358
                      "Both the origin and authoritative member node identifiers need to be set.");
1359
                  
1360
              }
1361
              pid = super.create(session, pid, object, sysmeta);
1362

    
1363
          } else {
1364
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1365
                  " isn't allowed to call create() on a Coordinating Node.";
1366
              logMetacat.info(msg);
1367
              throw new NotAuthorized("1100", msg);
1368
          }
1369
          
1370
      } catch (RuntimeException e) {
1371
          // Convert Hazelcast runtime exceptions to service failures
1372
          String msg = "There was a problem creating the object identified by " +
1373
              pid.getValue() + ". There error message was: " + e.getMessage();
1374
          throw new ServiceFailure("4893", msg);
1375
          
1376
      } finally {
1377
    	  if (lock != null) {
1378
	          lock.unlock();
1379
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1380
    	  }
1381
      }
1382
      
1383
      return pid;
1384

    
1385
  }
1386

    
1387
  /**
1388
   * Set access for a given object using the object identifier and a Subject
1389
   * under a given Session.
1390
   * 
1391
   * @param session - the Session object containing the credentials for the Subject
1392
   * @param pid - the object identifier for the given object to apply the policy
1393
   * @param policy - the access policy to be applied
1394
   * 
1395
   * @return true if the application of the policy succeeds
1396
   * @throws InvalidToken
1397
   * @throws ServiceFailure
1398
   * @throws NotFound
1399
   * @throws NotAuthorized
1400
   * @throws NotImplemented
1401
   * @throws InvalidRequest
1402
   */
1403
  public boolean setAccessPolicy(Session session, Identifier pid, 
1404
      AccessPolicy accessPolicy, long serialVersion) 
1405
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1406
      NotImplemented, InvalidRequest, VersionMismatch {
1407
      
1408
      // The lock to be used for this identifier
1409
      Lock lock = null;
1410
      SystemMetadata systemMetadata = null;
1411
      
1412
      boolean success = false;
1413
      
1414
      // get the subject
1415
      Subject subject = session.getSubject();
1416
      
1417
      // are we allowed to do this?
1418
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1419
          throw new NotAuthorized("4420", "not allowed by "
1420
                  + subject.getValue() + " on " + pid.getValue());
1421
      }
1422
      
1423
      try {
1424
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1425
          lock.lock();
1426
          logMetacat.debug("Locked identifier " + pid.getValue());
1427

    
1428
          try {
1429
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1430

    
1431
              if ( systemMetadata == null ) {
1432
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1433
                  
1434
              }
1435
              // does the request have the most current system metadata?
1436
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1437
                 String msg = "The requested system metadata version number " + 
1438
                     serialVersion + " differs from the current version at " +
1439
                     systemMetadata.getSerialVersion().longValue() +
1440
                     ". Please get the latest copy in order to modify it.";
1441
                 throw new VersionMismatch("4402", msg);
1442
                 
1443
              }
1444
              
1445
          } catch (RuntimeException e) {
1446
              // convert Hazelcast RuntimeException to NotFound
1447
              throw new NotFound("4400", "No record found for: " + pid);
1448
            
1449
          }
1450
              
1451
          // set the access policy
1452
          systemMetadata.setAccessPolicy(accessPolicy);
1453
          
1454
          // update the system metadata
1455
          try {
1456
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1457
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1458
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1459
              notifyReplicaNodes(systemMetadata);
1460
              
1461
          } catch (RuntimeException e) {
1462
              // convert Hazelcast RuntimeException to ServiceFailure
1463
              throw new ServiceFailure("4430", e.getMessage());
1464
            
1465
          }
1466
          
1467
      } catch (RuntimeException e) {
1468
          throw new ServiceFailure("4430", e.getMessage());
1469
          
1470
      } finally {
1471
          lock.unlock();
1472
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1473
        
1474
      }
1475

    
1476
    
1477
    // TODO: how do we know if the map was persisted?
1478
    success = true;
1479
    
1480
    return success;
1481
  }
1482

    
1483
  /**
1484
   * Full replacement of replication metadata in the system metadata for the 
1485
   * specified object, changes date system metadata modified
1486
   * 
1487
   * @param session - the Session object containing the credentials for the Subject
1488
   * @param pid - the object identifier for the given object to apply the policy
1489
   * @param replica - the replica to be updated
1490
   * @return
1491
   * @throws NotImplemented
1492
   * @throws NotAuthorized
1493
   * @throws ServiceFailure
1494
   * @throws InvalidRequest
1495
   * @throws NotFound
1496
   * @throws VersionMismatch
1497
   */
1498
  @Override
1499
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1500
      Replica replica, long serialVersion) 
1501
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1502
      NotFound, VersionMismatch {
1503
      
1504
      // The lock to be used for this identifier
1505
      Lock lock = null;
1506
      
1507
      // get the subject
1508
      Subject subject = session.getSubject();
1509
      
1510
      // are we allowed to do this?
1511
      try {
1512

    
1513
          // what is the controlling permission?
1514
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1515
              throw new NotAuthorized("4851", "not allowed by "
1516
                      + subject.getValue() + " on " + pid.getValue());
1517
          }
1518

    
1519
        
1520
      } catch (InvalidToken e) {
1521
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1522
                  " on " + pid.getValue());  
1523
          
1524
      }
1525

    
1526
      SystemMetadata systemMetadata = null;
1527
      try {
1528
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1529
          lock.lock();
1530
          logMetacat.debug("Locked identifier " + pid.getValue());
1531

    
1532
          try {      
1533
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1534

    
1535
              // does the request have the most current system metadata?
1536
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1537
                 String msg = "The requested system metadata version number " + 
1538
                     serialVersion + " differs from the current version at " +
1539
                     systemMetadata.getSerialVersion().longValue() +
1540
                     ". Please get the latest copy in order to modify it.";
1541
                 throw new VersionMismatch("4855", msg);
1542
              }
1543
              
1544
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1545
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1546
                  " : " + e.getMessage());
1547
            
1548
          }
1549
              
1550
          // set the status for the replica
1551
          List<Replica> replicas = systemMetadata.getReplicaList();
1552
          NodeReference replicaNode = replica.getReplicaMemberNode();
1553
          ReplicationStatus replicaStatus = replica.getReplicationStatus();
1554
          int index = 0;
1555
          for (Replica listedReplica: replicas) {
1556
              
1557
              // remove the replica that we are replacing
1558
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1559
                      // don't allow status to change from COMPLETED to anything other
1560
                      // than INVALIDATED: prevents overwrites from race conditions
1561
                	  if ( listedReplica.getReplicationStatus() == ReplicationStatus.COMPLETED &&
1562
            		    replicaStatus != ReplicationStatus.INVALIDATED ) {
1563
                	  throw new InvalidRequest("4853", "Status state change from " +
1564
                			  listedReplica.getReplicationStatus() + " to " +
1565
                			  replicaStatus.toString() + "is prohibited for identifier " +
1566
                			  pid.getValue() + " and target node " + 
1567
                			  listedReplica.getReplicaMemberNode().getValue());
1568

    
1569
            	  }
1570
                  replicas.remove(index);
1571
                  break;
1572
                  
1573
              }
1574
              index++;
1575
          }
1576
          
1577
          // add the new replica item
1578
          replicas.add(replica);
1579
          systemMetadata.setReplicaList(replicas);
1580
          
1581
          // update the metadata
1582
          try {
1583
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1584
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1585
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1586
            
1587
          } catch (RuntimeException e) {
1588
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1589
              throw new ServiceFailure("4852", e.getMessage());
1590
          
1591
          }
1592
          
1593
      } catch (RuntimeException e) {
1594
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1595
          throw new ServiceFailure("4852", e.getMessage());
1596
      
1597
      } finally {
1598
          lock.unlock();
1599
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1600
          
1601
      }
1602
    
1603
      return true;
1604
      
1605
  }
1606
  
1607
  /**
1608
   * 
1609
   */
1610
  @Override
1611
  public ObjectList listObjects(Session session, Date startTime, 
1612
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1613
      Integer start, Integer count)
1614
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1615
      ServiceFailure {
1616
      
1617
      ObjectList objectList = null;
1618
        try {
1619
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1620
        } catch (Exception e) {
1621
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1622
        }
1623

    
1624
        return objectList;
1625
  }
1626

    
1627
  
1628
 	/**
1629
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1630
 	 * @return cal  the list of checksum algorithms
1631
 	 * 
1632
 	 * @throws ServiceFailure
1633
 	 * @throws NotImplemented
1634
 	 */
1635
  @Override
1636
  public ChecksumAlgorithmList listChecksumAlgorithms()
1637
			throws ServiceFailure, NotImplemented {
1638
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1639
		cal.addAlgorithm("MD5");
1640
		cal.addAlgorithm("SHA-1");
1641
		return cal;
1642
		
1643
	}
1644

    
1645
  /**
1646
   * Notify replica Member Nodes of system metadata changes for a given pid
1647
   * 
1648
   * @param currentSystemMetadata - the up to date system metadata
1649
   */
1650
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1651
      
1652
      Session session = null;
1653
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1654
      MNode mn = null;
1655
      NodeReference replicaNodeRef = null;
1656
      CNode cn = null;
1657
      NodeType nodeType = null;
1658
      List<Node> nodeList = null;
1659
      
1660
      try {
1661
          cn = D1Client.getCN();
1662
          nodeList = cn.listNodes().getNodeList();
1663
          
1664
      } catch (Exception e) { // handle BaseException and other I/O issues
1665
          
1666
          // swallow errors since the call is not critical
1667
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1668
              "to communication issues with the CN: " + e.getMessage());
1669
          
1670
      }
1671
      
1672
      if ( replicaList != null ) {
1673
          
1674
          // iterate through the replicas and inform  MN replica nodes
1675
          for (Replica replica : replicaList) {
1676
              
1677
              replicaNodeRef = replica.getReplicaMemberNode();
1678
              try {
1679
                  if (nodeList != null) {
1680
                      // find the node type
1681
                      for (Node node : nodeList) {
1682
                          if (node.getIdentifier().getValue() == 
1683
                              replicaNodeRef.getValue()) {
1684
                              nodeType = node.getType();
1685
                              break;
1686
              
1687
                          }
1688
                      }
1689
                  }
1690
              
1691
                  // notify only MNs
1692
                  if (nodeType != null && nodeType == NodeType.MN) {
1693
                      mn = D1Client.getMN(replicaNodeRef);
1694
                      mn.systemMetadataChanged(session, 
1695
                          currentSystemMetadata.getIdentifier(), 
1696
                          currentSystemMetadata.getSerialVersion().longValue(),
1697
                          currentSystemMetadata.getDateSysMetadataModified());
1698
                  }
1699
              
1700
              } catch (Exception e) { // handle BaseException and other I/O issues
1701
              
1702
                  // swallow errors since the call is not critical
1703
                  logMetacat.error("Can't inform "
1704
                          + replicaNodeRef.getValue()
1705
                          + " of system metadata changes due "
1706
                          + "to communication issues with the CN: "
1707
                          + e.getMessage());
1708
              
1709
              }
1710
          }
1711
      }
1712
  }
1713

    
1714
	@Override
1715
	public boolean isAuthorized(Identifier pid, Permission permission)
1716
			throws ServiceFailure, InvalidToken, NotFound, NotAuthorized,
1717
			NotImplemented, InvalidRequest {
1718
		
1719
		return isAuthorized(null, pid, permission);
1720
	}
1721
	
1722
	@Override
1723
	public boolean setAccessPolicy(Identifier pid, AccessPolicy accessPolicy, long serialVersion)
1724
			throws InvalidToken, NotFound, NotImplemented, NotAuthorized,
1725
			ServiceFailure, InvalidRequest, VersionMismatch {
1726
		
1727
		return setAccessPolicy(null, pid, accessPolicy, serialVersion);
1728
	}
1729
	
1730
	@Override
1731
	public Identifier setRightsHolder(Identifier pid, Subject userId, long serialVersion)
1732
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1733
			NotImplemented, InvalidRequest, VersionMismatch {
1734
		
1735
		return setRightsHolder(null, pid, userId, serialVersion);
1736
	}
1737
	
1738
	@Override
1739
	public Identifier create(Identifier pid, InputStream object, SystemMetadata sysmeta)
1740
			throws InvalidToken, ServiceFailure, NotAuthorized,
1741
			IdentifierNotUnique, UnsupportedType, InsufficientResources,
1742
			InvalidSystemMetadata, NotImplemented, InvalidRequest {
1743

    
1744
		return create(null, pid, object, sysmeta);
1745
	}
1746
	
1747
	@Override
1748
	public Identifier delete(Identifier pid) throws InvalidToken, ServiceFailure,
1749
			NotAuthorized, NotFound, NotImplemented {
1750

    
1751
		return delete(null, pid);
1752
	}
1753
	
1754
	@Override
1755
	public Identifier generateIdentifier(String scheme, String fragment)
1756
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1757
			InvalidRequest {
1758

    
1759
		return generateIdentifier(null, scheme, fragment);
1760
	}
1761
	
1762
	@Override
1763
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1764
			Integer start, Integer count) throws InvalidToken, InvalidRequest,
1765
			ServiceFailure, NotAuthorized, NotImplemented, InsufficientResources {
1766

    
1767
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1768
	}
1769
	
1770
	@Override
1771
	public boolean hasReservation(Subject subject, Identifier pid)
1772
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1773
			NotImplemented, InvalidRequest, IdentifierNotUnique {
1774

    
1775
		return hasReservation(null, subject, pid);
1776
	}
1777
	
1778
	@Override
1779
	public Identifier registerSystemMetadata(Identifier pid, SystemMetadata sysmeta)
1780
			throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1781
			InvalidSystemMetadata, InvalidToken {
1782

    
1783
		return registerSystemMetadata(null, pid, sysmeta);
1784
	}
1785
	
1786
	@Override
1787
	public Identifier reserveIdentifier(Identifier pid) throws InvalidToken,
1788
			ServiceFailure, NotAuthorized, IdentifierNotUnique, NotImplemented,
1789
			InvalidRequest {
1790

    
1791
		return reserveIdentifier(null, pid);
1792
	}
1793
	
1794
	@Override
1795
	public boolean setObsoletedBy(Identifier pid, Identifier obsoletedByPid, long serialVersion)
1796
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
1797
			InvalidRequest, InvalidToken, VersionMismatch {
1798

    
1799
		return setObsoletedBy(null, pid, obsoletedByPid, serialVersion);
1800
	}
1801
	
1802
	@Override
1803
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1804
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1805

    
1806
		return describe(null, pid);
1807
	}
1808
	
1809
	@Override
1810
	public InputStream get(Identifier pid) throws InvalidToken, ServiceFailure,
1811
			NotAuthorized, NotFound, NotImplemented {
1812

    
1813
		return get(null, pid);
1814
	}
1815
	
1816
	@Override
1817
	public Checksum getChecksum(Identifier pid) throws InvalidToken,
1818
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1819

    
1820
		return getChecksum(null, pid);
1821
	}
1822
	
1823
	@Override
1824
	public SystemMetadata getSystemMetadata(Identifier pid) throws InvalidToken,
1825
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1826

    
1827
		return getSystemMetadata(null, pid);
1828
	}
1829
	
1830
	@Override
1831
	public ObjectList listObjects(Date startTime, Date endTime,
1832
			ObjectFormatIdentifier formatid, Boolean replicaStatus, Integer start, Integer count)
1833
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1834
			ServiceFailure {
1835

    
1836
		return listObjects(null, startTime, endTime, formatid, replicaStatus, start, count);
1837
	}
1838
	
1839
	@Override
1840
	public ObjectLocationList resolve(Identifier pid) throws InvalidToken,
1841
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1842

    
1843
		return resolve(null, pid);
1844
	}
1845
	
1846
	@Override
1847
	public ObjectList search(String queryType, String query) throws InvalidToken,
1848
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
1849

    
1850
		return search(null, queryType, query);
1851
	}
1852
	
1853
	@Override
1854
	public boolean deleteReplicationMetadata(Identifier pid, NodeReference nodeId,
1855
			long serialVersion) throws InvalidToken, InvalidRequest, ServiceFailure,
1856
			NotAuthorized, NotFound, NotImplemented, VersionMismatch {
1857

    
1858
		return deleteReplicationMetadata(null, pid, nodeId, serialVersion);
1859
	}
1860
	
1861
	@Override
1862
	public boolean isNodeAuthorized(Subject targetNodeSubject, Identifier pid)
1863
			throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
1864
			NotFound, InvalidRequest {
1865

    
1866
		return isNodeAuthorized(null, targetNodeSubject, pid);
1867
	}
1868
	
1869
	@Override
1870
	public boolean setReplicationPolicy(Identifier pid, ReplicationPolicy policy,
1871
			long serialVersion) throws NotImplemented, NotFound, NotAuthorized,
1872
			ServiceFailure, InvalidRequest, InvalidToken, VersionMismatch {
1873

    
1874
		return setReplicationPolicy(null, pid, policy, serialVersion);
1875
	}
1876
	
1877
	@Override
1878
	public boolean setReplicationStatus(Identifier pid, NodeReference targetNode,
1879
			ReplicationStatus status, BaseException failure) throws ServiceFailure,
1880
			NotImplemented, InvalidToken, NotAuthorized, InvalidRequest, NotFound {
1881

    
1882
		return setReplicationStatus(null, pid, targetNode, status, failure);
1883
	}
1884
	
1885
	@Override
1886
	public boolean updateReplicationMetadata(Identifier pid, Replica replica,
1887
			long serialVersion) throws NotImplemented, NotAuthorized, ServiceFailure,
1888
			NotFound, InvalidRequest, InvalidToken, VersionMismatch {
1889

    
1890
		return updateReplicationMetadata(null, pid, replica, serialVersion);
1891
	}
1892
}
(1-1/5)