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.NodeList;
47
import org.dataone.service.types.v1.NodeReference;
48
import org.dataone.service.types.v1.ObjectFormat;
49
import org.dataone.service.types.v1.ObjectFormatIdentifier;
50
import org.dataone.service.types.v1.ObjectFormatList;
51
import org.dataone.service.types.v1.ObjectList;
52
import org.dataone.service.types.v1.ObjectLocationList;
53
import org.dataone.service.types.v1.Permission;
54
import org.dataone.service.types.v1.Replica;
55
import org.dataone.service.types.v1.ReplicationPolicy;
56
import org.dataone.service.types.v1.ReplicationStatus;
57
import org.dataone.service.types.v1.Session;
58
import org.dataone.service.types.v1.Subject;
59
import org.dataone.service.types.v1.SystemMetadata;
60

    
61
import com.hazelcast.query.SqlPredicate;
62

    
63
import edu.ucsb.nceas.metacat.EventLog;
64
import edu.ucsb.nceas.metacat.IdentifierManager;
65
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
66
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
67
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
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
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
150
    } catch (McdbDocNotFoundException e) {
151
      throw new ServiceFailure("4882", e.getMessage());
152
    }
153

    
154
    return true;
155
  }
156

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

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

    
214
    return true;
215
  }
216

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

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

    
316
  /**
317
   * Resolve the location of a given object
318
   * 
319
   * @param session - the Session object containing the credentials for the Subject
320
   * @param pid - the object identifier for the given object
321
   * 
322
   * @return objectLocationList - the list of nodes known to contain the object
323
   * 
324
   * @throws InvalidRequest
325
   * @throws InvalidToken
326
   * @throws ServiceFailure
327
   * @throws NotAuthorized
328
   * @throws NotFound
329
   * @throws NotImplemented
330
   */
331
  @Override
332
  public ObjectLocationList resolve(Session session, Identifier pid)
333
    throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
334
    NotFound, NotImplemented {
335

    
336
    throw new NotImplemented("4131", "resolve not implemented");
337

    
338
  }
339

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

    
362
    ObjectList objectList = null;
363
    try {
364
        objectList = 
365
          IdentifierManager.getInstance().querySystemMetadata(
366
              null, //startTime, 
367
              null, //endTime,
368
              null, //objectFormat, 
369
              false, //replicaStatus, 
370
              0, //start, 
371
              -1 //count
372
              );
373
        
374
    } catch (Exception e) {
375
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
376
    }
377

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

    
399
            DBQuery metacat = new DBQuery();
400

    
401
            boolean useXMLIndex = (new Boolean(PropertyService
402
                    .getProperty("database.usexmlindex"))).booleanValue();
403
            String xmlquery = "query"; // we don't care the query in resultset,
404
            // the query can be anything
405
            PrintWriter out = null; // we don't want metacat result, so set out null
406

    
407
            // parameter: queryspecification, user, group, usingIndexOrNot
408
            StringBuffer result = metacat.createResultDocument(xmlquery,
409
                    metacatQuery, out, username, groupNames, useXMLIndex);
410

    
411
            // create result set transfer       
412
            String saxparser = PropertyService.getProperty("xml.saxparser");
413
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
414
                    new StringReader(result.toString()), saxparser, queryType
415
                            .getNamespace().get_value());
416
            ResultsetType records = metacatResultsetParser.getEcogridResult();
417

    
418
            System.out
419
                    .println(EcogridResultsetTransformer.toXMLString(records));
420
            response.setContentType("text/xml");
421
            out = response.getWriter();
422
            out.print(EcogridResultsetTransformer.toXMLString(records));
423

    
424
        } catch (Exception e) {
425
            e.printStackTrace();
426
        }*/
427
    
428

    
429
  }
430
  
431
  /**
432
   * Returns the object format registered in the DataONE Object Format 
433
   * Vocabulary for the given format identifier
434
   * 
435
   * @param fmtid - the identifier of the format requested
436
   * 
437
   * @return objectFormat - the object format requested
438
   * 
439
   * @throws InvalidRequest
440
   * @throws ServiceFailure
441
   * @throws NotFound
442
   * @throws InsufficientResources
443
   * @throws NotImplemented
444
   */
445
  @Override
446
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
447
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
448
    NotImplemented {
449
     
450
      return ObjectFormatService.getInstance().getFormat(fmtid);
451
      
452
  }
453

    
454
  /**
455
   * Returns a list of all object formats registered in the DataONE Object 
456
   * Format Vocabulary
457
    * 
458
   * @return objectFormatList - The list of object formats registered in 
459
   *                            the DataONE Object Format Vocabulary
460
   * 
461
   * @throws InvalidRequest
462
   * @throws ServiceFailure
463
   * @throws NotImplemented
464
   * @throws NotFound
465
   * @throws InsufficientResources
466
   */
467
  @Override
468
  public ObjectFormatList listFormats() 
469
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
470
    NotImplemented {
471

    
472
    return ObjectFormatService.getInstance().listFormats();
473
  }
474

    
475
  /**
476
   * Returns a list of nodes that have been registered with the DataONE infrastructure
477
    * 
478
   * @return nodeList - List of nodes from the registry
479
   * 
480
   * @throws ServiceFailure
481
   * @throws NotImplemented
482
   */
483
  @Override
484
  public NodeList listNodes() 
485
    throws NotImplemented, ServiceFailure {
486

    
487
    throw new NotImplemented("4800", "listNodes not implemented");
488
  }
489

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

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

    
528
        logMetacat.debug("Checking if identifier exists...");
529
        // Check that the identifier does not already exist
530
        if (IdentifierManager.getInstance().identifierExists(guid.getValue())) {
531
            throw new InvalidRequest("4863", 
532
                "GUID is already in use by an existing object.");
533
      
534
        }
535

    
536
        // insert the system metadata into the object store
537
        logMetacat.debug("Starting to insert SystemMetadata...");
538
        sysmeta.setDateSysMetadataModified(new Date());
539
        try {
540
          IdentifierManager.getInstance().createSystemMetadata(sysmeta);
541
          // force replication of this record
542
          ForceReplicationSystemMetadataHandler forceReplication = 
543
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
544
        } catch (Exception e) {
545
            throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
546
        }
547
        
548
        logMetacat.debug("Returning from registerSystemMetadata");
549
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
550
        return guid;
551
  }
552

    
553
  /**
554
   * Provides a mechanism for updating system metadata independently of its 
555
   * associated object
556
    * 
557
   * @param session - the Session object containing the credentials for the Subject
558
   * @param pid - The identifier of the system metadata
559
   * @param sysmeta - The system metadata to be registered
560
   * 
561
   * @return true if the update succeeds
562
   * 
563
   * @throws NotImplemented
564
   * @throws NotAuthorized
565
   * @throws ServiceFailure
566
   * @throws InvalidRequest
567
   * @throws InvalidSystemMetadata
568
   * @throws NotFound
569
   */
570
  @Override
571
  public boolean updateSystemMetadata(Session session, Identifier guid,
572
    SystemMetadata sysmeta) 
573
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
574
    InvalidSystemMetadata, NotFound {
575

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

    
592
        logMetacat.debug("Checking if identifier exists...");
593
        // Check that the identifier exists
594
        if (!IdentifierManager.getInstance().identifierExists(guid.getValue())) {
595
            throw new NotFound("000", 
596
                "GUID does not exist");
597
        }
598

    
599
        // update the system metadata into the object store
600
        logMetacat.debug("Starting to update SystemMetadata...");
601
        sysmeta.setDateSysMetadataModified(new Date());
602
        try {
603
          IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
604
          // force replication of this record
605
          ForceReplicationSystemMetadataHandler forceReplication = 
606
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
607
        } catch (Exception e) {
608
            throw new ServiceFailure("4862", "Error updating system metadata: " + e.getClass() + ": " + e.getMessage());
609
        }
610
        
611
        logMetacat.debug("Returning from updateSystemMetadata");
612
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "updateSystemMetadata");
613
        return true;
614
  }
615
  
616
  /**
617
   * Given an optional scope and format, reserves and returns an identifier 
618
   * within that scope and format that is unique and will not be 
619
   * used by any other sessions. 
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 scope - An optional string to be used to qualify the scope of 
624
   *                the identifier namespace, which is applied differently 
625
   *                depending on the format requested. If scope is not 
626
   *                supplied, a default scope will be used.
627
   * @param format - The optional name of the identifier format to be used, 
628
   *                  drawn from a DataONE-specific vocabulary of identifier 
629
   *                 format names, including several common syntaxes such 
630
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
631
   *                 format is not supplied by the caller, the CN service 
632
   *                 will use a default identifier format, which may change 
633
   *                 over time.
634
   * 
635
   * @return true if the registration succeeds
636
   * 
637
   * @throws InvalidToken
638
   * @throws ServiceFailure
639
   * @throws NotAuthorized
640
   * @throws IdentifierNotUnique
641
   * @throws NotImplemented
642
   */
643
  @Override
644
  public boolean reserveIdentifier(Session session, Identifier pid)
645
  throws InvalidToken, ServiceFailure,
646
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
647

    
648
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
649
  }
650
  
651
  @Override
652
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
653
  throws InvalidToken, ServiceFailure,
654
        NotAuthorized, NotImplemented, InvalidRequest {
655
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
656
  }
657
  
658
  /**
659
    * Checks whether the pid is reserved by the subject in the session param
660
    * If the reservation is held on the pid by the subject, we return true.
661
    * 
662
   * @param session - the Session object containing the Subject
663
   * @param pid - The identifier to check
664
   * 
665
   * @return true if the reservation exists for the subject/pid
666
   * 
667
   * @throws InvalidToken
668
   * @throws ServiceFailure
669
   * @throws NotFound - when the pid is not found (in use or in reservation)
670
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
671
   * @throws IdentifierNotUnique - when the pid is in use
672
   * @throws NotImplemented
673
   */
674

    
675
  @Override
676
  public boolean hasReservation(Session session, Identifier pid) 
677
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
678
      NotImplemented, InvalidRequest {
679
  
680
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
681
  }
682

    
683
  /**
684
   * Changes ownership (RightsHolder) of the specified object to the 
685
   * subject specified by userId
686
    * 
687
   * @param session - the Session object containing the credentials for the Subject
688
   * @param pid - Identifier of the object to be modified
689
   * @param userId - The subject that will be taking ownership of the specified object.
690
   *
691
   * @return pid - the identifier of the modified object
692
   * 
693
   * @throws ServiceFailure
694
   * @throws InvalidToken
695
   * @throws NotFound
696
   * @throws NotAuthorized
697
   * @throws NotImplemented
698
   * @throws InvalidRequest
699
   */  
700
  @Override
701
  public Identifier setOwner(Session session, Identifier pid, Subject userId)
702
    throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
703
    NotImplemented, InvalidRequest {
704
    
705
    // get the subject
706
    Subject subject = session.getSubject();
707
    // get the system metadata
708
    String guid = pid.getValue();
709
    
710
    // are we allowed to do this?
711
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
712
      throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);  
713
    }
714
    
715
    SystemMetadata systemMetadata = null;
716
    try {
717
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
718
    } catch (McdbDocNotFoundException e) {
719
      throw new NotFound("4460", "No record found for: " + guid);
720
    }
721
        
722
    // set the new rights holder
723
    systemMetadata.setRightsHolder(userId);
724
    
725
    // update the metadata
726
    try {
727
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
728
    } catch (McdbDocNotFoundException e) {
729
      throw new ServiceFailure("4490", e.getMessage());
730
    }
731

    
732
    return pid;
733
  }
734

    
735
  /**
736
   * Verify that a replication task is authorized by comparing the target node's
737
   * Subject (from the X.509 certificate-derived Session) with the list of 
738
   * subjects in the known, pending replication tasks map.
739
   * 
740
   * @param originatingNodeSession - Session information that contains the 
741
   *                                 identity of the calling user
742
   * @param targetNodeSubject - Subject identifying the target node
743
   * @param pid - the identifier of the object to be replicated
744
   * @param replicatePermission - the execute permission to be granted
745
   * 
746
   * @throws ServiceFailure
747
   * @throws NotImplemented
748
   * @throws InvalidToken
749
   * @throws NotAuthorized
750
   * @throws InvalidRequest
751
   * @throws NotFound
752
   */
753
  @Override
754
  public boolean isNodeAuthorized(Session originatingNodeSession, 
755
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
756
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
757
    NotFound, InvalidRequest {
758

    
759
	// build a predicate like: 
760
	    // "pid                    = '{pid}                   ' AND 
761
	    //  pemission              = '{permission}            ' AND
762
	    //  originatingNodeSubject = '{originatingNodeSubject}' AND
763
	    //  targetNodeSubject      = '{targetNodeSubject}     '"
764
	    boolean isAllowed = false;
765
	    String query = "";
766
	    query += "pid = '";
767
	    query += pid;
768
	    query += "' AND permission = '";
769
	    query += replicatePermission.name();
770
	    query += "' AND originatingNodeSubject = '";
771
	    query += originatingNodeSession.getSubject().getValue();
772
	    query += "' AND targetNodeSubject = '";
773
	    query += targetNodeSubject.getValue();
774
	    query += "'";
775
	    
776
	    logMetacat.debug("Pending replication task query is: " + query);
777
	    // search the hzPendingReplicationTasks map for the  originating node subject, 
778
	    // target node subject, pid, and replicate permission
779
	    
780
	    Set<CNReplicationTask> tasks = 
781
	      (Set<CNReplicationTask>) HazelcastService.getInstance().getPendingReplicationTasks().values(new SqlPredicate(query));
782
	    
783
	    // do we have a matching task?
784
	    if ( tasks.size() >= 1 ) {
785
	      isAllowed = true;
786
	      
787
	    }
788
	    
789
	    return isAllowed;
790
    
791
  }
792

    
793
}
(2-2/7)