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