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.EntryEvent;
64
import com.hazelcast.core.EntryListener;
65
import com.hazelcast.core.Hazelcast;
66
import com.hazelcast.core.IMap;
67
import com.hazelcast.core.IQueue;
68

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

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

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

    
92
  /* The name of the DataONE Hazelcast cluster group */
93
  private String groupName;
94

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

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

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

    
116
  /* the logger instance */
117
  private Logger logMetacat = null;
118

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

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

    
163
      // Listen for changes to the system metadata map
164
      systemMetadata.addEntryListener(this, true);
165
      
166
    } catch (PropertyNotFoundException e) {
167

    
168
      String msg = "Couldn't find Hazelcast properties for the DataONE clusters. " +
169
        "The error message was: " + e.getMessage();
170
      logMetacat.error(msg);
171
      
172
    }
173

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

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

    
224
    return true;
225
  }
226

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

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

    
284
    return true;
285
  }
286

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

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

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

    
406
    throw new NotImplemented("4131", "resolve not implemented");
407

    
408
  }
409

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

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

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

    
469
            DBQuery metacat = new DBQuery();
470

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

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

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

    
488
            System.out
489
                    .println(EcogridResultsetTransformer.toXMLString(records));
490
            response.setContentType("text/xml");
491
            out = response.getWriter();
492
            out.print(EcogridResultsetTransformer.toXMLString(records));
493

    
494
        } catch (Exception e) {
495
            e.printStackTrace();
496
        }*/
497
    
498

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

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

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

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

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

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

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

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

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

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

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

    
662
        logMetacat.debug("Checking if identifier exists...");
663
        // Check that the identifier exists
664
        if (!IdentifierManager.getInstance().identifierExists(guid.getValue())) {
665
            throw new NotFound("000", 
666
                "GUID does not exist");
667
        }
668

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

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

    
745
  @Override
746
  public boolean hasReservation(Session session, Identifier pid) 
747
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
748
      NotImplemented, InvalidRequest {
749
  
750
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
751
  }
752

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

    
802
    return pid;
803
  }
804

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

    
829
    throw new NotImplemented("4870", "isReplicationAuthorized not implemented");
830
    
831
  }
832

    
833
  /**
834
   * Implement the EntryListener interface for Hazelcast, reponding to entry
835
   * added events in the hzSystemMetadata map.  Evaluate the entry and create
836
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
837
   * 
838
   * @param event - The EntryEvent that occurred
839
   */
840
  @Override
841
  public void entryAdded(EntryEvent<Identifier, SystemMetadata> event) {
842

    
843
    // TODO evaluate the type of system metadata change, decide if it warrants
844
    // a replication event, what type (DATA, METADATA, RESOURCE), 
845
    // iteratively lock the PID, create and submit the tasks, and expect a result back. 
846
    // Deal with exceptions.
847
    
848
  }
849

    
850
  /**
851
   * Implement the EntryListener interface for Hazelcast, reponding to entry
852
   * evicted events in the hzSystemMetadata map.  Evaluate the entry and create
853
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
854
   * 
855
   * @param event - The EntryEvent that occurred
856
   */
857
  @Override
858
  public void entryEvicted(EntryEvent<Identifier, SystemMetadata> event) {
859
    // nothing to do, entries are still in the backing store
860
    
861
  }
862

    
863
  /**
864
   * Implement the EntryListener interface for Hazelcast, reponding to entry
865
   * removed events in the hzSystemMetadata map.  Evaluate the entry and create
866
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
867
   * 
868
   * @param event - The EntryEvent that occurred
869
   */
870
  @Override
871
  public void entryRemoved(EntryEvent<Identifier, SystemMetadata> event) {
872
    // we don't remove objects
873
    
874
  }
875

    
876
  /**
877
   * Implement the EntryListener interface for Hazelcast, reponding to entry
878
   * updated events in the hzSystemMetadata map.  Evaluate the entry and create
879
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
880
   * 
881
   * @param event - The EntryEvent that occurred
882
   */
883
  @Override
884
  public void entryUpdated(EntryEvent<Identifier, SystemMetadata> event) {
885

    
886
    // TODO evaluate the type of system metadata change, decide if it warrants
887
    // a replication event, what type (DATA, METADATA, RESOURCE), 
888
    // iteratively lock the PID, create and submit the tasks, and expect a result back. 
889
    // Deal with exceptions.
890
    
891
  }
892

    
893
}
(2-2/7)