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.Identifier;
60
import org.dataone.service.types.v1.Node;
61
import org.dataone.service.types.v1.NodeList;
62
import org.dataone.service.types.v1.NodeReference;
63
import org.dataone.service.types.v1.NodeType;
64
import org.dataone.service.types.v1.ObjectFormat;
65
import org.dataone.service.types.v1.ObjectFormatIdentifier;
66
import org.dataone.service.types.v1.ObjectFormatList;
67
import org.dataone.service.types.v1.ObjectList;
68
import org.dataone.service.types.v1.ObjectLocationList;
69
import org.dataone.service.types.v1.Permission;
70
import org.dataone.service.types.v1.Replica;
71
import org.dataone.service.types.v1.ReplicationPolicy;
72
import org.dataone.service.types.v1.ReplicationStatus;
73
import org.dataone.service.types.v1.Session;
74
import org.dataone.service.types.v1.Subject;
75
import org.dataone.service.types.v1.SystemMetadata;
76
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
77

    
78
import edu.ucsb.nceas.metacat.EventLog;
79
import edu.ucsb.nceas.metacat.IdentifierManager;
80
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
81

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

    
92
  /* the logger instance */
93
  private Logger logMetacat = null;
94

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

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

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

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

    
229
		// get the subject
230
		Subject subject = session.getSubject();
231

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

    
244
		}
245

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

    
252
			try {
253
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
254
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
255
				}
256

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

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

    
271
				}
272

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

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

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

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

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

    
339
	  // TODO: any CN-specific checks to perform?
340
	  
341
	  // defer to superclass implementation
342
      return super.delete(session, pid);
343
  }
344
  
345
  /**
346
   * Set the obsoletedBy attribute in System Metadata
347
   * @param session
348
   * @param pid
349
   * @param obsoletedByPid
350
   * @param serialVersion
351
   * @return
352
   * @throws NotImplemented
353
   * @throws NotFound
354
   * @throws NotAuthorized
355
   * @throws ServiceFailure
356
   * @throws InvalidRequest
357
   * @throws InvalidToken
358
   * @throws VersionMismatch
359
   */
360
  @Override
361
  public boolean setObsoletedBy(Session session, Identifier pid,
362
			Identifier obsoletedByPid, long serialVersion)
363
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
364
			InvalidRequest, InvalidToken, VersionMismatch {
365

    
366
		// The lock to be used for this identifier
367
		Lock lock = null;
368

    
369
		// get the subject
370
		Subject subject = session.getSubject();
371

    
372
		// are we allowed to do this?
373
		if (!isAuthorized(session, pid, Permission.WRITE)) {
374
			throw new NotAuthorized("4881", Permission.WRITE
375
					+ " not allowed by " + subject.getValue() + " on "
376
					+ pid.getValue());
377

    
378
		}
379

    
380

    
381
		SystemMetadata systemMetadata = null;
382
		try {
383
			lock = HazelcastService.getInstance().getLock(pid.getValue());
384
			lock.lock();
385
			logMetacat.debug("Locked identifier " + pid.getValue());
386

    
387
			try {
388
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
389
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
390
				}
391

    
392
				// did we get it correctly?
393
				if (systemMetadata == null) {
394
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
395
				}
396

    
397
				// does the request have the most current system metadata?
398
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
399
					String msg = "The requested system metadata version number "
400
							+ serialVersion
401
							+ " differs from the current version at "
402
							+ systemMetadata.getSerialVersion().longValue()
403
							+ ". Please get the latest copy in order to modify it.";
404
					throw new VersionMismatch("4886", msg);
405

    
406
				}
407

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

    
411
			}
412

    
413
			// set the new policy
414
			systemMetadata.setObsoletedBy(obsoletedByPid);
415

    
416
			// update the metadata
417
			try {
418
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
419
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
420
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
421
			} catch (RuntimeException e) {
422
				throw new ServiceFailure("4882", e.getMessage());
423
			}
424

    
425
		} catch (RuntimeException e) {
426
			throw new ServiceFailure("4882", e.getMessage());
427
		} finally {
428
			lock.unlock();
429
			logMetacat.debug("Unlocked identifier " + pid.getValue());
430
		}
431

    
432
		return true;
433
	}
434
  
435
  
436
  /**
437
   * Set the replication status for an object given the object identifier
438
   * 
439
   * @param session - the Session object containing the credentials for the Subject
440
   * @param pid - the object identifier for the given object
441
   * @param status - the replication status to be applied
442
   * 
443
   * @return true or false
444
   * 
445
   * @throws NotImplemented
446
   * @throws NotAuthorized
447
   * @throws ServiceFailure
448
   * @throws InvalidRequest
449
   * @throws InvalidToken
450
   * @throws NotFound
451
   * 
452
   */
453
  @Override
454
  public boolean setReplicationStatus(Session session, Identifier pid,
455
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
456
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
457
      InvalidRequest, NotFound {
458
	  
459
	  // cannot be called by public
460
	  if (session == null) {
461
		  throw new NotAuthorized("4720", "Session cannot be null");
462
	  }
463
      
464
      // The lock to be used for this identifier
465
      Lock lock = null;
466
      
467
      boolean allowed = false;
468
      int replicaEntryIndex = -1;
469
      List<Replica> replicas = null;
470
      // get the subject
471
      Subject subject = session.getSubject();
472
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
473
          " is " + status.toString());
474
      
475
      SystemMetadata systemMetadata = null;
476

    
477
      try {
478
          lock = HazelcastService.getInstance().getLock(pid.getValue());
479
          lock.lock();
480
          logMetacat.debug("Locked identifier " + pid.getValue());
481

    
482
          try {      
483
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
484

    
485
              // did we get it correctly?
486
              if ( systemMetadata == null ) {
487
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
488
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
489
                  
490
              }
491
              replicas = systemMetadata.getReplicaList();
492
              int count = 0;
493
              
494
              // was there a failure? log it
495
              if ( failure != null && status == ReplicationStatus.FAILED ) {
496
                 String msg = "The replication request of the object identified by " + 
497
                     pid.getValue() + " failed.  The error message was " +
498
                     failure.getMessage() + ".";
499
              }
500
              
501
              if (replicas.size() > 0 && replicas != null) {
502
                  // find the target replica index in the replica list
503
                  for (Replica replica : replicas) {
504
                      String replicaNodeStr = replica.getReplicaMemberNode()
505
                              .getValue();
506
                      String targetNodeStr = targetNode.getValue();
507
                      logMetacat.debug("Comparing " + replicaNodeStr + " to "
508
                              + targetNodeStr);
509
                  
510
                      if (replicaNodeStr.equals(targetNodeStr)) {
511
                          replicaEntryIndex = count;
512
                          logMetacat.debug("replica entry index is: "
513
                                  + replicaEntryIndex);
514
                          break;
515
                      }
516
                      count++;
517
                  
518
                  }
519
              }
520
              // are we allowed to do this? only CNs and target MNs are allowed
521
              CNode cn = D1Client.getCN();
522
              List<Node> nodes = cn.listNodes().getNodeList();
523
              
524
              // find the node in the node list
525
              for ( Node node : nodes ) {
526
                  
527
                  NodeReference nodeReference = node.getIdentifier();
528
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
529
                  
530
                  // allow target MN certs
531
                  if (targetNode.getValue().equals(nodeReference.getValue()) &&
532
                      node.getType() == NodeType.MN) {
533
                      List<Subject> nodeSubjects = node.getSubjectList();
534
                      
535
                      // check if the session subject is in the node subject list
536
                      for (Subject nodeSubject : nodeSubjects) {
537
                          logMetacat.debug("In setReplicationStatus(), comparing subjects: " +
538
                                  nodeSubject.getValue() + " and " + subject.getValue());
539
                          if ( nodeSubject.equals(subject) ) { // subject of session == target node subject
540
                              
541
                              // lastly limit to COMPLETED status updates from MNs only
542
                              if ( status == ReplicationStatus.COMPLETED ) {
543
                                  allowed = true;
544
                                  break;
545
                                  
546
                              }                              
547
                          }
548
                      }                 
549
                  }
550
              }
551

    
552
              if ( !allowed ) {
553
                  //check for CN admin access
554
                  allowed = isAuthorized(session, pid, Permission.WRITE);
555
                  
556
              }              
557
              
558
              if ( !allowed ) {
559
                  String msg = "The subject identified by "
560
                          + subject.getValue()
561
                          + " does not have permission to set the replication status for "
562
                          + "the replica identified by "
563
                          + targetNode.getValue() + ".";
564
                  logMetacat.info(msg);
565
                  throw new NotAuthorized("4720", msg);
566
                  
567
              }
568

    
569
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
570
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
571
                " : " + e.getMessage());
572
            
573
          }
574
          
575
          Replica targetReplica = new Replica();
576
          // set the status for the replica
577
          if ( replicaEntryIndex != -1 ) {
578
              targetReplica = replicas.get(replicaEntryIndex);
579
              targetReplica.setReplicationStatus(status);
580
              logMetacat.debug("Set the replication status for " + 
581
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
582
                  targetReplica.getReplicationStatus());
583
              
584
          } else {
585
              // this is a new entry, create it
586
              targetReplica.setReplicaMemberNode(targetNode);
587
              targetReplica.setReplicationStatus(status);
588
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
589
              replicas.add(targetReplica);
590
              
591
          }
592
          
593
          systemMetadata.setReplicaList(replicas);
594
                
595
          // update the metadata
596
          try {
597
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
598
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
599
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
600
              
601
              if ( status == ReplicationStatus.FAILED && failure != null ) {
602
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
603
                      " on target node " + targetNode + ". The exception was: " +
604
                      failure.getMessage());
605
              }
606
          } catch (RuntimeException e) {
607
              throw new ServiceFailure("4700", e.getMessage());
608
          
609
          }
610
          
611
    } catch (RuntimeException e) {
612
        String msg = "There was a RuntimeException getting the lock for " +
613
            pid.getValue();
614
        logMetacat.info(msg);
615
        
616
    } finally {
617
        lock.unlock();
618
        logMetacat.debug("Unlocked identifier " + pid.getValue());
619
        
620
    }
621
      
622
      return true;
623
  }
624
  
625
  /**
626
   * Return the checksum of the object given the identifier 
627
   * 
628
   * @param session - the Session object containing the credentials for the Subject
629
   * @param pid - the object identifier for the given object
630
   * 
631
   * @return checksum - the checksum of the object
632
   * 
633
   * @throws InvalidToken
634
   * @throws ServiceFailure
635
   * @throws NotAuthorized
636
   * @throws NotFound
637
   * @throws NotImplemented
638
   */
639
  @Override
640
  public Checksum getChecksum(Session session, Identifier pid)
641
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
642
    NotImplemented {
643
    
644
	boolean isAuthorized = false;
645
	try {
646
		isAuthorized = isAuthorized(session, pid, Permission.READ);
647
	} catch (InvalidRequest e) {
648
		throw new ServiceFailure("1410", e.getDescription());
649
	}  
650
    if (!isAuthorized) {
651
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
652
    }
653
    
654
    SystemMetadata systemMetadata = null;
655
    Checksum checksum = null;
656
    
657
    try {
658
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
659

    
660
        if (systemMetadata == null ) {
661
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
662
        }
663
        checksum = systemMetadata.getChecksum();
664
        
665
    } catch (RuntimeException e) {
666
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
667
            pid.getValue() + ". The error message was: " + e.getMessage());
668
      
669
    }
670
    
671
    return checksum;
672
  }
673

    
674
  /**
675
   * Resolve the location of a given object
676
   * 
677
   * @param session - the Session object containing the credentials for the Subject
678
   * @param pid - the object identifier for the given object
679
   * 
680
   * @return objectLocationList - the list of nodes known to contain the object
681
   * 
682
   * @throws InvalidToken
683
   * @throws ServiceFailure
684
   * @throws NotAuthorized
685
   * @throws NotFound
686
   * @throws NotImplemented
687
   */
688
  @Override
689
  public ObjectLocationList resolve(Session session, Identifier pid)
690
    throws InvalidToken, ServiceFailure, NotAuthorized,
691
    NotFound, NotImplemented {
692

    
693
    throw new NotImplemented("4131", "resolve not implemented");
694

    
695
  }
696

    
697
  /**
698
   * Search the metadata catalog for identifiers that match the criteria
699
   * 
700
   * @param session - the Session object containing the credentials for the Subject
701
   * @param queryType - An identifier for the type of query expression 
702
   *                    provided in the query
703
   * @param query -  The criteria for matching the characteristics of the 
704
   *                 metadata objects of interest
705
   * 
706
   * @return objectList - the list of objects matching the criteria
707
   * 
708
   * @throws InvalidToken
709
   * @throws ServiceFailure
710
   * @throws NotAuthorized
711
   * @throws InvalidRequest
712
   * @throws NotImplemented
713
   */
714
  @Override
715
  public ObjectList search(Session session, String queryType, String query)
716
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
717
    NotImplemented {
718

    
719
    ObjectList objectList = null;
720
    try {
721
        objectList = 
722
          IdentifierManager.getInstance().querySystemMetadata(
723
              null, //startTime, 
724
              null, //endTime,
725
              null, //objectFormat, 
726
              false, //replicaStatus, 
727
              0, //start, 
728
              -1 //count
729
              );
730
        
731
    } catch (Exception e) {
732
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
733
    }
734

    
735
      return objectList;
736
      
737
    //throw new NotImplemented("4281", "search not implemented");
738
    
739
    // the code block below is from an older implementation
740
    
741
    /*  This block commented out because of the EcoGrid circular dependency.
742
         *  For now, query will not be supported until the circularity can be
743
         *  resolved, probably by moving the ecogrid query syntax transformers
744
         *  directly into the Metacat codebase.  MBJ 2010-02-03
745
         
746
        try {
747
            EcogridQueryParser parser = new EcogridQueryParser(request
748
                    .getReader());
749
            parser.parseXML();
750
            QueryType queryType = parser.getEcogridQuery();
751
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
752
                new EcogridJavaToMetacatJavaQueryTransformer();
753
            QuerySpecification metacatQuery = queryTransformer
754
                    .transform(queryType);
755

    
756
            DBQuery metacat = new DBQuery();
757

    
758
            boolean useXMLIndex = (new Boolean(PropertyService
759
                    .getProperty("database.usexmlindex"))).booleanValue();
760
            String xmlquery = "query"; // we don't care the query in resultset,
761
            // the query can be anything
762
            PrintWriter out = null; // we don't want metacat result, so set out null
763

    
764
            // parameter: queryspecification, user, group, usingIndexOrNot
765
            StringBuffer result = metacat.createResultDocument(xmlquery,
766
                    metacatQuery, out, username, groupNames, useXMLIndex);
767

    
768
            // create result set transfer       
769
            String saxparser = PropertyService.getProperty("xml.saxparser");
770
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
771
                    new StringReader(result.toString()), saxparser, queryType
772
                            .getNamespace().get_value());
773
            ResultsetType records = metacatResultsetParser.getEcogridResult();
774

    
775
            System.out
776
                    .println(EcogridResultsetTransformer.toXMLString(records));
777
            response.setContentType("text/xml");
778
            out = response.getWriter();
779
            out.print(EcogridResultsetTransformer.toXMLString(records));
780

    
781
        } catch (Exception e) {
782
            e.printStackTrace();
783
        }*/
784
    
785

    
786
  }
787
  
788
  /**
789
   * Returns the object format registered in the DataONE Object Format 
790
   * Vocabulary for the given format identifier
791
   * 
792
   * @param fmtid - the identifier of the format requested
793
   * 
794
   * @return objectFormat - the object format requested
795
   * 
796
   * @throws ServiceFailure
797
   * @throws NotFound
798
   * @throws InsufficientResources
799
   * @throws NotImplemented
800
   */
801
  @Override
802
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
803
    throws ServiceFailure, NotFound, NotImplemented {
804
     
805
      return ObjectFormatService.getInstance().getFormat(fmtid);
806
      
807
  }
808

    
809
  /**
810
   * Returns a list of all object formats registered in the DataONE Object 
811
   * Format Vocabulary
812
    * 
813
   * @return objectFormatList - The list of object formats registered in 
814
   *                            the DataONE Object Format Vocabulary
815
   * 
816
   * @throws ServiceFailure
817
   * @throws NotImplemented
818
   * @throws InsufficientResources
819
   */
820
  @Override
821
  public ObjectFormatList listFormats() 
822
    throws ServiceFailure, NotImplemented {
823

    
824
    return ObjectFormatService.getInstance().listFormats();
825
  }
826

    
827
  /**
828
   * Returns a list of nodes that have been registered with the DataONE infrastructure
829
    * 
830
   * @return nodeList - List of nodes from the registry
831
   * 
832
   * @throws ServiceFailure
833
   * @throws NotImplemented
834
   */
835
  @Override
836
  public NodeList listNodes() 
837
    throws NotImplemented, ServiceFailure {
838

    
839
    throw new NotImplemented("4800", "listNodes not implemented");
840
  }
841

    
842
  /**
843
   * Provides a mechanism for adding system metadata independently of its 
844
   * associated object, such as when adding system metadata for data objects.
845
    * 
846
   * @param session - the Session object containing the credentials for the Subject
847
   * @param pid - The identifier of the object to register the system metadata against
848
   * @param sysmeta - The system metadata to be registered
849
   * 
850
   * @return true if the registration succeeds
851
   * 
852
   * @throws NotImplemented
853
   * @throws NotAuthorized
854
   * @throws ServiceFailure
855
   * @throws InvalidRequest
856
   * @throws InvalidSystemMetadata
857
   */
858
  @Override
859
  public Identifier registerSystemMetadata(Session session, Identifier pid,
860
      SystemMetadata sysmeta) 
861
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
862
      InvalidSystemMetadata {
863

    
864
      // The lock to be used for this identifier
865
      Lock lock = null;
866

    
867
      // TODO: control who can call this?
868
      if (session == null) {
869
          //TODO: many of the thrown exceptions do not use the correct error codes
870
          //check these against the docs and correct them
871
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
872
                  "  If you are not logged in, please do so and retry the request.");
873
      }
874
      
875
      // verify that guid == SystemMetadata.getIdentifier()
876
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
877
          "|" + sysmeta.getIdentifier().getValue());
878
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
879
          throw new InvalidRequest("4863", 
880
              "The identifier in method call (" + pid.getValue() + 
881
              ") does not match identifier in system metadata (" +
882
              sysmeta.getIdentifier().getValue() + ").");
883
      }
884

    
885
      try {
886
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
887
          lock.lock();
888
          logMetacat.debug("Locked identifier " + pid.getValue());
889
          logMetacat.debug("Checking if identifier exists...");
890
          // Check that the identifier does not already exist
891
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
892
              throw new InvalidRequest("4863", 
893
                  "The identifier is already in use by an existing object.");
894
          
895
          }
896
          
897
          // insert the system metadata into the object store
898
          logMetacat.debug("Starting to insert SystemMetadata...");
899
          try {
900
              sysmeta.setSerialVersion(BigInteger.ONE);
901
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
902
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
903
              
904
          } catch (RuntimeException e) {
905
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
906
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
907
                  e.getClass() + ": " + e.getMessage());
908
              
909
          }
910
          
911
      } catch (RuntimeException e) {
912
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
913
                  e.getClass() + ": " + e.getMessage());
914
          
915
      }  finally {
916
          lock.unlock();
917
          logMetacat.debug("Unlocked identifier " + pid.getValue());
918
          
919
      }
920

    
921
      
922
      logMetacat.debug("Returning from registerSystemMetadata");
923
      EventLog.getInstance().log(request.getRemoteAddr(), 
924
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
925
          pid.getValue(), "registerSystemMetadata");
926
      return pid;
927
  }
928
  
929
  /**
930
   * Given an optional scope and format, reserves and returns an identifier 
931
   * within that scope and format that is unique and will not be 
932
   * used by any other sessions. 
933
    * 
934
   * @param session - the Session object containing the credentials for the Subject
935
   * @param pid - The identifier of the object to register the system metadata against
936
   * @param scope - An optional string to be used to qualify the scope of 
937
   *                the identifier namespace, which is applied differently 
938
   *                depending on the format requested. If scope is not 
939
   *                supplied, a default scope will be used.
940
   * @param format - The optional name of the identifier format to be used, 
941
   *                  drawn from a DataONE-specific vocabulary of identifier 
942
   *                 format names, including several common syntaxes such 
943
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
944
   *                 format is not supplied by the caller, the CN service 
945
   *                 will use a default identifier format, which may change 
946
   *                 over time.
947
   * 
948
   * @return true if the registration succeeds
949
   * 
950
   * @throws InvalidToken
951
   * @throws ServiceFailure
952
   * @throws NotAuthorized
953
   * @throws IdentifierNotUnique
954
   * @throws NotImplemented
955
   */
956
  @Override
957
  public Identifier reserveIdentifier(Session session, Identifier pid)
958
  throws InvalidToken, ServiceFailure,
959
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
960

    
961
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
962
  }
963
  
964
  @Override
965
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
966
  throws InvalidToken, ServiceFailure,
967
        NotAuthorized, NotImplemented, InvalidRequest {
968
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
969
  }
970
  
971
  /**
972
    * Checks whether the pid is reserved by the subject in the session param
973
    * If the reservation is held on the pid by the subject, we return true.
974
    * 
975
   * @param session - the Session object containing the Subject
976
   * @param pid - The identifier to check
977
   * 
978
   * @return true if the reservation exists for the subject/pid
979
   * 
980
   * @throws InvalidToken
981
   * @throws ServiceFailure
982
   * @throws NotFound - when the pid is not found (in use or in reservation)
983
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
984
   * @throws IdentifierNotUnique - when the pid is in use
985
   * @throws NotImplemented
986
   */
987

    
988
  @Override
989
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
990
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
991
      NotImplemented, InvalidRequest {
992
  
993
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
994
  }
995

    
996
  /**
997
   * Changes ownership (RightsHolder) of the specified object to the 
998
   * subject specified by userId
999
    * 
1000
   * @param session - the Session object containing the credentials for the Subject
1001
   * @param pid - Identifier of the object to be modified
1002
   * @param userId - The subject that will be taking ownership of the specified object.
1003
   *
1004
   * @return pid - the identifier of the modified object
1005
   * 
1006
   * @throws ServiceFailure
1007
   * @throws InvalidToken
1008
   * @throws NotFound
1009
   * @throws NotAuthorized
1010
   * @throws NotImplemented
1011
   * @throws InvalidRequest
1012
   */  
1013
  @Override
1014
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1015
      long serialVersion)
1016
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1017
      NotImplemented, InvalidRequest, VersionMismatch {
1018
      
1019
      // The lock to be used for this identifier
1020
      Lock lock = null;
1021

    
1022
      // get the subject
1023
      Subject subject = session.getSubject();
1024
      
1025
      // are we allowed to do this?
1026
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1027
          throw new NotAuthorized("4440", "not allowed by "
1028
                  + subject.getValue() + " on " + pid.getValue());
1029
          
1030
      }
1031
      
1032
      SystemMetadata systemMetadata = null;
1033
      try {
1034
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1035
          logMetacat.debug("Locked identifier " + pid.getValue());
1036

    
1037
          try {
1038
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1039
              
1040
              // does the request have the most current system metadata?
1041
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1042
                 String msg = "The requested system metadata version number " + 
1043
                     serialVersion + " differs from the current version at " +
1044
                     systemMetadata.getSerialVersion().longValue() +
1045
                     ". Please get the latest copy in order to modify it.";
1046
                 throw new VersionMismatch("4443", msg);
1047
              }
1048
              
1049
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1050
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1051
              
1052
          }
1053
              
1054
          // set the new rights holder
1055
          systemMetadata.setRightsHolder(userId);
1056
          
1057
          // update the metadata
1058
          try {
1059
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1060
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1061
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1062
              notifyReplicaNodes(systemMetadata);
1063
              
1064
          } catch (RuntimeException e) {
1065
              throw new ServiceFailure("4490", e.getMessage());
1066
          
1067
          }
1068
          
1069
      } catch (RuntimeException e) {
1070
          throw new ServiceFailure("4490", e.getMessage());
1071
          
1072
      } finally {
1073
          lock.unlock();
1074
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1075
      
1076
      }
1077
      
1078
      return pid;
1079
  }
1080

    
1081
  /**
1082
   * Verify that a replication task is authorized by comparing the target node's
1083
   * Subject (from the X.509 certificate-derived Session) with the list of 
1084
   * subjects in the known, pending replication tasks map.
1085
   * 
1086
   * @param originatingNodeSession - Session information that contains the 
1087
   *                                 identity of the calling user
1088
   * @param targetNodeSubject - Subject identifying the target node
1089
   * @param pid - the identifier of the object to be replicated
1090
   * @param replicatePermission - the execute permission to be granted
1091
   * 
1092
   * @throws ServiceFailure
1093
   * @throws NotImplemented
1094
   * @throws InvalidToken
1095
   * @throws NotAuthorized
1096
   * @throws InvalidRequest
1097
   * @throws NotFound
1098
   */
1099
  @Override
1100
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1101
    Subject targetNodeSubject, Identifier pid) 
1102
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1103
    NotFound, InvalidRequest {
1104
    
1105
    boolean isAllowed = false;
1106
    SystemMetadata sysmeta = null;
1107
    NodeReference targetNode = null;
1108
    
1109
    try {
1110
      // get the target node reference from the nodes list
1111
      CNode cn = D1Client.getCN();
1112
      List<Node> nodes = cn.listNodes().getNodeList();
1113
      
1114
      if ( nodes != null ) {
1115
        for (Node node : nodes) {
1116
            
1117
            for (Subject nodeSubject : node.getSubjectList()) {
1118
                
1119
                if ( nodeSubject.equals(targetNodeSubject) ) {
1120
                    targetNode = node.getIdentifier();
1121
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1122
                    break;
1123
                }
1124
            }
1125
            
1126
            if ( targetNode != null) { break; }
1127
        }
1128
        
1129
      } else {
1130
          String msg = "Couldn't get the node list from the CN";
1131
          logMetacat.debug(msg);
1132
          throw new ServiceFailure("4872", msg);
1133
          
1134
      }
1135
      
1136
      // can't find a node listed with the given subject
1137
      if ( targetNode == null ) {
1138
          String msg = "There is no Member Node registered with a node subject " +
1139
              "matching " + targetNodeSubject.getValue();
1140
          logMetacat.info(msg);
1141
          throw new NotAuthorized("4871", msg);
1142
          
1143
      }
1144
      
1145
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1146
      
1147
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1148

    
1149
      if ( sysmeta != null ) {
1150
          
1151
          List<Replica> replicaList = sysmeta.getReplicaList();
1152
          
1153
          if ( replicaList != null ) {
1154
              
1155
              // find the replica with the status set to 'requested'
1156
              for (Replica replica : replicaList) {
1157
                  ReplicationStatus status = replica.getReplicationStatus();
1158
                  NodeReference listedNode = replica.getReplicaMemberNode();
1159
                  if ( listedNode != null && targetNode != null ) {
1160
                      logMetacat.debug("Comparing " + listedNode.getValue()
1161
                              + " to " + targetNode.getValue());
1162
                      
1163
                      if (listedNode.getValue().equals(targetNode.getValue())
1164
                              && status.equals(ReplicationStatus.REQUESTED)) {
1165
                          isAllowed = true;
1166
                          break;
1167

    
1168
                      }
1169
                  }
1170
              }
1171
          }
1172
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1173
              "to replicate: " + isAllowed + " for " + pid.getValue());
1174

    
1175
          
1176
      } else {
1177
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1178
          " is null.");          
1179
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1180
          
1181
      }
1182

    
1183
    } catch (RuntimeException e) {
1184
    	  ServiceFailure sf = new ServiceFailure("4872", 
1185
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1186
                e.getCause().getMessage());
1187
    	  sf.initCause(e);
1188
        throw sf;
1189
        
1190
    }
1191
      
1192
    return isAllowed;
1193
    
1194
  }
1195

    
1196
  /**
1197
   * Adds a new object to the Node, where the object is a science metadata object.
1198
   * 
1199
   * @param session - the Session object containing the credentials for the Subject
1200
   * @param pid - The object identifier to be created
1201
   * @param object - the object bytes
1202
   * @param sysmeta - the system metadata that describes the object  
1203
   * 
1204
   * @return pid - the object identifier created
1205
   * 
1206
   * @throws InvalidToken
1207
   * @throws ServiceFailure
1208
   * @throws NotAuthorized
1209
   * @throws IdentifierNotUnique
1210
   * @throws UnsupportedType
1211
   * @throws InsufficientResources
1212
   * @throws InvalidSystemMetadata
1213
   * @throws NotImplemented
1214
   * @throws InvalidRequest
1215
   */
1216
  public Identifier create(Session session, Identifier pid, InputStream object,
1217
    SystemMetadata sysmeta) 
1218
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1219
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1220
    NotImplemented, InvalidRequest {
1221
                  
1222
      // The lock to be used for this identifier
1223
      Lock lock = null;
1224

    
1225
      try {
1226
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1227
          // are we allowed?
1228
          boolean isAllowed = false;
1229
          CNode cn = D1Client.getCN();
1230
          NodeList nodeList = cn.listNodes();
1231
          
1232
          for (Node node : nodeList.getNodeList()) {
1233
              if ( node.getType().equals(NodeType.CN) ) {
1234
                  
1235
                  List<Subject> subjects = node.getSubjectList();
1236
                  for (Subject subject : subjects) {
1237
                     if (subject.equals(session.getSubject())) {
1238
                         isAllowed = true;
1239
                         break;
1240
                     }
1241
                  }
1242
              } else {
1243
                  
1244
              }
1245
          }
1246

    
1247
          // proceed if we're called by a CN
1248
          if ( isAllowed ) {
1249
              // create the coordinating node version of the document      
1250
              lock.lock();
1251
              logMetacat.debug("Locked identifier " + pid.getValue());
1252
              sysmeta.setSerialVersion(BigInteger.ONE);
1253
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1254
              sysmeta.setArchived(false); // this is a create op, not update
1255
              
1256
              // the CN should have set the origin and authoritative member node fields
1257
              try {
1258
                  sysmeta.getOriginMemberNode().getValue();
1259
                  sysmeta.getAuthoritativeMemberNode().getValue();
1260
                  
1261
              } catch (NullPointerException npe) {
1262
                  throw new InvalidSystemMetadata("4896", 
1263
                      "Both the origin and authoritative member node identifiers need to be set.");
1264
                  
1265
              }
1266
              pid = super.create(session, pid, object, sysmeta);
1267

    
1268
          } else {
1269
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1270
                  " isn't allowed to call create() on a Coordinating Node.";
1271
              logMetacat.info(msg);
1272
              throw new NotAuthorized("1100", msg);
1273
          }
1274
          
1275
      } catch (RuntimeException e) {
1276
          // Convert Hazelcast runtime exceptions to service failures
1277
          String msg = "There was a problem creating the object identified by " +
1278
              pid.getValue() + ". There error message was: " + e.getMessage();
1279
          throw new ServiceFailure("4893", msg);
1280
          
1281
      } finally {
1282
    	  if (lock != null) {
1283
	          lock.unlock();
1284
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1285
    	  }
1286
      }
1287
      
1288
      return pid;
1289

    
1290
  }
1291

    
1292
  /**
1293
   * Set access for a given object using the object identifier and a Subject
1294
   * under a given Session.
1295
   * 
1296
   * @param session - the Session object containing the credentials for the Subject
1297
   * @param pid - the object identifier for the given object to apply the policy
1298
   * @param policy - the access policy to be applied
1299
   * 
1300
   * @return true if the application of the policy succeeds
1301
   * @throws InvalidToken
1302
   * @throws ServiceFailure
1303
   * @throws NotFound
1304
   * @throws NotAuthorized
1305
   * @throws NotImplemented
1306
   * @throws InvalidRequest
1307
   */
1308
  public boolean setAccessPolicy(Session session, Identifier pid, 
1309
      AccessPolicy accessPolicy, long serialVersion) 
1310
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1311
      NotImplemented, InvalidRequest, VersionMismatch {
1312
      
1313
      // The lock to be used for this identifier
1314
      Lock lock = null;
1315
      SystemMetadata systemMetadata = null;
1316
      
1317
      boolean success = false;
1318
      
1319
      // get the subject
1320
      Subject subject = session.getSubject();
1321
      
1322
      // are we allowed to do this?
1323
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1324
          throw new NotAuthorized("4420", "not allowed by "
1325
                  + subject.getValue() + " on " + pid.getValue());
1326
      }
1327
      
1328
      try {
1329
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1330
          lock.lock();
1331
          logMetacat.debug("Locked identifier " + pid.getValue());
1332

    
1333
          try {
1334
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1335

    
1336
              if ( systemMetadata == null ) {
1337
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1338
                  
1339
              }
1340
              // does the request have the most current system metadata?
1341
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1342
                 String msg = "The requested system metadata version number " + 
1343
                     serialVersion + " differs from the current version at " +
1344
                     systemMetadata.getSerialVersion().longValue() +
1345
                     ". Please get the latest copy in order to modify it.";
1346
                 throw new VersionMismatch("4402", msg);
1347
                 
1348
              }
1349
              
1350
          } catch (RuntimeException e) {
1351
              // convert Hazelcast RuntimeException to NotFound
1352
              throw new NotFound("4400", "No record found for: " + pid);
1353
            
1354
          }
1355
              
1356
          // set the access policy
1357
          systemMetadata.setAccessPolicy(accessPolicy);
1358
          
1359
          // update the system metadata
1360
          try {
1361
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1362
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1363
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1364
              notifyReplicaNodes(systemMetadata);
1365
              
1366
          } catch (RuntimeException e) {
1367
              // convert Hazelcast RuntimeException to ServiceFailure
1368
              throw new ServiceFailure("4430", e.getMessage());
1369
            
1370
          }
1371
          
1372
      } catch (RuntimeException e) {
1373
          throw new ServiceFailure("4430", e.getMessage());
1374
          
1375
      } finally {
1376
          lock.unlock();
1377
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1378
        
1379
      }
1380

    
1381
    
1382
    // TODO: how do we know if the map was persisted?
1383
    success = true;
1384
    
1385
    return success;
1386
  }
1387

    
1388
  /**
1389
   * Full replacement of replication metadata in the system metadata for the 
1390
   * specified object, changes date system metadata modified
1391
   * 
1392
   * @param session - the Session object containing the credentials for the Subject
1393
   * @param pid - the object identifier for the given object to apply the policy
1394
   * @param replica - the replica to be updated
1395
   * @return
1396
   * @throws NotImplemented
1397
   * @throws NotAuthorized
1398
   * @throws ServiceFailure
1399
   * @throws InvalidRequest
1400
   * @throws NotFound
1401
   * @throws VersionMismatch
1402
   */
1403
  @Override
1404
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1405
      Replica replica, long serialVersion) 
1406
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1407
      NotFound, VersionMismatch {
1408
      
1409
      // The lock to be used for this identifier
1410
      Lock lock = null;
1411
      
1412
      // get the subject
1413
      Subject subject = session.getSubject();
1414
      
1415
      // are we allowed to do this?
1416
      try {
1417

    
1418
          // what is the controlling permission?
1419
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1420
              throw new NotAuthorized("4851", "not allowed by "
1421
                      + subject.getValue() + " on " + pid.getValue());
1422
          }
1423

    
1424
        
1425
      } catch (InvalidToken e) {
1426
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1427
                  " on " + pid.getValue());  
1428
          
1429
      }
1430

    
1431
      SystemMetadata systemMetadata = null;
1432
      try {
1433
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1434
          lock.lock();
1435
          logMetacat.debug("Locked identifier " + pid.getValue());
1436

    
1437
          try {      
1438
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1439

    
1440
              // does the request have the most current system metadata?
1441
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1442
                 String msg = "The requested system metadata version number " + 
1443
                     serialVersion + " differs from the current version at " +
1444
                     systemMetadata.getSerialVersion().longValue() +
1445
                     ". Please get the latest copy in order to modify it.";
1446
                 throw new VersionMismatch("4855", msg);
1447
              }
1448
              
1449
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1450
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1451
                  " : " + e.getMessage());
1452
            
1453
          }
1454
              
1455
          // set the status for the replica
1456
          List<Replica> replicas = systemMetadata.getReplicaList();
1457
          NodeReference replicaNode = replica.getReplicaMemberNode();
1458
          int index = 0;
1459
          for (Replica listedReplica: replicas) {
1460
              
1461
              // remove the replica that we are replacing
1462
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1463
                  replicas.remove(index);
1464
                  break;
1465
                  
1466
              }
1467
              index++;
1468
          }
1469
          
1470
          // add the new replica item
1471
          replicas.add(replica);
1472
          systemMetadata.setReplicaList(replicas);
1473
          
1474
          // update the metadata
1475
          try {
1476
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1477
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1478
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1479
            
1480
          } catch (RuntimeException e) {
1481
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1482
              throw new ServiceFailure("4852", e.getMessage());
1483
          
1484
          }
1485
          
1486
      } catch (RuntimeException e) {
1487
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1488
          throw new ServiceFailure("4852", e.getMessage());
1489
      
1490
      } finally {
1491
          lock.unlock();
1492
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1493
          
1494
      }
1495
    
1496
      return true;
1497
      
1498
  }
1499
  
1500
  /**
1501
   * 
1502
   */
1503
  @Override
1504
  public ObjectList listObjects(Session session, Date startTime, 
1505
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1506
      Integer start, Integer count)
1507
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1508
      ServiceFailure {
1509
      
1510
      ObjectList objectList = null;
1511
        try {
1512
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1513
        } catch (Exception e) {
1514
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1515
        }
1516

    
1517
        return objectList;
1518
  }
1519

    
1520
  
1521
 	/**
1522
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1523
 	 * @return cal  the list of checksum algorithms
1524
 	 * 
1525
 	 * @throws ServiceFailure
1526
 	 * @throws NotImplemented
1527
 	 */
1528
  @Override
1529
  public ChecksumAlgorithmList listChecksumAlgorithms()
1530
			throws ServiceFailure, NotImplemented {
1531
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1532
		cal.addAlgorithm("MD5");
1533
		cal.addAlgorithm("SHA-1");
1534
		return cal;
1535
		
1536
	}
1537

    
1538
  /**
1539
   * Notify replica Member Nodes of system metadata changes for a given pid
1540
   * 
1541
   * @param currentSystemMetadata - the up to date system metadata
1542
   */
1543
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1544
      
1545
      Session session = null;
1546
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1547
      MNode mn = null;
1548
      NodeReference replicaNodeRef = null;
1549
      CNode cn = null;
1550
      NodeType nodeType = null;
1551
      List<Node> nodeList = null;
1552
      
1553
      try {
1554
          cn = D1Client.getCN();
1555
          nodeList = cn.listNodes().getNodeList();
1556
          
1557
      } catch (Exception e) { // handle BaseException and other I/O issues
1558
          
1559
          // swallow errors since the call is not critical
1560
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1561
              "to communication issues with the CN: " + e.getMessage());
1562
          
1563
      }
1564
      
1565
      if ( replicaList != null ) {
1566
          
1567
          // iterate through the replicas and inform  MN replica nodes
1568
          for (Replica replica : replicaList) {
1569
              
1570
              replicaNodeRef = replica.getReplicaMemberNode();
1571
              try {
1572
                  if (nodeList != null) {
1573
                      // find the node type
1574
                      for (Node node : nodeList) {
1575
                          if (node.getIdentifier().getValue() == 
1576
                              replicaNodeRef.getValue()) {
1577
                              nodeType = node.getType();
1578
                              break;
1579
              
1580
                          }
1581
                      }
1582
                  }
1583
              
1584
                  // notify only MNs
1585
                  if (nodeType != null && nodeType == NodeType.MN) {
1586
                      mn = D1Client.getMN(replicaNodeRef);
1587
                      mn.systemMetadataChanged(session, 
1588
                          currentSystemMetadata.getIdentifier(), 
1589
                          currentSystemMetadata.getSerialVersion().longValue(),
1590
                          currentSystemMetadata.getDateSysMetadataModified());
1591
                  }
1592
              
1593
              } catch (Exception e) { // handle BaseException and other I/O issues
1594
              
1595
                  // swallow errors since the call is not critical
1596
                  logMetacat.error("Can't inform "
1597
                          + replicaNodeRef.getValue()
1598
                          + " of system metadata changes due "
1599
                          + "to communication issues with the CN: "
1600
                          + e.getMessage());
1601
              
1602
              }
1603
          }
1604
      }
1605
  }
1606
}
(1-1/5)