Project

General

Profile

1
/**
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
import java.io.InputStream;
27
import java.math.BigInteger;
28
import java.util.ArrayList;
29
import java.util.Calendar;
30
import java.util.Date;
31
import java.util.List;
32
import java.util.concurrent.locks.Lock;
33

    
34
import javax.servlet.http.HttpServletRequest;
35

    
36
import org.apache.log4j.Logger;
37
import org.dataone.client.v2.CNode;
38
import org.dataone.client.v2.itk.D1Client;
39
import org.dataone.client.v2.MNode;
40
import org.dataone.service.cn.v2.CNAuthorization;
41
import org.dataone.service.cn.v2.CNCore;
42
import org.dataone.service.cn.v2.CNRead;
43
import org.dataone.service.cn.v2.CNReplication;
44
import org.dataone.service.exceptions.BaseException;
45
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotFound;
52
import org.dataone.service.exceptions.NotImplemented;
53
import org.dataone.service.exceptions.ServiceFailure;
54
import org.dataone.service.exceptions.UnsupportedType;
55
import org.dataone.service.exceptions.VersionMismatch;
56
import org.dataone.service.types.v1.AccessPolicy;
57
import org.dataone.service.types.v1.Checksum;
58
import org.dataone.service.types.v1.ChecksumAlgorithmList;
59
import org.dataone.service.types.v1.DescribeResponse;
60
import org.dataone.service.types.v1.Event;
61
import org.dataone.service.types.v1.Identifier;
62
import org.dataone.service.types.v2.Log;
63
import org.dataone.service.types.v2.Node;
64
import org.dataone.service.types.v2.NodeList;
65
import org.dataone.service.types.v1.NodeReference;
66
import org.dataone.service.types.v1.NodeType;
67
import org.dataone.service.types.v2.ObjectFormat;
68
import org.dataone.service.types.v1.ObjectFormatIdentifier;
69
import org.dataone.service.types.v2.ObjectFormatList;
70
import org.dataone.service.types.v1.ObjectList;
71
import org.dataone.service.types.v1.ObjectLocationList;
72
import org.dataone.service.types.v1.Permission;
73
import org.dataone.service.types.v1.Replica;
74
import org.dataone.service.types.v1.ReplicationPolicy;
75
import org.dataone.service.types.v1.ReplicationStatus;
76
import org.dataone.service.types.v1.Session;
77
import org.dataone.service.types.v1.Subject;
78
import org.dataone.service.types.v2.SystemMetadata;
79
import org.dataone.service.types.v2.util.ServiceMethodRestrictionUtil;
80
import org.dataone.service.types.v1_1.QueryEngineDescription;
81
import org.dataone.service.types.v1_1.QueryEngineList;
82

    
83
import edu.ucsb.nceas.metacat.EventLog;
84
import edu.ucsb.nceas.metacat.IdentifierManager;
85
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
86
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
87
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
88

    
89
/**
90
 * Represents Metacat's implementation of the DataONE Coordinating Node 
91
 * service API. Methods implement the various CN* interfaces, and methods common
92
 * to both Member Node and Coordinating Node interfaces are found in the
93
 * D1NodeService super class.
94
 *
95
 */
96
public class CNodeService extends D1NodeService implements CNAuthorization,
97
    CNCore, CNRead, CNReplication {
98

    
99
  /* the logger instance */
100
  private Logger logMetacat = null;
101

    
102
  /**
103
   * singleton accessor
104
   */
105
  public static CNodeService getInstance(HttpServletRequest request) { 
106
    return new CNodeService(request);
107
  }
108
  
109
  /**
110
   * Constructor, private for singleton access
111
   */
112
  private CNodeService(HttpServletRequest request) {
113
    super(request);
114
    logMetacat = Logger.getLogger(CNodeService.class);
115
        
116
  }
117
    
118
  /**
119
   * Set the replication policy for an object given the object identifier
120
   * 
121
   * @param session - the Session object containing the credentials for the Subject
122
   * @param pid - the object identifier for the given object
123
   * @param policy - the replication policy to be applied
124
   * 
125
   * @return true or false
126
   * 
127
   * @throws NotImplemented
128
   * @throws NotAuthorized
129
   * @throws ServiceFailure
130
   * @throws InvalidRequest
131
   * @throws VersionMismatch
132
   * 
133
   */
134
  @Override
135
  public boolean setReplicationPolicy(Session session, Identifier pid,
136
      ReplicationPolicy policy, long serialVersion) 
137
      throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
138
      InvalidRequest, InvalidToken, VersionMismatch {
139
      
140
      // The lock to be used for this identifier
141
      Lock lock = null;
142
      
143
      // get the subject
144
      Subject subject = session.getSubject();
145
      
146
      // are we allowed to do this?
147
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
148
          throw new NotAuthorized("4881", Permission.CHANGE_PERMISSION
149
                  + " not allowed by " + subject.getValue() + " on "
150
                  + pid.getValue());
151
          
152
      }
153
      
154
      SystemMetadata systemMetadata = null;
155
      try {
156
          lock = HazelcastService.getInstance().getLock(pid.getValue());
157
          lock.lock();
158
          logMetacat.debug("Locked identifier " + pid.getValue());
159

    
160
          try {
161
              if ( HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid) ) {
162
                  systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
163
                  
164
              }
165
              
166
              // did we get it correctly?
167
              if ( systemMetadata == null ) {
168
                  throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
169
                  
170
              }
171

    
172
              // does the request have the most current system metadata?
173
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
174
                 String msg = "The requested system metadata version number " + 
175
                     serialVersion + " differs from the current version at " +
176
                     systemMetadata.getSerialVersion().longValue() +
177
                     ". Please get the latest copy in order to modify it.";
178
                 throw new VersionMismatch("4886", msg);
179
                 
180
              }
181
              
182
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
183
              throw new NotFound("4884", "No record found for: " + pid.getValue());
184
            
185
          }
186
          
187
          // set the new policy
188
          systemMetadata.setReplicationPolicy(policy);
189
          
190
          // update the metadata
191
          try {
192
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
193
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
194
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
195
              notifyReplicaNodes(systemMetadata);
196
              
197
          } catch (RuntimeException e) {
198
              throw new ServiceFailure("4882", e.getMessage());
199
          
200
          }
201
          
202
      } catch (RuntimeException e) {
203
          throw new ServiceFailure("4882", e.getMessage());
204
          
205
      } finally {
206
          lock.unlock();
207
          logMetacat.debug("Unlocked identifier " + pid.getValue());
208
          
209
      }
210
    
211
      return true;
212
  }
213

    
214
  /**
215
   * Deletes the replica from the given Member Node
216
   * NOTE: MN.delete() may be an "archive" operation. TBD.
217
   * @param session
218
   * @param pid
219
   * @param nodeId
220
   * @param serialVersion
221
   * @return
222
   * @throws InvalidToken
223
   * @throws ServiceFailure
224
   * @throws NotAuthorized
225
   * @throws NotFound
226
   * @throws NotImplemented
227
   * @throws VersionMismatch
228
   */
229
  @Override
230
  public boolean deleteReplicationMetadata(Session session, Identifier pid, NodeReference nodeId, long serialVersion) 
231
  	throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, VersionMismatch {
232
	  
233
	  	// The lock to be used for this identifier
234
		Lock lock = null;
235

    
236
		// get the subject
237
		Subject subject = session.getSubject();
238

    
239
		// are we allowed to do this?
240
		boolean isAuthorized = false;
241
		try {
242
			isAuthorized = isAuthorized(session, pid, Permission.WRITE);
243
		} catch (InvalidRequest e) {
244
			throw new ServiceFailure("4882", e.getDescription());
245
		}
246
		if (!isAuthorized) {
247
			throw new NotAuthorized("4881", Permission.WRITE
248
					+ " not allowed by " + subject.getValue() + " on "
249
					+ pid.getValue());
250

    
251
		}
252

    
253
		SystemMetadata systemMetadata = null;
254
		try {
255
			lock = HazelcastService.getInstance().getLock(pid.getValue());
256
			lock.lock();
257
			logMetacat.debug("Locked identifier " + pid.getValue());
258

    
259
			try {
260
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
261
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
262
				}
263

    
264
				// did we get it correctly?
265
				if (systemMetadata == null) {
266
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
267
				}
268

    
269
				// does the request have the most current system metadata?
270
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
271
					String msg = "The requested system metadata version number "
272
							+ serialVersion
273
							+ " differs from the current version at "
274
							+ systemMetadata.getSerialVersion().longValue()
275
							+ ". Please get the latest copy in order to modify it.";
276
					throw new VersionMismatch("4886", msg);
277

    
278
				}
279

    
280
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
281
				throw new NotFound("4884", "No record found for: " + pid.getValue());
282

    
283
			}
284
			  
285
			// check permissions
286
			// TODO: is this necessary?
287
			List<Node> nodeList = D1Client.getCN().listNodes().getNodeList();
288
			boolean isAllowed = ServiceMethodRestrictionUtil.isMethodAllowed(session.getSubject(), nodeList, "CNReplication", "deleteReplicationMetadata");
289
			if (!isAllowed) {
290
				throw new NotAuthorized("4881", "Caller is not authorized to deleteReplicationMetadata");
291
			}
292
			  
293
			// delete the replica from the given node
294
			// CSJ: use CN.delete() to truly delete a replica, semantically
295
			// deleteReplicaMetadata() only modifies the sytem metadata entry.
296
			//D1Client.getMN(nodeId).delete(session, pid);
297
			  
298
			// reflect that change in the system metadata
299
			List<Replica> updatedReplicas = new ArrayList<Replica>(systemMetadata.getReplicaList());
300
			for (Replica r: systemMetadata.getReplicaList()) {
301
				  if (r.getReplicaMemberNode().equals(nodeId)) {
302
					  updatedReplicas.remove(r);
303
					  break;
304
				  }
305
			}
306
			systemMetadata.setReplicaList(updatedReplicas);
307

    
308
			// update the metadata
309
			try {
310
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
311
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
312
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
313
			} catch (RuntimeException e) {
314
				throw new ServiceFailure("4882", e.getMessage());
315
			}
316

    
317
		} catch (RuntimeException e) {
318
			throw new ServiceFailure("4882", e.getMessage());
319
		} finally {
320
			lock.unlock();
321
			logMetacat.debug("Unlocked identifier " + pid.getValue());
322
		}
323

    
324
		return true;	  
325
	  
326
  }
327
  
328
  /**
329
   * Deletes an object from the Coordinating Node
330
   * 
331
   * @param session - the Session object containing the credentials for the Subject
332
   * @param pid - The object identifier to be deleted
333
   * 
334
   * @return pid - the identifier of the object used for the deletion
335
   * 
336
   * @throws InvalidToken
337
   * @throws ServiceFailure
338
   * @throws NotAuthorized
339
   * @throws NotFound
340
   * @throws NotImplemented
341
   * @throws InvalidRequest
342
   */
343
  @Override
344
  public Identifier delete(Session session, Identifier pid) 
345
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
346
      
347
      String localId = null;      // The corresponding docid for this pid
348
	  Lock lock = null;           // The lock to be used for this identifier
349
      CNode cn = null;            // a reference to the CN to get the node list    
350
      NodeType nodeType = null;   // the nodeType of the replica node being contacted
351
      List<Node> nodeList = null; // the list of nodes in this CN environment
352

    
353
      // check for a valid session
354
      if (session == null) {
355
        	throw new InvalidToken("4963", "No session has been provided");
356
        	
357
      }
358

    
359
      // do we have a valid pid?
360
      if (pid == null || pid.getValue().trim().equals("")) {
361
          throw new ServiceFailure("4960", "The provided identifier was invalid.");
362
          
363
      }
364

    
365
	  // check that it is CN/admin
366
	  boolean allowed = isAdminAuthorized(session);
367
	  
368
	  // additional check if it is the authoritative node if it is not the admin
369
      if(!allowed) {
370
          allowed = isAuthoritativeMNodeAdmin(session, pid);
371
          
372
      }
373
	  
374
	  if (!allowed) {
375
		  String msg = "The subject " + session.getSubject().getValue() + 
376
			  " is not allowed to call delete() on a Coordinating Node.";
377
		  logMetacat.info(msg);
378
		  throw new NotAuthorized("4960", msg);
379
		  
380
	  }
381
	  
382
	  // Don't defer to superclass implementation without a locally registered identifier
383
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
384
      // Check for the existing identifier
385
      try {
386
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
387
          super.delete(session, pid);
388
          
389
      } catch (McdbDocNotFoundException e) {
390
          // This object is not registered in the identifier table. Assume it is of formatType DATA,
391
    	  // and set the archive flag. (i.e. the *object* doesn't exist on the CN)
392
    	  
393
          try {
394
  			  lock = HazelcastService.getInstance().getLock(pid.getValue());
395
  			  lock.lock();
396
  			  logMetacat.debug("Locked identifier " + pid.getValue());
397

    
398
			  SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
399
			  if ( sysMeta != null ) {
400
				/*sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
401
				sysMeta.setArchived(true);
402
				sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
403
				HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);*/
404
			    //move the systemmetadata object from the map and delete the records in the systemmetadata database table
405
	            //since this is cn, we don't need worry about the mn solr index.
406
	            HazelcastService.getInstance().getSystemMetadataMap().remove(pid);
407
	            HazelcastService.getInstance().getIdentifiers().remove(pid);
408
	            String username = session.getSubject().getValue();//just for logging purpose
409
                //since data objects were not registered in the identifier table, we use pid as the docid
410
                EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, pid.getValue(), Event.DELETE.xmlValue());
411
				
412
			  } else {
413
				  throw new ServiceFailure("4962", "Couldn't delete the object " + pid.getValue() +
414
					  ". Couldn't obtain the system metadata record.");
415
				  
416
			  }
417
			  
418
		  } catch (RuntimeException re) {
419
			  throw new ServiceFailure("4962", "Couldn't delete " + pid.getValue() + 
420
				  ". The error message was: " + re.getMessage());
421
			  
422
		  } finally {
423
			  lock.unlock();
424
			  logMetacat.debug("Unlocked identifier " + pid.getValue());
425

    
426
		  }
427

    
428
          // NOTE: cannot log the delete without localId
429
//          EventLog.getInstance().log(request.getRemoteAddr(), 
430
//                  request.getHeader("User-Agent"), session.getSubject().getValue(), 
431
//                  pid.getValue(), Event.DELETE.xmlValue());
432

    
433
      }
434

    
435
      // get the node list
436
      try {
437
          cn = D1Client.getCN();
438
          nodeList = cn.listNodes().getNodeList();
439
          
440
      } catch (Exception e) { // handle BaseException and other I/O issues
441
          
442
          // swallow errors since the call is not critical
443
          logMetacat.error("Can't inform MNs of the deletion of " + pid.getValue() + 
444
              " due to communication issues with the CN: " + e.getMessage());
445
          
446
      }
447

    
448
	  // notify the replicas
449
	  if (systemMetadata.getReplicaList() != null) {
450
		  for (Replica replica: systemMetadata.getReplicaList()) {
451
			  NodeReference replicaNode = replica.getReplicaMemberNode();
452
			  try {
453
                  if (nodeList != null) {
454
                      // find the node type
455
                      for (Node node : nodeList) {
456
                          if ( node.getIdentifier().getValue().equals(replicaNode.getValue()) ) {
457
                              nodeType = node.getType();
458
                              break;
459
              
460
                          }
461
                      }
462
                  }
463
                  
464
                  // only send call MN.delete() to avoid an infinite loop with the CN
465
                  if (nodeType != null && nodeType == NodeType.MN) {
466
				      Identifier mnRetId = D1Client.getMN(replicaNode).delete(null, pid);
467
                  }
468
                  
469
			  } catch (Exception e) {
470
				  // all we can really do is log errors and carry on with life
471
				  logMetacat.error("Error deleting pid: " +  pid.getValue() + 
472
					  " from replica MN: " + replicaNode.getValue(), e);
473
			}
474
			  
475
		  }
476
	  }
477
	  
478
	  return pid;
479
      
480
  }
481
  
482
  /**
483
   * Archives an object from the Coordinating Node
484
   * 
485
   * @param session - the Session object containing the credentials for the Subject
486
   * @param pid - The object identifier to be deleted
487
   * 
488
   * @return pid - the identifier of the object used for the deletion
489
   * 
490
   * @throws InvalidToken
491
   * @throws ServiceFailure
492
   * @throws NotAuthorized
493
   * @throws NotFound
494
   * @throws NotImplemented
495
   * @throws InvalidRequest
496
   */
497
  @Override
498
  public Identifier archive(Session session, Identifier pid) 
499
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
500

    
501
      String localId = null; // The corresponding docid for this pid
502
	  Lock lock = null;      // The lock to be used for this identifier
503
      CNode cn = null;            // a reference to the CN to get the node list    
504
      NodeType nodeType = null;   // the nodeType of the replica node being contacted
505
      List<Node> nodeList = null; // the list of nodes in this CN environment
506
      
507

    
508
      // check for a valid session
509
      if (session == null) {
510
        	throw new InvalidToken("4973", "No session has been provided");
511
        	
512
      }
513

    
514
      // do we have a valid pid?
515
      if (pid == null || pid.getValue().trim().equals("")) {
516
          throw new ServiceFailure("4972", "The provided identifier was invalid.");
517
          
518
      }
519

    
520
	  // check that it is CN/admin
521
	  boolean allowed = isAdminAuthorized(session);
522
	  
523
	  //check if it is the authoritative member node
524
	  if(!allowed) {
525
	      allowed = isAuthoritativeMNodeAdmin(session, pid);
526
	  }
527
	  
528
	  if (!allowed) {
529
		  String msg = "The subject " + session.getSubject().getValue() + 
530
				  " is not allowed to call archive() on a Coordinating Node.";
531
		  logMetacat.info(msg);
532
		  throw new NotAuthorized("4970", msg);
533
	  }
534
	  
535
      // Check for the existing identifier
536
      try {
537
          localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
538
          super.archive(session, pid);
539
          
540
      } catch (McdbDocNotFoundException e) {
541
          // This object is not registered in the identifier table. Assume it is of formatType DATA,
542
    	  // and set the archive flag. (i.e. the *object* doesn't exist on the CN)
543
    	  
544
          try {
545
  			  lock = HazelcastService.getInstance().getLock(pid.getValue());
546
  			  lock.lock();
547
  			  logMetacat.debug("Locked identifier " + pid.getValue());
548

    
549
			  SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
550
			  if ( sysMeta != null ) {
551
				sysMeta.setSerialVersion(sysMeta.getSerialVersion().add(BigInteger.ONE));
552
				sysMeta.setArchived(true);
553
				sysMeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
554
				HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
555
			    // notify the replicas
556
				notifyReplicaNodes(sysMeta);
557
				  
558
			  } else {
559
				  throw new ServiceFailure("4972", "Couldn't archive the object " + pid.getValue() +
560
					  ". Couldn't obtain the system metadata record.");
561
				  
562
			  }
563
			  
564
		  } catch (RuntimeException re) {
565
			  throw new ServiceFailure("4972", "Couldn't archive " + pid.getValue() + 
566
				  ". The error message was: " + re.getMessage());
567
			  
568
		  } finally {
569
			  lock.unlock();
570
			  logMetacat.debug("Unlocked identifier " + pid.getValue());
571

    
572
		  }
573

    
574
          // NOTE: cannot log the archive without localId
575
//          EventLog.getInstance().log(request.getRemoteAddr(), 
576
//                  request.getHeader("User-Agent"), session.getSubject().getValue(), 
577
//                  pid.getValue(), Event.DELETE.xmlValue());
578

    
579
      }
580

    
581
	  return pid;
582
      
583
  }
584
  
585
  /**
586
   * Set the obsoletedBy attribute in System Metadata
587
   * @param session
588
   * @param pid
589
   * @param obsoletedByPid
590
   * @param serialVersion
591
   * @return
592
   * @throws NotImplemented
593
   * @throws NotFound
594
   * @throws NotAuthorized
595
   * @throws ServiceFailure
596
   * @throws InvalidRequest
597
   * @throws InvalidToken
598
   * @throws VersionMismatch
599
   */
600
  @Override
601
  public boolean setObsoletedBy(Session session, Identifier pid,
602
			Identifier obsoletedByPid, long serialVersion)
603
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
604
			InvalidRequest, InvalidToken, VersionMismatch {
605

    
606
		// The lock to be used for this identifier
607
		Lock lock = null;
608

    
609
		// get the subject
610
		Subject subject = session.getSubject();
611

    
612
		// are we allowed to do this?
613
		if (!isAuthorized(session, pid, Permission.WRITE)) {
614
			throw new NotAuthorized("4881", Permission.WRITE
615
					+ " not allowed by " + subject.getValue() + " on "
616
					+ pid.getValue());
617

    
618
		}
619

    
620

    
621
		SystemMetadata systemMetadata = null;
622
		try {
623
			lock = HazelcastService.getInstance().getLock(pid.getValue());
624
			lock.lock();
625
			logMetacat.debug("Locked identifier " + pid.getValue());
626

    
627
			try {
628
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
629
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
630
				}
631

    
632
				// did we get it correctly?
633
				if (systemMetadata == null) {
634
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
635
				}
636

    
637
				// does the request have the most current system metadata?
638
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
639
					String msg = "The requested system metadata version number "
640
							+ serialVersion
641
							+ " differs from the current version at "
642
							+ systemMetadata.getSerialVersion().longValue()
643
							+ ". Please get the latest copy in order to modify it.";
644
					throw new VersionMismatch("4886", msg);
645

    
646
				}
647

    
648
			} catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
649
				throw new NotFound("4884", "No record found for: " + pid.getValue());
650

    
651
			}
652

    
653
			// set the new policy
654
			systemMetadata.setObsoletedBy(obsoletedByPid);
655

    
656
			// update the metadata
657
			try {
658
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
659
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
660
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
661
			} catch (RuntimeException e) {
662
				throw new ServiceFailure("4882", e.getMessage());
663
			}
664

    
665
		} catch (RuntimeException e) {
666
			throw new ServiceFailure("4882", e.getMessage());
667
		} finally {
668
			lock.unlock();
669
			logMetacat.debug("Unlocked identifier " + pid.getValue());
670
		}
671

    
672
		return true;
673
	}
674
  
675
  
676
  /**
677
   * Set the replication status for an object given the object identifier
678
   * 
679
   * @param session - the Session object containing the credentials for the Subject
680
   * @param pid - the object identifier for the given object
681
   * @param status - the replication status to be applied
682
   * 
683
   * @return true or false
684
   * 
685
   * @throws NotImplemented
686
   * @throws NotAuthorized
687
   * @throws ServiceFailure
688
   * @throws InvalidRequest
689
   * @throws InvalidToken
690
   * @throws NotFound
691
   * 
692
   */
693
  @Override
694
  public boolean setReplicationStatus(Session session, Identifier pid,
695
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
696
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
697
      InvalidRequest, NotFound {
698
	  
699
	  // cannot be called by public
700
	  if (session == null) {
701
		  throw new NotAuthorized("4720", "Session cannot be null");
702
	  }
703
      
704
      // The lock to be used for this identifier
705
      Lock lock = null;
706
      
707
      boolean allowed = false;
708
      int replicaEntryIndex = -1;
709
      List<Replica> replicas = null;
710
      // get the subject
711
      Subject subject = session.getSubject();
712
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
713
          " is " + status.toString());
714
      
715
      SystemMetadata systemMetadata = null;
716

    
717
      try {
718
          lock = HazelcastService.getInstance().getLock(pid.getValue());
719
          lock.lock();
720
          logMetacat.debug("Locked identifier " + pid.getValue());
721

    
722
          try {      
723
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
724

    
725
              // did we get it correctly?
726
              if ( systemMetadata == null ) {
727
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
728
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
729
                  
730
              }
731
              replicas = systemMetadata.getReplicaList();
732
              int count = 0;
733
              
734
              // was there a failure? log it
735
              if ( failure != null && status.equals(ReplicationStatus.FAILED) ) {
736
                 String msg = "The replication request of the object identified by " + 
737
                     pid.getValue() + " failed.  The error message was " +
738
                     failure.getMessage() + ".";
739
              }
740
              
741
              if (replicas.size() > 0 && replicas != null) {
742
                  // find the target replica index in the replica list
743
                  for (Replica replica : replicas) {
744
                      String replicaNodeStr = replica.getReplicaMemberNode().getValue();
745
                      String targetNodeStr = targetNode.getValue();
746
                      logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
747
                  
748
                      if (replicaNodeStr.equals(targetNodeStr)) {
749
                          replicaEntryIndex = count;
750
                          logMetacat.debug("replica entry index is: "
751
                                  + replicaEntryIndex);
752
                          break;
753
                      }
754
                      count++;
755
                  
756
                  }
757
              }
758
              // are we allowed to do this? only CNs and target MNs are allowed
759
              CNode cn = D1Client.getCN();
760
              List<Node> nodes = cn.listNodes().getNodeList();
761
              
762
              // find the node in the node list
763
              for ( Node node : nodes ) {
764
                  
765
                  NodeReference nodeReference = node.getIdentifier();
766
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + 
767
                      nodeReference.getValue());
768
                  
769
                  // allow target MN certs
770
                  if ( targetNode.getValue().equals(nodeReference.getValue() ) &&
771
                      node.getType().equals(NodeType.MN)) {
772
                      List<Subject> nodeSubjects = node.getSubjectList();
773
                      
774
                      // check if the session subject is in the node subject list
775
                      for (Subject nodeSubject : nodeSubjects) {
776
                          logMetacat.debug("In setReplicationStatus(), comparing subjects: " +
777
                                  nodeSubject.getValue() + " and " + subject.getValue());
778
                          if ( nodeSubject.equals(subject) ) { // subject of session == target node subject
779
                              
780
                              // lastly limit to COMPLETED, INVALIDATED,
781
                              // and FAILED status updates from MNs only
782
                              if ( status.equals(ReplicationStatus.COMPLETED) ||
783
                                   status.equals(ReplicationStatus.INVALIDATED) ||
784
                                   status.equals(ReplicationStatus.FAILED)) {
785
                                  allowed = true;
786
                                  break;
787
                                  
788
                              }                              
789
                          }
790
                      }                 
791
                  }
792
              }
793

    
794
              if ( !allowed ) {
795
                  //check for CN admin access
796
                  allowed = isAuthorized(session, pid, Permission.WRITE);
797
                  
798
              }              
799
              
800
              if ( !allowed ) {
801
                  String msg = "The subject identified by "
802
                          + subject.getValue()
803
                          + " does not have permission to set the replication status for "
804
                          + "the replica identified by "
805
                          + targetNode.getValue() + ".";
806
                  logMetacat.info(msg);
807
                  throw new NotAuthorized("4720", msg);
808
                  
809
              }
810

    
811
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
812
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
813
                " : " + e.getMessage());
814
            
815
          }
816
          
817
          Replica targetReplica = new Replica();
818
          // set the status for the replica
819
          if ( replicaEntryIndex != -1 ) {
820
              targetReplica = replicas.get(replicaEntryIndex);
821
              
822
              // don't allow status to change from COMPLETED to anything other
823
              // than INVALIDATED: prevents overwrites from race conditions
824
              if ( targetReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
825
            	   !status.equals(ReplicationStatus.INVALIDATED)) {
826
            	  throw new InvalidRequest("4730", "Status state change from " +
827
            			  targetReplica.getReplicationStatus() + " to " +
828
            			  status.toString() + "is prohibited for identifier " +
829
            			  pid.getValue() + " and target node " + 
830
            			  targetReplica.getReplicaMemberNode().getValue());
831
              }
832
              
833
              targetReplica.setReplicationStatus(status);
834
              
835
              logMetacat.debug("Set the replication status for " + 
836
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
837
                  targetReplica.getReplicationStatus() + " for identifier " +
838
                  pid.getValue());
839
              
840
          } else {
841
              // this is a new entry, create it
842
              targetReplica.setReplicaMemberNode(targetNode);
843
              targetReplica.setReplicationStatus(status);
844
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
845
              replicas.add(targetReplica);
846
              
847
          }
848
          
849
          systemMetadata.setReplicaList(replicas);
850
                
851
          // update the metadata
852
          try {
853
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
854
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
855
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
856

    
857
              if ( !status.equals(ReplicationStatus.QUEUED) && 
858
            	   !status.equals(ReplicationStatus.REQUESTED)) {
859
                  
860
                logMetacat.trace("METRICS:\tREPLICATION:\tEND REQUEST:\tPID:\t" + pid.getValue() + 
861
                          "\tNODE:\t" + targetNode.getValue() + 
862
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
863
                
864
                logMetacat.trace("METRICS:\tREPLICATION:\t" + status.toString().toUpperCase() +
865
                          "\tPID:\t"  + pid.getValue() + 
866
                          "\tNODE:\t" + targetNode.getValue() + 
867
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
868
              }
869

    
870
              if ( status.equals(ReplicationStatus.FAILED) && failure != null ) {
871
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
872
                      " on target node " + targetNode + ". The exception was: " +
873
                      failure.getMessage());
874
              }
875
              
876
			  // update the replica nodes about the completed replica when complete
877
              if (status.equals(ReplicationStatus.COMPLETED)) {
878
				notifyReplicaNodes(systemMetadata);
879
			}
880
          
881
          } catch (RuntimeException e) {
882
              throw new ServiceFailure("4700", e.getMessage());
883
          
884
          }
885
          
886
    } catch (RuntimeException e) {
887
        String msg = "There was a RuntimeException getting the lock for " +
888
            pid.getValue();
889
        logMetacat.info(msg);
890
        
891
    } finally {
892
        lock.unlock();
893
        logMetacat.debug("Unlocked identifier " + pid.getValue());
894
        
895
    }
896
      
897
      return true;
898
  }
899
  
900
/**
901
   * Return the checksum of the object given the identifier 
902
   * 
903
   * @param session - the Session object containing the credentials for the Subject
904
   * @param pid - the object identifier for the given object
905
   * 
906
   * @return checksum - the checksum of the object
907
   * 
908
   * @throws InvalidToken
909
   * @throws ServiceFailure
910
   * @throws NotAuthorized
911
   * @throws NotFound
912
   * @throws NotImplemented
913
   */
914
  @Override
915
  public Checksum getChecksum(Session session, Identifier pid)
916
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
917
    NotImplemented {
918
    
919
	boolean isAuthorized = false;
920
	try {
921
		isAuthorized = isAuthorized(session, pid, Permission.READ);
922
	} catch (InvalidRequest e) {
923
		throw new ServiceFailure("1410", e.getDescription());
924
	}  
925
    if (!isAuthorized) {
926
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
927
    }
928
    
929
    SystemMetadata systemMetadata = null;
930
    Checksum checksum = null;
931
    
932
    try {
933
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
934

    
935
        if (systemMetadata == null ) {
936
            String error ="";
937
            String localId = null;
938
            try {
939
                localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
940
              
941
             } catch (Exception e) {
942
                logMetacat.warn("Couldn't find the local id for the pid "+pid.getValue());
943
            }
944
            
945
            if(localId != null && EventLog.getInstance().isDeleted(localId)) {
946
                error = DELETEDMESSAGE;
947
            } else if (localId == null && EventLog.getInstance().isDeleted(pid.getValue())) {
948
                error = DELETEDMESSAGE;
949
            }
950
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue()+". "+error);
951
        }
952
        checksum = systemMetadata.getChecksum();
953
        
954
    } catch (RuntimeException e) {
955
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
956
            pid.getValue() + ". The error message was: " + e.getMessage());
957
      
958
    }
959
    
960
    return checksum;
961
  }
962

    
963
  /**
964
   * Resolve the location of a given object
965
   * 
966
   * @param session - the Session object containing the credentials for the Subject
967
   * @param pid - the object identifier for the given object
968
   * 
969
   * @return objectLocationList - the list of nodes known to contain the object
970
   * 
971
   * @throws InvalidToken
972
   * @throws ServiceFailure
973
   * @throws NotAuthorized
974
   * @throws NotFound
975
   * @throws NotImplemented
976
   */
977
  @Override
978
  public ObjectLocationList resolve(Session session, Identifier pid)
979
    throws InvalidToken, ServiceFailure, NotAuthorized,
980
    NotFound, NotImplemented {
981

    
982
    throw new NotImplemented("4131", "resolve not implemented");
983

    
984
  }
985

    
986
  /**
987
   * Metacat does not implement this method at the CN level
988
   */
989
  @Override
990
  public ObjectList search(Session session, String queryType, String query)
991
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
992
    NotImplemented {
993

    
994
		  throw new NotImplemented("4281", "Metacat does not implement CN.search");
995
	  
996
//    ObjectList objectList = null;
997
//    try {
998
//        objectList = 
999
//          IdentifierManager.getInstance().querySystemMetadata(
1000
//              null, //startTime, 
1001
//              null, //endTime,
1002
//              null, //objectFormat, 
1003
//              false, //replicaStatus, 
1004
//              0, //start, 
1005
//              1000 //count
1006
//              );
1007
//        
1008
//    } catch (Exception e) {
1009
//      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
1010
//    }
1011
//
1012
//      return objectList;
1013
		  
1014
  }
1015
  
1016
  /**
1017
   * Returns the object format registered in the DataONE Object Format 
1018
   * Vocabulary for the given format identifier
1019
   * 
1020
   * @param fmtid - the identifier of the format requested
1021
   * 
1022
   * @return objectFormat - the object format requested
1023
   * 
1024
   * @throws ServiceFailure
1025
   * @throws NotFound
1026
   * @throws InsufficientResources
1027
   * @throws NotImplemented
1028
   */
1029
  @Override
1030
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
1031
    throws ServiceFailure, NotFound, NotImplemented {
1032
     
1033
      return ObjectFormatService.getInstance().getFormat(fmtid);
1034
      
1035
  }
1036

    
1037
  /**
1038
   * Returns a list of all object formats registered in the DataONE Object 
1039
   * Format Vocabulary
1040
    * 
1041
   * @return objectFormatList - The list of object formats registered in 
1042
   *                            the DataONE Object Format Vocabulary
1043
   * 
1044
   * @throws ServiceFailure
1045
   * @throws NotImplemented
1046
   * @throws InsufficientResources
1047
   */
1048
  @Override
1049
  public ObjectFormatList listFormats() 
1050
    throws ServiceFailure, NotImplemented {
1051

    
1052
    return ObjectFormatService.getInstance().listFormats();
1053
  }
1054

    
1055
  /**
1056
   * Returns a list of nodes that have been registered with the DataONE infrastructure
1057
    * 
1058
   * @return nodeList - List of nodes from the registry
1059
   * 
1060
   * @throws ServiceFailure
1061
   * @throws NotImplemented
1062
   */
1063
  @Override
1064
  public NodeList listNodes() 
1065
    throws NotImplemented, ServiceFailure {
1066

    
1067
    throw new NotImplemented("4800", "listNodes not implemented");
1068
  }
1069

    
1070
  /**
1071
   * Provides a mechanism for adding system metadata independently of its 
1072
   * associated object, such as when adding system metadata for data objects.
1073
    * 
1074
   * @param session - the Session object containing the credentials for the Subject
1075
   * @param pid - The identifier of the object to register the system metadata against
1076
   * @param sysmeta - The system metadata to be registered
1077
   * 
1078
   * @return true if the registration succeeds
1079
   * 
1080
   * @throws NotImplemented
1081
   * @throws NotAuthorized
1082
   * @throws ServiceFailure
1083
   * @throws InvalidRequest
1084
   * @throws InvalidSystemMetadata
1085
   */
1086
  @Override
1087
  public Identifier registerSystemMetadata(Session session, Identifier pid,
1088
      SystemMetadata sysmeta) 
1089
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
1090
      InvalidSystemMetadata {
1091

    
1092
      // The lock to be used for this identifier
1093
      Lock lock = null;
1094

    
1095
      // TODO: control who can call this?
1096
      if (session == null) {
1097
          //TODO: many of the thrown exceptions do not use the correct error codes
1098
          //check these against the docs and correct them
1099
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
1100
                  "  If you are not logged in, please do so and retry the request.");
1101
      }
1102
      
1103
      // verify that guid == SystemMetadata.getIdentifier()
1104
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
1105
          "|" + sysmeta.getIdentifier().getValue());
1106
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
1107
          throw new InvalidRequest("4863", 
1108
              "The identifier in method call (" + pid.getValue() + 
1109
              ") does not match identifier in system metadata (" +
1110
              sysmeta.getIdentifier().getValue() + ").");
1111
      }
1112

    
1113
      try {
1114
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
1115
          lock.lock();
1116
          logMetacat.debug("Locked identifier " + pid.getValue());
1117
          logMetacat.debug("Checking if identifier exists...");
1118
          // Check that the identifier does not already exist
1119
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
1120
              throw new InvalidRequest("4863", 
1121
                  "The identifier is already in use by an existing object.");
1122
          
1123
          }
1124
          
1125
          // insert the system metadata into the object store
1126
          logMetacat.debug("Starting to insert SystemMetadata...");
1127
          try {
1128
              sysmeta.setSerialVersion(BigInteger.ONE);
1129
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1130
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
1131
              
1132
          } catch (RuntimeException e) {
1133
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
1134
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
1135
                  e.getClass() + ": " + e.getMessage());
1136
              
1137
          }
1138
          
1139
      } catch (RuntimeException e) {
1140
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
1141
                  e.getClass() + ": " + e.getMessage());
1142
          
1143
      }  finally {
1144
          lock.unlock();
1145
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1146
          
1147
      }
1148

    
1149
      
1150
      logMetacat.debug("Returning from registerSystemMetadata");
1151
      
1152
      try {
1153
    	  String localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1154
    	  EventLog.getInstance().log(request.getRemoteAddr(), 
1155
    	          request.getHeader("User-Agent"), session.getSubject().getValue(), 
1156
    	          localId, "registerSystemMetadata");
1157
      } catch (McdbDocNotFoundException e) {
1158
    	  // do nothing, no localId to log with
1159
    	  logMetacat.warn("Could not log 'registerSystemMetadata' event because no localId was found for pid: " + pid.getValue());
1160
      }
1161
      
1162
      
1163
      return pid;
1164
  }
1165
  
1166
  /**
1167
   * Given an optional scope and format, reserves and returns an identifier 
1168
   * within that scope and format that is unique and will not be 
1169
   * used by any other sessions. 
1170
    * 
1171
   * @param session - the Session object containing the credentials for the Subject
1172
   * @param pid - The identifier of the object to register the system metadata against
1173
   * @param scope - An optional string to be used to qualify the scope of 
1174
   *                the identifier namespace, which is applied differently 
1175
   *                depending on the format requested. If scope is not 
1176
   *                supplied, a default scope will be used.
1177
   * @param format - The optional name of the identifier format to be used, 
1178
   *                  drawn from a DataONE-specific vocabulary of identifier 
1179
   *                 format names, including several common syntaxes such 
1180
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
1181
   *                 format is not supplied by the caller, the CN service 
1182
   *                 will use a default identifier format, which may change 
1183
   *                 over time.
1184
   * 
1185
   * @return true if the registration succeeds
1186
   * 
1187
   * @throws InvalidToken
1188
   * @throws ServiceFailure
1189
   * @throws NotAuthorized
1190
   * @throws IdentifierNotUnique
1191
   * @throws NotImplemented
1192
   */
1193
  @Override
1194
  public Identifier reserveIdentifier(Session session, Identifier pid)
1195
  throws InvalidToken, ServiceFailure,
1196
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
1197

    
1198
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
1199
  }
1200
  
1201
  @Override
1202
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
1203
  throws InvalidToken, ServiceFailure,
1204
        NotAuthorized, NotImplemented, InvalidRequest {
1205
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
1206
  }
1207
  
1208
  /**
1209
    * Checks whether the pid is reserved by the subject in the session param
1210
    * If the reservation is held on the pid by the subject, we return true.
1211
    * 
1212
   * @param session - the Session object containing the Subject
1213
   * @param pid - The identifier to check
1214
   * 
1215
   * @return true if the reservation exists for the subject/pid
1216
   * 
1217
   * @throws InvalidToken
1218
   * @throws ServiceFailure
1219
   * @throws NotFound - when the pid is not found (in use or in reservation)
1220
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
1221
   * @throws IdentifierNotUnique - when the pid is in use
1222
   * @throws NotImplemented
1223
   */
1224

    
1225
  @Override
1226
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
1227
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
1228
      NotImplemented, InvalidRequest {
1229
  
1230
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1231
  }
1232

    
1233
  /**
1234
   * Changes ownership (RightsHolder) of the specified object to the 
1235
   * subject specified by userId
1236
    * 
1237
   * @param session - the Session object containing the credentials for the Subject
1238
   * @param pid - Identifier of the object to be modified
1239
   * @param userId - The subject that will be taking ownership of the specified object.
1240
   *
1241
   * @return pid - the identifier of the modified object
1242
   * 
1243
   * @throws ServiceFailure
1244
   * @throws InvalidToken
1245
   * @throws NotFound
1246
   * @throws NotAuthorized
1247
   * @throws NotImplemented
1248
   * @throws InvalidRequest
1249
   */  
1250
  @Override
1251
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1252
      long serialVersion)
1253
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1254
      NotImplemented, InvalidRequest, VersionMismatch {
1255
      
1256
      // The lock to be used for this identifier
1257
      Lock lock = null;
1258

    
1259
      // get the subject
1260
      Subject subject = session.getSubject();
1261
      
1262
      // are we allowed to do this?
1263
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1264
          throw new NotAuthorized("4440", "not allowed by "
1265
                  + subject.getValue() + " on " + pid.getValue());
1266
          
1267
      }
1268
      
1269
      SystemMetadata systemMetadata = null;
1270
      try {
1271
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1272
          lock.lock();
1273
          logMetacat.debug("Locked identifier " + pid.getValue());
1274

    
1275
          try {
1276
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1277
              
1278
              // does the request have the most current system metadata?
1279
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1280
                 String msg = "The requested system metadata version number " + 
1281
                     serialVersion + " differs from the current version at " +
1282
                     systemMetadata.getSerialVersion().longValue() +
1283
                     ". Please get the latest copy in order to modify it.";
1284
                 throw new VersionMismatch("4443", msg);
1285
              }
1286
              
1287
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1288
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1289
              
1290
          }
1291
              
1292
          // set the new rights holder
1293
          systemMetadata.setRightsHolder(userId);
1294
          
1295
          // update the metadata
1296
          try {
1297
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1298
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1299
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1300
              notifyReplicaNodes(systemMetadata);
1301
              
1302
          } catch (RuntimeException e) {
1303
              throw new ServiceFailure("4490", e.getMessage());
1304
          
1305
          }
1306
          
1307
      } catch (RuntimeException e) {
1308
          throw new ServiceFailure("4490", e.getMessage());
1309
          
1310
      } finally {
1311
          lock.unlock();
1312
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1313
      
1314
      }
1315
      
1316
      return pid;
1317
  }
1318

    
1319
  /**
1320
   * Verify that a replication task is authorized by comparing the target node's
1321
   * Subject (from the X.509 certificate-derived Session) with the list of 
1322
   * subjects in the known, pending replication tasks map.
1323
   * 
1324
   * @param originatingNodeSession - Session information that contains the 
1325
   *                                 identity of the calling user
1326
   * @param targetNodeSubject - Subject identifying the target node
1327
   * @param pid - the identifier of the object to be replicated
1328
   * @param replicatePermission - the execute permission to be granted
1329
   * 
1330
   * @throws ServiceFailure
1331
   * @throws NotImplemented
1332
   * @throws InvalidToken
1333
   * @throws NotAuthorized
1334
   * @throws InvalidRequest
1335
   * @throws NotFound
1336
   */
1337
  @Override
1338
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1339
    Subject targetNodeSubject, Identifier pid) 
1340
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1341
    NotFound, InvalidRequest {
1342
    
1343
    boolean isAllowed = false;
1344
    SystemMetadata sysmeta = null;
1345
    NodeReference targetNode = null;
1346
    
1347
    try {
1348
      // get the target node reference from the nodes list
1349
      CNode cn = D1Client.getCN();
1350
      List<Node> nodes = cn.listNodes().getNodeList();
1351
      
1352
      if ( nodes != null ) {
1353
        for (Node node : nodes) {
1354
            
1355
        	if (node.getSubjectList() != null) {
1356
        		
1357
	            for (Subject nodeSubject : node.getSubjectList()) {
1358
	            	
1359
	                if ( nodeSubject.equals(targetNodeSubject) ) {
1360
	                    targetNode = node.getIdentifier();
1361
	                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1362
	                    break;
1363
	                }
1364
	            }
1365
        	}
1366
            
1367
            if ( targetNode != null) { break; }
1368
        }
1369
        
1370
      } else {
1371
          String msg = "Couldn't get the node list from the CN";
1372
          logMetacat.debug(msg);
1373
          throw new ServiceFailure("4872", msg);
1374
          
1375
      }
1376
      
1377
      // can't find a node listed with the given subject
1378
      if ( targetNode == null ) {
1379
          String msg = "There is no Member Node registered with a node subject " +
1380
              "matching " + targetNodeSubject.getValue();
1381
          logMetacat.info(msg);
1382
          throw new NotAuthorized("4871", msg);
1383
          
1384
      }
1385
      
1386
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1387
      
1388
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1389

    
1390
      if ( sysmeta != null ) {
1391
          
1392
          List<Replica> replicaList = sysmeta.getReplicaList();
1393
          
1394
          if ( replicaList != null ) {
1395
              
1396
              // find the replica with the status set to 'requested'
1397
              for (Replica replica : replicaList) {
1398
                  ReplicationStatus status = replica.getReplicationStatus();
1399
                  NodeReference listedNode = replica.getReplicaMemberNode();
1400
                  if ( listedNode != null && targetNode != null ) {
1401
                      logMetacat.debug("Comparing " + listedNode.getValue()
1402
                              + " to " + targetNode.getValue());
1403
                      
1404
                      if (listedNode.getValue().equals(targetNode.getValue())
1405
                              && status.equals(ReplicationStatus.REQUESTED)) {
1406
                          isAllowed = true;
1407
                          break;
1408

    
1409
                      }
1410
                  }
1411
              }
1412
          }
1413
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1414
              "to replicate: " + isAllowed + " for " + pid.getValue());
1415

    
1416
          
1417
      } else {
1418
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1419
          " is null.");
1420
          String error ="";
1421
          String localId = null;
1422
          try {
1423
              localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1424
            
1425
           } catch (Exception e) {
1426
              logMetacat.warn("Couldn't find the local id for the pid "+pid.getValue());
1427
          }
1428
          
1429
          if(localId != null && EventLog.getInstance().isDeleted(localId)) {
1430
              error = DELETEDMESSAGE;
1431
          } else if (localId == null && EventLog.getInstance().isDeleted(pid.getValue())) {
1432
              error = DELETEDMESSAGE;
1433
          }
1434
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue()+". "+error);
1435
          
1436
      }
1437

    
1438
    } catch (RuntimeException e) {
1439
    	  ServiceFailure sf = new ServiceFailure("4872", 
1440
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1441
                e.getMessage());
1442
    	  sf.initCause(e);
1443
        throw sf;
1444
        
1445
    }
1446
      
1447
    return isAllowed;
1448
    
1449
  }
1450

    
1451
  /**
1452
   * Adds a new object to the Node, where the object is a science metadata object.
1453
   * 
1454
   * @param session - the Session object containing the credentials for the Subject
1455
   * @param pid - The object identifier to be created
1456
   * @param object - the object bytes
1457
   * @param sysmeta - the system metadata that describes the object  
1458
   * 
1459
   * @return pid - the object identifier created
1460
   * 
1461
   * @throws InvalidToken
1462
   * @throws ServiceFailure
1463
   * @throws NotAuthorized
1464
   * @throws IdentifierNotUnique
1465
   * @throws UnsupportedType
1466
   * @throws InsufficientResources
1467
   * @throws InvalidSystemMetadata
1468
   * @throws NotImplemented
1469
   * @throws InvalidRequest
1470
   */
1471
  public Identifier create(Session session, Identifier pid, InputStream object,
1472
    SystemMetadata sysmeta) 
1473
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1474
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1475
    NotImplemented, InvalidRequest {
1476
                  
1477
      // The lock to be used for this identifier
1478
      Lock lock = null;
1479

    
1480
      try {
1481
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1482
          // are we allowed?
1483
          boolean isAllowed = false;
1484
          isAllowed = isAdminAuthorized(session);
1485
          
1486
          // additional check if it is the authoritative node if it is not the admin
1487
          if(!isAllowed) {
1488
              isAllowed = isAuthoritativeMNodeAdmin(session, pid);
1489
          }
1490

    
1491
          // proceed if we're called by a CN
1492
          if ( isAllowed ) {
1493
              // create the coordinating node version of the document      
1494
              lock.lock();
1495
              logMetacat.debug("Locked identifier " + pid.getValue());
1496
              sysmeta.setSerialVersion(BigInteger.ONE);
1497
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1498
              //sysmeta.setArchived(false); // this is a create op, not update
1499
              
1500
              // the CN should have set the origin and authoritative member node fields
1501
              try {
1502
                  sysmeta.getOriginMemberNode().getValue();
1503
                  sysmeta.getAuthoritativeMemberNode().getValue();
1504
                  
1505
              } catch (NullPointerException npe) {
1506
                  throw new InvalidSystemMetadata("4896", 
1507
                      "Both the origin and authoritative member node identifiers need to be set.");
1508
                  
1509
              }
1510
              pid = super.create(session, pid, object, sysmeta);
1511

    
1512
          } else {
1513
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1514
                  " isn't allowed to call create() on a Coordinating Node.";
1515
              logMetacat.info(msg);
1516
              throw new NotAuthorized("1100", msg);
1517
          }
1518
          
1519
      } catch (RuntimeException e) {
1520
          // Convert Hazelcast runtime exceptions to service failures
1521
          String msg = "There was a problem creating the object identified by " +
1522
              pid.getValue() + ". There error message was: " + e.getMessage();
1523
          throw new ServiceFailure("4893", msg);
1524
          
1525
      } finally {
1526
    	  if (lock != null) {
1527
	          lock.unlock();
1528
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1529
    	  }
1530
      }
1531
      
1532
      return pid;
1533

    
1534
  }
1535

    
1536
  /**
1537
   * Set access for a given object using the object identifier and a Subject
1538
   * under a given Session.
1539
   * 
1540
   * @param session - the Session object containing the credentials for the Subject
1541
   * @param pid - the object identifier for the given object to apply the policy
1542
   * @param policy - the access policy to be applied
1543
   * 
1544
   * @return true if the application of the policy succeeds
1545
   * @throws InvalidToken
1546
   * @throws ServiceFailure
1547
   * @throws NotFound
1548
   * @throws NotAuthorized
1549
   * @throws NotImplemented
1550
   * @throws InvalidRequest
1551
   */
1552
  public boolean setAccessPolicy(Session session, Identifier pid, 
1553
      AccessPolicy accessPolicy, long serialVersion) 
1554
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1555
      NotImplemented, InvalidRequest, VersionMismatch {
1556
      
1557
      // The lock to be used for this identifier
1558
      Lock lock = null;
1559
      SystemMetadata systemMetadata = null;
1560
      
1561
      boolean success = false;
1562
      
1563
      // get the subject
1564
      Subject subject = session.getSubject();
1565
      
1566
      // are we allowed to do this?
1567
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1568
          throw new NotAuthorized("4420", "not allowed by "
1569
                  + subject.getValue() + " on " + pid.getValue());
1570
      }
1571
      
1572
      try {
1573
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1574
          lock.lock();
1575
          logMetacat.debug("Locked identifier " + pid.getValue());
1576

    
1577
          try {
1578
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1579

    
1580
              if ( systemMetadata == null ) {
1581
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1582
                  
1583
              }
1584
              // does the request have the most current system metadata?
1585
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1586
                 String msg = "The requested system metadata version number " + 
1587
                     serialVersion + " differs from the current version at " +
1588
                     systemMetadata.getSerialVersion().longValue() +
1589
                     ". Please get the latest copy in order to modify it.";
1590
                 throw new VersionMismatch("4402", msg);
1591
                 
1592
              }
1593
              
1594
          } catch (RuntimeException e) {
1595
              // convert Hazelcast RuntimeException to NotFound
1596
              throw new NotFound("4400", "No record found for: " + pid);
1597
            
1598
          }
1599
              
1600
          // set the access policy
1601
          systemMetadata.setAccessPolicy(accessPolicy);
1602
          
1603
          // update the system metadata
1604
          try {
1605
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1606
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1607
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1608
              notifyReplicaNodes(systemMetadata);
1609
              
1610
          } catch (RuntimeException e) {
1611
              // convert Hazelcast RuntimeException to ServiceFailure
1612
              throw new ServiceFailure("4430", e.getMessage());
1613
            
1614
          }
1615
          
1616
      } catch (RuntimeException e) {
1617
          throw new ServiceFailure("4430", e.getMessage());
1618
          
1619
      } finally {
1620
          lock.unlock();
1621
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1622
        
1623
      }
1624

    
1625
    
1626
    // TODO: how do we know if the map was persisted?
1627
    success = true;
1628
    
1629
    return success;
1630
  }
1631

    
1632
  /**
1633
   * Full replacement of replication metadata in the system metadata for the 
1634
   * specified object, changes date system metadata modified
1635
   * 
1636
   * @param session - the Session object containing the credentials for the Subject
1637
   * @param pid - the object identifier for the given object to apply the policy
1638
   * @param replica - the replica to be updated
1639
   * @return
1640
   * @throws NotImplemented
1641
   * @throws NotAuthorized
1642
   * @throws ServiceFailure
1643
   * @throws InvalidRequest
1644
   * @throws NotFound
1645
   * @throws VersionMismatch
1646
   */
1647
  @Override
1648
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1649
      Replica replica, long serialVersion) 
1650
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1651
      NotFound, VersionMismatch {
1652
      
1653
      // The lock to be used for this identifier
1654
      Lock lock = null;
1655
      
1656
      // get the subject
1657
      Subject subject = session.getSubject();
1658
      
1659
      // are we allowed to do this?
1660
      try {
1661

    
1662
          // what is the controlling permission?
1663
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1664
              throw new NotAuthorized("4851", "not allowed by "
1665
                      + subject.getValue() + " on " + pid.getValue());
1666
          }
1667

    
1668
        
1669
      } catch (InvalidToken e) {
1670
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1671
                  " on " + pid.getValue());  
1672
          
1673
      }
1674

    
1675
      SystemMetadata systemMetadata = null;
1676
      try {
1677
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1678
          lock.lock();
1679
          logMetacat.debug("Locked identifier " + pid.getValue());
1680

    
1681
          try {      
1682
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1683

    
1684
              // does the request have the most current system metadata?
1685
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1686
                 String msg = "The requested system metadata version number " + 
1687
                     serialVersion + " differs from the current version at " +
1688
                     systemMetadata.getSerialVersion().longValue() +
1689
                     ". Please get the latest copy in order to modify it.";
1690
                 throw new VersionMismatch("4855", msg);
1691
              }
1692
              
1693
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1694
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1695
                  " : " + e.getMessage());
1696
            
1697
          }
1698
              
1699
          // set the status for the replica
1700
          List<Replica> replicas = systemMetadata.getReplicaList();
1701
          NodeReference replicaNode = replica.getReplicaMemberNode();
1702
          ReplicationStatus replicaStatus = replica.getReplicationStatus();
1703
          int index = 0;
1704
          for (Replica listedReplica: replicas) {
1705
              
1706
              // remove the replica that we are replacing
1707
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1708
                      // don't allow status to change from COMPLETED to anything other
1709
                      // than INVALIDATED: prevents overwrites from race conditions
1710
                	  if ( !listedReplica.getReplicationStatus().equals(replicaStatus) && 
1711
                	       listedReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
1712
            		       !replicaStatus.equals(ReplicationStatus.INVALIDATED) ) {
1713
                	  throw new InvalidRequest("4853", "Status state change from " +
1714
                			  listedReplica.getReplicationStatus() + " to " +
1715
                			  replicaStatus.toString() + "is prohibited for identifier " +
1716
                			  pid.getValue() + " and target node " + 
1717
                			  listedReplica.getReplicaMemberNode().getValue());
1718

    
1719
            	  }
1720
                  replicas.remove(index);
1721
                  break;
1722
                  
1723
              }
1724
              index++;
1725
          }
1726
          
1727
          // add the new replica item
1728
          replicas.add(replica);
1729
          systemMetadata.setReplicaList(replicas);
1730
          
1731
          // update the metadata
1732
          try {
1733
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1734
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1735
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1736
              
1737
              // inform replica nodes of the change if the status is complete
1738
              if ( replicaStatus.equals(ReplicationStatus.COMPLETED) ) {
1739
            	  notifyReplicaNodes(systemMetadata);
1740
            	  
1741
              }
1742
          } catch (RuntimeException e) {
1743
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1744
              throw new ServiceFailure("4852", e.getMessage());
1745
          
1746
          }
1747
          
1748
      } catch (RuntimeException e) {
1749
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1750
          throw new ServiceFailure("4852", e.getMessage());
1751
      
1752
      } finally {
1753
          lock.unlock();
1754
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1755
          
1756
      }
1757
    
1758
      return true;
1759
      
1760
  }
1761
  
1762
  /**
1763
   * 
1764
   */
1765
  @Override
1766
  public ObjectList listObjects(Session session, Date startTime, 
1767
      Date endTime, ObjectFormatIdentifier formatid, Identifier identifier, Boolean replicaStatus,
1768
      Integer start, Integer count)
1769
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1770
      ServiceFailure {
1771
      
1772
      ObjectList objectList = null;
1773
        try {
1774
        	if (count == null || count > MAXIMUM_DB_RECORD_COUNT) {
1775
            	count = MAXIMUM_DB_RECORD_COUNT;
1776
            }
1777
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1778
        } catch (Exception e) {
1779
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1780
        }
1781

    
1782
        return objectList;
1783
  }
1784

    
1785
  
1786
 	/**
1787
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1788
 	 * @return cal  the list of checksum algorithms
1789
 	 * 
1790
 	 * @throws ServiceFailure
1791
 	 * @throws NotImplemented
1792
 	 */
1793
  @Override
1794
  public ChecksumAlgorithmList listChecksumAlgorithms()
1795
			throws ServiceFailure, NotImplemented {
1796
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1797
		cal.addAlgorithm("MD5");
1798
		cal.addAlgorithm("SHA-1");
1799
		return cal;
1800
		
1801
	}
1802

    
1803
  /**
1804
   * Notify replica Member Nodes of system metadata changes for a given pid
1805
   * 
1806
   * @param currentSystemMetadata - the up to date system metadata
1807
   */
1808
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1809
      
1810
      Session session = null;
1811
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1812
      MNode mn = null;
1813
      NodeReference replicaNodeRef = null;
1814
      CNode cn = null;
1815
      NodeType nodeType = null;
1816
      List<Node> nodeList = null;
1817
      
1818
      try {
1819
          cn = D1Client.getCN();
1820
          nodeList = cn.listNodes().getNodeList();
1821
          
1822
      } catch (Exception e) { // handle BaseException and other I/O issues
1823
          
1824
          // swallow errors since the call is not critical
1825
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1826
              "to communication issues with the CN: " + e.getMessage());
1827
          
1828
      }
1829
      
1830
      if ( replicaList != null ) {
1831
          
1832
          // iterate through the replicas and inform  MN replica nodes
1833
          for (Replica replica : replicaList) {
1834
              
1835
              replicaNodeRef = replica.getReplicaMemberNode();
1836
              try {
1837
                  if (nodeList != null) {
1838
                      // find the node type
1839
                      for (Node node : nodeList) {
1840
                          if ( node.getIdentifier().getValue().equals(replicaNodeRef.getValue()) ) {
1841
                              nodeType = node.getType();
1842
                              break;
1843
              
1844
                          }
1845
                      }
1846
                  }
1847
              
1848
                  // notify only MNs
1849
                  if (nodeType != null && nodeType == NodeType.MN) {
1850
                      mn = D1Client.getMN(replicaNodeRef);
1851
                      mn.systemMetadataChanged(session, 
1852
                          currentSystemMetadata.getIdentifier(), 
1853
                          currentSystemMetadata.getSerialVersion().longValue(),
1854
                          currentSystemMetadata.getDateSysMetadataModified());
1855
                  }
1856
              
1857
              } catch (Exception e) { // handle BaseException and other I/O issues
1858
              
1859
                  // swallow errors since the call is not critical
1860
                  logMetacat.error("Can't inform "
1861
                          + replicaNodeRef.getValue()
1862
                          + " of system metadata changes due "
1863
                          + "to communication issues with the CN: "
1864
                          + e.getMessage());
1865
              
1866
              }
1867
          }
1868
      }
1869
  }
1870

    
1871
	@Override
1872
	public QueryEngineDescription getQueryEngineDescription(Session session,
1873
			String queryEngine) throws InvalidToken, ServiceFailure, NotAuthorized,
1874
			NotImplemented, NotFound {
1875
		throw new NotImplemented("0000", "CN query services are not implemented in Metacat.");
1876

    
1877
	}
1878
	
1879
	@Override
1880
	public QueryEngineList listQueryEngines(Session session) throws InvalidToken,
1881
			ServiceFailure, NotAuthorized, NotImplemented {
1882
		throw new NotImplemented("0000", "CN query services are not implemented in Metacat.");
1883

    
1884
	}
1885
	
1886
	@Override
1887
	public InputStream query(Session session, String queryEngine, String query)
1888
			throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
1889
			NotImplemented, NotFound {
1890
		throw new NotImplemented("0000", "CN query services are not implemented in Metacat.");
1891

    
1892
	}
1893
	
1894
	@Override
1895
	public Node getCapabilities() throws NotImplemented, ServiceFailure {
1896
		throw new NotImplemented("0000", "The CN capabilities are not stored in Metacat.");
1897
	}
1898
	
1899
	
1900
}
(1-1/7)