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.Calendar;
29
import java.util.Date;
30
import java.util.List;
31
import java.util.Set;
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

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

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

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

    
93
  /**
94
   * singleton accessor
95
   */
96
  public static CNodeService getInstance(HttpServletRequest request) { 
97
    return new CNodeService(request);
98
  }
99
  
100
  /**
101
   * Constructor, private for singleton access
102
   */
103
  private CNodeService(HttpServletRequest request) {
104
    super(request);
105
    logMetacat = Logger.getLogger(CNodeService.class);
106
        
107
  }
108
    
109
  /**
110
   * Set the replication policy for an object given the object identifier
111
   * 
112
   * @param session - the Session object containing the credentials for the Subject
113
   * @param pid - the object identifier for the given object
114
   * @param policy - the replication policy to be applied
115
   * 
116
   * @return true or false
117
   * 
118
   * @throws NotImplemented
119
   * @throws NotAuthorized
120
   * @throws ServiceFailure
121
   * @throws InvalidRequest
122
   * @throws VersionMismatch
123
   * 
124
   */
125
  @Override
126
  public boolean setReplicationPolicy(Session session, Identifier pid,
127
      ReplicationPolicy policy, long serialVersion) 
128
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
129
      InvalidRequest, InvalidToken, VersionMismatch {
130
      
131
      // The lock to be used for this identifier
132
      Lock lock = null;
133
      
134
      // get the subject
135
      Subject subject = session.getSubject();
136
      
137
      // are we allowed to do this?
138
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
139
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
140
              throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION
141
                      + " not allowed by " + subject.getValue() + " on "
142
                      + pid.getValue());
143
              
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
            
189
          } catch (RuntimeException e) {
190
              throw new ServiceFailure("4882", e.getMessage());
191
          
192
          }
193
          
194
      } catch (RuntimeException e) {
195
          throw new ServiceFailure("4882", e.getMessage());
196
          
197
      } finally {
198
          lock.unlock();
199
          logMetacat.debug("Unlocked identifier " + pid.getValue());
200
          
201
      }
202
    
203
      return true;
204
  }
205

    
206
  /**
207
   * Set the replication status for an object given the object identifier
208
   * 
209
   * @param session - the Session object containing the credentials for the Subject
210
   * @param pid - the object identifier for the given object
211
   * @param status - the replication status to be applied
212
   * 
213
   * @return true or false
214
   * 
215
   * @throws NotImplemented
216
   * @throws NotAuthorized
217
   * @throws ServiceFailure
218
   * @throws InvalidRequest
219
   * @throws InvalidToken
220
   * @throws NotFound
221
   * 
222
   */
223
  @Override
224
  public boolean setReplicationStatus(Session session, Identifier pid,
225
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
226
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
227
      InvalidRequest, NotFound {
228
      
229
      // The lock to be used for this identifier
230
      Lock lock = null;
231
      
232
      boolean allowed = false;
233
      int replicaEntryIndex = -1;
234
      List<Replica> replicas = null;
235
      // get the subject
236
      Subject subject = session.getSubject();
237
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
238
          " is " + status.toString());
239
      
240
      SystemMetadata systemMetadata = null;
241

    
242
      try {
243
          lock = HazelcastService.getInstance().getLock(pid.getValue());
244
          lock.lock();
245
          logMetacat.debug("Locked identifier " + pid.getValue());
246

    
247
          try {      
248
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
249

    
250
              // did we get it correctly?
251
              if ( systemMetadata == null ) {
252
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
253
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
254
                  
255
              }
256
              replicas = systemMetadata.getReplicaList();
257
              int count = 0;
258
              
259
              if ( replicas == null || replicas.size() < 1 ) {
260
                  logMetacat.debug("No replicas to evaluate for " + pid.getValue());
261
                  Replica newReplica = new Replica();
262
                  newReplica.setReplicaMemberNode(targetNode);
263
                  newReplica.setReplicationStatus(status);
264
                  newReplica.setReplicaVerified(Calendar.getInstance().getTime());
265
                  try {
266
                    // if there is no replica entry, create one
267
                    updateReplicationMetadata(session, pid, newReplica, 
268
                        systemMetadata.getSerialVersion().longValue());
269
                    
270
                  } catch (VersionMismatch e) {
271
                      // try again if we somehow don't have the correct version
272
                      String msg = "The serial version of the system metadata doesn't match. Trying again.";
273
                      logMetacat.info(msg);
274
                      systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
275
                      try {
276
                        updateReplicationMetadata(session, pid, newReplica, 
277
                            systemMetadata.getSerialVersion().longValue());
278
                        
279
                      } catch (VersionMismatch e1) {
280
                          throw new ServiceFailure("4700", 
281
                              "Couldn't get the correct serial version of the system metadata: " +
282
                              e1.getCause().getMessage());
283
                          
284
                      }
285

    
286
                  }
287
                  
288
              }
289
              
290
              // refresh the system metadata and replica list
291
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
292
              replicas = systemMetadata.getReplicaList();
293
                  
294
              // find the target replica index in the replica list
295
              for (Replica replica: replicas) {
296
                  String replicaNodeStr = replica.getReplicaMemberNode().getValue();
297
                  String targetNodeStr = targetNode.getValue();
298
                  logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
299
                  
300
                  if (replicaNodeStr.equals(targetNodeStr)) {
301
                      replicaEntryIndex = count;
302
                      logMetacat.debug("replica entry index is: " + replicaEntryIndex);
303
                      break;
304
                  }
305
                  count++;
306
                  
307
              }
308

    
309
              // are we allowed to do this? only CNs and target MNs are allowed
310
              CNode cn = D1Client.getCN();
311
              List<Node> nodes = cn.listNodes().getNodeList();
312
              
313
              // find the node in the node list
314
              for ( Node node : nodes ) {
315
                  
316
                  NodeReference nodeReference = node.getIdentifier();
317
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
318
                  
319
                  // allow target MN certs and CN certs
320
                  if (targetNode.getValue().equals(nodeReference.getValue()) ||
321
                      node.getType() == NodeType.CN) {
322
                      List<Subject> nodeSubjects = node.getSubjectList();
323
                      
324
                      // check if the session subject is in the node subject list
325
                      for (Subject nodeSubject : nodeSubjects) {
326
                          if ( nodeSubject.equals(subject) ) {
327
                              allowed = true; // subject of session == target node subject
328
                              break;
329
                              
330
                          }
331
                      }                 
332
                  }
333
              }
334

    
335
              if ( !allowed && !isAdminAuthorized(session, pid, Permission.WRITE)) {
336
                  String msg = "The subject identified by " + subject.getValue() +
337
                    " does not have permission to set the replication status for " +
338
                    "the replica identified by " + targetNode.getValue() + ".";
339
                  logMetacat.info(msg);
340
                  throw new NotAuthorized("4720", msg);
341
                  
342
              }
343
              
344
              // was there a failure? log it
345
              if ( failure != null && status == ReplicationStatus.FAILED ) {
346
                 String msg = "The replication request of the object identified by " + 
347
                     pid.getValue() + " failed.  The error message was " +
348
                     failure.getMessage() + ".";
349
              }
350
              
351
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
352
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
353
                " : " + e.getMessage());
354
            
355
          }
356
          
357
          // set the status for the replica
358
          if ( replicaEntryIndex != -1 ) {
359
              Replica targetReplica = replicas.get(replicaEntryIndex);
360
              targetReplica.setReplicationStatus(status);
361
              logMetacat.debug("Set the replication status for " + 
362
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
363
                  targetReplica.getReplicationStatus());
364
              
365
          } else {
366
              throw new InvalidRequest("4730", "There are no replicas to update.");
367

    
368
          }
369
          
370
          systemMetadata.setReplicaList(replicas);
371
                
372
          // update the metadata
373
          try {
374
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
375
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
376
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
377
              
378
              if ( status == ReplicationStatus.FAILED && failure != null ) {
379
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
380
                      " on target node " + targetNode + ". The exception was: " +
381
                      failure.getMessage());
382
              }
383
          } catch (RuntimeException e) {
384
              throw new ServiceFailure("4700", e.getMessage());
385
          
386
          }
387
          
388
    } catch (RuntimeException e) {
389
        String msg = "There was a RuntimeException getting the lock for " +
390
            pid.getValue();
391
        logMetacat.info(msg);
392
        
393
    } finally {
394
        lock.unlock();
395
        logMetacat.debug("Unlocked identifier " + pid.getValue());
396
        
397
    }
398
      
399
      return true;
400
  }
401

    
402
  /**
403
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
404
   * 
405
   * @param session - the Session object containing the credentials for the Subject
406
   * @param node - the node information for the given node be modified
407
   * 
408
   * @return true if the relationship exists
409
   * 
410
   * @throws InvalidToken
411
   * @throws ServiceFailure
412
   * @throws NotAuthorized
413
   * @throws NotFound
414
   * @throws InvalidRequest
415
   * @throws NotImplemented
416
   */
417
  @Override
418
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
419
    String relationship, Identifier pidOfObject) 
420
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
421
    InvalidRequest, NotImplemented {
422
    
423
    // The lock to be used for thyis identifier
424
    Lock lock = null;
425

    
426
    boolean asserted = false;
427
        
428
    // are we allowed to do this?
429
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
430
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());  
431
    }
432
    
433
    SystemMetadata systemMetadata = null;
434
    try {
435
        lock = HazelcastService.getInstance().getLock(pidOfSubject.getValue());
436
        lock.lock();
437
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
438
        
439
        
440
        // check relationships
441
        // TODO: use ORE map
442
        if (relationship.equalsIgnoreCase("describes")) {
443
          
444
        }
445
        
446
        if (relationship.equalsIgnoreCase("describedBy")) {
447
          
448
        }
449
        
450
        if (relationship.equalsIgnoreCase("derivedFrom")) {
451
          
452
        }
453
        
454
        if (relationship.equalsIgnoreCase("obsoletes")) {
455
            Identifier pid = systemMetadata.getObsoletes();
456
            if (pid.getValue().equals(pidOfObject.getValue())) {
457
              asserted = true;
458
            
459
        }
460
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
461
        }
462
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
463
            Identifier pid = systemMetadata.getObsoletedBy();
464
            if (pid.getValue().equals(pidOfObject.getValue())) {
465
              asserted = true;
466
            
467
        }
468
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
469
        }
470
              
471
    } catch (Exception e) {
472
        throw new ServiceFailure("4270", "Could not assert relation for: " + 
473
            pidOfSubject.getValue() +
474
            ". The error message was: " + e.getMessage());
475
      
476
    } finally {
477
        lock.unlock();
478
        logMetacat.debug("Unlocked identifier " + pidOfSubject.getValue());
479

    
480
    }
481
        
482
    return asserted;
483
  }
484
  
485
  /**
486
   * Return the checksum of the object given the identifier 
487
   * 
488
   * @param session - the Session object containing the credentials for the Subject
489
   * @param pid - the object identifier for the given object
490
   * 
491
   * @return checksum - the checksum of the object
492
   * 
493
   * @throws InvalidToken
494
   * @throws ServiceFailure
495
   * @throws NotAuthorized
496
   * @throws NotFound
497
   * @throws NotImplemented
498
   */
499
  @Override
500
  public Checksum getChecksum(Session session, Identifier pid)
501
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
502
    NotImplemented {
503
            
504
    if (!isAuthorized(session, pid, Permission.READ)) {
505
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
506
    }
507
    
508
    SystemMetadata systemMetadata = null;
509
    Checksum checksum = null;
510
    
511
    try {
512
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
513

    
514
        if (systemMetadata == null ) {
515
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
516
        }
517
        checksum = systemMetadata.getChecksum();
518
        
519
    } catch (RuntimeException e) {
520
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
521
            pid.getValue() + ". The error message was: " + e.getMessage());
522
      
523
    }
524
    
525
    return checksum;
526
  }
527

    
528
  /**
529
   * Resolve the location of a given object
530
   * 
531
   * @param session - the Session object containing the credentials for the Subject
532
   * @param pid - the object identifier for the given object
533
   * 
534
   * @return objectLocationList - the list of nodes known to contain the object
535
   * 
536
   * @throws InvalidToken
537
   * @throws ServiceFailure
538
   * @throws NotAuthorized
539
   * @throws NotFound
540
   * @throws NotImplemented
541
   */
542
  @Override
543
  public ObjectLocationList resolve(Session session, Identifier pid)
544
    throws InvalidToken, ServiceFailure, NotAuthorized,
545
    NotFound, NotImplemented {
546

    
547
    throw new NotImplemented("4131", "resolve not implemented");
548

    
549
  }
550

    
551
  /**
552
   * Search the metadata catalog for identifiers that match the criteria
553
   * 
554
   * @param session - the Session object containing the credentials for the Subject
555
   * @param queryType - An identifier for the type of query expression 
556
   *                    provided in the query
557
   * @param query -  The criteria for matching the characteristics of the 
558
   *                 metadata objects of interest
559
   * 
560
   * @return objectList - the list of objects matching the criteria
561
   * 
562
   * @throws InvalidToken
563
   * @throws ServiceFailure
564
   * @throws NotAuthorized
565
   * @throws InvalidRequest
566
   * @throws NotImplemented
567
   */
568
  @Override
569
  public ObjectList search(Session session, String queryType, String query)
570
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
571
    NotImplemented {
572

    
573
    ObjectList objectList = null;
574
    try {
575
        objectList = 
576
          IdentifierManager.getInstance().querySystemMetadata(
577
              null, //startTime, 
578
              null, //endTime,
579
              null, //objectFormat, 
580
              false, //replicaStatus, 
581
              0, //start, 
582
              -1 //count
583
              );
584
        
585
    } catch (Exception e) {
586
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
587
    }
588

    
589
      return objectList;
590
      
591
    //throw new NotImplemented("4281", "search not implemented");
592
    
593
    // the code block below is from an older implementation
594
    
595
    /*  This block commented out because of the EcoGrid circular dependency.
596
         *  For now, query will not be supported until the circularity can be
597
         *  resolved, probably by moving the ecogrid query syntax transformers
598
         *  directly into the Metacat codebase.  MBJ 2010-02-03
599
         
600
        try {
601
            EcogridQueryParser parser = new EcogridQueryParser(request
602
                    .getReader());
603
            parser.parseXML();
604
            QueryType queryType = parser.getEcogridQuery();
605
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
606
                new EcogridJavaToMetacatJavaQueryTransformer();
607
            QuerySpecification metacatQuery = queryTransformer
608
                    .transform(queryType);
609

    
610
            DBQuery metacat = new DBQuery();
611

    
612
            boolean useXMLIndex = (new Boolean(PropertyService
613
                    .getProperty("database.usexmlindex"))).booleanValue();
614
            String xmlquery = "query"; // we don't care the query in resultset,
615
            // the query can be anything
616
            PrintWriter out = null; // we don't want metacat result, so set out null
617

    
618
            // parameter: queryspecification, user, group, usingIndexOrNot
619
            StringBuffer result = metacat.createResultDocument(xmlquery,
620
                    metacatQuery, out, username, groupNames, useXMLIndex);
621

    
622
            // create result set transfer       
623
            String saxparser = PropertyService.getProperty("xml.saxparser");
624
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
625
                    new StringReader(result.toString()), saxparser, queryType
626
                            .getNamespace().get_value());
627
            ResultsetType records = metacatResultsetParser.getEcogridResult();
628

    
629
            System.out
630
                    .println(EcogridResultsetTransformer.toXMLString(records));
631
            response.setContentType("text/xml");
632
            out = response.getWriter();
633
            out.print(EcogridResultsetTransformer.toXMLString(records));
634

    
635
        } catch (Exception e) {
636
            e.printStackTrace();
637
        }*/
638
    
639

    
640
  }
641
  
642
  /**
643
   * Returns the object format registered in the DataONE Object Format 
644
   * Vocabulary for the given format identifier
645
   * 
646
   * @param fmtid - the identifier of the format requested
647
   * 
648
   * @return objectFormat - the object format requested
649
   * 
650
   * @throws ServiceFailure
651
   * @throws NotFound
652
   * @throws InsufficientResources
653
   * @throws NotImplemented
654
   */
655
  @Override
656
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
657
    throws ServiceFailure, NotFound, NotImplemented {
658
     
659
      return ObjectFormatService.getInstance().getFormat(fmtid);
660
      
661
  }
662

    
663
  /**
664
   * Returns a list of all object formats registered in the DataONE Object 
665
   * Format Vocabulary
666
    * 
667
   * @return objectFormatList - The list of object formats registered in 
668
   *                            the DataONE Object Format Vocabulary
669
   * 
670
   * @throws ServiceFailure
671
   * @throws NotImplemented
672
   * @throws InsufficientResources
673
   */
674
  @Override
675
  public ObjectFormatList listFormats() 
676
    throws ServiceFailure, NotImplemented {
677

    
678
    return ObjectFormatService.getInstance().listFormats();
679
  }
680

    
681
  /**
682
   * Returns a list of nodes that have been registered with the DataONE infrastructure
683
    * 
684
   * @return nodeList - List of nodes from the registry
685
   * 
686
   * @throws ServiceFailure
687
   * @throws NotImplemented
688
   */
689
  @Override
690
  public NodeList listNodes() 
691
    throws NotImplemented, ServiceFailure {
692

    
693
    throw new NotImplemented("4800", "listNodes not implemented");
694
  }
695

    
696
  /**
697
   * Provides a mechanism for adding system metadata independently of its 
698
   * associated object, such as when adding system metadata for data objects.
699
    * 
700
   * @param session - the Session object containing the credentials for the Subject
701
   * @param pid - The identifier of the object to register the system metadata against
702
   * @param sysmeta - The system metadata to be registered
703
   * 
704
   * @return true if the registration succeeds
705
   * 
706
   * @throws NotImplemented
707
   * @throws NotAuthorized
708
   * @throws ServiceFailure
709
   * @throws InvalidRequest
710
   * @throws InvalidSystemMetadata
711
   */
712
  @Override
713
  public Identifier registerSystemMetadata(Session session, Identifier pid,
714
      SystemMetadata sysmeta) 
715
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
716
      InvalidSystemMetadata {
717

    
718
      // The lock to be used for this identifier
719
      Lock lock = null;
720

    
721
      // TODO: control who can call this?
722
      if (session == null) {
723
          //TODO: many of the thrown exceptions do not use the correct error codes
724
          //check these against the docs and correct them
725
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
726
                  "  If you are not logged in, please do so and retry the request.");
727
      }
728
      
729
      // verify that guid == SystemMetadata.getIdentifier()
730
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
731
          "|" + sysmeta.getIdentifier().getValue());
732
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
733
          throw new InvalidRequest("4863", 
734
              "The identifier in method call (" + pid.getValue() + 
735
              ") does not match identifier in system metadata (" +
736
              sysmeta.getIdentifier().getValue() + ").");
737
      }
738

    
739
      try {
740
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
741
          lock.lock();
742
          logMetacat.debug("Locked identifier " + pid.getValue());
743
          logMetacat.debug("Checking if identifier exists...");
744
          // Check that the identifier does not already exist
745
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
746
              throw new InvalidRequest("4863", 
747
                  "The identifier is already in use by an existing object.");
748
          
749
          }
750
          
751
          // insert the system metadata into the object store
752
          logMetacat.debug("Starting to insert SystemMetadata...");
753
          try {
754
              sysmeta.setSerialVersion(BigInteger.ONE);
755
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
756
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
757
              
758
          } catch (RuntimeException e) {
759
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
760
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
761
                  e.getClass() + ": " + e.getMessage());
762
              
763
          }
764
          
765
      } catch (RuntimeException e) {
766
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
767
                  e.getClass() + ": " + e.getMessage());
768
          
769
      }  finally {
770
          lock.unlock();
771
          logMetacat.debug("Unlocked identifier " + pid.getValue());
772
          
773
      }
774

    
775
      
776
      logMetacat.debug("Returning from registerSystemMetadata");
777
      EventLog.getInstance().log(request.getRemoteAddr(), 
778
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
779
          pid.getValue(), "registerSystemMetadata");
780
      return pid;
781
  }
782
  
783
  /**
784
   * Given an optional scope and format, reserves and returns an identifier 
785
   * within that scope and format that is unique and will not be 
786
   * used by any other sessions. 
787
    * 
788
   * @param session - the Session object containing the credentials for the Subject
789
   * @param pid - The identifier of the object to register the system metadata against
790
   * @param scope - An optional string to be used to qualify the scope of 
791
   *                the identifier namespace, which is applied differently 
792
   *                depending on the format requested. If scope is not 
793
   *                supplied, a default scope will be used.
794
   * @param format - The optional name of the identifier format to be used, 
795
   *                  drawn from a DataONE-specific vocabulary of identifier 
796
   *                 format names, including several common syntaxes such 
797
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
798
   *                 format is not supplied by the caller, the CN service 
799
   *                 will use a default identifier format, which may change 
800
   *                 over time.
801
   * 
802
   * @return true if the registration succeeds
803
   * 
804
   * @throws InvalidToken
805
   * @throws ServiceFailure
806
   * @throws NotAuthorized
807
   * @throws IdentifierNotUnique
808
   * @throws NotImplemented
809
   */
810
  @Override
811
  public Identifier reserveIdentifier(Session session, Identifier pid)
812
  throws InvalidToken, ServiceFailure,
813
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
814

    
815
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
816
  }
817
  
818
  @Override
819
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
820
  throws InvalidToken, ServiceFailure,
821
        NotAuthorized, NotImplemented, InvalidRequest {
822
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
823
  }
824
  
825
  /**
826
    * Checks whether the pid is reserved by the subject in the session param
827
    * If the reservation is held on the pid by the subject, we return true.
828
    * 
829
   * @param session - the Session object containing the Subject
830
   * @param pid - The identifier to check
831
   * 
832
   * @return true if the reservation exists for the subject/pid
833
   * 
834
   * @throws InvalidToken
835
   * @throws ServiceFailure
836
   * @throws NotFound - when the pid is not found (in use or in reservation)
837
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
838
   * @throws IdentifierNotUnique - when the pid is in use
839
   * @throws NotImplemented
840
   */
841

    
842
  @Override
843
  public boolean hasReservation(Session session, Identifier pid) 
844
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
845
      NotImplemented, InvalidRequest {
846
  
847
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
848
  }
849

    
850
  /**
851
   * Changes ownership (RightsHolder) of the specified object to the 
852
   * subject specified by userId
853
    * 
854
   * @param session - the Session object containing the credentials for the Subject
855
   * @param pid - Identifier of the object to be modified
856
   * @param userId - The subject that will be taking ownership of the specified object.
857
   *
858
   * @return pid - the identifier of the modified object
859
   * 
860
   * @throws ServiceFailure
861
   * @throws InvalidToken
862
   * @throws NotFound
863
   * @throws NotAuthorized
864
   * @throws NotImplemented
865
   * @throws InvalidRequest
866
   */  
867
  @Override
868
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
869
      long serialVersion)
870
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
871
      NotImplemented, InvalidRequest, VersionMismatch {
872
      
873
      // The lock to be used for this identifier
874
      Lock lock = null;
875

    
876
      // get the subject
877
      Subject subject = session.getSubject();
878
      
879
      // are we allowed to do this?
880
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
881
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
882
              throw new NotAuthorized("4440", "not allowed by "
883
                      + subject.getValue() + " on " + pid.getValue());
884
              
885
          }
886
      }
887
      
888
      SystemMetadata systemMetadata = null;
889
      try {
890
          lock = HazelcastService.getInstance().getLock(pid.getValue());
891
          logMetacat.debug("Locked identifier " + pid.getValue());
892

    
893
          try {
894
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
895
              
896
              // does the request have the most current system metadata?
897
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
898
                 String msg = "The requested system metadata version number " + 
899
                     serialVersion + " differs from the current version at " +
900
                     systemMetadata.getSerialVersion().longValue() +
901
                     ". Please get the latest copy in order to modify it.";
902
                 throw new VersionMismatch("4443", msg);
903
              }
904
              
905
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
906
              throw new NotFound("4460", "No record found for: " + pid.getValue());
907
              
908
          }
909
              
910
          // set the new rights holder
911
          systemMetadata.setRightsHolder(userId);
912
          
913
          // update the metadata
914
          try {
915
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
916
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
917
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
918
              
919
          } catch (RuntimeException e) {
920
              throw new ServiceFailure("4490", e.getMessage());
921
          
922
          }
923
          
924
      } catch (RuntimeException e) {
925
          throw new ServiceFailure("4490", e.getMessage());
926
          
927
      } finally {
928
          lock.unlock();
929
          logMetacat.debug("Unlocked identifier " + pid.getValue());
930
      
931
      }
932
      
933
      return pid;
934
  }
935

    
936
  /**
937
   * Verify that a replication task is authorized by comparing the target node's
938
   * Subject (from the X.509 certificate-derived Session) with the list of 
939
   * subjects in the known, pending replication tasks map.
940
   * 
941
   * @param originatingNodeSession - Session information that contains the 
942
   *                                 identity of the calling user
943
   * @param targetNodeSubject - Subject identifying the target node
944
   * @param pid - the identifier of the object to be replicated
945
   * @param replicatePermission - the execute permission to be granted
946
   * 
947
   * @throws ServiceFailure
948
   * @throws NotImplemented
949
   * @throws InvalidToken
950
   * @throws NotAuthorized
951
   * @throws InvalidRequest
952
   * @throws NotFound
953
   */
954
  @Override
955
  public boolean isNodeAuthorized(Session originatingNodeSession, 
956
    Subject targetNodeSubject, Identifier pid) 
957
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
958
    NotFound, InvalidRequest {
959
    
960
    boolean isAllowed = false;
961
    SystemMetadata sysmeta = null;
962
    NodeReference targetNode = null;
963
    
964
    try {
965
      // get the target node reference from the nodes list
966
      CNode cn = D1Client.getCN();
967
      List<Node> nodes = cn.listNodes().getNodeList();
968
      
969
      if ( nodes != null ) {
970
        for (Node node : nodes) {
971
            
972
            for (Subject nodeSubject : node.getSubjectList()) {
973
                
974
                if ( nodeSubject.equals(targetNodeSubject) ) {
975
                    targetNode = node.getIdentifier();
976
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
977
                    break;
978
                }
979
            }
980
            
981
            if ( targetNode != null) { break; }
982
        }
983
        
984
      } else {
985
          String msg = "Couldn't get the node list from the CN";
986
          logMetacat.debug(msg);
987
          throw new ServiceFailure("4872", msg);
988
          
989
      }
990
      
991
      // can't find a node listed with the given subject
992
      if ( targetNode == null ) {
993
          String msg = "There is no Member Node registered with a node subject " +
994
              "matching " + targetNodeSubject.getValue();
995
          logMetacat.info(msg);
996
          throw new ServiceFailure("4872", msg);
997
          
998
      }
999
      
1000
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1001
      
1002
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1003

    
1004
      if ( sysmeta != null ) {
1005
          
1006
          List<Replica> replicaList = sysmeta.getReplicaList();
1007
          
1008
          if ( replicaList != null ) {
1009
              
1010
              // find the replica with the status set to 'requested'
1011
              for (Replica replica : replicaList) {
1012
                  ReplicationStatus status = replica.getReplicationStatus();
1013
                  NodeReference listedNode = replica.getReplicaMemberNode();
1014
                  if ( listedNode != null && targetNode != null ) {
1015
                      logMetacat.debug("Comparing " + listedNode.getValue()
1016
                              + " to " + targetNode.getValue());
1017
                      
1018
                      if (listedNode.getValue().equals(targetNode.getValue())
1019
                              && status.equals(ReplicationStatus.REQUESTED)) {
1020
                          isAllowed = true;
1021
                          break;
1022

    
1023
                      }
1024
                  }
1025
              }
1026
          }
1027
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1028
              "to replicate: " + isAllowed + " for " + pid.getValue());
1029

    
1030
          
1031
      } else {
1032
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1033
          " is null.");          
1034
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1035
          
1036
      }
1037

    
1038
    } catch (RuntimeException e) {
1039
    	  ServiceFailure sf = new ServiceFailure("4872", 
1040
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1041
                e.getCause().getMessage());
1042
    	  sf.initCause(e);
1043
        throw sf;
1044
        
1045
    }
1046
      
1047
    return isAllowed;
1048
    
1049
  }
1050

    
1051
  /**
1052
   * Adds a new object to the Node, where the object is a science metadata object.
1053
   * 
1054
   * @param session - the Session object containing the credentials for the Subject
1055
   * @param pid - The object identifier to be created
1056
   * @param object - the object bytes
1057
   * @param sysmeta - the system metadata that describes the object  
1058
   * 
1059
   * @return pid - the object identifier created
1060
   * 
1061
   * @throws InvalidToken
1062
   * @throws ServiceFailure
1063
   * @throws NotAuthorized
1064
   * @throws IdentifierNotUnique
1065
   * @throws UnsupportedType
1066
   * @throws InsufficientResources
1067
   * @throws InvalidSystemMetadata
1068
   * @throws NotImplemented
1069
   * @throws InvalidRequest
1070
   */
1071
  public Identifier create(Session session, Identifier pid, InputStream object,
1072
    SystemMetadata sysmeta) 
1073
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1074
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1075
    NotImplemented, InvalidRequest {
1076
      
1077
      
1078
      // The lock to be used for this identifier
1079
      Lock lock = null;
1080
      
1081
      try {
1082
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1083
          // are we allowed?
1084
          boolean isAllowed = false;
1085
          CNode cn = D1Client.getCN();
1086
          NodeList nodeList = cn.listNodes();
1087
          
1088
          for (Node node : nodeList.getNodeList()) {
1089
              if ( node.getType().equals(NodeType.CN) ) {
1090
                  
1091
                  List<Subject> subjects = node.getSubjectList();
1092
                  for (Subject subject : subjects) {
1093
                     if (subject.equals(session.getSubject())) {
1094
                         isAllowed = true;
1095
                         break;
1096
                     }
1097
                  }
1098
              }
1099
          }
1100

    
1101
          // proceed if we're called by a CN
1102
          if ( isAllowed ) {
1103
              // create the coordinating node version of the document      
1104
              lock.lock();
1105
              logMetacat.debug("Locked identifier " + pid.getValue());
1106
              sysmeta.setSerialVersion(BigInteger.ONE);
1107
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1108
              pid = super.create(session, pid, object, sysmeta);
1109

    
1110
          } else {
1111
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1112
                  " isn't allowed to call create() on a Coordinating Node.";
1113
              logMetacat.info(msg);
1114
              throw new NotAuthorized("1100", msg);
1115
          }
1116
          
1117
      } catch (RuntimeException e) {
1118
          // Convert Hazelcast runtime exceptions to service failures
1119
          String msg = "There was a problem creating the object identified by " +
1120
              pid.getValue() + ". There error message was: " + e.getMessage();
1121
          throw new ServiceFailure("4893", msg);
1122
          
1123
      } finally {
1124
    	  if (lock != null) {
1125
	          lock.unlock();
1126
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1127
    	  }
1128
      }
1129
      
1130
      return pid;
1131

    
1132
  }
1133

    
1134
  /**
1135
   * Set access for a given object using the object identifier and a Subject
1136
   * under a given Session.
1137
   * 
1138
   * @param session - the Session object containing the credentials for the Subject
1139
   * @param pid - the object identifier for the given object to apply the policy
1140
   * @param policy - the access policy to be applied
1141
   * 
1142
   * @return true if the application of the policy succeeds
1143
   * @throws InvalidToken
1144
   * @throws ServiceFailure
1145
   * @throws NotFound
1146
   * @throws NotAuthorized
1147
   * @throws NotImplemented
1148
   * @throws InvalidRequest
1149
   */
1150
  public boolean setAccessPolicy(Session session, Identifier pid, 
1151
      AccessPolicy accessPolicy, long serialVersion) 
1152
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1153
      NotImplemented, InvalidRequest, VersionMismatch {
1154
      
1155
      // The lock to be used for this identifier
1156
      Lock lock = null;
1157
      SystemMetadata systemMetadata = null;
1158
      
1159
      boolean success = false;
1160
      
1161
      // get the subject
1162
      Subject subject = session.getSubject();
1163
      
1164
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1165
          // are we allowed to do this?
1166
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1167
              throw new NotAuthorized("4420", "not allowed by "
1168
                      + subject.getValue() + " on " + pid.getValue());
1169
          }
1170
      }
1171
      
1172
      try {
1173
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1174
          lock.lock();
1175
          logMetacat.debug("Locked identifier " + pid.getValue());
1176

    
1177
          try {
1178
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1179

    
1180
              if ( systemMetadata == null ) {
1181
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1182
                  
1183
              }
1184
              // does the request have the most current system metadata?
1185
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1186
                 String msg = "The requested system metadata version number " + 
1187
                     serialVersion + " differs from the current version at " +
1188
                     systemMetadata.getSerialVersion().longValue() +
1189
                     ". Please get the latest copy in order to modify it.";
1190
                 throw new VersionMismatch("4402", msg);
1191
                 
1192
              }
1193
              
1194
          } catch (RuntimeException e) {
1195
              // convert Hazelcast RuntimeException to NotFound
1196
              throw new NotFound("4400", "No record found for: " + pid);
1197
            
1198
          }
1199
              
1200
          // set the access policy
1201
          systemMetadata.setAccessPolicy(accessPolicy);
1202
          
1203
          // update the system metadata
1204
          try {
1205
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1206
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1207
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1208
            
1209
          } catch (RuntimeException e) {
1210
              // convert Hazelcast RuntimeException to ServiceFailure
1211
              throw new ServiceFailure("4430", e.getMessage());
1212
            
1213
          }
1214
          
1215
      } catch (RuntimeException e) {
1216
          throw new ServiceFailure("4430", e.getMessage());
1217
          
1218
      } finally {
1219
          lock.unlock();
1220
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1221
        
1222
      }
1223

    
1224
    
1225
    // TODO: how do we know if the map was persisted?
1226
    success = true;
1227
    
1228
    return success;
1229
  }
1230

    
1231
  /**
1232
   * Full replacement of replication metadata in the system metadata for the 
1233
   * specified object, changes date system metadata modified
1234
   * 
1235
   * @param session - the Session object containing the credentials for the Subject
1236
   * @param pid - the object identifier for the given object to apply the policy
1237
   * @param replica - the replica to be updated
1238
   * @return
1239
   * @throws NotImplemented
1240
   * @throws NotAuthorized
1241
   * @throws ServiceFailure
1242
   * @throws InvalidRequest
1243
   * @throws NotFound
1244
   * @throws VersionMismatch
1245
   */
1246
  @Override
1247
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1248
      Replica replica, long serialVersion) 
1249
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1250
      NotFound, VersionMismatch {
1251
      
1252
      // The lock to be used for this identifier
1253
      Lock lock = null;
1254
      
1255
      // get the subject
1256
      Subject subject = session.getSubject();
1257
      
1258
      // are we allowed to do this?
1259
      try {
1260
        if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
1261
            // what is the controlling permission?
1262
            if (!isAuthorized(session, pid, Permission.WRITE)) {
1263
                throw new NotAuthorized("4851", "not allowed by "
1264
                        + subject.getValue() + " on " + pid.getValue());
1265
            }
1266
        }
1267
        
1268
      } catch (InvalidToken e) {
1269
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1270
                  " on " + pid.getValue());  
1271
          
1272
      }
1273

    
1274
      SystemMetadata systemMetadata = null;
1275
      try {
1276
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1277
          lock.lock();
1278
          logMetacat.debug("Locked identifier " + pid.getValue());
1279

    
1280
          try {      
1281
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1282

    
1283
              // does the request have the most current system metadata?
1284
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1285
                 String msg = "The requested system metadata version number " + 
1286
                     serialVersion + " differs from the current version at " +
1287
                     systemMetadata.getSerialVersion().longValue() +
1288
                     ". Please get the latest copy in order to modify it.";
1289
                 throw new VersionMismatch("4855", msg);
1290
              }
1291
              
1292
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1293
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1294
                  " : " + e.getMessage());
1295
            
1296
          }
1297
              
1298
          // set the status for the replica
1299
          List<Replica> replicas = systemMetadata.getReplicaList();
1300
          NodeReference replicaNode = replica.getReplicaMemberNode();
1301
          int index = 0;
1302
          for (Replica listedReplica: replicas) {
1303
              
1304
              // remove the replica that we are replacing
1305
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1306
                  replicas.remove(index);
1307
                  break;
1308
                  
1309
              }
1310
              index++;
1311
          }
1312
          
1313
          // add the new replica item
1314
          replicas.add(replica);
1315
          systemMetadata.setReplicaList(replicas);
1316
          
1317
          // update the metadata
1318
          try {
1319
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1320
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1321
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1322
            
1323
          } catch (RuntimeException e) {
1324
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1325
              throw new ServiceFailure("4852", e.getMessage());
1326
          
1327
          }
1328
          
1329
      } catch (RuntimeException e) {
1330
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1331
          throw new ServiceFailure("4852", e.getMessage());
1332
      
1333
      } finally {
1334
          lock.unlock();
1335
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1336
          
1337
      }
1338
    
1339
      return true;
1340
      
1341
  }
1342
  
1343
  /**
1344
   * 
1345
   */
1346
  @Override
1347
  public ObjectList listObjects(Session session, Date startTime, 
1348
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1349
      Integer start, Integer count)
1350
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1351
      ServiceFailure {
1352
      
1353
      ObjectList objectList = null;
1354
        try {
1355
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1356
        } catch (Exception e) {
1357
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1358
        }
1359

    
1360
        return objectList;
1361
  }
1362

    
1363
	/**
1364
	 * 
1365
	 */
1366
  @Override
1367
	public ChecksumAlgorithmList listChecksumAlgorithms()
1368
			throws ServiceFailure, NotImplemented {
1369
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1370
		cal.addAlgorithm("MD5");
1371
		cal.addAlgorithm("SHA-1");
1372
		return null;
1373
	}
1374
    
1375
}
(1-1/5)