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
				
409
			  } else {
410
				  throw new ServiceFailure("4962", "Couldn't delete the object " + pid.getValue() +
411
					  ". Couldn't obtain the system metadata record.");
412
				  
413
			  }
414
			  
415
		  } catch (RuntimeException re) {
416
			  throw new ServiceFailure("4962", "Couldn't delete " + pid.getValue() + 
417
				  ". The error message was: " + re.getMessage());
418
			  
419
		  } finally {
420
			  lock.unlock();
421
			  logMetacat.debug("Unlocked identifier " + pid.getValue());
422

    
423
		  }
424

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

    
430
      }
431

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

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

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

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

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

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

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

    
569
		  }
570

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

    
576
      }
577

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

    
603
		// The lock to be used for this identifier
604
		Lock lock = null;
605

    
606
		// get the subject
607
		Subject subject = session.getSubject();
608

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

    
615
		}
616

    
617

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

    
624
			try {
625
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
626
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
627
				}
628

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

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

    
643
				}
644

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

    
648
			}
649

    
650
			// set the new policy
651
			systemMetadata.setObsoletedBy(obsoletedByPid);
652

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

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

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

    
714
      try {
715
          lock = HazelcastService.getInstance().getLock(pid.getValue());
716
          lock.lock();
717
          logMetacat.debug("Locked identifier " + pid.getValue());
718

    
719
          try {      
720
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
721

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

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

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

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

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

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

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

    
977
    throw new NotImplemented("4131", "resolve not implemented");
978

    
979
  }
980

    
981
  /**
982
   * Metacat does not implement this method at the CN level
983
   */
984
  @Override
985
  public ObjectList search(Session session, String queryType, String query)
986
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
987
    NotImplemented {
988

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

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

    
1047
    return ObjectFormatService.getInstance().listFormats();
1048
  }
1049

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

    
1062
    throw new NotImplemented("4800", "listNodes not implemented");
1063
  }
1064

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

    
1087
      // The lock to be used for this identifier
1088
      Lock lock = null;
1089

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

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

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

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

    
1220
  @Override
1221
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
1222
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
1223
      NotImplemented, InvalidRequest {
1224
  
1225
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1226
  }
1227

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

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

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

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

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

    
1404
                      }
1405
                  }
1406
              }
1407
          }
1408
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1409
              "to replicate: " + isAllowed + " for " + pid.getValue());
1410

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

    
1431
    } catch (RuntimeException e) {
1432
    	  ServiceFailure sf = new ServiceFailure("4872", 
1433
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1434
                e.getMessage());
1435
    	  sf.initCause(e);
1436
        throw sf;
1437
        
1438
    }
1439
      
1440
    return isAllowed;
1441
    
1442
  }
1443

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

    
1473
      try {
1474
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1475
          // are we allowed?
1476
          boolean isAllowed = false;
1477
          isAllowed = isAdminAuthorized(session);
1478
          
1479
          // additional check if it is the authoritative node if it is not the admin
1480
          if(!isAllowed) {
1481
              isAllowed = isAuthoritativeMNodeAdmin(session, pid);
1482
          }
1483

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

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

    
1527
  }
1528

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

    
1570
          try {
1571
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1572

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

    
1618
    
1619
    // TODO: how do we know if the map was persisted?
1620
    success = true;
1621
    
1622
    return success;
1623
  }
1624

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

    
1655
          // what is the controlling permission?
1656
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1657
              throw new NotAuthorized("4851", "not allowed by "
1658
                      + subject.getValue() + " on " + pid.getValue());
1659
          }
1660

    
1661
        
1662
      } catch (InvalidToken e) {
1663
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1664
                  " on " + pid.getValue());  
1665
          
1666
      }
1667

    
1668
      SystemMetadata systemMetadata = null;
1669
      try {
1670
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1671
          lock.lock();
1672
          logMetacat.debug("Locked identifier " + pid.getValue());
1673

    
1674
          try {      
1675
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1676

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

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

    
1775
        return objectList;
1776
  }
1777

    
1778
  
1779
 	/**
1780
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1781
 	 * @return cal  the list of checksum algorithms
1782
 	 * 
1783
 	 * @throws ServiceFailure
1784
 	 * @throws NotImplemented
1785
 	 */
1786
  @Override
1787
  public ChecksumAlgorithmList listChecksumAlgorithms()
1788
			throws ServiceFailure, NotImplemented {
1789
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1790
		cal.addAlgorithm("MD5");
1791
		cal.addAlgorithm("SHA-1");
1792
		return cal;
1793
		
1794
	}
1795

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

    
1864
	@Override
1865
	public QueryEngineDescription getQueryEngineDescription(Session session,
1866
			String queryEngine) throws InvalidToken, ServiceFailure, NotAuthorized,
1867
			NotImplemented, NotFound {
1868
		throw new NotImplemented("0000", "CN query services are not implemented in Metacat.");
1869

    
1870
	}
1871
	
1872
	@Override
1873
	public QueryEngineList listQueryEngines(Session session) throws InvalidToken,
1874
			ServiceFailure, NotAuthorized, NotImplemented {
1875
		throw new NotImplemented("0000", "CN query services are not implemented in Metacat.");
1876

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

    
1885
	}
1886
	
1887
	@Override
1888
	public Node getCapabilities() throws NotImplemented, ServiceFailure {
1889
		throw new NotImplemented("0000", "The CN capabilities are not stored in Metacat.");
1890
	}
1891
	
1892
	
1893
}
(1-1/7)