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