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.IOException;
27
import java.io.InputStream;
28
import java.security.NoSuchAlgorithmException;
29
import java.sql.SQLException;
30
import java.text.SimpleDateFormat;
31
import java.util.Date;
32
import java.util.List;
33

    
34
import org.apache.commons.io.IOUtils;
35
import org.apache.log4j.Logger;
36
import org.dataone.client.D1Client;
37
import org.dataone.client.MNode;
38
import org.dataone.service.Constants;
39
import org.dataone.service.exceptions.IdentifierNotUnique;
40
import org.dataone.service.exceptions.InsufficientResources;
41
import org.dataone.service.exceptions.InvalidRequest;
42
import org.dataone.service.exceptions.InvalidSystemMetadata;
43
import org.dataone.service.exceptions.InvalidToken;
44
import org.dataone.service.exceptions.NotAuthorized;
45
import org.dataone.service.exceptions.NotFound;
46
import org.dataone.service.exceptions.NotImplemented;
47
import org.dataone.service.exceptions.ServiceFailure;
48
import org.dataone.service.exceptions.SynchronizationFailed;
49
import org.dataone.service.exceptions.UnsupportedType;
50
import org.dataone.service.mn.tier1.v1.MNCore;
51
import org.dataone.service.mn.tier1.v1.MNRead;
52
import org.dataone.service.mn.tier2.v1.MNAuthorization;
53
import org.dataone.service.mn.tier3.v1.MNStorage;
54
import org.dataone.service.mn.tier4.v1.MNReplication;
55
import org.dataone.service.types.v1.Checksum;
56
import org.dataone.service.types.v1.ChecksumAlgorithm;
57
import org.dataone.service.types.v1.DescribeResponse;
58
import org.dataone.service.types.v1.Event;
59
import org.dataone.service.types.v1.Group;
60
import org.dataone.service.types.v1.Identifier;
61
import org.dataone.service.types.v1.Log;
62
import org.dataone.service.types.v1.LogEntry;
63
import org.dataone.service.types.v1.MonitorInfo;
64
import org.dataone.service.types.v1.MonitorList;
65
import org.dataone.service.types.v1.Node;
66
import org.dataone.service.types.v1.NodeHealth;
67
import org.dataone.service.types.v1.NodeReference;
68
import org.dataone.service.types.v1.NodeState;
69
import org.dataone.service.types.v1.NodeType;
70
import org.dataone.service.types.v1.ObjectFormat;
71
import org.dataone.service.types.v1.ObjectFormatIdentifier;
72
import org.dataone.service.types.v1.ObjectList;
73
import org.dataone.service.types.v1.Permission;
74
import org.dataone.service.types.v1.Ping;
75
import org.dataone.service.types.v1.Schedule;
76
import org.dataone.service.types.v1.Service;
77
import org.dataone.service.types.v1.Services;
78
import org.dataone.service.types.v1.Session;
79
import org.dataone.service.types.v1.Status;
80
import org.dataone.service.types.v1.Subject;
81
import org.dataone.service.types.v1.Synchronization;
82
import org.dataone.service.types.v1.SystemMetadata;
83
import org.dataone.service.types.v1.util.ChecksumUtil;
84

    
85
import edu.ucsb.nceas.metacat.DocumentImpl;
86
import edu.ucsb.nceas.metacat.EventLog;
87
import edu.ucsb.nceas.metacat.IdentifierManager;
88
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
89
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
90
import edu.ucsb.nceas.metacat.database.DBConnection;
91
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
92
import edu.ucsb.nceas.metacat.properties.PropertyService;
93
import edu.ucsb.nceas.metacat.util.SystemUtil;
94
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
95

    
96
/**
97
 * Represents Metacat's implementation of the DataONE Member Node 
98
 * service API. Methods implement the various MN* interfaces, and methods common
99
 * to both Member Node and Coordinating Node interfaces are found in the
100
 * D1NodeService base class.
101
 * 
102
 * Implements:
103
 * MNCore.ping()
104
 * MNCore.getLogRecords()
105
 * MNCore.getObjectStatistics()
106
 * MNCore.getOperationStatistics()
107
 * MNCore.getStatus()
108
 * MNCore.getCapabilities()
109
 * MNRead.get()
110
 * MNRead.getSystemMetadata()
111
 * MNRead.describe()
112
 * MNRead.getChecksum()
113
 * MNRead.listObjects()
114
 * MNRead.synchronizationFailed()
115
 * MNAuthorization.isAuthorized()
116
 * MNAuthorization.setAccessPolicy()
117
 * MNStorage.create()
118
 * MNStorage.update()
119
 * MNStorage.delete()
120
 * MNReplication.replicate()
121
 * 
122
 */
123
public class MNodeService extends D1NodeService implements MNAuthorization,
124
  MNCore, MNRead, MNReplication, MNStorage {
125

    
126
  /* the instance of the MNodeService object */
127
  private static MNodeService instance = null;
128
  
129
  /* the logger instance */
130
  private Logger logMetacat = null;
131

    
132
  /**
133
   * Singleton accessor to get an instance of MNodeService.
134
   * 
135
   * @return instance - the instance of MNodeService
136
   */
137
  public static MNodeService getInstance() {
138
    if (instance == null) {
139

    
140
      instance = new MNodeService();
141
      
142
    }
143
    
144
    return instance;
145
  }
146
  
147
  /**
148
   * Constructor, private for singleton access
149
   */
150
  private MNodeService() {
151
    super();
152
    logMetacat = Logger.getLogger(MNodeService.class);
153
        
154
  }
155
    
156
  /**
157
   * Deletes an object from the Member Node, where the object is either a 
158
   * data object or a science metadata object.
159
   * 
160
   * @param session - the Session object containing the credentials for the Subject
161
   * @param pid - The object identifier to be deleted
162
   * 
163
   * @return pid - the identifier of the object used for the deletion
164
   * 
165
   * @throws InvalidToken
166
   * @throws ServiceFailure
167
   * @throws NotAuthorized
168
   * @throws NotFound
169
   * @throws NotImplemented
170
   * @throws InvalidRequest
171
   */
172
  @Override
173
  public Identifier delete(Session session, Identifier pid) 
174
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
175
    NotImplemented, InvalidRequest {
176

    
177
    String localId = null;
178
    boolean allowed = false;
179
    String username = Constants.PUBLIC_SUBJECT;
180
    String[] groupnames = null;
181
    if (session != null ) {
182
    	username = session.getSubject().getValue();
183
    	if (session.getSubjectList() != null) {
184
    		List<Group> groupList = session.getSubjectList().getGroupList();
185
    		if (groupList != null) {
186
    			groupnames = new String[groupList.size()];
187
    			for (int i = 0; i > groupList.size(); i++ ) {
188
    				groupnames[i] = groupList.get(i).getGroupName();
189
    			}
190
    		}
191
    	}
192
    }
193
    
194
    // do we have a valid pid?
195
    if ( pid == null || pid.getValue().trim().equals("") ) {
196
      throw new InvalidRequest("1322", "The provided identifier was invalid.");
197
    }
198

    
199
    // check for the existing identifier
200
    try {
201
      localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
202
    
203
    } catch (McdbDocNotFoundException e) {
204
      throw new InvalidRequest("1322", "The object with the provided " +
205
        "identifier was not found.");
206

    
207
    }
208
    
209
    // does the subject have DELETE (a D1 CHANGE_PERMISSION level) priveleges on the pid?
210
    allowed = isAuthorized(session, pid, Permission.CHANGE_PERMISSION);
211
    
212
    if ( allowed ) {
213
      try {
214
        // delete the document
215
        DocumentImpl.delete(localId, username, groupnames, null);
216
        EventLog.getInstance().log(metacatUrl, username, localId, Event.DELETE.toString());
217

    
218
      } catch (McdbDocNotFoundException e) {
219
        throw new InvalidRequest("1322", "The provided identifier was invalid.");
220

    
221
      } catch (SQLException e) {
222
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
223
          "The error message was: " + e.getMessage());
224

    
225
      } catch (InsufficientKarmaException e) {
226
        throw new NotAuthorized("1320", "The provided identity does not have " +
227
        "permission to DELETE objects on the Member Node.");
228
 
229
      } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
230
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
231
            "The error message was: " + e.getMessage());
232

    
233
      }
234

    
235
    } else {
236
      throw new NotAuthorized("1320", "The provided identity does not have " +
237
      "permission to DELETE objects on the Member Node.");
238
      
239
    }
240
    
241
    return pid;
242
  }
243

    
244

    
245
  /**
246
   * Updates an existing object by creating a new object identified by 
247
   * newPid on the Member Node which explicitly obsoletes the object 
248
   * identified by pid through appropriate changes to the SystemMetadata 
249
   * of pid and newPid
250
   * 
251
   * @param session - the Session object containing the credentials for the Subject
252
   * @param pid - The identifier of the object to be updated
253
   * @param object - the new object bytes
254
   * @param sysmeta - the new system metadata describing the object
255
   * 
256
   * @return newPid - the identifier of the new object
257
   * 
258
   * @throws InvalidToken
259
   * @throws ServiceFailure
260
   * @throws NotAuthorized
261
   * @throws NotFound
262
   * @throws NotImplemented
263
   * @throws IdentifierNotUnique
264
   * @throws UnsupportedType
265
   * @throws InsufficientResources
266
   * @throws InvalidSystemMetadata
267
   * @throws InvalidRequest
268
   */
269
  @Override
270
  public Identifier update(Session session, Identifier pid, InputStream object,
271
    Identifier newPid, SystemMetadata sysmeta) 
272
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
273
    UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
274
    NotImplemented, InvalidRequest {
275
	  
276
	// check if the pid has been reserved
277
	try {
278
		boolean hasReservation = D1Client.getCN().hasReservation(session, pid);
279
		if (!hasReservation) {
280
			throw new NotAuthorized("", "No reservation for pid: " + pid.getValue());
281
		}
282
	} catch (NotFound e) {
283
		// okay to continue
284
	}
285

    
286
    String localId = null;
287
    boolean allowed = false;
288
    boolean isScienceMetadata = false;
289
    Subject subject = session.getSubject();
290
    
291
    // do we have a valid pid?
292
    if ( pid == null || pid.getValue().trim().equals("") ) {
293
      throw new InvalidRequest("1202", "The provided identifier was invalid.");
294

    
295
    }
296

    
297
    // check for the existing identifier
298
    try {
299
      localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
300

    
301
    } catch (McdbDocNotFoundException e) {
302
      throw new InvalidRequest("1202", "The object with the provided " +
303
        "identifier was not found.");
304

    
305
    }
306

    
307
    // does the subject have WRITE ( == update) priveleges on the pid?
308
    allowed = isAuthorized(session, pid, Permission.WRITE);
309

    
310
    if ( allowed ) {
311
      
312
      // get the existing system metadata for the object
313
      SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
314
      
315
      // add the newPid to the obsoletedBy list for the existing sysmeta
316
      existingSysMeta.setObsoletedBy(newPid);
317
      
318
      // then update the existing system metadata
319
      updateSystemMetadata(existingSysMeta);
320
            
321
      // prep the new system metadata, add pid to the affected lists
322
      sysmeta.setObsoletes(pid);
323
      //sysmeta.addDerivedFrom(pid);
324

    
325
      // and insert the new system metadata
326
      insertSystemMetadata(sysmeta);
327
      
328
      isScienceMetadata = isScienceMetadata(sysmeta);
329
      
330
      // do we have XML metadata or a data object?
331
      if ( isScienceMetadata ) {
332
        
333
        // update the science metadata XML document
334
        // TODO: handle non-XML metadata/data documents (like netCDF)
335
        // TODO: don't put objects into memory using stream to string
336
        String objectAsXML = "";
337
        try {
338
          objectAsXML = IOUtils.toString(object, "UTF-8");
339
          localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
340
          // register the newPid and the generated localId
341
          if ( newPid != null ) {
342
        	  IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
343
            
344
          }
345
          
346
        } catch (IOException e) {
347
          String msg = "The Node is unable to create the object. " +
348
          "There was a problem converting the object to XML";
349
          logMetacat.info(msg);
350
          throw new ServiceFailure("1310", msg + ": " + e.getMessage());
351
        
352
        }
353
        
354
      } else {
355
        
356
        // update the data object
357
        localId = insertDataObject(object, newPid, session);
358
       
359
      }
360
      // log the update event
361
      EventLog.getInstance().log(metacatUrl, subject.getValue(), localId, "update");
362

    
363
    } else {
364
      throw new NotAuthorized("1200", "The provided identity does not have " +
365
      "permission to UPDATE the object identified by " +
366
      pid.getValue() + " on the Member Node.");
367
      
368
    }
369
    
370
    return newPid;
371
  }
372
  
373
  public Identifier create(Session session, Identifier pid,
374
			InputStream object, SystemMetadata sysmeta) throws InvalidToken,
375
			ServiceFailure, NotAuthorized, IdentifierNotUnique,
376
			UnsupportedType, InsufficientResources, InvalidSystemMetadata,
377
			NotImplemented, InvalidRequest {
378

    
379
		// check if the pid has been reserved
380
		try {
381
			boolean hasReservation = D1Client.getCN().hasReservation(session, pid);
382
			if (!hasReservation) {
383
				throw new NotAuthorized("", "No reservation for pid: " + pid.getValue());
384
			}
385
		} catch (NotFound e) {
386
			// okay to continue
387
		}
388

    
389
		return super.create(session, pid, object, sysmeta);
390
	}
391

    
392
  /**
393
   * Called by a Coordinating Node to request that the Member Node create a 
394
   * copy of the specified object by retrieving it from another Member 
395
   * Node and storing it locally so that it can be made accessible to 
396
   * the DataONE system.
397
   * 
398
   * @param session - the Session object containing the credentials for the Subject
399
   * @param sysmeta - Copy of the CN held system metadata for the object
400
   * @param sourceNode - A reference to node from which the content should be 
401
   *                     retrieved. The reference should be resolved by 
402
   *                     checking the CN node registry.
403
   * 
404
   * @return true if the replication succeeds
405
   * 
406
   * @throws ServiceFailure
407
   * @throws NotAuthorized
408
   * @throws NotImplemented
409
   * @throws UnsupportedType
410
   * @throws InsufficientResources
411
   * @throws InvalidRequest
412
   */
413
  @Override
414
  public boolean replicate(Session session, SystemMetadata sysmeta, 
415
    NodeReference sourceNode)
416
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
417
    InsufficientResources, UnsupportedType {
418

    
419
    boolean result = false;
420

    
421
    // TODO: check credentials
422
    
423
    // get the referenced object
424
    Identifier pid = sysmeta.getIdentifier();
425
    
426
    // get from the membernode
427
    // TODO: switch credentials for the server retrieval?
428
    MNode mn = D1Client.getMN(sourceNode);
429
    InputStream object = null;
430

    
431
	try {
432
		object = mn.get(session, pid);
433
	} catch (InvalidToken e) {
434
		e.printStackTrace();
435
		throw new ServiceFailure("2151", "Could not retrieve object to replicate (InvalidToken): " + e.getMessage());
436
	} catch (NotFound e) {
437
		e.printStackTrace();
438
		throw new ServiceFailure("2151", "Could not retrieve object to replicate (NotFound): " + e.getMessage());
439
	}
440
	
441
    // add it to local store
442
	Identifier retPid;
443
	try {
444
		retPid = create(session, pid, object, sysmeta);
445
		result = (retPid.getValue().equals(pid.getValue()));
446
	} catch (InvalidToken e) {
447
		e.printStackTrace();
448
		throw new ServiceFailure("2151", "Could not save object to local store (InvalidToken): " + e.getMessage());
449
	} catch (IdentifierNotUnique e) {
450
		e.printStackTrace();
451
		throw new ServiceFailure("2151", "Could not save object to local store (IdentifierNotUnique): " + e.getMessage());
452
	} catch (InvalidSystemMetadata e) {
453
		e.printStackTrace();
454
		throw new ServiceFailure("2151", "Could not save object to local store (InvalidSystemMetadata): " + e.getMessage());
455
	}
456
	        
457
    return result;
458
	  
459
  }
460

    
461
  /**
462
   * This method provides a lighter weight mechanism than 
463
   * MN_read.getSystemMetadata() for a client to determine basic 
464
   * properties of the referenced object.
465
   * 
466
   * @param session - the Session object containing the credentials for the Subject
467
   * @param pid - the identifier of the object to be described
468
   * 
469
   * @return describeResponse - A set of values providing a basic description 
470
   *                            of the object.
471
   * 
472
   * @throws InvalidToken
473
   * @throws ServiceFailure
474
   * @throws NotAuthorized
475
   * @throws NotFound
476
   * @throws NotImplemented
477
   * @throws InvalidRequest
478
   */
479
  @Override
480
  public DescribeResponse describe(Session session, Identifier pid)
481
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
482
    NotImplemented, InvalidRequest {
483
    
484
    if(session == null) {
485
      throw new InvalidToken("1370", "The session object is null");
486
      
487
    }
488
    
489
    if(pid == null || pid.getValue().trim().equals(""))
490
    {
491
      throw new InvalidRequest("1362", "The object identifier is null. " +
492
        "A valid identifier is required.");
493
        
494
    }
495
    
496
    SystemMetadata sysmeta = getSystemMetadata(session, pid);
497
    DescribeResponse describeResponse = 
498
      new DescribeResponse(sysmeta.getObjectFormat(), 
499
      sysmeta.getSize(), sysmeta.getDateSysMetadataModified(), sysmeta.getChecksum());
500
    
501
    return describeResponse;
502

    
503
  }
504

    
505
  /**
506
   * Return the object identified by the given object identifier
507
   * 
508
   * @param session - the Session object containing the credentials for the Subject
509
   * @param pid - the object identifier for the given object
510
   * 
511
   * @return inputStream - the input stream of the given object
512
   * 
513
   * @throws InvalidToken
514
   * @throws ServiceFailure
515
   * @throws NotAuthorized
516
   * @throws InvalidRequest
517
   * @throws NotImplemented
518
   */
519
  @Override
520
  public InputStream get(Session session, Identifier pid) 
521
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
522
    NotImplemented, InvalidRequest {
523
    
524
    return super.get(session, pid);
525
    
526
  }
527

    
528
  /**
529
   * Returns a Checksum for the specified object using an accepted hashing algorithm
530
   * 
531
   * @param session - the Session object containing the credentials for the Subject
532
   * @param pid - the object identifier for the given object
533
   * @param algorithm -  the name of an algorithm that will be used to compute 
534
   *                     a checksum of the bytes of the object
535
   * 
536
   * @return checksum - the checksum of the given object
537
   * 
538
   * @throws InvalidToken
539
   * @throws ServiceFailure
540
   * @throws NotAuthorized
541
   * @throws NotFound
542
   * @throws InvalidRequest
543
   * @throws NotImplemented
544
   */
545
  @Override
546
  public Checksum getChecksum(Session session, Identifier pid, String algorithm)
547
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
548
    InvalidRequest, NotImplemented {
549

    
550
    Checksum checksum = null;
551
    
552
    InputStream inputStream = get(session, pid);
553
    
554
    try {
555
      checksum = 
556
    	  ChecksumUtil.checksum(inputStream, ChecksumAlgorithm.convert(algorithm));
557
    
558
    } catch (NoSuchAlgorithmException e) {
559
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
560
        pid.getValue() +
561
        "could not be returned due to an internal error: " +
562
        e.getMessage());
563
      
564
    } catch (IOException e) {
565
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
566
        pid.getValue() +
567
        "could not be returned due to an internal error: " +
568
        e.getMessage());
569
      
570
    }
571
    
572
    if ( checksum == null ) {
573
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
574
        pid.getValue() +
575
        "could not be returned.");
576
      
577
    }
578
    
579
    return checksum;
580
  }
581

    
582
  /**
583
   * Return the system metadata for a given object
584
   * 
585
   * @param session - the Session object containing the credentials for the Subject
586
   * @param pid - the object identifier for the given object
587
   * 
588
   * @return inputStream - the input stream of the given system metadata object
589
   * 
590
   * @throws InvalidToken
591
   * @throws ServiceFailure
592
   * @throws NotAuthorized
593
   * @throws NotFound
594
   * @throws InvalidRequest
595
   * @throws NotImplemented
596
   */
597
  @Override
598
  public SystemMetadata getSystemMetadata(Session session, Identifier pid)
599
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
600
      InvalidRequest, NotImplemented {
601

    
602
    return super.getSystemMetadata(session, pid);
603
  }
604

    
605
  /**
606
   * Retrieve the list of objects present on the MN that match the calling parameters
607
   * 
608
   * @param session - the Session object containing the credentials for the Subject
609
   * @param startTime - Specifies the beginning of the time range from which 
610
   *                    to return object (>=)
611
   * @param endTime - Specifies the beginning of the time range from which 
612
   *                  to return object (>=)
613
   * @param objectFormat - Restrict results to the specified object format
614
   * @param replicaStatus - Indicates if replicated objects should be returned in the list
615
   * @param start - The zero-based index of the first value, relative to the 
616
   *                first record of the resultset that matches the parameters.
617
   * @param count - The maximum number of entries that should be returned in 
618
   *                the response. The Member Node may return less entries 
619
   *                than specified in this value.
620
   * 
621
   * @return objectList - the list of objects matching the criteria
622
   * 
623
   * @throws InvalidToken
624
   * @throws ServiceFailure
625
   * @throws NotAuthorized
626
   * @throws InvalidRequest
627
   * @throws NotImplemented
628
   */
629
  @Override
630
  public ObjectList listObjects(Session session, Date startTime, Date endTime,
631
    ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start, Integer count)
632
    throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure,
633
    InvalidToken {
634

    
635
    ObjectList objectList = null;
636
    
637
    try {
638
	    objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
639
	        objectFormatId, replicaStatus, start, count);
640
    } catch (Exception e) {
641
		throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
642
	}
643
    
644
    return objectList;
645
  }
646

    
647
  /**
648
   * Retrieve the list of objects present on the MN that match the calling parameters
649
   * 
650
   * @return node - the technical capabilities of the Member Node
651
   * 
652
   * @throws ServiceFailure
653
   * @throws NotAuthorized
654
   * @throws InvalidRequest
655
   * @throws NotImplemented
656
   */
657
  @Override
658
  public Node getCapabilities() throws NotImplemented, NotAuthorized,
659
      ServiceFailure, InvalidRequest {
660
    
661
  	String nodeName = null;
662
    String nodeId = null;
663
    String nodeUrl = null;
664
    String nodeDesc = null;
665
    String nodeType = null;
666
    String mnCoreServiceVersion = null;
667
    String mnReadServiceVersion = null;
668
    String mnAuthorizationServiceVersion = null;
669
    String mnStorageServiceVersion = null;
670
    String mnReplicationServiceVersion = null;
671

    
672
    boolean nodeSynchronize = false;
673
    boolean nodeReplicate = false;
674
    boolean mnCoreServiceAvailable = false;
675
    boolean mnReadServiceAvailable = false;
676
    boolean mnAuthorizationServiceAvailable = false;
677
    boolean mnStorageServiceAvailable = false;
678
    boolean mnReplicationServiceAvailable = false;
679

    
680
    try
681
    {
682
    	// get the properties of the node based on configuration information
683
      nodeId = PropertyService.getProperty("dataone.memberNodeId");
684
      nodeName = PropertyService.getProperty("dataone.nodeName");
685
      nodeUrl = SystemUtil.getContextURL() + "/d1/";
686
      nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
687
      nodeType = PropertyService.getProperty("dataone.nodeType");
688
      nodeSynchronize = 
689
      	new Boolean(PropertyService.getProperty(
690
      		"dataone.nodeSynchronize")).booleanValue();
691
      nodeReplicate = 
692
      	new Boolean(PropertyService.getProperty(
693
      		"dataone.nodeReplicate")).booleanValue();
694
      
695
      mnCoreServiceVersion = 
696
      	PropertyService.getProperty("dataone.mnCore.serviceVersion");
697
      mnReadServiceVersion = 
698
      	PropertyService.getProperty("dataone.mnRead.serviceVersion");
699
      mnAuthorizationServiceVersion = 
700
      	PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
701
      mnStorageServiceVersion = 
702
      	PropertyService.getProperty("dataone.mnStorage.serviceVersion");
703
      mnReplicationServiceVersion = 
704
      	PropertyService.getProperty("dataone.mnReplication.serviceVersion");
705
      
706
      mnCoreServiceAvailable = new Boolean(
707
      	PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
708
      mnReadServiceAvailable =  new Boolean(
709
      	PropertyService.getProperty(
710
      		"dataone.mnRead.serviceAvailable")).booleanValue();
711
      mnAuthorizationServiceAvailable =  new Boolean(
712
      	PropertyService.getProperty(
713
      		"dataone.mnAuthorization.serviceAvailable")).booleanValue();
714
      mnStorageServiceAvailable =  new Boolean(
715
      	PropertyService.getProperty(
716
      	  "dataone.mnStorage.serviceAvailable")).booleanValue();
717
      mnReplicationServiceAvailable =  new Boolean(
718
      	PropertyService.getProperty(
719
      	  "dataone.mnReplication.serviceAvailable")).booleanValue();
720

    
721
    } catch(PropertyNotFoundException pnfe) {
722
        logMetacat.error("MNodeService.getCapabilities(): " +
723
          "property not found: " + pnfe.getMessage());
724
        
725
    }
726

    
727
  	// Set the properties of the node based on configuration information and
728
    // calls to current status methods
729
	  Node node = new Node();
730
	  node.setBaseURL(metacatUrl + "/" + nodeType);
731
	  node.setDescription(nodeDesc);
732
	  
733
	  // set the node's health information
734
	  NodeHealth health = new NodeHealth();
735
	  NodeState state = NodeState.UP;
736
	  health.setState(state);
737
	  // set the ping response to the current value
738
	  Ping canPing = new Ping();
739
	  canPing.setSuccess(false);
740
	  try {
741
	    canPing.setSuccess(ping());
742
    } catch (InsufficientResources e) {
743
	    e.printStackTrace();
744
	    
745
    } catch (UnsupportedType e) {
746
	    e.printStackTrace();
747
	    
748
    }
749
	  health.setPing(canPing);
750
	  // TODO: getStatus() should be consulted here when it's implemented
751
	  Status nodeStatus = new Status();
752
	  nodeStatus.setSuccess(true); 
753
	  nodeStatus.setDateChecked(new Date());
754
	  health.setStatus(nodeStatus);
755
	  node.setHealth(health);
756
	  
757
	  NodeReference identifier = new NodeReference();
758
	  identifier.setValue(nodeId);
759
	  node.setIdentifier(identifier);
760
	  node.setName(nodeName + " -- WAR version WARVERSION");
761
	  node.setReplicate(new Boolean(nodeReplicate).booleanValue());
762
	  node.setSynchronize(new Boolean(nodeSynchronize).booleanValue());
763
	  
764
	  // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
765
	  Services services = new Services();
766

    
767
	  Service sMNCore = new Service();
768
	  sMNCore.setName("MNCore");
769
	  sMNCore.setVersion(mnCoreServiceVersion);
770
	  sMNCore.setAvailable(mnCoreServiceAvailable);
771
	  
772
	  Service sMNRead = new Service();
773
	  sMNRead.setName("MNRead");
774
	  sMNRead.setVersion(mnReadServiceVersion);
775
	  sMNRead.setAvailable(mnReadServiceAvailable);
776
	  
777
	  Service sMNAuthorization = new Service();
778
	  sMNAuthorization.setName("MNAuthorization");
779
	  sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
780
	  sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
781
	  
782
	  Service sMNStorage = new Service();
783
	  sMNStorage.setName("MNStorage");
784
	  sMNStorage.setVersion(mnStorageServiceVersion);
785
	  sMNStorage.setAvailable(mnStorageServiceAvailable);
786
	  
787
	  Service sMNReplication = new Service();
788
	  sMNReplication.setName("MNReplication");
789
	  sMNReplication.setVersion(mnReplicationServiceVersion);
790
	  sMNReplication.setAvailable(mnReplicationServiceAvailable);
791
	  
792
	  services.addService(sMNRead);
793
	  services.addService(sMNCore);
794
	  services.addService(sMNAuthorization);
795
	  services.addService(sMNStorage);
796
	  services.addService(sMNReplication);
797
	  node.setServices(services);
798
	  
799
	  // TODO: Determine the synchronization info without mock values
800
	  Synchronization synchronization = new Synchronization();
801
	  Schedule schedule = new Schedule();
802
	  Date now = new Date();
803
	  schedule.setYear(new SimpleDateFormat("yyyy").format(now));
804
	  schedule.setMon(new SimpleDateFormat("MM").format(now));
805
	  schedule.setMday(new SimpleDateFormat("dd").format(now));
806
	  schedule.setWday(new SimpleDateFormat("dd").format(now));
807
	  schedule.setHour(new SimpleDateFormat("HH").format(now));
808
	  schedule.setMin(new SimpleDateFormat("mm").format(now));
809
	  schedule.setSec(new SimpleDateFormat("ss").format(now));
810
	  synchronization.setSchedule(schedule);
811
	  synchronization.setLastHarvested(now);
812
	  synchronization.setLastCompleteHarvest(now);
813
	  node.setSynchronization(synchronization);
814
	  node.setSynchronize(false);
815
	  node.setType(NodeType.MN);
816
	  
817
	  return node;
818
  }
819

    
820
  /**
821
   * Returns the number of operations that have been serviced by the node 
822
   * over time periods of one and 24 hours.
823
   * 
824
   * @param session - the Session object containing the credentials for the Subject
825
   * @param period - An ISO8601 compatible DateTime range specifying the time 
826
   *                 range for which to return operation statistics.
827
   * @param requestor - Limit to operations performed by given requestor identity.
828
   * @param event -  Enumerated value indicating the type of event being examined
829
   * @param format - Limit to events involving objects of the specified format
830
   * 
831
   * @return the desired log records
832
   * 
833
   * @throws InvalidToken
834
   * @throws ServiceFailure
835
   * @throws NotAuthorized
836
   * @throws InvalidRequest
837
   * @throws NotImplemented
838
   */
839
  @Override
840
  public MonitorList getOperationStatistics(Session session, Date startTime,
841
  		Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId) 
842
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
843
    InsufficientResources, UnsupportedType {
844
    
845
	  MonitorList monitorList = new MonitorList();
846
	  
847
	  try {
848

    
849
		  // get log records first
850
		  Log logs = getLogRecords(session, startTime, endTime, event, 0, null);
851
		  
852
		  // TODO: aggregate by day or hour -- needs clarification
853
		  int count = 1;
854
		  for (LogEntry logEntry: logs.getLogEntryList()) {
855
			  Identifier pid = logEntry.getIdentifier();
856
			  Date logDate = logEntry.getDateLogged();
857
			  // if we are filtering by format
858
			  if (formatId != null) {
859
				  SystemMetadata sysmeta = IdentifierManager.getInstance().getSystemMetadata(pid.getValue());
860
				  if (!sysmeta.getObjectFormat().getFmtid().getValue().equals(formatId.getValue())) {
861
					  // does not match
862
					  continue;
863
				  }
864
			  }
865
			  MonitorInfo item = new MonitorInfo();
866
			  item.setCount(count);
867
			  item.setDate(new java.sql.Date(logDate.getTime()));
868
			  monitorList.addMonitorInfo(item);
869
			  
870
		  }
871
	} catch (Exception e) {
872
		e.printStackTrace();
873
		throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
874
	}
875
	  
876
	return monitorList;
877
	  
878
  }
879

    
880
  /**
881
   * Low level “are you alive” operation. A valid ping response is 
882
   * indicated by a HTTP status of 200.
883
   * 
884
   * @return true if the service is alive
885
   * 
886
   * @throws InvalidToken
887
   * @throws ServiceFailure
888
   * @throws NotAuthorized
889
   * @throws InvalidRequest
890
   * @throws NotImplemented
891
   */
892
  @Override
893
  public boolean ping() 
894
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
895
    InsufficientResources, UnsupportedType {
896

    
897
    // test if we can get a database connection
898
    boolean alive = false;
899
    int serialNumber = -1;
900
    DBConnection dbConn = null;
901
    try {
902
      dbConn = DBConnectionPool
903
      .getDBConnection("MNodeService.ping");
904
      serialNumber = dbConn.getCheckOutSerialNumber();
905
      alive = true;
906
      
907
    } catch (SQLException e) {
908
      return alive;
909
      
910
    } finally {
911
      // Return the database connection
912
      DBConnectionPool.returnDBConnection(dbConn, serialNumber);
913
    
914
    }
915

    
916
    return alive;
917
  }
918

    
919
  /**
920
   * A callback method used by a CN to indicate to a MN that it cannot 
921
   * complete synchronization of the science metadata identified by pid.  Log
922
   * the event in the metacat event log.
923
   * 
924
   * @param session
925
   * @param syncFailed
926
   * 
927
   * @throws ServiceFailure
928
   * @throws NotAuthorized
929
   * @throws InvalidRequest
930
   * @throws NotImplemented
931
   */
932
  @Override
933
  public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
934
      throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest {
935

    
936
    String localId;
937
    
938
    try {
939
      localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid());
940
    } catch (McdbDocNotFoundException e) {
941
      throw new ServiceFailure("2161", "The identifier specified by " +
942
          syncFailed.getPid() + 
943
          " was not found on this node.");
944
      
945
    }
946
    // TODO: update the CN URL below when the CNRead.SynchronizationFailed
947
    // method is changed to include the URL as a parameter
948
    logMetacat.debug("Synchronization for the object identified by " +
949
      syncFailed.getPid() + 
950
      " failed from " +
951
      "CN URL WILL GO HERE." +
952
      " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
953
    // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
954
    EventLog.getInstance().log("CN URL WILL GO HERE", 
955
      session.getSubject().getValue(), localId, "synchronization_failed");
956
    //EventLog.getInstance().log("CN URL WILL GO HERE", 
957
    //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
958

    
959
  }
960

    
961
}
(3-3/6)