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 6569 cjones
import java.io.IOException;
27
import java.io.InputStream;
28 6567 cjones
import java.math.BigInteger;
29
import java.util.Calendar;
30 6177 cjones
import java.util.Date;
31 6220 leinfelder
import java.util.List;
32 6419 leinfelder
import java.util.Set;
33 6177 cjones
34 6542 leinfelder
import javax.servlet.http.HttpServletRequest;
35
36 6569 cjones
import org.apache.commons.io.IOUtils;
37 6178 cjones
import org.apache.log4j.Logger;
38 6484 cjones
import org.dataone.client.CNode;
39
import org.dataone.client.D1Client;
40 6366 leinfelder
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 6177 cjones
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 6569 cjones
import org.dataone.service.exceptions.UnsupportedType;
54 6571 cjones
import org.dataone.service.types.v1.AccessPolicy;
55 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
56
import org.dataone.service.types.v1.Identifier;
57 6463 cjones
import org.dataone.service.types.v1.Node;
58 6366 leinfelder
import org.dataone.service.types.v1.NodeList;
59 6409 cjones
import org.dataone.service.types.v1.NodeReference;
60 6570 cjones
import org.dataone.service.types.v1.NodeType;
61 6366 leinfelder
import org.dataone.service.types.v1.ObjectFormat;
62
import org.dataone.service.types.v1.ObjectFormatIdentifier;
63
import org.dataone.service.types.v1.ObjectFormatList;
64
import org.dataone.service.types.v1.ObjectList;
65
import org.dataone.service.types.v1.ObjectLocationList;
66
import org.dataone.service.types.v1.Permission;
67
import org.dataone.service.types.v1.Replica;
68
import org.dataone.service.types.v1.ReplicationPolicy;
69
import org.dataone.service.types.v1.ReplicationStatus;
70
import org.dataone.service.types.v1.Session;
71
import org.dataone.service.types.v1.Subject;
72
import org.dataone.service.types.v1.SystemMetadata;
73 6569 cjones
import org.dataone.service.types.v1.util.ChecksumUtil;
74
import org.dataone.service.util.Constants;
75 6177 cjones
76 6419 leinfelder
import com.hazelcast.query.SqlPredicate;
77 6409 cjones
78 6188 leinfelder
import edu.ucsb.nceas.metacat.EventLog;
79
import edu.ucsb.nceas.metacat.IdentifierManager;
80 6194 leinfelder
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
81 6446 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
82 6188 leinfelder
83 6177 cjones
/**
84
 * Represents Metacat's implementation of the DataONE Coordinating Node
85 6179 cjones
 * service API. Methods implement the various CN* interfaces, and methods common
86 6177 cjones
 * to both Member Node and Coordinating Node interfaces are found in the
87
 * D1NodeService super class.
88
 *
89
 */
90
public class CNodeService extends D1NodeService implements CNAuthorization,
91 6446 leinfelder
    CNCore, CNRead, CNReplication {
92 6177 cjones
93 6178 cjones
  /* the logger instance */
94
  private Logger logMetacat = null;
95 6177 cjones
96 6178 cjones
  /**
97
   * singleton accessor
98
   */
99 6542 leinfelder
  public static CNodeService getInstance(HttpServletRequest request) {
100
    return new CNodeService(request);
101 6178 cjones
  }
102
103
  /**
104
   * Constructor, private for singleton access
105
   */
106 6542 leinfelder
  private CNodeService(HttpServletRequest request) {
107
    super(request);
108 6178 cjones
    logMetacat = Logger.getLogger(CNodeService.class);
109
110
  }
111
112 6410 cjones
  /**
113
   * Set the replication policy for an object given the object identifier
114
   *
115
   * @param session - the Session object containing the credentials for the Subject
116
   * @param pid - the object identifier for the given object
117
   * @param policy - the replication policy to be applied
118
   *
119
   * @return true or false
120
   *
121
   * @throws NotImplemented
122
   * @throws NotAuthorized
123
   * @throws ServiceFailure
124
   * @throws InvalidRequest
125
   *
126
   */
127 6471 jones
  @Override
128 6410 cjones
  public boolean setReplicationPolicy(Session session, Identifier pid,
129 6593 cjones
      ReplicationPolicy policy, long serialVersion)
130 6567 cjones
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
131
      InvalidRequest, InvalidToken {
132
133
      // get the subject
134
      Subject subject = session.getSubject();
135
136
      // are we allowed to do this?
137
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
138
        throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION +
139
            " not allowed by " + subject.getValue() + " on " + pid.getValue());
140
      }
141
142
      SystemMetadata systemMetadata = null;
143
      try {
144
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
145
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
146 6410 cjones
147 6593 cjones
148
          // does the request have the most current system metadata?
149
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
150
             String msg = "The requested system metadata version number " +
151
                 serialVersion + "differs from the current version at " +
152
                 systemMetadata.getSerialVersion().longValue() +
153
                 " Please get the latest copy in order to modify it.";
154
             throw new InvalidRequest("4883", msg);
155
          }
156
157 6567 cjones
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
158
        throw new NotFound("4884", "No record found for: " + pid.getValue());
159
160
      }
161
162
      // set the new policy
163
      systemMetadata.setReplicationPolicy(policy);
164
165
      // update the metadata
166
      try {
167
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
168
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
169
	      HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
170
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
171
172
      } catch (Exception e) {
173
		      throw new ServiceFailure("4882", e.getMessage());
174
175
	    } finally {
176
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
177
178
	    }
179 6410 cjones
180 6567 cjones
      return true;
181 6410 cjones
  }
182 6177 cjones
183 6410 cjones
  /**
184
   * Set the replication status for an object given the object identifier
185
   *
186
   * @param session - the Session object containing the credentials for the Subject
187
   * @param pid - the object identifier for the given object
188
   * @param status - the replication status to be applied
189
   *
190
   * @return true or false
191
   *
192
   * @throws NotImplemented
193
   * @throws NotAuthorized
194
   * @throws ServiceFailure
195
   * @throws InvalidRequest
196
   * @throws InvalidToken
197
   * @throws NotFound
198
   *
199
   */
200 6471 jones
  @Override
201 6410 cjones
  public boolean setReplicationStatus(Session session, Identifier pid,
202 6593 cjones
    NodeReference targetNode, ReplicationStatus status, long serialVersion)
203 6410 cjones
    throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized,
204
    InvalidRequest, NotFound {
205 6177 cjones
206 6410 cjones
    // get the subject
207
    Subject subject = session.getSubject();
208
209
    // are we allowed to do this?
210
    if (!isAuthorized(session, pid, Permission.WRITE)) {
211 6567 cjones
      throw new NotAuthorized("4720", Permission.WRITE + " not allowed by " +
212
          subject.getValue() + " on " + pid.getValue());
213 6410 cjones
    }
214
215
    SystemMetadata systemMetadata = null;
216 6567 cjones
    try {
217
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
218
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
219
220 6593 cjones
221
        // does the request have the most current system metadata?
222
        if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
223
           String msg = "The requested system metadata version number " +
224
               serialVersion + "differs from the current version at " +
225
               systemMetadata.getSerialVersion().longValue() +
226
               " Please get the latest copy in order to modify it.";
227
           throw new InvalidRequest("4730", msg);
228
        }
229
230 6567 cjones
    } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
231
      throw new NotFound("4740", "No record found for: " + pid.getValue() +
232
          " : " + e.getMessage());
233
234 6410 cjones
    }
235
236 6567 cjones
    // set the status for the replica
237 6410 cjones
    List<Replica> replicas = systemMetadata.getReplicaList();
238
    for (Replica replica: replicas) {
239 6567 cjones
        if (replica.getReplicaMemberNode().getValue().equals(targetNode.getValue())) {
240
            replica.setReplicationStatus(status);
241
242
        }
243 6410 cjones
    }
244
245
    // [re]set the list -- redundant?
246
    systemMetadata.setReplicaList(replicas);
247
248
    // update the metadata
249 6468 leinfelder
    try {
250 6567 cjones
        systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
251
        systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
252
	      HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
253
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
254
255 6468 leinfelder
    } catch (Exception e) {
256 6567 cjones
		    throw new ServiceFailure("4700", e.getMessage());
257
258
	  } finally {
259
	      HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
260
261
	  }
262 6468 leinfelder
263 6410 cjones
    return true;
264
  }
265 6177 cjones
266 6410 cjones
  /**
267
   * Test that the specified relationship between pidOfSubject and pidOfObject exists
268
   *
269
   * @param session - the Session object containing the credentials for the Subject
270
   * @param node - the node information for the given node be modified
271
   *
272
   * @return true if the relationship exists
273
   *
274
   * @throws InvalidToken
275
   * @throws ServiceFailure
276
   * @throws NotAuthorized
277
   * @throws NotFound
278
   * @throws InvalidRequest
279
   * @throws NotImplemented
280
   */
281 6471 jones
  @Override
282 6410 cjones
  public boolean assertRelation(Session session, Identifier pidOfSubject,
283
    String relationship, Identifier pidOfObject)
284
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
285
    InvalidRequest, NotImplemented {
286
287 6568 cjones
    boolean asserted = false;
288
289 6410 cjones
    // are we allowed to do this?
290
    if (!isAuthorized(session, pidOfSubject, Permission.READ)) {
291 6568 cjones
      throw new NotAuthorized("4881", Permission.READ + " not allowed on " + pidOfSubject.getValue());
292 6410 cjones
    }
293
294
    SystemMetadata systemMetadata = null;
295
    try {
296 6568 cjones
        HazelcastService.getInstance().getSystemMetadataMap().lock(pidOfSubject);
297
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pidOfSubject);
298 6410 cjones
299 6568 cjones
300
        // check relationships
301
        // TODO: use ORE map
302
        if (relationship.equalsIgnoreCase("describes")) {
303
304
        }
305
306
        if (relationship.equalsIgnoreCase("describedBy")) {
307
308
        }
309
310
        if (relationship.equalsIgnoreCase("derivedFrom")) {
311
312
        }
313
314
        if (relationship.equalsIgnoreCase("obsoletes")) {
315
            Identifier pid = systemMetadata.getObsoletes();
316
            if (pid.getValue().equals(pidOfObject.getValue())) {
317
              asserted = true;
318
319
        }
320
          //return systemMetadata.getObsoleteList().contains(pidOfObject);
321
        }
322
        if (relationship.equalsIgnoreCase("obsoletedBy")) {
323
            Identifier pid = systemMetadata.getObsoletedBy();
324
            if (pid.getValue().equals(pidOfObject.getValue())) {
325
              asserted = true;
326
327
        }
328
          //return systemMetadata.getObsoletedByList().contains(pidOfObject);
329
        }
330
331
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
332 6410 cjones
333 6568 cjones
    } catch (Exception e) {
334
        throw new ServiceFailure("4270", "Could not assert relation for: " +
335
            pidOfSubject.getValue() +
336
            ". The error message was: " + e.getMessage());
337 6410 cjones
338 6568 cjones
    } finally {
339
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pidOfSubject);
340
341 6410 cjones
    }
342 6568 cjones
343
    return asserted;
344 6410 cjones
  }
345
346
  /**
347
   * Return the checksum of the object given the identifier
348
   *
349
   * @param session - the Session object containing the credentials for the Subject
350
   * @param pid - the object identifier for the given object
351
   *
352
   * @return checksum - the checksum of the object
353
   *
354
   * @throws InvalidToken
355
   * @throws ServiceFailure
356
   * @throws NotAuthorized
357
   * @throws NotFound
358
   * @throws NotImplemented
359
   */
360 6471 jones
  @Override
361 6410 cjones
  public Checksum getChecksum(Session session, Identifier pid)
362
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
363 6622 leinfelder
    NotImplemented {
364 6568 cjones
365 6410 cjones
    if (!isAuthorized(session, pid, Permission.READ)) {
366 6568 cjones
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());
367 6410 cjones
    }
368 6568 cjones
369 6410 cjones
    SystemMetadata systemMetadata = null;
370 6568 cjones
    Checksum checksum = null;
371
372 6410 cjones
    try {
373 6568 cjones
        HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
374
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
375
        checksum = systemMetadata.getChecksum();
376
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
377
378
    } catch (Exception e) {
379
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " +
380
            pid.getValue() + ". The error message was: " + e.getMessage());
381
382
    } finally {
383
        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
384
385 6410 cjones
    }
386
387
    return checksum;
388
  }
389 6177 cjones
390 6410 cjones
  /**
391
   * Resolve the location of a given object
392
   *
393
   * @param session - the Session object containing the credentials for the Subject
394
   * @param pid - the object identifier for the given object
395
   *
396
   * @return objectLocationList - the list of nodes known to contain the object
397
   *
398
   * @throws InvalidToken
399
   * @throws ServiceFailure
400
   * @throws NotAuthorized
401
   * @throws NotFound
402
   * @throws NotImplemented
403
   */
404 6471 jones
  @Override
405 6410 cjones
  public ObjectLocationList resolve(Session session, Identifier pid)
406 6622 leinfelder
    throws InvalidToken, ServiceFailure, NotAuthorized,
407 6410 cjones
    NotFound, NotImplemented {
408 6177 cjones
409 6410 cjones
    throw new NotImplemented("4131", "resolve not implemented");
410 6303 leinfelder
411 6410 cjones
  }
412 6177 cjones
413 6410 cjones
  /**
414
   * Search the metadata catalog for identifiers that match the criteria
415
   *
416
   * @param session - the Session object containing the credentials for the Subject
417
   * @param queryType - An identifier for the type of query expression
418
   *                    provided in the query
419
   * @param query -  The criteria for matching the characteristics of the
420
   *                 metadata objects of interest
421
   *
422
   * @return objectList - the list of objects matching the criteria
423
   *
424
   * @throws InvalidToken
425
   * @throws ServiceFailure
426
   * @throws NotAuthorized
427
   * @throws InvalidRequest
428
   * @throws NotImplemented
429
   */
430 6471 jones
  @Override
431 6410 cjones
  public ObjectList search(Session session, String queryType, String query)
432
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
433
    NotImplemented {
434 6177 cjones
435 6410 cjones
    ObjectList objectList = null;
436
    try {
437
        objectList =
438
          IdentifierManager.getInstance().querySystemMetadata(
439
              null, //startTime,
440
              null, //endTime,
441
              null, //objectFormat,
442
              false, //replicaStatus,
443
              0, //start,
444
              -1 //count
445
              );
446
447
    } catch (Exception e) {
448
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
449
    }
450 6300 leinfelder
451 6410 cjones
      return objectList;
452
453
    //throw new NotImplemented("4281", "search not implemented");
454
455
    // the code block below is from an older implementation
456
457
    /*  This block commented out because of the EcoGrid circular dependency.
458 6281 leinfelder
         *  For now, query will not be supported until the circularity can be
459
         *  resolved, probably by moving the ecogrid query syntax transformers
460
         *  directly into the Metacat codebase.  MBJ 2010-02-03
461
462
        try {
463
            EcogridQueryParser parser = new EcogridQueryParser(request
464
                    .getReader());
465
            parser.parseXML();
466
            QueryType queryType = parser.getEcogridQuery();
467
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer =
468
                new EcogridJavaToMetacatJavaQueryTransformer();
469
            QuerySpecification metacatQuery = queryTransformer
470
                    .transform(queryType);
471 6223 leinfelder
472 6281 leinfelder
            DBQuery metacat = new DBQuery();
473
474
            boolean useXMLIndex = (new Boolean(PropertyService
475
                    .getProperty("database.usexmlindex"))).booleanValue();
476
            String xmlquery = "query"; // we don't care the query in resultset,
477
            // the query can be anything
478
            PrintWriter out = null; // we don't want metacat result, so set out null
479
480
            // parameter: queryspecification, user, group, usingIndexOrNot
481
            StringBuffer result = metacat.createResultDocument(xmlquery,
482
                    metacatQuery, out, username, groupNames, useXMLIndex);
483
484
            // create result set transfer
485
            String saxparser = PropertyService.getProperty("xml.saxparser");
486
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
487
                    new StringReader(result.toString()), saxparser, queryType
488
                            .getNamespace().get_value());
489
            ResultsetType records = metacatResultsetParser.getEcogridResult();
490
491
            System.out
492
                    .println(EcogridResultsetTransformer.toXMLString(records));
493
            response.setContentType("text/xml");
494
            out = response.getWriter();
495
            out.print(EcogridResultsetTransformer.toXMLString(records));
496
497
        } catch (Exception e) {
498
            e.printStackTrace();
499
        }*/
500 6410 cjones
501 6281 leinfelder
502 6410 cjones
  }
503
504
  /**
505
   * Returns the object format registered in the DataONE Object Format
506
   * Vocabulary for the given format identifier
507
   *
508
   * @param fmtid - the identifier of the format requested
509
   *
510
   * @return objectFormat - the object format requested
511
   *
512
   * @throws ServiceFailure
513
   * @throws NotFound
514
   * @throws InsufficientResources
515
   * @throws NotImplemented
516
   */
517 6471 jones
  @Override
518 6410 cjones
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
519 6622 leinfelder
    throws ServiceFailure, NotFound, InsufficientResources,
520 6410 cjones
    NotImplemented {
521
522
      return ObjectFormatService.getInstance().getFormat(fmtid);
523
524
  }
525 6177 cjones
526 6410 cjones
  /**
527 6177 cjones
   * Returns a list of all object formats registered in the DataONE Object
528
   * Format Vocabulary
529 6410 cjones
    *
530
   * @return objectFormatList - The list of object formats registered in
531
   *                            the DataONE Object Format Vocabulary
532
   *
533
   * @throws ServiceFailure
534
   * @throws NotImplemented
535
   * @throws InsufficientResources
536
   */
537 6471 jones
  @Override
538 6410 cjones
  public ObjectFormatList listFormats()
539 6622 leinfelder
    throws ServiceFailure, InsufficientResources,
540 6410 cjones
    NotImplemented {
541 6177 cjones
542 6410 cjones
    return ObjectFormatService.getInstance().listFormats();
543
  }
544 6177 cjones
545 6410 cjones
  /**
546 6177 cjones
   * Returns a list of nodes that have been registered with the DataONE infrastructure
547 6410 cjones
    *
548
   * @return nodeList - List of nodes from the registry
549
   *
550
   * @throws ServiceFailure
551
   * @throws NotImplemented
552
   */
553 6471 jones
  @Override
554 6410 cjones
  public NodeList listNodes()
555
    throws NotImplemented, ServiceFailure {
556 6177 cjones
557 6410 cjones
    throw new NotImplemented("4800", "listNodes not implemented");
558
  }
559 6177 cjones
560 6410 cjones
  /**
561 6177 cjones
   * Provides a mechanism for adding system metadata independently of its
562
   * associated object, such as when adding system metadata for data objects.
563 6410 cjones
    *
564
   * @param session - the Session object containing the credentials for the Subject
565
   * @param pid - The identifier of the object to register the system metadata against
566
   * @param sysmeta - The system metadata to be registered
567
   *
568
   * @return true if the registration succeeds
569
   *
570
   * @throws NotImplemented
571
   * @throws NotAuthorized
572
   * @throws ServiceFailure
573
   * @throws InvalidRequest
574
   * @throws InvalidSystemMetadata
575
   */
576 6471 jones
  @Override
577 6575 cjones
  public Identifier registerSystemMetadata(Session session, Identifier pid,
578
      SystemMetadata sysmeta)
579
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
580
      InvalidSystemMetadata {
581 6177 cjones
582 6575 cjones
      // TODO: control who can call this?
583
      if (session == null) {
584
          //TODO: many of the thrown exceptions do not use the correct error codes
585
          //check these against the docs and correct them
586
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
587
                  "  If you are not logged in, please do so and retry the request.");
588
      }
589
590
      // verify that guid == SystemMetadata.getIdentifier()
591
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() +
592
          "|" + sysmeta.getIdentifier().getValue());
593
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
594
          throw new InvalidRequest("4863",
595
              "The identifier in method call (" + pid.getValue() +
596
              ") does not match identifier in system metadata (" +
597
              sysmeta.getIdentifier().getValue() + ").");
598
      }
599 6188 leinfelder
600 6575 cjones
      logMetacat.debug("Checking if identifier exists...");
601
      // Check that the identifier does not already exist
602
      if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
603
          throw new InvalidRequest("4863",
604
              "The identifier is already in use by an existing object.");
605 6410 cjones
606 6575 cjones
      }
607
608
      // insert the system metadata into the object store
609
      logMetacat.debug("Starting to insert SystemMetadata...");
610
      try {
611
          HazelcastService.getInstance().getSystemMetadataMap().lock(sysmeta.getIdentifier());
612
          sysmeta.setSerialVersion(BigInteger.ONE);
613
          sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
614
          HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
615 6466 cjones
          HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
616 6575 cjones
617
      } catch (Exception e) {
618
      	logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
619
          throw new ServiceFailure("4862", "Error inserting system metadata: " +
620
              e.getClass() + ": " + e.getMessage());
621
622
      } finally {
623
        HazelcastService.getInstance().getSystemMetadataMap().unlock(sysmeta.getIdentifier());
624 6466 cjones
625 6575 cjones
      }
626
627
      logMetacat.debug("Returning from registerSystemMetadata");
628
      EventLog.getInstance().log(request.getRemoteAddr(),
629
          request.getHeader("User-Agent"), session.getSubject().getValue(),
630
          pid.getValue(), "registerSystemMetadata");
631
      return pid;
632 6410 cjones
  }
633
634
  /**
635 6177 cjones
   * Given an optional scope and format, reserves and returns an identifier
636
   * within that scope and format that is unique and will not be
637
   * used by any other sessions.
638 6410 cjones
    *
639
   * @param session - the Session object containing the credentials for the Subject
640
   * @param pid - The identifier of the object to register the system metadata against
641
   * @param scope - An optional string to be used to qualify the scope of
642
   *                the identifier namespace, which is applied differently
643
   *                depending on the format requested. If scope is not
644
   *                supplied, a default scope will be used.
645
   * @param format - The optional name of the identifier format to be used,
646
   *                  drawn from a DataONE-specific vocabulary of identifier
647
   *                 format names, including several common syntaxes such
648
   *                 as DOI, LSID, UUID, and LSRN, among others. If the
649
   *                 format is not supplied by the caller, the CN service
650
   *                 will use a default identifier format, which may change
651
   *                 over time.
652
   *
653
   * @return true if the registration succeeds
654
   *
655
   * @throws InvalidToken
656
   * @throws ServiceFailure
657
   * @throws NotAuthorized
658
   * @throws IdentifierNotUnique
659
   * @throws NotImplemented
660
   */
661 6471 jones
  @Override
662 6622 leinfelder
  public Identifier reserveIdentifier(Session session, Identifier pid)
663 6410 cjones
  throws InvalidToken, ServiceFailure,
664 6378 leinfelder
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
665 6177 cjones
666 6410 cjones
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
667
  }
668
669 6471 jones
  @Override
670 6410 cjones
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
671
  throws InvalidToken, ServiceFailure,
672 6378 leinfelder
        NotAuthorized, NotImplemented, InvalidRequest {
673 6410 cjones
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
674
  }
675
676
  /**
677
    * Checks whether the pid is reserved by the subject in the session param
678
    * If the reservation is held on the pid by the subject, we return true.
679
    *
680
   * @param session - the Session object containing the Subject
681
   * @param pid - The identifier to check
682
   *
683
   * @return true if the reservation exists for the subject/pid
684
   *
685
   * @throws InvalidToken
686
   * @throws ServiceFailure
687
   * @throws NotFound - when the pid is not found (in use or in reservation)
688
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
689
   * @throws IdentifierNotUnique - when the pid is in use
690
   * @throws NotImplemented
691
   */
692 6177 cjones
693 6471 jones
  @Override
694 6410 cjones
  public boolean hasReservation(Session session, Identifier pid)
695
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique,
696
      NotImplemented, InvalidRequest {
697
698
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
699
  }
700 6339 leinfelder
701 6410 cjones
  /**
702 6177 cjones
   * Changes ownership (RightsHolder) of the specified object to the
703
   * subject specified by userId
704 6410 cjones
    *
705
   * @param session - the Session object containing the credentials for the Subject
706
   * @param pid - Identifier of the object to be modified
707
   * @param userId - The subject that will be taking ownership of the specified object.
708
   *
709
   * @return pid - the identifier of the modified object
710
   *
711
   * @throws ServiceFailure
712
   * @throws InvalidToken
713
   * @throws NotFound
714
   * @throws NotAuthorized
715
   * @throws NotImplemented
716
   * @throws InvalidRequest
717
   */
718 6471 jones
  @Override
719 6593 cjones
  public Identifier setOwner(Session session, Identifier pid, Subject userId,
720
      long serialVersion)
721
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
722
      NotImplemented, InvalidRequest {
723
724
      // get the subject
725
      Subject subject = session.getSubject();
726
727
      // are we allowed to do this?
728
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
729
        throw new NotAuthorized("4440", "not allowed by " + subject.getValue() + " on " + pid.getValue());
730
      }
731
732
      SystemMetadata systemMetadata = null;
733
      try {
734
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
735
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
736
737
          // does the request have the most current system metadata?
738
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
739
             String msg = "The requested system metadata version number " +
740
                 serialVersion + "differs from the current version at " +
741
                 systemMetadata.getSerialVersion().longValue() +
742
                 " Please get the latest copy in order to modify it.";
743
             throw new InvalidRequest("4442", msg);
744
          }
745
746
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
747
          throw new NotFound("4460", "No record found for: " + pid.getValue());
748
749
      }
750
751
      // set the new rights holder
752
      systemMetadata.setRightsHolder(userId);
753
754
      // update the metadata
755
      try {
756
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
757
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
758
	        HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
759
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
760
761
      } catch (Exception e) {
762
		  throw new ServiceFailure("4490", e.getMessage());
763
764
	    } finally {
765
	        HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
766
	    }
767
768
      return pid;
769 6410 cjones
  }
770 6177 cjones
771 6410 cjones
  /**
772
   * Verify that a replication task is authorized by comparing the target node's
773
   * Subject (from the X.509 certificate-derived Session) with the list of
774
   * subjects in the known, pending replication tasks map.
775
   *
776
   * @param originatingNodeSession - Session information that contains the
777
   *                                 identity of the calling user
778
   * @param targetNodeSubject - Subject identifying the target node
779
   * @param pid - the identifier of the object to be replicated
780
   * @param replicatePermission - the execute permission to be granted
781
   *
782
   * @throws ServiceFailure
783
   * @throws NotImplemented
784
   * @throws InvalidToken
785
   * @throws NotAuthorized
786
   * @throws InvalidRequest
787
   * @throws NotFound
788
   */
789 6471 jones
  @Override
790 6409 cjones
  public boolean isNodeAuthorized(Session originatingNodeSession,
791 6384 cjones
    Subject targetNodeSubject, Identifier pid, Permission replicatePermission)
792 6410 cjones
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
793
    NotFound, InvalidRequest {
794 6384 cjones
795 6463 cjones
	  boolean isAllowed = false;
796
	  SystemMetadata sysmeta = null;
797
    NodeReference targetNode = null;
798
799
	  try {
800 6484 cjones
	    // get the target node reference from the nodes list
801
	    CNode cn = D1Client.getCN();
802 6624 cjones
	    List<Node> nodes = cn.listNodes().getNodeList();
803 6484 cjones
804
	    for ( Node node : nodes ) {
805
	        Subject nodeSubject = node.getSubject(0);
806
	        if (nodeSubject.getValue().equals(targetNodeSubject)) {
807
	            targetNode = node.getIdentifier();
808
809
	        }
810
	    }
811 6419 leinfelder
812 6484 cjones
813 6468 leinfelder
	    //lock, get, and unlock the pid
814 6463 cjones
	    HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
815 6468 leinfelder
	    sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
816 6463 cjones
	    List<Replica> replicaList = sysmeta.getReplicaList();
817 6419 leinfelder
818 6568 cjones
      // find the replica with the status set to 'requested'
819
      for (Replica replica : replicaList) {
820
          ReplicationStatus status = replica.getReplicationStatus();
821
          NodeReference listedNode = replica.getReplicaMemberNode();
822
          if (listedNode.equals(targetNode)
823
                  && status.equals(ReplicationStatus.REQUESTED)) {
824
              isAllowed = true;
825
              break;
826 6463 cjones
827 6568 cjones
          }
828
      }
829
830
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
831 6484 cjones
832 6463 cjones
	  } catch(Exception e) {
833 6568 cjones
	      // Catch Hazelcast RuntimeExceptions
834
	      throw new ServiceFailure("4872",
835
	          "Couldn't determine if node is allowed: " + e.getMessage());
836 6419 leinfelder
837 6463 cjones
	  } finally {
838
	    // always unlock the pid
839
      HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
840
841
	  }
842
843
	  return isAllowed;
844 6410 cjones
845 6384 cjones
  }
846
847 6569 cjones
  /**
848 6570 cjones
   * Adds a new object to the Node, where the object is a science metadata object.
849 6569 cjones
   *
850
   * @param session - the Session object containing the credentials for the Subject
851
   * @param pid - The object identifier to be created
852
   * @param object - the object bytes
853
   * @param sysmeta - the system metadata that describes the object
854
   *
855
   * @return pid - the object identifier created
856
   *
857
   * @throws InvalidToken
858
   * @throws ServiceFailure
859
   * @throws NotAuthorized
860
   * @throws IdentifierNotUnique
861
   * @throws UnsupportedType
862
   * @throws InsufficientResources
863
   * @throws InvalidSystemMetadata
864
   * @throws NotImplemented
865
   * @throws InvalidRequest
866
   */
867
  public Identifier create(Session session, Identifier pid, InputStream object,
868
    SystemMetadata sysmeta)
869
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
870
    UnsupportedType, InsufficientResources, InvalidSystemMetadata,
871
    NotImplemented, InvalidRequest {
872 6570 cjones
873
874
      try {
875
        // are we allowed?
876
          boolean isAllowed = false;
877
          CNode cn = D1Client.getCN();
878
          List<Node> nodes = (List<Node>) cn.listNodes();
879
880
          for (Node node : nodes) {
881
              if ( node.getType().equals(NodeType.CN) ) {
882
883
                  List<Subject> subjects = node.getSubjectList();
884
                  for (Subject subject : subjects) {
885
                     if (subject.getValue().equals(session.getSubject().getValue())) {
886
                         isAllowed = true;
887
                         break;
888
                     }
889
                  }
890
              }
891
          }
892
893
          // proceed if we're called by a CN
894
          if ( isAllowed ) {
895
              // create the coordinating node version of the document
896
              HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
897
              sysmeta.setSerialVersion(BigInteger.ONE);
898
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
899
              pid = super.create(session, pid, object, sysmeta);
900
              HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
901
902
          } else {
903
              String msg = "The subject listed as " + session.getSubject().getValue() +
904
                  " isn't allowed to call create() on a Coordinating Node.";
905
              logMetacat.info(msg);
906
              throw new NotAuthorized("1100", msg);
907
          }
908
909
      } catch (Exception e) {
910
          // Convert Hazelcast runtime exceptions to service failures
911
          String msg = "There was a problem creating the object identified by " +
912
              pid.getValue() + ". There error message was: " + e.getMessage();
913
914
      } finally {
915
          HazelcastService.getInstance().getSystemMetadataMap().unlock(pid);
916
917
      }
918
919 6569 cjones
      return pid;
920
921
  }
922
923 6571 cjones
  /**
924
   * Set access for a given object using the object identifier and a Subject
925
   * under a given Session.
926
   *
927
   * @param session - the Session object containing the credentials for the Subject
928
   * @param pid - the object identifier for the given object to apply the policy
929
   * @param policy - the access policy to be applied
930
   *
931
   * @return true if the application of the policy succeeds
932
   * @throws InvalidToken
933
   * @throws ServiceFailure
934
   * @throws NotFound
935
   * @throws NotAuthorized
936
   * @throws NotImplemented
937
   * @throws InvalidRequest
938
   */
939
  public boolean setAccessPolicy(Session session, Identifier pid,
940 6593 cjones
      AccessPolicy accessPolicy, long serialVersion)
941 6571 cjones
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
942
      NotImplemented, InvalidRequest {
943
944
      boolean success = false;
945
946
      // get the subject
947
      Subject subject = session.getSubject();
948
949
      // are we allowed to do this?
950
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
951
          throw new NotAuthorized("4420", "not allowed by " + subject.getValue() +
952
          " on " + pid.getValue());
953
      }
954
955
      SystemMetadata systemMetadata = null;
956
      try {
957
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
958
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
959
960 6593 cjones
          // does the request have the most current system metadata?
961
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
962
             String msg = "The requested system metadata version number " +
963
                 serialVersion + "differs from the current version at " +
964
                 systemMetadata.getSerialVersion().longValue() +
965
                 " Please get the latest copy in order to modify it.";
966
             throw new InvalidRequest("4402", msg);
967
          }
968
969 6571 cjones
      } catch (Exception e) {
970
          // convert Hazelcast RuntimeException to NotFound
971
          throw new NotFound("4400", "No record found for: " + pid);
972
973
      }
974
975
      // set the access policy
976
      systemMetadata.setAccessPolicy(accessPolicy);
977
978
      // update the system metadata
979
      try {
980
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
981
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
982
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
983
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
984
985
      } catch (Exception e) {
986
          // convert Hazelcast RuntimeException to ServiceFailure
987
          throw new ServiceFailure("4430", e.getMessage());
988
989
      } finally {
990
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
991
992
      }
993
994
    // TODO: how do we know if the map was persisted?
995
    success = true;
996
997
    return success;
998
  }
999
1000 6578 cjones
  /**
1001
   * Full replacement of replication metadata in the system metadata for the
1002
   * specified object, changes date system metadata modified
1003
   *
1004
   * @param session - the Session object containing the credentials for the Subject
1005
   * @param pid - the object identifier for the given object to apply the policy
1006
   * @param replica - the replica to be updated
1007
   * @return
1008
   * @throws NotImplemented
1009
   * @throws NotAuthorized
1010
   * @throws ServiceFailure
1011
   * @throws InvalidRequest
1012
   * @throws NotFound
1013
   */
1014
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1015 6593 cjones
      Replica replica, long serialVersion)
1016 6578 cjones
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1017
      NotFound {
1018
1019
      // get the subject
1020
      Subject subject = session.getSubject();
1021
1022
      // are we allowed to do this?
1023
      try {
1024
        // what is the controlling permission?
1025
        if (!isAuthorized(session, pid, Permission.WRITE)) {
1026
            throw new NotAuthorized("4851", "not allowed by " + subject.getValue() +
1027
            " on " + pid.getValue());
1028
        }
1029
1030
      } catch (InvalidToken e) {
1031
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() +
1032
                  " on " + pid.getValue());
1033
1034
      }
1035
1036
      SystemMetadata systemMetadata = null;
1037
      try {
1038
          HazelcastService.getInstance().getSystemMetadataMap().lock(pid);
1039
          systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1040
1041 6593 cjones
          // does the request have the most current system metadata?
1042
          if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1043
             String msg = "The requested system metadata version number " +
1044
                 serialVersion + "differs from the current version at " +
1045
                 systemMetadata.getSerialVersion().longValue() +
1046
                 " Please get the latest copy in order to modify it.";
1047
             throw new InvalidRequest("4853", msg);
1048
          }
1049
1050 6578 cjones
      } catch (Exception e) { // Catch is generic since HZ throws RuntimeException
1051
        throw new NotFound("4854", "No record found for: " + pid.getValue() +
1052
            " : " + e.getMessage());
1053
1054
      }
1055
1056
      // set the status for the replica
1057
      List<Replica> replicas = systemMetadata.getReplicaList();
1058
      NodeReference replicaNode = replica.getReplicaMemberNode();
1059
      int index = 0;
1060
      for (Replica listedReplica: replicas) {
1061
1062
          // remove the replica that we are replacing
1063
          if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1064
              replicas.remove(index);
1065
              break;
1066
1067
          }
1068
          index++;
1069
      }
1070
1071
      // add the new replica item
1072
      replicas.add(replica);
1073
      systemMetadata.setReplicaList(replicas);
1074
1075
      // update the metadata
1076
      try {
1077
          systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1078
          systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1079
          HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1080
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1081
1082
      } catch (Exception e) {
1083
          throw new ServiceFailure("4852", e.getMessage());
1084
1085
      } finally {
1086
          HazelcastService.getInstance().getSystemMetadataMap().unlock(systemMetadata.getIdentifier());
1087
1088
      }
1089
1090
      return true;
1091
1092
  }
1093 6622 leinfelder
1094
  	@Override
1095
  	public ObjectList listObjects(Session session, Date startTime,
1096
            Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1097
            Integer start, Integer count)
1098
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1099
			ServiceFailure {
1100
1101
  		ObjectList objectList = null;
1102
        try {
1103
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1104
        } catch (Exception e) {
1105
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1106
        }
1107
1108
        return objectList;
1109
	}
1110 6177 cjones
}