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.util.Date;
27
import java.util.List;
28
import java.util.Set;
29

    
30
import org.apache.log4j.Logger;
31
import org.dataone.service.cn.v1.CNAuthorization;
32
import org.dataone.service.cn.v1.CNCore;
33
import org.dataone.service.cn.v1.CNRead;
34
import org.dataone.service.cn.v1.CNReplication;
35
import org.dataone.service.exceptions.IdentifierNotUnique;
36
import org.dataone.service.exceptions.InsufficientResources;
37
import org.dataone.service.exceptions.InvalidRequest;
38
import org.dataone.service.exceptions.InvalidSystemMetadata;
39
import org.dataone.service.exceptions.InvalidToken;
40
import org.dataone.service.exceptions.NotAuthorized;
41
import org.dataone.service.exceptions.NotFound;
42
import org.dataone.service.exceptions.NotImplemented;
43
import org.dataone.service.exceptions.ServiceFailure;
44
import org.dataone.service.types.v1.Checksum;
45
import org.dataone.service.types.v1.Identifier;
46
import org.dataone.service.types.v1.Node;
47
import org.dataone.service.types.v1.NodeList;
48
import org.dataone.service.types.v1.NodeReference;
49
import org.dataone.service.types.v1.ObjectFormat;
50
import org.dataone.service.types.v1.ObjectFormatIdentifier;
51
import org.dataone.service.types.v1.ObjectFormatList;
52
import org.dataone.service.types.v1.ObjectList;
53
import org.dataone.service.types.v1.ObjectLocationList;
54
import org.dataone.service.types.v1.Permission;
55
import org.dataone.service.types.v1.Replica;
56
import org.dataone.service.types.v1.ReplicationPolicy;
57
import org.dataone.service.types.v1.ReplicationStatus;
58
import org.dataone.service.types.v1.Session;
59
import org.dataone.service.types.v1.Subject;
60
import org.dataone.service.types.v1.SystemMetadata;
61

    
62
import com.hazelcast.query.SqlPredicate;
63

    
64
import edu.ucsb.nceas.metacat.EventLog;
65
import edu.ucsb.nceas.metacat.IdentifierManager;
66
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
67
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
68

    
69
/**
70
 * Represents Metacat's implementation of the DataONE Coordinating Node 
71
 * service API. Methods implement the various CN* interfaces, and methods common
72
 * to both Member Node and Coordinating Node interfaces are found in the
73
 * D1NodeService super class.
74
 *
75
 */
76
public class CNodeService extends D1NodeService implements CNAuthorization,
77
    CNCore, CNRead, CNReplication {
78

    
79
  /* the instance of the CNodeService object */
80
  private static CNodeService instance = null;
81

    
82
  /* the logger instance */
83
  private Logger logMetacat = null;
84

    
85
  /**
86
   * singleton accessor
87
   */
88
  public static CNodeService getInstance() { 
89
    if (instance == null) {
90
    
91
      instance = new CNodeService();
92
      
93
    }
94
  
95
    return instance;
96
  }
97
  
98
  /**
99
   * Constructor, private for singleton access
100
   */
101
  private CNodeService() {
102
    super();
103
    logMetacat = Logger.getLogger(CNodeService.class);
104
        
105
  }
106
    
107
  /**
108
   * Set the replication policy for an object given the object identifier
109
   * 
110
   * @param session - the Session object containing the credentials for the Subject
111
   * @param pid - the object identifier for the given object
112
   * @param policy - the replication policy to be applied
113
   * 
114
   * @return true or false
115
   * 
116
   * @throws NotImplemented
117
   * @throws NotAuthorized
118
   * @throws ServiceFailure
119
   * @throws InvalidRequest
120
   * 
121
   */
122
  @Override
123
  public boolean setReplicationPolicy(Session session, Identifier pid,
124
    ReplicationPolicy policy) 
125
    throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, InvalidRequest, InvalidToken {
126

    
127
    // get the subject
128
    Subject subject = session.getSubject();
129
    // get the system metadata
130
    String guid = pid.getValue();
131
    
132
    // are we allowed to do this?
133
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
134
      throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + " not allowed by " + subject.getValue() + " on " + guid);  
135
    }
136
    
137
    SystemMetadata systemMetadata = null;
138
    try {
139
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
140
    } catch (McdbDocNotFoundException e) {
141
      throw new NotFound("4884", "No record found for: " + guid);
142
    }
143
        
144
    // set the new policy
145
    systemMetadata.setReplicationPolicy(policy);
146
    
147
    // update the metadata
148
    try {
149
	    HazelcastService.getInstance().getSystemMetadataMap().lock(systemMetadata.getIdentifier());
150
	    HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
151
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
152
    } catch (Exception e) {
153
		throw new ServiceFailure("4882", e.getMessage());
154
	} finally {
155
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
156
	}
157
    
158
    return true;
159
  }
160

    
161
  /**
162
   * Set the replication status for an object given the object identifier
163
   * 
164
   * @param session - the Session object containing the credentials for the Subject
165
   * @param pid - the object identifier for the given object
166
   * @param status - the replication status to be applied
167
   * 
168
   * @return true or false
169
   * 
170
   * @throws NotImplemented
171
   * @throws NotAuthorized
172
   * @throws ServiceFailure
173
   * @throws InvalidRequest
174
   * @throws InvalidToken
175
   * @throws NotFound
176
   * 
177
   */
178
  @Override
179
  public boolean setReplicationStatus(Session session, Identifier pid,
180
    NodeReference targetNode, ReplicationStatus status) 
181
    throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
182
    InvalidRequest, NotFound {
183

    
184
    // get the subject
185
    Subject subject = session.getSubject();
186
    // get the system metadata
187
    String guid = pid.getValue();
188
    
189
    // are we allowed to do this?
190
    if (!isAuthorized(session, pid, Permission.WRITE)) {
191
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + subject.getValue() + " on " + guid);  
192
    }
193
    
194
    SystemMetadata systemMetadata = null;
195
    try {
196
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
197
    } catch (McdbDocNotFoundException e) {
198
      throw new NotFound("4740", "No record found for: " + guid);
199
    }
200
        
201
    // set the status for each replica
202
    // TODO: should this method select a certain replica?
203
    List<Replica> replicas = systemMetadata.getReplicaList();
204
    for (Replica replica: replicas) {
205
      replica.setReplicationStatus(status);
206
    }
207
    
208
    // [re]set the list -- redundant?
209
    systemMetadata.setReplicaList(replicas);
210
    
211
    // update the metadata
212
    try {
213
	    HazelcastService.getInstance().getSystemMetadataMap().lock(systemMetadata.getIdentifier());
214
	    HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
215
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
216
    } catch (Exception e) {
217
		throw new ServiceFailure("4700", e.getMessage());
218
	} finally {
219
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
220
	}
221
	
222
    return true;
223
  }
224

    
225
  /**
226
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
227
   * 
228
   * @param session - the Session object containing the credentials for the Subject
229
   * @param node - the node information for the given node be modified
230
   * 
231
   * @return true if the relationship exists
232
   * 
233
   * @throws InvalidToken
234
   * @throws ServiceFailure
235
   * @throws NotAuthorized
236
   * @throws NotFound
237
   * @throws InvalidRequest
238
   * @throws NotImplemented
239
   */
240
  @Override
241
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
242
    String relationship, Identifier pidOfObject) 
243
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
244
    InvalidRequest, NotImplemented {
245
    
246
    
247
    // get the system metadata
248
    String guid1 = pidOfSubject.getValue();
249
    // are we allowed to do this?
250
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
251
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);  
252
    }
253
    
254
    SystemMetadata systemMetadata = null;
255
    try {
256
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
257
    } catch (McdbDocNotFoundException e) {
258
      throw new NotFound("4884", "No record found for: " + guid1);
259
    }
260
        
261
    // check relationships
262
    // TODO: use ORE map
263
    if (relationship.equalsIgnoreCase("describes")) {
264
      
265
    }
266
    if (relationship.equalsIgnoreCase("describedBy")) {
267
      
268
    }
269
    if (relationship.equalsIgnoreCase("derivedFrom")) {
270
      
271
    }
272
    if (relationship.equalsIgnoreCase("obsoletes")) {
273
      Identifier pid = systemMetadata.getObsoletes();
274
      if (pid.getValue().equals(pidOfObject.getValue())) {
275
        return true;
276
      }
277
      //return systemMetadata.getObsoleteList().contains(pidOfObject);
278
    }
279
    if (relationship.equalsIgnoreCase("obsoletedBy")) {
280
      Identifier pid = systemMetadata.getObsoletedBy();
281
      if (pid.getValue().equals(pidOfObject.getValue())) {
282
        return true;
283
      }
284
      //return systemMetadata.getObsoletedByList().contains(pidOfObject);
285
    }
286

    
287
    return false;
288
  }
289
  
290
  /**
291
   * Return the checksum of the object given the identifier 
292
   * 
293
   * @param session - the Session object containing the credentials for the Subject
294
   * @param pid - the object identifier for the given object
295
   * 
296
   * @return checksum - the checksum of the object
297
   * 
298
   * @throws InvalidToken
299
   * @throws ServiceFailure
300
   * @throws NotAuthorized
301
   * @throws NotFound
302
   * @throws InvalidRequest
303
   * @throws NotImplemented
304
   */
305
  @Override
306
  public Checksum getChecksum(Session session, Identifier pid)
307
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
308
    InvalidRequest, NotImplemented {
309
    
310
    if (!isAuthorized(session, pid, Permission.READ)) {
311
      throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
312
    }
313
    SystemMetadata systemMetadata = null;
314
    try {
315
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
316
    } catch (McdbDocNotFoundException e) {
317
      throw new NotFound("1420", "No record found for: " + pid.getValue());
318
    }
319
    Checksum checksum = systemMetadata.getChecksum();
320
    
321
    return checksum;
322
  }
323

    
324
  /**
325
   * Resolve the location of a given object
326
   * 
327
   * @param session - the Session object containing the credentials for the Subject
328
   * @param pid - the object identifier for the given object
329
   * 
330
   * @return objectLocationList - the list of nodes known to contain the object
331
   * 
332
   * @throws InvalidRequest
333
   * @throws InvalidToken
334
   * @throws ServiceFailure
335
   * @throws NotAuthorized
336
   * @throws NotFound
337
   * @throws NotImplemented
338
   */
339
  @Override
340
  public ObjectLocationList resolve(Session session, Identifier pid)
341
    throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
342
    NotFound, NotImplemented {
343

    
344
    throw new NotImplemented("4131", "resolve not implemented");
345

    
346
  }
347

    
348
  /**
349
   * Search the metadata catalog for identifiers that match the criteria
350
   * 
351
   * @param session - the Session object containing the credentials for the Subject
352
   * @param queryType - An identifier for the type of query expression 
353
   *                    provided in the query
354
   * @param query -  The criteria for matching the characteristics of the 
355
   *                 metadata objects of interest
356
   * 
357
   * @return objectList - the list of objects matching the criteria
358
   * 
359
   * @throws InvalidToken
360
   * @throws ServiceFailure
361
   * @throws NotAuthorized
362
   * @throws InvalidRequest
363
   * @throws NotImplemented
364
   */
365
  @Override
366
  public ObjectList search(Session session, String queryType, String query)
367
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
368
    NotImplemented {
369

    
370
    ObjectList objectList = null;
371
    try {
372
        objectList = 
373
          IdentifierManager.getInstance().querySystemMetadata(
374
              null, //startTime, 
375
              null, //endTime,
376
              null, //objectFormat, 
377
              false, //replicaStatus, 
378
              0, //start, 
379
              -1 //count
380
              );
381
        
382
    } catch (Exception e) {
383
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
384
    }
385

    
386
      return objectList;
387
      
388
    //throw new NotImplemented("4281", "search not implemented");
389
    
390
    // the code block below is from an older implementation
391
    
392
    /*  This block commented out because of the EcoGrid circular dependency.
393
         *  For now, query will not be supported until the circularity can be
394
         *  resolved, probably by moving the ecogrid query syntax transformers
395
         *  directly into the Metacat codebase.  MBJ 2010-02-03
396
         
397
        try {
398
            EcogridQueryParser parser = new EcogridQueryParser(request
399
                    .getReader());
400
            parser.parseXML();
401
            QueryType queryType = parser.getEcogridQuery();
402
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
403
                new EcogridJavaToMetacatJavaQueryTransformer();
404
            QuerySpecification metacatQuery = queryTransformer
405
                    .transform(queryType);
406

    
407
            DBQuery metacat = new DBQuery();
408

    
409
            boolean useXMLIndex = (new Boolean(PropertyService
410
                    .getProperty("database.usexmlindex"))).booleanValue();
411
            String xmlquery = "query"; // we don't care the query in resultset,
412
            // the query can be anything
413
            PrintWriter out = null; // we don't want metacat result, so set out null
414

    
415
            // parameter: queryspecification, user, group, usingIndexOrNot
416
            StringBuffer result = metacat.createResultDocument(xmlquery,
417
                    metacatQuery, out, username, groupNames, useXMLIndex);
418

    
419
            // create result set transfer       
420
            String saxparser = PropertyService.getProperty("xml.saxparser");
421
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
422
                    new StringReader(result.toString()), saxparser, queryType
423
                            .getNamespace().get_value());
424
            ResultsetType records = metacatResultsetParser.getEcogridResult();
425

    
426
            System.out
427
                    .println(EcogridResultsetTransformer.toXMLString(records));
428
            response.setContentType("text/xml");
429
            out = response.getWriter();
430
            out.print(EcogridResultsetTransformer.toXMLString(records));
431

    
432
        } catch (Exception e) {
433
            e.printStackTrace();
434
        }*/
435
    
436

    
437
  }
438
  
439
  /**
440
   * Returns the object format registered in the DataONE Object Format 
441
   * Vocabulary for the given format identifier
442
   * 
443
   * @param fmtid - the identifier of the format requested
444
   * 
445
   * @return objectFormat - the object format requested
446
   * 
447
   * @throws InvalidRequest
448
   * @throws ServiceFailure
449
   * @throws NotFound
450
   * @throws InsufficientResources
451
   * @throws NotImplemented
452
   */
453
  @Override
454
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
455
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
456
    NotImplemented {
457
     
458
      return ObjectFormatService.getInstance().getFormat(fmtid);
459
      
460
  }
461

    
462
  /**
463
   * Returns a list of all object formats registered in the DataONE Object 
464
   * Format Vocabulary
465
    * 
466
   * @return objectFormatList - The list of object formats registered in 
467
   *                            the DataONE Object Format Vocabulary
468
   * 
469
   * @throws InvalidRequest
470
   * @throws ServiceFailure
471
   * @throws NotImplemented
472
   * @throws NotFound
473
   * @throws InsufficientResources
474
   */
475
  @Override
476
  public ObjectFormatList listFormats() 
477
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
478
    NotImplemented {
479

    
480
    return ObjectFormatService.getInstance().listFormats();
481
  }
482

    
483
  /**
484
   * Returns a list of nodes that have been registered with the DataONE infrastructure
485
    * 
486
   * @return nodeList - List of nodes from the registry
487
   * 
488
   * @throws ServiceFailure
489
   * @throws NotImplemented
490
   */
491
  @Override
492
  public NodeList listNodes() 
493
    throws NotImplemented, ServiceFailure {
494

    
495
    throw new NotImplemented("4800", "listNodes not implemented");
496
  }
497

    
498
  /**
499
   * Provides a mechanism for adding system metadata independently of its 
500
   * associated object, such as when adding system metadata for data objects.
501
    * 
502
   * @param session - the Session object containing the credentials for the Subject
503
   * @param pid - The identifier of the object to register the system metadata against
504
   * @param sysmeta - The system metadata to be registered
505
   * 
506
   * @return true if the registration succeeds
507
   * 
508
   * @throws NotImplemented
509
   * @throws NotAuthorized
510
   * @throws ServiceFailure
511
   * @throws InvalidRequest
512
   * @throws InvalidSystemMetadata
513
   */
514
  @Override
515
  public Identifier registerSystemMetadata(Session session, Identifier guid,
516
    SystemMetadata sysmeta) 
517
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
518
    InvalidSystemMetadata {
519

    
520
    // TODO: control who can call this?
521
        if (session == null) {
522
            //TODO: many of the thrown exceptions do not use the correct error codes
523
            //check these against the docs and correct them
524
            throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
525
                    "  If you are not logged in, please do so and retry the request.");
526
        }
527
        
528
        // verify that guid == SystemMetadata.getIdentifier()
529
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
530
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
531
            throw new InvalidRequest("4863", 
532
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
533
                sysmeta.getIdentifier().getValue() + ").");
534
        }
535

    
536
        logMetacat.debug("Checking if identifier exists...");
537
        // Check that the identifier does not already exist
538
        if (IdentifierManager.getInstance().identifierExists(guid.getValue())) {
539
            throw new InvalidRequest("4863", 
540
                "GUID is already in use by an existing object.");
541
      
542
        }
543

    
544
        // insert the system metadata into the object store
545
        logMetacat.debug("Starting to insert SystemMetadata...");
546
        sysmeta.setDateSysMetadataModified(new Date());
547
        try {
548
          HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
549
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
550
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
551
        } catch (Exception e) {
552
            throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
553
        } finally {
554
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
555

    
556
        }
557
        
558
        logMetacat.debug("Returning from registerSystemMetadata");
559
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
560
        return guid;
561
  }
562

    
563
  /**
564
   * Provides a mechanism for updating system metadata independently of its 
565
   * associated object
566
    * 
567
   * @param session - the Session object containing the credentials for the Subject
568
   * @param pid - The identifier of the system metadata
569
   * @param sysmeta - The system metadata to be registered
570
   * 
571
   * @return true if the update succeeds
572
   * 
573
   * @throws NotImplemented
574
   * @throws NotAuthorized
575
   * @throws ServiceFailure
576
   * @throws InvalidRequest
577
   * @throws InvalidSystemMetadata
578
   * @throws NotFound
579
   */
580
  @Override
581
  public boolean updateSystemMetadata(Session session, Identifier guid,
582
    SystemMetadata sysmeta) 
583
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
584
    InvalidSystemMetadata, NotFound {
585

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

    
602
        logMetacat.debug("Checking if identifier exists...");
603
        // Check that the identifier exists
604
        if (!IdentifierManager.getInstance().identifierExists(guid.getValue())) {
605
            throw new NotFound("000", 
606
                "GUID does not exist");
607
        }
608

    
609
        // update the system metadata into the object store
610
        logMetacat.debug("Starting to update SystemMetadata...");
611
        sysmeta.setDateSysMetadataModified(new Date());
612
        
613
        // update system metadata
614
        try {
615
    	    HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
616
    	    HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
617
    	    HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
618
        } catch (Exception e) {
619
    		throw new ServiceFailure("4852", e.getMessage());
620
    	} finally {
621
    	    HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
622
    	}
623
        
624
        logMetacat.debug("Returning from updateSystemMetadata");
625
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "updateSystemMetadata");
626
        return true;
627
  }
628
  
629
  /**
630
   * Given an optional scope and format, reserves and returns an identifier 
631
   * within that scope and format that is unique and will not be 
632
   * used by any other sessions. 
633
    * 
634
   * @param session - the Session object containing the credentials for the Subject
635
   * @param pid - The identifier of the object to register the system metadata against
636
   * @param scope - An optional string to be used to qualify the scope of 
637
   *                the identifier namespace, which is applied differently 
638
   *                depending on the format requested. If scope is not 
639
   *                supplied, a default scope will be used.
640
   * @param format - The optional name of the identifier format to be used, 
641
   *                  drawn from a DataONE-specific vocabulary of identifier 
642
   *                 format names, including several common syntaxes such 
643
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
644
   *                 format is not supplied by the caller, the CN service 
645
   *                 will use a default identifier format, which may change 
646
   *                 over time.
647
   * 
648
   * @return true if the registration succeeds
649
   * 
650
   * @throws InvalidToken
651
   * @throws ServiceFailure
652
   * @throws NotAuthorized
653
   * @throws IdentifierNotUnique
654
   * @throws NotImplemented
655
   */
656
  @Override
657
  public boolean reserveIdentifier(Session session, Identifier pid)
658
  throws InvalidToken, ServiceFailure,
659
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
660

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

    
688
  @Override
689
  public boolean hasReservation(Session session, Identifier pid) 
690
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
691
      NotImplemented, InvalidRequest {
692
  
693
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
694
  }
695

    
696
  /**
697
   * Changes ownership (RightsHolder) of the specified object to the 
698
   * subject specified by userId
699
    * 
700
   * @param session - the Session object containing the credentials for the Subject
701
   * @param pid - Identifier of the object to be modified
702
   * @param userId - The subject that will be taking ownership of the specified object.
703
   *
704
   * @return pid - the identifier of the modified object
705
   * 
706
   * @throws ServiceFailure
707
   * @throws InvalidToken
708
   * @throws NotFound
709
   * @throws NotAuthorized
710
   * @throws NotImplemented
711
   * @throws InvalidRequest
712
   */  
713
  @Override
714
  public Identifier setOwner(Session session, Identifier pid, Subject userId)
715
    throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
716
    NotImplemented, InvalidRequest {
717
    
718
    // get the subject
719
    Subject subject = session.getSubject();
720
    // get the system metadata
721
    String guid = pid.getValue();
722
    
723
    // are we allowed to do this?
724
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
725
      throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);  
726
    }
727
    
728
    SystemMetadata systemMetadata = null;
729
    try {
730
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
731
    } catch (McdbDocNotFoundException e) {
732
      throw new NotFound("4460", "No record found for: " + guid);
733
    }
734
        
735
    // set the new rights holder
736
    systemMetadata.setRightsHolder(userId);
737
    
738
    // update the metadata
739
    try {
740
	    HazelcastService.getInstance().getSystemMetadataMap().lock(systemMetadata.getIdentifier());
741
	    HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
742
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
743
    } catch (Exception e) {
744
		throw new ServiceFailure("4490", e.getMessage());
745
	} finally {
746
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
747
	}
748
    
749
    return pid;
750
  }
751

    
752
  /**
753
   * Verify that a replication task is authorized by comparing the target node's
754
   * Subject (from the X.509 certificate-derived Session) with the list of 
755
   * subjects in the known, pending replication tasks map.
756
   * 
757
   * @param originatingNodeSession - Session information that contains the 
758
   *                                 identity of the calling user
759
   * @param targetNodeSubject - Subject identifying the target node
760
   * @param pid - the identifier of the object to be replicated
761
   * @param replicatePermission - the execute permission to be granted
762
   * 
763
   * @throws ServiceFailure
764
   * @throws NotImplemented
765
   * @throws InvalidToken
766
   * @throws NotAuthorized
767
   * @throws InvalidRequest
768
   * @throws NotFound
769
   */
770
  @Override
771
  public boolean isNodeAuthorized(Session originatingNodeSession, 
772
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
773
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
774
    NotFound, InvalidRequest {
775

    
776
	  boolean isAllowed = false;
777
	  SystemMetadata sysmeta = null;
778
    String query = "subject = '" + targetNodeSubject + "'";
779
    NodeReference targetNode = null;
780
    
781
	  try {
782
	    // get the target node reference from the hzNodes map
783
	    Set<Node> nodeList = (Set<Node>) 
784
	      HazelcastService.getInstance().getNodesMap().values(new SqlPredicate(query));
785
	    
786
	    // we should only have one subject DN per node
787
	    for (Node node : nodeList) {      
788
	      targetNode = node.getIdentifier();
789
	      break;
790
	        
791
      }
792
	    //lock, get, and unlock the pid
793
	    HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
794
	    sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
795
	    HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
796
	    List<Replica> replicaList = sysmeta.getReplicaList();
797
	    
798
	    // find the replica with the status set to 'requested'
799
	    for(Replica replica : replicaList) {
800
	      ReplicationStatus status = replica.getReplicationStatus();
801
	      NodeReference listedNode = replica.getReplicaMemberNode();
802
	      if ( listedNode.equals(targetNode) && 
803
	           status.equals(ReplicationStatus.REQUESTED)) {
804
	        isAllowed = true;
805
	        break;
806
	        
807
	      }
808
	    }
809

    
810
	  } catch(Exception e) {
811
	    // Catch Hazelcast RuntimeExceptions
812
	    
813
	  } finally {
814
	    // always unlock the pid
815
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
816

    
817
	  }
818
	    
819
	  return isAllowed;
820
    
821
  }
822

    
823
}
(1-1/6)