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 7073 cjones
import org.dataone.client.MNode;
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 6792 cjones
import org.dataone.service.exceptions.BaseException;
45 6177 cjones
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotFound;
52
import org.dataone.service.exceptions.NotImplemented;
53
import org.dataone.service.exceptions.ServiceFailure;
54 6569 cjones
import org.dataone.service.exceptions.UnsupportedType;
55 6869 cjones
import org.dataone.service.exceptions.VersionMismatch;
56 6571 cjones
import org.dataone.service.types.v1.AccessPolicy;
57 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
58 6803 leinfelder
import org.dataone.service.types.v1.ChecksumAlgorithmList;
59 7144 leinfelder
import org.dataone.service.types.v1.DescribeResponse;
60
import org.dataone.service.types.v1.Event;
61 6366 leinfelder
import org.dataone.service.types.v1.Identifier;
62 7144 leinfelder
import org.dataone.service.types.v1.Log;
63 6463 cjones
import org.dataone.service.types.v1.Node;
64 6366 leinfelder
import org.dataone.service.types.v1.NodeList;
65 6409 cjones
import org.dataone.service.types.v1.NodeReference;
66 6570 cjones
import org.dataone.service.types.v1.NodeType;
67 6366 leinfelder
import org.dataone.service.types.v1.ObjectFormat;
68
import org.dataone.service.types.v1.ObjectFormatIdentifier;
69
import org.dataone.service.types.v1.ObjectFormatList;
70
import org.dataone.service.types.v1.ObjectList;
71
import org.dataone.service.types.v1.ObjectLocationList;
72
import org.dataone.service.types.v1.Permission;
73
import org.dataone.service.types.v1.Replica;
74
import org.dataone.service.types.v1.ReplicationPolicy;
75
import org.dataone.service.types.v1.ReplicationStatus;
76
import org.dataone.service.types.v1.Session;
77
import org.dataone.service.types.v1.Subject;
78
import org.dataone.service.types.v1.SystemMetadata;
79 6881 leinfelder
import org.dataone.service.types.v1.util.ServiceMethodRestrictionUtil;
80 7401 cjones
import org.dataone.service.types.v1_1.QueryEngineDescription;
81
import org.dataone.service.types.v1_1.QueryEngineList;
82 6177 cjones
83 6188 leinfelder
import edu.ucsb.nceas.metacat.EventLog;
84
import edu.ucsb.nceas.metacat.IdentifierManager;
85 8444 cjones
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
86 6446 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
87 6188 leinfelder
88 6177 cjones
/**
89
 * Represents Metacat's implementation of the DataONE Coordinating Node
90 6179 cjones
 * service API. Methods implement the various CN* interfaces, and methods common
91 6177 cjones
 * to both Member Node and Coordinating Node interfaces are found in the
92
 * D1NodeService super class.
93
 *
94
 */
95
public class CNodeService extends D1NodeService implements CNAuthorization,
96 6446 leinfelder
    CNCore, CNRead, CNReplication {
97 6177 cjones
98 6178 cjones
  /* the logger instance */
99
  private Logger logMetacat = null;
100 6177 cjones
101 6178 cjones
  /**
102
   * singleton accessor
103
   */
104 6542 leinfelder
  public static CNodeService getInstance(HttpServletRequest request) {
105
    return new CNodeService(request);
106 6178 cjones
  }
107
108
  /**
109
   * Constructor, private for singleton access
110
   */
111 6542 leinfelder
  private CNodeService(HttpServletRequest request) {
112
    super(request);
113 6178 cjones
    logMetacat = Logger.getLogger(CNodeService.class);
114
115
  }
116
117 6410 cjones
  /**
118
   * Set the replication policy for an object given the object identifier
119
   *
120
   * @param session - the Session object containing the credentials for the Subject
121
   * @param pid - the object identifier for the given object
122
   * @param policy - the replication policy to be applied
123
   *
124
   * @return true or false
125
   *
126
   * @throws NotImplemented
127
   * @throws NotAuthorized
128
   * @throws ServiceFailure
129
   * @throws InvalidRequest
130 6869 cjones
   * @throws VersionMismatch
131 6410 cjones
   *
132
   */
133 6471 jones
  @Override
134 6410 cjones
  public boolean setReplicationPolicy(Session session, Identifier pid,
135 6593 cjones
      ReplicationPolicy policy, long serialVersion)
136 6567 cjones
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
137 6869 cjones
      InvalidRequest, InvalidToken, VersionMismatch {
138 6567 cjones
139 6717 cjones
      // The lock to be used for this identifier
140 6859 cjones
      Lock lock = null;
141 6702 cjones
142 6567 cjones
      // get the subject
143
      Subject subject = session.getSubject();
144
145
      // are we allowed to do this?
146 7068 cjones
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
147
          throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION
148
                  + " not allowed by " + subject.getValue() + " on "
149
                  + pid.getValue());
150
151 6869 cjones
      }
152
153
      SystemMetadata systemMetadata = null;
154 6567 cjones
      try {
155 6858 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
156
          lock.lock();
157 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
158 6858 cjones
159
          try {
160
              if ( HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid) ) {
161
                  systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
162
163
              }
164 6869 cjones
165
              // did we get it correctly?
166
              if ( systemMetadata == null ) {
167
                  throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
168
169
              }
170 6858 cjones
171
              // does the request have the most current system metadata?
172
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
173
                 String msg = "The requested system metadata version number " +
174
                     serialVersion + " differs from the current version at " +
175
                     systemMetadata.getSerialVersion().longValue() +
176
                     ". Please get the latest copy in order to modify it.";
177 6869 cjones
                 throw new VersionMismatch("4886", msg);
178
179 6858 cjones
              }
180 6717 cjones
181 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
182 6858 cjones
              throw new NotFound("4884", "No record found for: " + pid.getValue());
183
184 6717 cjones
          }
185 6858 cjones
186
          // set the new policy
187
          systemMetadata.setReplicationPolicy(policy);
188
189
          // update the metadata
190
          try {
191
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
192
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
193
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
194 7076 cjones
              notifyReplicaNodes(systemMetadata);
195
196 6869 cjones
          } catch (RuntimeException e) {
197 6858 cjones
              throw new ServiceFailure("4882", e.getMessage());
198
199 6593 cjones
          }
200
201 6869 cjones
      } catch (RuntimeException e) {
202
          throw new ServiceFailure("4882", e.getMessage());
203
204
      } finally {
205
          lock.unlock();
206
          logMetacat.debug("Unlocked identifier " + pid.getValue());
207
208
      }
209 6410 cjones
210 6567 cjones
      return true;
211 6410 cjones
  }
212 6177 cjones
213 6410 cjones
  /**
214 6881 leinfelder
   * Deletes the replica from the given Member Node
215
   * NOTE: MN.delete() may be an "archive" operation. TBD.
216
   * @param session
217
   * @param pid
218
   * @param nodeId
219
   * @param serialVersion
220
   * @return
221
   * @throws InvalidToken
222
   * @throws ServiceFailure
223
   * @throws NotAuthorized
224
   * @throws NotFound
225
   * @throws NotImplemented
226 6883 leinfelder
   * @throws VersionMismatch
227 6881 leinfelder
   */
228 6884 leinfelder
  @Override
229 6881 leinfelder
  public boolean deleteReplicationMetadata(Session session, Identifier pid, NodeReference nodeId, long serialVersion)
230 6883 leinfelder
  	throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, VersionMismatch {
231 6881 leinfelder
232 6883 leinfelder
	  	// The lock to be used for this identifier
233
		Lock lock = null;
234
235
		// get the subject
236
		Subject subject = session.getSubject();
237
238
		// are we allowed to do this?
239 7068 cjones
		boolean isAuthorized = false;
240
		try {
241
			isAuthorized = isAuthorized(session, pid, Permission.WRITE);
242
		} catch (InvalidRequest e) {
243
			throw new ServiceFailure("4882", e.getDescription());
244
		}
245
		if (!isAuthorized) {
246
			throw new NotAuthorized("4881", Permission.WRITE
247
					+ " not allowed by " + subject.getValue() + " on "
248
					+ pid.getValue());
249 6883 leinfelder
250
		}
251
252
		SystemMetadata systemMetadata = null;
253
		try {
254
			lock = HazelcastService.getInstance().getLock(pid.getValue());
255
			lock.lock();
256
			logMetacat.debug("Locked identifier " + pid.getValue());
257
258
			try {
259
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
260
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
261
				}
262
263
				// did we get it correctly?
264
				if (systemMetadata == null) {
265
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
266
				}
267
268
				// does the request have the most current system metadata?
269
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
270
					String msg = "The requested system metadata version number "
271
							+ serialVersion
272
							+ " differs from the current version at "
273
							+ systemMetadata.getSerialVersion().longValue()
274
							+ ". Please get the latest copy in order to modify it.";
275
					throw new VersionMismatch("4886", msg);
276
277
				}
278
279
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
280
				throw new NotFound("4884", "No record found for: " + pid.getValue());
281
282
			}
283
284
			// check permissions
285
			// TODO: is this necessary?
286
			List<Node> nodeList = D1Client.getCN().listNodes().getNodeList();
287
			boolean isAllowed = ServiceMethodRestrictionUtil.isMethodAllowed(session.getSubject(), nodeList, "CNReplication", "deleteReplicationMetadata");
288 7218 cjones
			if (!isAllowed) {
289 6883 leinfelder
				throw new NotAuthorized("4881", "Caller is not authorized to deleteReplicationMetadata");
290
			}
291
292
			// delete the replica from the given node
293 7252 cjones
			// CSJ: use CN.delete() to truly delete a replica, semantically
294
			// deleteReplicaMetadata() only modifies the sytem metadata entry.
295 7251 cjones
			//D1Client.getMN(nodeId).delete(session, pid);
296 6883 leinfelder
297
			// reflect that change in the system metadata
298
			List<Replica> updatedReplicas = new ArrayList<Replica>(systemMetadata.getReplicaList());
299
			for (Replica r: systemMetadata.getReplicaList()) {
300
				  if (r.getReplicaMemberNode().equals(nodeId)) {
301
					  updatedReplicas.remove(r);
302
					  break;
303
				  }
304
			}
305
			systemMetadata.setReplicaList(updatedReplicas);
306
307
			// update the metadata
308
			try {
309
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
310
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
311
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
312
			} catch (RuntimeException e) {
313
				throw new ServiceFailure("4882", e.getMessage());
314
			}
315
316
		} catch (RuntimeException e) {
317
			throw new ServiceFailure("4882", e.getMessage());
318
		} finally {
319
			lock.unlock();
320
			logMetacat.debug("Unlocked identifier " + pid.getValue());
321
		}
322
323
		return true;
324 6881 leinfelder
325
  }
326
327 6883 leinfelder
  /**
328 8444 cjones
   * Deletes an object from the Coordinating Node
329 7077 leinfelder
   *
330
   * @param session - the Session object containing the credentials for the Subject
331
   * @param pid - The object identifier to be deleted
332
   *
333
   * @return pid - the identifier of the object used for the deletion
334
   *
335
   * @throws InvalidToken
336
   * @throws ServiceFailure
337
   * @throws NotAuthorized
338
   * @throws NotFound
339
   * @throws NotImplemented
340
   * @throws InvalidRequest
341
   */
342
  @Override
343
  public Identifier delete(Session session, Identifier pid)
344
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
345 8444 cjones
346 8450 cjones
      String localId = null;      // The corresponding docid for this pid
347
	  Lock lock = null;           // The lock to be used for this identifier
348
      CNode cn = null;            // a reference to the CN to get the node list
349
      NodeType nodeType = null;   // the nodeType of the replica node being contacted
350
      List<Node> nodeList = null; // the list of nodes in this CN environment
351 7077 leinfelder
352 8444 cjones
      // check for a valid session
353
      if (session == null) {
354
        	throw new InvalidToken("4963", "No session has been provided");
355
356
      }
357
358
      // do we have a valid pid?
359
      if (pid == null || pid.getValue().trim().equals("")) {
360
          throw new ServiceFailure("4960", "The provided identifier was invalid.");
361
362
      }
363
364 7078 leinfelder
	  // check that it is CN/admin
365 7142 leinfelder
	  boolean allowed = isAdminAuthorized(session);
366 7077 leinfelder
367 8360 tao
	  // additional check if it is the authoritative node if it is not the admin
368
      if(!allowed) {
369
          allowed = isAuthoritativeMNodeAdmin(session, pid);
370 8444 cjones
371 8360 tao
      }
372
373 7078 leinfelder
	  if (!allowed) {
374 8444 cjones
		  String msg = "The subject " + session.getSubject().getValue() +
375
			  " is not allowed to call delete() on a Coordinating Node.";
376 7078 leinfelder
		  logMetacat.info(msg);
377 8444 cjones
		  throw new NotAuthorized("4960", msg);
378
379 7078 leinfelder
	  }
380
381 8444 cjones
	  // Don't defer to superclass implementation without a locally registered identifier
382
383
      // Check for the existing identifier
384
      try {
385
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
386
          super.delete(session, pid);
387
388
      } catch (McdbDocNotFoundException e) {
389
          // This object is not registered in the identifier table. Assume it is of formatType DATA,
390
    	  // and set the archive flag. (i.e. the *object* doesn't exist on the CN)
391
392
          try {
393
  			  lock = HazelcastService.getInstance().getLock(pid.getValue());
394
  			  lock.lock();
395
  			  logMetacat.debug("Locked identifier " + pid.getValue());
396
397
			  SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
398
			  if ( sysMeta != null ) {
399
				sysMeta.setArchived(true);
400
				sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
401
				HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
402
403
			  } else {
404
				  throw new ServiceFailure("4962", "Couldn't delete the object " + pid.getValue() +
405
					  ". Couldn't obtain the system metadata record.");
406
407
			  }
408
409
		  } catch (RuntimeException re) {
410
			  throw new ServiceFailure("4962", "Couldn't delete " + pid.getValue() +
411
				  ". The error message was: " + re.getMessage());
412
413
		  } finally {
414
			  lock.unlock();
415
			  logMetacat.debug("Unlocked identifier " + pid.getValue());
416
417
		  }
418
419
          // Log the delete
420
          EventLog.getInstance().log(request.getRemoteAddr(),
421
                  request.getHeader("User-Agent"), session.getSubject().getValue(),
422
                  pid.getValue(), Event.DELETE.xmlValue());
423
424
      }
425
426 8450 cjones
      // get the node list
427
      try {
428
          cn = D1Client.getCN();
429
          nodeList = cn.listNodes().getNodeList();
430
431
      } catch (Exception e) { // handle BaseException and other I/O issues
432
433
          // swallow errors since the call is not critical
434
          logMetacat.error("Can't inform MNs of the deletion of " + pid.getValue() +
435
              " due to communication issues with the CN: " + e.getMessage());
436
437
      }
438
439 7147 leinfelder
	  // notify the replicas
440
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
441
	  if (systemMetadata.getReplicaList() != null) {
442
		  for (Replica replica: systemMetadata.getReplicaList()) {
443
			  NodeReference replicaNode = replica.getReplicaMemberNode();
444
			  try {
445 8450 cjones
                  if (nodeList != null) {
446
                      // find the node type
447
                      for (Node node : nodeList) {
448
                          if ( node.getIdentifier().getValue().equals(replicaNode.getValue()) ) {
449
                              nodeType = node.getType();
450
                              break;
451
452
                          }
453
                      }
454
                  }
455
456
                  // only send call MN.delete() to avoid an infinite loop with the CN
457
                  if (nodeType != null && nodeType == NodeType.MN) {
458
				      Identifier mnRetId = D1Client.getMN(replicaNode).delete(null, pid);
459
                  }
460
461 7147 leinfelder
			  } catch (Exception e) {
462
				  // all we can really do is log errors and carry on with life
463 8450 cjones
				  logMetacat.error("Error deleting pid: " +  pid.getValue() +
464
					  " from replica MN: " + replicaNode.getValue(), e);
465 7147 leinfelder
			}
466
467
		  }
468
	  }
469
470 8444 cjones
	  return pid;
471 7147 leinfelder
472 7077 leinfelder
  }
473
474
  /**
475 8444 cjones
   * Archives an object from the Coordinating Node
476 7148 leinfelder
   *
477
   * @param session - the Session object containing the credentials for the Subject
478
   * @param pid - The object identifier to be deleted
479
   *
480
   * @return pid - the identifier of the object used for the deletion
481
   *
482
   * @throws InvalidToken
483
   * @throws ServiceFailure
484
   * @throws NotAuthorized
485
   * @throws NotFound
486
   * @throws NotImplemented
487
   * @throws InvalidRequest
488
   */
489
  @Override
490
  public Identifier archive(Session session, Identifier pid)
491
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
492
493 8444 cjones
      String localId = null; // The corresponding docid for this pid
494
	  Lock lock = null;      // The lock to be used for this identifier
495 8450 cjones
      CNode cn = null;            // a reference to the CN to get the node list
496
      NodeType nodeType = null;   // the nodeType of the replica node being contacted
497
      List<Node> nodeList = null; // the list of nodes in this CN environment
498
499 8444 cjones
500
      // check for a valid session
501
      if (session == null) {
502
        	throw new InvalidToken("4973", "No session has been provided");
503
504
      }
505
506
      // do we have a valid pid?
507
      if (pid == null || pid.getValue().trim().equals("")) {
508
          throw new ServiceFailure("4972", "The provided identifier was invalid.");
509
510
      }
511
512 7148 leinfelder
	  // check that it is CN/admin
513
	  boolean allowed = isAdminAuthorized(session);
514
515 8360 tao
	  //check if it is the authoritative member node
516
	  if(!allowed) {
517
	      allowed = isAuthoritativeMNodeAdmin(session, pid);
518
	  }
519
520 7148 leinfelder
	  if (!allowed) {
521 8444 cjones
		  String msg = "The subject " + session.getSubject().getValue() +
522
				  " is not allowed to call archive() on a Coordinating Node.";
523 7148 leinfelder
		  logMetacat.info(msg);
524 8444 cjones
		  throw new NotAuthorized("4970", msg);
525 7148 leinfelder
	  }
526
527 8444 cjones
      // Check for the existing identifier
528
      try {
529
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
530
          super.archive(session, pid);
531
532
      } catch (McdbDocNotFoundException e) {
533
          // This object is not registered in the identifier table. Assume it is of formatType DATA,
534
    	  // and set the archive flag. (i.e. the *object* doesn't exist on the CN)
535
536
          try {
537
  			  lock = HazelcastService.getInstance().getLock(pid.getValue());
538
  			  lock.lock();
539
  			  logMetacat.debug("Locked identifier " + pid.getValue());
540
541
			  SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
542
			  if ( sysMeta != null ) {
543
				sysMeta.setArchived(true);
544
				sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
545
				HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
546
547
			  } else {
548
				  throw new ServiceFailure("4972", "Couldn't archive the object " + pid.getValue() +
549
					  ". Couldn't obtain the system metadata record.");
550
551
			  }
552
553
		  } catch (RuntimeException re) {
554
			  throw new ServiceFailure("4972", "Couldn't archive " + pid.getValue() +
555
				  ". The error message was: " + re.getMessage());
556
557
		  } finally {
558
			  lock.unlock();
559
			  logMetacat.debug("Unlocked identifier " + pid.getValue());
560
561
		  }
562
563
          // Log the archive
564
          EventLog.getInstance().log(request.getRemoteAddr(),
565
                  request.getHeader("User-Agent"), session.getSubject().getValue(),
566
                  pid.getValue(), Event.DELETE.xmlValue());
567
568
      }
569 8450 cjones
570
      // get the node list
571
      try {
572
          cn = D1Client.getCN();
573
          nodeList = cn.listNodes().getNodeList();
574
575
      } catch (Exception e) { // handle BaseException and other I/O issues
576
577
          // swallow errors since the call is not critical
578
          logMetacat.error("Can't inform MNs of the archive of " + pid.getValue() +
579
              " due to communication issues with the CN: " + e.getMessage());
580
581
      }
582
583 7148 leinfelder
	  // notify the replicas
584
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
585
	  if (systemMetadata.getReplicaList() != null) {
586
		  for (Replica replica: systemMetadata.getReplicaList()) {
587
			  NodeReference replicaNode = replica.getReplicaMemberNode();
588
			  try {
589 8450 cjones
                  if (nodeList != null) {
590
                      // find the node type
591
                      for (Node node : nodeList) {
592
                          if ( node.getIdentifier().getValue().equals(replicaNode.getValue()) ) {
593
                              nodeType = node.getType();
594
                              break;
595
596
                          }
597
                      }
598
                  }
599
600
                  // only send call MN.archive() to avoid an infinite loop with the CN
601
                  if (nodeType != null && nodeType == NodeType.MN) {
602
				      Identifier mnRetId = D1Client.getMN(replicaNode).archive(null, pid);
603
604
                  }
605
606 7148 leinfelder
			  } catch (Exception e) {
607
				  // all we can really do is log errors and carry on with life
608 8450 cjones
				  logMetacat.error("Error archiving pid: " +  pid.getValue() +
609
					  " from replica MN: " + replicaNode.getValue(), e);
610 7148 leinfelder
			}
611
612
		  }
613
	  }
614
615 8444 cjones
	  return pid;
616 7148 leinfelder
617
  }
618
619
  /**
620 6883 leinfelder
   * Set the obsoletedBy attribute in System Metadata
621
   * @param session
622
   * @param pid
623
   * @param obsoletedByPid
624
   * @param serialVersion
625
   * @return
626
   * @throws NotImplemented
627
   * @throws NotFound
628
   * @throws NotAuthorized
629
   * @throws ServiceFailure
630
   * @throws InvalidRequest
631
   * @throws InvalidToken
632
   * @throws VersionMismatch
633
   */
634 6884 leinfelder
  @Override
635 6883 leinfelder
  public boolean setObsoletedBy(Session session, Identifier pid,
636
			Identifier obsoletedByPid, long serialVersion)
637
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
638
			InvalidRequest, InvalidToken, VersionMismatch {
639
640
		// The lock to be used for this identifier
641
		Lock lock = null;
642
643
		// get the subject
644
		Subject subject = session.getSubject();
645
646
		// are we allowed to do this?
647 7068 cjones
		if (!isAuthorized(session, pid, Permission.WRITE)) {
648
			throw new NotAuthorized("4881", Permission.WRITE
649
					+ " not allowed by " + subject.getValue() + " on "
650
					+ pid.getValue());
651 6883 leinfelder
652
		}
653
654 7068 cjones
655 6883 leinfelder
		SystemMetadata systemMetadata = null;
656
		try {
657
			lock = HazelcastService.getInstance().getLock(pid.getValue());
658
			lock.lock();
659
			logMetacat.debug("Locked identifier " + pid.getValue());
660
661
			try {
662
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
663
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
664
				}
665
666
				// did we get it correctly?
667
				if (systemMetadata == null) {
668
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
669
				}
670
671
				// does the request have the most current system metadata?
672
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
673
					String msg = "The requested system metadata version number "
674
							+ serialVersion
675
							+ " differs from the current version at "
676
							+ systemMetadata.getSerialVersion().longValue()
677
							+ ". Please get the latest copy in order to modify it.";
678
					throw new VersionMismatch("4886", msg);
679
680
				}
681
682
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
683
				throw new NotFound("4884", "No record found for: " + pid.getValue());
684
685
			}
686
687
			// set the new policy
688
			systemMetadata.setObsoletedBy(obsoletedByPid);
689
690
			// update the metadata
691
			try {
692
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
693
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
694
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
695
			} catch (RuntimeException e) {
696
				throw new ServiceFailure("4882", e.getMessage());
697
			}
698
699
		} catch (RuntimeException e) {
700
			throw new ServiceFailure("4882", e.getMessage());
701
		} finally {
702
			lock.unlock();
703
			logMetacat.debug("Unlocked identifier " + pid.getValue());
704
		}
705
706
		return true;
707
	}
708 6881 leinfelder
709 6883 leinfelder
710 6881 leinfelder
  /**
711 6410 cjones
   * Set the replication status for an object given the object identifier
712
   *
713
   * @param session - the Session object containing the credentials for the Subject
714
   * @param pid - the object identifier for the given object
715
   * @param status - the replication status to be applied
716
   *
717
   * @return true or false
718
   *
719
   * @throws NotImplemented
720
   * @throws NotAuthorized
721
   * @throws ServiceFailure
722
   * @throws InvalidRequest
723
   * @throws InvalidToken
724
   * @throws NotFound
725
   *
726
   */
727 6471 jones
  @Override
728 6410 cjones
  public boolean setReplicationStatus(Session session, Identifier pid,
729 6792 cjones
      NodeReference targetNode, ReplicationStatus status, BaseException failure)
730 6644 cjones
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized,
731
      InvalidRequest, NotFound {
732 7066 leinfelder
733
	  // cannot be called by public
734
	  if (session == null) {
735
		  throw new NotAuthorized("4720", "Session cannot be null");
736
	  }
737 6644 cjones
738 6702 cjones
      // The lock to be used for this identifier
739 6859 cjones
      Lock lock = null;
740 6702 cjones
741 6644 cjones
      boolean allowed = false;
742
      int replicaEntryIndex = -1;
743
      List<Replica> replicas = null;
744
      // get the subject
745
      Subject subject = session.getSubject();
746 6676 cjones
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
747
          " is " + status.toString());
748 6644 cjones
749
      SystemMetadata systemMetadata = null;
750 6858 cjones
751
      try {
752 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
753 6702 cjones
          lock.lock();
754 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
755
756 6858 cjones
          try {
757
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
758 6657 cjones
759 6869 cjones
              // did we get it correctly?
760 6858 cjones
              if ( systemMetadata == null ) {
761
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
762 6869 cjones
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
763 6858 cjones
764
              }
765
              replicas = systemMetadata.getReplicaList();
766
              int count = 0;
767 6657 cjones
768 6876 cjones
              // was there a failure? log it
769 7600 cjones
              if ( failure != null && status.equals(ReplicationStatus.FAILED) ) {
770 6876 cjones
                 String msg = "The replication request of the object identified by " +
771
                     pid.getValue() + " failed.  The error message was " +
772
                     failure.getMessage() + ".";
773 6858 cjones
              }
774 6869 cjones
775 6876 cjones
              if (replicas.size() > 0 && replicas != null) {
776
                  // find the target replica index in the replica list
777
                  for (Replica replica : replicas) {
778 7600 cjones
                      String replicaNodeStr = replica.getReplicaMemberNode().getValue();
779 6876 cjones
                      String targetNodeStr = targetNode.getValue();
780 7600 cjones
                      logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
781 6869 cjones
782 6876 cjones
                      if (replicaNodeStr.equals(targetNodeStr)) {
783
                          replicaEntryIndex = count;
784
                          logMetacat.debug("replica entry index is: "
785
                                  + replicaEntryIndex);
786
                          break;
787
                      }
788
                      count++;
789 6858 cjones
790
                  }
791
              }
792
              // are we allowed to do this? only CNs and target MNs are allowed
793
              CNode cn = D1Client.getCN();
794
              List<Node> nodes = cn.listNodes().getNodeList();
795 6657 cjones
796 6858 cjones
              // find the node in the node list
797
              for ( Node node : nodes ) {
798
799
                  NodeReference nodeReference = node.getIdentifier();
800 7600 cjones
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " +
801
                      nodeReference.getValue());
802 6858 cjones
803 7074 cjones
                  // allow target MN certs
804 7600 cjones
                  if ( targetNode.getValue().equals(nodeReference.getValue() ) &&
805
                      node.getType().equals(NodeType.MN)) {
806 6858 cjones
                      List<Subject> nodeSubjects = node.getSubjectList();
807
808
                      // check if the session subject is in the node subject list
809
                      for (Subject nodeSubject : nodeSubjects) {
810 7071 cjones
                          logMetacat.debug("In setReplicationStatus(), comparing subjects: " +
811
                                  nodeSubject.getValue() + " and " + subject.getValue());
812 7074 cjones
                          if ( nodeSubject.equals(subject) ) { // subject of session == target node subject
813 6858 cjones
814 7179 cjones
                              // lastly limit to COMPLETED, INVALIDATED,
815
                              // and FAILED status updates from MNs only
816 7600 cjones
                              if ( status.equals(ReplicationStatus.COMPLETED) ||
817
                                   status.equals(ReplicationStatus.INVALIDATED) ||
818
                                   status.equals(ReplicationStatus.FAILED)) {
819 7074 cjones
                                  allowed = true;
820
                                  break;
821
822
                              }
823 6858 cjones
                          }
824
                      }
825
                  }
826
              }
827 6657 cjones
828 7071 cjones
              if ( !allowed ) {
829
                  //check for CN admin access
830
                  allowed = isAuthorized(session, pid, Permission.WRITE);
831 6858 cjones
832 7071 cjones
              }
833
834
              if ( !allowed ) {
835
                  String msg = "The subject identified by "
836
                          + subject.getValue()
837
                          + " does not have permission to set the replication status for "
838
                          + "the replica identified by "
839
                          + targetNode.getValue() + ".";
840
                  logMetacat.info(msg);
841
                  throw new NotAuthorized("4720", msg);
842
843 6858 cjones
              }
844 6876 cjones
845 6858 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
846
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
847
                " : " + e.getMessage());
848
849 6644 cjones
          }
850
851 6876 cjones
          Replica targetReplica = new Replica();
852 6858 cjones
          // set the status for the replica
853
          if ( replicaEntryIndex != -1 ) {
854 6876 cjones
              targetReplica = replicas.get(replicaEntryIndex);
855 7231 cjones
856
              // don't allow status to change from COMPLETED to anything other
857
              // than INVALIDATED: prevents overwrites from race conditions
858 7600 cjones
              if ( targetReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
859
            	   !status.equals(ReplicationStatus.INVALIDATED)) {
860 7231 cjones
            	  throw new InvalidRequest("4730", "Status state change from " +
861
            			  targetReplica.getReplicationStatus() + " to " +
862
            			  status.toString() + "is prohibited for identifier " +
863
            			  pid.getValue() + " and target node " +
864
            			  targetReplica.getReplicaMemberNode().getValue());
865
              }
866
867 6858 cjones
              targetReplica.setReplicationStatus(status);
868 7514 cjones
869 6858 cjones
              logMetacat.debug("Set the replication status for " +
870
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
871 7231 cjones
                  targetReplica.getReplicationStatus() + " for identifier " +
872
                  pid.getValue());
873 6644 cjones
874 6858 cjones
          } else {
875 6876 cjones
              // this is a new entry, create it
876
              targetReplica.setReplicaMemberNode(targetNode);
877
              targetReplica.setReplicationStatus(status);
878
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
879
              replicas.add(targetReplica);
880
881 6644 cjones
          }
882
883 6858 cjones
          systemMetadata.setReplicaList(replicas);
884
885
          // update the metadata
886
          try {
887
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
888
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
889
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
890 7179 cjones
891 7600 cjones
              if ( !status.equals(ReplicationStatus.QUEUED) &&
892
            	   !status.equals(ReplicationStatus.REQUESTED)) {
893 7179 cjones
894
                logMetacat.trace("METRICS:\tREPLICATION:\tEND REQUEST:\tPID:\t" + pid.getValue() +
895
                          "\tNODE:\t" + targetNode.getValue() +
896
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
897
898
                logMetacat.trace("METRICS:\tREPLICATION:\t" + status.toString().toUpperCase() +
899
                          "\tPID:\t"  + pid.getValue() +
900
                          "\tNODE:\t" + targetNode.getValue() +
901
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
902
              }
903
904 7600 cjones
              if ( status.equals(ReplicationStatus.FAILED) && failure != null ) {
905 6858 cjones
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
906
                      " on target node " + targetNode + ". The exception was: " +
907
                      failure.getMessage());
908
              }
909 7514 cjones
910
			  // update the replica nodes about the completed replica when complete
911 7600 cjones
              if (status.equals(ReplicationStatus.COMPLETED)) {
912 8439 cjones
				notifyReplicaNodes(systemMetadata);
913 7514 cjones
			}
914
915 6869 cjones
          } catch (RuntimeException e) {
916 6858 cjones
              throw new ServiceFailure("4700", e.getMessage());
917
918 6644 cjones
          }
919
920 6867 cjones
    } catch (RuntimeException e) {
921
        String msg = "There was a RuntimeException getting the lock for " +
922
            pid.getValue();
923
        logMetacat.info(msg);
924
925
    } finally {
926 6858 cjones
        lock.unlock();
927
        logMetacat.debug("Unlocked identifier " + pid.getValue());
928 6593 cjones
929 6858 cjones
    }
930 6676 cjones
931 6644 cjones
      return true;
932 6410 cjones
  }
933
934 7514 cjones
/**
935 6410 cjones
   * Return the checksum of the object given the identifier
936
   *
937
   * @param session - the Session object containing the credentials for the Subject
938
   * @param pid - the object identifier for the given object
939
   *
940
   * @return checksum - the checksum of the object
941
   *
942
   * @throws InvalidToken
943
   * @throws ServiceFailure
944
   * @throws NotAuthorized
945
   * @throws NotFound
946
   * @throws NotImplemented
947
   */
948 6471 jones
  @Override
949 6410 cjones
  public Checksum getChecksum(Session session, Identifier pid)
950
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
951 6622 leinfelder
    NotImplemented {
952 7029 leinfelder
953
	boolean isAuthorized = false;
954
	try {
955
		isAuthorized = isAuthorized(session, pid, Permission.READ);
956
	} catch (InvalidRequest e) {
957
		throw new ServiceFailure("1410", e.getDescription());
958
	}
959
    if (!isAuthorized) {
960 6568 cjones
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());
961 6410 cjones
    }
962 6568 cjones
963 6410 cjones
    SystemMetadata systemMetadata = null;
964 6568 cjones
    Checksum checksum = null;
965
966 6410 cjones
    try {
967 6869 cjones
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
968
969
        if (systemMetadata == null ) {
970
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
971
        }
972 6568 cjones
        checksum = systemMetadata.getChecksum();
973 6869 cjones
974
    } catch (RuntimeException e) {
975 6568 cjones
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " +
976
            pid.getValue() + ". The error message was: " + e.getMessage());
977
978 6410 cjones
    }
979
980
    return checksum;
981
  }
982 6177 cjones
983 6410 cjones
  /**
984
   * Resolve the location of a given object
985
   *
986
   * @param session - the Session object containing the credentials for the Subject
987
   * @param pid - the object identifier for the given object
988
   *
989
   * @return objectLocationList - the list of nodes known to contain the object
990
   *
991
   * @throws InvalidToken
992
   * @throws ServiceFailure
993
   * @throws NotAuthorized
994
   * @throws NotFound
995
   * @throws NotImplemented
996
   */
997 6471 jones
  @Override
998 6410 cjones
  public ObjectLocationList resolve(Session session, Identifier pid)
999 6622 leinfelder
    throws InvalidToken, ServiceFailure, NotAuthorized,
1000 6410 cjones
    NotFound, NotImplemented {
1001 6177 cjones
1002 6410 cjones
    throw new NotImplemented("4131", "resolve not implemented");
1003 6303 leinfelder
1004 6410 cjones
  }
1005 6177 cjones
1006 6410 cjones
  /**
1007 7464 leinfelder
   * Metacat does not implement this method at the CN level
1008 6410 cjones
   */
1009 6471 jones
  @Override
1010 6410 cjones
  public ObjectList search(Session session, String queryType, String query)
1011
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
1012
    NotImplemented {
1013 6177 cjones
1014 7464 leinfelder
		  throw new NotImplemented("4281", "Metacat does not implement CN.search");
1015
1016
//    ObjectList objectList = null;
1017
//    try {
1018
//        objectList =
1019
//          IdentifierManager.getInstance().querySystemMetadata(
1020
//              null, //startTime,
1021
//              null, //endTime,
1022
//              null, //objectFormat,
1023
//              false, //replicaStatus,
1024
//              0, //start,
1025
//              1000 //count
1026
//              );
1027
//
1028
//    } catch (Exception e) {
1029
//      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
1030
//    }
1031
//
1032
//      return objectList;
1033
1034 6410 cjones
  }
1035
1036
  /**
1037
   * Returns the object format registered in the DataONE Object Format
1038
   * Vocabulary for the given format identifier
1039
   *
1040
   * @param fmtid - the identifier of the format requested
1041
   *
1042
   * @return objectFormat - the object format requested
1043
   *
1044
   * @throws ServiceFailure
1045
   * @throws NotFound
1046
   * @throws InsufficientResources
1047
   * @throws NotImplemented
1048
   */
1049 6471 jones
  @Override
1050 6410 cjones
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
1051 6803 leinfelder
    throws ServiceFailure, NotFound, NotImplemented {
1052 6410 cjones
1053
      return ObjectFormatService.getInstance().getFormat(fmtid);
1054
1055
  }
1056 6177 cjones
1057 6410 cjones
  /**
1058 6177 cjones
   * Returns a list of all object formats registered in the DataONE Object
1059
   * Format Vocabulary
1060 6410 cjones
    *
1061
   * @return objectFormatList - The list of object formats registered in
1062
   *                            the DataONE Object Format Vocabulary
1063
   *
1064
   * @throws ServiceFailure
1065
   * @throws NotImplemented
1066
   * @throws InsufficientResources
1067
   */
1068 6471 jones
  @Override
1069 6410 cjones
  public ObjectFormatList listFormats()
1070 6803 leinfelder
    throws ServiceFailure, NotImplemented {
1071 6177 cjones
1072 6410 cjones
    return ObjectFormatService.getInstance().listFormats();
1073
  }
1074 6177 cjones
1075 6410 cjones
  /**
1076 6177 cjones
   * Returns a list of nodes that have been registered with the DataONE infrastructure
1077 6410 cjones
    *
1078
   * @return nodeList - List of nodes from the registry
1079
   *
1080
   * @throws ServiceFailure
1081
   * @throws NotImplemented
1082
   */
1083 6471 jones
  @Override
1084 6410 cjones
  public NodeList listNodes()
1085
    throws NotImplemented, ServiceFailure {
1086 6177 cjones
1087 6410 cjones
    throw new NotImplemented("4800", "listNodes not implemented");
1088
  }
1089 6177 cjones
1090 6410 cjones
  /**
1091 6177 cjones
   * Provides a mechanism for adding system metadata independently of its
1092
   * associated object, such as when adding system metadata for data objects.
1093 6410 cjones
    *
1094
   * @param session - the Session object containing the credentials for the Subject
1095
   * @param pid - The identifier of the object to register the system metadata against
1096
   * @param sysmeta - The system metadata to be registered
1097
   *
1098
   * @return true if the registration succeeds
1099
   *
1100
   * @throws NotImplemented
1101
   * @throws NotAuthorized
1102
   * @throws ServiceFailure
1103
   * @throws InvalidRequest
1104
   * @throws InvalidSystemMetadata
1105
   */
1106 6471 jones
  @Override
1107 6575 cjones
  public Identifier registerSystemMetadata(Session session, Identifier pid,
1108
      SystemMetadata sysmeta)
1109
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1110
      InvalidSystemMetadata {
1111 6177 cjones
1112 6702 cjones
      // The lock to be used for this identifier
1113 6859 cjones
      Lock lock = null;
1114 6702 cjones
1115 6575 cjones
      // TODO: control who can call this?
1116
      if (session == null) {
1117
          //TODO: many of the thrown exceptions do not use the correct error codes
1118
          //check these against the docs and correct them
1119
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
1120
                  "  If you are not logged in, please do so and retry the request.");
1121
      }
1122
1123
      // verify that guid == SystemMetadata.getIdentifier()
1124
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() +
1125
          "|" + sysmeta.getIdentifier().getValue());
1126
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
1127
          throw new InvalidRequest("4863",
1128
              "The identifier in method call (" + pid.getValue() +
1129
              ") does not match identifier in system metadata (" +
1130
              sysmeta.getIdentifier().getValue() + ").");
1131
      }
1132 6188 leinfelder
1133 6575 cjones
      try {
1134 6703 cjones
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
1135 6863 cjones
          lock.lock();
1136 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1137 6863 cjones
          logMetacat.debug("Checking if identifier exists...");
1138
          // Check that the identifier does not already exist
1139
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
1140
              throw new InvalidRequest("4863",
1141
                  "The identifier is already in use by an existing object.");
1142 6575 cjones
1143 6863 cjones
          }
1144
1145
          // insert the system metadata into the object store
1146
          logMetacat.debug("Starting to insert SystemMetadata...");
1147
          try {
1148
              sysmeta.setSerialVersion(BigInteger.ONE);
1149
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1150
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
1151
1152 6869 cjones
          } catch (RuntimeException e) {
1153 6863 cjones
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
1154
              throw new ServiceFailure("4862", "Error inserting system metadata: " +
1155
                  e.getClass() + ": " + e.getMessage());
1156
1157
          }
1158
1159 6869 cjones
      } catch (RuntimeException e) {
1160 6575 cjones
          throw new ServiceFailure("4862", "Error inserting system metadata: " +
1161 6863 cjones
                  e.getClass() + ": " + e.getMessage());
1162 6575 cjones
1163 6863 cjones
      }  finally {
1164
          lock.unlock();
1165
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1166
1167
      }
1168 6466 cjones
1169 6575 cjones
1170
      logMetacat.debug("Returning from registerSystemMetadata");
1171
      EventLog.getInstance().log(request.getRemoteAddr(),
1172
          request.getHeader("User-Agent"), session.getSubject().getValue(),
1173
          pid.getValue(), "registerSystemMetadata");
1174
      return pid;
1175 6410 cjones
  }
1176
1177
  /**
1178 6177 cjones
   * Given an optional scope and format, reserves and returns an identifier
1179
   * within that scope and format that is unique and will not be
1180
   * used by any other sessions.
1181 6410 cjones
    *
1182
   * @param session - the Session object containing the credentials for the Subject
1183
   * @param pid - The identifier of the object to register the system metadata against
1184
   * @param scope - An optional string to be used to qualify the scope of
1185
   *                the identifier namespace, which is applied differently
1186
   *                depending on the format requested. If scope is not
1187
   *                supplied, a default scope will be used.
1188
   * @param format - The optional name of the identifier format to be used,
1189
   *                  drawn from a DataONE-specific vocabulary of identifier
1190
   *                 format names, including several common syntaxes such
1191
   *                 as DOI, LSID, UUID, and LSRN, among others. If the
1192
   *                 format is not supplied by the caller, the CN service
1193
   *                 will use a default identifier format, which may change
1194
   *                 over time.
1195
   *
1196
   * @return true if the registration succeeds
1197
   *
1198
   * @throws InvalidToken
1199
   * @throws ServiceFailure
1200
   * @throws NotAuthorized
1201
   * @throws IdentifierNotUnique
1202
   * @throws NotImplemented
1203
   */
1204 6471 jones
  @Override
1205 6622 leinfelder
  public Identifier reserveIdentifier(Session session, Identifier pid)
1206 6410 cjones
  throws InvalidToken, ServiceFailure,
1207 6378 leinfelder
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
1208 6177 cjones
1209 6410 cjones
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
1210
  }
1211
1212 6471 jones
  @Override
1213 6410 cjones
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
1214
  throws InvalidToken, ServiceFailure,
1215 6378 leinfelder
        NotAuthorized, NotImplemented, InvalidRequest {
1216 6410 cjones
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
1217
  }
1218
1219
  /**
1220
    * Checks whether the pid is reserved by the subject in the session param
1221
    * If the reservation is held on the pid by the subject, we return true.
1222
    *
1223
   * @param session - the Session object containing the Subject
1224
   * @param pid - The identifier to check
1225
   *
1226
   * @return true if the reservation exists for the subject/pid
1227
   *
1228
   * @throws InvalidToken
1229
   * @throws ServiceFailure
1230
   * @throws NotFound - when the pid is not found (in use or in reservation)
1231
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
1232
   * @throws IdentifierNotUnique - when the pid is in use
1233
   * @throws NotImplemented
1234
   */
1235 6177 cjones
1236 6471 jones
  @Override
1237 6934 leinfelder
  public boolean hasReservation(Session session, Subject subject, Identifier pid)
1238 6410 cjones
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique,
1239
      NotImplemented, InvalidRequest {
1240
1241
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1242
  }
1243 6339 leinfelder
1244 6410 cjones
  /**
1245 6177 cjones
   * Changes ownership (RightsHolder) of the specified object to the
1246
   * subject specified by userId
1247 6410 cjones
    *
1248
   * @param session - the Session object containing the credentials for the Subject
1249
   * @param pid - Identifier of the object to be modified
1250
   * @param userId - The subject that will be taking ownership of the specified object.
1251
   *
1252
   * @return pid - the identifier of the modified object
1253
   *
1254
   * @throws ServiceFailure
1255
   * @throws InvalidToken
1256
   * @throws NotFound
1257
   * @throws NotAuthorized
1258
   * @throws NotImplemented
1259
   * @throws InvalidRequest
1260
   */
1261 6471 jones
  @Override
1262 6803 leinfelder
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1263 6593 cjones
      long serialVersion)
1264
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1265 6869 cjones
      NotImplemented, InvalidRequest, VersionMismatch {
1266 6593 cjones
1267 6702 cjones
      // The lock to be used for this identifier
1268 6859 cjones
      Lock lock = null;
1269 6702 cjones
1270 6593 cjones
      // get the subject
1271
      Subject subject = session.getSubject();
1272
1273
      // are we allowed to do this?
1274 7068 cjones
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1275
          throw new NotAuthorized("4440", "not allowed by "
1276
                  + subject.getValue() + " on " + pid.getValue());
1277
1278 6869 cjones
      }
1279
1280
      SystemMetadata systemMetadata = null;
1281 6593 cjones
      try {
1282 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1283 7467 leinfelder
          lock.lock();
1284 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1285
1286 6858 cjones
          try {
1287
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1288
1289
              // does the request have the most current system metadata?
1290
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1291
                 String msg = "The requested system metadata version number " +
1292
                     serialVersion + " differs from the current version at " +
1293
                     systemMetadata.getSerialVersion().longValue() +
1294
                     ". Please get the latest copy in order to modify it.";
1295 6869 cjones
                 throw new VersionMismatch("4443", msg);
1296 6858 cjones
              }
1297
1298 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1299 6858 cjones
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1300
1301 6593 cjones
          }
1302 6858 cjones
1303
          // set the new rights holder
1304
          systemMetadata.setRightsHolder(userId);
1305 6593 cjones
1306 6858 cjones
          // update the metadata
1307
          try {
1308
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1309
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1310
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1311 7076 cjones
              notifyReplicaNodes(systemMetadata);
1312 6858 cjones
1313 6869 cjones
          } catch (RuntimeException e) {
1314
              throw new ServiceFailure("4490", e.getMessage());
1315 6593 cjones
1316 6858 cjones
          }
1317 6593 cjones
1318 6869 cjones
      } catch (RuntimeException e) {
1319 6858 cjones
          throw new ServiceFailure("4490", e.getMessage());
1320 6644 cjones
1321
      } finally {
1322 6702 cjones
          lock.unlock();
1323 6717 cjones
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1324 6858 cjones
1325 6644 cjones
      }
1326
1327 6869 cjones
      return pid;
1328 6410 cjones
  }
1329 6177 cjones
1330 6410 cjones
  /**
1331
   * Verify that a replication task is authorized by comparing the target node's
1332
   * Subject (from the X.509 certificate-derived Session) with the list of
1333
   * subjects in the known, pending replication tasks map.
1334
   *
1335
   * @param originatingNodeSession - Session information that contains the
1336
   *                                 identity of the calling user
1337
   * @param targetNodeSubject - Subject identifying the target node
1338
   * @param pid - the identifier of the object to be replicated
1339
   * @param replicatePermission - the execute permission to be granted
1340
   *
1341
   * @throws ServiceFailure
1342
   * @throws NotImplemented
1343
   * @throws InvalidToken
1344
   * @throws NotAuthorized
1345
   * @throws InvalidRequest
1346
   * @throws NotFound
1347
   */
1348 6471 jones
  @Override
1349 6409 cjones
  public boolean isNodeAuthorized(Session originatingNodeSession,
1350 6777 leinfelder
    Subject targetNodeSubject, Identifier pid)
1351 6410 cjones
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
1352
    NotFound, InvalidRequest {
1353 6702 cjones
1354 6644 cjones
    boolean isAllowed = false;
1355
    SystemMetadata sysmeta = null;
1356 6463 cjones
    NodeReference targetNode = null;
1357
1358 6644 cjones
    try {
1359
      // get the target node reference from the nodes list
1360
      CNode cn = D1Client.getCN();
1361
      List<Node> nodes = cn.listNodes().getNodeList();
1362 6665 cjones
1363 6657 cjones
      if ( nodes != null ) {
1364
        for (Node node : nodes) {
1365 6665 cjones
1366 7141 leinfelder
        	if (node.getSubjectList() != null) {
1367
1368
	            for (Subject nodeSubject : node.getSubjectList()) {
1369
1370
	                if ( nodeSubject.equals(targetNodeSubject) ) {
1371
	                    targetNode = node.getIdentifier();
1372
	                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1373
	                    break;
1374
	                }
1375
	            }
1376
        	}
1377 6657 cjones
1378 6665 cjones
            if ( targetNode != null) { break; }
1379 6657 cjones
        }
1380
1381
      } else {
1382
          String msg = "Couldn't get the node list from the CN";
1383
          logMetacat.debug(msg);
1384
          throw new ServiceFailure("4872", msg);
1385
1386 6644 cjones
      }
1387 6757 cjones
1388
      // can't find a node listed with the given subject
1389
      if ( targetNode == null ) {
1390
          String msg = "There is no Member Node registered with a node subject " +
1391
              "matching " + targetNodeSubject.getValue();
1392
          logMetacat.info(msg);
1393 7062 leinfelder
          throw new NotAuthorized("4871", msg);
1394 6757 cjones
1395
      }
1396
1397 6657 cjones
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1398
1399 6644 cjones
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1400 6463 cjones
1401 6657 cjones
      if ( sysmeta != null ) {
1402
1403
          List<Replica> replicaList = sysmeta.getReplicaList();
1404
1405
          if ( replicaList != null ) {
1406
1407
              // find the replica with the status set to 'requested'
1408
              for (Replica replica : replicaList) {
1409
                  ReplicationStatus status = replica.getReplicationStatus();
1410
                  NodeReference listedNode = replica.getReplicaMemberNode();
1411 6757 cjones
                  if ( listedNode != null && targetNode != null ) {
1412
                      logMetacat.debug("Comparing " + listedNode.getValue()
1413
                              + " to " + targetNode.getValue());
1414
1415
                      if (listedNode.getValue().equals(targetNode.getValue())
1416
                              && status.equals(ReplicationStatus.REQUESTED)) {
1417
                          isAllowed = true;
1418
                          break;
1419 6657 cjones
1420 6757 cjones
                      }
1421 6657 cjones
                  }
1422
              }
1423 6568 cjones
          }
1424 6665 cjones
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1425
              "to replicate: " + isAllowed + " for " + pid.getValue());
1426
1427 6657 cjones
1428
      } else {
1429
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1430
          " is null.");
1431 6869 cjones
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1432 6657 cjones
1433 6568 cjones
      }
1434 6484 cjones
1435 6662 cjones
    } catch (RuntimeException e) {
1436 6665 cjones
    	  ServiceFailure sf = new ServiceFailure("4872",
1437
                "Runtime Exception: Couldn't determine if node is allowed: " +
1438 7140 leinfelder
                e.getMessage());
1439 6665 cjones
    	  sf.initCause(e);
1440 6659 leinfelder
        throw sf;
1441 6636 cjones
1442 6644 cjones
    }
1443
1444
    return isAllowed;
1445 6410 cjones
1446 6384 cjones
  }
1447
1448 6569 cjones
  /**
1449 6570 cjones
   * Adds a new object to the Node, where the object is a science metadata object.
1450 6569 cjones
   *
1451
   * @param session - the Session object containing the credentials for the Subject
1452
   * @param pid - The object identifier to be created
1453
   * @param object - the object bytes
1454
   * @param sysmeta - the system metadata that describes the object
1455
   *
1456
   * @return pid - the object identifier created
1457
   *
1458
   * @throws InvalidToken
1459
   * @throws ServiceFailure
1460
   * @throws NotAuthorized
1461
   * @throws IdentifierNotUnique
1462
   * @throws UnsupportedType
1463
   * @throws InsufficientResources
1464
   * @throws InvalidSystemMetadata
1465
   * @throws NotImplemented
1466
   * @throws InvalidRequest
1467
   */
1468
  public Identifier create(Session session, Identifier pid, InputStream object,
1469
    SystemMetadata sysmeta)
1470
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
1471
    UnsupportedType, InsufficientResources, InvalidSystemMetadata,
1472
    NotImplemented, InvalidRequest {
1473 6917 cjones
1474 6702 cjones
      // The lock to be used for this identifier
1475 6859 cjones
      Lock lock = null;
1476 6917 cjones
1477 6570 cjones
      try {
1478 6869 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1479
          // are we allowed?
1480 6570 cjones
          boolean isAllowed = false;
1481 7142 leinfelder
          isAllowed = isAdminAuthorized(session);
1482 8360 tao
1483
          // additional check if it is the authoritative node if it is not the admin
1484
          if(!isAllowed) {
1485
              isAllowed = isAuthoritativeMNodeAdmin(session, pid);
1486
          }
1487 6570 cjones
1488
          // proceed if we're called by a CN
1489
          if ( isAllowed ) {
1490
              // create the coordinating node version of the document
1491 6702 cjones
              lock.lock();
1492 6867 cjones
              logMetacat.debug("Locked identifier " + pid.getValue());
1493 6570 cjones
              sysmeta.setSerialVersion(BigInteger.ONE);
1494
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1495 6917 cjones
              sysmeta.setArchived(false); // this is a create op, not update
1496
1497
              // the CN should have set the origin and authoritative member node fields
1498
              try {
1499
                  sysmeta.getOriginMemberNode().getValue();
1500
                  sysmeta.getAuthoritativeMemberNode().getValue();
1501
1502
              } catch (NullPointerException npe) {
1503
                  throw new InvalidSystemMetadata("4896",
1504
                      "Both the origin and authoritative member node identifiers need to be set.");
1505
1506
              }
1507 6570 cjones
              pid = super.create(session, pid, object, sysmeta);
1508
1509
          } else {
1510
              String msg = "The subject listed as " + session.getSubject().getValue() +
1511
                  " isn't allowed to call create() on a Coordinating Node.";
1512
              logMetacat.info(msg);
1513
              throw new NotAuthorized("1100", msg);
1514
          }
1515
1516 6676 cjones
      } catch (RuntimeException e) {
1517 6570 cjones
          // Convert Hazelcast runtime exceptions to service failures
1518
          String msg = "There was a problem creating the object identified by " +
1519
              pid.getValue() + ". There error message was: " + e.getMessage();
1520 6676 cjones
          throw new ServiceFailure("4893", msg);
1521 6570 cjones
1522
      } finally {
1523 6805 leinfelder
    	  if (lock != null) {
1524
	          lock.unlock();
1525
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1526
    	  }
1527 6570 cjones
      }
1528
1529 6569 cjones
      return pid;
1530
1531
  }
1532
1533 6571 cjones
  /**
1534
   * Set access for a given object using the object identifier and a Subject
1535
   * under a given Session.
1536
   *
1537
   * @param session - the Session object containing the credentials for the Subject
1538
   * @param pid - the object identifier for the given object to apply the policy
1539
   * @param policy - the access policy to be applied
1540
   *
1541
   * @return true if the application of the policy succeeds
1542
   * @throws InvalidToken
1543
   * @throws ServiceFailure
1544
   * @throws NotFound
1545
   * @throws NotAuthorized
1546
   * @throws NotImplemented
1547
   * @throws InvalidRequest
1548
   */
1549
  public boolean setAccessPolicy(Session session, Identifier pid,
1550 6593 cjones
      AccessPolicy accessPolicy, long serialVersion)
1551 6571 cjones
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1552 6869 cjones
      NotImplemented, InvalidRequest, VersionMismatch {
1553 6571 cjones
1554 6702 cjones
      // The lock to be used for this identifier
1555 6859 cjones
      Lock lock = null;
1556 6869 cjones
      SystemMetadata systemMetadata = null;
1557 6702 cjones
1558 6571 cjones
      boolean success = false;
1559
1560
      // get the subject
1561
      Subject subject = session.getSubject();
1562
1563 7068 cjones
      // are we allowed to do this?
1564
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1565
          throw new NotAuthorized("4420", "not allowed by "
1566
                  + subject.getValue() + " on " + pid.getValue());
1567 6571 cjones
      }
1568
1569
      try {
1570 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1571 6702 cjones
          lock.lock();
1572 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1573
1574 6858 cjones
          try {
1575
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1576 6571 cjones
1577 6869 cjones
              if ( systemMetadata == null ) {
1578
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1579
1580
              }
1581 6858 cjones
              // does the request have the most current system metadata?
1582
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1583
                 String msg = "The requested system metadata version number " +
1584
                     serialVersion + " differs from the current version at " +
1585
                     systemMetadata.getSerialVersion().longValue() +
1586
                     ". Please get the latest copy in order to modify it.";
1587 6869 cjones
                 throw new VersionMismatch("4402", msg);
1588
1589 6858 cjones
              }
1590
1591 6869 cjones
          } catch (RuntimeException e) {
1592 6858 cjones
              // convert Hazelcast RuntimeException to NotFound
1593
              throw new NotFound("4400", "No record found for: " + pid);
1594
1595 6593 cjones
          }
1596 6858 cjones
1597
          // set the access policy
1598
          systemMetadata.setAccessPolicy(accessPolicy);
1599 6593 cjones
1600 6858 cjones
          // update the system metadata
1601
          try {
1602
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1603
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1604
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1605 7076 cjones
              notifyReplicaNodes(systemMetadata);
1606
1607 6869 cjones
          } catch (RuntimeException e) {
1608 6858 cjones
              // convert Hazelcast RuntimeException to ServiceFailure
1609
              throw new ServiceFailure("4430", e.getMessage());
1610
1611
          }
1612 6571 cjones
1613 6869 cjones
      } catch (RuntimeException e) {
1614 6571 cjones
          throw new ServiceFailure("4430", e.getMessage());
1615 6858 cjones
1616 6571 cjones
      } finally {
1617 6702 cjones
          lock.unlock();
1618 6717 cjones
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1619 6571 cjones
1620
      }
1621 6858 cjones
1622 6571 cjones
1623
    // TODO: how do we know if the map was persisted?
1624
    success = true;
1625
1626
    return success;
1627
  }
1628
1629 6578 cjones
  /**
1630
   * Full replacement of replication metadata in the system metadata for the
1631
   * specified object, changes date system metadata modified
1632
   *
1633
   * @param session - the Session object containing the credentials for the Subject
1634
   * @param pid - the object identifier for the given object to apply the policy
1635
   * @param replica - the replica to be updated
1636
   * @return
1637
   * @throws NotImplemented
1638
   * @throws NotAuthorized
1639
   * @throws ServiceFailure
1640
   * @throws InvalidRequest
1641
   * @throws NotFound
1642 6869 cjones
   * @throws VersionMismatch
1643 6578 cjones
   */
1644 6869 cjones
  @Override
1645 6578 cjones
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1646 6593 cjones
      Replica replica, long serialVersion)
1647 6578 cjones
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1648 6869 cjones
      NotFound, VersionMismatch {
1649 6578 cjones
1650 6702 cjones
      // The lock to be used for this identifier
1651 6859 cjones
      Lock lock = null;
1652 6702 cjones
1653 6578 cjones
      // get the subject
1654
      Subject subject = session.getSubject();
1655
1656
      // are we allowed to do this?
1657
      try {
1658 7068 cjones
1659
          // what is the controlling permission?
1660
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1661
              throw new NotAuthorized("4851", "not allowed by "
1662
                      + subject.getValue() + " on " + pid.getValue());
1663
          }
1664
1665 6578 cjones
1666
      } catch (InvalidToken e) {
1667
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() +
1668
                  " on " + pid.getValue());
1669
1670
      }
1671
1672
      SystemMetadata systemMetadata = null;
1673 6858 cjones
      try {
1674 6703 cjones
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1675 6702 cjones
          lock.lock();
1676 6867 cjones
          logMetacat.debug("Locked identifier " + pid.getValue());
1677 6578 cjones
1678 6858 cjones
          try {
1679
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1680
1681
              // does the request have the most current system metadata?
1682
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1683
                 String msg = "The requested system metadata version number " +
1684
                     serialVersion + " differs from the current version at " +
1685
                     systemMetadata.getSerialVersion().longValue() +
1686
                     ". Please get the latest copy in order to modify it.";
1687 6869 cjones
                 throw new VersionMismatch("4855", msg);
1688 6858 cjones
              }
1689
1690 6869 cjones
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1691
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1692
                  " : " + e.getMessage());
1693 6858 cjones
1694 6593 cjones
          }
1695 6858 cjones
1696
          // set the status for the replica
1697
          List<Replica> replicas = systemMetadata.getReplicaList();
1698
          NodeReference replicaNode = replica.getReplicaMemberNode();
1699 7231 cjones
          ReplicationStatus replicaStatus = replica.getReplicationStatus();
1700 6858 cjones
          int index = 0;
1701
          for (Replica listedReplica: replicas) {
1702
1703
              // remove the replica that we are replacing
1704
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1705 7231 cjones
                      // don't allow status to change from COMPLETED to anything other
1706
                      // than INVALIDATED: prevents overwrites from race conditions
1707 7600 cjones
                	  if ( !listedReplica.getReplicationStatus().equals(replicaStatus) &&
1708
                	       listedReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
1709
            		       !replicaStatus.equals(ReplicationStatus.INVALIDATED) ) {
1710 7231 cjones
                	  throw new InvalidRequest("4853", "Status state change from " +
1711
                			  listedReplica.getReplicationStatus() + " to " +
1712
                			  replicaStatus.toString() + "is prohibited for identifier " +
1713
                			  pid.getValue() + " and target node " +
1714
                			  listedReplica.getReplicaMemberNode().getValue());
1715
1716
            	  }
1717 6858 cjones
                  replicas.remove(index);
1718
                  break;
1719
1720
              }
1721
              index++;
1722
          }
1723 6593 cjones
1724 6858 cjones
          // add the new replica item
1725
          replicas.add(replica);
1726
          systemMetadata.setReplicaList(replicas);
1727 6578 cjones
1728 6858 cjones
          // update the metadata
1729
          try {
1730
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1731
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1732
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1733 7514 cjones
1734
              // inform replica nodes of the change if the status is complete
1735 7600 cjones
              if ( replicaStatus.equals(ReplicationStatus.COMPLETED) ) {
1736 8439 cjones
            	  notifyReplicaNodes(systemMetadata);
1737 7514 cjones
1738
              }
1739 6869 cjones
          } catch (RuntimeException e) {
1740
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1741 6858 cjones
              throw new ServiceFailure("4852", e.getMessage());
1742 6578 cjones
1743
          }
1744 6858 cjones
1745 6869 cjones
      } catch (RuntimeException e) {
1746
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1747
          throw new ServiceFailure("4852", e.getMessage());
1748
1749
      } finally {
1750
          lock.unlock();
1751
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1752
1753
      }
1754 6578 cjones
1755
      return true;
1756
1757
  }
1758 6622 leinfelder
1759 6869 cjones
  /**
1760
   *
1761
   */
1762
  @Override
1763 6858 cjones
  public ObjectList listObjects(Session session, Date startTime,
1764
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1765
      Integer start, Integer count)
1766 6644 cjones
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1767
      ServiceFailure {
1768
1769
      ObjectList objectList = null;
1770 6622 leinfelder
        try {
1771 7439 leinfelder
        	if (count == null || count > MAXIMUM_DB_RECORD_COUNT) {
1772
            	count = MAXIMUM_DB_RECORD_COUNT;
1773
            }
1774 6622 leinfelder
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1775
        } catch (Exception e) {
1776
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1777
        }
1778
1779
        return objectList;
1780 6644 cjones
  }
1781 6803 leinfelder
1782 7012 cjones
1783
 	/**
1784
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1785
 	 * @return cal  the list of checksum algorithms
1786
 	 *
1787
 	 * @throws ServiceFailure
1788
 	 * @throws NotImplemented
1789
 	 */
1790 6869 cjones
  @Override
1791 7012 cjones
  public ChecksumAlgorithmList listChecksumAlgorithms()
1792 6803 leinfelder
			throws ServiceFailure, NotImplemented {
1793
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1794
		cal.addAlgorithm("MD5");
1795
		cal.addAlgorithm("SHA-1");
1796 7012 cjones
		return cal;
1797
1798 6803 leinfelder
	}
1799 7073 cjones
1800
  /**
1801
   * Notify replica Member Nodes of system metadata changes for a given pid
1802
   *
1803
   * @param currentSystemMetadata - the up to date system metadata
1804
   */
1805
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1806
1807
      Session session = null;
1808
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1809
      MNode mn = null;
1810
      NodeReference replicaNodeRef = null;
1811
      CNode cn = null;
1812
      NodeType nodeType = null;
1813
      List<Node> nodeList = null;
1814
1815
      try {
1816
          cn = D1Client.getCN();
1817
          nodeList = cn.listNodes().getNodeList();
1818
1819
      } catch (Exception e) { // handle BaseException and other I/O issues
1820
1821
          // swallow errors since the call is not critical
1822
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1823
              "to communication issues with the CN: " + e.getMessage());
1824
1825
      }
1826
1827
      if ( replicaList != null ) {
1828
1829
          // iterate through the replicas and inform  MN replica nodes
1830
          for (Replica replica : replicaList) {
1831
1832
              replicaNodeRef = replica.getReplicaMemberNode();
1833
              try {
1834
                  if (nodeList != null) {
1835
                      // find the node type
1836
                      for (Node node : nodeList) {
1837 8450 cjones
                          if ( node.getIdentifier().getValue().equals(replicaNodeRef.getValue()) ) {
1838 7073 cjones
                              nodeType = node.getType();
1839
                              break;
1840
1841
                          }
1842
                      }
1843
                  }
1844
1845
                  // notify only MNs
1846
                  if (nodeType != null && nodeType == NodeType.MN) {
1847
                      mn = D1Client.getMN(replicaNodeRef);
1848
                      mn.systemMetadataChanged(session,
1849
                          currentSystemMetadata.getIdentifier(),
1850
                          currentSystemMetadata.getSerialVersion().longValue(),
1851
                          currentSystemMetadata.getDateSysMetadataModified());
1852
                  }
1853
1854
              } catch (Exception e) { // handle BaseException and other I/O issues
1855
1856
                  // swallow errors since the call is not critical
1857
                  logMetacat.error("Can't inform "
1858
                          + replicaNodeRef.getValue()
1859
                          + " of system metadata changes due "
1860
                          + "to communication issues with the CN: "
1861
                          + e.getMessage());
1862
1863
              }
1864
          }
1865
      }
1866
  }
1867 7144 leinfelder
1868
	@Override
1869
	public boolean isAuthorized(Identifier pid, Permission permission)
1870
			throws ServiceFailure, InvalidToken, NotFound, NotAuthorized,
1871
			NotImplemented, InvalidRequest {
1872
1873
		return isAuthorized(null, pid, permission);
1874
	}
1875
1876
	@Override
1877
	public boolean setAccessPolicy(Identifier pid, AccessPolicy accessPolicy, long serialVersion)
1878
			throws InvalidToken, NotFound, NotImplemented, NotAuthorized,
1879
			ServiceFailure, InvalidRequest, VersionMismatch {
1880
1881
		return setAccessPolicy(null, pid, accessPolicy, serialVersion);
1882
	}
1883
1884
	@Override
1885
	public Identifier setRightsHolder(Identifier pid, Subject userId, long serialVersion)
1886
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1887
			NotImplemented, InvalidRequest, VersionMismatch {
1888
1889
		return setRightsHolder(null, pid, userId, serialVersion);
1890
	}
1891
1892
	@Override
1893
	public Identifier create(Identifier pid, InputStream object, SystemMetadata sysmeta)
1894
			throws InvalidToken, ServiceFailure, NotAuthorized,
1895
			IdentifierNotUnique, UnsupportedType, InsufficientResources,
1896
			InvalidSystemMetadata, NotImplemented, InvalidRequest {
1897
1898
		return create(null, pid, object, sysmeta);
1899
	}
1900
1901
	@Override
1902
	public Identifier delete(Identifier pid) throws InvalidToken, ServiceFailure,
1903 7171 leinfelder
			NotAuthorized, NotFound, NotImplemented {
1904 7144 leinfelder
1905
		return delete(null, pid);
1906
	}
1907
1908
	@Override
1909
	public Identifier generateIdentifier(String scheme, String fragment)
1910
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1911
			InvalidRequest {
1912
1913
		return generateIdentifier(null, scheme, fragment);
1914
	}
1915
1916
	@Override
1917
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1918
			Integer start, Integer count) throws InvalidToken, InvalidRequest,
1919
			ServiceFailure, NotAuthorized, NotImplemented, InsufficientResources {
1920
1921
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1922
	}
1923
1924
	@Override
1925
	public boolean hasReservation(Subject subject, Identifier pid)
1926
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1927
			NotImplemented, InvalidRequest, IdentifierNotUnique {
1928
1929
		return hasReservation(null, subject, pid);
1930
	}
1931
1932
	@Override
1933
	public Identifier registerSystemMetadata(Identifier pid, SystemMetadata sysmeta)
1934
			throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1935
			InvalidSystemMetadata, InvalidToken {
1936
1937
		return registerSystemMetadata(null, pid, sysmeta);
1938
	}
1939
1940
	@Override
1941
	public Identifier reserveIdentifier(Identifier pid) throws InvalidToken,
1942
			ServiceFailure, NotAuthorized, IdentifierNotUnique, NotImplemented,
1943
			InvalidRequest {
1944
1945
		return reserveIdentifier(null, pid);
1946
	}
1947
1948
	@Override
1949
	public boolean setObsoletedBy(Identifier pid, Identifier obsoletedByPid, long serialVersion)
1950
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
1951
			InvalidRequest, InvalidToken, VersionMismatch {
1952
1953
		return setObsoletedBy(null, pid, obsoletedByPid, serialVersion);
1954
	}
1955
1956
	@Override
1957
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1958
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1959
1960
		return describe(null, pid);
1961
	}
1962
1963
	@Override
1964
	public InputStream get(Identifier pid) throws InvalidToken, ServiceFailure,
1965
			NotAuthorized, NotFound, NotImplemented {
1966
1967
		return get(null, pid);
1968
	}
1969
1970
	@Override
1971
	public Checksum getChecksum(Identifier pid) throws InvalidToken,
1972
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1973
1974
		return getChecksum(null, pid);
1975
	}
1976
1977
	@Override
1978
	public SystemMetadata getSystemMetadata(Identifier pid) throws InvalidToken,
1979
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1980
1981
		return getSystemMetadata(null, pid);
1982
	}
1983
1984
	@Override
1985
	public ObjectList listObjects(Date startTime, Date endTime,
1986
			ObjectFormatIdentifier formatid, Boolean replicaStatus, Integer start, Integer count)
1987
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1988
			ServiceFailure {
1989
1990
		return listObjects(null, startTime, endTime, formatid, replicaStatus, start, count);
1991
	}
1992
1993
	@Override
1994
	public ObjectLocationList resolve(Identifier pid) throws InvalidToken,
1995
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1996
1997
		return resolve(null, pid);
1998
	}
1999
2000
	@Override
2001
	public ObjectList search(String queryType, String query) throws InvalidToken,
2002
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
2003
2004
		return search(null, queryType, query);
2005
	}
2006
2007
	@Override
2008
	public boolean deleteReplicationMetadata(Identifier pid, NodeReference nodeId,
2009
			long serialVersion) throws InvalidToken, InvalidRequest, ServiceFailure,
2010
			NotAuthorized, NotFound, NotImplemented, VersionMismatch {
2011
2012
		return deleteReplicationMetadata(null, pid, nodeId, serialVersion);
2013
	}
2014
2015
	@Override
2016
	public boolean isNodeAuthorized(Subject targetNodeSubject, Identifier pid)
2017
			throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
2018
			NotFound, InvalidRequest {
2019
2020
		return isNodeAuthorized(null, targetNodeSubject, pid);
2021
	}
2022
2023
	@Override
2024
	public boolean setReplicationPolicy(Identifier pid, ReplicationPolicy policy,
2025
			long serialVersion) throws NotImplemented, NotFound, NotAuthorized,
2026
			ServiceFailure, InvalidRequest, InvalidToken, VersionMismatch {
2027
2028
		return setReplicationPolicy(null, pid, policy, serialVersion);
2029
	}
2030
2031
	@Override
2032
	public boolean setReplicationStatus(Identifier pid, NodeReference targetNode,
2033
			ReplicationStatus status, BaseException failure) throws ServiceFailure,
2034
			NotImplemented, InvalidToken, NotAuthorized, InvalidRequest, NotFound {
2035
2036
		return setReplicationStatus(null, pid, targetNode, status, failure);
2037
	}
2038
2039
	@Override
2040
	public boolean updateReplicationMetadata(Identifier pid, Replica replica,
2041
			long serialVersion) throws NotImplemented, NotAuthorized, ServiceFailure,
2042
			NotFound, InvalidRequest, InvalidToken, VersionMismatch {
2043
2044
		return updateReplicationMetadata(null, pid, replica, serialVersion);
2045
	}
2046 7401 cjones
2047
  @Override
2048
  public QueryEngineDescription getQueryEngineDescription(String arg0)
2049
          throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
2050
          NotFound {
2051
      throw new NotImplemented("4410", "getQueryEngineDescription not implemented");
2052
2053
  }
2054
2055
  @Override
2056
  public QueryEngineList listQueryEngines() throws InvalidToken, ServiceFailure,
2057
          NotAuthorized, NotImplemented {
2058
      throw new NotImplemented("4420", "listQueryEngines not implemented");
2059
2060
  }
2061
2062
  @Override
2063
  public InputStream query(String arg0, String arg1) throws InvalidToken,
2064
          ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, NotFound {
2065
      throw new NotImplemented("4324", "query not implemented");
2066
2067
  }
2068 6177 cjones
}