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.types.v1.AccessPolicy;
55
import org.dataone.service.types.v1.Checksum;
56
import org.dataone.service.types.v1.ChecksumAlgorithmList;
57
import org.dataone.service.types.v1.Identifier;
58
import org.dataone.service.types.v1.Node;
59
import org.dataone.service.types.v1.NodeList;
60
import org.dataone.service.types.v1.NodeReference;
61
import org.dataone.service.types.v1.NodeType;
62
import org.dataone.service.types.v1.ObjectFormat;
63
import org.dataone.service.types.v1.ObjectFormatIdentifier;
64
import org.dataone.service.types.v1.ObjectFormatList;
65
import org.dataone.service.types.v1.ObjectList;
66
import org.dataone.service.types.v1.ObjectLocationList;
67
import org.dataone.service.types.v1.Permission;
68
import org.dataone.service.types.v1.Replica;
69
import org.dataone.service.types.v1.ReplicationPolicy;
70
import org.dataone.service.types.v1.ReplicationStatus;
71
import org.dataone.service.types.v1.Session;
72
import org.dataone.service.types.v1.Subject;
73
import org.dataone.service.types.v1.SystemMetadata;
74

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

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

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

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

    
149
          try {
150
              if ( HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid) ) {
151
                  systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
152
                  
153
              }
154
            
155

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

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

    
232
      try {
233
          lock = HazelcastService.getInstance().getLock(pid.getValue());
234
          lock.lock();
235
          logMetacat.debug("Locked identifier " + pid.getValue());
236

    
237
          try {      
238
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
239

    
240
              if ( systemMetadata == null ) {
241
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
242
                  
243
              }
244
              replicas = systemMetadata.getReplicaList();
245
              int count = 0;
246
              
247
              if ( replicas == null || replicas.size() < 1 ) {
248
                  logMetacat.debug("no replicas to evaluate");
249
                  throw new InvalidRequest("4730", "There are no replicas to update.");
250
                  
251
              }
252

    
253
              // find the target replica index in the replica list
254
              for (Replica replica: replicas) {
255
                  String replicaNodeStr = replica.getReplicaMemberNode().getValue();
256
                  String targetNodeStr = targetNode.getValue();
257
                  logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
258
                  
259
                  if (replicaNodeStr.equals(targetNodeStr)) {
260
                      replicaEntryIndex = count;
261
                      logMetacat.debug("replica entry index is: " + replicaEntryIndex);
262
                      break;
263
                  }
264
                  count++;
265
                  
266
              }
267

    
268
              // are we allowed to do this? only CNs and target MNs are allowed
269
              CNode cn = D1Client.getCN();
270
              List<Node> nodes = cn.listNodes().getNodeList();
271
              
272
              // find the node in the node list
273
              for ( Node node : nodes ) {
274
                  
275
                  NodeReference nodeReference = node.getIdentifier();
276
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
277
                  
278
                  // allow target MN certs and CN certs
279
                  if (targetNode.getValue().equals(nodeReference.getValue()) ||
280
                      node.getType() == NodeType.CN) {
281
                      List<Subject> nodeSubjects = node.getSubjectList();
282
                      
283
                      // check if the session subject is in the node subject list
284
                      for (Subject nodeSubject : nodeSubjects) {
285
                          if ( nodeSubject.equals(subject) ) {
286
                              allowed = true; // subject of session == target node subject
287
                              break;
288
                              
289
                          }
290
                      }                 
291
                  }
292
              }
293

    
294
              if ( !allowed && !isAdminAuthorized(session, pid, Permission.WRITE)) {
295
                  String msg = "The subject identified by " + subject.getValue() +
296
                    " does not have permission to set the replication status for " +
297
                    "the replica identified by " + targetNode.getValue() + ".";
298
                  logMetacat.info(msg);
299
                  throw new NotAuthorized("4720", msg);
300
                  
301
              }
302
              
303
              // was there a failure? log it
304
              if ( failure != null && status == ReplicationStatus.FAILED ) {
305
                 String msg = "The replication request of the object identified by " + 
306
                     pid.getValue() + " failed.  The error message was " +
307
                     failure.getMessage() + ".";
308
              }
309
              
310
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
311
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
312
                " : " + e.getMessage());
313
            
314
          }
315
          
316
          // set the status for the replica
317
          if ( replicaEntryIndex != -1 ) {
318
              Replica targetReplica = replicas.get(replicaEntryIndex);
319
              targetReplica.setReplicationStatus(status);
320
              logMetacat.debug("Set the replication status for " + 
321
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
322
                  targetReplica.getReplicationStatus());
323
              
324
          } else {
325
              throw new InvalidRequest("4730", "There are no replicas to update.");
326

    
327
          }
328
          
329
          systemMetadata.setReplicaList(replicas);
330
                
331
          // update the metadata
332
          try {
333
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
334
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
335
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
336
              
337
              if ( status == ReplicationStatus.FAILED && failure != null ) {
338
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
339
                      " on target node " + targetNode + ". The exception was: " +
340
                      failure.getMessage());
341
              }
342
          } catch (Exception e) {
343
              throw new ServiceFailure("4700", e.getMessage());
344
          
345
          }
346
          
347
    } catch (RuntimeException e) {
348
        String msg = "There was a RuntimeException getting the lock for " +
349
            pid.getValue();
350
        logMetacat.info(msg);
351
        
352
    } finally {
353
        lock.unlock();
354
        logMetacat.debug("Unlocked identifier " + pid.getValue());
355
        
356
    }
357
      
358
      return true;
359
  }
360

    
361
  /**
362
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
363
   * 
364
   * @param session - the Session object containing the credentials for the Subject
365
   * @param node - the node information for the given node be modified
366
   * 
367
   * @return true if the relationship exists
368
   * 
369
   * @throws InvalidToken
370
   * @throws ServiceFailure
371
   * @throws NotAuthorized
372
   * @throws NotFound
373
   * @throws InvalidRequest
374
   * @throws NotImplemented
375
   */
376
  @Override
377
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
378
    String relationship, Identifier pidOfObject) 
379
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
380
    InvalidRequest, NotImplemented {
381
    
382
    // The lock to be used for thyis identifier
383
    Lock lock = null;
384

    
385
    boolean asserted = false;
386
        
387
    // are we allowed to do this?
388
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
389
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());  
390
    }
391
    
392
    SystemMetadata systemMetadata = null;
393
    try {
394
        lock = HazelcastService.getInstance().getLock(pidOfSubject.getValue());
395
        lock.lock();
396
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
397
        
398
        
399
        // check relationships
400
        // TODO: use ORE map
401
        if (relationship.equalsIgnoreCase("describes")) {
402
          
403
        }
404
        
405
        if (relationship.equalsIgnoreCase("describedBy")) {
406
          
407
        }
408
        
409
        if (relationship.equalsIgnoreCase("derivedFrom")) {
410
          
411
        }
412
        
413
        if (relationship.equalsIgnoreCase("obsoletes")) {
414
            Identifier pid = systemMetadata.getObsoletes();
415
            if (pid.getValue().equals(pidOfObject.getValue())) {
416
              asserted = true;
417
            
418
        }
419
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
420
        }
421
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
422
            Identifier pid = systemMetadata.getObsoletedBy();
423
            if (pid.getValue().equals(pidOfObject.getValue())) {
424
              asserted = true;
425
            
426
        }
427
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
428
        }
429
              
430
    } catch (Exception e) {
431
        throw new ServiceFailure("4270", "Could not assert relation for: " + 
432
            pidOfSubject.getValue() +
433
            ". The error message was: " + e.getMessage());
434
      
435
    } finally {
436
        lock.unlock();
437
        logMetacat.debug("Unlocked identifier " + pidOfSubject.getValue());
438

    
439
    }
440
        
441
    return asserted;
442
  }
443
  
444
  /**
445
   * Return the checksum of the object given the identifier 
446
   * 
447
   * @param session - the Session object containing the credentials for the Subject
448
   * @param pid - the object identifier for the given object
449
   * 
450
   * @return checksum - the checksum of the object
451
   * 
452
   * @throws InvalidToken
453
   * @throws ServiceFailure
454
   * @throws NotAuthorized
455
   * @throws NotFound
456
   * @throws NotImplemented
457
   */
458
  @Override
459
  public Checksum getChecksum(Session session, Identifier pid)
460
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
461
    NotImplemented {
462
        
463
    // The lock to be used for thyis identifier
464
    Lock lock = null;
465
    
466
    if (!isAuthorized(session, pid, Permission.READ)) {
467
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
468
    }
469
    
470
    SystemMetadata systemMetadata = null;
471
    Checksum checksum = null;
472
    
473
    try {
474
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
475
        checksum = systemMetadata.getChecksum();
476

    
477
    } catch (Exception e) {
478
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
479
            pid.getValue() + ". The error message was: " + e.getMessage());
480
      
481
    }
482
    
483
    return checksum;
484
  }
485

    
486
  /**
487
   * Resolve the location of a given object
488
   * 
489
   * @param session - the Session object containing the credentials for the Subject
490
   * @param pid - the object identifier for the given object
491
   * 
492
   * @return objectLocationList - the list of nodes known to contain the object
493
   * 
494
   * @throws InvalidToken
495
   * @throws ServiceFailure
496
   * @throws NotAuthorized
497
   * @throws NotFound
498
   * @throws NotImplemented
499
   */
500
  @Override
501
  public ObjectLocationList resolve(Session session, Identifier pid)
502
    throws InvalidToken, ServiceFailure, NotAuthorized,
503
    NotFound, NotImplemented {
504

    
505
    throw new NotImplemented("4131", "resolve not implemented");
506

    
507
  }
508

    
509
  /**
510
   * Search the metadata catalog for identifiers that match the criteria
511
   * 
512
   * @param session - the Session object containing the credentials for the Subject
513
   * @param queryType - An identifier for the type of query expression 
514
   *                    provided in the query
515
   * @param query -  The criteria for matching the characteristics of the 
516
   *                 metadata objects of interest
517
   * 
518
   * @return objectList - the list of objects matching the criteria
519
   * 
520
   * @throws InvalidToken
521
   * @throws ServiceFailure
522
   * @throws NotAuthorized
523
   * @throws InvalidRequest
524
   * @throws NotImplemented
525
   */
526
  @Override
527
  public ObjectList search(Session session, String queryType, String query)
528
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
529
    NotImplemented {
530

    
531
    ObjectList objectList = null;
532
    try {
533
        objectList = 
534
          IdentifierManager.getInstance().querySystemMetadata(
535
              null, //startTime, 
536
              null, //endTime,
537
              null, //objectFormat, 
538
              false, //replicaStatus, 
539
              0, //start, 
540
              -1 //count
541
              );
542
        
543
    } catch (Exception e) {
544
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
545
    }
546

    
547
      return objectList;
548
      
549
    //throw new NotImplemented("4281", "search not implemented");
550
    
551
    // the code block below is from an older implementation
552
    
553
    /*  This block commented out because of the EcoGrid circular dependency.
554
         *  For now, query will not be supported until the circularity can be
555
         *  resolved, probably by moving the ecogrid query syntax transformers
556
         *  directly into the Metacat codebase.  MBJ 2010-02-03
557
         
558
        try {
559
            EcogridQueryParser parser = new EcogridQueryParser(request
560
                    .getReader());
561
            parser.parseXML();
562
            QueryType queryType = parser.getEcogridQuery();
563
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
564
                new EcogridJavaToMetacatJavaQueryTransformer();
565
            QuerySpecification metacatQuery = queryTransformer
566
                    .transform(queryType);
567

    
568
            DBQuery metacat = new DBQuery();
569

    
570
            boolean useXMLIndex = (new Boolean(PropertyService
571
                    .getProperty("database.usexmlindex"))).booleanValue();
572
            String xmlquery = "query"; // we don't care the query in resultset,
573
            // the query can be anything
574
            PrintWriter out = null; // we don't want metacat result, so set out null
575

    
576
            // parameter: queryspecification, user, group, usingIndexOrNot
577
            StringBuffer result = metacat.createResultDocument(xmlquery,
578
                    metacatQuery, out, username, groupNames, useXMLIndex);
579

    
580
            // create result set transfer       
581
            String saxparser = PropertyService.getProperty("xml.saxparser");
582
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
583
                    new StringReader(result.toString()), saxparser, queryType
584
                            .getNamespace().get_value());
585
            ResultsetType records = metacatResultsetParser.getEcogridResult();
586

    
587
            System.out
588
                    .println(EcogridResultsetTransformer.toXMLString(records));
589
            response.setContentType("text/xml");
590
            out = response.getWriter();
591
            out.print(EcogridResultsetTransformer.toXMLString(records));
592

    
593
        } catch (Exception e) {
594
            e.printStackTrace();
595
        }*/
596
    
597

    
598
  }
599
  
600
  /**
601
   * Returns the object format registered in the DataONE Object Format 
602
   * Vocabulary for the given format identifier
603
   * 
604
   * @param fmtid - the identifier of the format requested
605
   * 
606
   * @return objectFormat - the object format requested
607
   * 
608
   * @throws ServiceFailure
609
   * @throws NotFound
610
   * @throws InsufficientResources
611
   * @throws NotImplemented
612
   */
613
  @Override
614
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
615
    throws ServiceFailure, NotFound, NotImplemented {
616
     
617
      return ObjectFormatService.getInstance().getFormat(fmtid);
618
      
619
  }
620

    
621
  /**
622
   * Returns a list of all object formats registered in the DataONE Object 
623
   * Format Vocabulary
624
    * 
625
   * @return objectFormatList - The list of object formats registered in 
626
   *                            the DataONE Object Format Vocabulary
627
   * 
628
   * @throws ServiceFailure
629
   * @throws NotImplemented
630
   * @throws InsufficientResources
631
   */
632
  @Override
633
  public ObjectFormatList listFormats() 
634
    throws ServiceFailure, NotImplemented {
635

    
636
    return ObjectFormatService.getInstance().listFormats();
637
  }
638

    
639
  /**
640
   * Returns a list of nodes that have been registered with the DataONE infrastructure
641
    * 
642
   * @return nodeList - List of nodes from the registry
643
   * 
644
   * @throws ServiceFailure
645
   * @throws NotImplemented
646
   */
647
  @Override
648
  public NodeList listNodes() 
649
    throws NotImplemented, ServiceFailure {
650

    
651
    throw new NotImplemented("4800", "listNodes not implemented");
652
  }
653

    
654
  /**
655
   * Provides a mechanism for adding system metadata independently of its 
656
   * associated object, such as when adding system metadata for data objects.
657
    * 
658
   * @param session - the Session object containing the credentials for the Subject
659
   * @param pid - The identifier of the object to register the system metadata against
660
   * @param sysmeta - The system metadata to be registered
661
   * 
662
   * @return true if the registration succeeds
663
   * 
664
   * @throws NotImplemented
665
   * @throws NotAuthorized
666
   * @throws ServiceFailure
667
   * @throws InvalidRequest
668
   * @throws InvalidSystemMetadata
669
   */
670
  @Override
671
  public Identifier registerSystemMetadata(Session session, Identifier pid,
672
      SystemMetadata sysmeta) 
673
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
674
      InvalidSystemMetadata {
675

    
676
      // The lock to be used for this identifier
677
      Lock lock = null;
678

    
679
      // TODO: control who can call this?
680
      if (session == null) {
681
          //TODO: many of the thrown exceptions do not use the correct error codes
682
          //check these against the docs and correct them
683
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
684
                  "  If you are not logged in, please do so and retry the request.");
685
      }
686
      
687
      // verify that guid == SystemMetadata.getIdentifier()
688
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
689
          "|" + sysmeta.getIdentifier().getValue());
690
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
691
          throw new InvalidRequest("4863", 
692
              "The identifier in method call (" + pid.getValue() + 
693
              ") does not match identifier in system metadata (" +
694
              sysmeta.getIdentifier().getValue() + ").");
695
      }
696

    
697
      try {
698
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
699
          lock.lock();
700
          logMetacat.debug("Locked identifier " + pid.getValue());
701
          logMetacat.debug("Checking if identifier exists...");
702
          // Check that the identifier does not already exist
703
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
704
              throw new InvalidRequest("4863", 
705
                  "The identifier is already in use by an existing object.");
706
          
707
          }
708
          
709
          // insert the system metadata into the object store
710
          logMetacat.debug("Starting to insert SystemMetadata...");
711
          try {
712
              sysmeta.setSerialVersion(BigInteger.ONE);
713
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
714
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
715
              
716
          } catch (Exception e) {
717
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
718
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
719
                  e.getClass() + ": " + e.getMessage());
720
              
721
          }
722
          
723
      } catch (Exception e) {
724
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
725
                  e.getClass() + ": " + e.getMessage());
726
          
727
      }  finally {
728
          lock.unlock();
729
          logMetacat.debug("Unlocked identifier " + pid.getValue());
730
          
731
      }
732

    
733
      
734
      logMetacat.debug("Returning from registerSystemMetadata");
735
      EventLog.getInstance().log(request.getRemoteAddr(), 
736
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
737
          pid.getValue(), "registerSystemMetadata");
738
      return pid;
739
  }
740
  
741
  /**
742
   * Given an optional scope and format, reserves and returns an identifier 
743
   * within that scope and format that is unique and will not be 
744
   * used by any other sessions. 
745
    * 
746
   * @param session - the Session object containing the credentials for the Subject
747
   * @param pid - The identifier of the object to register the system metadata against
748
   * @param scope - An optional string to be used to qualify the scope of 
749
   *                the identifier namespace, which is applied differently 
750
   *                depending on the format requested. If scope is not 
751
   *                supplied, a default scope will be used.
752
   * @param format - The optional name of the identifier format to be used, 
753
   *                  drawn from a DataONE-specific vocabulary of identifier 
754
   *                 format names, including several common syntaxes such 
755
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
756
   *                 format is not supplied by the caller, the CN service 
757
   *                 will use a default identifier format, which may change 
758
   *                 over time.
759
   * 
760
   * @return true if the registration succeeds
761
   * 
762
   * @throws InvalidToken
763
   * @throws ServiceFailure
764
   * @throws NotAuthorized
765
   * @throws IdentifierNotUnique
766
   * @throws NotImplemented
767
   */
768
  @Override
769
  public Identifier reserveIdentifier(Session session, Identifier pid)
770
  throws InvalidToken, ServiceFailure,
771
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
772

    
773
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
774
  }
775
  
776
  @Override
777
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
778
  throws InvalidToken, ServiceFailure,
779
        NotAuthorized, NotImplemented, InvalidRequest {
780
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
781
  }
782
  
783
  /**
784
    * Checks whether the pid is reserved by the subject in the session param
785
    * If the reservation is held on the pid by the subject, we return true.
786
    * 
787
   * @param session - the Session object containing the Subject
788
   * @param pid - The identifier to check
789
   * 
790
   * @return true if the reservation exists for the subject/pid
791
   * 
792
   * @throws InvalidToken
793
   * @throws ServiceFailure
794
   * @throws NotFound - when the pid is not found (in use or in reservation)
795
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
796
   * @throws IdentifierNotUnique - when the pid is in use
797
   * @throws NotImplemented
798
   */
799

    
800
  @Override
801
  public boolean hasReservation(Session session, Identifier pid) 
802
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
803
      NotImplemented, InvalidRequest {
804
  
805
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
806
  }
807

    
808
  /**
809
   * Changes ownership (RightsHolder) of the specified object to the 
810
   * subject specified by userId
811
    * 
812
   * @param session - the Session object containing the credentials for the Subject
813
   * @param pid - Identifier of the object to be modified
814
   * @param userId - The subject that will be taking ownership of the specified object.
815
   *
816
   * @return pid - the identifier of the modified object
817
   * 
818
   * @throws ServiceFailure
819
   * @throws InvalidToken
820
   * @throws NotFound
821
   * @throws NotAuthorized
822
   * @throws NotImplemented
823
   * @throws InvalidRequest
824
   */  
825
  @Override
826
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
827
      long serialVersion)
828
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
829
      NotImplemented, InvalidRequest {
830
      
831
      // The lock to be used for this identifier
832
      Lock lock = null;
833

    
834
      // get the subject
835
      Subject subject = session.getSubject();
836
      
837
      // are we allowed to do this?
838
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
839
        if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
840
            throw new NotAuthorized("4440", "not allowed by "
841
                    + subject.getValue() + " on " + pid.getValue());
842
        }
843
    }
844
    SystemMetadata systemMetadata = null;
845
      try {
846
          lock = HazelcastService.getInstance().getLock(pid.getValue());
847
          logMetacat.debug("Locked identifier " + pid.getValue());
848

    
849
          try {
850
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
851
              
852
              // does the request have the most current system metadata?
853
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
854
                 String msg = "The requested system metadata version number " + 
855
                     serialVersion + " differs from the current version at " +
856
                     systemMetadata.getSerialVersion().longValue() +
857
                     ". Please get the latest copy in order to modify it.";
858
                 throw new InvalidRequest("4442", msg);
859
              }
860
              
861
          } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
862
              throw new NotFound("4460", "No record found for: " + pid.getValue());
863
              
864
          }
865
              
866
          // set the new rights holder
867
          systemMetadata.setRightsHolder(userId);
868
          
869
          // update the metadata
870
          try {
871
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
872
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
873
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
874
              
875
          } catch (Exception e) {
876
          throw new ServiceFailure("4490", e.getMessage());
877
          
878
          }
879
          
880
      } catch (Exception e) {
881
          throw new ServiceFailure("4490", e.getMessage());
882
          
883
      } finally {
884
          lock.unlock();
885
          logMetacat.debug("Unlocked identifier " + pid.getValue());
886
      
887
      }
888
      
889
    return pid;
890
  }
891

    
892
  /**
893
   * Verify that a replication task is authorized by comparing the target node's
894
   * Subject (from the X.509 certificate-derived Session) with the list of 
895
   * subjects in the known, pending replication tasks map.
896
   * 
897
   * @param originatingNodeSession - Session information that contains the 
898
   *                                 identity of the calling user
899
   * @param targetNodeSubject - Subject identifying the target node
900
   * @param pid - the identifier of the object to be replicated
901
   * @param replicatePermission - the execute permission to be granted
902
   * 
903
   * @throws ServiceFailure
904
   * @throws NotImplemented
905
   * @throws InvalidToken
906
   * @throws NotAuthorized
907
   * @throws InvalidRequest
908
   * @throws NotFound
909
   */
910
  @Override
911
  public boolean isNodeAuthorized(Session originatingNodeSession, 
912
    Subject targetNodeSubject, Identifier pid) 
913
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
914
    NotFound, InvalidRequest {
915

    
916
    // The lock to be used for this identifier
917
    Lock lock = null;
918
    
919
    boolean isAllowed = false;
920
    SystemMetadata sysmeta = null;
921
    NodeReference targetNode = null;
922
    
923
    try {
924
      // get the target node reference from the nodes list
925
      CNode cn = D1Client.getCN();
926
      List<Node> nodes = cn.listNodes().getNodeList();
927
      
928
      if ( nodes != null ) {
929
        for (Node node : nodes) {
930
            
931
            for (Subject nodeSubject : node.getSubjectList()) {
932
                
933
                if ( nodeSubject.equals(targetNodeSubject) ) {
934
                    targetNode = node.getIdentifier();
935
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
936
                    break;
937
                }
938
            }
939
            
940
            if ( targetNode != null) { break; }
941
        }
942
        
943
      } else {
944
          String msg = "Couldn't get the node list from the CN";
945
          logMetacat.debug(msg);
946
          throw new ServiceFailure("4872", msg);
947
          
948
      }
949
      
950
      // can't find a node listed with the given subject
951
      if ( targetNode == null ) {
952
          String msg = "There is no Member Node registered with a node subject " +
953
              "matching " + targetNodeSubject.getValue();
954
          logMetacat.info(msg);
955
          throw new ServiceFailure("4872", msg);
956
          
957
      }
958
      
959
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
960
      
961
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
962

    
963
      if ( sysmeta != null ) {
964
          
965
          List<Replica> replicaList = sysmeta.getReplicaList();
966
          
967
          if ( replicaList != null ) {
968
              
969
              // find the replica with the status set to 'requested'
970
              for (Replica replica : replicaList) {
971
                  ReplicationStatus status = replica.getReplicationStatus();
972
                  NodeReference listedNode = replica.getReplicaMemberNode();
973
                  if ( listedNode != null && targetNode != null ) {
974
                      logMetacat.debug("Comparing " + listedNode.getValue()
975
                              + " to " + targetNode.getValue());
976
                      
977
                      if (listedNode.getValue().equals(targetNode.getValue())
978
                              && status.equals(ReplicationStatus.REQUESTED)) {
979
                          isAllowed = true;
980
                          break;
981

    
982
                      }
983
                  }
984
              }
985
          }
986
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
987
              "to replicate: " + isAllowed + " for " + pid.getValue());
988

    
989
          
990
      } else {
991
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
992
          " is null.");          
993
          
994
      }
995

    
996
    } catch (RuntimeException e) {
997
    	  ServiceFailure sf = new ServiceFailure("4872", 
998
                "Runtime Exception: Couldn't determine if node is allowed: " + 
999
                e.getCause().getMessage());
1000
    	  sf.initCause(e);
1001
        throw sf;
1002
        
1003
    }
1004
      
1005
    return isAllowed;
1006
    
1007
  }
1008

    
1009
  /**
1010
   * Adds a new object to the Node, where the object is a science metadata object.
1011
   * 
1012
   * @param session - the Session object containing the credentials for the Subject
1013
   * @param pid - The object identifier to be created
1014
   * @param object - the object bytes
1015
   * @param sysmeta - the system metadata that describes the object  
1016
   * 
1017
   * @return pid - the object identifier created
1018
   * 
1019
   * @throws InvalidToken
1020
   * @throws ServiceFailure
1021
   * @throws NotAuthorized
1022
   * @throws IdentifierNotUnique
1023
   * @throws UnsupportedType
1024
   * @throws InsufficientResources
1025
   * @throws InvalidSystemMetadata
1026
   * @throws NotImplemented
1027
   * @throws InvalidRequest
1028
   */
1029
  public Identifier create(Session session, Identifier pid, InputStream object,
1030
    SystemMetadata sysmeta) 
1031
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1032
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1033
    NotImplemented, InvalidRequest {
1034
      
1035
      
1036
      // The lock to be used for this identifier
1037
      Lock lock = null;
1038
      
1039
      try {
1040
        // are we allowed?
1041
          boolean isAllowed = false;
1042
          CNode cn = D1Client.getCN();
1043
          NodeList nodeList = cn.listNodes();
1044
          
1045
          for (Node node : nodeList.getNodeList()) {
1046
              if ( node.getType().equals(NodeType.CN) ) {
1047
                  
1048
                  List<Subject> subjects = node.getSubjectList();
1049
                  for (Subject subject : subjects) {
1050
                     if (subject.equals(session.getSubject())) {
1051
                         isAllowed = true;
1052
                         break;
1053
                     }
1054
                  }
1055
              }
1056
          }
1057

    
1058
          // proceed if we're called by a CN
1059
          if ( isAllowed ) {
1060
              // create the coordinating node version of the document      
1061
              lock = HazelcastService.getInstance().getLock(pid.getValue());
1062
              lock.lock();
1063
              logMetacat.debug("Locked identifier " + pid.getValue());
1064
              sysmeta.setSerialVersion(BigInteger.ONE);
1065
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1066
              pid = super.create(session, pid, object, sysmeta);
1067

    
1068
          } else {
1069
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1070
                  " isn't allowed to call create() on a Coordinating Node.";
1071
              logMetacat.info(msg);
1072
              throw new NotAuthorized("1100", msg);
1073
          }
1074
          
1075
      } catch (RuntimeException e) {
1076
          // Convert Hazelcast runtime exceptions to service failures
1077
          String msg = "There was a problem creating the object identified by " +
1078
              pid.getValue() + ". There error message was: " + e.getMessage();
1079
          throw new ServiceFailure("4893", msg);
1080
          
1081
      } finally {
1082
    	  if (lock != null) {
1083
	          lock.unlock();
1084
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1085
    	  }
1086
      }
1087
      
1088
      return pid;
1089

    
1090
  }
1091

    
1092
  /**
1093
   * Set access for a given object using the object identifier and a Subject
1094
   * under a given Session.
1095
   * 
1096
   * @param session - the Session object containing the credentials for the Subject
1097
   * @param pid - the object identifier for the given object to apply the policy
1098
   * @param policy - the access policy to be applied
1099
   * 
1100
   * @return true if the application of the policy succeeds
1101
   * @throws InvalidToken
1102
   * @throws ServiceFailure
1103
   * @throws NotFound
1104
   * @throws NotAuthorized
1105
   * @throws NotImplemented
1106
   * @throws InvalidRequest
1107
   */
1108
  public boolean setAccessPolicy(Session session, Identifier pid, 
1109
      AccessPolicy accessPolicy, long serialVersion) 
1110
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1111
      NotImplemented, InvalidRequest {
1112
      
1113
      // The lock to be used for this identifier
1114
      Lock lock = null;
1115
      
1116
      boolean success = false;
1117
      
1118
      // get the subject
1119
      Subject subject = session.getSubject();
1120
      
1121
      // are we allowed to do this?
1122
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1123
          throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + 
1124
          " on " + pid.getValue());  
1125
      }
1126
      
1127
      SystemMetadata systemMetadata = null;
1128
      try {
1129
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1130
          lock.lock();
1131
          logMetacat.debug("Locked identifier " + pid.getValue());
1132

    
1133
          try {
1134
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1135

    
1136
              // does the request have the most current system metadata?
1137
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1138
                 String msg = "The requested system metadata version number " + 
1139
                     serialVersion + " differs from the current version at " +
1140
                     systemMetadata.getSerialVersion().longValue() +
1141
                     ". Please get the latest copy in order to modify it.";
1142
                 throw new InvalidRequest("4402", msg);
1143
              }
1144
              
1145
          } catch (Exception e) {
1146
              // convert Hazelcast RuntimeException to NotFound
1147
              throw new NotFound("4400", "No record found for: " + pid);
1148
            
1149
          }
1150
              
1151
          // set the access policy
1152
          systemMetadata.setAccessPolicy(accessPolicy);
1153
          
1154
          // update the system metadata
1155
          try {
1156
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1157
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1158
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1159
            
1160
          } catch (Exception e) {
1161
              // convert Hazelcast RuntimeException to ServiceFailure
1162
              throw new ServiceFailure("4430", e.getMessage());
1163
            
1164
          }
1165
          
1166
      } catch (Exception e) {
1167
          throw new ServiceFailure("4430", e.getMessage());
1168
          
1169
      } finally {
1170
          lock.unlock();
1171
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1172
        
1173
      }
1174

    
1175
    
1176
    // TODO: how do we know if the map was persisted?
1177
    success = true;
1178
    
1179
    return success;
1180
  }
1181

    
1182
  /**
1183
   * Full replacement of replication metadata in the system metadata for the 
1184
   * specified object, changes date system metadata modified
1185
   * 
1186
   * @param session - the Session object containing the credentials for the Subject
1187
   * @param pid - the object identifier for the given object to apply the policy
1188
   * @param replica - the replica to be updated
1189
   * @return
1190
   * @throws NotImplemented
1191
   * @throws NotAuthorized
1192
   * @throws ServiceFailure
1193
   * @throws InvalidRequest
1194
   * @throws NotFound
1195
   */
1196
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1197
      Replica replica, long serialVersion) 
1198
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1199
      NotFound {
1200
      
1201
      // The lock to be used for this identifier
1202
      Lock lock = null;
1203
      
1204
      // get the subject
1205
      Subject subject = session.getSubject();
1206
      
1207
      // are we allowed to do this?
1208
      try {
1209
        if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
1210
            // what is the controlling permission?
1211
            if (!isAuthorized(session, pid, Permission.WRITE)) {
1212
                throw new NotAuthorized("4851", "not allowed by "
1213
                        + subject.getValue() + " on " + pid.getValue());
1214
            }
1215
        }
1216
        
1217
      } catch (InvalidToken e) {
1218
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1219
                  " on " + pid.getValue());  
1220
          
1221
      }
1222

    
1223
      SystemMetadata systemMetadata = null;
1224
      try {
1225
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1226
          lock.lock();
1227
          logMetacat.debug("Locked identifier " + pid.getValue());
1228

    
1229
          try {      
1230
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1231

    
1232
              // does the request have the most current system metadata?
1233
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1234
                 String msg = "The requested system metadata version number " + 
1235
                     serialVersion + " differs from the current version at " +
1236
                     systemMetadata.getSerialVersion().longValue() +
1237
                     ". Please get the latest copy in order to modify it.";
1238
                 throw new InvalidRequest("4853", msg);
1239
              }
1240
              
1241
          } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1242
            throw new NotFound("4854", "No record found for: " + pid.getValue() +
1243
                " : " + e.getMessage());
1244
            
1245
          }
1246
              
1247
          // set the status for the replica
1248
          List<Replica> replicas = systemMetadata.getReplicaList();
1249
          NodeReference replicaNode = replica.getReplicaMemberNode();
1250
          int index = 0;
1251
          for (Replica listedReplica: replicas) {
1252
              
1253
              // remove the replica that we are replacing
1254
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1255
                  replicas.remove(index);
1256
                  break;
1257
                  
1258
              }
1259
              index++;
1260
          }
1261
          
1262
          // add the new replica item
1263
          replicas.add(replica);
1264
          systemMetadata.setReplicaList(replicas);
1265
          
1266
          // update the metadata
1267
          try {
1268
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1269
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1270
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1271
            
1272
          } catch (Exception e) {
1273
              throw new ServiceFailure("4852", e.getMessage());
1274
          
1275
          }
1276
          
1277
    } catch (Exception e) {
1278
        throw new ServiceFailure("4852", e.getMessage());
1279

    
1280
    } finally {
1281
        lock.unlock();
1282
        logMetacat.debug("Unlocked identifier " + pid.getValue());
1283
        
1284
    }
1285
    
1286
      return true;
1287
      
1288
  }
1289
  
1290
    @Override
1291
  public ObjectList listObjects(Session session, Date startTime, 
1292
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1293
      Integer start, Integer count)
1294
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1295
      ServiceFailure {
1296
      
1297
      ObjectList objectList = null;
1298
        try {
1299
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1300
        } catch (Exception e) {
1301
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1302
        }
1303

    
1304
        return objectList;
1305
  }
1306

    
1307
	@Override
1308
	public ChecksumAlgorithmList listChecksumAlgorithms()
1309
			throws ServiceFailure, NotImplemented {
1310
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1311
		cal.addAlgorithm("MD5");
1312
		cal.addAlgorithm("SHA-1");
1313
		return null;
1314
	}
1315
    
1316
}
(1-1/5)