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