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

    
72
import com.hazelcast.core.ILock;
73

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

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

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

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

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

    
180
          
181
      }
182
    
183
      return true;
184
  }
185

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

    
226
          if ( systemMetadata == null ) {
227
              logMetacat.debug("systemMetadata is null for " + pid.getValue());
228
              
229
          }
230
          replicas = systemMetadata.getReplicaList();
231
          int count = 0;
232
          
233
          if ( replicas == null || replicas.size() < 1 ) {
234
              logMetacat.debug("no replicas to evaluate");
235
              throw new InvalidRequest("4730", "There are no replicas to update.");
236
              
237
          }
238

    
239
          // find the target replica index in the replica list
240
          for (Replica replica: replicas) {
241
              String replicaNodeStr = replica.getReplicaMemberNode().getValue();
242
              String targetNodeStr = targetNode.getValue();
243
              logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
244
              
245
              if (replicaNodeStr.equals(targetNodeStr)) {
246
                  replicaEntryIndex = count;
247
                  logMetacat.debug("replica entry index is: " + replicaEntryIndex);
248
                  break;
249
              }
250
              count++;
251
              
252
          }
253

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

    
280
          if ( !allowed ) {
281
              String msg = "The subject identified by " + subject.getValue() +
282
                " does not have permission to set the replication status for " +
283
                "the replica identified by " + targetNode.getValue() + ".";
284
              logMetacat.info(msg);
285
              throw new NotAuthorized("4720", msg);
286
              
287
          }
288
          
289
          // does the request have the most current system metadata?
290
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
291
             String msg = "The requested system metadata version number " + 
292
                 serialVersion + " differs from the current version at " +
293
                 systemMetadata.getSerialVersion().longValue() +
294
                 ". Please get the latest copy in order to modify it.";
295
             throw new InvalidRequest("4730", msg);
296
          }
297
          
298
      } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
299
        throw new NotFound("4740", "No record found for: " + pid.getValue() +
300
            " : " + e.getMessage());
301
        
302
      }
303
          
304
      // set the status for the replica
305
      if ( replicaEntryIndex != -1 ) {
306
          Replica targetReplica = replicas.get(replicaEntryIndex);
307
          targetReplica.setReplicationStatus(status);
308
          logMetacat.debug("Set the replication status for " + 
309
              targetReplica.getReplicaMemberNode().getValue() + " to " +
310
              targetReplica.getReplicationStatus());
311
          
312
      } else {
313
          throw new InvalidRequest("4730", "There are no replicas to update.");
314

    
315
      }
316
      
317
      systemMetadata.setReplicaList(replicas);
318
            
319
      // update the metadata
320
      try {
321
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
322
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
323
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
324
        
325
      } catch (Exception e) {
326
          throw new ServiceFailure("4700", e.getMessage());
327
      
328
      } finally {
329
          lock.unlock();
330
          logMetacat.debug("Unlocked identifier " + pid.getValue());
331
         
332
      }
333
      
334
      return true;
335
  }
336

    
337
  /**
338
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
339
   * 
340
   * @param session - the Session object containing the credentials for the Subject
341
   * @param node - the node information for the given node be modified
342
   * 
343
   * @return true if the relationship exists
344
   * 
345
   * @throws InvalidToken
346
   * @throws ServiceFailure
347
   * @throws NotAuthorized
348
   * @throws NotFound
349
   * @throws InvalidRequest
350
   * @throws NotImplemented
351
   */
352
  @Override
353
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
354
    String relationship, Identifier pidOfObject) 
355
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
356
    InvalidRequest, NotImplemented {
357
    
358
    // The lock to be used for thyis identifier
359
    ILock lock = null;
360

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

    
415
    }
416
        
417
    return asserted;
418
  }
419
  
420
  /**
421
   * Return the checksum of the object given the identifier 
422
   * 
423
   * @param session - the Session object containing the credentials for the Subject
424
   * @param pid - the object identifier for the given object
425
   * 
426
   * @return checksum - the checksum of the object
427
   * 
428
   * @throws InvalidToken
429
   * @throws ServiceFailure
430
   * @throws NotAuthorized
431
   * @throws NotFound
432
   * @throws NotImplemented
433
   */
434
  @Override
435
  public Checksum getChecksum(Session session, Identifier pid)
436
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
437
    NotImplemented {
438
        
439
    // The lock to be used for thyis identifier
440
    ILock lock = null;
441
    
442
    if (!isAuthorized(session, pid, Permission.READ)) {
443
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
444
    }
445
    
446
    SystemMetadata systemMetadata = null;
447
    Checksum checksum = null;
448
    
449
    try {
450
        lock = HazelcastService.getInstance().getLock(pid.getValue());
451
        lock.lock();
452
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
453
        checksum = systemMetadata.getChecksum();
454

    
455
    } catch (Exception e) {
456
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
457
            pid.getValue() + ". The error message was: " + e.getMessage());
458
      
459
    } finally {
460
        lock.unlock();
461
        logMetacat.debug("Unlocked identifier " + pid.getValue());
462
        
463
    }
464
    
465
    return checksum;
466
  }
467

    
468
  /**
469
   * Resolve the location of a given object
470
   * 
471
   * @param session - the Session object containing the credentials for the Subject
472
   * @param pid - the object identifier for the given object
473
   * 
474
   * @return objectLocationList - the list of nodes known to contain the object
475
   * 
476
   * @throws InvalidToken
477
   * @throws ServiceFailure
478
   * @throws NotAuthorized
479
   * @throws NotFound
480
   * @throws NotImplemented
481
   */
482
  @Override
483
  public ObjectLocationList resolve(Session session, Identifier pid)
484
    throws InvalidToken, ServiceFailure, NotAuthorized,
485
    NotFound, NotImplemented {
486

    
487
    throw new NotImplemented("4131", "resolve not implemented");
488

    
489
  }
490

    
491
  /**
492
   * Search the metadata catalog for identifiers that match the criteria
493
   * 
494
   * @param session - the Session object containing the credentials for the Subject
495
   * @param queryType - An identifier for the type of query expression 
496
   *                    provided in the query
497
   * @param query -  The criteria for matching the characteristics of the 
498
   *                 metadata objects of interest
499
   * 
500
   * @return objectList - the list of objects matching the criteria
501
   * 
502
   * @throws InvalidToken
503
   * @throws ServiceFailure
504
   * @throws NotAuthorized
505
   * @throws InvalidRequest
506
   * @throws NotImplemented
507
   */
508
  @Override
509
  public ObjectList search(Session session, String queryType, String query)
510
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
511
    NotImplemented {
512

    
513
    ObjectList objectList = null;
514
    try {
515
        objectList = 
516
          IdentifierManager.getInstance().querySystemMetadata(
517
              null, //startTime, 
518
              null, //endTime,
519
              null, //objectFormat, 
520
              false, //replicaStatus, 
521
              0, //start, 
522
              -1 //count
523
              );
524
        
525
    } catch (Exception e) {
526
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
527
    }
528

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

    
550
            DBQuery metacat = new DBQuery();
551

    
552
            boolean useXMLIndex = (new Boolean(PropertyService
553
                    .getProperty("database.usexmlindex"))).booleanValue();
554
            String xmlquery = "query"; // we don't care the query in resultset,
555
            // the query can be anything
556
            PrintWriter out = null; // we don't want metacat result, so set out null
557

    
558
            // parameter: queryspecification, user, group, usingIndexOrNot
559
            StringBuffer result = metacat.createResultDocument(xmlquery,
560
                    metacatQuery, out, username, groupNames, useXMLIndex);
561

    
562
            // create result set transfer       
563
            String saxparser = PropertyService.getProperty("xml.saxparser");
564
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
565
                    new StringReader(result.toString()), saxparser, queryType
566
                            .getNamespace().get_value());
567
            ResultsetType records = metacatResultsetParser.getEcogridResult();
568

    
569
            System.out
570
                    .println(EcogridResultsetTransformer.toXMLString(records));
571
            response.setContentType("text/xml");
572
            out = response.getWriter();
573
            out.print(EcogridResultsetTransformer.toXMLString(records));
574

    
575
        } catch (Exception e) {
576
            e.printStackTrace();
577
        }*/
578
    
579

    
580
  }
581
  
582
  /**
583
   * Returns the object format registered in the DataONE Object Format 
584
   * Vocabulary for the given format identifier
585
   * 
586
   * @param fmtid - the identifier of the format requested
587
   * 
588
   * @return objectFormat - the object format requested
589
   * 
590
   * @throws ServiceFailure
591
   * @throws NotFound
592
   * @throws InsufficientResources
593
   * @throws NotImplemented
594
   */
595
  @Override
596
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
597
    throws ServiceFailure, NotFound, InsufficientResources,
598
    NotImplemented {
599
     
600
      return ObjectFormatService.getInstance().getFormat(fmtid);
601
      
602
  }
603

    
604
  /**
605
   * Returns a list of all object formats registered in the DataONE Object 
606
   * Format Vocabulary
607
    * 
608
   * @return objectFormatList - The list of object formats registered in 
609
   *                            the DataONE Object Format Vocabulary
610
   * 
611
   * @throws ServiceFailure
612
   * @throws NotImplemented
613
   * @throws InsufficientResources
614
   */
615
  @Override
616
  public ObjectFormatList listFormats() 
617
    throws ServiceFailure, InsufficientResources, 
618
    NotImplemented {
619

    
620
    return ObjectFormatService.getInstance().listFormats();
621
  }
622

    
623
  /**
624
   * Returns a list of nodes that have been registered with the DataONE infrastructure
625
    * 
626
   * @return nodeList - List of nodes from the registry
627
   * 
628
   * @throws ServiceFailure
629
   * @throws NotImplemented
630
   */
631
  @Override
632
  public NodeList listNodes() 
633
    throws NotImplemented, ServiceFailure {
634

    
635
    throw new NotImplemented("4800", "listNodes not implemented");
636
  }
637

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

    
660
      // The lock to be used for this identifier
661
      ILock lock = null;
662

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

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

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

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

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

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

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

    
851
      }
852
      
853
      return pid;
854
  }
855

    
856
  /**
857
   * Verify that a replication task is authorized by comparing the target node's
858
   * Subject (from the X.509 certificate-derived Session) with the list of 
859
   * subjects in the known, pending replication tasks map.
860
   * 
861
   * @param originatingNodeSession - Session information that contains the 
862
   *                                 identity of the calling user
863
   * @param targetNodeSubject - Subject identifying the target node
864
   * @param pid - the identifier of the object to be replicated
865
   * @param replicatePermission - the execute permission to be granted
866
   * 
867
   * @throws ServiceFailure
868
   * @throws NotImplemented
869
   * @throws InvalidToken
870
   * @throws NotAuthorized
871
   * @throws InvalidRequest
872
   * @throws NotFound
873
   */
874
  @Override
875
  public boolean isNodeAuthorized(Session originatingNodeSession, 
876
    Subject targetNodeSubject, Identifier pid) 
877
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
878
    NotFound, InvalidRequest {
879

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

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

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

    
956
          
957
      } else {
958
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
959
          " is null.");          
960
          
961
      }
962

    
963
    } catch (RuntimeException e) {
964
    	  ServiceFailure sf = new ServiceFailure("4872", 
965
                "Runtime Exception: Couldn't determine if node is allowed: " + 
966
                e.getCause().getMessage());
967
    	  sf.initCause(e);
968
        throw sf;
969
        
970
    } finally {
971
      // always unlock the pid
972
      lock.unlock();
973
      logMetacat.debug("Unlocked identifier " + pid.getValue());
974

    
975
    }
976
      
977
    return isAllowed;
978
    
979
  }
980

    
981
  /**
982
   * Adds a new object to the Node, where the object is a science metadata object.
983
   * 
984
   * @param session - the Session object containing the credentials for the Subject
985
   * @param pid - The object identifier to be created
986
   * @param object - the object bytes
987
   * @param sysmeta - the system metadata that describes the object  
988
   * 
989
   * @return pid - the object identifier created
990
   * 
991
   * @throws InvalidToken
992
   * @throws ServiceFailure
993
   * @throws NotAuthorized
994
   * @throws IdentifierNotUnique
995
   * @throws UnsupportedType
996
   * @throws InsufficientResources
997
   * @throws InvalidSystemMetadata
998
   * @throws NotImplemented
999
   * @throws InvalidRequest
1000
   */
1001
  public Identifier create(Session session, Identifier pid, InputStream object,
1002
    SystemMetadata sysmeta) 
1003
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1004
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1005
    NotImplemented, InvalidRequest {
1006
      
1007
      
1008
      // The lock to be used for this identifier
1009
      ILock lock = null;
1010
      
1011
      try {
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 = HazelcastService.getInstance().getLock(pid.getValue());
1034
              lock.lock();
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
          lock.unlock();
1054
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1055
     
1056
      }
1057
      
1058
      return pid;
1059

    
1060
  }
1061

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

    
1103
          // does the request have the most current system metadata?
1104
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1105
             String msg = "The requested system metadata version number " + 
1106
                 serialVersion + " differs from the current version at " +
1107
                 systemMetadata.getSerialVersion().longValue() +
1108
                 ". Please get the latest copy in order to modify it.";
1109
             throw new InvalidRequest("4402", msg);
1110
          }
1111
          
1112
      } catch (Exception e) {
1113
          // convert Hazelcast RuntimeException to NotFound
1114
          throw new NotFound("4400", "No record found for: " + pid);
1115
        
1116
      }
1117
          
1118
      // set the access policy
1119
      systemMetadata.setAccessPolicy(accessPolicy);
1120
      
1121
      // update the system metadata
1122
      try {
1123
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1124
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1125
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1126
        
1127
      } catch (Exception e) {
1128
          // convert Hazelcast RuntimeException to ServiceFailure
1129
          throw new ServiceFailure("4430", e.getMessage());
1130
        
1131
      } finally {
1132
          lock.unlock();
1133
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1134
        
1135
      }
1136
    
1137
    // TODO: how do we know if the map was persisted?
1138
    success = true;
1139
    
1140
    return success;
1141
  }
1142

    
1143
  /**
1144
   * Full replacement of replication metadata in the system metadata for the 
1145
   * specified object, changes date system metadata modified
1146
   * 
1147
   * @param session - the Session object containing the credentials for the Subject
1148
   * @param pid - the object identifier for the given object to apply the policy
1149
   * @param replica - the replica to be updated
1150
   * @return
1151
   * @throws NotImplemented
1152
   * @throws NotAuthorized
1153
   * @throws ServiceFailure
1154
   * @throws InvalidRequest
1155
   * @throws NotFound
1156
   */
1157
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1158
      Replica replica, long serialVersion) 
1159
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1160
      NotFound {
1161
      
1162
      // The lock to be used for this identifier
1163
      ILock lock = null;
1164
      
1165
      // get the subject
1166
      Subject subject = session.getSubject();
1167
      
1168
      // are we allowed to do this?
1169
      try {
1170
        // what is the controlling permission?
1171
        if (!isAuthorized(session, pid, Permission.WRITE)) {
1172
            throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1173
            " on " + pid.getValue());  
1174
        }
1175
        
1176
      } catch (InvalidToken e) {
1177
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1178
                  " on " + pid.getValue());  
1179
          
1180
      }
1181

    
1182
      SystemMetadata systemMetadata = null;
1183
      try {      
1184
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1185
          lock.lock();
1186
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1187

    
1188
          // does the request have the most current system metadata?
1189
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1190
             String msg = "The requested system metadata version number " + 
1191
                 serialVersion + " differs from the current version at " +
1192
                 systemMetadata.getSerialVersion().longValue() +
1193
                 ". Please get the latest copy in order to modify it.";
1194
             throw new InvalidRequest("4853", msg);
1195
          }
1196
          
1197
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1198
        throw new NotFound("4854", "No record found for: " + pid.getValue() +
1199
            " : " + e.getMessage());
1200
        
1201
      }
1202
          
1203
      // set the status for the replica
1204
      List<Replica> replicas = systemMetadata.getReplicaList();
1205
      NodeReference replicaNode = replica.getReplicaMemberNode();
1206
      int index = 0;
1207
      for (Replica listedReplica: replicas) {
1208
          
1209
          // remove the replica that we are replacing
1210
          if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1211
              replicas.remove(index);
1212
              break;
1213
              
1214
          }
1215
          index++;
1216
      }
1217
      
1218
      // add the new replica item
1219
      replicas.add(replica);
1220
      systemMetadata.setReplicaList(replicas);
1221
      
1222
      // update the metadata
1223
      try {
1224
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1225
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1226
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1227
        
1228
      } catch (Exception e) {
1229
          throw new ServiceFailure("4852", e.getMessage());
1230
      
1231
      } finally {
1232
          lock.unlock();
1233
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1234
          
1235
      }
1236
    
1237
      return true;
1238
      
1239
  }
1240
  
1241
    @Override
1242
    public ObjectList listObjects(Session session, Date startTime, 
1243
            Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1244
            Integer start, Integer count)
1245
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1246
      ServiceFailure {
1247
      
1248
      ObjectList objectList = null;
1249
        try {
1250
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1251
        } catch (Exception e) {
1252
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1253
        }
1254

    
1255
        return objectList;
1256
  }
1257
    
1258
}
(1-1/5)