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

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

    
74
import com.hazelcast.core.ILock;
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
   * 
123
   */
124
  @Override
125
  public boolean setReplicationPolicy(Session session, Identifier pid,
126
      ReplicationPolicy policy, long serialVersion) 
127
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
128
      InvalidRequest, InvalidToken {
129
      
130
      // The lock to be used for this identifier
131
      ILock lock = null;
132
      
133
      // get the subject
134
      Subject subject = session.getSubject();
135
      
136
      // are we allowed to do this?
137
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
138
        throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + 
139
            " not allowed by " + subject.getValue() + " on " + pid.getValue());  
140
      }
141
      
142
      SystemMetadata systemMetadata = null;
143
      try {
144
          lock = HazelcastService.getInstance().getLock(pid.getValue());
145
          lock.lock();
146

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

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

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

    
230
      try {
231
          lock = HazelcastService.getInstance().getLock(pid.getValue());
232
          lock.lock();
233
          try {      
234
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
235

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

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

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

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

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

    
352
  /**
353
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
354
   * 
355
   * @param session - the Session object containing the credentials for the Subject
356
   * @param node - the node information for the given node be modified
357
   * 
358
   * @return true if the relationship exists
359
   * 
360
   * @throws InvalidToken
361
   * @throws ServiceFailure
362
   * @throws NotAuthorized
363
   * @throws NotFound
364
   * @throws InvalidRequest
365
   * @throws NotImplemented
366
   */
367
  @Override
368
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
369
    String relationship, Identifier pidOfObject) 
370
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
371
    InvalidRequest, NotImplemented {
372
    
373
    // The lock to be used for thyis identifier
374
    ILock lock = null;
375

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

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

    
468
    } catch (Exception e) {
469
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
470
            pid.getValue() + ". The error message was: " + e.getMessage());
471
      
472
    }
473
    
474
    return checksum;
475
  }
476

    
477
  /**
478
   * Resolve the location of a given object
479
   * 
480
   * @param session - the Session object containing the credentials for the Subject
481
   * @param pid - the object identifier for the given object
482
   * 
483
   * @return objectLocationList - the list of nodes known to contain the object
484
   * 
485
   * @throws InvalidToken
486
   * @throws ServiceFailure
487
   * @throws NotAuthorized
488
   * @throws NotFound
489
   * @throws NotImplemented
490
   */
491
  @Override
492
  public ObjectLocationList resolve(Session session, Identifier pid)
493
    throws InvalidToken, ServiceFailure, NotAuthorized,
494
    NotFound, NotImplemented {
495

    
496
    throw new NotImplemented("4131", "resolve not implemented");
497

    
498
  }
499

    
500
  /**
501
   * Search the metadata catalog for identifiers that match the criteria
502
   * 
503
   * @param session - the Session object containing the credentials for the Subject
504
   * @param queryType - An identifier for the type of query expression 
505
   *                    provided in the query
506
   * @param query -  The criteria for matching the characteristics of the 
507
   *                 metadata objects of interest
508
   * 
509
   * @return objectList - the list of objects matching the criteria
510
   * 
511
   * @throws InvalidToken
512
   * @throws ServiceFailure
513
   * @throws NotAuthorized
514
   * @throws InvalidRequest
515
   * @throws NotImplemented
516
   */
517
  @Override
518
  public ObjectList search(Session session, String queryType, String query)
519
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
520
    NotImplemented {
521

    
522
    ObjectList objectList = null;
523
    try {
524
        objectList = 
525
          IdentifierManager.getInstance().querySystemMetadata(
526
              null, //startTime, 
527
              null, //endTime,
528
              null, //objectFormat, 
529
              false, //replicaStatus, 
530
              0, //start, 
531
              -1 //count
532
              );
533
        
534
    } catch (Exception e) {
535
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
536
    }
537

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

    
559
            DBQuery metacat = new DBQuery();
560

    
561
            boolean useXMLIndex = (new Boolean(PropertyService
562
                    .getProperty("database.usexmlindex"))).booleanValue();
563
            String xmlquery = "query"; // we don't care the query in resultset,
564
            // the query can be anything
565
            PrintWriter out = null; // we don't want metacat result, so set out null
566

    
567
            // parameter: queryspecification, user, group, usingIndexOrNot
568
            StringBuffer result = metacat.createResultDocument(xmlquery,
569
                    metacatQuery, out, username, groupNames, useXMLIndex);
570

    
571
            // create result set transfer       
572
            String saxparser = PropertyService.getProperty("xml.saxparser");
573
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
574
                    new StringReader(result.toString()), saxparser, queryType
575
                            .getNamespace().get_value());
576
            ResultsetType records = metacatResultsetParser.getEcogridResult();
577

    
578
            System.out
579
                    .println(EcogridResultsetTransformer.toXMLString(records));
580
            response.setContentType("text/xml");
581
            out = response.getWriter();
582
            out.print(EcogridResultsetTransformer.toXMLString(records));
583

    
584
        } catch (Exception e) {
585
            e.printStackTrace();
586
        }*/
587
    
588

    
589
  }
590
  
591
  /**
592
   * Returns the object format registered in the DataONE Object Format 
593
   * Vocabulary for the given format identifier
594
   * 
595
   * @param fmtid - the identifier of the format requested
596
   * 
597
   * @return objectFormat - the object format requested
598
   * 
599
   * @throws ServiceFailure
600
   * @throws NotFound
601
   * @throws InsufficientResources
602
   * @throws NotImplemented
603
   */
604
  @Override
605
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
606
    throws ServiceFailure, NotFound, NotImplemented {
607
     
608
      return ObjectFormatService.getInstance().getFormat(fmtid);
609
      
610
  }
611

    
612
  /**
613
   * Returns a list of all object formats registered in the DataONE Object 
614
   * Format Vocabulary
615
    * 
616
   * @return objectFormatList - The list of object formats registered in 
617
   *                            the DataONE Object Format Vocabulary
618
   * 
619
   * @throws ServiceFailure
620
   * @throws NotImplemented
621
   * @throws InsufficientResources
622
   */
623
  @Override
624
  public ObjectFormatList listFormats() 
625
    throws ServiceFailure, NotImplemented {
626

    
627
    return ObjectFormatService.getInstance().listFormats();
628
  }
629

    
630
  /**
631
   * Returns a list of nodes that have been registered with the DataONE infrastructure
632
    * 
633
   * @return nodeList - List of nodes from the registry
634
   * 
635
   * @throws ServiceFailure
636
   * @throws NotImplemented
637
   */
638
  @Override
639
  public NodeList listNodes() 
640
    throws NotImplemented, ServiceFailure {
641

    
642
    throw new NotImplemented("4800", "listNodes not implemented");
643
  }
644

    
645
  /**
646
   * Provides a mechanism for adding system metadata independently of its 
647
   * associated object, such as when adding system metadata for data objects.
648
    * 
649
   * @param session - the Session object containing the credentials for the Subject
650
   * @param pid - The identifier of the object to register the system metadata against
651
   * @param sysmeta - The system metadata to be registered
652
   * 
653
   * @return true if the registration succeeds
654
   * 
655
   * @throws NotImplemented
656
   * @throws NotAuthorized
657
   * @throws ServiceFailure
658
   * @throws InvalidRequest
659
   * @throws InvalidSystemMetadata
660
   */
661
  @Override
662
  public Identifier registerSystemMetadata(Session session, Identifier pid,
663
      SystemMetadata sysmeta) 
664
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
665
      InvalidSystemMetadata {
666

    
667
      // The lock to be used for this identifier
668
      ILock lock = null;
669

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

    
688
      logMetacat.debug("Checking if identifier exists...");
689
      // Check that the identifier does not already exist
690
      if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
691
          throw new InvalidRequest("4863", 
692
              "The identifier is already in use by an existing object.");
693
      
694
      }
695
      
696
      // insert the system metadata into the object store
697
      logMetacat.debug("Starting to insert SystemMetadata...");
698
      try {
699
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
700
          sysmeta.setSerialVersion(BigInteger.ONE);
701
          sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
702
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
703
          
704
      } catch (Exception e) {
705
        logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
706
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
707
              e.getClass() + ": " + e.getMessage());
708
          
709
      } finally {
710
        lock.unlock();
711
        logMetacat.debug("Unlocked identifier " + pid.getValue());
712

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

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

    
781
  @Override
782
  public boolean hasReservation(Session session, Identifier pid) 
783
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
784
      NotImplemented, InvalidRequest {
785
  
786
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
787
  }
788

    
789
  /**
790
   * Changes ownership (RightsHolder) of the specified object to the 
791
   * subject specified by userId
792
    * 
793
   * @param session - the Session object containing the credentials for the Subject
794
   * @param pid - Identifier of the object to be modified
795
   * @param userId - The subject that will be taking ownership of the specified object.
796
   *
797
   * @return pid - the identifier of the modified object
798
   * 
799
   * @throws ServiceFailure
800
   * @throws InvalidToken
801
   * @throws NotFound
802
   * @throws NotAuthorized
803
   * @throws NotImplemented
804
   * @throws InvalidRequest
805
   */  
806
  @Override
807
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
808
      long serialVersion)
809
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
810
      NotImplemented, InvalidRequest {
811
      
812
      // The lock to be used for this identifier
813
      ILock lock = null;
814

    
815
      // get the subject
816
      Subject subject = session.getSubject();
817
      
818
      // are we allowed to do this?
819
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
820
        throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + pid.getValue());  
821
      }
822
      
823
      SystemMetadata systemMetadata = null;
824
      try {
825
          lock = HazelcastService.getInstance().getLock(pid.getValue());
826
          
827
          try {
828
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
829
              
830
              // does the request have the most current system metadata?
831
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
832
                 String msg = "The requested system metadata version number " + 
833
                     serialVersion + " differs from the current version at " +
834
                     systemMetadata.getSerialVersion().longValue() +
835
                     ". Please get the latest copy in order to modify it.";
836
                 throw new InvalidRequest("4442", msg);
837
              }
838
              
839
          } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
840
              throw new NotFound("4460", "No record found for: " + pid.getValue());
841
              
842
          }
843
              
844
          // set the new rights holder
845
          systemMetadata.setRightsHolder(userId);
846
          
847
          // update the metadata
848
          try {
849
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
850
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
851
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
852
              
853
          } catch (Exception e) {
854
          throw new ServiceFailure("4490", e.getMessage());
855
          
856
          }
857
          
858
      } catch (Exception e) {
859
          throw new ServiceFailure("4490", e.getMessage());
860
          
861
      } finally {
862
          lock.unlock();
863
          logMetacat.debug("Unlocked identifier " + pid.getValue());
864
      
865
      }
866
      
867
    return pid;
868
  }
869

    
870
  /**
871
   * Verify that a replication task is authorized by comparing the target node's
872
   * Subject (from the X.509 certificate-derived Session) with the list of 
873
   * subjects in the known, pending replication tasks map.
874
   * 
875
   * @param originatingNodeSession - Session information that contains the 
876
   *                                 identity of the calling user
877
   * @param targetNodeSubject - Subject identifying the target node
878
   * @param pid - the identifier of the object to be replicated
879
   * @param replicatePermission - the execute permission to be granted
880
   * 
881
   * @throws ServiceFailure
882
   * @throws NotImplemented
883
   * @throws InvalidToken
884
   * @throws NotAuthorized
885
   * @throws InvalidRequest
886
   * @throws NotFound
887
   */
888
  @Override
889
  public boolean isNodeAuthorized(Session originatingNodeSession, 
890
    Subject targetNodeSubject, Identifier pid) 
891
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
892
    NotFound, InvalidRequest {
893

    
894
    // The lock to be used for this identifier
895
    ILock lock = null;
896
    
897
    boolean isAllowed = false;
898
    SystemMetadata sysmeta = null;
899
    NodeReference targetNode = null;
900
    
901
    try {
902
      // get the target node reference from the nodes list
903
      CNode cn = D1Client.getCN();
904
      List<Node> nodes = cn.listNodes().getNodeList();
905
      
906
      if ( nodes != null ) {
907
        for (Node node : nodes) {
908
            
909
            for (Subject nodeSubject : node.getSubjectList()) {
910
                
911
                if ( nodeSubject.equals(targetNodeSubject) ) {
912
                    targetNode = node.getIdentifier();
913
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
914
                    break;
915
                }
916
            }
917
            
918
            if ( targetNode != null) { break; }
919
        }
920
        
921
      } else {
922
          String msg = "Couldn't get the node list from the CN";
923
          logMetacat.debug(msg);
924
          throw new ServiceFailure("4872", msg);
925
          
926
      }
927
      
928
      // can't find a node listed with the given subject
929
      if ( targetNode == null ) {
930
          String msg = "There is no Member Node registered with a node subject " +
931
              "matching " + targetNodeSubject.getValue();
932
          logMetacat.info(msg);
933
          throw new ServiceFailure("4872", msg);
934
          
935
      }
936
      
937
      //lock, get, and unlock the pid
938
      lock = HazelcastService.getInstance().getLock(pid.getValue());
939
      lock.lock();
940
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
941
      
942
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
943

    
944
      if ( sysmeta != null ) {
945
          
946
          List<Replica> replicaList = sysmeta.getReplicaList();
947
          
948
          if ( replicaList != null ) {
949
              
950
              // find the replica with the status set to 'requested'
951
              for (Replica replica : replicaList) {
952
                  ReplicationStatus status = replica.getReplicationStatus();
953
                  NodeReference listedNode = replica.getReplicaMemberNode();
954
                  if ( listedNode != null && targetNode != null ) {
955
                      logMetacat.debug("Comparing " + listedNode.getValue()
956
                              + " to " + targetNode.getValue());
957
                      
958
                      if (listedNode.getValue().equals(targetNode.getValue())
959
                              && status.equals(ReplicationStatus.REQUESTED)) {
960
                          isAllowed = true;
961
                          break;
962

    
963
                      }
964
                  }
965
              }
966
          }
967
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
968
              "to replicate: " + isAllowed + " for " + pid.getValue());
969

    
970
          
971
      } else {
972
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
973
          " is null.");          
974
          
975
      }
976

    
977
    } catch (RuntimeException e) {
978
    	  ServiceFailure sf = new ServiceFailure("4872", 
979
                "Runtime Exception: Couldn't determine if node is allowed: " + 
980
                e.getCause().getMessage());
981
    	  sf.initCause(e);
982
        throw sf;
983
        
984
    } finally {
985
      // always unlock the pid
986
      lock.unlock();
987
      logMetacat.debug("Unlocked identifier " + pid.getValue());
988

    
989
    }
990
      
991
    return isAllowed;
992
    
993
  }
994

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

    
1044
          // proceed if we're called by a CN
1045
          if ( isAllowed ) {
1046
              // create the coordinating node version of the document      
1047
              lock = HazelcastService.getInstance().getLock(pid.getValue());
1048
              lock.lock();
1049
              sysmeta.setSerialVersion(BigInteger.ONE);
1050
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1051
              pid = super.create(session, pid, object, sysmeta);
1052

    
1053
          } else {
1054
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1055
                  " isn't allowed to call create() on a Coordinating Node.";
1056
              logMetacat.info(msg);
1057
              throw new NotAuthorized("1100", msg);
1058
          }
1059
          
1060
      } catch (RuntimeException e) {
1061
          // Convert Hazelcast runtime exceptions to service failures
1062
          String msg = "There was a problem creating the object identified by " +
1063
              pid.getValue() + ". There error message was: " + e.getMessage();
1064
          throw new ServiceFailure("4893", msg);
1065
          
1066
      } finally {
1067
    	  if (lock != null) {
1068
	          lock.unlock();
1069
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1070
    	  }
1071
      }
1072
      
1073
      return pid;
1074

    
1075
  }
1076

    
1077
  /**
1078
   * Set access for a given object using the object identifier and a Subject
1079
   * under a given Session.
1080
   * 
1081
   * @param session - the Session object containing the credentials for the Subject
1082
   * @param pid - the object identifier for the given object to apply the policy
1083
   * @param policy - the access policy to be applied
1084
   * 
1085
   * @return true if the application of the policy succeeds
1086
   * @throws InvalidToken
1087
   * @throws ServiceFailure
1088
   * @throws NotFound
1089
   * @throws NotAuthorized
1090
   * @throws NotImplemented
1091
   * @throws InvalidRequest
1092
   */
1093
  public boolean setAccessPolicy(Session session, Identifier pid, 
1094
      AccessPolicy accessPolicy, long serialVersion) 
1095
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1096
      NotImplemented, InvalidRequest {
1097
      
1098
      // The lock to be used for this identifier
1099
      ILock lock = null;
1100
      
1101
      boolean success = false;
1102
      
1103
      // get the subject
1104
      Subject subject = session.getSubject();
1105
      
1106
      // are we allowed to do this?
1107
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1108
          throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + 
1109
          " on " + pid.getValue());  
1110
      }
1111
      
1112
      SystemMetadata systemMetadata = null;
1113
      try {
1114
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1115
          lock.lock();
1116
          
1117
          try {
1118
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1119

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

    
1159
    
1160
    // TODO: how do we know if the map was persisted?
1161
    success = true;
1162
    
1163
    return success;
1164
  }
1165

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

    
1205
      SystemMetadata systemMetadata = null;
1206
      try {
1207
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1208
          lock.lock();
1209

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

    
1213
              // does the request have the most current system metadata?
1214
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1215
                 String msg = "The requested system metadata version number " + 
1216
                     serialVersion + " differs from the current version at " +
1217
                     systemMetadata.getSerialVersion().longValue() +
1218
                     ". Please get the latest copy in order to modify it.";
1219
                 throw new InvalidRequest("4853", msg);
1220
              }
1221
              
1222
          } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1223
            throw new NotFound("4854", "No record found for: " + pid.getValue() +
1224
                " : " + e.getMessage());
1225
            
1226
          }
1227
              
1228
          // set the status for the replica
1229
          List<Replica> replicas = systemMetadata.getReplicaList();
1230
          NodeReference replicaNode = replica.getReplicaMemberNode();
1231
          int index = 0;
1232
          for (Replica listedReplica: replicas) {
1233
              
1234
              // remove the replica that we are replacing
1235
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1236
                  replicas.remove(index);
1237
                  break;
1238
                  
1239
              }
1240
              index++;
1241
          }
1242
          
1243
          // add the new replica item
1244
          replicas.add(replica);
1245
          systemMetadata.setReplicaList(replicas);
1246
          
1247
          // update the metadata
1248
          try {
1249
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1250
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1251
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1252
            
1253
          } catch (Exception e) {
1254
              throw new ServiceFailure("4852", e.getMessage());
1255
          
1256
          }
1257
          
1258
    } catch (Exception e) {
1259
        throw new ServiceFailure("4852", e.getMessage());
1260

    
1261
    } finally {
1262
        lock.unlock();
1263
        logMetacat.debug("Unlocked identifier " + pid.getValue());
1264
        
1265
    }
1266
    
1267
      return true;
1268
      
1269
  }
1270
  
1271
    @Override
1272
  public ObjectList listObjects(Session session, Date startTime, 
1273
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1274
      Integer start, Integer count)
1275
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1276
      ServiceFailure {
1277
      
1278
      ObjectList objectList = null;
1279
        try {
1280
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1281
        } catch (Exception e) {
1282
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1283
        }
1284

    
1285
        return objectList;
1286
  }
1287

    
1288
	@Override
1289
	public ChecksumAlgorithmList listChecksumAlgorithms()
1290
			throws ServiceFailure, NotImplemented {
1291
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1292
		cal.addAlgorithm("MD5");
1293
		cal.addAlgorithm("SHA-1");
1294
		return null;
1295
	}
1296
    
1297
}
(1-1/5)