Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000-2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author:  $'
7
 *     '$Date:  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23

    
24
package edu.ucsb.nceas.metacat.dataone;
25

    
26
import java.io.IOException;
27
import java.io.InputStream;
28
import java.math.BigInteger;
29
import java.util.Calendar;
30
import java.util.Date;
31
import java.util.List;
32
import java.util.Set;
33

    
34
import javax.servlet.http.HttpServletRequest;
35

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

    
74
import com.hazelcast.query.SqlPredicate;
75

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

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

    
91
  /* the logger instance */
92
  private Logger logMetacat = null;
93

    
94
  /**
95
   * singleton accessor
96
   */
97
  public static CNodeService getInstance(HttpServletRequest request) { 
98
    return new CNodeService(request);
99
  }
100
  
101
  /**
102
   * Constructor, private for singleton access
103
   */
104
  private CNodeService(HttpServletRequest request) {
105
    super(request);
106
    logMetacat = Logger.getLogger(CNodeService.class);
107
        
108
  }
109
    
110
  /**
111
   * Set the replication policy for an object given the object identifier
112
   * 
113
   * @param session - the Session object containing the credentials for the Subject
114
   * @param pid - the object identifier for the given object
115
   * @param policy - the replication policy to be applied
116
   * 
117
   * @return true or false
118
   * 
119
   * @throws NotImplemented
120
   * @throws NotAuthorized
121
   * @throws ServiceFailure
122
   * @throws InvalidRequest
123
   * 
124
   */
125
  @Override
126
  public boolean setReplicationPolicy(Session session, Identifier pid,
127
      ReplicationPolicy policy) 
128
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
129
      InvalidRequest, InvalidToken {
130
      
131
      // get the subject
132
      Subject subject = session.getSubject();
133
      
134
      // are we allowed to do this?
135
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
136
        throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + 
137
            " not allowed by " + subject.getValue() + " on " + pid.getValue());  
138
      }
139
      
140
      SystemMetadata systemMetadata = null;
141
      try {
142
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
143
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
144
        
145
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
146
        throw new NotFound("4884", "No record found for: " + pid.getValue());
147
        
148
      }
149
          
150
      // set the new policy
151
      systemMetadata.setReplicationPolicy(policy);
152
      
153
      // update the metadata
154
      try {
155
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
156
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
157
	      HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
158
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
159
	      
160
      } catch (Exception e) {
161
		      throw new ServiceFailure("4882", e.getMessage());
162
		  
163
	    } finally {
164
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
165
	        
166
	    }
167
    
168
      return true;
169
  }
170

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

    
194
    // get the subject
195
    Subject subject = session.getSubject();
196
    
197
    // are we allowed to do this?
198
    if (!isAuthorized(session, pid, Permission.WRITE)) {
199
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + 
200
          subject.getValue() + " on " + pid.getValue());  
201
    }
202
    
203
    SystemMetadata systemMetadata = null;
204
    try {      
205
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
206
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
207

    
208
    } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
209
      throw new NotFound("4740", "No record found for: " + pid.getValue() +
210
          " : " + e.getMessage());
211
      
212
    }
213
        
214
    // set the status for the replica
215
    List<Replica> replicas = systemMetadata.getReplicaList();
216
    for (Replica replica: replicas) {
217
        if (replica.getReplicaMemberNode().getValue().equals(targetNode.getValue())) {
218
            replica.setReplicationStatus(status);
219
            
220
        }
221
    }
222
    
223
    // [re]set the list -- redundant?
224
    systemMetadata.setReplicaList(replicas);
225
    
226
    // update the metadata
227
    try {
228
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
229
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
230
	      HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
231
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
232
	    
233
    } catch (Exception e) {
234
		    throw new ServiceFailure("4700", e.getMessage());
235
		
236
	  } finally {
237
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
238
	      
239
	  }
240
	
241
    return true;
242
  }
243

    
244
  /**
245
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
246
   * 
247
   * @param session - the Session object containing the credentials for the Subject
248
   * @param node - the node information for the given node be modified
249
   * 
250
   * @return true if the relationship exists
251
   * 
252
   * @throws InvalidToken
253
   * @throws ServiceFailure
254
   * @throws NotAuthorized
255
   * @throws NotFound
256
   * @throws InvalidRequest
257
   * @throws NotImplemented
258
   */
259
  @Override
260
  public boolean assertRelation(Session session, Identifier pidOfSubject, 
261
    String relationship, Identifier pidOfObject) 
262
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
263
    InvalidRequest, NotImplemented {
264
    
265
    boolean asserted = false;
266
        
267
    // are we allowed to do this?
268
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
269
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());  
270
    }
271
    
272
    SystemMetadata systemMetadata = null;
273
    try {
274
        HazelcastService.getInstance().getSystemMetadataMap().lock(pidOfSubject);
275
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
276
        
277
        
278
        // check relationships
279
        // TODO: use ORE map
280
        if (relationship.equalsIgnoreCase("describes")) {
281
          
282
        }
283
        
284
        if (relationship.equalsIgnoreCase("describedBy")) {
285
          
286
        }
287
        
288
        if (relationship.equalsIgnoreCase("derivedFrom")) {
289
          
290
        }
291
        
292
        if (relationship.equalsIgnoreCase("obsoletes")) {
293
            Identifier pid = systemMetadata.getObsoletes();
294
            if (pid.getValue().equals(pidOfObject.getValue())) {
295
              asserted = true;
296
            
297
        }
298
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
299
        }
300
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
301
            Identifier pid = systemMetadata.getObsoletedBy();
302
            if (pid.getValue().equals(pidOfObject.getValue())) {
303
              asserted = true;
304
            
305
        }
306
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
307
        }
308
        
309
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
310
      
311
    } catch (Exception e) {
312
        throw new ServiceFailure("4270", "Could not assert relation for: " + 
313
            pidOfSubject.getValue() +
314
            ". The error message was: " + e.getMessage());
315
      
316
    } finally {
317
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
318

    
319
    }
320
        
321
    return asserted;
322
  }
323
  
324
  /**
325
   * Return the checksum of the object given the identifier 
326
   * 
327
   * @param session - the Session object containing the credentials for the Subject
328
   * @param pid - the object identifier for the given object
329
   * 
330
   * @return checksum - the checksum of the object
331
   * 
332
   * @throws InvalidToken
333
   * @throws ServiceFailure
334
   * @throws NotAuthorized
335
   * @throws NotFound
336
   * @throws InvalidRequest
337
   * @throws NotImplemented
338
   */
339
  @Override
340
  public Checksum getChecksum(Session session, Identifier pid)
341
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
342
    InvalidRequest, NotImplemented {
343
        
344
    if (!isAuthorized(session, pid, Permission.READ)) {
345
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
346
    }
347
    
348
    SystemMetadata systemMetadata = null;
349
    Checksum checksum = null;
350
    
351
    try {
352
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
353
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
354
        checksum = systemMetadata.getChecksum();
355
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
356

    
357
    } catch (Exception e) {
358
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
359
            pid.getValue() + ". The error message was: " + e.getMessage());
360
      
361
    } finally {
362
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
363
        
364
    }
365
    
366
    return checksum;
367
  }
368

    
369
  /**
370
   * Resolve the location of a given object
371
   * 
372
   * @param session - the Session object containing the credentials for the Subject
373
   * @param pid - the object identifier for the given object
374
   * 
375
   * @return objectLocationList - the list of nodes known to contain the object
376
   * 
377
   * @throws InvalidRequest
378
   * @throws InvalidToken
379
   * @throws ServiceFailure
380
   * @throws NotAuthorized
381
   * @throws NotFound
382
   * @throws NotImplemented
383
   */
384
  @Override
385
  public ObjectLocationList resolve(Session session, Identifier pid)
386
    throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
387
    NotFound, NotImplemented {
388

    
389
    throw new NotImplemented("4131", "resolve not implemented");
390

    
391
  }
392

    
393
  /**
394
   * Search the metadata catalog for identifiers that match the criteria
395
   * 
396
   * @param session - the Session object containing the credentials for the Subject
397
   * @param queryType - An identifier for the type of query expression 
398
   *                    provided in the query
399
   * @param query -  The criteria for matching the characteristics of the 
400
   *                 metadata objects of interest
401
   * 
402
   * @return objectList - the list of objects matching the criteria
403
   * 
404
   * @throws InvalidToken
405
   * @throws ServiceFailure
406
   * @throws NotAuthorized
407
   * @throws InvalidRequest
408
   * @throws NotImplemented
409
   */
410
  @Override
411
  public ObjectList search(Session session, String queryType, String query)
412
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
413
    NotImplemented {
414

    
415
    ObjectList objectList = null;
416
    try {
417
        objectList = 
418
          IdentifierManager.getInstance().querySystemMetadata(
419
              null, //startTime, 
420
              null, //endTime,
421
              null, //objectFormat, 
422
              false, //replicaStatus, 
423
              0, //start, 
424
              -1 //count
425
              );
426
        
427
    } catch (Exception e) {
428
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
429
    }
430

    
431
      return objectList;
432
      
433
    //throw new NotImplemented("4281", "search not implemented");
434
    
435
    // the code block below is from an older implementation
436
    
437
    /*  This block commented out because of the EcoGrid circular dependency.
438
         *  For now, query will not be supported until the circularity can be
439
         *  resolved, probably by moving the ecogrid query syntax transformers
440
         *  directly into the Metacat codebase.  MBJ 2010-02-03
441
         
442
        try {
443
            EcogridQueryParser parser = new EcogridQueryParser(request
444
                    .getReader());
445
            parser.parseXML();
446
            QueryType queryType = parser.getEcogridQuery();
447
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
448
                new EcogridJavaToMetacatJavaQueryTransformer();
449
            QuerySpecification metacatQuery = queryTransformer
450
                    .transform(queryType);
451

    
452
            DBQuery metacat = new DBQuery();
453

    
454
            boolean useXMLIndex = (new Boolean(PropertyService
455
                    .getProperty("database.usexmlindex"))).booleanValue();
456
            String xmlquery = "query"; // we don't care the query in resultset,
457
            // the query can be anything
458
            PrintWriter out = null; // we don't want metacat result, so set out null
459

    
460
            // parameter: queryspecification, user, group, usingIndexOrNot
461
            StringBuffer result = metacat.createResultDocument(xmlquery,
462
                    metacatQuery, out, username, groupNames, useXMLIndex);
463

    
464
            // create result set transfer       
465
            String saxparser = PropertyService.getProperty("xml.saxparser");
466
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
467
                    new StringReader(result.toString()), saxparser, queryType
468
                            .getNamespace().get_value());
469
            ResultsetType records = metacatResultsetParser.getEcogridResult();
470

    
471
            System.out
472
                    .println(EcogridResultsetTransformer.toXMLString(records));
473
            response.setContentType("text/xml");
474
            out = response.getWriter();
475
            out.print(EcogridResultsetTransformer.toXMLString(records));
476

    
477
        } catch (Exception e) {
478
            e.printStackTrace();
479
        }*/
480
    
481

    
482
  }
483
  
484
  /**
485
   * Returns the object format registered in the DataONE Object Format 
486
   * Vocabulary for the given format identifier
487
   * 
488
   * @param fmtid - the identifier of the format requested
489
   * 
490
   * @return objectFormat - the object format requested
491
   * 
492
   * @throws InvalidRequest
493
   * @throws ServiceFailure
494
   * @throws NotFound
495
   * @throws InsufficientResources
496
   * @throws NotImplemented
497
   */
498
  @Override
499
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
500
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
501
    NotImplemented {
502
     
503
      return ObjectFormatService.getInstance().getFormat(fmtid);
504
      
505
  }
506

    
507
  /**
508
   * Returns a list of all object formats registered in the DataONE Object 
509
   * Format Vocabulary
510
    * 
511
   * @return objectFormatList - The list of object formats registered in 
512
   *                            the DataONE Object Format Vocabulary
513
   * 
514
   * @throws InvalidRequest
515
   * @throws ServiceFailure
516
   * @throws NotImplemented
517
   * @throws NotFound
518
   * @throws InsufficientResources
519
   */
520
  @Override
521
  public ObjectFormatList listFormats() 
522
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, 
523
    NotImplemented {
524

    
525
    return ObjectFormatService.getInstance().listFormats();
526
  }
527

    
528
  /**
529
   * Returns a list of nodes that have been registered with the DataONE infrastructure
530
    * 
531
   * @return nodeList - List of nodes from the registry
532
   * 
533
   * @throws ServiceFailure
534
   * @throws NotImplemented
535
   */
536
  @Override
537
  public NodeList listNodes() 
538
    throws NotImplemented, ServiceFailure {
539

    
540
    throw new NotImplemented("4800", "listNodes not implemented");
541
  }
542

    
543
  /**
544
   * Provides a mechanism for adding system metadata independently of its 
545
   * associated object, such as when adding system metadata for data objects.
546
    * 
547
   * @param session - the Session object containing the credentials for the Subject
548
   * @param pid - The identifier of the object to register the system metadata against
549
   * @param sysmeta - The system metadata to be registered
550
   * 
551
   * @return true if the registration succeeds
552
   * 
553
   * @throws NotImplemented
554
   * @throws NotAuthorized
555
   * @throws ServiceFailure
556
   * @throws InvalidRequest
557
   * @throws InvalidSystemMetadata
558
   */
559
  @Override
560
  public Identifier registerSystemMetadata(Session session, Identifier guid,
561
    SystemMetadata sysmeta) 
562
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
563
    InvalidSystemMetadata {
564

    
565
    // TODO: control who can call this?
566
        if (session == null) {
567
            //TODO: many of the thrown exceptions do not use the correct error codes
568
            //check these against the docs and correct them
569
            throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
570
                    "  If you are not logged in, please do so and retry the request.");
571
        }
572
        
573
        // verify that guid == SystemMetadata.getIdentifier()
574
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + 
575
            "|" + sysmeta.getIdentifier().getValue());
576
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
577
            throw new InvalidRequest("4863", 
578
                "The identifier in method call (" + guid.getValue() + 
579
                ") does not match identifier in system metadata (" +
580
                sysmeta.getIdentifier().getValue() + ").");
581
        }
582

    
583
        logMetacat.debug("Checking if identifier exists...");
584
        // Check that the identifier does not already exist
585
        if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(guid)) {
586
            throw new InvalidRequest("4863", 
587
                "The identifier is already in use by an existing object.");
588
      
589
        }
590
        
591
        // insert the system metadata into the object store
592
        logMetacat.debug("Starting to insert SystemMetadata...");
593
        try {
594
            HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
595
            sysmeta.setSerialVersion(BigInteger.ONE);
596
            sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
597
            HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
598
            HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
599
            
600
        } catch (Exception e) {
601
        	logMetacat.error("Problem registering system metadata: " + guid.getValue(), e);
602
            throw new ServiceFailure("4862", "Error inserting system metadata: " + 
603
                e.getClass() + ": " + e.getMessage());
604
            
605
        } finally {
606
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
607

    
608
        }
609
        
610
        logMetacat.debug("Returning from registerSystemMetadata");
611
        EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
612
        return guid;
613
  }
614

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

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

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

    
663
      // update the system metadata into the object store
664
      logMetacat.debug("Starting to update SystemMetadata...");
665
      
666
      // update system metadata
667
      try {
668
    	    HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
669
          SystemMetadata currentSysMeta = 
670
              HazelcastService.getInstance().getSystemMetadataMap().get(sysmeta.getIdentifier());
671
          
672
          // only update if the requester has the most current system metadata
673
          if (sysmeta.getSerialVersion().compareTo(currentSysMeta.getSerialVersion()) != 0 ) {
674
              String msg = "The serial version of the system metadata " + 
675
                  "to be updated does not equal the serial version of the current " +
676
                  "system metadata for identifier " + guid + ". Ensure that the " +
677
                  "copy is the most current before updating.";
678
              logMetacat.warn(msg);
679
              HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
680
              throw new InvalidRequest("4913", msg);
681
              
682
          }
683
          
684
          sysmeta.setSerialVersion(currentSysMeta.getSerialVersion().add(BigInteger.ONE));
685
          sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
686
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
687
    	    HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
688
    	    
689
      } catch (Exception e) {
690
    	throw new ServiceFailure("4852", e.getMessage());
691
    	
692
    	} finally {
693
    	    HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
694
    	  
695
    	}
696
      
697
      logMetacat.debug("Returning from updateSystemMetadata");
698
      EventLog.getInstance().log(request.getRemoteAddr(), 
699
                                 request.getHeader("User-Agent"), 
700
                                 session.getSubject().getValue(), 
701
                                 guid.getValue(), "updateSystemMetadata");
702
      return true;
703
  }
704
  
705
  /**
706
   * Given an optional scope and format, reserves and returns an identifier 
707
   * within that scope and format that is unique and will not be 
708
   * used by any other sessions. 
709
    * 
710
   * @param session - the Session object containing the credentials for the Subject
711
   * @param pid - The identifier of the object to register the system metadata against
712
   * @param scope - An optional string to be used to qualify the scope of 
713
   *                the identifier namespace, which is applied differently 
714
   *                depending on the format requested. If scope is not 
715
   *                supplied, a default scope will be used.
716
   * @param format - The optional name of the identifier format to be used, 
717
   *                  drawn from a DataONE-specific vocabulary of identifier 
718
   *                 format names, including several common syntaxes such 
719
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
720
   *                 format is not supplied by the caller, the CN service 
721
   *                 will use a default identifier format, which may change 
722
   *                 over time.
723
   * 
724
   * @return true if the registration succeeds
725
   * 
726
   * @throws InvalidToken
727
   * @throws ServiceFailure
728
   * @throws NotAuthorized
729
   * @throws IdentifierNotUnique
730
   * @throws NotImplemented
731
   */
732
  @Override
733
  public boolean reserveIdentifier(Session session, Identifier pid)
734
  throws InvalidToken, ServiceFailure,
735
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
736

    
737
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
738
  }
739
  
740
  @Override
741
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
742
  throws InvalidToken, ServiceFailure,
743
        NotAuthorized, NotImplemented, InvalidRequest {
744
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
745
  }
746
  
747
  /**
748
    * Checks whether the pid is reserved by the subject in the session param
749
    * If the reservation is held on the pid by the subject, we return true.
750
    * 
751
   * @param session - the Session object containing the Subject
752
   * @param pid - The identifier to check
753
   * 
754
   * @return true if the reservation exists for the subject/pid
755
   * 
756
   * @throws InvalidToken
757
   * @throws ServiceFailure
758
   * @throws NotFound - when the pid is not found (in use or in reservation)
759
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
760
   * @throws IdentifierNotUnique - when the pid is in use
761
   * @throws NotImplemented
762
   */
763

    
764
  @Override
765
  public boolean hasReservation(Session session, Identifier pid) 
766
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
767
      NotImplemented, InvalidRequest {
768
  
769
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
770
  }
771

    
772
  /**
773
   * Changes ownership (RightsHolder) of the specified object to the 
774
   * subject specified by userId
775
    * 
776
   * @param session - the Session object containing the credentials for the Subject
777
   * @param pid - Identifier of the object to be modified
778
   * @param userId - The subject that will be taking ownership of the specified object.
779
   *
780
   * @return pid - the identifier of the modified object
781
   * 
782
   * @throws ServiceFailure
783
   * @throws InvalidToken
784
   * @throws NotFound
785
   * @throws NotAuthorized
786
   * @throws NotImplemented
787
   * @throws InvalidRequest
788
   */  
789
  @Override
790
  public Identifier setOwner(Session session, Identifier pid, Subject userId)
791
    throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
792
    NotImplemented, InvalidRequest {
793
    
794
    // get the subject
795
    Subject subject = session.getSubject();
796
    
797
    // are we allowed to do this?
798
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
799
      throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + pid.getValue());  
800
    }
801
    
802
    SystemMetadata systemMetadata = null;
803
    try {
804
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
805
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
806
        
807
    } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
808
        throw new NotFound("4460", "No record found for: " + pid.getValue());
809
        
810
    }
811
        
812
    // set the new rights holder
813
    systemMetadata.setRightsHolder(userId);
814
    
815
    // update the metadata
816
    try {
817
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
818
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
819
	      HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
820
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
821
	      
822
    } catch (Exception e) {
823
		throw new ServiceFailure("4490", e.getMessage());
824
		
825
	  } finally {
826
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
827
	  }
828
    
829
    return pid;
830
  }
831

    
832
  /**
833
   * Verify that a replication task is authorized by comparing the target node's
834
   * Subject (from the X.509 certificate-derived Session) with the list of 
835
   * subjects in the known, pending replication tasks map.
836
   * 
837
   * @param originatingNodeSession - Session information that contains the 
838
   *                                 identity of the calling user
839
   * @param targetNodeSubject - Subject identifying the target node
840
   * @param pid - the identifier of the object to be replicated
841
   * @param replicatePermission - the execute permission to be granted
842
   * 
843
   * @throws ServiceFailure
844
   * @throws NotImplemented
845
   * @throws InvalidToken
846
   * @throws NotAuthorized
847
   * @throws InvalidRequest
848
   * @throws NotFound
849
   */
850
  @Override
851
  public boolean isNodeAuthorized(Session originatingNodeSession, 
852
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission) 
853
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
854
    NotFound, InvalidRequest {
855

    
856
	  boolean isAllowed = false;
857
	  SystemMetadata sysmeta = null;
858
    NodeReference targetNode = null;
859
    
860
	  try {
861
	    // get the target node reference from the nodes list
862
	    CNode cn = D1Client.getCN();
863
	    List<Node> nodes = (List<Node>) cn.listNodes();
864

    
865
	    for ( Node node : nodes ) {
866
	        Subject nodeSubject = node.getSubject(0);
867
	        if (nodeSubject.getValue().equals(targetNodeSubject)) {
868
	            targetNode = node.getIdentifier();
869
	            
870
	        }
871
	    }
872
	    
873

    
874
	    //lock, get, and unlock the pid
875
	    HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
876
	    sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
877
	    List<Replica> replicaList = sysmeta.getReplicaList();
878
	    
879
      // find the replica with the status set to 'requested'
880
      for (Replica replica : replicaList) {
881
          ReplicationStatus status = replica.getReplicationStatus();
882
          NodeReference listedNode = replica.getReplicaMemberNode();
883
          if (listedNode.equals(targetNode)
884
                  && status.equals(ReplicationStatus.REQUESTED)) {
885
              isAllowed = true;
886
              break;
887

    
888
          }
889
      }
890
      
891
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
892

    
893
	  } catch(Exception e) {
894
	      // Catch Hazelcast RuntimeExceptions
895
	      throw new ServiceFailure("4872", 
896
	          "Couldn't determine if node is allowed: " + e.getMessage());
897
	    
898
	  } finally {
899
	    // always unlock the pid
900
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
901

    
902
	  }
903
	    
904
	  return isAllowed;
905
    
906
  }
907

    
908
  /**
909
   * Adds a new object to the Node, where the object is either a data 
910
   * object or a science metadata object. This method is called by clients 
911
   * to create new data objects on Member Nodes or internally for Coordinating
912
   * Nodes
913
   * 
914
   * @param session - the Session object containing the credentials for the Subject
915
   * @param pid - The object identifier to be created
916
   * @param object - the object bytes
917
   * @param sysmeta - the system metadata that describes the object  
918
   * 
919
   * @return pid - the object identifier created
920
   * 
921
   * @throws InvalidToken
922
   * @throws ServiceFailure
923
   * @throws NotAuthorized
924
   * @throws IdentifierNotUnique
925
   * @throws UnsupportedType
926
   * @throws InsufficientResources
927
   * @throws InvalidSystemMetadata
928
   * @throws NotImplemented
929
   * @throws InvalidRequest
930
   */
931
  public Identifier create(Session session, Identifier pid, InputStream object,
932
    SystemMetadata sysmeta) 
933
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
934
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
935
    NotImplemented, InvalidRequest {
936
        
937
      return pid;
938

    
939
  }
940

    
941
}
(1-1/4)