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