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