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

    
29
import org.apache.log4j.Logger;
30
import org.dataone.configuration.Settings;
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.client.HazelcastClient;
63
import com.hazelcast.core.Hazelcast;
64
import com.hazelcast.core.IMap;
65
import com.hazelcast.core.IQueue;
66

    
67
import edu.ucsb.nceas.metacat.EventLog;
68
import edu.ucsb.nceas.metacat.IdentifierManager;
69
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
70
import edu.ucsb.nceas.metacat.properties.PropertyService;
71
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
72
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
73

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

    
84
  /* the instance of the CNodeService object */
85
  private static CNodeService instance = null;
86
  
87
  /* The instance of the Hazelcast client */
88
  private HazelcastClient hzClient;
89

    
90
  /* The name of the DataONE Hazelcast cluster group */
91
  private String groupName;
92

    
93
  /* The name of the DataONE Hazelcast cluster password */
94
  private String groupPassword;
95
  
96
  /* The name of the DataONE Hazelcast cluster IP addresses */
97
  private String addressList;
98
  
99
  /* The name of the node map */
100
  private String nodeMap;
101

    
102
  /* The name of the system metadata map */
103
  private String systemMetadataMap;
104
  
105
  /* The Hazelcast distributed task id generator namespace */
106
  private String taskIds;
107
  
108
  /* The Hazelcast distributed system metadata map */
109
  private IMap<NodeReference, Node> nodes;
110

    
111
  /* The Hazelcast distributed system metadata map */
112
  private IMap<Identifier, SystemMetadata> systemMetadata;
113

    
114
  /* the logger instance */
115
  private Logger logMetacat = null;
116

    
117
  /**
118
   * singleton accessor
119
   */
120
  public static CNodeService getInstance() { 
121
    if (instance == null) {
122
    
123
      instance = new CNodeService();
124
      
125
    }
126
  
127
    return instance;
128
  }
129
  
130
  /**
131
   * Constructor, private for singleton access
132
   */
133
  private CNodeService() {
134
    super();
135
    logMetacat = Logger.getLogger(CNodeService.class);
136
    
137
    // Get configuration properties on instantiation
138
    try {
139
      groupName = 
140
        PropertyService.getProperty("dataone.hazelcast.processCluster.groupName");
141
      groupPassword = 
142
        PropertyService.getProperty("dataone.hazelcast.processCluster.password");
143
      addressList = 
144
        PropertyService.getProperty("dataone.hazelcast.processCluster.instances");
145
      nodeMap = 
146
        PropertyService.getProperty("dataone.hazelcast.processCluster.nodesMap");
147
      systemMetadataMap = 
148
        PropertyService.getProperty("dataone.hazelcast.storageCluster.systemMetadata");
149
      taskIds = 
150
        PropertyService.getProperty("dataone.hazelcast.storageCluster.tasksIdGenerator");
151
      
152
      // Become a DataONE-process cluster client
153
      String[] addresses = addressList.split(",");
154
      hzClient = 
155
        HazelcastClient.newHazelcastClient(this.groupName, this.groupPassword, addresses);
156
      nodes = hzClient.getMap(nodeMap);
157

    
158
      // Get a reference to the shared system metadata map as a cluster member
159
      systemMetadata = Hazelcast.getMap(systemMetadataMap);
160

    
161
    } catch (PropertyNotFoundException e) {
162

    
163
      String msg = "Couldn't find Hazelcast properties for the DataONE clusters. " +
164
        "The error message was: " + e.getMessage();
165
      logMetacat.error(msg);
166
      
167
    }
168

    
169
        
170
  }
171
    
172
  /**
173
   * Set the replication policy for an object given the object identifier
174
   * 
175
   * @param session - the Session object containing the credentials for the Subject
176
   * @param pid - the object identifier for the given object
177
   * @param policy - the replication policy to be applied
178
   * 
179
   * @return true or false
180
   * 
181
   * @throws NotImplemented
182
   * @throws NotAuthorized
183
   * @throws ServiceFailure
184
   * @throws InvalidRequest
185
   * 
186
   */
187
  @Override
188
  public boolean setReplicationPolicy(Session session, Identifier pid,
189
    ReplicationPolicy policy) 
190
    throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, InvalidRequest, InvalidToken {
191

    
192
    // get the subject
193
    Subject subject = session.getSubject();
194
    // get the system metadata
195
    String guid = pid.getValue();
196
    
197
    // are we allowed to do this?
198
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
199
      throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + " not allowed by " + subject.getValue() + " on " + guid);  
200
    }
201
    
202
    SystemMetadata systemMetadata = null;
203
    try {
204
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
205
    } catch (McdbDocNotFoundException e) {
206
      throw new NotFound("4884", "No record found for: " + guid);
207
    }
208
        
209
    // set the new policy
210
    systemMetadata.setReplicationPolicy(policy);
211
    
212
    // update the metadata
213
    try {
214
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
215
    } catch (McdbDocNotFoundException e) {
216
      throw new ServiceFailure("4882", e.getMessage());
217
    }
218

    
219
    return true;
220
  }
221

    
222
  /**
223
   * Set the replication status for an object given the object identifier
224
   * 
225
   * @param session - the Session object containing the credentials for the Subject
226
   * @param pid - the object identifier for the given object
227
   * @param status - the replication status to be applied
228
   * 
229
   * @return true or false
230
   * 
231
   * @throws NotImplemented
232
   * @throws NotAuthorized
233
   * @throws ServiceFailure
234
   * @throws InvalidRequest
235
   * @throws InvalidToken
236
   * @throws NotFound
237
   * 
238
   */
239
  @Override
240
  public boolean setReplicationStatus(Session session, Identifier pid,
241
    NodeReference targetNode, ReplicationStatus status) 
242
    throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
243
    InvalidRequest, NotFound {
244

    
245
    // get the subject
246
    Subject subject = session.getSubject();
247
    // get the system metadata
248
    String guid = pid.getValue();
249
    
250
    // are we allowed to do this?
251
    if (!isAuthorized(session, pid, Permission.WRITE)) {
252
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + subject.getValue() + " on " + guid);  
253
    }
254
    
255
    SystemMetadata systemMetadata = null;
256
    try {
257
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
258
    } catch (McdbDocNotFoundException e) {
259
      throw new NotFound("4740", "No record found for: " + guid);
260
    }
261
        
262
    // set the status for each replica
263
    // TODO: should this method select a certain replica?
264
    List<Replica> replicas = systemMetadata.getReplicaList();
265
    for (Replica replica: replicas) {
266
      replica.setReplicationStatus(status);
267
    }
268
    
269
    // [re]set the list -- redundant?
270
    systemMetadata.setReplicaList(replicas);
271
    
272
    // update the metadata
273
    try {
274
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
275
    } catch (McdbDocNotFoundException e) {
276
      throw new ServiceFailure("4700", e.getMessage());
277
    }
278

    
279
    return true;
280
  }
281

    
282
  /**
283
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
284
   * 
285
   * @param session - the Session object containing the credentials for the Subject
286
   * @param node - the node information for the given node be modified
287
   * 
288
   * @return true if the relationship exists
289
   * 
290
   * @throws InvalidToken
291
   * @throws ServiceFailure
292
   * @throws NotAuthorized
293
   * @throws NotFound
294
   * @throws InvalidRequest
295
   * @throws NotImplemented
296
   */
297
  @Override
298
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
299
    String relationship, Identifier pidOfObject) 
300
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
301
    InvalidRequest, NotImplemented {
302
    
303
    
304
    // get the system metadata
305
    String guid1 = pidOfSubject.getValue();
306
    // are we allowed to do this?
307
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
308
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);  
309
    }
310
    
311
    SystemMetadata systemMetadata = null;
312
    try {
313
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
314
    } catch (McdbDocNotFoundException e) {
315
      throw new NotFound("4884", "No record found for: " + guid1);
316
    }
317
        
318
    // check relationships
319
    // TODO: use ORE map
320
    if (relationship.equalsIgnoreCase("describes")) {
321
      
322
    }
323
    if (relationship.equalsIgnoreCase("describedBy")) {
324
      
325
    }
326
    if (relationship.equalsIgnoreCase("derivedFrom")) {
327
      
328
    }
329
    if (relationship.equalsIgnoreCase("obsoletes")) {
330
      Identifier pid = systemMetadata.getObsoletes();
331
      if (pid.getValue().equals(pidOfObject.getValue())) {
332
        return true;
333
      }
334
      //return systemMetadata.getObsoleteList().contains(pidOfObject);
335
    }
336
    if (relationship.equalsIgnoreCase("obsoletedBy")) {
337
      Identifier pid = systemMetadata.getObsoletedBy();
338
      if (pid.getValue().equals(pidOfObject.getValue())) {
339
        return true;
340
      }
341
      //return systemMetadata.getObsoletedByList().contains(pidOfObject);
342
    }
343

    
344
    return false;
345
  }
346
  
347
  /**
348
   * Return the checksum of the object given the identifier 
349
   * 
350
   * @param session - the Session object containing the credentials for the Subject
351
   * @param pid - the object identifier for the given object
352
   * 
353
   * @return checksum - the checksum of the object
354
   * 
355
   * @throws InvalidToken
356
   * @throws ServiceFailure
357
   * @throws NotAuthorized
358
   * @throws NotFound
359
   * @throws InvalidRequest
360
   * @throws NotImplemented
361
   */
362
  @Override
363
  public Checksum getChecksum(Session session, Identifier pid)
364
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
365
    InvalidRequest, NotImplemented {
366
    
367
    if (!isAuthorized(session, pid, Permission.READ)) {
368
      throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
369
    }
370
    SystemMetadata systemMetadata = null;
371
    try {
372
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
373
    } catch (McdbDocNotFoundException e) {
374
      throw new NotFound("1420", "No record found for: " + pid.getValue());
375
    }
376
    Checksum checksum = systemMetadata.getChecksum();
377
    
378
    return checksum;
379
  }
380

    
381
  /**
382
   * Resolve the location of a given object
383
   * 
384
   * @param session - the Session object containing the credentials for the Subject
385
   * @param pid - the object identifier for the given object
386
   * 
387
   * @return objectLocationList - the list of nodes known to contain the object
388
   * 
389
   * @throws InvalidRequest
390
   * @throws InvalidToken
391
   * @throws ServiceFailure
392
   * @throws NotAuthorized
393
   * @throws NotFound
394
   * @throws NotImplemented
395
   */
396
  @Override
397
  public ObjectLocationList resolve(Session session, Identifier pid)
398
    throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
399
    NotFound, NotImplemented {
400

    
401
    throw new NotImplemented("4131", "resolve not implemented");
402

    
403
  }
404

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

    
427
    ObjectList objectList = null;
428
    try {
429
        objectList = 
430
          IdentifierManager.getInstance().querySystemMetadata(
431
              null, //startTime, 
432
              null, //endTime,
433
              null, //objectFormat, 
434
              false, //replicaStatus, 
435
              0, //start, 
436
              -1 //count
437
              );
438
        
439
    } catch (Exception e) {
440
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
441
    }
442

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

    
464
            DBQuery metacat = new DBQuery();
465

    
466
            boolean useXMLIndex = (new Boolean(PropertyService
467
                    .getProperty("database.usexmlindex"))).booleanValue();
468
            String xmlquery = "query"; // we don't care the query in resultset,
469
            // the query can be anything
470
            PrintWriter out = null; // we don't want metacat result, so set out null
471

    
472
            // parameter: queryspecification, user, group, usingIndexOrNot
473
            StringBuffer result = metacat.createResultDocument(xmlquery,
474
                    metacatQuery, out, username, groupNames, useXMLIndex);
475

    
476
            // create result set transfer       
477
            String saxparser = PropertyService.getProperty("xml.saxparser");
478
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
479
                    new StringReader(result.toString()), saxparser, queryType
480
                            .getNamespace().get_value());
481
            ResultsetType records = metacatResultsetParser.getEcogridResult();
482

    
483
            System.out
484
                    .println(EcogridResultsetTransformer.toXMLString(records));
485
            response.setContentType("text/xml");
486
            out = response.getWriter();
487
            out.print(EcogridResultsetTransformer.toXMLString(records));
488

    
489
        } catch (Exception e) {
490
            e.printStackTrace();
491
        }*/
492
    
493

    
494
  }
495
  
496
  /**
497
   * Returns the object format registered in the DataONE Object Format 
498
   * Vocabulary for the given format identifier
499
   * 
500
   * @param fmtid - the identifier of the format requested
501
   * 
502
   * @return objectFormat - the object format requested
503
   * 
504
   * @throws InvalidRequest
505
   * @throws ServiceFailure
506
   * @throws NotFound
507
   * @throws InsufficientResources
508
   * @throws NotImplemented
509
   */
510
  @Override
511
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
512
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
513
    NotImplemented {
514
     
515
      return ObjectFormatService.getInstance().getFormat(fmtid);
516
      
517
  }
518

    
519
  /**
520
   * Returns a list of all object formats registered in the DataONE Object 
521
   * Format Vocabulary
522
    * 
523
   * @return objectFormatList - The list of object formats registered in 
524
   *                            the DataONE Object Format Vocabulary
525
   * 
526
   * @throws InvalidRequest
527
   * @throws ServiceFailure
528
   * @throws NotImplemented
529
   * @throws NotFound
530
   * @throws InsufficientResources
531
   */
532
  @Override
533
  public ObjectFormatList listFormats() 
534
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
535
    NotImplemented {
536

    
537
    return ObjectFormatService.getInstance().listFormats();
538
  }
539

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

    
552
    throw new NotImplemented("4800", "listNodes not implemented");
553
  }
554

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

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

    
593
        logMetacat.debug("Checking if identifier exists...");
594
        // Check that the identifier does not already exist
595
        if (IdentifierManager.getInstance().identifierExists(guid.getValue())) {
596
            throw new InvalidRequest("4863", 
597
                "GUID is already in use by an existing object.");
598
      
599
        }
600

    
601
        // insert the system metadata into the object store
602
        logMetacat.debug("Starting to insert SystemMetadata...");
603
        sysmeta.setDateSysMetadataModified(new Date());
604
        try {
605
          IdentifierManager.getInstance().createSystemMetadata(sysmeta);
606
          // force replication of this record
607
          ForceReplicationSystemMetadataHandler forceReplication = 
608
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
609
        } catch (Exception e) {
610
            throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
611
        }
612
        
613
        logMetacat.debug("Returning from registerSystemMetadata");
614
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
615
        return guid;
616
  }
617

    
618
  /**
619
   * Provides a mechanism for updating system metadata independently of its 
620
   * associated object
621
    * 
622
   * @param session - the Session object containing the credentials for the Subject
623
   * @param pid - The identifier of the system metadata
624
   * @param sysmeta - The system metadata to be registered
625
   * 
626
   * @return true if the update succeeds
627
   * 
628
   * @throws NotImplemented
629
   * @throws NotAuthorized
630
   * @throws ServiceFailure
631
   * @throws InvalidRequest
632
   * @throws InvalidSystemMetadata
633
   * @throws NotFound
634
   */
635
  @Override
636
  public boolean updateSystemMetadata(Session session, Identifier guid,
637
    SystemMetadata sysmeta) 
638
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
639
    InvalidSystemMetadata, NotFound {
640

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

    
657
        logMetacat.debug("Checking if identifier exists...");
658
        // Check that the identifier exists
659
        if (!IdentifierManager.getInstance().identifierExists(guid.getValue())) {
660
            throw new NotFound("000", 
661
                "GUID does not exist");
662
        }
663

    
664
        // update the system metadata into the object store
665
        logMetacat.debug("Starting to update SystemMetadata...");
666
        sysmeta.setDateSysMetadataModified(new Date());
667
        try {
668
          IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
669
          // force replication of this record
670
          ForceReplicationSystemMetadataHandler forceReplication = 
671
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
672
        } catch (Exception e) {
673
            throw new ServiceFailure("4862", "Error updating system metadata: " + e.getClass() + ": " + e.getMessage());
674
        }
675
        
676
        logMetacat.debug("Returning from updateSystemMetadata");
677
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "updateSystemMetadata");
678
        return true;
679
  }
680
  
681
  /**
682
   * Given an optional scope and format, reserves and returns an identifier 
683
   * within that scope and format that is unique and will not be 
684
   * used by any other sessions. 
685
    * 
686
   * @param session - the Session object containing the credentials for the Subject
687
   * @param pid - The identifier of the object to register the system metadata against
688
   * @param scope - An optional string to be used to qualify the scope of 
689
   *                the identifier namespace, which is applied differently 
690
   *                depending on the format requested. If scope is not 
691
   *                supplied, a default scope will be used.
692
   * @param format - The optional name of the identifier format to be used, 
693
   *                  drawn from a DataONE-specific vocabulary of identifier 
694
   *                 format names, including several common syntaxes such 
695
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
696
   *                 format is not supplied by the caller, the CN service 
697
   *                 will use a default identifier format, which may change 
698
   *                 over time.
699
   * 
700
   * @return true if the registration succeeds
701
   * 
702
   * @throws InvalidToken
703
   * @throws ServiceFailure
704
   * @throws NotAuthorized
705
   * @throws IdentifierNotUnique
706
   * @throws NotImplemented
707
   */
708
  @Override
709
  public boolean reserveIdentifier(Session session, Identifier pid)
710
  throws InvalidToken, ServiceFailure,
711
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
712

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

    
740
  @Override
741
  public boolean hasReservation(Session session, Identifier pid) 
742
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
743
      NotImplemented, InvalidRequest {
744
  
745
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
746
  }
747

    
748
  /**
749
   * Changes ownership (RightsHolder) of the specified object to the 
750
   * subject specified by userId
751
    * 
752
   * @param session - the Session object containing the credentials for the Subject
753
   * @param pid - Identifier of the object to be modified
754
   * @param userId - The subject that will be taking ownership of the specified object.
755
   *
756
   * @return pid - the identifier of the modified object
757
   * 
758
   * @throws ServiceFailure
759
   * @throws InvalidToken
760
   * @throws NotFound
761
   * @throws NotAuthorized
762
   * @throws NotImplemented
763
   * @throws InvalidRequest
764
   */  
765
  @Override
766
  public Identifier setOwner(Session session, Identifier pid, Subject userId)
767
    throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
768
    NotImplemented, InvalidRequest {
769
    
770
    // get the subject
771
    Subject subject = session.getSubject();
772
    // get the system metadata
773
    String guid = pid.getValue();
774
    
775
    // are we allowed to do this?
776
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
777
      throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);  
778
    }
779
    
780
    SystemMetadata systemMetadata = null;
781
    try {
782
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
783
    } catch (McdbDocNotFoundException e) {
784
      throw new NotFound("4460", "No record found for: " + guid);
785
    }
786
        
787
    // set the new rights holder
788
    systemMetadata.setRightsHolder(userId);
789
    
790
    // update the metadata
791
    try {
792
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
793
    } catch (McdbDocNotFoundException e) {
794
      throw new ServiceFailure("4490", e.getMessage());
795
    }
796

    
797
    return pid;
798
  }
799

    
800
  /**
801
   * Verify that a replication task is authorized by comparing the target node's
802
   * Subject (from the X.509 certificate-derived Session) with the list of 
803
   * subjects in the known, pending replication tasks map.
804
   * 
805
   * @param originatingNodeSession - Session information that contains the 
806
   *                                 identity of the calling user
807
   * @param targetNodeSubject - Subject identifying the target node
808
   * @param pid - the identifier of the object to be replicated
809
   * @param replicatePermission - the execute permission to be granted
810
   * 
811
   * @throws ServiceFailure
812
   * @throws NotImplemented
813
   * @throws InvalidToken
814
   * @throws NotAuthorized
815
   * @throws InvalidRequest
816
   * @throws NotFound
817
   */
818
  @Override
819
  public boolean isNodeAuthorized(Session originatingNodeSession, 
820
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
821
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
822
    NotFound, InvalidRequest {
823

    
824
    throw new NotImplemented("4870", "isReplicationAuthorized not implemented");
825
    
826
  }
827

    
828
}
(2-2/7)