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.concurrent.locks.Lock;
32

    
33
import javax.servlet.http.HttpServletRequest;
34

    
35
import org.apache.log4j.Logger;
36
import org.dataone.client.CNode;
37
import org.dataone.client.D1Client;
38
import org.dataone.service.cn.v1.CNAuthorization;
39
import org.dataone.service.cn.v1.CNCore;
40
import org.dataone.service.cn.v1.CNRead;
41
import org.dataone.service.cn.v1.CNReplication;
42
import org.dataone.service.exceptions.BaseException;
43
import org.dataone.service.exceptions.IdentifierNotUnique;
44
import org.dataone.service.exceptions.InsufficientResources;
45
import org.dataone.service.exceptions.InvalidRequest;
46
import org.dataone.service.exceptions.InvalidSystemMetadata;
47
import org.dataone.service.exceptions.InvalidToken;
48
import org.dataone.service.exceptions.NotAuthorized;
49
import org.dataone.service.exceptions.NotFound;
50
import org.dataone.service.exceptions.NotImplemented;
51
import org.dataone.service.exceptions.ServiceFailure;
52
import org.dataone.service.exceptions.UnsupportedType;
53
import org.dataone.service.exceptions.VersionMismatch;
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
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
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
   * Deletes the replica from the given Member Node
208
   * NOTE: MN.delete() may be an "archive" operation. TBD.
209
   * @param session
210
   * @param pid
211
   * @param nodeId
212
   * @param serialVersion
213
   * @return
214
   * @throws InvalidToken
215
   * @throws ServiceFailure
216
   * @throws NotAuthorized
217
   * @throws NotFound
218
   * @throws NotImplemented
219
   */
220
  public boolean deleteReplicationMetadata(Session session, Identifier pid, NodeReference nodeId, long serialVersion) 
221
  	throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
222
	  
223
	  List<Node> nodeList = D1Client.getCN().listNodes().getNodeList();
224
	  
225
	  // check permissions
226
	  boolean isAllowed = ServiceMethodRestrictionUtil.isMethodAllowed(session.getSubject(), nodeList, "CNReplication", "deleteReplicationMetadata");
227
	  
228
	  if (isAllowed) {
229
		  throw new NotAuthorized("0000", "Caller is not authorized to deleteReplicationMetadata");
230
	  }
231
	  
232
	  // delete the replica from the given node
233
	  D1Client.getMN(nodeId).delete(session, pid);
234
	  
235
	  return true;
236
  }
237
  
238
  
239
  /**
240
   * Set the replication status for an object given the object identifier
241
   * 
242
   * @param session - the Session object containing the credentials for the Subject
243
   * @param pid - the object identifier for the given object
244
   * @param status - the replication status to be applied
245
   * 
246
   * @return true or false
247
   * 
248
   * @throws NotImplemented
249
   * @throws NotAuthorized
250
   * @throws ServiceFailure
251
   * @throws InvalidRequest
252
   * @throws InvalidToken
253
   * @throws NotFound
254
   * 
255
   */
256
  @Override
257
  public boolean setReplicationStatus(Session session, Identifier pid,
258
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
259
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
260
      InvalidRequest, NotFound {
261
      
262
      // The lock to be used for this identifier
263
      Lock lock = null;
264
      
265
      boolean allowed = false;
266
      int replicaEntryIndex = -1;
267
      List<Replica> replicas = null;
268
      // get the subject
269
      Subject subject = session.getSubject();
270
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
271
          " is " + status.toString());
272
      
273
      SystemMetadata systemMetadata = null;
274

    
275
      try {
276
          lock = HazelcastService.getInstance().getLock(pid.getValue());
277
          lock.lock();
278
          logMetacat.debug("Locked identifier " + pid.getValue());
279

    
280
          try {      
281
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
282

    
283
              // did we get it correctly?
284
              if ( systemMetadata == null ) {
285
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
286
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
287
                  
288
              }
289
              replicas = systemMetadata.getReplicaList();
290
              int count = 0;
291
              
292
              // was there a failure? log it
293
              if ( failure != null && status == ReplicationStatus.FAILED ) {
294
                 String msg = "The replication request of the object identified by " + 
295
                     pid.getValue() + " failed.  The error message was " +
296
                     failure.getMessage() + ".";
297
              }
298
              
299
              if (replicas.size() > 0 && replicas != null) {
300
                  // find the target replica index in the replica list
301
                  for (Replica replica : replicas) {
302
                      String replicaNodeStr = replica.getReplicaMemberNode()
303
                              .getValue();
304
                      String targetNodeStr = targetNode.getValue();
305
                      logMetacat.debug("Comparing " + replicaNodeStr + " to "
306
                              + targetNodeStr);
307
                  
308
                      if (replicaNodeStr.equals(targetNodeStr)) {
309
                          replicaEntryIndex = count;
310
                          logMetacat.debug("replica entry index is: "
311
                                  + replicaEntryIndex);
312
                          break;
313
                      }
314
                      count++;
315
                  
316
                  }
317
              }
318
              // are we allowed to do this? only CNs and target MNs are allowed
319
              CNode cn = D1Client.getCN();
320
              List<Node> nodes = cn.listNodes().getNodeList();
321
              
322
              // find the node in the node list
323
              for ( Node node : nodes ) {
324
                  
325
                  NodeReference nodeReference = node.getIdentifier();
326
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
327
                  
328
                  // allow target MN certs and CN certs
329
                  if (targetNode.getValue().equals(nodeReference.getValue()) ||
330
                      node.getType() == NodeType.CN) {
331
                      List<Subject> nodeSubjects = node.getSubjectList();
332
                      
333
                      // check if the session subject is in the node subject list
334
                      for (Subject nodeSubject : nodeSubjects) {
335
                          if ( nodeSubject.equals(subject) ) {
336
                              allowed = true; // subject of session == target node subject
337
                              break;
338
                              
339
                          }
340
                      }                 
341
                  }
342
              }
343

    
344
              if ( !isAdminAuthorized(session, pid, Permission.WRITE) ) {
345
                  if (!allowed) {
346
                    String msg = "The subject identified by "
347
                            + subject.getValue()
348
                            + " does not have permission to set the replication status for "
349
                            + "the replica identified by "
350
                            + targetNode.getValue() + ".";
351
                    logMetacat.info(msg);
352
                    throw new NotAuthorized("4720", msg);
353
                }
354
                  
355
              }
356
              
357

    
358
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
359
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
360
                " : " + e.getMessage());
361
            
362
          }
363
          
364
          Replica targetReplica = new Replica();
365
          // set the status for the replica
366
          if ( replicaEntryIndex != -1 ) {
367
              targetReplica = replicas.get(replicaEntryIndex);
368
              targetReplica.setReplicationStatus(status);
369
              logMetacat.debug("Set the replication status for " + 
370
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
371
                  targetReplica.getReplicationStatus());
372
              
373
          } else {
374
              // this is a new entry, create it
375
              targetReplica.setReplicaMemberNode(targetNode);
376
              targetReplica.setReplicationStatus(status);
377
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
378
              replicas.add(targetReplica);
379
              
380
          }
381
          
382
          systemMetadata.setReplicaList(replicas);
383
                
384
          // update the metadata
385
          try {
386
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
387
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
388
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
389
              
390
              if ( status == ReplicationStatus.FAILED && failure != null ) {
391
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
392
                      " on target node " + targetNode + ". The exception was: " +
393
                      failure.getMessage());
394
              }
395
          } catch (RuntimeException e) {
396
              throw new ServiceFailure("4700", e.getMessage());
397
          
398
          }
399
          
400
    } catch (RuntimeException e) {
401
        String msg = "There was a RuntimeException getting the lock for " +
402
            pid.getValue();
403
        logMetacat.info(msg);
404
        
405
    } finally {
406
        lock.unlock();
407
        logMetacat.debug("Unlocked identifier " + pid.getValue());
408
        
409
    }
410
      
411
      return true;
412
  }
413
  
414
  /**
415
   * Return the checksum of the object given the identifier 
416
   * 
417
   * @param session - the Session object containing the credentials for the Subject
418
   * @param pid - the object identifier for the given object
419
   * 
420
   * @return checksum - the checksum of the object
421
   * 
422
   * @throws InvalidToken
423
   * @throws ServiceFailure
424
   * @throws NotAuthorized
425
   * @throws NotFound
426
   * @throws NotImplemented
427
   */
428
  @Override
429
  public Checksum getChecksum(Session session, Identifier pid)
430
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
431
    NotImplemented {
432
            
433
    if (!isAuthorized(session, pid, Permission.READ)) {
434
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
435
    }
436
    
437
    SystemMetadata systemMetadata = null;
438
    Checksum checksum = null;
439
    
440
    try {
441
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
442

    
443
        if (systemMetadata == null ) {
444
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
445
        }
446
        checksum = systemMetadata.getChecksum();
447
        
448
    } catch (RuntimeException e) {
449
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
450
            pid.getValue() + ". The error message was: " + e.getMessage());
451
      
452
    }
453
    
454
    return checksum;
455
  }
456

    
457
  /**
458
   * Resolve the location of a given object
459
   * 
460
   * @param session - the Session object containing the credentials for the Subject
461
   * @param pid - the object identifier for the given object
462
   * 
463
   * @return objectLocationList - the list of nodes known to contain the object
464
   * 
465
   * @throws InvalidToken
466
   * @throws ServiceFailure
467
   * @throws NotAuthorized
468
   * @throws NotFound
469
   * @throws NotImplemented
470
   */
471
  @Override
472
  public ObjectLocationList resolve(Session session, Identifier pid)
473
    throws InvalidToken, ServiceFailure, NotAuthorized,
474
    NotFound, NotImplemented {
475

    
476
    throw new NotImplemented("4131", "resolve not implemented");
477

    
478
  }
479

    
480
  /**
481
   * Search the metadata catalog for identifiers that match the criteria
482
   * 
483
   * @param session - the Session object containing the credentials for the Subject
484
   * @param queryType - An identifier for the type of query expression 
485
   *                    provided in the query
486
   * @param query -  The criteria for matching the characteristics of the 
487
   *                 metadata objects of interest
488
   * 
489
   * @return objectList - the list of objects matching the criteria
490
   * 
491
   * @throws InvalidToken
492
   * @throws ServiceFailure
493
   * @throws NotAuthorized
494
   * @throws InvalidRequest
495
   * @throws NotImplemented
496
   */
497
  @Override
498
  public ObjectList search(Session session, String queryType, String query)
499
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
500
    NotImplemented {
501

    
502
    ObjectList objectList = null;
503
    try {
504
        objectList = 
505
          IdentifierManager.getInstance().querySystemMetadata(
506
              null, //startTime, 
507
              null, //endTime,
508
              null, //objectFormat, 
509
              false, //replicaStatus, 
510
              0, //start, 
511
              -1 //count
512
              );
513
        
514
    } catch (Exception e) {
515
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
516
    }
517

    
518
      return objectList;
519
      
520
    //throw new NotImplemented("4281", "search not implemented");
521
    
522
    // the code block below is from an older implementation
523
    
524
    /*  This block commented out because of the EcoGrid circular dependency.
525
         *  For now, query will not be supported until the circularity can be
526
         *  resolved, probably by moving the ecogrid query syntax transformers
527
         *  directly into the Metacat codebase.  MBJ 2010-02-03
528
         
529
        try {
530
            EcogridQueryParser parser = new EcogridQueryParser(request
531
                    .getReader());
532
            parser.parseXML();
533
            QueryType queryType = parser.getEcogridQuery();
534
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
535
                new EcogridJavaToMetacatJavaQueryTransformer();
536
            QuerySpecification metacatQuery = queryTransformer
537
                    .transform(queryType);
538

    
539
            DBQuery metacat = new DBQuery();
540

    
541
            boolean useXMLIndex = (new Boolean(PropertyService
542
                    .getProperty("database.usexmlindex"))).booleanValue();
543
            String xmlquery = "query"; // we don't care the query in resultset,
544
            // the query can be anything
545
            PrintWriter out = null; // we don't want metacat result, so set out null
546

    
547
            // parameter: queryspecification, user, group, usingIndexOrNot
548
            StringBuffer result = metacat.createResultDocument(xmlquery,
549
                    metacatQuery, out, username, groupNames, useXMLIndex);
550

    
551
            // create result set transfer       
552
            String saxparser = PropertyService.getProperty("xml.saxparser");
553
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
554
                    new StringReader(result.toString()), saxparser, queryType
555
                            .getNamespace().get_value());
556
            ResultsetType records = metacatResultsetParser.getEcogridResult();
557

    
558
            System.out
559
                    .println(EcogridResultsetTransformer.toXMLString(records));
560
            response.setContentType("text/xml");
561
            out = response.getWriter();
562
            out.print(EcogridResultsetTransformer.toXMLString(records));
563

    
564
        } catch (Exception e) {
565
            e.printStackTrace();
566
        }*/
567
    
568

    
569
  }
570
  
571
  /**
572
   * Returns the object format registered in the DataONE Object Format 
573
   * Vocabulary for the given format identifier
574
   * 
575
   * @param fmtid - the identifier of the format requested
576
   * 
577
   * @return objectFormat - the object format requested
578
   * 
579
   * @throws ServiceFailure
580
   * @throws NotFound
581
   * @throws InsufficientResources
582
   * @throws NotImplemented
583
   */
584
  @Override
585
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
586
    throws ServiceFailure, NotFound, NotImplemented {
587
     
588
      return ObjectFormatService.getInstance().getFormat(fmtid);
589
      
590
  }
591

    
592
  /**
593
   * Returns a list of all object formats registered in the DataONE Object 
594
   * Format Vocabulary
595
    * 
596
   * @return objectFormatList - The list of object formats registered in 
597
   *                            the DataONE Object Format Vocabulary
598
   * 
599
   * @throws ServiceFailure
600
   * @throws NotImplemented
601
   * @throws InsufficientResources
602
   */
603
  @Override
604
  public ObjectFormatList listFormats() 
605
    throws ServiceFailure, NotImplemented {
606

    
607
    return ObjectFormatService.getInstance().listFormats();
608
  }
609

    
610
  /**
611
   * Returns a list of nodes that have been registered with the DataONE infrastructure
612
    * 
613
   * @return nodeList - List of nodes from the registry
614
   * 
615
   * @throws ServiceFailure
616
   * @throws NotImplemented
617
   */
618
  @Override
619
  public NodeList listNodes() 
620
    throws NotImplemented, ServiceFailure {
621

    
622
    throw new NotImplemented("4800", "listNodes not implemented");
623
  }
624

    
625
  /**
626
   * Provides a mechanism for adding system metadata independently of its 
627
   * associated object, such as when adding system metadata for data objects.
628
    * 
629
   * @param session - the Session object containing the credentials for the Subject
630
   * @param pid - The identifier of the object to register the system metadata against
631
   * @param sysmeta - The system metadata to be registered
632
   * 
633
   * @return true if the registration succeeds
634
   * 
635
   * @throws NotImplemented
636
   * @throws NotAuthorized
637
   * @throws ServiceFailure
638
   * @throws InvalidRequest
639
   * @throws InvalidSystemMetadata
640
   */
641
  @Override
642
  public Identifier registerSystemMetadata(Session session, Identifier pid,
643
      SystemMetadata sysmeta) 
644
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
645
      InvalidSystemMetadata {
646

    
647
      // The lock to be used for this identifier
648
      Lock lock = null;
649

    
650
      // TODO: control who can call this?
651
      if (session == null) {
652
          //TODO: many of the thrown exceptions do not use the correct error codes
653
          //check these against the docs and correct them
654
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
655
                  "  If you are not logged in, please do so and retry the request.");
656
      }
657
      
658
      // verify that guid == SystemMetadata.getIdentifier()
659
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
660
          "|" + sysmeta.getIdentifier().getValue());
661
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
662
          throw new InvalidRequest("4863", 
663
              "The identifier in method call (" + pid.getValue() + 
664
              ") does not match identifier in system metadata (" +
665
              sysmeta.getIdentifier().getValue() + ").");
666
      }
667

    
668
      try {
669
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
670
          lock.lock();
671
          logMetacat.debug("Locked identifier " + pid.getValue());
672
          logMetacat.debug("Checking if identifier exists...");
673
          // Check that the identifier does not already exist
674
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
675
              throw new InvalidRequest("4863", 
676
                  "The identifier is already in use by an existing object.");
677
          
678
          }
679
          
680
          // insert the system metadata into the object store
681
          logMetacat.debug("Starting to insert SystemMetadata...");
682
          try {
683
              sysmeta.setSerialVersion(BigInteger.ONE);
684
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
685
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
686
              
687
          } catch (RuntimeException e) {
688
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
689
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
690
                  e.getClass() + ": " + e.getMessage());
691
              
692
          }
693
          
694
      } catch (RuntimeException e) {
695
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
696
                  e.getClass() + ": " + e.getMessage());
697
          
698
      }  finally {
699
          lock.unlock();
700
          logMetacat.debug("Unlocked identifier " + pid.getValue());
701
          
702
      }
703

    
704
      
705
      logMetacat.debug("Returning from registerSystemMetadata");
706
      EventLog.getInstance().log(request.getRemoteAddr(), 
707
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
708
          pid.getValue(), "registerSystemMetadata");
709
      return pid;
710
  }
711
  
712
  /**
713
   * Given an optional scope and format, reserves and returns an identifier 
714
   * within that scope and format that is unique and will not be 
715
   * used by any other sessions. 
716
    * 
717
   * @param session - the Session object containing the credentials for the Subject
718
   * @param pid - The identifier of the object to register the system metadata against
719
   * @param scope - An optional string to be used to qualify the scope of 
720
   *                the identifier namespace, which is applied differently 
721
   *                depending on the format requested. If scope is not 
722
   *                supplied, a default scope will be used.
723
   * @param format - The optional name of the identifier format to be used, 
724
   *                  drawn from a DataONE-specific vocabulary of identifier 
725
   *                 format names, including several common syntaxes such 
726
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
727
   *                 format is not supplied by the caller, the CN service 
728
   *                 will use a default identifier format, which may change 
729
   *                 over time.
730
   * 
731
   * @return true if the registration succeeds
732
   * 
733
   * @throws InvalidToken
734
   * @throws ServiceFailure
735
   * @throws NotAuthorized
736
   * @throws IdentifierNotUnique
737
   * @throws NotImplemented
738
   */
739
  @Override
740
  public Identifier reserveIdentifier(Session session, Identifier pid)
741
  throws InvalidToken, ServiceFailure,
742
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
743

    
744
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
745
  }
746
  
747
  @Override
748
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
749
  throws InvalidToken, ServiceFailure,
750
        NotAuthorized, NotImplemented, InvalidRequest {
751
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
752
  }
753
  
754
  /**
755
    * Checks whether the pid is reserved by the subject in the session param
756
    * If the reservation is held on the pid by the subject, we return true.
757
    * 
758
   * @param session - the Session object containing the Subject
759
   * @param pid - The identifier to check
760
   * 
761
   * @return true if the reservation exists for the subject/pid
762
   * 
763
   * @throws InvalidToken
764
   * @throws ServiceFailure
765
   * @throws NotFound - when the pid is not found (in use or in reservation)
766
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
767
   * @throws IdentifierNotUnique - when the pid is in use
768
   * @throws NotImplemented
769
   */
770

    
771
  @Override
772
  public boolean hasReservation(Session session, Identifier pid) 
773
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
774
      NotImplemented, InvalidRequest {
775
  
776
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
777
  }
778

    
779
  /**
780
   * Changes ownership (RightsHolder) of the specified object to the 
781
   * subject specified by userId
782
    * 
783
   * @param session - the Session object containing the credentials for the Subject
784
   * @param pid - Identifier of the object to be modified
785
   * @param userId - The subject that will be taking ownership of the specified object.
786
   *
787
   * @return pid - the identifier of the modified object
788
   * 
789
   * @throws ServiceFailure
790
   * @throws InvalidToken
791
   * @throws NotFound
792
   * @throws NotAuthorized
793
   * @throws NotImplemented
794
   * @throws InvalidRequest
795
   */  
796
  @Override
797
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
798
      long serialVersion)
799
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
800
      NotImplemented, InvalidRequest, VersionMismatch {
801
      
802
      // The lock to be used for this identifier
803
      Lock lock = null;
804

    
805
      // get the subject
806
      Subject subject = session.getSubject();
807
      
808
      // are we allowed to do this?
809
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
810
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
811
              throw new NotAuthorized("4440", "not allowed by "
812
                      + subject.getValue() + " on " + pid.getValue());
813
              
814
          }
815
      }
816
      
817
      SystemMetadata systemMetadata = null;
818
      try {
819
          lock = HazelcastService.getInstance().getLock(pid.getValue());
820
          logMetacat.debug("Locked identifier " + pid.getValue());
821

    
822
          try {
823
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
824
              
825
              // does the request have the most current system metadata?
826
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
827
                 String msg = "The requested system metadata version number " + 
828
                     serialVersion + " differs from the current version at " +
829
                     systemMetadata.getSerialVersion().longValue() +
830
                     ". Please get the latest copy in order to modify it.";
831
                 throw new VersionMismatch("4443", msg);
832
              }
833
              
834
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
835
              throw new NotFound("4460", "No record found for: " + pid.getValue());
836
              
837
          }
838
              
839
          // set the new rights holder
840
          systemMetadata.setRightsHolder(userId);
841
          
842
          // update the metadata
843
          try {
844
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
845
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
846
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
847
              
848
          } catch (RuntimeException e) {
849
              throw new ServiceFailure("4490", e.getMessage());
850
          
851
          }
852
          
853
      } catch (RuntimeException e) {
854
          throw new ServiceFailure("4490", e.getMessage());
855
          
856
      } finally {
857
          lock.unlock();
858
          logMetacat.debug("Unlocked identifier " + pid.getValue());
859
      
860
      }
861
      
862
      return pid;
863
  }
864

    
865
  /**
866
   * Verify that a replication task is authorized by comparing the target node's
867
   * Subject (from the X.509 certificate-derived Session) with the list of 
868
   * subjects in the known, pending replication tasks map.
869
   * 
870
   * @param originatingNodeSession - Session information that contains the 
871
   *                                 identity of the calling user
872
   * @param targetNodeSubject - Subject identifying the target node
873
   * @param pid - the identifier of the object to be replicated
874
   * @param replicatePermission - the execute permission to be granted
875
   * 
876
   * @throws ServiceFailure
877
   * @throws NotImplemented
878
   * @throws InvalidToken
879
   * @throws NotAuthorized
880
   * @throws InvalidRequest
881
   * @throws NotFound
882
   */
883
  @Override
884
  public boolean isNodeAuthorized(Session originatingNodeSession, 
885
    Subject targetNodeSubject, Identifier pid) 
886
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
887
    NotFound, InvalidRequest {
888
    
889
    boolean isAllowed = false;
890
    SystemMetadata sysmeta = null;
891
    NodeReference targetNode = null;
892
    
893
    try {
894
      // get the target node reference from the nodes list
895
      CNode cn = D1Client.getCN();
896
      List<Node> nodes = cn.listNodes().getNodeList();
897
      
898
      if ( nodes != null ) {
899
        for (Node node : nodes) {
900
            
901
            for (Subject nodeSubject : node.getSubjectList()) {
902
                
903
                if ( nodeSubject.equals(targetNodeSubject) ) {
904
                    targetNode = node.getIdentifier();
905
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
906
                    break;
907
                }
908
            }
909
            
910
            if ( targetNode != null) { break; }
911
        }
912
        
913
      } else {
914
          String msg = "Couldn't get the node list from the CN";
915
          logMetacat.debug(msg);
916
          throw new ServiceFailure("4872", msg);
917
          
918
      }
919
      
920
      // can't find a node listed with the given subject
921
      if ( targetNode == null ) {
922
          String msg = "There is no Member Node registered with a node subject " +
923
              "matching " + targetNodeSubject.getValue();
924
          logMetacat.info(msg);
925
          throw new ServiceFailure("4872", msg);
926
          
927
      }
928
      
929
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
930
      
931
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
932

    
933
      if ( sysmeta != null ) {
934
          
935
          List<Replica> replicaList = sysmeta.getReplicaList();
936
          
937
          if ( replicaList != null ) {
938
              
939
              // find the replica with the status set to 'requested'
940
              for (Replica replica : replicaList) {
941
                  ReplicationStatus status = replica.getReplicationStatus();
942
                  NodeReference listedNode = replica.getReplicaMemberNode();
943
                  if ( listedNode != null && targetNode != null ) {
944
                      logMetacat.debug("Comparing " + listedNode.getValue()
945
                              + " to " + targetNode.getValue());
946
                      
947
                      if (listedNode.getValue().equals(targetNode.getValue())
948
                              && status.equals(ReplicationStatus.REQUESTED)) {
949
                          isAllowed = true;
950
                          break;
951

    
952
                      }
953
                  }
954
              }
955
          }
956
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
957
              "to replicate: " + isAllowed + " for " + pid.getValue());
958

    
959
          
960
      } else {
961
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
962
          " is null.");          
963
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
964
          
965
      }
966

    
967
    } catch (RuntimeException e) {
968
    	  ServiceFailure sf = new ServiceFailure("4872", 
969
                "Runtime Exception: Couldn't determine if node is allowed: " + 
970
                e.getCause().getMessage());
971
    	  sf.initCause(e);
972
        throw sf;
973
        
974
    }
975
      
976
    return isAllowed;
977
    
978
  }
979

    
980
  /**
981
   * Adds a new object to the Node, where the object is a science metadata object.
982
   * 
983
   * @param session - the Session object containing the credentials for the Subject
984
   * @param pid - The object identifier to be created
985
   * @param object - the object bytes
986
   * @param sysmeta - the system metadata that describes the object  
987
   * 
988
   * @return pid - the object identifier created
989
   * 
990
   * @throws InvalidToken
991
   * @throws ServiceFailure
992
   * @throws NotAuthorized
993
   * @throws IdentifierNotUnique
994
   * @throws UnsupportedType
995
   * @throws InsufficientResources
996
   * @throws InvalidSystemMetadata
997
   * @throws NotImplemented
998
   * @throws InvalidRequest
999
   */
1000
  public Identifier create(Session session, Identifier pid, InputStream object,
1001
    SystemMetadata sysmeta) 
1002
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1003
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1004
    NotImplemented, InvalidRequest {
1005
      
1006
      
1007
      // The lock to be used for this identifier
1008
      Lock lock = null;
1009
      
1010
      try {
1011
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1012
          // are we allowed?
1013
          boolean isAllowed = false;
1014
          CNode cn = D1Client.getCN();
1015
          NodeList nodeList = cn.listNodes();
1016
          
1017
          for (Node node : nodeList.getNodeList()) {
1018
              if ( node.getType().equals(NodeType.CN) ) {
1019
                  
1020
                  List<Subject> subjects = node.getSubjectList();
1021
                  for (Subject subject : subjects) {
1022
                     if (subject.equals(session.getSubject())) {
1023
                         isAllowed = true;
1024
                         break;
1025
                     }
1026
                  }
1027
              }
1028
          }
1029

    
1030
          // proceed if we're called by a CN
1031
          if ( isAllowed ) {
1032
              // create the coordinating node version of the document      
1033
              lock.lock();
1034
              logMetacat.debug("Locked identifier " + pid.getValue());
1035
              sysmeta.setSerialVersion(BigInteger.ONE);
1036
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1037
              pid = super.create(session, pid, object, sysmeta);
1038

    
1039
          } else {
1040
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1041
                  " isn't allowed to call create() on a Coordinating Node.";
1042
              logMetacat.info(msg);
1043
              throw new NotAuthorized("1100", msg);
1044
          }
1045
          
1046
      } catch (RuntimeException e) {
1047
          // Convert Hazelcast runtime exceptions to service failures
1048
          String msg = "There was a problem creating the object identified by " +
1049
              pid.getValue() + ". There error message was: " + e.getMessage();
1050
          throw new ServiceFailure("4893", msg);
1051
          
1052
      } finally {
1053
    	  if (lock != null) {
1054
	          lock.unlock();
1055
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1056
    	  }
1057
      }
1058
      
1059
      return pid;
1060

    
1061
  }
1062

    
1063
  /**
1064
   * Set access for a given object using the object identifier and a Subject
1065
   * under a given Session.
1066
   * 
1067
   * @param session - the Session object containing the credentials for the Subject
1068
   * @param pid - the object identifier for the given object to apply the policy
1069
   * @param policy - the access policy to be applied
1070
   * 
1071
   * @return true if the application of the policy succeeds
1072
   * @throws InvalidToken
1073
   * @throws ServiceFailure
1074
   * @throws NotFound
1075
   * @throws NotAuthorized
1076
   * @throws NotImplemented
1077
   * @throws InvalidRequest
1078
   */
1079
  public boolean setAccessPolicy(Session session, Identifier pid, 
1080
      AccessPolicy accessPolicy, long serialVersion) 
1081
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1082
      NotImplemented, InvalidRequest, VersionMismatch {
1083
      
1084
      // The lock to be used for this identifier
1085
      Lock lock = null;
1086
      SystemMetadata systemMetadata = null;
1087
      
1088
      boolean success = false;
1089
      
1090
      // get the subject
1091
      Subject subject = session.getSubject();
1092
      
1093
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1094
          // are we allowed to do this?
1095
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1096
              throw new NotAuthorized("4420", "not allowed by "
1097
                      + subject.getValue() + " on " + pid.getValue());
1098
          }
1099
      }
1100
      
1101
      try {
1102
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1103
          lock.lock();
1104
          logMetacat.debug("Locked identifier " + pid.getValue());
1105

    
1106
          try {
1107
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1108

    
1109
              if ( systemMetadata == null ) {
1110
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1111
                  
1112
              }
1113
              // does the request have the most current system metadata?
1114
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1115
                 String msg = "The requested system metadata version number " + 
1116
                     serialVersion + " differs from the current version at " +
1117
                     systemMetadata.getSerialVersion().longValue() +
1118
                     ". Please get the latest copy in order to modify it.";
1119
                 throw new VersionMismatch("4402", msg);
1120
                 
1121
              }
1122
              
1123
          } catch (RuntimeException e) {
1124
              // convert Hazelcast RuntimeException to NotFound
1125
              throw new NotFound("4400", "No record found for: " + pid);
1126
            
1127
          }
1128
              
1129
          // set the access policy
1130
          systemMetadata.setAccessPolicy(accessPolicy);
1131
          
1132
          // update the system metadata
1133
          try {
1134
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1135
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1136
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1137
            
1138
          } catch (RuntimeException e) {
1139
              // convert Hazelcast RuntimeException to ServiceFailure
1140
              throw new ServiceFailure("4430", e.getMessage());
1141
            
1142
          }
1143
          
1144
      } catch (RuntimeException e) {
1145
          throw new ServiceFailure("4430", e.getMessage());
1146
          
1147
      } finally {
1148
          lock.unlock();
1149
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1150
        
1151
      }
1152

    
1153
    
1154
    // TODO: how do we know if the map was persisted?
1155
    success = true;
1156
    
1157
    return success;
1158
  }
1159

    
1160
  /**
1161
   * Full replacement of replication metadata in the system metadata for the 
1162
   * specified object, changes date system metadata modified
1163
   * 
1164
   * @param session - the Session object containing the credentials for the Subject
1165
   * @param pid - the object identifier for the given object to apply the policy
1166
   * @param replica - the replica to be updated
1167
   * @return
1168
   * @throws NotImplemented
1169
   * @throws NotAuthorized
1170
   * @throws ServiceFailure
1171
   * @throws InvalidRequest
1172
   * @throws NotFound
1173
   * @throws VersionMismatch
1174
   */
1175
  @Override
1176
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1177
      Replica replica, long serialVersion) 
1178
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1179
      NotFound, VersionMismatch {
1180
      
1181
      // The lock to be used for this identifier
1182
      Lock lock = null;
1183
      
1184
      // get the subject
1185
      Subject subject = session.getSubject();
1186
      
1187
      // are we allowed to do this?
1188
      try {
1189
        if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
1190
            // what is the controlling permission?
1191
            if (!isAuthorized(session, pid, Permission.WRITE)) {
1192
                throw new NotAuthorized("4851", "not allowed by "
1193
                        + subject.getValue() + " on " + pid.getValue());
1194
            }
1195
        }
1196
        
1197
      } catch (InvalidToken e) {
1198
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1199
                  " on " + pid.getValue());  
1200
          
1201
      }
1202

    
1203
      SystemMetadata systemMetadata = null;
1204
      try {
1205
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1206
          lock.lock();
1207
          logMetacat.debug("Locked identifier " + pid.getValue());
1208

    
1209
          try {      
1210
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1211

    
1212
              // does the request have the most current system metadata?
1213
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1214
                 String msg = "The requested system metadata version number " + 
1215
                     serialVersion + " differs from the current version at " +
1216
                     systemMetadata.getSerialVersion().longValue() +
1217
                     ". Please get the latest copy in order to modify it.";
1218
                 throw new VersionMismatch("4855", msg);
1219
              }
1220
              
1221
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1222
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1223
                  " : " + e.getMessage());
1224
            
1225
          }
1226
              
1227
          // set the status for the replica
1228
          List<Replica> replicas = systemMetadata.getReplicaList();
1229
          NodeReference replicaNode = replica.getReplicaMemberNode();
1230
          int index = 0;
1231
          for (Replica listedReplica: replicas) {
1232
              
1233
              // remove the replica that we are replacing
1234
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1235
                  replicas.remove(index);
1236
                  break;
1237
                  
1238
              }
1239
              index++;
1240
          }
1241
          
1242
          // add the new replica item
1243
          replicas.add(replica);
1244
          systemMetadata.setReplicaList(replicas);
1245
          
1246
          // update the metadata
1247
          try {
1248
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1249
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1250
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1251
            
1252
          } catch (RuntimeException e) {
1253
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1254
              throw new ServiceFailure("4852", e.getMessage());
1255
          
1256
          }
1257
          
1258
      } catch (RuntimeException e) {
1259
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1260
          throw new ServiceFailure("4852", e.getMessage());
1261
      
1262
      } finally {
1263
          lock.unlock();
1264
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1265
          
1266
      }
1267
    
1268
      return true;
1269
      
1270
  }
1271
  
1272
  /**
1273
   * 
1274
   */
1275
  @Override
1276
  public ObjectList listObjects(Session session, Date startTime, 
1277
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1278
      Integer start, Integer count)
1279
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1280
      ServiceFailure {
1281
      
1282
      ObjectList objectList = null;
1283
        try {
1284
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1285
        } catch (Exception e) {
1286
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1287
        }
1288

    
1289
        return objectList;
1290
  }
1291

    
1292
	/**
1293
	 * 
1294
	 */
1295
  @Override
1296
	public ChecksumAlgorithmList listChecksumAlgorithms()
1297
			throws ServiceFailure, NotImplemented {
1298
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1299
		cal.addAlgorithm("MD5");
1300
		cal.addAlgorithm("SHA-1");
1301
		return null;
1302
	}
1303
    
1304
}
(1-1/5)