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.CNode;
38
import org.dataone.client.D1Client;
39
import org.dataone.client.MNode;
40
import org.dataone.service.cn.v1.CNAuthorization;
41
import org.dataone.service.cn.v1.CNCore;
42
import org.dataone.service.cn.v1.CNRead;
43
import org.dataone.service.cn.v1.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.v1.Log;
63
import org.dataone.service.types.v1.Node;
64
import org.dataone.service.types.v1.NodeList;
65
import org.dataone.service.types.v1.NodeReference;
66
import org.dataone.service.types.v1.NodeType;
67
import org.dataone.service.types.v1.ObjectFormat;
68
import org.dataone.service.types.v1.ObjectFormatIdentifier;
69
import org.dataone.service.types.v1.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.v1.SystemMetadata;
79
import org.dataone.service.types.v1.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.dataone.hazelcast.HazelcastService;
86

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

    
97
  /* the logger instance */
98
  private Logger logMetacat = null;
99

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

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

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

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

    
234
		// get the subject
235
		Subject subject = session.getSubject();
236

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

    
249
		}
250

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

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

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

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

    
276
				}
277

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

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

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

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

    
322
		return true;	  
323
	  
324
  }
325
  
326
  /**
327
   * Deletes an object from the Coordinating Node, where the object is a 
328
   * a science metadata object.
329
   * 
330
   * @param session - the Session object containing the credentials for the Subject
331
   * @param pid - The object identifier to be deleted
332
   * 
333
   * @return pid - the identifier of the object used for the deletion
334
   * 
335
   * @throws InvalidToken
336
   * @throws ServiceFailure
337
   * @throws NotAuthorized
338
   * @throws NotFound
339
   * @throws NotImplemented
340
   * @throws InvalidRequest
341
   */
342
  @Override
343
  public Identifier delete(Session session, Identifier pid) 
344
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
345

    
346
	  // check that it is CN/admin
347
	  boolean allowed = isAdminAuthorized(session);
348
	  
349
	  // additional check if it is the authoritative node if it is not the admin
350
      if(!allowed) {
351
          allowed = isAuthoritativeMNodeAdmin(session, pid);
352
      }
353
	  
354
	  if (!allowed) {
355
		  String msg = "The subject is not allowed to call delete() on a Coordinating Node.";
356
		  logMetacat.info(msg);
357
		  throw new NotAuthorized("1320", msg);
358
	  }
359
	  
360
	  // defer to superclass implementation
361
	  Identifier retId =  super.delete(session, pid);
362
      
363
	  // notify the replicas
364
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
365
	  if (systemMetadata.getReplicaList() != null) {
366
		  for (Replica replica: systemMetadata.getReplicaList()) {
367
			  NodeReference replicaNode = replica.getReplicaMemberNode();
368
			  try {
369
				  Identifier mnRetId = D1Client.getMN(replicaNode).delete(null, pid);
370
			  } catch (Exception e) {
371
				  // all we can really do is log errors and carry on with life
372
				  logMetacat.error("Error deleting pid: " +  pid.getValue() + " from replica MN: " + replicaNode.getValue(), e);
373
			}
374
			  
375
		  }
376
	  }
377
	  
378
	  return retId;
379
      
380
  }
381
  
382
  /**
383
   * Deletes an object from the Coordinating Node, where the object is a 
384
   * a science metadata object.
385
   * 
386
   * @param session - the Session object containing the credentials for the Subject
387
   * @param pid - The object identifier to be deleted
388
   * 
389
   * @return pid - the identifier of the object used for the deletion
390
   * 
391
   * @throws InvalidToken
392
   * @throws ServiceFailure
393
   * @throws NotAuthorized
394
   * @throws NotFound
395
   * @throws NotImplemented
396
   * @throws InvalidRequest
397
   */
398
  @Override
399
  public Identifier archive(Session session, Identifier pid) 
400
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
401

    
402
	  // check that it is CN/admin
403
	  boolean allowed = isAdminAuthorized(session);
404
	  
405
	  //check if it is the authoritative member node
406
	  if(!allowed) {
407
	      allowed = isAuthoritativeMNodeAdmin(session, pid);
408
	  }
409
	  
410
	  if (!allowed) {
411
		  String msg = "The subject is not allowed to call archive() on a Coordinating Node.";
412
		  logMetacat.info(msg);
413
		  throw new NotAuthorized("1320", msg);
414
	  }
415
	  
416
	  // defer to superclass implementation
417
	  Identifier retId =  super.archive(session, pid);
418
      
419
	  // notify the replicas
420
	  SystemMetadata systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
421
	  if (systemMetadata.getReplicaList() != null) {
422
		  for (Replica replica: systemMetadata.getReplicaList()) {
423
			  NodeReference replicaNode = replica.getReplicaMemberNode();
424
			  try {
425
				  // TODO: implement in the clients
426
				  //Identifier mnRetId = D1Client.getMN(replicaNode).archive(null, pid);
427
			  } catch (Exception e) {
428
				  // all we can really do is log errors and carry on with life
429
				  logMetacat.error("Error archiving pid: " +  pid.getValue() + " from replica MN: " + replicaNode.getValue(), e);
430
			}
431
			  
432
		  }
433
	  }
434
	  
435
	  return retId;
436
      
437
  }
438
  
439
  /**
440
   * Set the obsoletedBy attribute in System Metadata
441
   * @param session
442
   * @param pid
443
   * @param obsoletedByPid
444
   * @param serialVersion
445
   * @return
446
   * @throws NotImplemented
447
   * @throws NotFound
448
   * @throws NotAuthorized
449
   * @throws ServiceFailure
450
   * @throws InvalidRequest
451
   * @throws InvalidToken
452
   * @throws VersionMismatch
453
   */
454
  @Override
455
  public boolean setObsoletedBy(Session session, Identifier pid,
456
			Identifier obsoletedByPid, long serialVersion)
457
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
458
			InvalidRequest, InvalidToken, VersionMismatch {
459

    
460
		// The lock to be used for this identifier
461
		Lock lock = null;
462

    
463
		// get the subject
464
		Subject subject = session.getSubject();
465

    
466
		// are we allowed to do this?
467
		if (!isAuthorized(session, pid, Permission.WRITE)) {
468
			throw new NotAuthorized("4881", Permission.WRITE
469
					+ " not allowed by " + subject.getValue() + " on "
470
					+ pid.getValue());
471

    
472
		}
473

    
474

    
475
		SystemMetadata systemMetadata = null;
476
		try {
477
			lock = HazelcastService.getInstance().getLock(pid.getValue());
478
			lock.lock();
479
			logMetacat.debug("Locked identifier " + pid.getValue());
480

    
481
			try {
482
				if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
483
					systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
484
				}
485

    
486
				// did we get it correctly?
487
				if (systemMetadata == null) {
488
					throw new NotFound("4884", "Couldn't find an object identified by " + pid.getValue());
489
				}
490

    
491
				// does the request have the most current system metadata?
492
				if (systemMetadata.getSerialVersion().longValue() != serialVersion) {
493
					String msg = "The requested system metadata version number "
494
							+ serialVersion
495
							+ " differs from the current version at "
496
							+ systemMetadata.getSerialVersion().longValue()
497
							+ ". Please get the latest copy in order to modify it.";
498
					throw new VersionMismatch("4886", msg);
499

    
500
				}
501

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

    
505
			}
506

    
507
			// set the new policy
508
			systemMetadata.setObsoletedBy(obsoletedByPid);
509

    
510
			// update the metadata
511
			try {
512
				systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
513
				systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
514
				HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
515
			} catch (RuntimeException e) {
516
				throw new ServiceFailure("4882", e.getMessage());
517
			}
518

    
519
		} catch (RuntimeException e) {
520
			throw new ServiceFailure("4882", e.getMessage());
521
		} finally {
522
			lock.unlock();
523
			logMetacat.debug("Unlocked identifier " + pid.getValue());
524
		}
525

    
526
		return true;
527
	}
528
  
529
  
530
  /**
531
   * Set the replication status for an object given the object identifier
532
   * 
533
   * @param session - the Session object containing the credentials for the Subject
534
   * @param pid - the object identifier for the given object
535
   * @param status - the replication status to be applied
536
   * 
537
   * @return true or false
538
   * 
539
   * @throws NotImplemented
540
   * @throws NotAuthorized
541
   * @throws ServiceFailure
542
   * @throws InvalidRequest
543
   * @throws InvalidToken
544
   * @throws NotFound
545
   * 
546
   */
547
  @Override
548
  public boolean setReplicationStatus(Session session, Identifier pid,
549
      NodeReference targetNode, ReplicationStatus status, BaseException failure) 
550
      throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
551
      InvalidRequest, NotFound {
552
	  
553
	  // cannot be called by public
554
	  if (session == null) {
555
		  throw new NotAuthorized("4720", "Session cannot be null");
556
	  }
557
      
558
      // The lock to be used for this identifier
559
      Lock lock = null;
560
      
561
      boolean allowed = false;
562
      int replicaEntryIndex = -1;
563
      List<Replica> replicas = null;
564
      // get the subject
565
      Subject subject = session.getSubject();
566
      logMetacat.debug("ReplicationStatus for identifier " + pid.getValue() +
567
          " is " + status.toString());
568
      
569
      SystemMetadata systemMetadata = null;
570

    
571
      try {
572
          lock = HazelcastService.getInstance().getLock(pid.getValue());
573
          lock.lock();
574
          logMetacat.debug("Locked identifier " + pid.getValue());
575

    
576
          try {      
577
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
578

    
579
              // did we get it correctly?
580
              if ( systemMetadata == null ) {
581
                  logMetacat.debug("systemMetadata is null for " + pid.getValue());
582
                  throw new NotFound("4740", "Couldn't find an object identified by " + pid.getValue());
583
                  
584
              }
585
              replicas = systemMetadata.getReplicaList();
586
              int count = 0;
587
              
588
              // was there a failure? log it
589
              if ( failure != null && status.equals(ReplicationStatus.FAILED) ) {
590
                 String msg = "The replication request of the object identified by " + 
591
                     pid.getValue() + " failed.  The error message was " +
592
                     failure.getMessage() + ".";
593
              }
594
              
595
              if (replicas.size() > 0 && replicas != null) {
596
                  // find the target replica index in the replica list
597
                  for (Replica replica : replicas) {
598
                      String replicaNodeStr = replica.getReplicaMemberNode().getValue();
599
                      String targetNodeStr = targetNode.getValue();
600
                      logMetacat.debug("Comparing " + replicaNodeStr + " to " + targetNodeStr);
601
                  
602
                      if (replicaNodeStr.equals(targetNodeStr)) {
603
                          replicaEntryIndex = count;
604
                          logMetacat.debug("replica entry index is: "
605
                                  + replicaEntryIndex);
606
                          break;
607
                      }
608
                      count++;
609
                  
610
                  }
611
              }
612
              // are we allowed to do this? only CNs and target MNs are allowed
613
              CNode cn = D1Client.getCN();
614
              List<Node> nodes = cn.listNodes().getNodeList();
615
              
616
              // find the node in the node list
617
              for ( Node node : nodes ) {
618
                  
619
                  NodeReference nodeReference = node.getIdentifier();
620
                  logMetacat.debug("In setReplicationStatus(), Node reference is: " + 
621
                      nodeReference.getValue());
622
                  
623
                  // allow target MN certs
624
                  if ( targetNode.getValue().equals(nodeReference.getValue() ) &&
625
                      node.getType().equals(NodeType.MN)) {
626
                      List<Subject> nodeSubjects = node.getSubjectList();
627
                      
628
                      // check if the session subject is in the node subject list
629
                      for (Subject nodeSubject : nodeSubjects) {
630
                          logMetacat.debug("In setReplicationStatus(), comparing subjects: " +
631
                                  nodeSubject.getValue() + " and " + subject.getValue());
632
                          if ( nodeSubject.equals(subject) ) { // subject of session == target node subject
633
                              
634
                              // lastly limit to COMPLETED, INVALIDATED,
635
                              // and FAILED status updates from MNs only
636
                              if ( status.equals(ReplicationStatus.COMPLETED) ||
637
                                   status.equals(ReplicationStatus.INVALIDATED) ||
638
                                   status.equals(ReplicationStatus.FAILED)) {
639
                                  allowed = true;
640
                                  break;
641
                                  
642
                              }                              
643
                          }
644
                      }                 
645
                  }
646
              }
647

    
648
              if ( !allowed ) {
649
                  //check for CN admin access
650
                  allowed = isAuthorized(session, pid, Permission.WRITE);
651
                  
652
              }              
653
              
654
              if ( !allowed ) {
655
                  String msg = "The subject identified by "
656
                          + subject.getValue()
657
                          + " does not have permission to set the replication status for "
658
                          + "the replica identified by "
659
                          + targetNode.getValue() + ".";
660
                  logMetacat.info(msg);
661
                  throw new NotAuthorized("4720", msg);
662
                  
663
              }
664

    
665
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
666
            throw new NotFound("4740", "No record found for: " + pid.getValue() +
667
                " : " + e.getMessage());
668
            
669
          }
670
          
671
          Replica targetReplica = new Replica();
672
          // set the status for the replica
673
          if ( replicaEntryIndex != -1 ) {
674
              targetReplica = replicas.get(replicaEntryIndex);
675
              
676
              // don't allow status to change from COMPLETED to anything other
677
              // than INVALIDATED: prevents overwrites from race conditions
678
              if ( targetReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
679
            	   !status.equals(ReplicationStatus.INVALIDATED)) {
680
            	  throw new InvalidRequest("4730", "Status state change from " +
681
            			  targetReplica.getReplicationStatus() + " to " +
682
            			  status.toString() + "is prohibited for identifier " +
683
            			  pid.getValue() + " and target node " + 
684
            			  targetReplica.getReplicaMemberNode().getValue());
685
              }
686
              
687
              targetReplica.setReplicationStatus(status);
688
              
689
              logMetacat.debug("Set the replication status for " + 
690
                  targetReplica.getReplicaMemberNode().getValue() + " to " +
691
                  targetReplica.getReplicationStatus() + " for identifier " +
692
                  pid.getValue());
693
              
694
          } else {
695
              // this is a new entry, create it
696
              targetReplica.setReplicaMemberNode(targetNode);
697
              targetReplica.setReplicationStatus(status);
698
              targetReplica.setReplicaVerified(Calendar.getInstance().getTime());
699
              replicas.add(targetReplica);
700
              
701
          }
702
          
703
          systemMetadata.setReplicaList(replicas);
704
                
705
          // update the metadata
706
          try {
707
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
708
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
709
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
710

    
711
              if ( !status.equals(ReplicationStatus.QUEUED) && 
712
            	   !status.equals(ReplicationStatus.REQUESTED)) {
713
                  
714
                logMetacat.trace("METRICS:\tREPLICATION:\tEND REQUEST:\tPID:\t" + pid.getValue() + 
715
                          "\tNODE:\t" + targetNode.getValue() + 
716
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
717
                
718
                logMetacat.trace("METRICS:\tREPLICATION:\t" + status.toString().toUpperCase() +
719
                          "\tPID:\t"  + pid.getValue() + 
720
                          "\tNODE:\t" + targetNode.getValue() + 
721
                          "\tSIZE:\t" + systemMetadata.getSize().intValue());
722
              }
723

    
724
              if ( status.equals(ReplicationStatus.FAILED) && failure != null ) {
725
                  logMetacat.warn("Replication failed for identifier " + pid.getValue() +
726
                      " on target node " + targetNode + ". The exception was: " +
727
                      failure.getMessage());
728
              }
729
              
730
			  // update the replica nodes about the completed replica when complete
731
              if (status.equals(ReplicationStatus.COMPLETED)) {
732
				notifyReplicaNodes(systemMetadata);
733
			}
734
          
735
          } catch (RuntimeException e) {
736
              throw new ServiceFailure("4700", e.getMessage());
737
          
738
          }
739
          
740
    } catch (RuntimeException e) {
741
        String msg = "There was a RuntimeException getting the lock for " +
742
            pid.getValue();
743
        logMetacat.info(msg);
744
        
745
    } finally {
746
        lock.unlock();
747
        logMetacat.debug("Unlocked identifier " + pid.getValue());
748
        
749
    }
750
      
751
      return true;
752
  }
753
  
754
/**
755
   * Return the checksum of the object given the identifier 
756
   * 
757
   * @param session - the Session object containing the credentials for the Subject
758
   * @param pid - the object identifier for the given object
759
   * 
760
   * @return checksum - the checksum of the object
761
   * 
762
   * @throws InvalidToken
763
   * @throws ServiceFailure
764
   * @throws NotAuthorized
765
   * @throws NotFound
766
   * @throws NotImplemented
767
   */
768
  @Override
769
  public Checksum getChecksum(Session session, Identifier pid)
770
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
771
    NotImplemented {
772
    
773
	boolean isAuthorized = false;
774
	try {
775
		isAuthorized = isAuthorized(session, pid, Permission.READ);
776
	} catch (InvalidRequest e) {
777
		throw new ServiceFailure("1410", e.getDescription());
778
	}  
779
    if (!isAuthorized) {
780
        throw new NotAuthorized("1400", Permission.READ + " not allowed on " + pid.getValue());  
781
    }
782
    
783
    SystemMetadata systemMetadata = null;
784
    Checksum checksum = null;
785
    
786
    try {
787
        systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);        
788

    
789
        if (systemMetadata == null ) {
790
            throw new NotFound("1420", "Couldn't find an object identified by " + pid.getValue());
791
        }
792
        checksum = systemMetadata.getChecksum();
793
        
794
    } catch (RuntimeException e) {
795
        throw new ServiceFailure("1410", "An error occurred getting the checksum for " + 
796
            pid.getValue() + ". The error message was: " + e.getMessage());
797
      
798
    }
799
    
800
    return checksum;
801
  }
802

    
803
  /**
804
   * Resolve the location of a given object
805
   * 
806
   * @param session - the Session object containing the credentials for the Subject
807
   * @param pid - the object identifier for the given object
808
   * 
809
   * @return objectLocationList - the list of nodes known to contain the object
810
   * 
811
   * @throws InvalidToken
812
   * @throws ServiceFailure
813
   * @throws NotAuthorized
814
   * @throws NotFound
815
   * @throws NotImplemented
816
   */
817
  @Override
818
  public ObjectLocationList resolve(Session session, Identifier pid)
819
    throws InvalidToken, ServiceFailure, NotAuthorized,
820
    NotFound, NotImplemented {
821

    
822
    throw new NotImplemented("4131", "resolve not implemented");
823

    
824
  }
825

    
826
  /**
827
   * Metacat does not implement this method at the CN level
828
   */
829
  @Override
830
  public ObjectList search(Session session, String queryType, String query)
831
    throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
832
    NotImplemented {
833

    
834
		  throw new NotImplemented("4281", "Metacat does not implement CN.search");
835
	  
836
//    ObjectList objectList = null;
837
//    try {
838
//        objectList = 
839
//          IdentifierManager.getInstance().querySystemMetadata(
840
//              null, //startTime, 
841
//              null, //endTime,
842
//              null, //objectFormat, 
843
//              false, //replicaStatus, 
844
//              0, //start, 
845
//              1000 //count
846
//              );
847
//        
848
//    } catch (Exception e) {
849
//      throw new ServiceFailure("4310", "Error querying system metadata: " + e.getMessage());
850
//    }
851
//
852
//      return objectList;
853
		  
854
  }
855
  
856
  /**
857
   * Returns the object format registered in the DataONE Object Format 
858
   * Vocabulary for the given format identifier
859
   * 
860
   * @param fmtid - the identifier of the format requested
861
   * 
862
   * @return objectFormat - the object format requested
863
   * 
864
   * @throws ServiceFailure
865
   * @throws NotFound
866
   * @throws InsufficientResources
867
   * @throws NotImplemented
868
   */
869
  @Override
870
  public ObjectFormat getFormat(ObjectFormatIdentifier fmtid)
871
    throws ServiceFailure, NotFound, NotImplemented {
872
     
873
      return ObjectFormatService.getInstance().getFormat(fmtid);
874
      
875
  }
876

    
877
  /**
878
   * Returns a list of all object formats registered in the DataONE Object 
879
   * Format Vocabulary
880
    * 
881
   * @return objectFormatList - The list of object formats registered in 
882
   *                            the DataONE Object Format Vocabulary
883
   * 
884
   * @throws ServiceFailure
885
   * @throws NotImplemented
886
   * @throws InsufficientResources
887
   */
888
  @Override
889
  public ObjectFormatList listFormats() 
890
    throws ServiceFailure, NotImplemented {
891

    
892
    return ObjectFormatService.getInstance().listFormats();
893
  }
894

    
895
  /**
896
   * Returns a list of nodes that have been registered with the DataONE infrastructure
897
    * 
898
   * @return nodeList - List of nodes from the registry
899
   * 
900
   * @throws ServiceFailure
901
   * @throws NotImplemented
902
   */
903
  @Override
904
  public NodeList listNodes() 
905
    throws NotImplemented, ServiceFailure {
906

    
907
    throw new NotImplemented("4800", "listNodes not implemented");
908
  }
909

    
910
  /**
911
   * Provides a mechanism for adding system metadata independently of its 
912
   * associated object, such as when adding system metadata for data objects.
913
    * 
914
   * @param session - the Session object containing the credentials for the Subject
915
   * @param pid - The identifier of the object to register the system metadata against
916
   * @param sysmeta - The system metadata to be registered
917
   * 
918
   * @return true if the registration succeeds
919
   * 
920
   * @throws NotImplemented
921
   * @throws NotAuthorized
922
   * @throws ServiceFailure
923
   * @throws InvalidRequest
924
   * @throws InvalidSystemMetadata
925
   */
926
  @Override
927
  public Identifier registerSystemMetadata(Session session, Identifier pid,
928
      SystemMetadata sysmeta) 
929
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest, 
930
      InvalidSystemMetadata {
931

    
932
      // The lock to be used for this identifier
933
      Lock lock = null;
934

    
935
      // TODO: control who can call this?
936
      if (session == null) {
937
          //TODO: many of the thrown exceptions do not use the correct error codes
938
          //check these against the docs and correct them
939
          throw new NotAuthorized("4861", "No Session - could not authorize for registration." +
940
                  "  If you are not logged in, please do so and retry the request.");
941
      }
942
      
943
      // verify that guid == SystemMetadata.getIdentifier()
944
      logMetacat.debug("Comparing guid|sysmeta_guid: " + pid.getValue() + 
945
          "|" + sysmeta.getIdentifier().getValue());
946
      if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
947
          throw new InvalidRequest("4863", 
948
              "The identifier in method call (" + pid.getValue() + 
949
              ") does not match identifier in system metadata (" +
950
              sysmeta.getIdentifier().getValue() + ").");
951
      }
952

    
953
      try {
954
          lock = HazelcastService.getInstance().getLock(sysmeta.getIdentifier().getValue());
955
          lock.lock();
956
          logMetacat.debug("Locked identifier " + pid.getValue());
957
          logMetacat.debug("Checking if identifier exists...");
958
          // Check that the identifier does not already exist
959
          if (HazelcastService.getInstance().getSystemMetadataMap().containsKey(pid)) {
960
              throw new InvalidRequest("4863", 
961
                  "The identifier is already in use by an existing object.");
962
          
963
          }
964
          
965
          // insert the system metadata into the object store
966
          logMetacat.debug("Starting to insert SystemMetadata...");
967
          try {
968
              sysmeta.setSerialVersion(BigInteger.ONE);
969
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
970
              HazelcastService.getInstance().getSystemMetadataMap().put(sysmeta.getIdentifier(), sysmeta);
971
              
972
          } catch (RuntimeException e) {
973
            logMetacat.error("Problem registering system metadata: " + pid.getValue(), e);
974
              throw new ServiceFailure("4862", "Error inserting system metadata: " + 
975
                  e.getClass() + ": " + e.getMessage());
976
              
977
          }
978
          
979
      } catch (RuntimeException e) {
980
          throw new ServiceFailure("4862", "Error inserting system metadata: " + 
981
                  e.getClass() + ": " + e.getMessage());
982
          
983
      }  finally {
984
          lock.unlock();
985
          logMetacat.debug("Unlocked identifier " + pid.getValue());
986
          
987
      }
988

    
989
      
990
      logMetacat.debug("Returning from registerSystemMetadata");
991
      EventLog.getInstance().log(request.getRemoteAddr(), 
992
          request.getHeader("User-Agent"), session.getSubject().getValue(), 
993
          pid.getValue(), "registerSystemMetadata");
994
      return pid;
995
  }
996
  
997
  /**
998
   * Given an optional scope and format, reserves and returns an identifier 
999
   * within that scope and format that is unique and will not be 
1000
   * used by any other sessions. 
1001
    * 
1002
   * @param session - the Session object containing the credentials for the Subject
1003
   * @param pid - The identifier of the object to register the system metadata against
1004
   * @param scope - An optional string to be used to qualify the scope of 
1005
   *                the identifier namespace, which is applied differently 
1006
   *                depending on the format requested. If scope is not 
1007
   *                supplied, a default scope will be used.
1008
   * @param format - The optional name of the identifier format to be used, 
1009
   *                  drawn from a DataONE-specific vocabulary of identifier 
1010
   *                 format names, including several common syntaxes such 
1011
   *                 as DOI, LSID, UUID, and LSRN, among others. If the 
1012
   *                 format is not supplied by the caller, the CN service 
1013
   *                 will use a default identifier format, which may change 
1014
   *                 over time.
1015
   * 
1016
   * @return true if the registration succeeds
1017
   * 
1018
   * @throws InvalidToken
1019
   * @throws ServiceFailure
1020
   * @throws NotAuthorized
1021
   * @throws IdentifierNotUnique
1022
   * @throws NotImplemented
1023
   */
1024
  @Override
1025
  public Identifier reserveIdentifier(Session session, Identifier pid)
1026
  throws InvalidToken, ServiceFailure,
1027
        NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest {
1028

    
1029
    throw new NotImplemented("4191", "reserveIdentifier not implemented on this node");
1030
  }
1031
  
1032
  @Override
1033
  public Identifier generateIdentifier(Session session, String scheme, String fragment)
1034
  throws InvalidToken, ServiceFailure,
1035
        NotAuthorized, NotImplemented, InvalidRequest {
1036
    throw new NotImplemented("4191", "generateIdentifier not implemented on this node");
1037
  }
1038
  
1039
  /**
1040
    * Checks whether the pid is reserved by the subject in the session param
1041
    * If the reservation is held on the pid by the subject, we return true.
1042
    * 
1043
   * @param session - the Session object containing the Subject
1044
   * @param pid - The identifier to check
1045
   * 
1046
   * @return true if the reservation exists for the subject/pid
1047
   * 
1048
   * @throws InvalidToken
1049
   * @throws ServiceFailure
1050
   * @throws NotFound - when the pid is not found (in use or in reservation)
1051
   * @throws NotAuthorized - when the subject does not hold a reservation on the pid
1052
   * @throws IdentifierNotUnique - when the pid is in use
1053
   * @throws NotImplemented
1054
   */
1055

    
1056
  @Override
1057
  public boolean hasReservation(Session session, Subject subject, Identifier pid) 
1058
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, IdentifierNotUnique, 
1059
      NotImplemented, InvalidRequest {
1060
  
1061
      throw new NotImplemented("4191", "hasReservation not implemented on this node");
1062
  }
1063

    
1064
  /**
1065
   * Changes ownership (RightsHolder) of the specified object to the 
1066
   * subject specified by userId
1067
    * 
1068
   * @param session - the Session object containing the credentials for the Subject
1069
   * @param pid - Identifier of the object to be modified
1070
   * @param userId - The subject that will be taking ownership of the specified object.
1071
   *
1072
   * @return pid - the identifier of the modified object
1073
   * 
1074
   * @throws ServiceFailure
1075
   * @throws InvalidToken
1076
   * @throws NotFound
1077
   * @throws NotAuthorized
1078
   * @throws NotImplemented
1079
   * @throws InvalidRequest
1080
   */  
1081
  @Override
1082
  public Identifier setRightsHolder(Session session, Identifier pid, Subject userId,
1083
      long serialVersion)
1084
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1085
      NotImplemented, InvalidRequest, VersionMismatch {
1086
      
1087
      // The lock to be used for this identifier
1088
      Lock lock = null;
1089

    
1090
      // get the subject
1091
      Subject subject = session.getSubject();
1092
      
1093
      // are we allowed to do this?
1094
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1095
          throw new NotAuthorized("4440", "not allowed by "
1096
                  + subject.getValue() + " on " + pid.getValue());
1097
          
1098
      }
1099
      
1100
      SystemMetadata systemMetadata = null;
1101
      try {
1102
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1103
          lock.lock();
1104
          logMetacat.debug("Locked identifier " + pid.getValue());
1105

    
1106
          try {
1107
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1108
              
1109
              // does the request have the most current system metadata?
1110
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1111
                 String msg = "The requested system metadata version number " + 
1112
                     serialVersion + " differs from the current version at " +
1113
                     systemMetadata.getSerialVersion().longValue() +
1114
                     ". Please get the latest copy in order to modify it.";
1115
                 throw new VersionMismatch("4443", msg);
1116
              }
1117
              
1118
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1119
              throw new NotFound("4460", "No record found for: " + pid.getValue());
1120
              
1121
          }
1122
              
1123
          // set the new rights holder
1124
          systemMetadata.setRightsHolder(userId);
1125
          
1126
          // update the metadata
1127
          try {
1128
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1129
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1130
              HazelcastService.getInstance().getSystemMetadataMap().put(pid, systemMetadata);
1131
              notifyReplicaNodes(systemMetadata);
1132
              
1133
          } catch (RuntimeException e) {
1134
              throw new ServiceFailure("4490", e.getMessage());
1135
          
1136
          }
1137
          
1138
      } catch (RuntimeException e) {
1139
          throw new ServiceFailure("4490", e.getMessage());
1140
          
1141
      } finally {
1142
          lock.unlock();
1143
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1144
      
1145
      }
1146
      
1147
      return pid;
1148
  }
1149

    
1150
  /**
1151
   * Verify that a replication task is authorized by comparing the target node's
1152
   * Subject (from the X.509 certificate-derived Session) with the list of 
1153
   * subjects in the known, pending replication tasks map.
1154
   * 
1155
   * @param originatingNodeSession - Session information that contains the 
1156
   *                                 identity of the calling user
1157
   * @param targetNodeSubject - Subject identifying the target node
1158
   * @param pid - the identifier of the object to be replicated
1159
   * @param replicatePermission - the execute permission to be granted
1160
   * 
1161
   * @throws ServiceFailure
1162
   * @throws NotImplemented
1163
   * @throws InvalidToken
1164
   * @throws NotAuthorized
1165
   * @throws InvalidRequest
1166
   * @throws NotFound
1167
   */
1168
  @Override
1169
  public boolean isNodeAuthorized(Session originatingNodeSession, 
1170
    Subject targetNodeSubject, Identifier pid) 
1171
    throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
1172
    NotFound, InvalidRequest {
1173
    
1174
    boolean isAllowed = false;
1175
    SystemMetadata sysmeta = null;
1176
    NodeReference targetNode = null;
1177
    
1178
    try {
1179
      // get the target node reference from the nodes list
1180
      CNode cn = D1Client.getCN();
1181
      List<Node> nodes = cn.listNodes().getNodeList();
1182
      
1183
      if ( nodes != null ) {
1184
        for (Node node : nodes) {
1185
            
1186
        	if (node.getSubjectList() != null) {
1187
        		
1188
	            for (Subject nodeSubject : node.getSubjectList()) {
1189
	            	
1190
	                if ( nodeSubject.equals(targetNodeSubject) ) {
1191
	                    targetNode = node.getIdentifier();
1192
	                    logMetacat.debug("targetNode is : " + targetNode.getValue());
1193
	                    break;
1194
	                }
1195
	            }
1196
        	}
1197
            
1198
            if ( targetNode != null) { break; }
1199
        }
1200
        
1201
      } else {
1202
          String msg = "Couldn't get the node list from the CN";
1203
          logMetacat.debug(msg);
1204
          throw new ServiceFailure("4872", msg);
1205
          
1206
      }
1207
      
1208
      // can't find a node listed with the given subject
1209
      if ( targetNode == null ) {
1210
          String msg = "There is no Member Node registered with a node subject " +
1211
              "matching " + targetNodeSubject.getValue();
1212
          logMetacat.info(msg);
1213
          throw new NotAuthorized("4871", msg);
1214
          
1215
      }
1216
      
1217
      logMetacat.debug("Getting system metadata for identifier " + pid.getValue());
1218
      
1219
      sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1220

    
1221
      if ( sysmeta != null ) {
1222
          
1223
          List<Replica> replicaList = sysmeta.getReplicaList();
1224
          
1225
          if ( replicaList != null ) {
1226
              
1227
              // find the replica with the status set to 'requested'
1228
              for (Replica replica : replicaList) {
1229
                  ReplicationStatus status = replica.getReplicationStatus();
1230
                  NodeReference listedNode = replica.getReplicaMemberNode();
1231
                  if ( listedNode != null && targetNode != null ) {
1232
                      logMetacat.debug("Comparing " + listedNode.getValue()
1233
                              + " to " + targetNode.getValue());
1234
                      
1235
                      if (listedNode.getValue().equals(targetNode.getValue())
1236
                              && status.equals(ReplicationStatus.REQUESTED)) {
1237
                          isAllowed = true;
1238
                          break;
1239

    
1240
                      }
1241
                  }
1242
              }
1243
          }
1244
          logMetacat.debug("The " + targetNode.getValue() + " is allowed " +
1245
              "to replicate: " + isAllowed + " for " + pid.getValue());
1246

    
1247
          
1248
      } else {
1249
          logMetacat.debug("System metadata for identifier " + pid.getValue() +
1250
          " is null.");          
1251
          throw new NotFound("4874", "Couldn't find an object identified by " + pid.getValue());
1252
          
1253
      }
1254

    
1255
    } catch (RuntimeException e) {
1256
    	  ServiceFailure sf = new ServiceFailure("4872", 
1257
                "Runtime Exception: Couldn't determine if node is allowed: " + 
1258
                e.getMessage());
1259
    	  sf.initCause(e);
1260
        throw sf;
1261
        
1262
    }
1263
      
1264
    return isAllowed;
1265
    
1266
  }
1267

    
1268
  /**
1269
   * Adds a new object to the Node, where the object is a science metadata object.
1270
   * 
1271
   * @param session - the Session object containing the credentials for the Subject
1272
   * @param pid - The object identifier to be created
1273
   * @param object - the object bytes
1274
   * @param sysmeta - the system metadata that describes the object  
1275
   * 
1276
   * @return pid - the object identifier created
1277
   * 
1278
   * @throws InvalidToken
1279
   * @throws ServiceFailure
1280
   * @throws NotAuthorized
1281
   * @throws IdentifierNotUnique
1282
   * @throws UnsupportedType
1283
   * @throws InsufficientResources
1284
   * @throws InvalidSystemMetadata
1285
   * @throws NotImplemented
1286
   * @throws InvalidRequest
1287
   */
1288
  public Identifier create(Session session, Identifier pid, InputStream object,
1289
    SystemMetadata sysmeta) 
1290
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
1291
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
1292
    NotImplemented, InvalidRequest {
1293
                  
1294
      // The lock to be used for this identifier
1295
      Lock lock = null;
1296

    
1297
      try {
1298
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1299
          // are we allowed?
1300
          boolean isAllowed = false;
1301
          isAllowed = isAdminAuthorized(session);
1302
          
1303
          // additional check if it is the authoritative node if it is not the admin
1304
          if(!isAllowed) {
1305
              isAllowed = isAuthoritativeMNodeAdmin(session, pid);
1306
          }
1307

    
1308
          // proceed if we're called by a CN
1309
          if ( isAllowed ) {
1310
              // create the coordinating node version of the document      
1311
              lock.lock();
1312
              logMetacat.debug("Locked identifier " + pid.getValue());
1313
              sysmeta.setSerialVersion(BigInteger.ONE);
1314
              sysmeta.setDateSysMetadataModified(Calendar.getInstance().getTime());
1315
              sysmeta.setArchived(false); // this is a create op, not update
1316
              
1317
              // the CN should have set the origin and authoritative member node fields
1318
              try {
1319
                  sysmeta.getOriginMemberNode().getValue();
1320
                  sysmeta.getAuthoritativeMemberNode().getValue();
1321
                  
1322
              } catch (NullPointerException npe) {
1323
                  throw new InvalidSystemMetadata("4896", 
1324
                      "Both the origin and authoritative member node identifiers need to be set.");
1325
                  
1326
              }
1327
              pid = super.create(session, pid, object, sysmeta);
1328

    
1329
          } else {
1330
              String msg = "The subject listed as " + session.getSubject().getValue() + 
1331
                  " isn't allowed to call create() on a Coordinating Node.";
1332
              logMetacat.info(msg);
1333
              throw new NotAuthorized("1100", msg);
1334
          }
1335
          
1336
      } catch (RuntimeException e) {
1337
          // Convert Hazelcast runtime exceptions to service failures
1338
          String msg = "There was a problem creating the object identified by " +
1339
              pid.getValue() + ". There error message was: " + e.getMessage();
1340
          throw new ServiceFailure("4893", msg);
1341
          
1342
      } finally {
1343
    	  if (lock != null) {
1344
	          lock.unlock();
1345
	          logMetacat.debug("Unlocked identifier " + pid.getValue());
1346
    	  }
1347
      }
1348
      
1349
      return pid;
1350

    
1351
  }
1352

    
1353
  /**
1354
   * Set access for a given object using the object identifier and a Subject
1355
   * under a given Session.
1356
   * 
1357
   * @param session - the Session object containing the credentials for the Subject
1358
   * @param pid - the object identifier for the given object to apply the policy
1359
   * @param policy - the access policy to be applied
1360
   * 
1361
   * @return true if the application of the policy succeeds
1362
   * @throws InvalidToken
1363
   * @throws ServiceFailure
1364
   * @throws NotFound
1365
   * @throws NotAuthorized
1366
   * @throws NotImplemented
1367
   * @throws InvalidRequest
1368
   */
1369
  public boolean setAccessPolicy(Session session, Identifier pid, 
1370
      AccessPolicy accessPolicy, long serialVersion) 
1371
      throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1372
      NotImplemented, InvalidRequest, VersionMismatch {
1373
      
1374
      // The lock to be used for this identifier
1375
      Lock lock = null;
1376
      SystemMetadata systemMetadata = null;
1377
      
1378
      boolean success = false;
1379
      
1380
      // get the subject
1381
      Subject subject = session.getSubject();
1382
      
1383
      // are we allowed to do this?
1384
      if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
1385
          throw new NotAuthorized("4420", "not allowed by "
1386
                  + subject.getValue() + " on " + pid.getValue());
1387
      }
1388
      
1389
      try {
1390
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1391
          lock.lock();
1392
          logMetacat.debug("Locked identifier " + pid.getValue());
1393

    
1394
          try {
1395
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1396

    
1397
              if ( systemMetadata == null ) {
1398
                  throw new NotFound("4400", "Couldn't find an object identified by " + pid.getValue());
1399
                  
1400
              }
1401
              // does the request have the most current system metadata?
1402
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1403
                 String msg = "The requested system metadata version number " + 
1404
                     serialVersion + " differs from the current version at " +
1405
                     systemMetadata.getSerialVersion().longValue() +
1406
                     ". Please get the latest copy in order to modify it.";
1407
                 throw new VersionMismatch("4402", msg);
1408
                 
1409
              }
1410
              
1411
          } catch (RuntimeException e) {
1412
              // convert Hazelcast RuntimeException to NotFound
1413
              throw new NotFound("4400", "No record found for: " + pid);
1414
            
1415
          }
1416
              
1417
          // set the access policy
1418
          systemMetadata.setAccessPolicy(accessPolicy);
1419
          
1420
          // update the system metadata
1421
          try {
1422
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1423
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1424
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1425
              notifyReplicaNodes(systemMetadata);
1426
              
1427
          } catch (RuntimeException e) {
1428
              // convert Hazelcast RuntimeException to ServiceFailure
1429
              throw new ServiceFailure("4430", e.getMessage());
1430
            
1431
          }
1432
          
1433
      } catch (RuntimeException e) {
1434
          throw new ServiceFailure("4430", e.getMessage());
1435
          
1436
      } finally {
1437
          lock.unlock();
1438
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1439
        
1440
      }
1441

    
1442
    
1443
    // TODO: how do we know if the map was persisted?
1444
    success = true;
1445
    
1446
    return success;
1447
  }
1448

    
1449
  /**
1450
   * Full replacement of replication metadata in the system metadata for the 
1451
   * specified object, changes date system metadata modified
1452
   * 
1453
   * @param session - the Session object containing the credentials for the Subject
1454
   * @param pid - the object identifier for the given object to apply the policy
1455
   * @param replica - the replica to be updated
1456
   * @return
1457
   * @throws NotImplemented
1458
   * @throws NotAuthorized
1459
   * @throws ServiceFailure
1460
   * @throws InvalidRequest
1461
   * @throws NotFound
1462
   * @throws VersionMismatch
1463
   */
1464
  @Override
1465
  public boolean updateReplicationMetadata(Session session, Identifier pid,
1466
      Replica replica, long serialVersion) 
1467
      throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1468
      NotFound, VersionMismatch {
1469
      
1470
      // The lock to be used for this identifier
1471
      Lock lock = null;
1472
      
1473
      // get the subject
1474
      Subject subject = session.getSubject();
1475
      
1476
      // are we allowed to do this?
1477
      try {
1478

    
1479
          // what is the controlling permission?
1480
          if (!isAuthorized(session, pid, Permission.WRITE)) {
1481
              throw new NotAuthorized("4851", "not allowed by "
1482
                      + subject.getValue() + " on " + pid.getValue());
1483
          }
1484

    
1485
        
1486
      } catch (InvalidToken e) {
1487
          throw new NotAuthorized("4851", "not allowed by " + subject.getValue() + 
1488
                  " on " + pid.getValue());  
1489
          
1490
      }
1491

    
1492
      SystemMetadata systemMetadata = null;
1493
      try {
1494
          lock = HazelcastService.getInstance().getLock(pid.getValue());
1495
          lock.lock();
1496
          logMetacat.debug("Locked identifier " + pid.getValue());
1497

    
1498
          try {      
1499
              systemMetadata = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1500

    
1501
              // does the request have the most current system metadata?
1502
              if ( systemMetadata.getSerialVersion().longValue() != serialVersion ) {
1503
                 String msg = "The requested system metadata version number " + 
1504
                     serialVersion + " differs from the current version at " +
1505
                     systemMetadata.getSerialVersion().longValue() +
1506
                     ". Please get the latest copy in order to modify it.";
1507
                 throw new VersionMismatch("4855", msg);
1508
              }
1509
              
1510
          } catch (RuntimeException e) { // Catch is generic since HZ throws RuntimeException
1511
              throw new NotFound("4854", "No record found for: " + pid.getValue() +
1512
                  " : " + e.getMessage());
1513
            
1514
          }
1515
              
1516
          // set the status for the replica
1517
          List<Replica> replicas = systemMetadata.getReplicaList();
1518
          NodeReference replicaNode = replica.getReplicaMemberNode();
1519
          ReplicationStatus replicaStatus = replica.getReplicationStatus();
1520
          int index = 0;
1521
          for (Replica listedReplica: replicas) {
1522
              
1523
              // remove the replica that we are replacing
1524
              if ( replicaNode.getValue().equals(listedReplica.getReplicaMemberNode().getValue())) {
1525
                      // don't allow status to change from COMPLETED to anything other
1526
                      // than INVALIDATED: prevents overwrites from race conditions
1527
                	  if ( !listedReplica.getReplicationStatus().equals(replicaStatus) && 
1528
                	       listedReplica.getReplicationStatus().equals(ReplicationStatus.COMPLETED) &&
1529
            		       !replicaStatus.equals(ReplicationStatus.INVALIDATED) ) {
1530
                	  throw new InvalidRequest("4853", "Status state change from " +
1531
                			  listedReplica.getReplicationStatus() + " to " +
1532
                			  replicaStatus.toString() + "is prohibited for identifier " +
1533
                			  pid.getValue() + " and target node " + 
1534
                			  listedReplica.getReplicaMemberNode().getValue());
1535

    
1536
            	  }
1537
                  replicas.remove(index);
1538
                  break;
1539
                  
1540
              }
1541
              index++;
1542
          }
1543
          
1544
          // add the new replica item
1545
          replicas.add(replica);
1546
          systemMetadata.setReplicaList(replicas);
1547
          
1548
          // update the metadata
1549
          try {
1550
              systemMetadata.setSerialVersion(systemMetadata.getSerialVersion().add(BigInteger.ONE));
1551
              systemMetadata.setDateSysMetadataModified(Calendar.getInstance().getTime());
1552
              HazelcastService.getInstance().getSystemMetadataMap().put(systemMetadata.getIdentifier(), systemMetadata);
1553
              
1554
              // inform replica nodes of the change if the status is complete
1555
              if ( replicaStatus.equals(ReplicationStatus.COMPLETED) ) {
1556
            	  notifyReplicaNodes(systemMetadata);
1557
            	  
1558
              }
1559
          } catch (RuntimeException e) {
1560
              logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1561
              throw new ServiceFailure("4852", e.getMessage());
1562
          
1563
          }
1564
          
1565
      } catch (RuntimeException e) {
1566
          logMetacat.info("Unknown RuntimeException thrown: " + e.getCause().getMessage());
1567
          throw new ServiceFailure("4852", e.getMessage());
1568
      
1569
      } finally {
1570
          lock.unlock();
1571
          logMetacat.debug("Unlocked identifier " + pid.getValue());
1572
          
1573
      }
1574
    
1575
      return true;
1576
      
1577
  }
1578
  
1579
  /**
1580
   * 
1581
   */
1582
  @Override
1583
  public ObjectList listObjects(Session session, Date startTime, 
1584
      Date endTime, ObjectFormatIdentifier formatid, Boolean replicaStatus,
1585
      Integer start, Integer count)
1586
      throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1587
      ServiceFailure {
1588
      
1589
      ObjectList objectList = null;
1590
        try {
1591
        	if (count == null || count > MAXIMUM_DB_RECORD_COUNT) {
1592
            	count = MAXIMUM_DB_RECORD_COUNT;
1593
            }
1594
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, formatid, replicaStatus, start, count);
1595
        } catch (Exception e) {
1596
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
1597
        }
1598

    
1599
        return objectList;
1600
  }
1601

    
1602
  
1603
 	/**
1604
 	 * Returns a list of checksum algorithms that are supported by DataONE.
1605
 	 * @return cal  the list of checksum algorithms
1606
 	 * 
1607
 	 * @throws ServiceFailure
1608
 	 * @throws NotImplemented
1609
 	 */
1610
  @Override
1611
  public ChecksumAlgorithmList listChecksumAlgorithms()
1612
			throws ServiceFailure, NotImplemented {
1613
		ChecksumAlgorithmList cal = new ChecksumAlgorithmList();
1614
		cal.addAlgorithm("MD5");
1615
		cal.addAlgorithm("SHA-1");
1616
		return cal;
1617
		
1618
	}
1619

    
1620
  /**
1621
   * Notify replica Member Nodes of system metadata changes for a given pid
1622
   * 
1623
   * @param currentSystemMetadata - the up to date system metadata
1624
   */
1625
  public void notifyReplicaNodes(SystemMetadata currentSystemMetadata) {
1626
      
1627
      Session session = null;
1628
      List<Replica> replicaList = currentSystemMetadata.getReplicaList();
1629
      MNode mn = null;
1630
      NodeReference replicaNodeRef = null;
1631
      CNode cn = null;
1632
      NodeType nodeType = null;
1633
      List<Node> nodeList = null;
1634
      
1635
      try {
1636
          cn = D1Client.getCN();
1637
          nodeList = cn.listNodes().getNodeList();
1638
          
1639
      } catch (Exception e) { // handle BaseException and other I/O issues
1640
          
1641
          // swallow errors since the call is not critical
1642
          logMetacat.error("Can't inform MNs of system metadata changes due " +
1643
              "to communication issues with the CN: " + e.getMessage());
1644
          
1645
      }
1646
      
1647
      if ( replicaList != null ) {
1648
          
1649
          // iterate through the replicas and inform  MN replica nodes
1650
          for (Replica replica : replicaList) {
1651
              
1652
              replicaNodeRef = replica.getReplicaMemberNode();
1653
              try {
1654
                  if (nodeList != null) {
1655
                      // find the node type
1656
                      for (Node node : nodeList) {
1657
                          if (node.getIdentifier().getValue() == 
1658
                              replicaNodeRef.getValue()) {
1659
                              nodeType = node.getType();
1660
                              break;
1661
              
1662
                          }
1663
                      }
1664
                  }
1665
              
1666
                  // notify only MNs
1667
                  if (nodeType != null && nodeType == NodeType.MN) {
1668
                      mn = D1Client.getMN(replicaNodeRef);
1669
                      mn.systemMetadataChanged(session, 
1670
                          currentSystemMetadata.getIdentifier(), 
1671
                          currentSystemMetadata.getSerialVersion().longValue(),
1672
                          currentSystemMetadata.getDateSysMetadataModified());
1673
                  }
1674
              
1675
              } catch (Exception e) { // handle BaseException and other I/O issues
1676
              
1677
                  // swallow errors since the call is not critical
1678
                  logMetacat.error("Can't inform "
1679
                          + replicaNodeRef.getValue()
1680
                          + " of system metadata changes due "
1681
                          + "to communication issues with the CN: "
1682
                          + e.getMessage());
1683
              
1684
              }
1685
          }
1686
      }
1687
  }
1688

    
1689
	@Override
1690
	public boolean isAuthorized(Identifier pid, Permission permission)
1691
			throws ServiceFailure, InvalidToken, NotFound, NotAuthorized,
1692
			NotImplemented, InvalidRequest {
1693
		
1694
		return isAuthorized(null, pid, permission);
1695
	}
1696
	
1697
	@Override
1698
	public boolean setAccessPolicy(Identifier pid, AccessPolicy accessPolicy, long serialVersion)
1699
			throws InvalidToken, NotFound, NotImplemented, NotAuthorized,
1700
			ServiceFailure, InvalidRequest, VersionMismatch {
1701
		
1702
		return setAccessPolicy(null, pid, accessPolicy, serialVersion);
1703
	}
1704
	
1705
	@Override
1706
	public Identifier setRightsHolder(Identifier pid, Subject userId, long serialVersion)
1707
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1708
			NotImplemented, InvalidRequest, VersionMismatch {
1709
		
1710
		return setRightsHolder(null, pid, userId, serialVersion);
1711
	}
1712
	
1713
	@Override
1714
	public Identifier create(Identifier pid, InputStream object, SystemMetadata sysmeta)
1715
			throws InvalidToken, ServiceFailure, NotAuthorized,
1716
			IdentifierNotUnique, UnsupportedType, InsufficientResources,
1717
			InvalidSystemMetadata, NotImplemented, InvalidRequest {
1718

    
1719
		return create(null, pid, object, sysmeta);
1720
	}
1721
	
1722
	@Override
1723
	public Identifier delete(Identifier pid) throws InvalidToken, ServiceFailure,
1724
			NotAuthorized, NotFound, NotImplemented {
1725

    
1726
		return delete(null, pid);
1727
	}
1728
	
1729
	@Override
1730
	public Identifier generateIdentifier(String scheme, String fragment)
1731
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1732
			InvalidRequest {
1733

    
1734
		return generateIdentifier(null, scheme, fragment);
1735
	}
1736
	
1737
	@Override
1738
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1739
			Integer start, Integer count) throws InvalidToken, InvalidRequest,
1740
			ServiceFailure, NotAuthorized, NotImplemented, InsufficientResources {
1741

    
1742
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1743
	}
1744
	
1745
	@Override
1746
	public boolean hasReservation(Subject subject, Identifier pid)
1747
			throws InvalidToken, ServiceFailure, NotFound, NotAuthorized,
1748
			NotImplemented, InvalidRequest, IdentifierNotUnique {
1749

    
1750
		return hasReservation(null, subject, pid);
1751
	}
1752
	
1753
	@Override
1754
	public Identifier registerSystemMetadata(Identifier pid, SystemMetadata sysmeta)
1755
			throws NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest,
1756
			InvalidSystemMetadata, InvalidToken {
1757

    
1758
		return registerSystemMetadata(null, pid, sysmeta);
1759
	}
1760
	
1761
	@Override
1762
	public Identifier reserveIdentifier(Identifier pid) throws InvalidToken,
1763
			ServiceFailure, NotAuthorized, IdentifierNotUnique, NotImplemented,
1764
			InvalidRequest {
1765

    
1766
		return reserveIdentifier(null, pid);
1767
	}
1768
	
1769
	@Override
1770
	public boolean setObsoletedBy(Identifier pid, Identifier obsoletedByPid, long serialVersion)
1771
			throws NotImplemented, NotFound, NotAuthorized, ServiceFailure,
1772
			InvalidRequest, InvalidToken, VersionMismatch {
1773

    
1774
		return setObsoletedBy(null, pid, obsoletedByPid, serialVersion);
1775
	}
1776
	
1777
	@Override
1778
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1779
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1780

    
1781
		return describe(null, pid);
1782
	}
1783
	
1784
	@Override
1785
	public InputStream get(Identifier pid) throws InvalidToken, ServiceFailure,
1786
			NotAuthorized, NotFound, NotImplemented {
1787

    
1788
		return get(null, pid);
1789
	}
1790
	
1791
	@Override
1792
	public Checksum getChecksum(Identifier pid) throws InvalidToken,
1793
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1794

    
1795
		return getChecksum(null, pid);
1796
	}
1797
	
1798
	@Override
1799
	public SystemMetadata getSystemMetadata(Identifier pid) throws InvalidToken,
1800
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1801

    
1802
		return getSystemMetadata(null, pid);
1803
	}
1804
	
1805
	@Override
1806
	public ObjectList listObjects(Date startTime, Date endTime,
1807
			ObjectFormatIdentifier formatid, Boolean replicaStatus, Integer start, Integer count)
1808
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1809
			ServiceFailure {
1810

    
1811
		return listObjects(null, startTime, endTime, formatid, replicaStatus, start, count);
1812
	}
1813
	
1814
	@Override
1815
	public ObjectLocationList resolve(Identifier pid) throws InvalidToken,
1816
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1817

    
1818
		return resolve(null, pid);
1819
	}
1820
	
1821
	@Override
1822
	public ObjectList search(String queryType, String query) throws InvalidToken,
1823
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented {
1824

    
1825
		return search(null, queryType, query);
1826
	}
1827
	
1828
	@Override
1829
	public boolean deleteReplicationMetadata(Identifier pid, NodeReference nodeId,
1830
			long serialVersion) throws InvalidToken, InvalidRequest, ServiceFailure,
1831
			NotAuthorized, NotFound, NotImplemented, VersionMismatch {
1832

    
1833
		return deleteReplicationMetadata(null, pid, nodeId, serialVersion);
1834
	}
1835
	
1836
	@Override
1837
	public boolean isNodeAuthorized(Subject targetNodeSubject, Identifier pid)
1838
			throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure,
1839
			NotFound, InvalidRequest {
1840

    
1841
		return isNodeAuthorized(null, targetNodeSubject, pid);
1842
	}
1843
	
1844
	@Override
1845
	public boolean setReplicationPolicy(Identifier pid, ReplicationPolicy policy,
1846
			long serialVersion) throws NotImplemented, NotFound, NotAuthorized,
1847
			ServiceFailure, InvalidRequest, InvalidToken, VersionMismatch {
1848

    
1849
		return setReplicationPolicy(null, pid, policy, serialVersion);
1850
	}
1851
	
1852
	@Override
1853
	public boolean setReplicationStatus(Identifier pid, NodeReference targetNode,
1854
			ReplicationStatus status, BaseException failure) throws ServiceFailure,
1855
			NotImplemented, InvalidToken, NotAuthorized, InvalidRequest, NotFound {
1856

    
1857
		return setReplicationStatus(null, pid, targetNode, status, failure);
1858
	}
1859
	
1860
	@Override
1861
	public boolean updateReplicationMetadata(Identifier pid, Replica replica,
1862
			long serialVersion) throws NotImplemented, NotAuthorized, ServiceFailure,
1863
			NotFound, InvalidRequest, InvalidToken, VersionMismatch {
1864

    
1865
		return updateReplicationMetadata(null, pid, replica, serialVersion);
1866
	}
1867

    
1868
  @Override
1869
  public QueryEngineDescription getQueryEngineDescription(String arg0)
1870
          throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1871
          NotFound {
1872
      throw new NotImplemented("4410", "getQueryEngineDescription not implemented");
1873
      
1874
  }
1875

    
1876
  @Override
1877
  public QueryEngineList listQueryEngines() throws InvalidToken, ServiceFailure,
1878
          NotAuthorized, NotImplemented {
1879
      throw new NotImplemented("4420", "listQueryEngines not implemented");
1880
      
1881
  }
1882

    
1883
  @Override
1884
  public InputStream query(String arg0, String arg1) throws InvalidToken,
1885
          ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, NotFound {
1886
      throw new NotImplemented("4324", "query not implemented");
1887
      
1888
  }
1889
}
(1-1/6)