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.util.ArrayList;
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.service.exceptions.IdentifierNotUnique;
37
import org.dataone.service.exceptions.InsufficientResources;
38
import org.dataone.service.exceptions.InvalidRequest;
39
import org.dataone.service.exceptions.InvalidSystemMetadata;
40
import org.dataone.service.exceptions.InvalidToken;
41
import org.dataone.service.exceptions.NotAuthorized;
42
import org.dataone.service.exceptions.NotFound;
43
import org.dataone.service.exceptions.NotImplemented;
44
import org.dataone.service.exceptions.ServiceFailure;
45
import org.dataone.service.exceptions.SynchronizationFailed;
46
import org.dataone.service.exceptions.UnsupportedType;
47
import org.dataone.service.mn.tier1.MNCore;
48
import org.dataone.service.mn.tier1.MNRead;
49
import org.dataone.service.mn.tier2.MNAuthorization;
50
import org.dataone.service.mn.tier3.MNStorage;
51
import org.dataone.service.mn.tier4.MNReplication;
52
import org.dataone.service.types.AccessPolicy;
53
import org.dataone.service.types.Checksum;
54
import org.dataone.service.types.ChecksumAlgorithm;
55
import org.dataone.service.types.DescribeResponse;
56
import org.dataone.service.types.Event;
57
import org.dataone.service.types.Group;
58
import org.dataone.service.types.Identifier;
59
import org.dataone.service.types.Log;
60
import org.dataone.service.types.MonitorList;
61
import org.dataone.service.types.Node;
62
import org.dataone.service.types.NodeReference;
63
import org.dataone.service.types.ObjectFormat;
64
import org.dataone.service.types.ObjectFormatIdentifier;
65
import org.dataone.service.types.ObjectList;
66
import org.dataone.service.types.Permission;
67
import org.dataone.service.types.Session;
68
import org.dataone.service.types.Subject;
69
import org.dataone.service.types.SystemMetadata;
70
import org.dataone.service.types.util.ServiceTypeUtil;
71

    
72
import edu.ucsb.nceas.metacat.DocumentImpl;
73
import edu.ucsb.nceas.metacat.EventLog;
74
import edu.ucsb.nceas.metacat.IdentifierManager;
75
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
76
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
77
import edu.ucsb.nceas.metacat.database.DBConnection;
78
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
79
import edu.ucsb.nceas.metacat.properties.PropertyService;
80
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
81

    
82
/**
83
 * Represents Metacat's implementation of the DataONE Member Node 
84
 * service API. Methods implement the various MN* interfaces, and methods common
85
 * to both Member Node and Coordinating Node interfaces are found in the
86
 * D1NodeService base class.
87
 * 
88
 * Implements:
89
 * MNCore.ping()
90
 * MNCore.getLogRecords()
91
 * MNCore.getObjectStatistics()
92
 * MNCore.getOperationStatistics()
93
 * MNCore.getStatus()
94
 * MNCore.getCapabilities()
95
 * MNRead.get()
96
 * MNRead.getSystemMetadata()
97
 * MNRead.describe()
98
 * MNRead.getChecksum()
99
 * MNRead.listObjects()
100
 * MNRead.synchronizationFailed()
101
 * MNAuthorization.isAuthorized()
102
 * MNAuthorization.setAccessPolicy()
103
 * MNStorage.create()
104
 * MNStorage.update()
105
 * MNStorage.delete()
106
 * MNReplication.replicate()
107
 * 
108
 */
109
public class MNodeService extends D1NodeService implements MNAuthorization,
110
  MNCore, MNRead, MNReplication, MNStorage {
111

    
112
  /* the instance of the MNodeService object */
113
  private static MNodeService instance = null;
114
  
115
  /* the logger instance */
116
  private Logger logMetacat = null;
117

    
118
  /**
119
   * Singleton accessor to get an instance of MNodeService.
120
   * 
121
   * @return instance - the instance of MNodeService
122
   */
123
  public static MNodeService getInstance() {
124
    if (instance == null) {
125

    
126
      instance = new MNodeService();
127
      
128
    }
129
    
130
    return instance;
131
  }
132
  
133
  /**
134
   * Constructor, private for singleton access
135
   */
136
  private MNodeService() {
137
    super();
138
    logMetacat = Logger.getLogger(MNodeService.class);
139
        
140
  }
141
    
142
  /**
143
   * Deletes an object from the Member Node, where the object is either a 
144
   * data object or a science metadata object.
145
   * 
146
   * @param session - the Session object containing the credentials for the Subject
147
   * @param pid - The object identifier to be deleted
148
   * 
149
   * @return pid - the identifier of the object used for the deletion
150
   * 
151
   * @throws InvalidToken
152
   * @throws ServiceFailure
153
   * @throws NotAuthorized
154
   * @throws NotFound
155
   * @throws NotImplemented
156
   * @throws InvalidRequest
157
   */
158
  @Override
159
  public Identifier delete(Session session, Identifier pid) 
160
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
161
    NotImplemented, InvalidRequest {
162

    
163
    String localId = null;
164
    boolean allowed = false;
165
    Subject subject = session.getSubject();
166
    List<Group> groupList = new ArrayList<Group>();
167
    String[] groups = new String[0];
168
    if ( session.getSubjectList() != null ) {
169
      groupList = session.getSubjectList().getGroupList();
170
      groups = new String[groupList.size()];
171
    	
172
    }
173
    IdentifierManager im = IdentifierManager.getInstance();
174
    
175
    // put the group names into a string array
176
    if( session != null ) {
177
      for ( int i = 0; i > groupList.size(); i++ ) {
178
        groups[i] = groupList.get(i).getGroupName();
179
        
180
      }
181
    }
182

    
183
    // be sure the user is authenticated for delete()
184
    if (subject.getValue() == null || 
185
        subject.getValue().toLowerCase().equals("public") ) {
186
      throw new NotAuthorized("1320", "The provided identity does not have " +
187
        "permission to DELETE objects on the Member Node.");
188
      
189
    }
190
    
191
    // do we have a valid pid?
192
    if ( pid == null || pid.getValue().trim().equals("") ) {
193
      throw new InvalidRequest("1322", "The provided identifier was invalid.");
194

    
195
    }
196

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

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

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

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

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

    
231
      }
232

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

    
242

    
243
  /**
244
   * Updates an existing object by creating a new object identified by 
245
   * newPid on the Member Node which explicitly obsoletes the object 
246
   * identified by pid through appropriate changes to the SystemMetadata 
247
   * of pid and newPid
248
   * 
249
   * @param session - the Session object containing the credentials for the Subject
250
   * @param pid - The identifier of the object to be updated
251
   * @param object - the new object bytes
252
   * @param sysmeta - the new system metadata describing the object
253
   * 
254
   * @return newPid - the identifier of the new object
255
   * 
256
   * @throws InvalidToken
257
   * @throws ServiceFailure
258
   * @throws NotAuthorized
259
   * @throws NotFound
260
   * @throws NotImplemented
261
   * @throws IdentifierNotUnique
262
   * @throws UnsupportedType
263
   * @throws InsufficientResources
264
   * @throws InvalidSystemMetadata
265
   * @throws InvalidRequest
266
   */
267
  @Override
268
  public Identifier update(Session session, Identifier pid, InputStream object,
269
    Identifier newPid, SystemMetadata sysmeta) 
270
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
271
    UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
272
    NotImplemented, InvalidRequest {
273

    
274
    String localId = null;
275
    boolean allowed = false;
276
    boolean isScienceMetadata = false;
277
    List<Group> groupList = new ArrayList<Group>();
278
    String[] groups = new String[0];
279
    Subject subject = session.getSubject();
280
    if ( session.getSubjectList() != null ) {
281
      groupList = session.getSubjectList().getGroupList();
282
      groups = new String[groupList.size()];
283
    	
284
    }
285
    IdentifierManager im = IdentifierManager.getInstance();
286

    
287
    // put the group names into a string array
288
    if( session != null ) {
289
      for ( int i = 0; i > groupList.size(); i++ ) {
290
        groups[i] = groupList.get(i).getGroupName();
291
        
292
      }
293
    }
294

    
295
    // be sure the user is authenticated for update()
296
    if (subject.getValue() == null || 
297
        subject.getValue().toLowerCase().equals("public") ) {
298
      throw new NotAuthorized("1200", "The provided identity does not have " +
299
        "permission to UPDATE objects on the Member Node.");
300
      
301
    }
302

    
303
    // do we have a valid pid?
304
    if ( pid == null || pid.getValue().trim().equals("") ) {
305
      throw new InvalidRequest("1202", "The provided identifier was invalid.");
306

    
307
    }
308

    
309
    // check for the existing identifier
310
    try {
311
      localId = im.getLocalId(pid.getValue());
312

    
313
    } catch (McdbDocNotFoundException e) {
314
      throw new InvalidRequest("1202", "The object with the provided " +
315
        "identifier was not found.");
316

    
317
    }
318

    
319
    // does the subject have WRITE ( == update) priveleges on the pid?
320
    allowed = isAuthorized(session, pid, Permission.WRITE);
321

    
322
    if ( allowed ) {
323
      
324
      // get the existing system metadata for the object
325
      SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
326
      
327
      // add the obsoleted pid to the obsoletedBy list
328
      List<Identifier> obsoletedList = existingSysMeta.getObsoletedByList();
329
      obsoletedList.add(pid);
330
      existingSysMeta.setObsoletedByList(obsoletedList);
331
      
332
      // then update the existing system metadata
333
      updateSystemMetadata(existingSysMeta);
334
      
335
      // prep the new system metadata, add pid to the obsoletes list
336
      sysmeta.addObsolete(pid);
337
      
338
      // and insert the new system metadata
339
      insertSystemMetadata(sysmeta);
340
      
341
      isScienceMetadata = isScienceMetadata(sysmeta);
342
      
343
      // do we have XML metadata or a data object?
344
      if ( isScienceMetadata ) {
345
        
346
        // update the science metadata XML document
347
        // TODO: handle non-XML metadata/data documents (like netCDF)
348
        // TODO: don't put objects into memory using stream to string
349
        String objectAsXML = "";
350
        try {
351
          objectAsXML = IOUtils.toString(object, "UTF-8");
352
          localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
353
          // register the newPid and the generated localId
354
          if ( newPid != null ) {
355
            im.createMapping(newPid.getValue(), localId);
356
            
357
          }
358
          
359
        } catch (IOException e) {
360
          String msg = "The Node is unable to create the object. " +
361
          "There was a problem converting the object to XML";
362
          logMetacat.info(msg);
363
          throw new ServiceFailure("1310", msg + ": " + e.getMessage());
364
        
365
        }
366
        
367
      } else {
368
        
369
        // update the data object
370
        localId = insertDataObject(object, newPid, session);
371
        // register the newPid and the generated localId
372
        if ( newPid != null ) {
373
          im.createMapping(newPid.getValue(), localId);
374
          
375
        }
376
       
377
      }
378
      // log the update event
379
      EventLog.getInstance().log(metacatUrl, subject.getValue(), localId, "update");
380

    
381
    } else {
382
      throw new NotAuthorized("1200", "The provided identity does not have " +
383
      "permission to UPDATE the object identified by " +
384
      pid.getValue() + " on the Member Node.");
385
      
386
    }
387
    
388
    return pid;
389
  }
390

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

    
418
    return false;
419
  }
420

    
421
  /**
422
   * This method provides a lighter weight mechanism than 
423
   * MN_read.getSystemMetadata() for a client to determine basic 
424
   * properties of the referenced object.
425
   * 
426
   * @param session - the Session object containing the credentials for the Subject
427
   * @param pid - the identifier of the object to be described
428
   * 
429
   * @return describeResponse - A set of values providing a basic description 
430
   *                            of the object.
431
   * 
432
   * @throws InvalidToken
433
   * @throws ServiceFailure
434
   * @throws NotAuthorized
435
   * @throws NotFound
436
   * @throws NotImplemented
437
   * @throws InvalidRequest
438
   */
439
  @Override
440
  public DescribeResponse describe(Session session, Identifier pid)
441
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
442
    NotImplemented, InvalidRequest {
443
    
444
    if(session == null) {
445
      throw new InvalidToken("1370", "The session object is null");
446
      
447
    }
448
    
449
    if(pid == null || pid.getValue().trim().equals(""))
450
    {
451
      throw new InvalidRequest("1362", "The object identifier is null. " +
452
        "A valid identifier is required.");
453
        
454
    }
455
    
456
    SystemMetadata sysmeta = getSystemMetadata(session, pid);
457
    DescribeResponse describeResponse = 
458
      new DescribeResponse(sysmeta.getObjectFormat(), 
459
      sysmeta.getSize(), sysmeta.getDateSysMetadataModified(), sysmeta.getChecksum());
460
    
461
    return describeResponse;
462

    
463
  }
464

    
465
  /**
466
   * Return the object identified by the given object identifier
467
   * 
468
   * @param session - the Session object containing the credentials for the Subject
469
   * @param pid - the object identifier for the given object
470
   * 
471
   * @return inputStream - the input stream of the given object
472
   * 
473
   * @throws InvalidToken
474
   * @throws ServiceFailure
475
   * @throws NotAuthorized
476
   * @throws InvalidRequest
477
   * @throws NotImplemented
478
   */
479
  @Override
480
  public InputStream get(Session session, Identifier pid) 
481
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
482
    NotImplemented, InvalidRequest {
483
    
484
    return super.get(session, pid);
485
    
486
  }
487

    
488
  /**
489
   * Returns a Checksum for the specified object using an accepted hashing algorithm
490
   * 
491
   * @param session - the Session object containing the credentials for the Subject
492
   * @param pid - the object identifier for the given object
493
   * @param algorithm -  the name of an algorithm that will be used to compute 
494
   *                     a checksum of the bytes of the object
495
   * 
496
   * @return checksum - the checksum of the given object
497
   * 
498
   * @throws InvalidToken
499
   * @throws ServiceFailure
500
   * @throws NotAuthorized
501
   * @throws NotFound
502
   * @throws InvalidRequest
503
   * @throws NotImplemented
504
   */
505
  @Override
506
  public Checksum getChecksum(Session session, Identifier pid, String algorithm)
507
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
508
    InvalidRequest, NotImplemented {
509

    
510
    Checksum checksum = null;
511
    
512
    InputStream inputStream = get(session, pid);
513
    
514
    try {
515
      checksum = 
516
        ServiceTypeUtil.checksum(inputStream, ChecksumAlgorithm.convert(algorithm));
517
    
518
    } catch (NoSuchAlgorithmException e) {
519
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
520
        pid.getValue() +
521
        "could not be returned due to an internal error: " +
522
        e.getMessage());
523
      
524
    } catch (IOException e) {
525
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
526
        pid.getValue() +
527
        "could not be returned due to an internal error: " +
528
        e.getMessage());
529
      
530
    }
531
    
532
    if ( checksum == null ) {
533
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
534
        pid.getValue() +
535
        "could not be returned.");
536
      
537
    }
538
    
539
    return checksum;
540
  }
541

    
542
  /**
543
   * Return the system metadata for a given object
544
   * 
545
   * @param session - the Session object containing the credentials for the Subject
546
   * @param pid - the object identifier for the given object
547
   * 
548
   * @return inputStream - the input stream of the given system metadata object
549
   * 
550
   * @throws InvalidToken
551
   * @throws ServiceFailure
552
   * @throws NotAuthorized
553
   * @throws NotFound
554
   * @throws InvalidRequest
555
   * @throws NotImplemented
556
   */
557
  @Override
558
  public SystemMetadata getSystemMetadata(Session session, Identifier pid)
559
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
560
      InvalidRequest, NotImplemented {
561

    
562
    return super.getSystemMetadata(session, pid);
563
  }
564

    
565
  /**
566
   * Retrieve the list of objects present on the MN that match the calling parameters
567
   * 
568
   * @param session - the Session object containing the credentials for the Subject
569
   * @param startTime - Specifies the beginning of the time range from which 
570
   *                    to return object (>=)
571
   * @param endTime - Specifies the beginning of the time range from which 
572
   *                  to return object (>=)
573
   * @param objectFormat - Restrict results to the specified object format
574
   * @param replicaStatus - Indicates if replicated objects should be returned in the list
575
   * @param start - The zero-based index of the first value, relative to the 
576
   *                first record of the resultset that matches the parameters.
577
   * @param count - The maximum number of entries that should be returned in 
578
   *                the response. The Member Node may return less entries 
579
   *                than specified in this value.
580
   * 
581
   * @return objectList - the list of objects matching the criteria
582
   * 
583
   * @throws InvalidToken
584
   * @throws ServiceFailure
585
   * @throws NotAuthorized
586
   * @throws InvalidRequest
587
   * @throws NotImplemented
588
   */
589
  @Override
590
  public ObjectList listObjects(Session session, Date startTime, Date endTime,
591
    ObjectFormat objectFormat, Boolean replicaStatus, Integer start, Integer count)
592
    throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure,
593
    InvalidToken {
594

    
595
    ObjectList objectList = null;
596
    
597
    try {
598
	    objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
599
	        objectFormat, replicaStatus, start, count);
600
    } catch (Exception e) {
601
		throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
602
	}
603
    
604
    return objectList;
605
  }
606

    
607
  /**
608
   * Retrieve the list of objects present on the MN that match the calling parameters
609
   * 
610
   * @return node - the technical capabilities of the Member Node
611
   * 
612
   * @throws ServiceFailure
613
   * @throws NotAuthorized
614
   * @throws InvalidRequest
615
   * @throws NotImplemented
616
   */
617
  @Override
618
  public Node getCapabilities() throws NotImplemented, NotAuthorized,
619
      ServiceFailure, InvalidRequest {
620

    
621
		throw new NotImplemented("2160", "getCapabilities() not yet implemented");
622
  }
623

    
624
  /**
625
   * Returns the number of operations that have been serviced by the node 
626
   * over time periods of one and 24 hours.
627
   * 
628
   * @param session - the Session object containing the credentials for the Subject
629
   * @param period - An ISO8601 compatible DateTime range specifying the time 
630
   *                 range for which to return operation statistics.
631
   * @param requestor - Limit to operations performed by given requestor identity.
632
   * @param event -  Enumerated value indicating the type of event being examined
633
   * @param format - Limit to events involving objects of the specified format
634
   * 
635
   * @return the desired log records
636
   * 
637
   * @throws InvalidToken
638
   * @throws ServiceFailure
639
   * @throws NotAuthorized
640
   * @throws InvalidRequest
641
   * @throws NotImplemented
642
   */
643
  @Override
644
  public MonitorList getOperationStatistics(Session session, Date startTime,
645
  		Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId) 
646
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
647
    InsufficientResources, UnsupportedType {
648
    
649
		throw new NotImplemented("2080", "getOperationsStatistics not yet implemented");
650
  }
651

    
652
  /**
653
   * Low level “are you alive” operation. A valid ping response is 
654
   * indicated by a HTTP status of 200.
655
   * 
656
   * @return true if the service is alive
657
   * 
658
   * @throws InvalidToken
659
   * @throws ServiceFailure
660
   * @throws NotAuthorized
661
   * @throws InvalidRequest
662
   * @throws NotImplemented
663
   */
664
  @Override
665
  public boolean ping() 
666
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
667
    InsufficientResources, UnsupportedType {
668

    
669
    // test if we can get a database connection
670
    boolean alive = false;
671
    int serialNumber = -1;
672
    DBConnection dbConn = null;
673
    try {
674
      dbConn = DBConnectionPool
675
      .getDBConnection("MNodeService.ping");
676
      serialNumber = dbConn.getCheckOutSerialNumber();
677
      alive = true;
678
      
679
    } catch (SQLException e) {
680
      return alive;
681
      
682
    } finally {
683
      // Return the database connection
684
      DBConnectionPool.returnDBConnection(dbConn, serialNumber);
685
    
686
    }
687

    
688
    return alive;
689
  }
690

    
691
  /**
692
   * A callback method used by a CN to indicate to a MN that it cannot 
693
   * complete synchronization of the science metadata identified by pid.  Log
694
   * the event in the metacat event log.
695
   * 
696
   * @param session
697
   * @param syncFailed
698
   * 
699
   * @throws ServiceFailure
700
   * @throws NotAuthorized
701
   * @throws InvalidRequest
702
   * @throws NotImplemented
703
   */
704
  @Override
705
  public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
706
      throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest {
707

    
708
    String localId;
709
    
710
    try {
711
      localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid().getValue());
712
    } catch (McdbDocNotFoundException e) {
713
      throw new ServiceFailure("2161", "The identifier specified by " +
714
          syncFailed.getPid().getValue() + 
715
          " was not found on this node.");
716
      
717
    }
718
    // TODO: update the CN URL below when the CNRead.SynchronizationFailed
719
    // method is changed to include the URL as a parameter
720
    logMetacat.debug("Synchronization for the object identified by " +
721
      syncFailed.getPid().getValue() + 
722
      " failed from " +
723
      "CN URL WILL GO HERE." +
724
      " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
725
    // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
726
    EventLog.getInstance().log("CN URL WILL GO HERE", 
727
      session.getSubject().getValue(), localId, "synchronization_failed");
728
    //EventLog.getInstance().log("CN URL WILL GO HERE", 
729
    //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
730

    
731
  }
732

    
733
}
(5-5/8)