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 edu.ucsb.nceas.metacat.EventLog;
73
import edu.ucsb.nceas.metacat.IdentifierManager;
74
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
75

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

    
86
  /* the logger instance */
87
  private Logger logMetacat = null;
88

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

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

    
176
  /**
177
   * Set the replication status for an object given the object identifier
178
   * 
179
   * @param session - the Session object containing the credentials for the Subject
180
   * @param pid - the object identifier for the given object
181
   * @param status - the replication status to be applied
182
   * 
183
   * @return true or false
184
   * 
185
   * @throws NotImplemented
186
   * @throws NotAuthorized
187
   * @throws ServiceFailure
188
   * @throws InvalidRequest
189
   * @throws InvalidToken
190
   * @throws NotFound
191
   * 
192
   */
193
  @Override
194
  public boolean setReplicationStatus(Session session, Identifier pid,
195
      NodeReference targetNode, ReplicationStatus status, long serialVersion) 
196
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
197
      InvalidRequest, NotFound {
198
      
199
      boolean allowed = false;
200
      int replicaEntryIndex = -1;
201
      List<Replica> replicas = null;
202
      // get the subject
203
      Subject subject = session.getSubject();
204
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
205
          " is " + status.toString());
206
      
207
      SystemMetadata systemMetadata = null;
208
      try {      
209
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
210
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
211

    
212
          if ( systemMetadata == null ) {
213
              logMetacat.debug("systemMetadata is null for " + pid.getValue());
214
              
215
          }
216
          replicas = systemMetadata.getReplicaList();
217
          int count = 0;
218
          
219
          if ( replicas == null || replicas.size() < 1 ) {
220
              logMetacat.debug("no replicas to evaluate");
221
              throw new InvalidRequest("4730", "There are no replicas to update.");
222
              
223
          }
224

    
225
          // find the target replica index in the replica list
226
          for (Replica replica: replicas) {
227
              String replicaNodeStr = replica.getReplicaMemberNode().getValue();
228
              String targetNodeStr = targetNode.getValue();
229
              logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
230
              
231
              if (replicaNodeStr.equals(targetNodeStr)) {
232
                  replicaEntryIndex = count;
233
                  logMetacat.debug("replica entry index is: " + replicaEntryIndex);
234
                  break;
235
              }
236
              count++;
237
              
238
          }
239

    
240
          // are we allowed to do this? only CNs and target MNs are allowed
241
          CNode cn = D1Client.getCN();
242
          List<Node> nodes = cn.listNodes().getNodeList();
243
          
244
          // find the node in the node list
245
          for ( Node node : nodes ) {
246
              
247
              NodeReference nodeReference = node.getIdentifier();
248
              logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
249
              
250
              // allow target MN certs and CN certs
251
              if (targetNode.getValue().equals(nodeReference.getValue()) ||
252
                  node.getType() == NodeType.CN) {
253
                  List<Subject> nodeSubjects = node.getSubjectList();
254
                  
255
                  // check if the session subject is in the node subject list
256
                  for (Subject nodeSubject : nodeSubjects) {
257
                      if ( nodeSubject.equals(subject) ) {
258
                          allowed = true; // subject of session == target node subject
259
                          break;
260
                          
261
                      }
262
                  }                 
263
              }
264
          }
265

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

    
301
      }
302
      
303
      systemMetadata.setReplicaList(replicas);
304
            
305
      // update the metadata
306
      try {
307
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
308
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
309
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
310
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
311
        
312
      } catch (Exception e) {
313
          throw new ServiceFailure("4700", e.getMessage());
314
      
315
      } finally {
316
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
317
          
318
      }
319
      
320
      return true;
321
  }
322

    
323
  /**
324
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
325
   * 
326
   * @param session - the Session object containing the credentials for the Subject
327
   * @param node - the node information for the given node be modified
328
   * 
329
   * @return true if the relationship exists
330
   * 
331
   * @throws InvalidToken
332
   * @throws ServiceFailure
333
   * @throws NotAuthorized
334
   * @throws NotFound
335
   * @throws InvalidRequest
336
   * @throws NotImplemented
337
   */
338
  @Override
339
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
340
    String relationship, Identifier pidOfObject) 
341
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
342
    InvalidRequest, NotImplemented {
343
    
344
    boolean asserted = false;
345
        
346
    // are we allowed to do this?
347
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
348
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());  
349
    }
350
    
351
    SystemMetadata systemMetadata = null;
352
    try {
353
        HazelcastService.getInstance().getSystemMetadataMap().lock(pidOfSubject);
354
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
355
        
356
        
357
        // check relationships
358
        // TODO: use ORE map
359
        if (relationship.equalsIgnoreCase("describes")) {
360
          
361
        }
362
        
363
        if (relationship.equalsIgnoreCase("describedBy")) {
364
          
365
        }
366
        
367
        if (relationship.equalsIgnoreCase("derivedFrom")) {
368
          
369
        }
370
        
371
        if (relationship.equalsIgnoreCase("obsoletes")) {
372
            Identifier pid = systemMetadata.getObsoletes();
373
            if (pid.getValue().equals(pidOfObject.getValue())) {
374
              asserted = true;
375
            
376
        }
377
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
378
        }
379
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
380
            Identifier pid = systemMetadata.getObsoletedBy();
381
            if (pid.getValue().equals(pidOfObject.getValue())) {
382
              asserted = true;
383
            
384
        }
385
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
386
        }
387
        
388
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
389
      
390
    } catch (Exception e) {
391
        throw new ServiceFailure("4270", "Could not assert relation for: " + 
392
            pidOfSubject.getValue() +
393
            ". The error message was: " + e.getMessage());
394
      
395
    } finally {
396
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
397

    
398
    }
399
        
400
    return asserted;
401
  }
402
  
403
  /**
404
   * Return the checksum of the object given the identifier 
405
   * 
406
   * @param session - the Session object containing the credentials for the Subject
407
   * @param pid - the object identifier for the given object
408
   * 
409
   * @return checksum - the checksum of the object
410
   * 
411
   * @throws InvalidToken
412
   * @throws ServiceFailure
413
   * @throws NotAuthorized
414
   * @throws NotFound
415
   * @throws NotImplemented
416
   */
417
  @Override
418
  public Checksum getChecksum(Session session, Identifier pid)
419
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
420
    NotImplemented {
421
        
422
    if (!isAuthorized(session, pid, Permission.READ)) {
423
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
424
    }
425
    
426
    SystemMetadata systemMetadata = null;
427
    Checksum checksum = null;
428
    
429
    try {
430
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
431
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
432
        checksum = systemMetadata.getChecksum();
433
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
434

    
435
    } catch (Exception e) {
436
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
437
            pid.getValue() + ". The error message was: " + e.getMessage());
438
      
439
    } finally {
440
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
441
        
442
    }
443
    
444
    return checksum;
445
  }
446

    
447
  /**
448
   * Resolve the location of a given object
449
   * 
450
   * @param session - the Session object containing the credentials for the Subject
451
   * @param pid - the object identifier for the given object
452
   * 
453
   * @return objectLocationList - the list of nodes known to contain the object
454
   * 
455
   * @throws InvalidToken
456
   * @throws ServiceFailure
457
   * @throws NotAuthorized
458
   * @throws NotFound
459
   * @throws NotImplemented
460
   */
461
  @Override
462
  public ObjectLocationList resolve(Session session, Identifier pid)
463
    throws InvalidToken, ServiceFailure, NotAuthorized,
464
    NotFound, NotImplemented {
465

    
466
    throw new NotImplemented("4131", "resolve not implemented");
467

    
468
  }
469

    
470
  /**
471
   * Search the metadata catalog for identifiers that match the criteria
472
   * 
473
   * @param session - the Session object containing the credentials for the Subject
474
   * @param queryType - An identifier for the type of query expression 
475
   *                    provided in the query
476
   * @param query -  The criteria for matching the characteristics of the 
477
   *                 metadata objects of interest
478
   * 
479
   * @return objectList - the list of objects matching the criteria
480
   * 
481
   * @throws InvalidToken
482
   * @throws ServiceFailure
483
   * @throws NotAuthorized
484
   * @throws InvalidRequest
485
   * @throws NotImplemented
486
   */
487
  @Override
488
  public ObjectList search(Session session, String queryType, String query)
489
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
490
    NotImplemented {
491

    
492
    ObjectList objectList = null;
493
    try {
494
        objectList = 
495
          IdentifierManager.getInstance().querySystemMetadata(
496
              null, //startTime, 
497
              null, //endTime,
498
              null, //objectFormat, 
499
              false, //replicaStatus, 
500
              0, //start, 
501
              -1 //count
502
              );
503
        
504
    } catch (Exception e) {
505
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
506
    }
507

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

    
529
            DBQuery metacat = new DBQuery();
530

    
531
            boolean useXMLIndex = (new Boolean(PropertyService
532
                    .getProperty("database.usexmlindex"))).booleanValue();
533
            String xmlquery = "query"; // we don't care the query in resultset,
534
            // the query can be anything
535
            PrintWriter out = null; // we don't want metacat result, so set out null
536

    
537
            // parameter: queryspecification, user, group, usingIndexOrNot
538
            StringBuffer result = metacat.createResultDocument(xmlquery,
539
                    metacatQuery, out, username, groupNames, useXMLIndex);
540

    
541
            // create result set transfer       
542
            String saxparser = PropertyService.getProperty("xml.saxparser");
543
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
544
                    new StringReader(result.toString()), saxparser, queryType
545
                            .getNamespace().get_value());
546
            ResultsetType records = metacatResultsetParser.getEcogridResult();
547

    
548
            System.out
549
                    .println(EcogridResultsetTransformer.toXMLString(records));
550
            response.setContentType("text/xml");
551
            out = response.getWriter();
552
            out.print(EcogridResultsetTransformer.toXMLString(records));
553

    
554
        } catch (Exception e) {
555
            e.printStackTrace();
556
        }*/
557
    
558

    
559
  }
560
  
561
  /**
562
   * Returns the object format registered in the DataONE Object Format 
563
   * Vocabulary for the given format identifier
564
   * 
565
   * @param fmtid - the identifier of the format requested
566
   * 
567
   * @return objectFormat - the object format requested
568
   * 
569
   * @throws ServiceFailure
570
   * @throws NotFound
571
   * @throws InsufficientResources
572
   * @throws NotImplemented
573
   */
574
  @Override
575
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
576
    throws ServiceFailure, NotFound, InsufficientResources,
577
    NotImplemented {
578
     
579
      return ObjectFormatService.getInstance().getFormat(fmtid);
580
      
581
  }
582

    
583
  /**
584
   * Returns a list of all object formats registered in the DataONE Object 
585
   * Format Vocabulary
586
    * 
587
   * @return objectFormatList - The list of object formats registered in 
588
   *                            the DataONE Object Format Vocabulary
589
   * 
590
   * @throws ServiceFailure
591
   * @throws NotImplemented
592
   * @throws InsufficientResources
593
   */
594
  @Override
595
  public ObjectFormatList listFormats() 
596
    throws ServiceFailure, InsufficientResources, 
597
    NotImplemented {
598

    
599
    return ObjectFormatService.getInstance().listFormats();
600
  }
601

    
602
  /**
603
   * Returns a list of nodes that have been registered with the DataONE infrastructure
604
    * 
605
   * @return nodeList - List of nodes from the registry
606
   * 
607
   * @throws ServiceFailure
608
   * @throws NotImplemented
609
   */
610
  @Override
611
  public NodeList listNodes() 
612
    throws NotImplemented, ServiceFailure {
613

    
614
    throw new NotImplemented("4800", "listNodes not implemented");
615
  }
616

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

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

    
657
      logMetacat.debug("Checking if identifier exists...");
658
      // Check that the identifier does not already exist
659
      if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
660
          throw new InvalidRequest("4863", 
661
              "The identifier is already in use by an existing object.");
662
      
663
      }
664
      
665
      // insert the system metadata into the object store
666
      logMetacat.debug("Starting to insert SystemMetadata...");
667
      try {
668
          HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
669
          sysmeta.setSerialVersion(BigInteger.ONE);
670
          sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
671
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
672
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
673
          
674
      } catch (Exception e) {
675
        logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
676
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
677
              e.getClass() + ": " + e.getMessage());
678
          
679
      } finally {
680
        HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
681

    
682
      }
683
      
684
      logMetacat.debug("Returning from registerSystemMetadata");
685
      EventLog.getInstance().log(request.getRemoteAddr(), 
686
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
687
          pid.getValue(), "registerSystemMetadata");
688
      return pid;
689
  }
690
  
691
  /**
692
   * Given an optional scope and format, reserves and returns an identifier 
693
   * within that scope and format that is unique and will not be 
694
   * used by any other sessions. 
695
    * 
696
   * @param session - the Session object containing the credentials for the Subject
697
   * @param pid - The identifier of the object to register the system metadata against
698
   * @param scope - An optional string to be used to qualify the scope of 
699
   *                the identifier namespace, which is applied differently 
700
   *                depending on the format requested. If scope is not 
701
   *                supplied, a default scope will be used.
702
   * @param format - The optional name of the identifier format to be used, 
703
   *                  drawn from a DataONE-specific vocabulary of identifier 
704
   *                 format names, including several common syntaxes such 
705
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
706
   *                 format is not supplied by the caller, the CN service 
707
   *                 will use a default identifier format, which may change 
708
   *                 over time.
709
   * 
710
   * @return true if the registration succeeds
711
   * 
712
   * @throws InvalidToken
713
   * @throws ServiceFailure
714
   * @throws NotAuthorized
715
   * @throws IdentifierNotUnique
716
   * @throws NotImplemented
717
   */
718
  @Override
719
  public Identifier reserveIdentifier(Session session, Identifier pid)
720
  throws InvalidToken, ServiceFailure,
721
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
722

    
723
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
724
  }
725
  
726
  @Override
727
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
728
  throws InvalidToken, ServiceFailure,
729
        NotAuthorized, NotImplemented, InvalidRequest {
730
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
731
  }
732
  
733
  /**
734
    * Checks whether the pid is reserved by the subject in the session param
735
    * If the reservation is held on the pid by the subject, we return true.
736
    * 
737
   * @param session - the Session object containing the Subject
738
   * @param pid - The identifier to check
739
   * 
740
   * @return true if the reservation exists for the subject/pid
741
   * 
742
   * @throws InvalidToken
743
   * @throws ServiceFailure
744
   * @throws NotFound - when the pid is not found (in use or in reservation)
745
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
746
   * @throws IdentifierNotUnique - when the pid is in use
747
   * @throws NotImplemented
748
   */
749

    
750
  @Override
751
  public boolean hasReservation(Session session, Identifier pid) 
752
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
753
      NotImplemented, InvalidRequest {
754
  
755
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
756
  }
757

    
758
  /**
759
   * Changes ownership (RightsHolder) of the specified object to the 
760
   * subject specified by userId
761
    * 
762
   * @param session - the Session object containing the credentials for the Subject
763
   * @param pid - Identifier of the object to be modified
764
   * @param userId - The subject that will be taking ownership of the specified object.
765
   *
766
   * @return pid - the identifier of the modified object
767
   * 
768
   * @throws ServiceFailure
769
   * @throws InvalidToken
770
   * @throws NotFound
771
   * @throws NotAuthorized
772
   * @throws NotImplemented
773
   * @throws InvalidRequest
774
   */  
775
  @Override
776
  public Identifier setOwner(Session session, Identifier pid, Subject userId,
777
      long serialVersion)
778
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
779
      NotImplemented, InvalidRequest {
780
      
781
      // get the subject
782
      Subject subject = session.getSubject();
783
      
784
      // are we allowed to do this?
785
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
786
        throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + pid.getValue());  
787
      }
788
      
789
      SystemMetadata systemMetadata = null;
790
      try {
791
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
792
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
793
          
794
          // does the request have the most current system metadata?
795
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
796
             String msg = "The requested system metadata version number " + 
797
                 serialVersion + " differs from the current version at " +
798
                 systemMetadata.getSerialVersion().longValue() +
799
                 ". Please get the latest copy in order to modify it.";
800
             throw new InvalidRequest("4442", msg);
801
          }
802
          
803
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
804
          throw new NotFound("4460", "No record found for: " + pid.getValue());
805
          
806
      }
807
          
808
      // set the new rights holder
809
      systemMetadata.setRightsHolder(userId);
810
      
811
      // update the metadata
812
      try {
813
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
814
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
815
          HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
816
          HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
817
          
818
      } catch (Exception e) {
819
      throw new ServiceFailure("4490", e.getMessage());
820
      
821
      } finally {
822
          HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
823
      }
824
      
825
      return pid;
826
  }
827

    
828
  /**
829
   * Verify that a replication task is authorized by comparing the target node's
830
   * Subject (from the X.509 certificate-derived Session) with the list of 
831
   * subjects in the known, pending replication tasks map.
832
   * 
833
   * @param originatingNodeSession - Session information that contains the 
834
   *                                 identity of the calling user
835
   * @param targetNodeSubject - Subject identifying the target node
836
   * @param pid - the identifier of the object to be replicated
837
   * @param replicatePermission - the execute permission to be granted
838
   * 
839
   * @throws ServiceFailure
840
   * @throws NotImplemented
841
   * @throws InvalidToken
842
   * @throws NotAuthorized
843
   * @throws InvalidRequest
844
   * @throws NotFound
845
   */
846
  @Override
847
  public boolean isNodeAuthorized(Session originatingNodeSession, 
848
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
849
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
850
    NotFound, InvalidRequest {
851

    
852
    boolean isAllowed = false;
853
    SystemMetadata sysmeta = null;
854
    NodeReference targetNode = null;
855
    
856
    try {
857
      // get the target node reference from the nodes list
858
      CNode cn = D1Client.getCN();
859
      List<Node> nodes = cn.listNodes().getNodeList();
860
      
861
      if ( nodes != null ) {
862
        for (Node node : nodes) {
863
            
864
            for (Subject nodeSubject : node.getSubjectList()) {
865
                
866
                if ( nodeSubject.equals(targetNodeSubject) ) {
867
                    targetNode = node.getIdentifier();
868
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
869
                    break;
870
                }
871
            }
872
            
873
            if ( targetNode != null) { break; }
874
        }
875
        
876
      } else {
877
          String msg = "Couldn't get the node list from the CN";
878
          logMetacat.debug(msg);
879
          throw new ServiceFailure("4872", msg);
880
          
881
      }
882
      //lock, get, and unlock the pid
883
      //HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
884
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
885
      
886
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
887

    
888
      if ( sysmeta != null ) {
889
          
890
          List<Replica> replicaList = sysmeta.getReplicaList();
891
          
892
          if ( replicaList != null ) {
893
              
894
              // find the replica with the status set to 'requested'
895
              for (Replica replica : replicaList) {
896
                  ReplicationStatus status = replica.getReplicationStatus();
897
                  NodeReference listedNode = replica.getReplicaMemberNode();
898
                  logMetacat.debug("Comparing " + listedNode.getValue() + " to " + 
899
                      targetNode.getValue());
900
                  if (listedNode.getValue().equals(targetNode.getValue())
901
                          && status.equals(ReplicationStatus.REQUESTED)) {
902
                      isAllowed = true;
903
                      break;
904

    
905
                  }
906
              }
907
          }
908
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
909
              "to replicate: " + isAllowed + " for " + pid.getValue());
910

    
911
          
912
      } else {
913
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
914
          " is null.");          
915
          
916
      }
917
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
918

    
919
    } catch (RuntimeException e) {
920
    	  ServiceFailure sf = new ServiceFailure("4872", 
921
                "Runtime Exception: Couldn't determine if node is allowed: " + 
922
                e.getMessage());
923
    	  sf.initCause(e);
924
        throw sf;
925
        
926
    } finally {
927
      // always unlock the pid
928
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
929

    
930
    }
931
      
932
    return isAllowed;
933
    
934
  }
935

    
936
  /**
937
   * Adds a new object to the Node, where the object is a science metadata object.
938
   * 
939
   * @param session - the Session object containing the credentials for the Subject
940
   * @param pid - The object identifier to be created
941
   * @param object - the object bytes
942
   * @param sysmeta - the system metadata that describes the object  
943
   * 
944
   * @return pid - the object identifier created
945
   * 
946
   * @throws InvalidToken
947
   * @throws ServiceFailure
948
   * @throws NotAuthorized
949
   * @throws IdentifierNotUnique
950
   * @throws UnsupportedType
951
   * @throws InsufficientResources
952
   * @throws InvalidSystemMetadata
953
   * @throws NotImplemented
954
   * @throws InvalidRequest
955
   */
956
  public Identifier create(Session session, Identifier pid, InputStream object,
957
    SystemMetadata sysmeta) 
958
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
959
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
960
    NotImplemented, InvalidRequest {
961
      
962
      
963
      try {
964
        // are we allowed?
965
          boolean isAllowed = false;
966
          CNode cn = D1Client.getCN();
967
          List<Node> nodes = (List<Node>) cn.listNodes();
968
          
969
          for (Node node : nodes) {
970
              if ( node.getType().equals(NodeType.CN) ) {
971
                  
972
                  List<Subject> subjects = node.getSubjectList();
973
                  for (Subject subject : subjects) {
974
                     if (subject.getValue().equals(session.getSubject().getValue())) {
975
                         isAllowed = true;
976
                         break;
977
                     }
978
                  }
979
              }
980
          }
981

    
982
          // proceed if we're called by a CN
983
          if ( isAllowed ) {
984
              // create the coordinating node version of the document      
985
              HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
986
              sysmeta.setSerialVersion(BigInteger.ONE);
987
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
988
              pid = super.create(session, pid, object, sysmeta);
989
              HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
990

    
991
          } else {
992
              String msg = "The subject listed as " + session.getSubject().getValue() + 
993
                  " isn't allowed to call create() on a Coordinating Node.";
994
              logMetacat.info(msg);
995
              throw new NotAuthorized("1100", msg);
996
          }
997
          
998
      } catch (RuntimeException e) {
999
          // Convert Hazelcast runtime exceptions to service failures
1000
          String msg = "There was a problem creating the object identified by " +
1001
              pid.getValue() + ". There error message was: " + e.getMessage();
1002
          throw new ServiceFailure("4893", msg);
1003
          
1004
      } finally {
1005
          HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
1006
      
1007
      }
1008
      
1009
      return pid;
1010

    
1011
  }
1012

    
1013
  /**
1014
   * Set access for a given object using the object identifier and a Subject
1015
   * under a given Session.
1016
   * 
1017
   * @param session - the Session object containing the credentials for the Subject
1018
   * @param pid - the object identifier for the given object to apply the policy
1019
   * @param policy - the access policy to be applied
1020
   * 
1021
   * @return true if the application of the policy succeeds
1022
   * @throws InvalidToken
1023
   * @throws ServiceFailure
1024
   * @throws NotFound
1025
   * @throws NotAuthorized
1026
   * @throws NotImplemented
1027
   * @throws InvalidRequest
1028
   */
1029
  public boolean setAccessPolicy(Session session, Identifier pid, 
1030
      AccessPolicy accessPolicy, long serialVersion) 
1031
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1032
      NotImplemented, InvalidRequest {
1033
      
1034
      boolean success = false;
1035
      
1036
      // get the subject
1037
      Subject subject = session.getSubject();
1038
      
1039
      // are we allowed to do this?
1040
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1041
          throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + 
1042
          " on " + pid.getValue());  
1043
      }
1044
      
1045
      SystemMetadata systemMetadata = null;
1046
      try {
1047
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
1048
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1049

    
1050
          // does the request have the most current system metadata?
1051
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1052
             String msg = "The requested system metadata version number " + 
1053
                 serialVersion + " differs from the current version at " +
1054
                 systemMetadata.getSerialVersion().longValue() +
1055
                 ". Please get the latest copy in order to modify it.";
1056
             throw new InvalidRequest("4402", msg);
1057
          }
1058
          
1059
      } catch (Exception e) {
1060
          // convert Hazelcast RuntimeException to NotFound
1061
          throw new NotFound("4400", "No record found for: " + pid);
1062
        
1063
      }
1064
          
1065
      // set the access policy
1066
      systemMetadata.setAccessPolicy(accessPolicy);
1067
      
1068
      // update the system metadata
1069
      try {
1070
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1071
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1072
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1073
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1074
        
1075
      } catch (Exception e) {
1076
          // convert Hazelcast RuntimeException to ServiceFailure
1077
          throw new ServiceFailure("4430", e.getMessage());
1078
        
1079
      } finally {
1080
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1081
        
1082
      }
1083
    
1084
    // TODO: how do we know if the map was persisted?
1085
    success = true;
1086
    
1087
    return success;
1088
  }
1089

    
1090
  /**
1091
   * Full replacement of replication metadata in the system metadata for the 
1092
   * specified object, changes date system metadata modified
1093
   * 
1094
   * @param session - the Session object containing the credentials for the Subject
1095
   * @param pid - the object identifier for the given object to apply the policy
1096
   * @param replica - the replica to be updated
1097
   * @return
1098
   * @throws NotImplemented
1099
   * @throws NotAuthorized
1100
   * @throws ServiceFailure
1101
   * @throws InvalidRequest
1102
   * @throws NotFound
1103
   */
1104
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1105
      Replica replica, long serialVersion) 
1106
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1107
      NotFound {
1108
      
1109
      // get the subject
1110
      Subject subject = session.getSubject();
1111
      
1112
      // are we allowed to do this?
1113
      try {
1114
        // what is the controlling permission?
1115
        if (!isAuthorized(session, pid, Permission.WRITE)) {
1116
            throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1117
            " on " + pid.getValue());  
1118
        }
1119
        
1120
      } catch (InvalidToken e) {
1121
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1122
                  " on " + pid.getValue());  
1123
          
1124
      }
1125

    
1126
      SystemMetadata systemMetadata = null;
1127
      try {      
1128
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
1129
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1130

    
1131
          // does the request have the most current system metadata?
1132
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1133
             String msg = "The requested system metadata version number " + 
1134
                 serialVersion + " differs from the current version at " +
1135
                 systemMetadata.getSerialVersion().longValue() +
1136
                 ". Please get the latest copy in order to modify it.";
1137
             throw new InvalidRequest("4853", msg);
1138
          }
1139
          
1140
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1141
        throw new NotFound("4854", "No record found for: " + pid.getValue() +
1142
            " : " + e.getMessage());
1143
        
1144
      }
1145
          
1146
      // set the status for the replica
1147
      List<Replica> replicas = systemMetadata.getReplicaList();
1148
      NodeReference replicaNode = replica.getReplicaMemberNode();
1149
      int index = 0;
1150
      for (Replica listedReplica: replicas) {
1151
          
1152
          // remove the replica that we are replacing
1153
          if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1154
              replicas.remove(index);
1155
              break;
1156
              
1157
          }
1158
          index++;
1159
      }
1160
      
1161
      // add the new replica item
1162
      replicas.add(replica);
1163
      systemMetadata.setReplicaList(replicas);
1164
      
1165
      // update the metadata
1166
      try {
1167
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1168
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1169
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1170
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1171
        
1172
      } catch (Exception e) {
1173
          throw new ServiceFailure("4852", e.getMessage());
1174
      
1175
      } finally {
1176
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1177
          
1178
      }
1179
    
1180
      return true;
1181
      
1182
  }
1183
  
1184
    @Override
1185
    public ObjectList listObjects(Session session, Date startTime, 
1186
            Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1187
            Integer start, Integer count)
1188
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1189
      ServiceFailure {
1190
      
1191
      ObjectList objectList = null;
1192
        try {
1193
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1194
        } catch (Exception e) {
1195
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1196
        }
1197

    
1198
        return objectList;
1199
  }
1200
    
1201
}
(1-1/4)