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.service.cn.v1.CNAuthorization;
40
import org.dataone.service.cn.v1.CNCore;
41
import org.dataone.service.cn.v1.CNRead;
42
import org.dataone.service.cn.v1.CNReplication;
43
import org.dataone.service.exceptions.BaseException;
44
import org.dataone.service.exceptions.IdentifierNotUnique;
45
import org.dataone.service.exceptions.InsufficientResources;
46
import org.dataone.service.exceptions.InvalidRequest;
47
import org.dataone.service.exceptions.InvalidSystemMetadata;
48
import org.dataone.service.exceptions.InvalidToken;
49
import org.dataone.service.exceptions.NotAuthorized;
50
import org.dataone.service.exceptions.NotFound;
51
import org.dataone.service.exceptions.NotImplemented;
52
import org.dataone.service.exceptions.ServiceFailure;
53
import org.dataone.service.exceptions.UnsupportedType;
54
import org.dataone.service.exceptions.VersionMismatch;
55
import org.dataone.service.types.v1.AccessPolicy;
56
import org.dataone.service.types.v1.Checksum;
57
import org.dataone.service.types.v1.ChecksumAlgorithmList;
58
import org.dataone.service.types.v1.Identifier;
59
import org.dataone.service.types.v1.Node;
60
import org.dataone.service.types.v1.NodeList;
61
import org.dataone.service.types.v1.NodeReference;
62
import org.dataone.service.types.v1.NodeType;
63
import org.dataone.service.types.v1.ObjectFormat;
64
import org.dataone.service.types.v1.ObjectFormatIdentifier;
65
import org.dataone.service.types.v1.ObjectFormatList;
66
import org.dataone.service.types.v1.ObjectList;
67
import org.dataone.service.types.v1.ObjectLocationList;
68
import org.dataone.service.types.v1.Permission;
69
import org.dataone.service.types.v1.Replica;
70
import org.dataone.service.types.v1.ReplicationPolicy;
71
import org.dataone.service.types.v1.ReplicationStatus;
72
import org.dataone.service.types.v1.Session;
73
import org.dataone.service.types.v1.Subject;
74
import org.dataone.service.types.v1.SystemMetadata;
75
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
76

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

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

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

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

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

    
166
              // does the request have the most current system metadata?
167
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
168
                 String msg = "The requested system metadata version number " + 
169
                     serialVersion + " differs from the current version at " +
170
                     systemMetadata.getSerialVersion().longValue() +
171
                     ". Please get the latest copy in order to modify it.";
172
                 throw new VersionMismatch("4886", msg);
173
                 
174
              }
175
              
176
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
177
              throw new NotFound("4884", "No record found for: " + pid.getValue());
178
            
179
          }
180
          
181
          // set the new policy
182
          systemMetadata.setReplicationPolicy(policy);
183
          
184
          // update the metadata
185
          try {
186
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
187
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
188
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), 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
		if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
234
			boolean isAuthorized = false;
235
			try {
236
				isAuthorized = isAuthorized(session, pid, Permission.WRITE);
237
			} catch (InvalidRequest e) {
238
				throw new ServiceFailure("4882", e.getDescription());
239
			}
240
			if (!isAuthorized) {
241
				throw new NotAuthorized("4881", Permission.WRITE
242
						+ " not allowed by " + subject.getValue() + " on "
243
						+ pid.getValue());
244

    
245
			}
246
		}
247

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

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

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

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

    
273
				}
274

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

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

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

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

    
317
		return true;	  
318
	  
319
  }
320
  
321
  /**
322
   * Set the obsoletedBy attribute in System Metadata
323
   * @param session
324
   * @param pid
325
   * @param obsoletedByPid
326
   * @param serialVersion
327
   * @return
328
   * @throws NotImplemented
329
   * @throws NotFound
330
   * @throws NotAuthorized
331
   * @throws ServiceFailure
332
   * @throws InvalidRequest
333
   * @throws InvalidToken
334
   * @throws VersionMismatch
335
   */
336
  @Override
337
  public boolean setObsoletedBy(Session session, Identifier pid,
338
			Identifier obsoletedByPid, long serialVersion)
339
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
340
			InvalidRequest, InvalidToken, VersionMismatch {
341

    
342
		// The lock to be used for this identifier
343
		Lock lock = null;
344

    
345
		// get the subject
346
		Subject subject = session.getSubject();
347

    
348
		// are we allowed to do this?
349
		if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
350
			if (!isAuthorized(session, pid, Permission.WRITE)) {
351
				throw new NotAuthorized("4881", Permission.WRITE
352
						+ " not allowed by " + subject.getValue() + " on "
353
						+ pid.getValue());
354

    
355
			}
356
		}
357

    
358
		SystemMetadata systemMetadata = null;
359
		try {
360
			lock = HazelcastService.getInstance().getLock(pid.getValue());
361
			lock.lock();
362
			logMetacat.debug("Locked identifier " + pid.getValue());
363

    
364
			try {
365
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
366
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
367
				}
368

    
369
				// did we get it correctly?
370
				if (systemMetadata == null) {
371
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
372
				}
373

    
374
				// does the request have the most current system metadata?
375
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
376
					String msg = "The requested system metadata version number "
377
							+ serialVersion
378
							+ " differs from the current version at "
379
							+ systemMetadata.getSerialVersion().longValue()
380
							+ ". Please get the latest copy in order to modify it.";
381
					throw new VersionMismatch("4886", msg);
382

    
383
				}
384

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

    
388
			}
389

    
390
			// set the new policy
391
			systemMetadata.setObsoletedBy(obsoletedByPid);
392

    
393
			// update the metadata
394
			try {
395
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
396
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
397
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
398
			} catch (RuntimeException e) {
399
				throw new ServiceFailure("4882", e.getMessage());
400
			}
401

    
402
		} catch (RuntimeException e) {
403
			throw new ServiceFailure("4882", e.getMessage());
404
		} finally {
405
			lock.unlock();
406
			logMetacat.debug("Unlocked identifier " + pid.getValue());
407
		}
408

    
409
		return true;
410
	}
411
  
412
  
413
  /**
414
   * Set the replication status for an object given the object identifier
415
   * 
416
   * @param session - the Session object containing the credentials for the Subject
417
   * @param pid - the object identifier for the given object
418
   * @param status - the replication status to be applied
419
   * 
420
   * @return true or false
421
   * 
422
   * @throws NotImplemented
423
   * @throws NotAuthorized
424
   * @throws ServiceFailure
425
   * @throws InvalidRequest
426
   * @throws InvalidToken
427
   * @throws NotFound
428
   * 
429
   */
430
  @Override
431
  public boolean setReplicationStatus(Session session, Identifier pid,
432
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
433
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
434
      InvalidRequest, NotFound {
435
      
436
      // The lock to be used for this identifier
437
      Lock lock = null;
438
      
439
      boolean allowed = false;
440
      int replicaEntryIndex = -1;
441
      List<Replica> replicas = null;
442
      // get the subject
443
      Subject subject = session.getSubject();
444
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
445
          " is " + status.toString());
446
      
447
      SystemMetadata systemMetadata = null;
448

    
449
      try {
450
          lock = HazelcastService.getInstance().getLock(pid.getValue());
451
          lock.lock();
452
          logMetacat.debug("Locked identifier " + pid.getValue());
453

    
454
          try {      
455
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
456

    
457
              // did we get it correctly?
458
              if ( systemMetadata == null ) {
459
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
460
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
461
                  
462
              }
463
              replicas = systemMetadata.getReplicaList();
464
              int count = 0;
465
              
466
              // was there a failure? log it
467
              if ( failure != null && status == ReplicationStatus.FAILED ) {
468
                 String msg = "The replication request of the object identified by " + 
469
                     pid.getValue() + " failed.  The error message was " +
470
                     failure.getMessage() + ".";
471
              }
472
              
473
              if (replicas.size() > 0 && replicas != null) {
474
                  // find the target replica index in the replica list
475
                  for (Replica replica : replicas) {
476
                      String replicaNodeStr = replica.getReplicaMemberNode()
477
                              .getValue();
478
                      String targetNodeStr = targetNode.getValue();
479
                      logMetacat.debug("Comparing " + replicaNodeStr + " to "
480
                              + targetNodeStr);
481
                  
482
                      if (replicaNodeStr.equals(targetNodeStr)) {
483
                          replicaEntryIndex = count;
484
                          logMetacat.debug("replica entry index is: "
485
                                  + replicaEntryIndex);
486
                          break;
487
                      }
488
                      count++;
489
                  
490
                  }
491
              }
492
              // are we allowed to do this? only CNs and target MNs are allowed
493
              CNode cn = D1Client.getCN();
494
              List<Node> nodes = cn.listNodes().getNodeList();
495
              
496
              // find the node in the node list
497
              for ( Node node : nodes ) {
498
                  
499
                  NodeReference nodeReference = node.getIdentifier();
500
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
501
                  
502
                  // allow target MN certs and CN certs
503
                  if (targetNode.getValue().equals(nodeReference.getValue()) ||
504
                      node.getType() == NodeType.CN) {
505
                      List<Subject> nodeSubjects = node.getSubjectList();
506
                      
507
                      // check if the session subject is in the node subject list
508
                      for (Subject nodeSubject : nodeSubjects) {
509
                          if ( nodeSubject.equals(subject) ) {
510
                              allowed = true; // subject of session == target node subject
511
                              break;
512
                              
513
                          }
514
                      }                 
515
                  }
516
              }
517

    
518
              if ( !isAdminAuthorized(session, pid, Permission.WRITE) ) {
519
                  if (!allowed) {
520
                    String msg = "The subject identified by "
521
                            + subject.getValue()
522
                            + " does not have permission to set the replication status for "
523
                            + "the replica identified by "
524
                            + targetNode.getValue() + ".";
525
                    logMetacat.info(msg);
526
                    throw new NotAuthorized("4720", msg);
527
                }
528
                  
529
              }
530
              
531

    
532
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
533
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
534
                " : " + e.getMessage());
535
            
536
          }
537
          
538
          Replica targetReplica = new Replica();
539
          // set the status for the replica
540
          if ( replicaEntryIndex != -1 ) {
541
              targetReplica = replicas.get(replicaEntryIndex);
542
              targetReplica.setReplicationStatus(status);
543
              logMetacat.debug("Set the replication status for " + 
544
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
545
                  targetReplica.getReplicationStatus());
546
              
547
          } else {
548
              // this is a new entry, create it
549
              targetReplica.setReplicaMemberNode(targetNode);
550
              targetReplica.setReplicationStatus(status);
551
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
552
              replicas.add(targetReplica);
553
              
554
          }
555
          
556
          systemMetadata.setReplicaList(replicas);
557
                
558
          // update the metadata
559
          try {
560
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
561
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
562
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
563
              
564
              if ( status == ReplicationStatus.FAILED && failure != null ) {
565
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
566
                      " on target node " + targetNode + ". The exception was: " +
567
                      failure.getMessage());
568
              }
569
          } catch (RuntimeException e) {
570
              throw new ServiceFailure("4700", e.getMessage());
571
          
572
          }
573
          
574
    } catch (RuntimeException e) {
575
        String msg = "There was a RuntimeException getting the lock for " +
576
            pid.getValue();
577
        logMetacat.info(msg);
578
        
579
    } finally {
580
        lock.unlock();
581
        logMetacat.debug("Unlocked identifier " + pid.getValue());
582
        
583
    }
584
      
585
      return true;
586
  }
587
  
588
  /**
589
   * Return the checksum of the object given the identifier 
590
   * 
591
   * @param session - the Session object containing the credentials for the Subject
592
   * @param pid - the object identifier for the given object
593
   * 
594
   * @return checksum - the checksum of the object
595
   * 
596
   * @throws InvalidToken
597
   * @throws ServiceFailure
598
   * @throws NotAuthorized
599
   * @throws NotFound
600
   * @throws NotImplemented
601
   */
602
  @Override
603
  public Checksum getChecksum(Session session, Identifier pid)
604
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
605
    NotImplemented {
606
    
607
	boolean isAuthorized = false;
608
	try {
609
		isAuthorized = isAuthorized(session, pid, Permission.READ);
610
	} catch (InvalidRequest e) {
611
		throw new ServiceFailure("1410", e.getDescription());
612
	}  
613
    if (!isAuthorized) {
614
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
615
    }
616
    
617
    SystemMetadata systemMetadata = null;
618
    Checksum checksum = null;
619
    
620
    try {
621
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
622

    
623
        if (systemMetadata == null ) {
624
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
625
        }
626
        checksum = systemMetadata.getChecksum();
627
        
628
    } catch (RuntimeException e) {
629
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
630
            pid.getValue() + ". The error message was: " + e.getMessage());
631
      
632
    }
633
    
634
    return checksum;
635
  }
636

    
637
  /**
638
   * Resolve the location of a given object
639
   * 
640
   * @param session - the Session object containing the credentials for the Subject
641
   * @param pid - the object identifier for the given object
642
   * 
643
   * @return objectLocationList - the list of nodes known to contain the object
644
   * 
645
   * @throws InvalidToken
646
   * @throws ServiceFailure
647
   * @throws NotAuthorized
648
   * @throws NotFound
649
   * @throws NotImplemented
650
   */
651
  @Override
652
  public ObjectLocationList resolve(Session session, Identifier pid)
653
    throws InvalidToken, ServiceFailure, NotAuthorized,
654
    NotFound, NotImplemented {
655

    
656
    throw new NotImplemented("4131", "resolve not implemented");
657

    
658
  }
659

    
660
  /**
661
   * Search the metadata catalog for identifiers that match the criteria
662
   * 
663
   * @param session - the Session object containing the credentials for the Subject
664
   * @param queryType - An identifier for the type of query expression 
665
   *                    provided in the query
666
   * @param query -  The criteria for matching the characteristics of the 
667
   *                 metadata objects of interest
668
   * 
669
   * @return objectList - the list of objects matching the criteria
670
   * 
671
   * @throws InvalidToken
672
   * @throws ServiceFailure
673
   * @throws NotAuthorized
674
   * @throws InvalidRequest
675
   * @throws NotImplemented
676
   */
677
  @Override
678
  public ObjectList search(Session session, String queryType, String query)
679
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
680
    NotImplemented {
681

    
682
    ObjectList objectList = null;
683
    try {
684
        objectList = 
685
          IdentifierManager.getInstance().querySystemMetadata(
686
              null, //startTime, 
687
              null, //endTime,
688
              null, //objectFormat, 
689
              false, //replicaStatus, 
690
              0, //start, 
691
              -1 //count
692
              );
693
        
694
    } catch (Exception e) {
695
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
696
    }
697

    
698
      return objectList;
699
      
700
    //throw new NotImplemented("4281", "search not implemented");
701
    
702
    // the code block below is from an older implementation
703
    
704
    /*  This block commented out because of the EcoGrid circular dependency.
705
         *  For now, query will not be supported until the circularity can be
706
         *  resolved, probably by moving the ecogrid query syntax transformers
707
         *  directly into the Metacat codebase.  MBJ 2010-02-03
708
         
709
        try {
710
            EcogridQueryParser parser = new EcogridQueryParser(request
711
                    .getReader());
712
            parser.parseXML();
713
            QueryType queryType = parser.getEcogridQuery();
714
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
715
                new EcogridJavaToMetacatJavaQueryTransformer();
716
            QuerySpecification metacatQuery = queryTransformer
717
                    .transform(queryType);
718

    
719
            DBQuery metacat = new DBQuery();
720

    
721
            boolean useXMLIndex = (new Boolean(PropertyService
722
                    .getProperty("database.usexmlindex"))).booleanValue();
723
            String xmlquery = "query"; // we don't care the query in resultset,
724
            // the query can be anything
725
            PrintWriter out = null; // we don't want metacat result, so set out null
726

    
727
            // parameter: queryspecification, user, group, usingIndexOrNot
728
            StringBuffer result = metacat.createResultDocument(xmlquery,
729
                    metacatQuery, out, username, groupNames, useXMLIndex);
730

    
731
            // create result set transfer       
732
            String saxparser = PropertyService.getProperty("xml.saxparser");
733
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
734
                    new StringReader(result.toString()), saxparser, queryType
735
                            .getNamespace().get_value());
736
            ResultsetType records = metacatResultsetParser.getEcogridResult();
737

    
738
            System.out
739
                    .println(EcogridResultsetTransformer.toXMLString(records));
740
            response.setContentType("text/xml");
741
            out = response.getWriter();
742
            out.print(EcogridResultsetTransformer.toXMLString(records));
743

    
744
        } catch (Exception e) {
745
            e.printStackTrace();
746
        }*/
747
    
748

    
749
  }
750
  
751
  /**
752
   * Returns the object format registered in the DataONE Object Format 
753
   * Vocabulary for the given format identifier
754
   * 
755
   * @param fmtid - the identifier of the format requested
756
   * 
757
   * @return objectFormat - the object format requested
758
   * 
759
   * @throws ServiceFailure
760
   * @throws NotFound
761
   * @throws InsufficientResources
762
   * @throws NotImplemented
763
   */
764
  @Override
765
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
766
    throws ServiceFailure, NotFound, NotImplemented {
767
     
768
      return ObjectFormatService.getInstance().getFormat(fmtid);
769
      
770
  }
771

    
772
  /**
773
   * Returns a list of all object formats registered in the DataONE Object 
774
   * Format Vocabulary
775
    * 
776
   * @return objectFormatList - The list of object formats registered in 
777
   *                            the DataONE Object Format Vocabulary
778
   * 
779
   * @throws ServiceFailure
780
   * @throws NotImplemented
781
   * @throws InsufficientResources
782
   */
783
  @Override
784
  public ObjectFormatList listFormats() 
785
    throws ServiceFailure, NotImplemented {
786

    
787
    return ObjectFormatService.getInstance().listFormats();
788
  }
789

    
790
  /**
791
   * Returns a list of nodes that have been registered with the DataONE infrastructure
792
    * 
793
   * @return nodeList - List of nodes from the registry
794
   * 
795
   * @throws ServiceFailure
796
   * @throws NotImplemented
797
   */
798
  @Override
799
  public NodeList listNodes() 
800
    throws NotImplemented, ServiceFailure {
801

    
802
    throw new NotImplemented("4800", "listNodes not implemented");
803
  }
804

    
805
  /**
806
   * Provides a mechanism for adding system metadata independently of its 
807
   * associated object, such as when adding system metadata for data objects.
808
    * 
809
   * @param session - the Session object containing the credentials for the Subject
810
   * @param pid - The identifier of the object to register the system metadata against
811
   * @param sysmeta - The system metadata to be registered
812
   * 
813
   * @return true if the registration succeeds
814
   * 
815
   * @throws NotImplemented
816
   * @throws NotAuthorized
817
   * @throws ServiceFailure
818
   * @throws InvalidRequest
819
   * @throws InvalidSystemMetadata
820
   */
821
  @Override
822
  public Identifier registerSystemMetadata(Session session, Identifier pid,
823
      SystemMetadata sysmeta) 
824
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
825
      InvalidSystemMetadata {
826

    
827
      // The lock to be used for this identifier
828
      Lock lock = null;
829

    
830
      // TODO: control who can call this?
831
      if (session == null) {
832
          //TODO: many of the thrown exceptions do not use the correct error codes
833
          //check these against the docs and correct them
834
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
835
                  "  If you are not logged in, please do so and retry the request.");
836
      }
837
      
838
      // verify that guid == SystemMetadata.getIdentifier()
839
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
840
          "|" + sysmeta.getIdentifier().getValue());
841
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
842
          throw new InvalidRequest("4863", 
843
              "The identifier in method call (" + pid.getValue() + 
844
              ") does not match identifier in system metadata (" +
845
              sysmeta.getIdentifier().getValue() + ").");
846
      }
847

    
848
      try {
849
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
850
          lock.lock();
851
          logMetacat.debug("Locked identifier " + pid.getValue());
852
          logMetacat.debug("Checking if identifier exists...");
853
          // Check that the identifier does not already exist
854
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
855
              throw new InvalidRequest("4863", 
856
                  "The identifier is already in use by an existing object.");
857
          
858
          }
859
          
860
          // insert the system metadata into the object store
861
          logMetacat.debug("Starting to insert SystemMetadata...");
862
          try {
863
              sysmeta.setSerialVersion(BigInteger.ONE);
864
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
865
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
866
              
867
          } catch (RuntimeException e) {
868
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
869
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
870
                  e.getClass() + ": " + e.getMessage());
871
              
872
          }
873
          
874
      } catch (RuntimeException e) {
875
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
876
                  e.getClass() + ": " + e.getMessage());
877
          
878
      }  finally {
879
          lock.unlock();
880
          logMetacat.debug("Unlocked identifier " + pid.getValue());
881
          
882
      }
883

    
884
      
885
      logMetacat.debug("Returning from registerSystemMetadata");
886
      EventLog.getInstance().log(request.getRemoteAddr(), 
887
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
888
          pid.getValue(), "registerSystemMetadata");
889
      return pid;
890
  }
891
  
892
  /**
893
   * Given an optional scope and format, reserves and returns an identifier 
894
   * within that scope and format that is unique and will not be 
895
   * used by any other sessions. 
896
    * 
897
   * @param session - the Session object containing the credentials for the Subject
898
   * @param pid - The identifier of the object to register the system metadata against
899
   * @param scope - An optional string to be used to qualify the scope of 
900
   *                the identifier namespace, which is applied differently 
901
   *                depending on the format requested. If scope is not 
902
   *                supplied, a default scope will be used.
903
   * @param format - The optional name of the identifier format to be used, 
904
   *                  drawn from a DataONE-specific vocabulary of identifier 
905
   *                 format names, including several common syntaxes such 
906
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
907
   *                 format is not supplied by the caller, the CN service 
908
   *                 will use a default identifier format, which may change 
909
   *                 over time.
910
   * 
911
   * @return true if the registration succeeds
912
   * 
913
   * @throws InvalidToken
914
   * @throws ServiceFailure
915
   * @throws NotAuthorized
916
   * @throws IdentifierNotUnique
917
   * @throws NotImplemented
918
   */
919
  @Override
920
  public Identifier reserveIdentifier(Session session, Identifier pid)
921
  throws InvalidToken, ServiceFailure,
922
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
923

    
924
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
925
  }
926
  
927
  @Override
928
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
929
  throws InvalidToken, ServiceFailure,
930
        NotAuthorized, NotImplemented, InvalidRequest {
931
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
932
  }
933
  
934
  /**
935
    * Checks whether the pid is reserved by the subject in the session param
936
    * If the reservation is held on the pid by the subject, we return true.
937
    * 
938
   * @param session - the Session object containing the Subject
939
   * @param pid - The identifier to check
940
   * 
941
   * @return true if the reservation exists for the subject/pid
942
   * 
943
   * @throws InvalidToken
944
   * @throws ServiceFailure
945
   * @throws NotFound - when the pid is not found (in use or in reservation)
946
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
947
   * @throws IdentifierNotUnique - when the pid is in use
948
   * @throws NotImplemented
949
   */
950

    
951
  @Override
952
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
953
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
954
      NotImplemented, InvalidRequest {
955
  
956
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
957
  }
958

    
959
  /**
960
   * Changes ownership (RightsHolder) of the specified object to the 
961
   * subject specified by userId
962
    * 
963
   * @param session - the Session object containing the credentials for the Subject
964
   * @param pid - Identifier of the object to be modified
965
   * @param userId - The subject that will be taking ownership of the specified object.
966
   *
967
   * @return pid - the identifier of the modified object
968
   * 
969
   * @throws ServiceFailure
970
   * @throws InvalidToken
971
   * @throws NotFound
972
   * @throws NotAuthorized
973
   * @throws NotImplemented
974
   * @throws InvalidRequest
975
   */  
976
  @Override
977
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
978
      long serialVersion)
979
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
980
      NotImplemented, InvalidRequest, VersionMismatch {
981
      
982
      // The lock to be used for this identifier
983
      Lock lock = null;
984

    
985
      // get the subject
986
      Subject subject = session.getSubject();
987
      
988
      // are we allowed to do this?
989
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
990
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
991
              throw new NotAuthorized("4440", "not allowed by "
992
                      + subject.getValue() + " on " + pid.getValue());
993
              
994
          }
995
      }
996
      
997
      SystemMetadata systemMetadata = null;
998
      try {
999
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1000
          logMetacat.debug("Locked identifier " + pid.getValue());
1001

    
1002
          try {
1003
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1004
              
1005
              // does the request have the most current system metadata?
1006
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1007
                 String msg = "The requested system metadata version number " + 
1008
                     serialVersion + " differs from the current version at " +
1009
                     systemMetadata.getSerialVersion().longValue() +
1010
                     ". Please get the latest copy in order to modify it.";
1011
                 throw new VersionMismatch("4443", msg);
1012
              }
1013
              
1014
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1015
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1016
              
1017
          }
1018
              
1019
          // set the new rights holder
1020
          systemMetadata.setRightsHolder(userId);
1021
          
1022
          // update the metadata
1023
          try {
1024
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1025
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1026
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1027
              
1028
          } catch (RuntimeException e) {
1029
              throw new ServiceFailure("4490", e.getMessage());
1030
          
1031
          }
1032
          
1033
      } catch (RuntimeException e) {
1034
          throw new ServiceFailure("4490", e.getMessage());
1035
          
1036
      } finally {
1037
          lock.unlock();
1038
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1039
      
1040
      }
1041
      
1042
      return pid;
1043
  }
1044

    
1045
  /**
1046
   * Verify that a replication task is authorized by comparing the target node's
1047
   * Subject (from the X.509 certificate-derived Session) with the list of 
1048
   * subjects in the known, pending replication tasks map.
1049
   * 
1050
   * @param originatingNodeSession - Session information that contains the 
1051
   *                                 identity of the calling user
1052
   * @param targetNodeSubject - Subject identifying the target node
1053
   * @param pid - the identifier of the object to be replicated
1054
   * @param replicatePermission - the execute permission to be granted
1055
   * 
1056
   * @throws ServiceFailure
1057
   * @throws NotImplemented
1058
   * @throws InvalidToken
1059
   * @throws NotAuthorized
1060
   * @throws InvalidRequest
1061
   * @throws NotFound
1062
   */
1063
  @Override
1064
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1065
    Subject targetNodeSubject, Identifier pid) 
1066
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1067
    NotFound, InvalidRequest {
1068
    
1069
    boolean isAllowed = false;
1070
    SystemMetadata sysmeta = null;
1071
    NodeReference targetNode = null;
1072
    
1073
    try {
1074
      // get the target node reference from the nodes list
1075
      CNode cn = D1Client.getCN();
1076
      List<Node> nodes = cn.listNodes().getNodeList();
1077
      
1078
      if ( nodes != null ) {
1079
        for (Node node : nodes) {
1080
            
1081
            for (Subject nodeSubject : node.getSubjectList()) {
1082
                
1083
                if ( nodeSubject.equals(targetNodeSubject) ) {
1084
                    targetNode = node.getIdentifier();
1085
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1086
                    break;
1087
                }
1088
            }
1089
            
1090
            if ( targetNode != null) { break; }
1091
        }
1092
        
1093
      } else {
1094
          String msg = "Couldn't get the node list from the CN";
1095
          logMetacat.debug(msg);
1096
          throw new ServiceFailure("4872", msg);
1097
          
1098
      }
1099
      
1100
      // can't find a node listed with the given subject
1101
      if ( targetNode == null ) {
1102
          String msg = "There is no Member Node registered with a node subject " +
1103
              "matching " + targetNodeSubject.getValue();
1104
          logMetacat.info(msg);
1105
          throw new NotAuthorized("4871", msg);
1106
          
1107
      }
1108
      
1109
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1110
      
1111
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1112

    
1113
      if ( sysmeta != null ) {
1114
          
1115
          List<Replica> replicaList = sysmeta.getReplicaList();
1116
          
1117
          if ( replicaList != null ) {
1118
              
1119
              // find the replica with the status set to 'requested'
1120
              for (Replica replica : replicaList) {
1121
                  ReplicationStatus status = replica.getReplicationStatus();
1122
                  NodeReference listedNode = replica.getReplicaMemberNode();
1123
                  if ( listedNode != null && targetNode != null ) {
1124
                      logMetacat.debug("Comparing " + listedNode.getValue()
1125
                              + " to " + targetNode.getValue());
1126
                      
1127
                      if (listedNode.getValue().equals(targetNode.getValue())
1128
                              && status.equals(ReplicationStatus.REQUESTED)) {
1129
                          isAllowed = true;
1130
                          break;
1131

    
1132
                      }
1133
                  }
1134
              }
1135
          }
1136
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1137
              "to replicate: " + isAllowed + " for " + pid.getValue());
1138

    
1139
          
1140
      } else {
1141
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1142
          " is null.");          
1143
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1144
          
1145
      }
1146

    
1147
    } catch (RuntimeException e) {
1148
    	  ServiceFailure sf = new ServiceFailure("4872", 
1149
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1150
                e.getCause().getMessage());
1151
    	  sf.initCause(e);
1152
        throw sf;
1153
        
1154
    }
1155
      
1156
    return isAllowed;
1157
    
1158
  }
1159

    
1160
  /**
1161
   * Adds a new object to the Node, where the object is a science metadata object.
1162
   * 
1163
   * @param session - the Session object containing the credentials for the Subject
1164
   * @param pid - The object identifier to be created
1165
   * @param object - the object bytes
1166
   * @param sysmeta - the system metadata that describes the object  
1167
   * 
1168
   * @return pid - the object identifier created
1169
   * 
1170
   * @throws InvalidToken
1171
   * @throws ServiceFailure
1172
   * @throws NotAuthorized
1173
   * @throws IdentifierNotUnique
1174
   * @throws UnsupportedType
1175
   * @throws InsufficientResources
1176
   * @throws InvalidSystemMetadata
1177
   * @throws NotImplemented
1178
   * @throws InvalidRequest
1179
   */
1180
  public Identifier create(Session session, Identifier pid, InputStream object,
1181
    SystemMetadata sysmeta) 
1182
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1183
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1184
    NotImplemented, InvalidRequest {
1185
                  
1186
      // The lock to be used for this identifier
1187
      Lock lock = null;
1188

    
1189
      try {
1190
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1191
          // are we allowed?
1192
          boolean isAllowed = false;
1193
          CNode cn = D1Client.getCN();
1194
          NodeList nodeList = cn.listNodes();
1195
          
1196
          for (Node node : nodeList.getNodeList()) {
1197
              if ( node.getType().equals(NodeType.CN) ) {
1198
                  
1199
                  List<Subject> subjects = node.getSubjectList();
1200
                  for (Subject subject : subjects) {
1201
                     if (subject.equals(session.getSubject())) {
1202
                         isAllowed = true;
1203
                         break;
1204
                     }
1205
                  }
1206
              } else {
1207
                  
1208
              }
1209
          }
1210

    
1211
          // proceed if we're called by a CN
1212
          if ( isAllowed ) {
1213
              // create the coordinating node version of the document      
1214
              lock.lock();
1215
              logMetacat.debug("Locked identifier " + pid.getValue());
1216
              sysmeta.setSerialVersion(BigInteger.ONE);
1217
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1218
              sysmeta.setArchived(false); // this is a create op, not update
1219
              
1220
              // the CN should have set the origin and authoritative member node fields
1221
              try {
1222
                  sysmeta.getOriginMemberNode().getValue();
1223
                  sysmeta.getAuthoritativeMemberNode().getValue();
1224
                  
1225
              } catch (NullPointerException npe) {
1226
                  throw new InvalidSystemMetadata("4896", 
1227
                      "Both the origin and authoritative member node identifiers need to be set.");
1228
                  
1229
              }
1230
              pid = super.create(session, pid, object, sysmeta);
1231

    
1232
          } else {
1233
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1234
                  " isn't allowed to call create() on a Coordinating Node.";
1235
              logMetacat.info(msg);
1236
              throw new NotAuthorized("1100", msg);
1237
          }
1238
          
1239
      } catch (RuntimeException e) {
1240
          // Convert Hazelcast runtime exceptions to service failures
1241
          String msg = "There was a problem creating the object identified by " +
1242
              pid.getValue() + ". There error message was: " + e.getMessage();
1243
          throw new ServiceFailure("4893", msg);
1244
          
1245
      } finally {
1246
    	  if (lock != null) {
1247
	          lock.unlock();
1248
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1249
    	  }
1250
      }
1251
      
1252
      return pid;
1253

    
1254
  }
1255

    
1256
  /**
1257
   * Set access for a given object using the object identifier and a Subject
1258
   * under a given Session.
1259
   * 
1260
   * @param session - the Session object containing the credentials for the Subject
1261
   * @param pid - the object identifier for the given object to apply the policy
1262
   * @param policy - the access policy to be applied
1263
   * 
1264
   * @return true if the application of the policy succeeds
1265
   * @throws InvalidToken
1266
   * @throws ServiceFailure
1267
   * @throws NotFound
1268
   * @throws NotAuthorized
1269
   * @throws NotImplemented
1270
   * @throws InvalidRequest
1271
   */
1272
  public boolean setAccessPolicy(Session session, Identifier pid, 
1273
      AccessPolicy accessPolicy, long serialVersion) 
1274
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1275
      NotImplemented, InvalidRequest, VersionMismatch {
1276
      
1277
      // The lock to be used for this identifier
1278
      Lock lock = null;
1279
      SystemMetadata systemMetadata = null;
1280
      
1281
      boolean success = false;
1282
      
1283
      // get the subject
1284
      Subject subject = session.getSubject();
1285
      
1286
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1287
          // are we allowed to do this?
1288
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1289
              throw new NotAuthorized("4420", "not allowed by "
1290
                      + subject.getValue() + " on " + pid.getValue());
1291
          }
1292
      }
1293
      
1294
      try {
1295
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1296
          lock.lock();
1297
          logMetacat.debug("Locked identifier " + pid.getValue());
1298

    
1299
          try {
1300
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1301

    
1302
              if ( systemMetadata == null ) {
1303
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1304
                  
1305
              }
1306
              // does the request have the most current system metadata?
1307
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1308
                 String msg = "The requested system metadata version number " + 
1309
                     serialVersion + " differs from the current version at " +
1310
                     systemMetadata.getSerialVersion().longValue() +
1311
                     ". Please get the latest copy in order to modify it.";
1312
                 throw new VersionMismatch("4402", msg);
1313
                 
1314
              }
1315
              
1316
          } catch (RuntimeException e) {
1317
              // convert Hazelcast RuntimeException to NotFound
1318
              throw new NotFound("4400", "No record found for: " + pid);
1319
            
1320
          }
1321
              
1322
          // set the access policy
1323
          systemMetadata.setAccessPolicy(accessPolicy);
1324
          
1325
          // update the system metadata
1326
          try {
1327
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1328
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1329
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1330
            
1331
          } catch (RuntimeException e) {
1332
              // convert Hazelcast RuntimeException to ServiceFailure
1333
              throw new ServiceFailure("4430", e.getMessage());
1334
            
1335
          }
1336
          
1337
      } catch (RuntimeException e) {
1338
          throw new ServiceFailure("4430", e.getMessage());
1339
          
1340
      } finally {
1341
          lock.unlock();
1342
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1343
        
1344
      }
1345

    
1346
    
1347
    // TODO: how do we know if the map was persisted?
1348
    success = true;
1349
    
1350
    return success;
1351
  }
1352

    
1353
  /**
1354
   * Full replacement of replication metadata in the system metadata for the 
1355
   * specified object, changes date system metadata modified
1356
   * 
1357
   * @param session - the Session object containing the credentials for the Subject
1358
   * @param pid - the object identifier for the given object to apply the policy
1359
   * @param replica - the replica to be updated
1360
   * @return
1361
   * @throws NotImplemented
1362
   * @throws NotAuthorized
1363
   * @throws ServiceFailure
1364
   * @throws InvalidRequest
1365
   * @throws NotFound
1366
   * @throws VersionMismatch
1367
   */
1368
  @Override
1369
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1370
      Replica replica, long serialVersion) 
1371
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1372
      NotFound, VersionMismatch {
1373
      
1374
      // The lock to be used for this identifier
1375
      Lock lock = null;
1376
      
1377
      // get the subject
1378
      Subject subject = session.getSubject();
1379
      
1380
      // are we allowed to do this?
1381
      try {
1382
        if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
1383
            // what is the controlling permission?
1384
            if (!isAuthorized(session, pid, Permission.WRITE)) {
1385
                throw new NotAuthorized("4851", "not allowed by "
1386
                        + subject.getValue() + " on " + pid.getValue());
1387
            }
1388
        }
1389
        
1390
      } catch (InvalidToken e) {
1391
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1392
                  " on " + pid.getValue());  
1393
          
1394
      }
1395

    
1396
      SystemMetadata systemMetadata = null;
1397
      try {
1398
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1399
          lock.lock();
1400
          logMetacat.debug("Locked identifier " + pid.getValue());
1401

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

    
1405
              // does the request have the most current system metadata?
1406
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1407
                 String msg = "The requested system metadata version number " + 
1408
                     serialVersion + " differs from the current version at " +
1409
                     systemMetadata.getSerialVersion().longValue() +
1410
                     ". Please get the latest copy in order to modify it.";
1411
                 throw new VersionMismatch("4855", msg);
1412
              }
1413
              
1414
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1415
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1416
                  " : " + e.getMessage());
1417
            
1418
          }
1419
              
1420
          // set the status for the replica
1421
          List<Replica> replicas = systemMetadata.getReplicaList();
1422
          NodeReference replicaNode = replica.getReplicaMemberNode();
1423
          int index = 0;
1424
          for (Replica listedReplica: replicas) {
1425
              
1426
              // remove the replica that we are replacing
1427
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1428
                  replicas.remove(index);
1429
                  break;
1430
                  
1431
              }
1432
              index++;
1433
          }
1434
          
1435
          // add the new replica item
1436
          replicas.add(replica);
1437
          systemMetadata.setReplicaList(replicas);
1438
          
1439
          // update the metadata
1440
          try {
1441
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1442
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1443
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1444
            
1445
          } catch (RuntimeException e) {
1446
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1447
              throw new ServiceFailure("4852", e.getMessage());
1448
          
1449
          }
1450
          
1451
      } catch (RuntimeException e) {
1452
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1453
          throw new ServiceFailure("4852", e.getMessage());
1454
      
1455
      } finally {
1456
          lock.unlock();
1457
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1458
          
1459
      }
1460
    
1461
      return true;
1462
      
1463
  }
1464
  
1465
  /**
1466
   * 
1467
   */
1468
  @Override
1469
  public ObjectList listObjects(Session session, Date startTime, 
1470
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1471
      Integer start, Integer count)
1472
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1473
      ServiceFailure {
1474
      
1475
      ObjectList objectList = null;
1476
        try {
1477
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1478
        } catch (Exception e) {
1479
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1480
        }
1481

    
1482
        return objectList;
1483
  }
1484

    
1485
  
1486
 	/**
1487
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1488
 	 * @return cal  the list of checksum algorithms
1489
 	 * 
1490
 	 * @throws ServiceFailure
1491
 	 * @throws NotImplemented
1492
 	 */
1493
  @Override
1494
  public ChecksumAlgorithmList listChecksumAlgorithms()
1495
			throws ServiceFailure, NotImplemented {
1496
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1497
		cal.addAlgorithm("MD5");
1498
		cal.addAlgorithm("SHA-1");
1499
		return cal;
1500
		
1501
	}
1502
    
1503
}
(1-1/5)