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.InputStream;
27 6567 cjones
import java.math.BigInteger;
28 6883 leinfelder
import java.util.ArrayList;
29 6567 cjones
import java.util.Calendar;
30 6177 cjones
import java.util.Date;
31 6220 leinfelder
import java.util.List;
32 6859 cjones
import java.util.concurrent.locks.Lock;
33 6177 cjones
34 6542 leinfelder
import javax.servlet.http.HttpServletRequest;
35
36 6178 cjones
import org.apache.log4j.Logger;
37 6484 cjones
import org.dataone.client.CNode;
38
import org.dataone.client.D1Client;
39 6366 leinfelder
import org.dataone.service.cn.v1.CNAuthorization;
40
import org.dataone.service.cn.v1.CNCore;
41
import org.dataone.service.cn.v1.CNRead;
42
import org.dataone.service.cn.v1.CNReplication;
43 6792 cjones
import org.dataone.service.exceptions.BaseException;
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 6869 cjones
import org.dataone.service.exceptions.VersionMismatch;
55 6571 cjones
import org.dataone.service.types.v1.AccessPolicy;
56 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
57 6803 leinfelder
import org.dataone.service.types.v1.ChecksumAlgorithmList;
58 6366 leinfelder
import org.dataone.service.types.v1.Identifier;
59 6463 cjones
import org.dataone.service.types.v1.Node;
60 6366 leinfelder
import org.dataone.service.types.v1.NodeList;
61 6409 cjones
import org.dataone.service.types.v1.NodeReference;
62 6570 cjones
import org.dataone.service.types.v1.NodeType;
63 6366 leinfelder
import org.dataone.service.types.v1.ObjectFormat;
64
import org.dataone.service.types.v1.ObjectFormatIdentifier;
65
import org.dataone.service.types.v1.ObjectFormatList;
66
import org.dataone.service.types.v1.ObjectList;
67
import org.dataone.service.types.v1.ObjectLocationList;
68
import org.dataone.service.types.v1.Permission;
69
import org.dataone.service.types.v1.Replica;
70
import org.dataone.service.types.v1.ReplicationPolicy;
71
import org.dataone.service.types.v1.ReplicationStatus;
72
import org.dataone.service.types.v1.Session;
73
import org.dataone.service.types.v1.Subject;
74
import org.dataone.service.types.v1.SystemMetadata;
75 6881 leinfelder
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
76 6177 cjones
77 6188 leinfelder
import edu.ucsb.nceas.metacat.EventLog;
78
import edu.ucsb.nceas.metacat.IdentifierManager;
79 6446 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
80 6188 leinfelder
81 6177 cjones
/**
82
 * Represents Metacat's implementation of the DataONE Coordinating Node
83 6179 cjones
 * service API. Methods implement the various CN* interfaces, and methods common
84 6177 cjones
 * to both Member Node and Coordinating Node interfaces are found in the
85
 * D1NodeService super class.
86
 *
87
 */
88
public class CNodeService extends D1NodeService implements CNAuthorization,
89 6446 leinfelder
    CNCore, CNRead, CNReplication {
90 6177 cjones
91 6178 cjones
  /* the logger instance */
92
  private Logger logMetacat = null;
93 6177 cjones
94 6178 cjones
  /**
95
   * singleton accessor
96
   */
97 6542 leinfelder
  public static CNodeService getInstance(HttpServletRequest request) {
98
    return new CNodeService(request);
99 6178 cjones
  }
100
101
  /**
102
   * Constructor, private for singleton access
103
   */
104 6542 leinfelder
  private CNodeService(HttpServletRequest request) {
105
    super(request);
106 6178 cjones
    logMetacat = Logger.getLogger(CNodeService.class);
107
108
  }
109
110 6410 cjones
  /**
111
   * Set the replication policy for an object given the object identifier
112
   *
113
   * @param session - the Session object containing the credentials for the Subject
114
   * @param pid - the object identifier for the given object
115
   * @param policy - the replication policy to be applied
116
   *
117
   * @return true or false
118
   *
119
   * @throws NotImplemented
120
   * @throws NotAuthorized
121
   * @throws ServiceFailure
122
   * @throws InvalidRequest
123 6869 cjones
   * @throws VersionMismatch
124 6410 cjones
   *
125
   */
126 6471 jones
  @Override
127 6410 cjones
  public boolean setReplicationPolicy(Session session, Identifier pid,
128 6593 cjones
      ReplicationPolicy policy, long serialVersion)
129 6567 cjones
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
130 6869 cjones
      InvalidRequest, InvalidToken, VersionMismatch {
131 6567 cjones
132 6717 cjones
      // The lock to be used for this identifier
133 6859 cjones
      Lock lock = null;
134 6702 cjones
135 6567 cjones
      // get the subject
136
      Subject subject = session.getSubject();
137
138
      // are we allowed to do this?
139 6866 cjones
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
140 6869 cjones
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
141
              throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION
142
                      + " not allowed by " + subject.getValue() + " on "
143
                      + pid.getValue());
144
145
          }
146
      }
147
148
      SystemMetadata systemMetadata = null;
149 6567 cjones
      try {
150 6858 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
151
          lock.lock();
152 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
153 6858 cjones
154
          try {
155
              if ( HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid) ) {
156
                  systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
157
158
              }
159 6869 cjones
160
              // did we get it correctly?
161
              if ( systemMetadata == null ) {
162
                  throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
163
164
              }
165 6858 cjones
166
              // does the request have the most current system metadata?
167
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
168
                 String msg = "The requested system metadata version number " +
169
                     serialVersion + " differs from the current version at " +
170
                     systemMetadata.getSerialVersion().longValue() +
171
                     ". Please get the latest copy in order to modify it.";
172 6869 cjones
                 throw new VersionMismatch("4886", msg);
173
174 6858 cjones
              }
175 6717 cjones
176 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
177 6858 cjones
              throw new NotFound("4884", "No record found for: " + pid.getValue());
178
179 6717 cjones
          }
180 6858 cjones
181
          // set the new policy
182
          systemMetadata.setReplicationPolicy(policy);
183
184
          // update the metadata
185
          try {
186
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
187
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
188
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
189
190 6869 cjones
          } catch (RuntimeException e) {
191 6858 cjones
              throw new ServiceFailure("4882", e.getMessage());
192
193 6593 cjones
          }
194
195 6869 cjones
      } catch (RuntimeException e) {
196
          throw new ServiceFailure("4882", e.getMessage());
197
198
      } finally {
199
          lock.unlock();
200
          logMetacat.debug("Unlocked identifier " + pid.getValue());
201
202
      }
203 6410 cjones
204 6567 cjones
      return true;
205 6410 cjones
  }
206 6177 cjones
207 6410 cjones
  /**
208 6881 leinfelder
   * Deletes the replica from the given Member Node
209
   * NOTE: MN.delete() may be an "archive" operation. TBD.
210
   * @param session
211
   * @param pid
212
   * @param nodeId
213
   * @param serialVersion
214
   * @return
215
   * @throws InvalidToken
216
   * @throws ServiceFailure
217
   * @throws NotAuthorized
218
   * @throws NotFound
219
   * @throws NotImplemented
220 6883 leinfelder
   * @throws VersionMismatch
221 6881 leinfelder
   */
222 6884 leinfelder
  @Override
223 6881 leinfelder
  public boolean deleteReplicationMetadata(Session session, Identifier pid, NodeReference nodeId, long serialVersion)
224 6883 leinfelder
  	throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, VersionMismatch {
225 6881 leinfelder
226 6883 leinfelder
	  	// The lock to be used for this identifier
227
		Lock lock = null;
228
229
		// get the subject
230
		Subject subject = session.getSubject();
231
232
		// are we allowed to do this?
233
		if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
234 7029 leinfelder
			boolean isAuthorized = false;
235
			try {
236
				isAuthorized = isAuthorized(session, pid, Permission.WRITE);
237
			} catch (InvalidRequest e) {
238
				throw new ServiceFailure("4882", e.getDescription());
239
			}
240
			if (!isAuthorized) {
241 6883 leinfelder
				throw new NotAuthorized("4881", Permission.WRITE
242
						+ " not allowed by " + subject.getValue() + " on "
243
						+ pid.getValue());
244
245
			}
246
		}
247
248
		SystemMetadata systemMetadata = null;
249
		try {
250
			lock = HazelcastService.getInstance().getLock(pid.getValue());
251
			lock.lock();
252
			logMetacat.debug("Locked identifier " + pid.getValue());
253
254
			try {
255
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
256
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
257
				}
258
259
				// did we get it correctly?
260
				if (systemMetadata == null) {
261
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
262
				}
263
264
				// does the request have the most current system metadata?
265
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
266
					String msg = "The requested system metadata version number "
267
							+ serialVersion
268
							+ " differs from the current version at "
269
							+ systemMetadata.getSerialVersion().longValue()
270
							+ ". Please get the latest copy in order to modify it.";
271
					throw new VersionMismatch("4886", msg);
272
273
				}
274
275
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
276
				throw new NotFound("4884", "No record found for: " + pid.getValue());
277
278
			}
279
280
			// check permissions
281
			// TODO: is this necessary?
282
			List<Node> nodeList = D1Client.getCN().listNodes().getNodeList();
283
			boolean isAllowed = ServiceMethodRestrictionUtil.isMethodAllowed(session.getSubject(), nodeList, "CNReplication", "deleteReplicationMetadata");
284
			if (isAllowed) {
285
				throw new NotAuthorized("4881", "Caller is not authorized to deleteReplicationMetadata");
286
			}
287
288
			// delete the replica from the given node
289
			D1Client.getMN(nodeId).delete(session, pid);
290
291
			// reflect that change in the system metadata
292
			List<Replica> updatedReplicas = new ArrayList<Replica>(systemMetadata.getReplicaList());
293
			for (Replica r: systemMetadata.getReplicaList()) {
294
				  if (r.getReplicaMemberNode().equals(nodeId)) {
295
					  updatedReplicas.remove(r);
296
					  break;
297
				  }
298
			}
299
			systemMetadata.setReplicaList(updatedReplicas);
300
301
			// update the metadata
302
			try {
303
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
304
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
305
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
306
			} catch (RuntimeException e) {
307
				throw new ServiceFailure("4882", e.getMessage());
308
			}
309
310
		} catch (RuntimeException e) {
311
			throw new ServiceFailure("4882", e.getMessage());
312
		} finally {
313
			lock.unlock();
314
			logMetacat.debug("Unlocked identifier " + pid.getValue());
315
		}
316
317
		return true;
318 6881 leinfelder
319
  }
320
321 6883 leinfelder
  /**
322
   * Set the obsoletedBy attribute in System Metadata
323
   * @param session
324
   * @param pid
325
   * @param obsoletedByPid
326
   * @param serialVersion
327
   * @return
328
   * @throws NotImplemented
329
   * @throws NotFound
330
   * @throws NotAuthorized
331
   * @throws ServiceFailure
332
   * @throws InvalidRequest
333
   * @throws InvalidToken
334
   * @throws VersionMismatch
335
   */
336 6884 leinfelder
  @Override
337 6883 leinfelder
  public boolean setObsoletedBy(Session session, Identifier pid,
338
			Identifier obsoletedByPid, long serialVersion)
339
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
340
			InvalidRequest, InvalidToken, VersionMismatch {
341
342
		// The lock to be used for this identifier
343
		Lock lock = null;
344
345
		// get the subject
346
		Subject subject = session.getSubject();
347
348
		// are we allowed to do this?
349
		if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
350
			if (!isAuthorized(session, pid, Permission.WRITE)) {
351
				throw new NotAuthorized("4881", Permission.WRITE
352
						+ " not allowed by " + subject.getValue() + " on "
353
						+ pid.getValue());
354
355
			}
356
		}
357
358
		SystemMetadata systemMetadata = null;
359
		try {
360
			lock = HazelcastService.getInstance().getLock(pid.getValue());
361
			lock.lock();
362
			logMetacat.debug("Locked identifier " + pid.getValue());
363
364
			try {
365
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
366
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
367
				}
368
369
				// did we get it correctly?
370
				if (systemMetadata == null) {
371
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
372
				}
373
374
				// does the request have the most current system metadata?
375
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
376
					String msg = "The requested system metadata version number "
377
							+ serialVersion
378
							+ " differs from the current version at "
379
							+ systemMetadata.getSerialVersion().longValue()
380
							+ ". Please get the latest copy in order to modify it.";
381
					throw new VersionMismatch("4886", msg);
382
383
				}
384
385
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
386
				throw new NotFound("4884", "No record found for: " + pid.getValue());
387
388
			}
389
390
			// set the new policy
391
			systemMetadata.setObsoletedBy(obsoletedByPid);
392
393
			// update the metadata
394
			try {
395
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
396
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
397
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
398
			} catch (RuntimeException e) {
399
				throw new ServiceFailure("4882", e.getMessage());
400
			}
401
402
		} catch (RuntimeException e) {
403
			throw new ServiceFailure("4882", e.getMessage());
404
		} finally {
405
			lock.unlock();
406
			logMetacat.debug("Unlocked identifier " + pid.getValue());
407
		}
408
409
		return true;
410
	}
411 6881 leinfelder
412 6883 leinfelder
413 6881 leinfelder
  /**
414 6410 cjones
   * Set the replication status for an object given the object identifier
415
   *
416
   * @param session - the Session object containing the credentials for the Subject
417
   * @param pid - the object identifier for the given object
418
   * @param status - the replication status to be applied
419
   *
420
   * @return true or false
421
   *
422
   * @throws NotImplemented
423
   * @throws NotAuthorized
424
   * @throws ServiceFailure
425
   * @throws InvalidRequest
426
   * @throws InvalidToken
427
   * @throws NotFound
428
   *
429
   */
430 6471 jones
  @Override
431 6410 cjones
  public boolean setReplicationStatus(Session session, Identifier pid,
432 6792 cjones
      NodeReference targetNode, ReplicationStatus status, BaseException failure)
433 6644 cjones
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized,
434
      InvalidRequest, NotFound {
435
436 6702 cjones
      // The lock to be used for this identifier
437 6859 cjones
      Lock lock = null;
438 6702 cjones
439 6644 cjones
      boolean allowed = false;
440
      int replicaEntryIndex = -1;
441
      List<Replica> replicas = null;
442
      // get the subject
443
      Subject subject = session.getSubject();
444 6676 cjones
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
445
          " is " + status.toString());
446 6644 cjones
447
      SystemMetadata systemMetadata = null;
448 6858 cjones
449
      try {
450 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
451 6702 cjones
          lock.lock();
452 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
453
454 6858 cjones
          try {
455
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
456 6657 cjones
457 6869 cjones
              // did we get it correctly?
458 6858 cjones
              if ( systemMetadata == null ) {
459
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
460 6869 cjones
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
461 6858 cjones
462
              }
463
              replicas = systemMetadata.getReplicaList();
464
              int count = 0;
465 6657 cjones
466 6876 cjones
              // was there a failure? log it
467
              if ( failure != null && status == ReplicationStatus.FAILED ) {
468
                 String msg = "The replication request of the object identified by " +
469
                     pid.getValue() + " failed.  The error message was " +
470
                     failure.getMessage() + ".";
471 6858 cjones
              }
472 6869 cjones
473 6876 cjones
              if (replicas.size() > 0 && replicas != null) {
474
                  // find the target replica index in the replica list
475
                  for (Replica replica : replicas) {
476
                      String replicaNodeStr = replica.getReplicaMemberNode()
477
                              .getValue();
478
                      String targetNodeStr = targetNode.getValue();
479
                      logMetacat.debug("Comparing " + replicaNodeStr + " to "
480
                              + targetNodeStr);
481 6869 cjones
482 6876 cjones
                      if (replicaNodeStr.equals(targetNodeStr)) {
483
                          replicaEntryIndex = count;
484
                          logMetacat.debug("replica entry index is: "
485
                                  + replicaEntryIndex);
486
                          break;
487
                      }
488
                      count++;
489 6858 cjones
490
                  }
491
              }
492
              // are we allowed to do this? only CNs and target MNs are allowed
493
              CNode cn = D1Client.getCN();
494
              List<Node> nodes = cn.listNodes().getNodeList();
495 6657 cjones
496 6858 cjones
              // find the node in the node list
497
              for ( Node node : nodes ) {
498
499
                  NodeReference nodeReference = node.getIdentifier();
500
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + nodeReference.getValue());
501
502
                  // allow target MN certs and CN certs
503
                  if (targetNode.getValue().equals(nodeReference.getValue()) ||
504
                      node.getType() == NodeType.CN) {
505
                      List<Subject> nodeSubjects = node.getSubjectList();
506
507
                      // check if the session subject is in the node subject list
508
                      for (Subject nodeSubject : nodeSubjects) {
509
                          if ( nodeSubject.equals(subject) ) {
510
                              allowed = true; // subject of session == target node subject
511
                              break;
512
513
                          }
514
                      }
515
                  }
516
              }
517 6657 cjones
518 6876 cjones
              if ( !isAdminAuthorized(session, pid, Permission.WRITE) ) {
519
                  if (!allowed) {
520
                    String msg = "The subject identified by "
521
                            + subject.getValue()
522
                            + " does not have permission to set the replication status for "
523
                            + "the replica identified by "
524
                            + targetNode.getValue() + ".";
525
                    logMetacat.info(msg);
526
                    throw new NotAuthorized("4720", msg);
527
                }
528 6858 cjones
529
              }
530 6657 cjones
531 6876 cjones
532 6858 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
533
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
534
                " : " + e.getMessage());
535
536 6644 cjones
          }
537
538 6876 cjones
          Replica targetReplica = new Replica();
539 6858 cjones
          // set the status for the replica
540
          if ( replicaEntryIndex != -1 ) {
541 6876 cjones
              targetReplica = replicas.get(replicaEntryIndex);
542 6858 cjones
              targetReplica.setReplicationStatus(status);
543
              logMetacat.debug("Set the replication status for " +
544
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
545
                  targetReplica.getReplicationStatus());
546 6644 cjones
547 6858 cjones
          } else {
548 6876 cjones
              // this is a new entry, create it
549
              targetReplica.setReplicaMemberNode(targetNode);
550
              targetReplica.setReplicationStatus(status);
551
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
552
              replicas.add(targetReplica);
553
554 6644 cjones
          }
555
556 6858 cjones
          systemMetadata.setReplicaList(replicas);
557
558
          // update the metadata
559
          try {
560
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
561
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
562
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
563
564
              if ( status == ReplicationStatus.FAILED && failure != null ) {
565
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
566
                      " on target node " + targetNode + ". The exception was: " +
567
                      failure.getMessage());
568
              }
569 6869 cjones
          } catch (RuntimeException e) {
570 6858 cjones
              throw new ServiceFailure("4700", e.getMessage());
571
572 6644 cjones
          }
573
574 6867 cjones
    } catch (RuntimeException e) {
575
        String msg = "There was a RuntimeException getting the lock for " +
576
            pid.getValue();
577
        logMetacat.info(msg);
578
579
    } finally {
580 6858 cjones
        lock.unlock();
581
        logMetacat.debug("Unlocked identifier " + pid.getValue());
582 6593 cjones
583 6858 cjones
    }
584 6676 cjones
585 6644 cjones
      return true;
586 6410 cjones
  }
587
588
  /**
589
   * Return the checksum of the object given the identifier
590
   *
591
   * @param session - the Session object containing the credentials for the Subject
592
   * @param pid - the object identifier for the given object
593
   *
594
   * @return checksum - the checksum of the object
595
   *
596
   * @throws InvalidToken
597
   * @throws ServiceFailure
598
   * @throws NotAuthorized
599
   * @throws NotFound
600
   * @throws NotImplemented
601
   */
602 6471 jones
  @Override
603 6410 cjones
  public Checksum getChecksum(Session session, Identifier pid)
604
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
605 6622 leinfelder
    NotImplemented {
606 7029 leinfelder
607
	boolean isAuthorized = false;
608
	try {
609
		isAuthorized = isAuthorized(session, pid, Permission.READ);
610
	} catch (InvalidRequest e) {
611
		throw new ServiceFailure("1410", e.getDescription());
612
	}
613
    if (!isAuthorized) {
614 6568 cjones
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());
615 6410 cjones
    }
616 6568 cjones
617 6410 cjones
    SystemMetadata systemMetadata = null;
618 6568 cjones
    Checksum checksum = null;
619
620 6410 cjones
    try {
621 6869 cjones
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
622
623
        if (systemMetadata == null ) {
624
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
625
        }
626 6568 cjones
        checksum = systemMetadata.getChecksum();
627 6869 cjones
628
    } catch (RuntimeException e) {
629 6568 cjones
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " +
630
            pid.getValue() + ". The error message was: " + e.getMessage());
631
632 6410 cjones
    }
633
634
    return checksum;
635
  }
636 6177 cjones
637 6410 cjones
  /**
638
   * Resolve the location of a given object
639
   *
640
   * @param session - the Session object containing the credentials for the Subject
641
   * @param pid - the object identifier for the given object
642
   *
643
   * @return objectLocationList - the list of nodes known to contain the object
644
   *
645
   * @throws InvalidToken
646
   * @throws ServiceFailure
647
   * @throws NotAuthorized
648
   * @throws NotFound
649
   * @throws NotImplemented
650
   */
651 6471 jones
  @Override
652 6410 cjones
  public ObjectLocationList resolve(Session session, Identifier pid)
653 6622 leinfelder
    throws InvalidToken, ServiceFailure, NotAuthorized,
654 6410 cjones
    NotFound, NotImplemented {
655 6177 cjones
656 6410 cjones
    throw new NotImplemented("4131", "resolve not implemented");
657 6303 leinfelder
658 6410 cjones
  }
659 6177 cjones
660 6410 cjones
  /**
661
   * Search the metadata catalog for identifiers that match the criteria
662
   *
663
   * @param session - the Session object containing the credentials for the Subject
664
   * @param queryType - An identifier for the type of query expression
665
   *                    provided in the query
666
   * @param query -  The criteria for matching the characteristics of the
667
   *                 metadata objects of interest
668
   *
669
   * @return objectList - the list of objects matching the criteria
670
   *
671
   * @throws InvalidToken
672
   * @throws ServiceFailure
673
   * @throws NotAuthorized
674
   * @throws InvalidRequest
675
   * @throws NotImplemented
676
   */
677 6471 jones
  @Override
678 6410 cjones
  public ObjectList search(Session session, String queryType, String query)
679
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
680
    NotImplemented {
681 6177 cjones
682 6410 cjones
    ObjectList objectList = null;
683
    try {
684
        objectList =
685
          IdentifierManager.getInstance().querySystemMetadata(
686
              null, //startTime,
687
              null, //endTime,
688
              null, //objectFormat,
689
              false, //replicaStatus,
690
              0, //start,
691
              -1 //count
692
              );
693
694
    } catch (Exception e) {
695
      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
696
    }
697 6300 leinfelder
698 6410 cjones
      return objectList;
699
700
    //throw new NotImplemented("4281", "search not implemented");
701
702
    // the code block below is from an older implementation
703
704
    /*  This block commented out because of the EcoGrid circular dependency.
705 6281 leinfelder
         *  For now, query will not be supported until the circularity can be
706
         *  resolved, probably by moving the ecogrid query syntax transformers
707
         *  directly into the Metacat codebase.  MBJ 2010-02-03
708
709
        try {
710
            EcogridQueryParser parser = new EcogridQueryParser(request
711
                    .getReader());
712
            parser.parseXML();
713
            QueryType queryType = parser.getEcogridQuery();
714
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer =
715
                new EcogridJavaToMetacatJavaQueryTransformer();
716
            QuerySpecification metacatQuery = queryTransformer
717
                    .transform(queryType);
718 6223 leinfelder
719 6281 leinfelder
            DBQuery metacat = new DBQuery();
720
721
            boolean useXMLIndex = (new Boolean(PropertyService
722
                    .getProperty("database.usexmlindex"))).booleanValue();
723
            String xmlquery = "query"; // we don't care the query in resultset,
724
            // the query can be anything
725
            PrintWriter out = null; // we don't want metacat result, so set out null
726
727
            // parameter: queryspecification, user, group, usingIndexOrNot
728
            StringBuffer result = metacat.createResultDocument(xmlquery,
729
                    metacatQuery, out, username, groupNames, useXMLIndex);
730
731
            // create result set transfer
732
            String saxparser = PropertyService.getProperty("xml.saxparser");
733
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
734
                    new StringReader(result.toString()), saxparser, queryType
735
                            .getNamespace().get_value());
736
            ResultsetType records = metacatResultsetParser.getEcogridResult();
737
738
            System.out
739
                    .println(EcogridResultsetTransformer.toXMLString(records));
740
            response.setContentType("text/xml");
741
            out = response.getWriter();
742
            out.print(EcogridResultsetTransformer.toXMLString(records));
743
744
        } catch (Exception e) {
745
            e.printStackTrace();
746
        }*/
747 6410 cjones
748 6281 leinfelder
749 6410 cjones
  }
750
751
  /**
752
   * Returns the object format registered in the DataONE Object Format
753
   * Vocabulary for the given format identifier
754
   *
755
   * @param fmtid - the identifier of the format requested
756
   *
757
   * @return objectFormat - the object format requested
758
   *
759
   * @throws ServiceFailure
760
   * @throws NotFound
761
   * @throws InsufficientResources
762
   * @throws NotImplemented
763
   */
764 6471 jones
  @Override
765 6410 cjones
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
766 6803 leinfelder
    throws ServiceFailure, NotFound, NotImplemented {
767 6410 cjones
768
      return ObjectFormatService.getInstance().getFormat(fmtid);
769
770
  }
771 6177 cjones
772 6410 cjones
  /**
773 6177 cjones
   * Returns a list of all object formats registered in the DataONE Object
774
   * Format Vocabulary
775 6410 cjones
    *
776
   * @return objectFormatList - The list of object formats registered in
777
   *                            the DataONE Object Format Vocabulary
778
   *
779
   * @throws ServiceFailure
780
   * @throws NotImplemented
781
   * @throws InsufficientResources
782
   */
783 6471 jones
  @Override
784 6410 cjones
  public ObjectFormatList listFormats()
785 6803 leinfelder
    throws ServiceFailure, NotImplemented {
786 6177 cjones
787 6410 cjones
    return ObjectFormatService.getInstance().listFormats();
788
  }
789 6177 cjones
790 6410 cjones
  /**
791 6177 cjones
   * Returns a list of nodes that have been registered with the DataONE infrastructure
792 6410 cjones
    *
793
   * @return nodeList - List of nodes from the registry
794
   *
795
   * @throws ServiceFailure
796
   * @throws NotImplemented
797
   */
798 6471 jones
  @Override
799 6410 cjones
  public NodeList listNodes()
800
    throws NotImplemented, ServiceFailure {
801 6177 cjones
802 6410 cjones
    throw new NotImplemented("4800", "listNodes not implemented");
803
  }
804 6177 cjones
805 6410 cjones
  /**
806 6177 cjones
   * Provides a mechanism for adding system metadata independently of its
807
   * associated object, such as when adding system metadata for data objects.
808 6410 cjones
    *
809
   * @param session - the Session object containing the credentials for the Subject
810
   * @param pid - The identifier of the object to register the system metadata against
811
   * @param sysmeta - The system metadata to be registered
812
   *
813
   * @return true if the registration succeeds
814
   *
815
   * @throws NotImplemented
816
   * @throws NotAuthorized
817
   * @throws ServiceFailure
818
   * @throws InvalidRequest
819
   * @throws InvalidSystemMetadata
820
   */
821 6471 jones
  @Override
822 6575 cjones
  public Identifier registerSystemMetadata(Session session, Identifier pid,
823
      SystemMetadata sysmeta)
824
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
825
      InvalidSystemMetadata {
826 6177 cjones
827 6702 cjones
      // The lock to be used for this identifier
828 6859 cjones
      Lock lock = null;
829 6702 cjones
830 6575 cjones
      // TODO: control who can call this?
831
      if (session == null) {
832
          //TODO: many of the thrown exceptions do not use the correct error codes
833
          //check these against the docs and correct them
834
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
835
                  "  If you are not logged in, please do so and retry the request.");
836
      }
837
838
      // verify that guid == SystemMetadata.getIdentifier()
839
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() +
840
          "|" + sysmeta.getIdentifier().getValue());
841
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
842
          throw new InvalidRequest("4863",
843
              "The identifier in method call (" + pid.getValue() +
844
              ") does not match identifier in system metadata (" +
845
              sysmeta.getIdentifier().getValue() + ").");
846
      }
847 6188 leinfelder
848 6575 cjones
      try {
849 6703 cjones
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
850 6863 cjones
          lock.lock();
851 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
852 6863 cjones
          logMetacat.debug("Checking if identifier exists...");
853
          // Check that the identifier does not already exist
854
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
855
              throw new InvalidRequest("4863",
856
                  "The identifier is already in use by an existing object.");
857 6575 cjones
858 6863 cjones
          }
859
860
          // insert the system metadata into the object store
861
          logMetacat.debug("Starting to insert SystemMetadata...");
862
          try {
863
              sysmeta.setSerialVersion(BigInteger.ONE);
864
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
865
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
866
867 6869 cjones
          } catch (RuntimeException e) {
868 6863 cjones
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
869
              throw new ServiceFailure("4862", "Error inserting system metadata: " +
870
                  e.getClass() + ": " + e.getMessage());
871
872
          }
873
874 6869 cjones
      } catch (RuntimeException e) {
875 6575 cjones
          throw new ServiceFailure("4862", "Error inserting system metadata: " +
876 6863 cjones
                  e.getClass() + ": " + e.getMessage());
877 6575 cjones
878 6863 cjones
      }  finally {
879
          lock.unlock();
880
          logMetacat.debug("Unlocked identifier " + pid.getValue());
881
882
      }
883 6466 cjones
884 6575 cjones
885
      logMetacat.debug("Returning from registerSystemMetadata");
886
      EventLog.getInstance().log(request.getRemoteAddr(),
887
          request.getHeader("User-Agent"), session.getSubject().getValue(),
888
          pid.getValue(), "registerSystemMetadata");
889
      return pid;
890 6410 cjones
  }
891
892
  /**
893 6177 cjones
   * Given an optional scope and format, reserves and returns an identifier
894
   * within that scope and format that is unique and will not be
895
   * used by any other sessions.
896 6410 cjones
    *
897
   * @param session - the Session object containing the credentials for the Subject
898
   * @param pid - The identifier of the object to register the system metadata against
899
   * @param scope - An optional string to be used to qualify the scope of
900
   *                the identifier namespace, which is applied differently
901
   *                depending on the format requested. If scope is not
902
   *                supplied, a default scope will be used.
903
   * @param format - The optional name of the identifier format to be used,
904
   *                  drawn from a DataONE-specific vocabulary of identifier
905
   *                 format names, including several common syntaxes such
906
   *                 as DOI, LSID, UUID, and LSRN, among others. If the
907
   *                 format is not supplied by the caller, the CN service
908
   *                 will use a default identifier format, which may change
909
   *                 over time.
910
   *
911
   * @return true if the registration succeeds
912
   *
913
   * @throws InvalidToken
914
   * @throws ServiceFailure
915
   * @throws NotAuthorized
916
   * @throws IdentifierNotUnique
917
   * @throws NotImplemented
918
   */
919 6471 jones
  @Override
920 6622 leinfelder
  public Identifier reserveIdentifier(Session session, Identifier pid)
921 6410 cjones
  throws InvalidToken, ServiceFailure,
922 6378 leinfelder
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
923 6177 cjones
924 6410 cjones
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
925
  }
926
927 6471 jones
  @Override
928 6410 cjones
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
929
  throws InvalidToken, ServiceFailure,
930 6378 leinfelder
        NotAuthorized, NotImplemented, InvalidRequest {
931 6410 cjones
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
932
  }
933
934
  /**
935
    * Checks whether the pid is reserved by the subject in the session param
936
    * If the reservation is held on the pid by the subject, we return true.
937
    *
938
   * @param session - the Session object containing the Subject
939
   * @param pid - The identifier to check
940
   *
941
   * @return true if the reservation exists for the subject/pid
942
   *
943
   * @throws InvalidToken
944
   * @throws ServiceFailure
945
   * @throws NotFound - when the pid is not found (in use or in reservation)
946
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
947
   * @throws IdentifierNotUnique - when the pid is in use
948
   * @throws NotImplemented
949
   */
950 6177 cjones
951 6471 jones
  @Override
952 6934 leinfelder
  public boolean hasReservation(Session session, Subject subject, Identifier pid)
953 6410 cjones
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique,
954
      NotImplemented, InvalidRequest {
955
956
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
957
  }
958 6339 leinfelder
959 6410 cjones
  /**
960 6177 cjones
   * Changes ownership (RightsHolder) of the specified object to the
961
   * subject specified by userId
962 6410 cjones
    *
963
   * @param session - the Session object containing the credentials for the Subject
964
   * @param pid - Identifier of the object to be modified
965
   * @param userId - The subject that will be taking ownership of the specified object.
966
   *
967
   * @return pid - the identifier of the modified object
968
   *
969
   * @throws ServiceFailure
970
   * @throws InvalidToken
971
   * @throws NotFound
972
   * @throws NotAuthorized
973
   * @throws NotImplemented
974
   * @throws InvalidRequest
975
   */
976 6471 jones
  @Override
977 6803 leinfelder
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
978 6593 cjones
      long serialVersion)
979
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
980 6869 cjones
      NotImplemented, InvalidRequest, VersionMismatch {
981 6593 cjones
982 6702 cjones
      // The lock to be used for this identifier
983 6859 cjones
      Lock lock = null;
984 6702 cjones
985 6593 cjones
      // get the subject
986
      Subject subject = session.getSubject();
987
988
      // are we allowed to do this?
989 6866 cjones
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
990 6869 cjones
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
991
              throw new NotAuthorized("4440", "not allowed by "
992
                      + subject.getValue() + " on " + pid.getValue());
993
994
          }
995
      }
996
997
      SystemMetadata systemMetadata = null;
998 6593 cjones
      try {
999 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1000 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1001
1002 6858 cjones
          try {
1003
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1004
1005
              // does the request have the most current system metadata?
1006
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1007
                 String msg = "The requested system metadata version number " +
1008
                     serialVersion + " differs from the current version at " +
1009
                     systemMetadata.getSerialVersion().longValue() +
1010
                     ". Please get the latest copy in order to modify it.";
1011 6869 cjones
                 throw new VersionMismatch("4443", msg);
1012 6858 cjones
              }
1013
1014 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1015 6858 cjones
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1016
1017 6593 cjones
          }
1018 6858 cjones
1019
          // set the new rights holder
1020
          systemMetadata.setRightsHolder(userId);
1021 6593 cjones
1022 6858 cjones
          // update the metadata
1023
          try {
1024
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1025
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1026
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1027
1028 6869 cjones
          } catch (RuntimeException e) {
1029
              throw new ServiceFailure("4490", e.getMessage());
1030 6593 cjones
1031 6858 cjones
          }
1032 6593 cjones
1033 6869 cjones
      } catch (RuntimeException e) {
1034 6858 cjones
          throw new ServiceFailure("4490", e.getMessage());
1035 6644 cjones
1036
      } finally {
1037 6702 cjones
          lock.unlock();
1038 6717 cjones
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1039 6858 cjones
1040 6644 cjones
      }
1041
1042 6869 cjones
      return pid;
1043 6410 cjones
  }
1044 6177 cjones
1045 6410 cjones
  /**
1046
   * Verify that a replication task is authorized by comparing the target node's
1047
   * Subject (from the X.509 certificate-derived Session) with the list of
1048
   * subjects in the known, pending replication tasks map.
1049
   *
1050
   * @param originatingNodeSession - Session information that contains the
1051
   *                                 identity of the calling user
1052
   * @param targetNodeSubject - Subject identifying the target node
1053
   * @param pid - the identifier of the object to be replicated
1054
   * @param replicatePermission - the execute permission to be granted
1055
   *
1056
   * @throws ServiceFailure
1057
   * @throws NotImplemented
1058
   * @throws InvalidToken
1059
   * @throws NotAuthorized
1060
   * @throws InvalidRequest
1061
   * @throws NotFound
1062
   */
1063 6471 jones
  @Override
1064 6409 cjones
  public boolean isNodeAuthorized(Session originatingNodeSession,
1065 6777 leinfelder
    Subject targetNodeSubject, Identifier pid)
1066 6410 cjones
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
1067
    NotFound, InvalidRequest {
1068 6702 cjones
1069 6644 cjones
    boolean isAllowed = false;
1070
    SystemMetadata sysmeta = null;
1071 6463 cjones
    NodeReference targetNode = null;
1072
1073 6644 cjones
    try {
1074
      // get the target node reference from the nodes list
1075
      CNode cn = D1Client.getCN();
1076
      List<Node> nodes = cn.listNodes().getNodeList();
1077 6665 cjones
1078 6657 cjones
      if ( nodes != null ) {
1079
        for (Node node : nodes) {
1080 6665 cjones
1081 6657 cjones
            for (Subject nodeSubject : node.getSubjectList()) {
1082 6665 cjones
1083 6676 cjones
                if ( nodeSubject.equals(targetNodeSubject) ) {
1084 6657 cjones
                    targetNode = node.getIdentifier();
1085
                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1086
                    break;
1087
                }
1088
            }
1089
1090 6665 cjones
            if ( targetNode != null) { break; }
1091 6657 cjones
        }
1092
1093
      } else {
1094
          String msg = "Couldn't get the node list from the CN";
1095
          logMetacat.debug(msg);
1096
          throw new ServiceFailure("4872", msg);
1097
1098 6644 cjones
      }
1099 6757 cjones
1100
      // can't find a node listed with the given subject
1101
      if ( targetNode == null ) {
1102
          String msg = "There is no Member Node registered with a node subject " +
1103
              "matching " + targetNodeSubject.getValue();
1104
          logMetacat.info(msg);
1105 7062 leinfelder
          throw new NotAuthorized("4871", msg);
1106 6757 cjones
1107
      }
1108
1109 6657 cjones
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1110
1111 6644 cjones
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1112 6463 cjones
1113 6657 cjones
      if ( sysmeta != null ) {
1114
1115
          List<Replica> replicaList = sysmeta.getReplicaList();
1116
1117
          if ( replicaList != null ) {
1118
1119
              // find the replica with the status set to 'requested'
1120
              for (Replica replica : replicaList) {
1121
                  ReplicationStatus status = replica.getReplicationStatus();
1122
                  NodeReference listedNode = replica.getReplicaMemberNode();
1123 6757 cjones
                  if ( listedNode != null && targetNode != null ) {
1124
                      logMetacat.debug("Comparing " + listedNode.getValue()
1125
                              + " to " + targetNode.getValue());
1126
1127
                      if (listedNode.getValue().equals(targetNode.getValue())
1128
                              && status.equals(ReplicationStatus.REQUESTED)) {
1129
                          isAllowed = true;
1130
                          break;
1131 6657 cjones
1132 6757 cjones
                      }
1133 6657 cjones
                  }
1134
              }
1135 6568 cjones
          }
1136 6665 cjones
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1137
              "to replicate: " + isAllowed + " for " + pid.getValue());
1138
1139 6657 cjones
1140
      } else {
1141
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1142
          " is null.");
1143 6869 cjones
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1144 6657 cjones
1145 6568 cjones
      }
1146 6484 cjones
1147 6662 cjones
    } catch (RuntimeException e) {
1148 6665 cjones
    	  ServiceFailure sf = new ServiceFailure("4872",
1149
                "Runtime Exception: Couldn't determine if node is allowed: " +
1150 6757 cjones
                e.getCause().getMessage());
1151 6665 cjones
    	  sf.initCause(e);
1152 6659 leinfelder
        throw sf;
1153 6636 cjones
1154 6644 cjones
    }
1155
1156
    return isAllowed;
1157 6410 cjones
1158 6384 cjones
  }
1159
1160 6569 cjones
  /**
1161 6570 cjones
   * Adds a new object to the Node, where the object is a science metadata object.
1162 6569 cjones
   *
1163
   * @param session - the Session object containing the credentials for the Subject
1164
   * @param pid - The object identifier to be created
1165
   * @param object - the object bytes
1166
   * @param sysmeta - the system metadata that describes the object
1167
   *
1168
   * @return pid - the object identifier created
1169
   *
1170
   * @throws InvalidToken
1171
   * @throws ServiceFailure
1172
   * @throws NotAuthorized
1173
   * @throws IdentifierNotUnique
1174
   * @throws UnsupportedType
1175
   * @throws InsufficientResources
1176
   * @throws InvalidSystemMetadata
1177
   * @throws NotImplemented
1178
   * @throws InvalidRequest
1179
   */
1180
  public Identifier create(Session session, Identifier pid, InputStream object,
1181
    SystemMetadata sysmeta)
1182
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
1183
    UnsupportedType, InsufficientResources, InvalidSystemMetadata,
1184
    NotImplemented, InvalidRequest {
1185 6917 cjones
1186 6702 cjones
      // The lock to be used for this identifier
1187 6859 cjones
      Lock lock = null;
1188 6917 cjones
1189 6570 cjones
      try {
1190 6869 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1191
          // are we allowed?
1192 6570 cjones
          boolean isAllowed = false;
1193
          CNode cn = D1Client.getCN();
1194 6687 leinfelder
          NodeList nodeList = cn.listNodes();
1195 6570 cjones
1196 6687 leinfelder
          for (Node node : nodeList.getNodeList()) {
1197 6570 cjones
              if ( node.getType().equals(NodeType.CN) ) {
1198
1199
                  List<Subject> subjects = node.getSubjectList();
1200
                  for (Subject subject : subjects) {
1201 6688 leinfelder
                     if (subject.equals(session.getSubject())) {
1202 6570 cjones
                         isAllowed = true;
1203
                         break;
1204
                     }
1205
                  }
1206 6917 cjones
              } else {
1207
1208 6570 cjones
              }
1209
          }
1210
1211
          // proceed if we're called by a CN
1212
          if ( isAllowed ) {
1213
              // create the coordinating node version of the document
1214 6702 cjones
              lock.lock();
1215 6867 cjones
              logMetacat.debug("Locked identifier " + pid.getValue());
1216 6570 cjones
              sysmeta.setSerialVersion(BigInteger.ONE);
1217
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1218 6917 cjones
              sysmeta.setArchived(false); // this is a create op, not update
1219
1220
              // the CN should have set the origin and authoritative member node fields
1221
              try {
1222
                  sysmeta.getOriginMemberNode().getValue();
1223
                  sysmeta.getAuthoritativeMemberNode().getValue();
1224
1225
              } catch (NullPointerException npe) {
1226
                  throw new InvalidSystemMetadata("4896",
1227
                      "Both the origin and authoritative member node identifiers need to be set.");
1228
1229
              }
1230 6570 cjones
              pid = super.create(session, pid, object, sysmeta);
1231
1232
          } else {
1233
              String msg = "The subject listed as " + session.getSubject().getValue() +
1234
                  " isn't allowed to call create() on a Coordinating Node.";
1235
              logMetacat.info(msg);
1236
              throw new NotAuthorized("1100", msg);
1237
          }
1238
1239 6676 cjones
      } catch (RuntimeException e) {
1240 6570 cjones
          // Convert Hazelcast runtime exceptions to service failures
1241
          String msg = "There was a problem creating the object identified by " +
1242
              pid.getValue() + ". There error message was: " + e.getMessage();
1243 6676 cjones
          throw new ServiceFailure("4893", msg);
1244 6570 cjones
1245
      } finally {
1246 6805 leinfelder
    	  if (lock != null) {
1247
	          lock.unlock();
1248
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1249
    	  }
1250 6570 cjones
      }
1251
1252 6569 cjones
      return pid;
1253
1254
  }
1255
1256 6571 cjones
  /**
1257
   * Set access for a given object using the object identifier and a Subject
1258
   * under a given Session.
1259
   *
1260
   * @param session - the Session object containing the credentials for the Subject
1261
   * @param pid - the object identifier for the given object to apply the policy
1262
   * @param policy - the access policy to be applied
1263
   *
1264
   * @return true if the application of the policy succeeds
1265
   * @throws InvalidToken
1266
   * @throws ServiceFailure
1267
   * @throws NotFound
1268
   * @throws NotAuthorized
1269
   * @throws NotImplemented
1270
   * @throws InvalidRequest
1271
   */
1272
  public boolean setAccessPolicy(Session session, Identifier pid,
1273 6593 cjones
      AccessPolicy accessPolicy, long serialVersion)
1274 6571 cjones
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1275 6869 cjones
      NotImplemented, InvalidRequest, VersionMismatch {
1276 6571 cjones
1277 6702 cjones
      // The lock to be used for this identifier
1278 6859 cjones
      Lock lock = null;
1279 6869 cjones
      SystemMetadata systemMetadata = null;
1280 6702 cjones
1281 6571 cjones
      boolean success = false;
1282
1283
      // get the subject
1284
      Subject subject = session.getSubject();
1285
1286 6869 cjones
      if (!isAdminAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1287
          // are we allowed to do this?
1288
          if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1289
              throw new NotAuthorized("4420", "not allowed by "
1290
                      + subject.getValue() + " on " + pid.getValue());
1291
          }
1292 6571 cjones
      }
1293
1294
      try {
1295 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1296 6702 cjones
          lock.lock();
1297 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1298
1299 6858 cjones
          try {
1300
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1301 6571 cjones
1302 6869 cjones
              if ( systemMetadata == null ) {
1303
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1304
1305
              }
1306 6858 cjones
              // does the request have the most current system metadata?
1307
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1308
                 String msg = "The requested system metadata version number " +
1309
                     serialVersion + " differs from the current version at " +
1310
                     systemMetadata.getSerialVersion().longValue() +
1311
                     ". Please get the latest copy in order to modify it.";
1312 6869 cjones
                 throw new VersionMismatch("4402", msg);
1313
1314 6858 cjones
              }
1315
1316 6869 cjones
          } catch (RuntimeException e) {
1317 6858 cjones
              // convert Hazelcast RuntimeException to NotFound
1318
              throw new NotFound("4400", "No record found for: " + pid);
1319
1320 6593 cjones
          }
1321 6858 cjones
1322
          // set the access policy
1323
          systemMetadata.setAccessPolicy(accessPolicy);
1324 6593 cjones
1325 6858 cjones
          // update the system metadata
1326
          try {
1327
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1328
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1329
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1330
1331 6869 cjones
          } catch (RuntimeException e) {
1332 6858 cjones
              // convert Hazelcast RuntimeException to ServiceFailure
1333
              throw new ServiceFailure("4430", e.getMessage());
1334
1335
          }
1336 6571 cjones
1337 6869 cjones
      } catch (RuntimeException e) {
1338 6571 cjones
          throw new ServiceFailure("4430", e.getMessage());
1339 6858 cjones
1340 6571 cjones
      } finally {
1341 6702 cjones
          lock.unlock();
1342 6717 cjones
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1343 6571 cjones
1344
      }
1345 6858 cjones
1346 6571 cjones
1347
    // TODO: how do we know if the map was persisted?
1348
    success = true;
1349
1350
    return success;
1351
  }
1352
1353 6578 cjones
  /**
1354
   * Full replacement of replication metadata in the system metadata for the
1355
   * specified object, changes date system metadata modified
1356
   *
1357
   * @param session - the Session object containing the credentials for the Subject
1358
   * @param pid - the object identifier for the given object to apply the policy
1359
   * @param replica - the replica to be updated
1360
   * @return
1361
   * @throws NotImplemented
1362
   * @throws NotAuthorized
1363
   * @throws ServiceFailure
1364
   * @throws InvalidRequest
1365
   * @throws NotFound
1366 6869 cjones
   * @throws VersionMismatch
1367 6578 cjones
   */
1368 6869 cjones
  @Override
1369 6578 cjones
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1370 6593 cjones
      Replica replica, long serialVersion)
1371 6578 cjones
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1372 6869 cjones
      NotFound, VersionMismatch {
1373 6578 cjones
1374 6702 cjones
      // The lock to be used for this identifier
1375 6859 cjones
      Lock lock = null;
1376 6702 cjones
1377 6578 cjones
      // get the subject
1378
      Subject subject = session.getSubject();
1379
1380
      // are we allowed to do this?
1381
      try {
1382 6866 cjones
        if (!isAdminAuthorized(session, pid, Permission.WRITE)) {
1383
            // what is the controlling permission?
1384
            if (!isAuthorized(session, pid, Permission.WRITE)) {
1385
                throw new NotAuthorized("4851", "not allowed by "
1386
                        + subject.getValue() + " on " + pid.getValue());
1387
            }
1388 6578 cjones
        }
1389
1390
      } catch (InvalidToken e) {
1391
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() +
1392
                  " on " + pid.getValue());
1393
1394
      }
1395
1396
      SystemMetadata systemMetadata = null;
1397 6858 cjones
      try {
1398 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1399 6702 cjones
          lock.lock();
1400 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1401 6578 cjones
1402 6858 cjones
          try {
1403
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1404
1405
              // does the request have the most current system metadata?
1406
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1407
                 String msg = "The requested system metadata version number " +
1408
                     serialVersion + " differs from the current version at " +
1409
                     systemMetadata.getSerialVersion().longValue() +
1410
                     ". Please get the latest copy in order to modify it.";
1411 6869 cjones
                 throw new VersionMismatch("4855", msg);
1412 6858 cjones
              }
1413
1414 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1415
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1416
                  " : " + e.getMessage());
1417 6858 cjones
1418 6593 cjones
          }
1419 6858 cjones
1420
          // set the status for the replica
1421
          List<Replica> replicas = systemMetadata.getReplicaList();
1422
          NodeReference replicaNode = replica.getReplicaMemberNode();
1423
          int index = 0;
1424
          for (Replica listedReplica: replicas) {
1425
1426
              // remove the replica that we are replacing
1427
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1428
                  replicas.remove(index);
1429
                  break;
1430
1431
              }
1432
              index++;
1433
          }
1434 6593 cjones
1435 6858 cjones
          // add the new replica item
1436
          replicas.add(replica);
1437
          systemMetadata.setReplicaList(replicas);
1438 6578 cjones
1439 6858 cjones
          // update the metadata
1440
          try {
1441
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1442
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1443
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1444
1445 6869 cjones
          } catch (RuntimeException e) {
1446
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1447 6858 cjones
              throw new ServiceFailure("4852", e.getMessage());
1448 6578 cjones
1449
          }
1450 6858 cjones
1451 6869 cjones
      } catch (RuntimeException e) {
1452
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1453
          throw new ServiceFailure("4852", e.getMessage());
1454
1455
      } finally {
1456
          lock.unlock();
1457
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1458
1459
      }
1460 6578 cjones
1461
      return true;
1462
1463
  }
1464 6622 leinfelder
1465 6869 cjones
  /**
1466
   *
1467
   */
1468
  @Override
1469 6858 cjones
  public ObjectList listObjects(Session session, Date startTime,
1470
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1471
      Integer start, Integer count)
1472 6644 cjones
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1473
      ServiceFailure {
1474
1475
      ObjectList objectList = null;
1476 6622 leinfelder
        try {
1477
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1478
        } catch (Exception e) {
1479
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1480
        }
1481
1482
        return objectList;
1483 6644 cjones
  }
1484 6803 leinfelder
1485 7012 cjones
1486
 	/**
1487
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1488
 	 * @return cal  the list of checksum algorithms
1489
 	 *
1490
 	 * @throws ServiceFailure
1491
 	 * @throws NotImplemented
1492
 	 */
1493 6869 cjones
  @Override
1494 7012 cjones
  public ChecksumAlgorithmList listChecksumAlgorithms()
1495 6803 leinfelder
			throws ServiceFailure, NotImplemented {
1496
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1497
		cal.addAlgorithm("MD5");
1498
		cal.addAlgorithm("SHA-1");
1499 7012 cjones
		return cal;
1500
1501 6803 leinfelder
	}
1502 6657 cjones
1503 6177 cjones
}