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.IOException;
27
import java.io.InputStream;
28
import java.math.BigInteger;
29
import java.util.Calendar;
30
import java.util.Date;
31
import java.util.List;
32
import java.util.Set;
33

    
34
import javax.servlet.http.HttpServletRequest;
35

    
36
import org.apache.commons.io.IOUtils;
37
import org.apache.log4j.Logger;
38
import org.dataone.client.CNode;
39
import org.dataone.client.D1Client;
40
import org.dataone.service.cn.v1.CNAuthorization;
41
import org.dataone.service.cn.v1.CNCore;
42
import org.dataone.service.cn.v1.CNRead;
43
import org.dataone.service.cn.v1.CNReplication;
44
import org.dataone.service.exceptions.IdentifierNotUnique;
45
import org.dataone.service.exceptions.InsufficientResources;
46
import org.dataone.service.exceptions.InvalidRequest;
47
import org.dataone.service.exceptions.InvalidSystemMetadata;
48
import org.dataone.service.exceptions.InvalidToken;
49
import org.dataone.service.exceptions.NotAuthorized;
50
import org.dataone.service.exceptions.NotFound;
51
import org.dataone.service.exceptions.NotImplemented;
52
import org.dataone.service.exceptions.ServiceFailure;
53
import org.dataone.service.exceptions.UnsupportedType;
54
import org.dataone.service.types.v1.AccessPolicy;
55
import org.dataone.service.types.v1.Checksum;
56
import org.dataone.service.types.v1.Identifier;
57
import org.dataone.service.types.v1.Node;
58
import org.dataone.service.types.v1.NodeList;
59
import org.dataone.service.types.v1.NodeReference;
60
import org.dataone.service.types.v1.NodeType;
61
import org.dataone.service.types.v1.ObjectFormat;
62
import org.dataone.service.types.v1.ObjectFormatIdentifier;
63
import org.dataone.service.types.v1.ObjectFormatList;
64
import org.dataone.service.types.v1.ObjectList;
65
import org.dataone.service.types.v1.ObjectLocationList;
66
import org.dataone.service.types.v1.Permission;
67
import org.dataone.service.types.v1.Replica;
68
import org.dataone.service.types.v1.ReplicationPolicy;
69
import org.dataone.service.types.v1.ReplicationStatus;
70
import org.dataone.service.types.v1.Session;
71
import org.dataone.service.types.v1.Subject;
72
import org.dataone.service.types.v1.SystemMetadata;
73
import org.dataone.service.types.v1.util.ChecksumUtil;
74
import org.dataone.service.util.Constants;
75

    
76
import com.hazelcast.query.SqlPredicate;
77

    
78
import edu.ucsb.nceas.metacat.EventLog;
79
import edu.ucsb.nceas.metacat.IdentifierManager;
80
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
81
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
82

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

    
93
  /* the logger instance */
94
  private Logger logMetacat = null;
95

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

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

    
183
  /**
184
   * Set the replication status for an object given the object identifier
185
   * 
186
   * @param session - the Session object containing the credentials for the Subject
187
   * @param pid - the object identifier for the given object
188
   * @param status - the replication status to be applied
189
   * 
190
   * @return true or false
191
   * 
192
   * @throws NotImplemented
193
   * @throws NotAuthorized
194
   * @throws ServiceFailure
195
   * @throws InvalidRequest
196
   * @throws InvalidToken
197
   * @throws NotFound
198
   * 
199
   */
200
  @Override
201
  public boolean setReplicationStatus(Session session, Identifier pid,
202
    NodeReference targetNode, ReplicationStatus status, long serialVersion) 
203
    throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
204
    InvalidRequest, NotFound {
205

    
206
    // get the subject
207
    Subject subject = session.getSubject();
208
    
209
    // are we allowed to do this?
210
    if (!isAuthorized(session, pid, Permission.WRITE)) {
211
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + 
212
          subject.getValue() + " on " + pid.getValue());  
213
    }
214
    
215
    SystemMetadata systemMetadata = null;
216
    try {      
217
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
218
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
219

    
220

    
221
        // does the request have the most current system metadata?
222
        if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
223
           String msg = "The requested system metadata version number " + 
224
               serialVersion + "differs from the current version at " +
225
               systemMetadata.getSerialVersion().longValue() +
226
               " Please get the latest copy in order to modify it.";
227
           throw new InvalidRequest("4730", msg);
228
        }
229
        
230
    } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
231
      throw new NotFound("4740", "No record found for: " + pid.getValue() +
232
          " : " + e.getMessage());
233
      
234
    }
235
        
236
    // set the status for the replica
237
    List<Replica> replicas = systemMetadata.getReplicaList();
238
    for (Replica replica: replicas) {
239
        if (replica.getReplicaMemberNode().getValue().equals(targetNode.getValue())) {
240
            replica.setReplicationStatus(status);
241
            
242
        }
243
    }
244
    
245
    // [re]set the list -- redundant?
246
    systemMetadata.setReplicaList(replicas);
247
    
248
    // update the metadata
249
    try {
250
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
251
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
252
	      HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
253
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
254
	    
255
    } catch (Exception e) {
256
		    throw new ServiceFailure("4700", e.getMessage());
257
		
258
	  } finally {
259
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
260
	      
261
	  }
262
	
263
    return true;
264
  }
265

    
266
  /**
267
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
268
   * 
269
   * @param session - the Session object containing the credentials for the Subject
270
   * @param node - the node information for the given node be modified
271
   * 
272
   * @return true if the relationship exists
273
   * 
274
   * @throws InvalidToken
275
   * @throws ServiceFailure
276
   * @throws NotAuthorized
277
   * @throws NotFound
278
   * @throws InvalidRequest
279
   * @throws NotImplemented
280
   */
281
  @Override
282
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
283
    String relationship, Identifier pidOfObject) 
284
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
285
    InvalidRequest, NotImplemented {
286
    
287
    boolean asserted = false;
288
        
289
    // are we allowed to do this?
290
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
291
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());  
292
    }
293
    
294
    SystemMetadata systemMetadata = null;
295
    try {
296
        HazelcastService.getInstance().getSystemMetadataMap().lock(pidOfSubject);
297
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
298
        
299
        
300
        // check relationships
301
        // TODO: use ORE map
302
        if (relationship.equalsIgnoreCase("describes")) {
303
          
304
        }
305
        
306
        if (relationship.equalsIgnoreCase("describedBy")) {
307
          
308
        }
309
        
310
        if (relationship.equalsIgnoreCase("derivedFrom")) {
311
          
312
        }
313
        
314
        if (relationship.equalsIgnoreCase("obsoletes")) {
315
            Identifier pid = systemMetadata.getObsoletes();
316
            if (pid.getValue().equals(pidOfObject.getValue())) {
317
              asserted = true;
318
            
319
        }
320
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
321
        }
322
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
323
            Identifier pid = systemMetadata.getObsoletedBy();
324
            if (pid.getValue().equals(pidOfObject.getValue())) {
325
              asserted = true;
326
            
327
        }
328
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
329
        }
330
        
331
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
332
      
333
    } catch (Exception e) {
334
        throw new ServiceFailure("4270", "Could not assert relation for: " + 
335
            pidOfSubject.getValue() +
336
            ". The error message was: " + e.getMessage());
337
      
338
    } finally {
339
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
340

    
341
    }
342
        
343
    return asserted;
344
  }
345
  
346
  /**
347
   * Return the checksum of the object given the identifier 
348
   * 
349
   * @param session - the Session object containing the credentials for the Subject
350
   * @param pid - the object identifier for the given object
351
   * 
352
   * @return checksum - the checksum of the object
353
   * 
354
   * @throws InvalidToken
355
   * @throws ServiceFailure
356
   * @throws NotAuthorized
357
   * @throws NotFound
358
   * @throws NotImplemented
359
   */
360
  @Override
361
  public Checksum getChecksum(Session session, Identifier pid)
362
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
363
    NotImplemented {
364
        
365
    if (!isAuthorized(session, pid, Permission.READ)) {
366
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
367
    }
368
    
369
    SystemMetadata systemMetadata = null;
370
    Checksum checksum = null;
371
    
372
    try {
373
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
374
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
375
        checksum = systemMetadata.getChecksum();
376
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
377

    
378
    } catch (Exception e) {
379
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
380
            pid.getValue() + ". The error message was: " + e.getMessage());
381
      
382
    } finally {
383
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
384
        
385
    }
386
    
387
    return checksum;
388
  }
389

    
390
  /**
391
   * Resolve the location of a given object
392
   * 
393
   * @param session - the Session object containing the credentials for the Subject
394
   * @param pid - the object identifier for the given object
395
   * 
396
   * @return objectLocationList - the list of nodes known to contain the object
397
   * 
398
   * @throws InvalidToken
399
   * @throws ServiceFailure
400
   * @throws NotAuthorized
401
   * @throws NotFound
402
   * @throws NotImplemented
403
   */
404
  @Override
405
  public ObjectLocationList resolve(Session session, Identifier pid)
406
    throws InvalidToken, ServiceFailure, NotAuthorized,
407
    NotFound, NotImplemented {
408

    
409
    throw new NotImplemented("4131", "resolve not implemented");
410

    
411
  }
412

    
413
  /**
414
   * Search the metadata catalog for identifiers that match the criteria
415
   * 
416
   * @param session - the Session object containing the credentials for the Subject
417
   * @param queryType - An identifier for the type of query expression 
418
   *                    provided in the query
419
   * @param query -  The criteria for matching the characteristics of the 
420
   *                 metadata objects of interest
421
   * 
422
   * @return objectList - the list of objects matching the criteria
423
   * 
424
   * @throws InvalidToken
425
   * @throws ServiceFailure
426
   * @throws NotAuthorized
427
   * @throws InvalidRequest
428
   * @throws NotImplemented
429
   */
430
  @Override
431
  public ObjectList search(Session session, String queryType, String query)
432
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
433
    NotImplemented {
434

    
435
    ObjectList objectList = null;
436
    try {
437
        objectList = 
438
          IdentifierManager.getInstance().querySystemMetadata(
439
              null, //startTime, 
440
              null, //endTime,
441
              null, //objectFormat, 
442
              false, //replicaStatus, 
443
              0, //start, 
444
              -1 //count
445
              );
446
        
447
    } catch (Exception e) {
448
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
449
    }
450

    
451
      return objectList;
452
      
453
    //throw new NotImplemented("4281", "search not implemented");
454
    
455
    // the code block below is from an older implementation
456
    
457
    /*  This block commented out because of the EcoGrid circular dependency.
458
         *  For now, query will not be supported until the circularity can be
459
         *  resolved, probably by moving the ecogrid query syntax transformers
460
         *  directly into the Metacat codebase.  MBJ 2010-02-03
461
         
462
        try {
463
            EcogridQueryParser parser = new EcogridQueryParser(request
464
                    .getReader());
465
            parser.parseXML();
466
            QueryType queryType = parser.getEcogridQuery();
467
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
468
                new EcogridJavaToMetacatJavaQueryTransformer();
469
            QuerySpecification metacatQuery = queryTransformer
470
                    .transform(queryType);
471

    
472
            DBQuery metacat = new DBQuery();
473

    
474
            boolean useXMLIndex = (new Boolean(PropertyService
475
                    .getProperty("database.usexmlindex"))).booleanValue();
476
            String xmlquery = "query"; // we don't care the query in resultset,
477
            // the query can be anything
478
            PrintWriter out = null; // we don't want metacat result, so set out null
479

    
480
            // parameter: queryspecification, user, group, usingIndexOrNot
481
            StringBuffer result = metacat.createResultDocument(xmlquery,
482
                    metacatQuery, out, username, groupNames, useXMLIndex);
483

    
484
            // create result set transfer       
485
            String saxparser = PropertyService.getProperty("xml.saxparser");
486
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
487
                    new StringReader(result.toString()), saxparser, queryType
488
                            .getNamespace().get_value());
489
            ResultsetType records = metacatResultsetParser.getEcogridResult();
490

    
491
            System.out
492
                    .println(EcogridResultsetTransformer.toXMLString(records));
493
            response.setContentType("text/xml");
494
            out = response.getWriter();
495
            out.print(EcogridResultsetTransformer.toXMLString(records));
496

    
497
        } catch (Exception e) {
498
            e.printStackTrace();
499
        }*/
500
    
501

    
502
  }
503
  
504
  /**
505
   * Returns the object format registered in the DataONE Object Format 
506
   * Vocabulary for the given format identifier
507
   * 
508
   * @param fmtid - the identifier of the format requested
509
   * 
510
   * @return objectFormat - the object format requested
511
   * 
512
   * @throws ServiceFailure
513
   * @throws NotFound
514
   * @throws InsufficientResources
515
   * @throws NotImplemented
516
   */
517
  @Override
518
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
519
    throws ServiceFailure, NotFound, InsufficientResources,
520
    NotImplemented {
521
     
522
      return ObjectFormatService.getInstance().getFormat(fmtid);
523
      
524
  }
525

    
526
  /**
527
   * Returns a list of all object formats registered in the DataONE Object 
528
   * Format Vocabulary
529
    * 
530
   * @return objectFormatList - The list of object formats registered in 
531
   *                            the DataONE Object Format Vocabulary
532
   * 
533
   * @throws ServiceFailure
534
   * @throws NotImplemented
535
   * @throws InsufficientResources
536
   */
537
  @Override
538
  public ObjectFormatList listFormats() 
539
    throws ServiceFailure, InsufficientResources, 
540
    NotImplemented {
541

    
542
    return ObjectFormatService.getInstance().listFormats();
543
  }
544

    
545
  /**
546
   * Returns a list of nodes that have been registered with the DataONE infrastructure
547
    * 
548
   * @return nodeList - List of nodes from the registry
549
   * 
550
   * @throws ServiceFailure
551
   * @throws NotImplemented
552
   */
553
  @Override
554
  public NodeList listNodes() 
555
    throws NotImplemented, ServiceFailure {
556

    
557
    throw new NotImplemented("4800", "listNodes not implemented");
558
  }
559

    
560
  /**
561
   * Provides a mechanism for adding system metadata independently of its 
562
   * associated object, such as when adding system metadata for data objects.
563
    * 
564
   * @param session - the Session object containing the credentials for the Subject
565
   * @param pid - The identifier of the object to register the system metadata against
566
   * @param sysmeta - The system metadata to be registered
567
   * 
568
   * @return true if the registration succeeds
569
   * 
570
   * @throws NotImplemented
571
   * @throws NotAuthorized
572
   * @throws ServiceFailure
573
   * @throws InvalidRequest
574
   * @throws InvalidSystemMetadata
575
   */
576
  @Override
577
  public Identifier registerSystemMetadata(Session session, Identifier pid,
578
      SystemMetadata sysmeta) 
579
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
580
      InvalidSystemMetadata {
581

    
582
      // TODO: control who can call this?
583
      if (session == null) {
584
          //TODO: many of the thrown exceptions do not use the correct error codes
585
          //check these against the docs and correct them
586
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
587
                  "  If you are not logged in, please do so and retry the request.");
588
      }
589
      
590
      // verify that guid == SystemMetadata.getIdentifier()
591
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
592
          "|" + sysmeta.getIdentifier().getValue());
593
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
594
          throw new InvalidRequest("4863", 
595
              "The identifier in method call (" + pid.getValue() + 
596
              ") does not match identifier in system metadata (" +
597
              sysmeta.getIdentifier().getValue() + ").");
598
      }
599

    
600
      logMetacat.debug("Checking if identifier exists...");
601
      // Check that the identifier does not already exist
602
      if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
603
          throw new InvalidRequest("4863", 
604
              "The identifier is already in use by an existing object.");
605
      
606
      }
607
      
608
      // insert the system metadata into the object store
609
      logMetacat.debug("Starting to insert SystemMetadata...");
610
      try {
611
          HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
612
          sysmeta.setSerialVersion(BigInteger.ONE);
613
          sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
614
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
615
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
616
          
617
      } catch (Exception e) {
618
      	logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
619
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
620
              e.getClass() + ": " + e.getMessage());
621
          
622
      } finally {
623
        HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
624

    
625
      }
626
      
627
      logMetacat.debug("Returning from registerSystemMetadata");
628
      EventLog.getInstance().log(request.getRemoteAddr(), 
629
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
630
          pid.getValue(), "registerSystemMetadata");
631
      return pid;
632
  }
633
  
634
  /**
635
   * Given an optional scope and format, reserves and returns an identifier 
636
   * within that scope and format that is unique and will not be 
637
   * used by any other sessions. 
638
    * 
639
   * @param session - the Session object containing the credentials for the Subject
640
   * @param pid - The identifier of the object to register the system metadata against
641
   * @param scope - An optional string to be used to qualify the scope of 
642
   *                the identifier namespace, which is applied differently 
643
   *                depending on the format requested. If scope is not 
644
   *                supplied, a default scope will be used.
645
   * @param format - The optional name of the identifier format to be used, 
646
   *                  drawn from a DataONE-specific vocabulary of identifier 
647
   *                 format names, including several common syntaxes such 
648
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
649
   *                 format is not supplied by the caller, the CN service 
650
   *                 will use a default identifier format, which may change 
651
   *                 over time.
652
   * 
653
   * @return true if the registration succeeds
654
   * 
655
   * @throws InvalidToken
656
   * @throws ServiceFailure
657
   * @throws NotAuthorized
658
   * @throws IdentifierNotUnique
659
   * @throws NotImplemented
660
   */
661
  @Override
662
  public Identifier reserveIdentifier(Session session, Identifier pid)
663
  throws InvalidToken, ServiceFailure,
664
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
665

    
666
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
667
  }
668
  
669
  @Override
670
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
671
  throws InvalidToken, ServiceFailure,
672
        NotAuthorized, NotImplemented, InvalidRequest {
673
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
674
  }
675
  
676
  /**
677
    * Checks whether the pid is reserved by the subject in the session param
678
    * If the reservation is held on the pid by the subject, we return true.
679
    * 
680
   * @param session - the Session object containing the Subject
681
   * @param pid - The identifier to check
682
   * 
683
   * @return true if the reservation exists for the subject/pid
684
   * 
685
   * @throws InvalidToken
686
   * @throws ServiceFailure
687
   * @throws NotFound - when the pid is not found (in use or in reservation)
688
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
689
   * @throws IdentifierNotUnique - when the pid is in use
690
   * @throws NotImplemented
691
   */
692

    
693
  @Override
694
  public boolean hasReservation(Session session, Identifier pid) 
695
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
696
      NotImplemented, InvalidRequest {
697
  
698
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
699
  }
700

    
701
  /**
702
   * Changes ownership (RightsHolder) of the specified object to the 
703
   * subject specified by userId
704
    * 
705
   * @param session - the Session object containing the credentials for the Subject
706
   * @param pid - Identifier of the object to be modified
707
   * @param userId - The subject that will be taking ownership of the specified object.
708
   *
709
   * @return pid - the identifier of the modified object
710
   * 
711
   * @throws ServiceFailure
712
   * @throws InvalidToken
713
   * @throws NotFound
714
   * @throws NotAuthorized
715
   * @throws NotImplemented
716
   * @throws InvalidRequest
717
   */  
718
  @Override
719
  public Identifier setOwner(Session session, Identifier pid, Subject userId,
720
      long serialVersion)
721
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
722
      NotImplemented, InvalidRequest {
723
      
724
      // get the subject
725
      Subject subject = session.getSubject();
726
      
727
      // are we allowed to do this?
728
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
729
        throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + pid.getValue());  
730
      }
731
      
732
      SystemMetadata systemMetadata = null;
733
      try {
734
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
735
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
736
          
737
          // does the request have the most current system metadata?
738
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
739
             String msg = "The requested system metadata version number " + 
740
                 serialVersion + "differs from the current version at " +
741
                 systemMetadata.getSerialVersion().longValue() +
742
                 " Please get the latest copy in order to modify it.";
743
             throw new InvalidRequest("4442", msg);
744
          }
745
          
746
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
747
          throw new NotFound("4460", "No record found for: " + pid.getValue());
748
          
749
      }
750
          
751
      // set the new rights holder
752
      systemMetadata.setRightsHolder(userId);
753
      
754
      // update the metadata
755
      try {
756
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
757
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
758
	        HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
759
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
760
	        
761
      } catch (Exception e) {
762
		  throw new ServiceFailure("4490", e.getMessage());
763
		  
764
	    } finally {
765
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
766
	    }
767
      
768
      return pid;
769
  }
770

    
771
  /**
772
   * Verify that a replication task is authorized by comparing the target node's
773
   * Subject (from the X.509 certificate-derived Session) with the list of 
774
   * subjects in the known, pending replication tasks map.
775
   * 
776
   * @param originatingNodeSession - Session information that contains the 
777
   *                                 identity of the calling user
778
   * @param targetNodeSubject - Subject identifying the target node
779
   * @param pid - the identifier of the object to be replicated
780
   * @param replicatePermission - the execute permission to be granted
781
   * 
782
   * @throws ServiceFailure
783
   * @throws NotImplemented
784
   * @throws InvalidToken
785
   * @throws NotAuthorized
786
   * @throws InvalidRequest
787
   * @throws NotFound
788
   */
789
  @Override
790
  public boolean isNodeAuthorized(Session originatingNodeSession, 
791
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
792
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
793
    NotFound, InvalidRequest {
794

    
795
	  boolean isAllowed = false;
796
	  SystemMetadata sysmeta = null;
797
    NodeReference targetNode = null;
798
    
799
	  try {
800
	    // get the target node reference from the nodes list
801
	    CNode cn = D1Client.getCN();
802
	    List<Node> nodes = (List<Node>) cn.listNodes();
803

    
804
	    for ( Node node : nodes ) {
805
	        Subject nodeSubject = node.getSubject(0);
806
	        if (nodeSubject.getValue().equals(targetNodeSubject)) {
807
	            targetNode = node.getIdentifier();
808
	            
809
	        }
810
	    }
811
	    
812

    
813
	    //lock, get, and unlock the pid
814
	    HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
815
	    sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
816
	    List<Replica> replicaList = sysmeta.getReplicaList();
817
	    
818
      // find the replica with the status set to 'requested'
819
      for (Replica replica : replicaList) {
820
          ReplicationStatus status = replica.getReplicationStatus();
821
          NodeReference listedNode = replica.getReplicaMemberNode();
822
          if (listedNode.equals(targetNode)
823
                  && status.equals(ReplicationStatus.REQUESTED)) {
824
              isAllowed = true;
825
              break;
826

    
827
          }
828
      }
829
      
830
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
831

    
832
	  } catch(Exception e) {
833
	      // Catch Hazelcast RuntimeExceptions
834
	      throw new ServiceFailure("4872", 
835
	          "Couldn't determine if node is allowed: " + e.getMessage());
836
	    
837
	  } finally {
838
	    // always unlock the pid
839
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
840

    
841
	  }
842
	    
843
	  return isAllowed;
844
    
845
  }
846

    
847
  /**
848
   * Adds a new object to the Node, where the object is a science metadata object.
849
   * 
850
   * @param session - the Session object containing the credentials for the Subject
851
   * @param pid - The object identifier to be created
852
   * @param object - the object bytes
853
   * @param sysmeta - the system metadata that describes the object  
854
   * 
855
   * @return pid - the object identifier created
856
   * 
857
   * @throws InvalidToken
858
   * @throws ServiceFailure
859
   * @throws NotAuthorized
860
   * @throws IdentifierNotUnique
861
   * @throws UnsupportedType
862
   * @throws InsufficientResources
863
   * @throws InvalidSystemMetadata
864
   * @throws NotImplemented
865
   * @throws InvalidRequest
866
   */
867
  public Identifier create(Session session, Identifier pid, InputStream object,
868
    SystemMetadata sysmeta) 
869
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
870
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
871
    NotImplemented, InvalidRequest {
872
      
873
      
874
      try {
875
        // are we allowed?
876
          boolean isAllowed = false;
877
          CNode cn = D1Client.getCN();
878
          List<Node> nodes = (List<Node>) cn.listNodes();
879
          
880
          for (Node node : nodes) {
881
              if ( node.getType().equals(NodeType.CN) ) {
882
                  
883
                  List<Subject> subjects = node.getSubjectList();
884
                  for (Subject subject : subjects) {
885
                     if (subject.getValue().equals(session.getSubject().getValue())) {
886
                         isAllowed = true;
887
                         break;
888
                     }
889
                  }
890
              }
891
          }
892

    
893
          // proceed if we're called by a CN
894
          if ( isAllowed ) {
895
              // create the coordinating node version of the document      
896
              HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
897
              sysmeta.setSerialVersion(BigInteger.ONE);
898
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
899
              pid = super.create(session, pid, object, sysmeta);
900
              HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
901

    
902
          } else {
903
              String msg = "The subject listed as " + session.getSubject().getValue() + 
904
                  " isn't allowed to call create() on a Coordinating Node.";
905
              logMetacat.info(msg);
906
              throw new NotAuthorized("1100", msg);
907
          }
908
          
909
      } catch (Exception e) {
910
          // Convert Hazelcast runtime exceptions to service failures
911
          String msg = "There was a problem creating the object identified by " +
912
              pid.getValue() + ". There error message was: " + e.getMessage();
913
          
914
      } finally {
915
          HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
916
      
917
      }
918
      
919
      return pid;
920

    
921
  }
922

    
923
  /**
924
   * Set access for a given object using the object identifier and a Subject
925
   * under a given Session.
926
   * 
927
   * @param session - the Session object containing the credentials for the Subject
928
   * @param pid - the object identifier for the given object to apply the policy
929
   * @param policy - the access policy to be applied
930
   * 
931
   * @return true if the application of the policy succeeds
932
   * @throws InvalidToken
933
   * @throws ServiceFailure
934
   * @throws NotFound
935
   * @throws NotAuthorized
936
   * @throws NotImplemented
937
   * @throws InvalidRequest
938
   */
939
  public boolean setAccessPolicy(Session session, Identifier pid, 
940
      AccessPolicy accessPolicy, long serialVersion) 
941
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
942
      NotImplemented, InvalidRequest {
943
      
944
      boolean success = false;
945
      
946
      // get the subject
947
      Subject subject = session.getSubject();
948
      
949
      // are we allowed to do this?
950
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
951
          throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + 
952
          " on " + pid.getValue());  
953
      }
954
      
955
      SystemMetadata systemMetadata = null;
956
      try {
957
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
958
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
959

    
960
          // does the request have the most current system metadata?
961
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
962
             String msg = "The requested system metadata version number " + 
963
                 serialVersion + "differs from the current version at " +
964
                 systemMetadata.getSerialVersion().longValue() +
965
                 " Please get the latest copy in order to modify it.";
966
             throw new InvalidRequest("4402", msg);
967
          }
968
          
969
      } catch (Exception e) {
970
          // convert Hazelcast RuntimeException to NotFound
971
          throw new NotFound("4400", "No record found for: " + pid);
972
        
973
      }
974
          
975
      // set the access policy
976
      systemMetadata.setAccessPolicy(accessPolicy);
977
      
978
      // update the system metadata
979
      try {
980
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
981
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
982
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
983
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
984
        
985
      } catch (Exception e) {
986
          // convert Hazelcast RuntimeException to ServiceFailure
987
          throw new ServiceFailure("4430", e.getMessage());
988
        
989
      } finally {
990
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
991
        
992
      }
993
    
994
    // TODO: how do we know if the map was persisted?
995
    success = true;
996
    
997
    return success;
998
  }
999

    
1000
  /**
1001
   * Full replacement of replication metadata in the system metadata for the 
1002
   * specified object, changes date system metadata modified
1003
   * 
1004
   * @param session - the Session object containing the credentials for the Subject
1005
   * @param pid - the object identifier for the given object to apply the policy
1006
   * @param replica - the replica to be updated
1007
   * @return
1008
   * @throws NotImplemented
1009
   * @throws NotAuthorized
1010
   * @throws ServiceFailure
1011
   * @throws InvalidRequest
1012
   * @throws NotFound
1013
   */
1014
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1015
      Replica replica, long serialVersion) 
1016
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1017
      NotFound {
1018
      
1019
      // get the subject
1020
      Subject subject = session.getSubject();
1021
      
1022
      // are we allowed to do this?
1023
      try {
1024
        // what is the controlling permission?
1025
        if (!isAuthorized(session, pid, Permission.WRITE)) {
1026
            throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1027
            " on " + pid.getValue());  
1028
        }
1029
        
1030
      } catch (InvalidToken e) {
1031
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1032
                  " on " + pid.getValue());  
1033
          
1034
      }
1035

    
1036
      SystemMetadata systemMetadata = null;
1037
      try {      
1038
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
1039
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1040

    
1041
          // does the request have the most current system metadata?
1042
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1043
             String msg = "The requested system metadata version number " + 
1044
                 serialVersion + "differs from the current version at " +
1045
                 systemMetadata.getSerialVersion().longValue() +
1046
                 " Please get the latest copy in order to modify it.";
1047
             throw new InvalidRequest("4853", msg);
1048
          }
1049
          
1050
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1051
        throw new NotFound("4854", "No record found for: " + pid.getValue() +
1052
            " : " + e.getMessage());
1053
        
1054
      }
1055
          
1056
      // set the status for the replica
1057
      List<Replica> replicas = systemMetadata.getReplicaList();
1058
      NodeReference replicaNode = replica.getReplicaMemberNode();
1059
      int index = 0;
1060
      for (Replica listedReplica: replicas) {
1061
          
1062
          // remove the replica that we are replacing
1063
          if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1064
              replicas.remove(index);
1065
              break;
1066
              
1067
          }
1068
          index++;
1069
      }
1070
      
1071
      // add the new replica item
1072
      replicas.add(replica);
1073
      systemMetadata.setReplicaList(replicas);
1074
      
1075
      // update the metadata
1076
      try {
1077
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1078
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1079
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1080
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1081
        
1082
      } catch (Exception e) {
1083
          throw new ServiceFailure("4852", e.getMessage());
1084
      
1085
      } finally {
1086
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1087
          
1088
      }
1089
    
1090
      return true;
1091
      
1092
  }
1093
  
1094
  	@Override
1095
  	public ObjectList listObjects(Session session, Date startTime, 
1096
            Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1097
            Integer start, Integer count)
1098
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1099
			ServiceFailure {
1100
  		
1101
  		ObjectList objectList = null;
1102
        try {
1103
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1104
        } catch (Exception e) {
1105
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1106
        }
1107

    
1108
        return objectList;
1109
	}
1110
}
(1-1/4)