Project

General

Profile

1 6177 cjones
/**
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 6220 leinfelder
import java.util.List;
28 6419 leinfelder
import java.util.Set;
29 6177 cjones
30 6178 cjones
import org.apache.log4j.Logger;
31 6409 cjones
import org.dataone.configuration.Settings;
32 6366 leinfelder
import org.dataone.service.cn.v1.CNAuthorization;
33
import org.dataone.service.cn.v1.CNCore;
34
import org.dataone.service.cn.v1.CNRead;
35
import org.dataone.service.cn.v1.CNReplication;
36 6177 cjones
import org.dataone.service.exceptions.IdentifierNotUnique;
37
import org.dataone.service.exceptions.InsufficientResources;
38
import org.dataone.service.exceptions.InvalidRequest;
39
import org.dataone.service.exceptions.InvalidSystemMetadata;
40
import org.dataone.service.exceptions.InvalidToken;
41
import org.dataone.service.exceptions.NotAuthorized;
42
import org.dataone.service.exceptions.NotFound;
43
import org.dataone.service.exceptions.NotImplemented;
44
import org.dataone.service.exceptions.ServiceFailure;
45 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
46
import org.dataone.service.types.v1.Identifier;
47 6409 cjones
import org.dataone.service.types.v1.Node;
48 6366 leinfelder
import org.dataone.service.types.v1.NodeList;
49 6409 cjones
import org.dataone.service.types.v1.NodeReference;
50 6366 leinfelder
import org.dataone.service.types.v1.ObjectFormat;
51
import org.dataone.service.types.v1.ObjectFormatIdentifier;
52
import org.dataone.service.types.v1.ObjectFormatList;
53
import org.dataone.service.types.v1.ObjectList;
54
import org.dataone.service.types.v1.ObjectLocationList;
55
import org.dataone.service.types.v1.Permission;
56
import org.dataone.service.types.v1.Replica;
57
import org.dataone.service.types.v1.ReplicationPolicy;
58
import org.dataone.service.types.v1.ReplicationStatus;
59
import org.dataone.service.types.v1.Session;
60
import org.dataone.service.types.v1.Subject;
61
import org.dataone.service.types.v1.SystemMetadata;
62 6177 cjones
63 6409 cjones
import com.hazelcast.client.HazelcastClient;
64 6411 cjones
import com.hazelcast.core.EntryEvent;
65
import com.hazelcast.core.EntryListener;
66 6409 cjones
import com.hazelcast.core.Hazelcast;
67
import com.hazelcast.core.IMap;
68
import com.hazelcast.core.IQueue;
69 6419 leinfelder
import com.hazelcast.query.SqlPredicate;
70 6409 cjones
71 6188 leinfelder
import edu.ucsb.nceas.metacat.EventLog;
72
import edu.ucsb.nceas.metacat.IdentifierManager;
73 6194 leinfelder
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
74 6409 cjones
import edu.ucsb.nceas.metacat.properties.PropertyService;
75 6188 leinfelder
import edu.ucsb.nceas.metacat.replication.ForceReplicationSystemMetadataHandler;
76 6409 cjones
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
77 6188 leinfelder
78 6177 cjones
/**
79
 * Represents Metacat's implementation of the DataONE Coordinating Node
80 6179 cjones
 * service API. Methods implement the various CN* interfaces, and methods common
81 6177 cjones
 * to both Member Node and Coordinating Node interfaces are found in the
82
 * D1NodeService super class.
83
 *
84
 */
85
public class CNodeService extends D1NodeService implements CNAuthorization,
86 6411 cjones
    CNCore, CNRead, CNReplication, EntryListener<Identifier, SystemMetadata> {
87 6177 cjones
88 6410 cjones
  /* the instance of the CNodeService object */
89 6178 cjones
  private static CNodeService instance = null;
90
91 6409 cjones
  /* The instance of the Hazelcast client */
92
  private HazelcastClient hzClient;
93
94
  /* The name of the DataONE Hazelcast cluster group */
95
  private String groupName;
96
97
  /* The name of the DataONE Hazelcast cluster password */
98
  private String groupPassword;
99
100
  /* The name of the DataONE Hazelcast cluster IP addresses */
101
  private String addressList;
102
103
  /* The name of the node map */
104
  private String nodeMap;
105
106
  /* The name of the system metadata map */
107
  private String systemMetadataMap;
108
109
  /* The Hazelcast distributed task id generator namespace */
110
  private String taskIds;
111
112 6419 leinfelder
  /* The name of the pending replication tasks map */
113
  private String pendingTasksQueue;
114
115 6409 cjones
  /* The Hazelcast distributed system metadata map */
116
  private IMap<NodeReference, Node> nodes;
117
118
  /* The Hazelcast distributed system metadata map */
119
  private IMap<Identifier, SystemMetadata> systemMetadata;
120 6419 leinfelder
121
  /* The Hazelcast distributed pending replication tasks map*/
122
  private IMap<String, CNReplicationTask> pendingReplicationTasks;
123 6409 cjones
124 6178 cjones
  /* the logger instance */
125
  private Logger logMetacat = null;
126 6177 cjones
127 6178 cjones
  /**
128
   * singleton accessor
129
   */
130 6241 cjones
  public static CNodeService getInstance() {
131 6178 cjones
    if (instance == null) {
132 6241 cjones
133 6254 cjones
      instance = new CNodeService();
134 6241 cjones
135 6178 cjones
    }
136 6241 cjones
137 6178 cjones
    return instance;
138
  }
139
140
  /**
141
   * Constructor, private for singleton access
142
   */
143 6254 cjones
  private CNodeService() {
144 6410 cjones
    super();
145 6178 cjones
    logMetacat = Logger.getLogger(CNodeService.class);
146 6409 cjones
147
    // Get configuration properties on instantiation
148
    try {
149 6410 cjones
      groupName =
150
        PropertyService.getProperty("dataone.hazelcast.processCluster.groupName");
151
      groupPassword =
152
        PropertyService.getProperty("dataone.hazelcast.processCluster.password");
153
      addressList =
154
        PropertyService.getProperty("dataone.hazelcast.processCluster.instances");
155
      nodeMap =
156
        PropertyService.getProperty("dataone.hazelcast.processCluster.nodesMap");
157
      systemMetadataMap =
158 6427 leinfelder
        PropertyService.getProperty("dataone.hazelcast.storageCluster.systemMetadataMap");
159 6419 leinfelder
      pendingTasksQueue =
160
    	PropertyService.getProperty("dataone.hazelcast.replicationPendingTasks");
161 6410 cjones
162
      // Become a DataONE-process cluster client
163 6427 leinfelder
      //TODO: where should this be?
164
//      String[] addresses = addressList.split(",");
165
//      hzClient =
166
//        HazelcastClient.newHazelcastClient(this.groupName, this.groupPassword, addresses);
167
//      nodes = hzClient.getMap(nodeMap);
168
//      pendingReplicationTasks = hzClient.getMap(pendingTasksQueue);
169 6419 leinfelder
170 6409 cjones
      // Get a reference to the shared system metadata map as a cluster member
171 6410 cjones
      systemMetadata = Hazelcast.getMap(systemMetadataMap);
172 6419 leinfelder
173 6411 cjones
      // Listen for changes to the system metadata map
174
      systemMetadata.addEntryListener(this, true);
175
176 6409 cjones
    } catch (PropertyNotFoundException e) {
177
178 6410 cjones
      String msg = "Couldn't find Hazelcast properties for the DataONE clusters. " +
179
        "The error message was: " + e.getMessage();
180
      logMetacat.error(msg);
181
182 6409 cjones
    }
183
184 6178 cjones
185
  }
186
187 6410 cjones
  /**
188
   * Set the replication policy for an object given the object identifier
189
   *
190
   * @param session - the Session object containing the credentials for the Subject
191
   * @param pid - the object identifier for the given object
192
   * @param policy - the replication policy to be applied
193
   *
194
   * @return true or false
195
   *
196
   * @throws NotImplemented
197
   * @throws NotAuthorized
198
   * @throws ServiceFailure
199
   * @throws InvalidRequest
200
   *
201
   */
202
  @Override
203
  public boolean setReplicationPolicy(Session session, Identifier pid,
204
    ReplicationPolicy policy)
205
    throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, InvalidRequest, InvalidToken {
206 6177 cjones
207 6410 cjones
    // get the subject
208
    Subject subject = session.getSubject();
209
    // get the system metadata
210
    String guid = pid.getValue();
211
212
    // are we allowed to do this?
213
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
214
      throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION + " not allowed by " + subject.getValue() + " on " + guid);
215
    }
216
217
    SystemMetadata systemMetadata = null;
218
    try {
219
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
220
    } catch (McdbDocNotFoundException e) {
221
      throw new NotFound("4884", "No record found for: " + guid);
222
    }
223
224
    // set the new policy
225
    systemMetadata.setReplicationPolicy(policy);
226
227
    // update the metadata
228
    try {
229
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
230
    } catch (McdbDocNotFoundException e) {
231
      throw new ServiceFailure("4882", e.getMessage());
232
    }
233 6219 leinfelder
234 6410 cjones
    return true;
235
  }
236 6177 cjones
237 6410 cjones
  /**
238
   * Set the replication status for an object given the object identifier
239
   *
240
   * @param session - the Session object containing the credentials for the Subject
241
   * @param pid - the object identifier for the given object
242
   * @param status - the replication status to be applied
243
   *
244
   * @return true or false
245
   *
246
   * @throws NotImplemented
247
   * @throws NotAuthorized
248
   * @throws ServiceFailure
249
   * @throws InvalidRequest
250
   * @throws InvalidToken
251
   * @throws NotFound
252
   *
253
   */
254
  @Override
255
  public boolean setReplicationStatus(Session session, Identifier pid,
256
    NodeReference targetNode, ReplicationStatus status)
257
    throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized,
258
    InvalidRequest, NotFound {
259 6177 cjones
260 6410 cjones
    // get the subject
261
    Subject subject = session.getSubject();
262
    // get the system metadata
263
    String guid = pid.getValue();
264
265
    // are we allowed to do this?
266
    if (!isAuthorized(session, pid, Permission.WRITE)) {
267
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " + subject.getValue() + " on " + guid);
268
    }
269
270
    SystemMetadata systemMetadata = null;
271
    try {
272
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
273
    } catch (McdbDocNotFoundException e) {
274
      throw new NotFound("4740", "No record found for: " + guid);
275
    }
276
277
    // set the status for each replica
278
    // TODO: should this method select a certain replica?
279
    List<Replica> replicas = systemMetadata.getReplicaList();
280
    for (Replica replica: replicas) {
281
      replica.setReplicationStatus(status);
282
    }
283
284
    // [re]set the list -- redundant?
285
    systemMetadata.setReplicaList(replicas);
286
287
    // update the metadata
288
    try {
289
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
290
    } catch (McdbDocNotFoundException e) {
291
      throw new ServiceFailure("4700", e.getMessage());
292
    }
293 6220 leinfelder
294 6410 cjones
    return true;
295
  }
296 6177 cjones
297 6410 cjones
  /**
298
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
299
   *
300
   * @param session - the Session object containing the credentials for the Subject
301
   * @param node - the node information for the given node be modified
302
   *
303
   * @return true if the relationship exists
304
   *
305
   * @throws InvalidToken
306
   * @throws ServiceFailure
307
   * @throws NotAuthorized
308
   * @throws NotFound
309
   * @throws InvalidRequest
310
   * @throws NotImplemented
311
   */
312
  @Override
313
  public boolean assertRelation(Session session, Identifier pidOfSubject,
314
    String relationship, Identifier pidOfObject)
315
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
316
    InvalidRequest, NotImplemented {
317
318
319
    // get the system metadata
320
    String guid1 = pidOfSubject.getValue();
321
    // are we allowed to do this?
322
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
323
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + guid1);
324
    }
325
326
    SystemMetadata systemMetadata = null;
327
    try {
328
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid1);
329
    } catch (McdbDocNotFoundException e) {
330
      throw new NotFound("4884", "No record found for: " + guid1);
331
    }
332
333
    // check relationships
334
    // TODO: use ORE map
335
    if (relationship.equalsIgnoreCase("describes")) {
336
337
    }
338
    if (relationship.equalsIgnoreCase("describedBy")) {
339
340
    }
341
    if (relationship.equalsIgnoreCase("derivedFrom")) {
342
343
    }
344
    if (relationship.equalsIgnoreCase("obsoletes")) {
345
      Identifier pid = systemMetadata.getObsoletes();
346
      if (pid.getValue().equals(pidOfObject.getValue())) {
347
        return true;
348
      }
349
      //return systemMetadata.getObsoleteList().contains(pidOfObject);
350
    }
351
    if (relationship.equalsIgnoreCase("obsoletedBy")) {
352
      Identifier pid = systemMetadata.getObsoletedBy();
353
      if (pid.getValue().equals(pidOfObject.getValue())) {
354
        return true;
355
      }
356
      //return systemMetadata.getObsoletedByList().contains(pidOfObject);
357
    }
358 6221 leinfelder
359 6410 cjones
    return false;
360
  }
361
362
  /**
363
   * Return the checksum of the object given the identifier
364
   *
365
   * @param session - the Session object containing the credentials for the Subject
366
   * @param pid - the object identifier for the given object
367
   *
368
   * @return checksum - the checksum of the object
369
   *
370
   * @throws InvalidToken
371
   * @throws ServiceFailure
372
   * @throws NotAuthorized
373
   * @throws NotFound
374
   * @throws InvalidRequest
375
   * @throws NotImplemented
376
   */
377
  @Override
378
  public Checksum getChecksum(Session session, Identifier pid)
379
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
380
    InvalidRequest, NotImplemented {
381
382
    if (!isAuthorized(session, pid, Permission.READ)) {
383
      throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());
384
    }
385
    SystemMetadata systemMetadata = null;
386
    try {
387
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
388
    } catch (McdbDocNotFoundException e) {
389
      throw new NotFound("1420", "No record found for: " + pid.getValue());
390
    }
391
    Checksum checksum = systemMetadata.getChecksum();
392
393
    return checksum;
394
  }
395 6177 cjones
396 6410 cjones
  /**
397
   * Resolve the location of a given object
398
   *
399
   * @param session - the Session object containing the credentials for the Subject
400
   * @param pid - the object identifier for the given object
401
   *
402
   * @return objectLocationList - the list of nodes known to contain the object
403
   *
404
   * @throws InvalidRequest
405
   * @throws InvalidToken
406
   * @throws ServiceFailure
407
   * @throws NotAuthorized
408
   * @throws NotFound
409
   * @throws NotImplemented
410
   */
411
  @Override
412
  public ObjectLocationList resolve(Session session, Identifier pid)
413
    throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized,
414
    NotFound, NotImplemented {
415 6177 cjones
416 6410 cjones
    throw new NotImplemented("4131", "resolve not implemented");
417 6303 leinfelder
418 6410 cjones
  }
419 6177 cjones
420 6410 cjones
  /**
421
   * Search the metadata catalog for identifiers that match the criteria
422
   *
423
   * @param session - the Session object containing the credentials for the Subject
424
   * @param queryType - An identifier for the type of query expression
425
   *                    provided in the query
426
   * @param query -  The criteria for matching the characteristics of the
427
   *                 metadata objects of interest
428
   *
429
   * @return objectList - the list of objects matching the criteria
430
   *
431
   * @throws InvalidToken
432
   * @throws ServiceFailure
433
   * @throws NotAuthorized
434
   * @throws InvalidRequest
435
   * @throws NotImplemented
436
   */
437
  @Override
438
  public ObjectList search(Session session, String queryType, String query)
439
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
440
    NotImplemented {
441 6177 cjones
442 6410 cjones
    ObjectList objectList = null;
443
    try {
444
        objectList =
445
          IdentifierManager.getInstance().querySystemMetadata(
446
              null, //startTime,
447
              null, //endTime,
448
              null, //objectFormat,
449
              false, //replicaStatus,
450
              0, //start,
451
              -1 //count
452
              );
453
454
    } catch (Exception e) {
455
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
456
    }
457 6300 leinfelder
458 6410 cjones
      return objectList;
459
460
    //throw new NotImplemented("4281", "search not implemented");
461
462
    // the code block below is from an older implementation
463
464
    /*  This block commented out because of the EcoGrid circular dependency.
465 6281 leinfelder
         *  For now, query will not be supported until the circularity can be
466
         *  resolved, probably by moving the ecogrid query syntax transformers
467
         *  directly into the Metacat codebase.  MBJ 2010-02-03
468
469
        try {
470
            EcogridQueryParser parser = new EcogridQueryParser(request
471
                    .getReader());
472
            parser.parseXML();
473
            QueryType queryType = parser.getEcogridQuery();
474
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer =
475
                new EcogridJavaToMetacatJavaQueryTransformer();
476
            QuerySpecification metacatQuery = queryTransformer
477
                    .transform(queryType);
478 6223 leinfelder
479 6281 leinfelder
            DBQuery metacat = new DBQuery();
480
481
            boolean useXMLIndex = (new Boolean(PropertyService
482
                    .getProperty("database.usexmlindex"))).booleanValue();
483
            String xmlquery = "query"; // we don't care the query in resultset,
484
            // the query can be anything
485
            PrintWriter out = null; // we don't want metacat result, so set out null
486
487
            // parameter: queryspecification, user, group, usingIndexOrNot
488
            StringBuffer result = metacat.createResultDocument(xmlquery,
489
                    metacatQuery, out, username, groupNames, useXMLIndex);
490
491
            // create result set transfer
492
            String saxparser = PropertyService.getProperty("xml.saxparser");
493
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
494
                    new StringReader(result.toString()), saxparser, queryType
495
                            .getNamespace().get_value());
496
            ResultsetType records = metacatResultsetParser.getEcogridResult();
497
498
            System.out
499
                    .println(EcogridResultsetTransformer.toXMLString(records));
500
            response.setContentType("text/xml");
501
            out = response.getWriter();
502
            out.print(EcogridResultsetTransformer.toXMLString(records));
503
504
        } catch (Exception e) {
505
            e.printStackTrace();
506
        }*/
507 6410 cjones
508 6281 leinfelder
509 6410 cjones
  }
510
511
  /**
512
   * Returns the object format registered in the DataONE Object Format
513
   * Vocabulary for the given format identifier
514
   *
515
   * @param fmtid - the identifier of the format requested
516
   *
517
   * @return objectFormat - the object format requested
518
   *
519
   * @throws InvalidRequest
520
   * @throws ServiceFailure
521
   * @throws NotFound
522
   * @throws InsufficientResources
523
   * @throws NotImplemented
524
   */
525
  @Override
526
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
527
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
528
    NotImplemented {
529
530
      return ObjectFormatService.getInstance().getFormat(fmtid);
531
532
  }
533 6177 cjones
534 6410 cjones
  /**
535 6177 cjones
   * Returns a list of all object formats registered in the DataONE Object
536
   * Format Vocabulary
537 6410 cjones
    *
538
   * @return objectFormatList - The list of object formats registered in
539
   *                            the DataONE Object Format Vocabulary
540
   *
541
   * @throws InvalidRequest
542
   * @throws ServiceFailure
543
   * @throws NotImplemented
544
   * @throws NotFound
545
   * @throws InsufficientResources
546
   */
547
  @Override
548
  public ObjectFormatList listFormats()
549
    throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources,
550
    NotImplemented {
551 6177 cjones
552 6410 cjones
    return ObjectFormatService.getInstance().listFormats();
553
  }
554 6177 cjones
555 6410 cjones
  /**
556 6177 cjones
   * Returns a list of nodes that have been registered with the DataONE infrastructure
557 6410 cjones
    *
558
   * @return nodeList - List of nodes from the registry
559
   *
560
   * @throws ServiceFailure
561
   * @throws NotImplemented
562
   */
563
  @Override
564
  public NodeList listNodes()
565
    throws NotImplemented, ServiceFailure {
566 6177 cjones
567 6410 cjones
    throw new NotImplemented("4800", "listNodes not implemented");
568
  }
569 6177 cjones
570 6410 cjones
  /**
571 6177 cjones
   * Provides a mechanism for adding system metadata independently of its
572
   * associated object, such as when adding system metadata for data objects.
573 6410 cjones
    *
574
   * @param session - the Session object containing the credentials for the Subject
575
   * @param pid - The identifier of the object to register the system metadata against
576
   * @param sysmeta - The system metadata to be registered
577
   *
578
   * @return true if the registration succeeds
579
   *
580
   * @throws NotImplemented
581
   * @throws NotAuthorized
582
   * @throws ServiceFailure
583
   * @throws InvalidRequest
584
   * @throws InvalidSystemMetadata
585
   */
586
  @Override
587
  public Identifier registerSystemMetadata(Session session, Identifier guid,
588
    SystemMetadata sysmeta)
589
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
590
    InvalidSystemMetadata {
591 6177 cjones
592 6410 cjones
    // TODO: control who can call this?
593
        if (session == null) {
594
            //TODO: many of the thrown exceptions do not use the correct error codes
595
            //check these against the docs and correct them
596
            throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
597
                    "  If you are not logged in, please do so and retry the request.");
598
        }
599
600
        // verify that guid == SystemMetadata.getIdentifier()
601
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
602
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
603
            throw new InvalidRequest("4863",
604
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
605
                sysmeta.getIdentifier().getValue() + ").");
606
        }
607 6188 leinfelder
608 6410 cjones
        logMetacat.debug("Checking if identifier exists...");
609
        // Check that the identifier does not already exist
610
        if (IdentifierManager.getInstance().identifierExists(guid.getValue())) {
611
            throw new InvalidRequest("4863",
612
                "GUID is already in use by an existing object.");
613
614
        }
615 6188 leinfelder
616 6410 cjones
        // insert the system metadata into the object store
617
        logMetacat.debug("Starting to insert SystemMetadata...");
618
        sysmeta.setDateSysMetadataModified(new Date());
619
        try {
620
          IdentifierManager.getInstance().createSystemMetadata(sysmeta);
621
          // force replication of this record
622
          ForceReplicationSystemMetadataHandler forceReplication =
623
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
624
        } catch (Exception e) {
625
            throw new ServiceFailure("4862", "Error inserting system metadata: " + e.getClass() + ": " + e.getMessage());
626
        }
627
628
        logMetacat.debug("Returning from registerSystemMetadata");
629
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "registerSystemMetadata");
630
        return guid;
631
  }
632 6177 cjones
633 6410 cjones
  /**
634
   * Provides a mechanism for updating system metadata independently of its
635
   * associated object
636
    *
637
   * @param session - the Session object containing the credentials for the Subject
638
   * @param pid - The identifier of the system metadata
639
   * @param sysmeta - The system metadata to be registered
640
   *
641
   * @return true if the update succeeds
642
   *
643
   * @throws NotImplemented
644
   * @throws NotAuthorized
645
   * @throws ServiceFailure
646
   * @throws InvalidRequest
647
   * @throws InvalidSystemMetadata
648
   * @throws NotFound
649
   */
650
  @Override
651
  public boolean updateSystemMetadata(Session session, Identifier guid,
652
    SystemMetadata sysmeta)
653
    throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
654
    InvalidSystemMetadata, NotFound {
655 6378 leinfelder
656 6410 cjones
    // TODO: control who can call this?
657
        if (session == null) {
658
            //TODO: many of the thrown exceptions do not use the correct error codes
659
            //check these against the docs and correct them
660
            throw new NotAuthorized("4861", "No Session - could not authorize for update." +
661
                    "  If you are not logged in, please do so and retry the request.");
662
        }
663
664
        // verify that guid == SystemMetadata.getIdentifier()
665
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
666
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
667
            throw new InvalidRequest("4863",
668
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
669
                sysmeta.getIdentifier().getValue() + ").");
670
        }
671 6378 leinfelder
672 6410 cjones
        logMetacat.debug("Checking if identifier exists...");
673
        // Check that the identifier exists
674
        if (!IdentifierManager.getInstance().identifierExists(guid.getValue())) {
675
            throw new NotFound("000",
676
                "GUID does not exist");
677
        }
678 6378 leinfelder
679 6410 cjones
        // update the system metadata into the object store
680
        logMetacat.debug("Starting to update SystemMetadata...");
681
        sysmeta.setDateSysMetadataModified(new Date());
682
        try {
683
          IdentifierManager.getInstance().updateSystemMetadata(sysmeta);
684
          // force replication of this record
685
          ForceReplicationSystemMetadataHandler forceReplication =
686
            new ForceReplicationSystemMetadataHandler(guid.getValue(), null);
687
        } catch (Exception e) {
688
            throw new ServiceFailure("4862", "Error updating system metadata: " + e.getClass() + ": " + e.getMessage());
689
        }
690
691
        logMetacat.debug("Returning from updateSystemMetadata");
692
        EventLog.getInstance().log(null, session.getSubject().getValue(), guid.getValue(), "updateSystemMetadata");
693
        return true;
694
  }
695
696
  /**
697 6177 cjones
   * Given an optional scope and format, reserves and returns an identifier
698
   * within that scope and format that is unique and will not be
699
   * used by any other sessions.
700 6410 cjones
    *
701
   * @param session - the Session object containing the credentials for the Subject
702
   * @param pid - The identifier of the object to register the system metadata against
703
   * @param scope - An optional string to be used to qualify the scope of
704
   *                the identifier namespace, which is applied differently
705
   *                depending on the format requested. If scope is not
706
   *                supplied, a default scope will be used.
707
   * @param format - The optional name of the identifier format to be used,
708
   *                  drawn from a DataONE-specific vocabulary of identifier
709
   *                 format names, including several common syntaxes such
710
   *                 as DOI, LSID, UUID, and LSRN, among others. If the
711
   *                 format is not supplied by the caller, the CN service
712
   *                 will use a default identifier format, which may change
713
   *                 over time.
714
   *
715
   * @return true if the registration succeeds
716
   *
717
   * @throws InvalidToken
718
   * @throws ServiceFailure
719
   * @throws NotAuthorized
720
   * @throws IdentifierNotUnique
721
   * @throws NotImplemented
722
   */
723
  @Override
724
  public boolean reserveIdentifier(Session session, Identifier pid)
725
  throws InvalidToken, ServiceFailure,
726 6378 leinfelder
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
727 6177 cjones
728 6410 cjones
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
729
  }
730
731
  @Override
732
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
733
  throws InvalidToken, ServiceFailure,
734 6378 leinfelder
        NotAuthorized, NotImplemented, InvalidRequest {
735 6410 cjones
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
736
  }
737
738
  /**
739
    * Checks whether the pid is reserved by the subject in the session param
740
    * If the reservation is held on the pid by the subject, we return true.
741
    *
742
   * @param session - the Session object containing the Subject
743
   * @param pid - The identifier to check
744
   *
745
   * @return true if the reservation exists for the subject/pid
746
   *
747
   * @throws InvalidToken
748
   * @throws ServiceFailure
749
   * @throws NotFound - when the pid is not found (in use or in reservation)
750
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
751
   * @throws IdentifierNotUnique - when the pid is in use
752
   * @throws NotImplemented
753
   */
754 6177 cjones
755 6410 cjones
  @Override
756
  public boolean hasReservation(Session session, Identifier pid)
757
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique,
758
      NotImplemented, InvalidRequest {
759
760
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
761
  }
762 6339 leinfelder
763 6410 cjones
  /**
764 6177 cjones
   * Changes ownership (RightsHolder) of the specified object to the
765
   * subject specified by userId
766 6410 cjones
    *
767
   * @param session - the Session object containing the credentials for the Subject
768
   * @param pid - Identifier of the object to be modified
769
   * @param userId - The subject that will be taking ownership of the specified object.
770
   *
771
   * @return pid - the identifier of the modified object
772
   *
773
   * @throws ServiceFailure
774
   * @throws InvalidToken
775
   * @throws NotFound
776
   * @throws NotAuthorized
777
   * @throws NotImplemented
778
   * @throws InvalidRequest
779
   */
780
  @Override
781
  public Identifier setOwner(Session session, Identifier pid, Subject userId)
782
    throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
783
    NotImplemented, InvalidRequest {
784
785
    // get the subject
786
    Subject subject = session.getSubject();
787
    // get the system metadata
788
    String guid = pid.getValue();
789
790
    // are we allowed to do this?
791
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
792
      throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + guid);
793
    }
794
795
    SystemMetadata systemMetadata = null;
796
    try {
797
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
798
    } catch (McdbDocNotFoundException e) {
799
      throw new NotFound("4460", "No record found for: " + guid);
800
    }
801
802
    // set the new rights holder
803
    systemMetadata.setRightsHolder(userId);
804
805
    // update the metadata
806
    try {
807
      IdentifierManager.getInstance().updateSystemMetadata(systemMetadata);
808
    } catch (McdbDocNotFoundException e) {
809
      throw new ServiceFailure("4490", e.getMessage());
810
    }
811 6177 cjones
812 6410 cjones
    return pid;
813
  }
814 6177 cjones
815 6410 cjones
  /**
816
   * Verify that a replication task is authorized by comparing the target node's
817
   * Subject (from the X.509 certificate-derived Session) with the list of
818
   * subjects in the known, pending replication tasks map.
819
   *
820
   * @param originatingNodeSession - Session information that contains the
821
   *                                 identity of the calling user
822
   * @param targetNodeSubject - Subject identifying the target node
823
   * @param pid - the identifier of the object to be replicated
824
   * @param replicatePermission - the execute permission to be granted
825
   *
826
   * @throws ServiceFailure
827
   * @throws NotImplemented
828
   * @throws InvalidToken
829
   * @throws NotAuthorized
830
   * @throws InvalidRequest
831
   * @throws NotFound
832
   */
833
  @Override
834 6409 cjones
  public boolean isNodeAuthorized(Session originatingNodeSession,
835 6384 cjones
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission)
836 6410 cjones
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
837
    NotFound, InvalidRequest {
838 6384 cjones
839 6419 leinfelder
	// build a predicate like:
840
	    // "pid                    = '{pid}                   ' AND
841
	    //  pemission              = '{permission}            ' AND
842
	    //  originatingNodeSubject = '{originatingNodeSubject}' AND
843
	    //  targetNodeSubject      = '{targetNodeSubject}     '"
844
	    boolean isAllowed = false;
845
	    String query = "";
846
	    query += "pid = '";
847
	    query += pid;
848
	    query += "' AND permission = '";
849
	    query += replicatePermission.name();
850
	    query += "' AND originatingNodeSubject = '";
851
	    query += originatingNodeSession.getSubject().getValue();
852
	    query += "' AND targetNodeSubject = '";
853
	    query += targetNodeSubject.getValue();
854
	    query += "'";
855
856
	    logMetacat.debug("Pending replication task query is: " + query);
857
	    // search the hzPendingReplicationTasks map for the  originating node subject,
858
	    // target node subject, pid, and replicate permission
859
860
	    Set<CNReplicationTask> tasks =
861
	      (Set<CNReplicationTask>) this.pendingReplicationTasks.values(new SqlPredicate(query));
862
863
	    // do we have a matching task?
864
	    if ( tasks.size() >= 1 ) {
865
	      isAllowed = true;
866
867
	    }
868
869
	    return isAllowed;
870 6410 cjones
871 6384 cjones
  }
872
873 6411 cjones
  /**
874
   * Implement the EntryListener interface for Hazelcast, reponding to entry
875
   * added events in the hzSystemMetadata map.  Evaluate the entry and create
876
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
877
   *
878
   * @param event - The EntryEvent that occurred
879
   */
880
  @Override
881
  public void entryAdded(EntryEvent<Identifier, SystemMetadata> event) {
882
883
    // TODO evaluate the type of system metadata change, decide if it warrants
884
    // a replication event, what type (DATA, METADATA, RESOURCE),
885
    // iteratively lock the PID, create and submit the tasks, and expect a result back.
886
    // Deal with exceptions.
887
888
  }
889
890
  /**
891
   * Implement the EntryListener interface for Hazelcast, reponding to entry
892
   * evicted events in the hzSystemMetadata map.  Evaluate the entry and create
893
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
894
   *
895
   * @param event - The EntryEvent that occurred
896
   */
897
  @Override
898
  public void entryEvicted(EntryEvent<Identifier, SystemMetadata> event) {
899
    // nothing to do, entries are still in the backing store
900
901
  }
902
903
  /**
904
   * Implement the EntryListener interface for Hazelcast, reponding to entry
905
   * removed events in the hzSystemMetadata map.  Evaluate the entry and create
906
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
907
   *
908
   * @param event - The EntryEvent that occurred
909
   */
910
  @Override
911
  public void entryRemoved(EntryEvent<Identifier, SystemMetadata> event) {
912
    // we don't remove objects
913
914
  }
915
916
  /**
917
   * Implement the EntryListener interface for Hazelcast, reponding to entry
918
   * updated events in the hzSystemMetadata map.  Evaluate the entry and create
919
   * CNReplicationTasks as appropriate (for DATA, METADATA, RESOURCE)
920
   *
921
   * @param event - The EntryEvent that occurred
922
   */
923
  @Override
924
  public void entryUpdated(EntryEvent<Identifier, SystemMetadata> event) {
925
926
    // TODO evaluate the type of system metadata change, decide if it warrants
927
    // a replication event, what type (DATA, METADATA, RESOURCE),
928
    // iteratively lock the PID, create and submit the tasks, and expect a result back.
929
    // Deal with exceptions.
930
931
  }
932
933 6177 cjones
}