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.Date;
31
import java.util.List;
32

    
33
import org.apache.commons.io.IOUtils;
34
import org.apache.log4j.Logger;
35
import org.dataone.service.exceptions.IdentifierNotUnique;
36
import org.dataone.service.exceptions.InsufficientResources;
37
import org.dataone.service.exceptions.InvalidRequest;
38
import org.dataone.service.exceptions.InvalidSystemMetadata;
39
import org.dataone.service.exceptions.InvalidToken;
40
import org.dataone.service.exceptions.NotAuthorized;
41
import org.dataone.service.exceptions.NotFound;
42
import org.dataone.service.exceptions.NotImplemented;
43
import org.dataone.service.exceptions.ServiceFailure;
44
import org.dataone.service.exceptions.SynchronizationFailed;
45
import org.dataone.service.exceptions.UnsupportedType;
46
import org.dataone.service.mn.tier1.MNCore;
47
import org.dataone.service.mn.tier1.MNRead;
48
import org.dataone.service.mn.tier2.MNAuthorization;
49
import org.dataone.service.mn.tier3.MNStorage;
50
import org.dataone.service.mn.tier4.MNReplication;
51
import org.dataone.service.types.AccessPolicy;
52
import org.dataone.service.types.Checksum;
53
import org.dataone.service.types.ChecksumAlgorithm;
54
import org.dataone.service.types.DescribeResponse;
55
import org.dataone.service.types.Event;
56
import org.dataone.service.types.Group;
57
import org.dataone.service.types.Identifier;
58
import org.dataone.service.types.Log;
59
import org.dataone.service.types.MonitorList;
60
import org.dataone.service.types.Node;
61
import org.dataone.service.types.NodeReference;
62
import org.dataone.service.types.ObjectFormat;
63
import org.dataone.service.types.ObjectList;
64
import org.dataone.service.types.Permission;
65
import org.dataone.service.types.Session;
66
import org.dataone.service.types.Subject;
67
import org.dataone.service.types.SystemMetadata;
68
import org.dataone.service.types.util.ServiceTypeUtil;
69

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

    
80
/**
81
 * Represents Metacat's implementation of the DataONE Member Node 
82
 * service API. Methods implement the various MN* interfaces, and methods common
83
 * to both Member Node and Coordinating Node interfaces are found in the
84
 * D1NodeService base class.
85
 */
86
public class MNodeService extends D1NodeService implements MNAuthorization,
87
  MNCore, MNRead, MNReplication, MNStorage {
88

    
89
  /* the instance of the MNodeService object */
90
  private static MNodeService instance = null;
91
  
92
  /* the logger instance */
93
  private Logger logMetacat = null;
94

    
95
  /**
96
   * Singleton accessor to get an instance of MNodeService.
97
   * 
98
   * @return instance - the instance of MNodeService
99
   */
100
  public static MNodeService getInstance() {
101
    if (instance == null) {
102

    
103
      instance = new MNodeService();
104
      
105
    }
106
    
107
    return instance;
108
  }
109
  
110
  /**
111
   * Constructor, private for singleton access
112
   */
113
  private MNodeService() {
114
    super();
115
    logMetacat = Logger.getLogger(MNodeService.class);
116
        
117
  }
118
    
119
  /**
120
   * Deletes an object from the Member Node, where the object is either a 
121
   * data object or a science metadata object.
122
   * 
123
   * @param session - the Session object containing the credentials for the Subject
124
   * @param pid - The object identifier to be deleted
125
   * 
126
   * @return pid - the identifier of the object used for the deletion
127
   * 
128
   * @throws InvalidToken
129
   * @throws ServiceFailure
130
   * @throws NotAuthorized
131
   * @throws NotFound
132
   * @throws NotImplemented
133
   * @throws InvalidRequest
134
   */
135
  @Override
136
  public Identifier delete(Session session, Identifier pid) 
137
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
138
    NotImplemented, InvalidRequest {
139

    
140
    String localId = null;
141
    boolean allowed = false;
142
    Subject subject = session.getSubject();
143
    List<Group> groupList = session.getSubjectList().getGroupList();
144
    String[] groups = new String[groupList.size()];
145
    IdentifierManager im = IdentifierManager.getInstance();
146
    
147
    // put the group names into a string array
148
    if( session != null ) {
149
      for ( int i = 0; i > groupList.size(); i++ ) {
150
        groups[i] = groupList.get(i).getGroupName();
151
        
152
      }
153
    }
154

    
155
    // be sure the user is authenticated for delete()
156
    if (subject.getValue() == null || 
157
        subject.getValue().toLowerCase().equals("public") ) {
158
      throw new NotAuthorized("1320", "The provided identity does not have " +
159
        "permission to DELETE objects on the Member Node.");
160
      
161
    }
162
    
163
    // do we have a valid pid?
164
    if ( pid == null || pid.getValue().trim().equals("") ) {
165
      throw new InvalidRequest("1322", "The provided identifier was invalid.");
166

    
167
    }
168

    
169
    // check for the existing identifier
170
    try {
171
      localId = im.getLocalId(pid.getValue());
172
    
173
    } catch (McdbDocNotFoundException e) {
174
      throw new InvalidRequest("1322", "The object with the provided " +
175
        "identifier was not found.");
176

    
177
    }
178
    
179
    // does the subject have DELETE (a D1 CHANGE_PERMISSION level) priveleges on the pid?
180
    allowed = isAuthorized(session, pid, Permission.CHANGE_PERMISSION);
181
    
182
    if ( allowed ) {
183
      try {
184
        // delete the document
185
        DocumentImpl.delete(localId, subject.getValue(), groups, null);
186
        EventLog.getInstance().log(metacatUrl, subject.getValue(), localId, "delete");
187

    
188
      } catch (McdbDocNotFoundException e) {
189
        throw new InvalidRequest("1322", "The provided identifier was invalid.");
190

    
191
      } catch (SQLException e) {
192
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
193
          "The error message was: " + e.getMessage());
194

    
195
      } catch (InsufficientKarmaException e) {
196
        throw new NotAuthorized("1320", "The provided identity does not have " +
197
        "permission to DELETE objects on the Member Node.");
198
 
199
      } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
200
        throw new ServiceFailure("1350", "There was a problem deleting the object." +
201
            "The error message was: " + e.getMessage());
202

    
203
      }
204

    
205
    } else {
206
      throw new NotAuthorized("1320", "The provided identity does not have " +
207
      "permission to DELETE objects on the Member Node.");
208
      
209
    }
210
    
211
    return pid;
212
  }
213

    
214

    
215
  /**
216
   * Updates an existing object by creating a new object identified by 
217
   * newPid on the Member Node which explicitly obsoletes the object 
218
   * identified by pid through appropriate changes to the SystemMetadata 
219
   * of pid and newPid
220
   * 
221
   * @param session - the Session object containing the credentials for the Subject
222
   * @param pid - The identifier of the object to be updated
223
   * @param object - the new object bytes
224
   * @param sysmeta - the new system metadata describing the object
225
   * 
226
   * @return newPid - the identifier of the new object
227
   * 
228
   * @throws InvalidToken
229
   * @throws ServiceFailure
230
   * @throws NotAuthorized
231
   * @throws NotFound
232
   * @throws NotImplemented
233
   * @throws IdentifierNotUnique
234
   * @throws UnsupportedType
235
   * @throws InsufficientResources
236
   * @throws InvalidSystemMetadata
237
   * @throws InvalidRequest
238
   */
239
  @Override
240
  public Identifier update(Session session, Identifier pid, InputStream object,
241
    Identifier newPid, SystemMetadata sysmeta) 
242
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
243
    UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
244
    NotImplemented, InvalidRequest {
245

    
246
    String localId = null;
247
    boolean allowed = false;
248
    boolean isScienceMetadata = false;
249
    Subject subject = session.getSubject();
250
    List<Group> groupList = session.getSubjectList().getGroupList();
251
    String[] groups = new String[groupList.size()];
252
    IdentifierManager im = IdentifierManager.getInstance();
253

    
254
    // put the group names into a string array
255
    if( session != null ) {
256
      for ( int i = 0; i > groupList.size(); i++ ) {
257
        groups[i] = groupList.get(i).getGroupName();
258
        
259
      }
260
    }
261

    
262
    // be sure the user is authenticated for update()
263
    if (subject.getValue() == null || 
264
        subject.getValue().toLowerCase().equals("public") ) {
265
      throw new NotAuthorized("1200", "The provided identity does not have " +
266
        "permission to UPDATE objects on the Member Node.");
267
      
268
    }
269

    
270
    // do we have a valid pid?
271
    if ( pid == null || pid.getValue().trim().equals("") ) {
272
      throw new InvalidRequest("1202", "The provided identifier was invalid.");
273

    
274
    }
275

    
276
    // check for the existing identifier
277
    try {
278
      localId = im.getLocalId(pid.getValue());
279

    
280
    } catch (McdbDocNotFoundException e) {
281
      throw new InvalidRequest("1202", "The object with the provided " +
282
        "identifier was not found.");
283

    
284
    }
285

    
286
    // does the subject have WRITE ( == update) priveleges on the pid?
287
    allowed = isAuthorized(session, pid, Permission.WRITE);
288

    
289
    if ( allowed ) {
290
      
291
      // get the existing system metadata for the object
292
      SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
293
      
294
      // add the obsoleted pid to the obsoletedBy list
295
      List<Identifier> obsoletedList = existingSysMeta.getObsoletedByList();
296
      obsoletedList.add(pid);
297
      existingSysMeta.setObsoletedByList(obsoletedList);
298
      
299
      // then update the existing system metadata
300
      updateSystemMetadata(existingSysMeta);
301
      
302
      // prep the new system metadata, add pid to the obsoletes list
303
      sysmeta.addObsolete(pid);
304
      
305
      // and insert the new system metadata
306
      insertSystemMetadata(sysmeta);
307
      
308
      isScienceMetadata = isScienceMetadata(sysmeta);
309
      
310
      // do we have XML metadata or a data object?
311
      if ( isScienceMetadata ) {
312
        
313
        // update the science metadata XML document
314
        // TODO: handle non-XML metadata/data documents (like netCDF)
315
        // TODO: don't put objects into memory using stream to string
316
        String objectAsXML = "";
317
        try {
318
          objectAsXML = IOUtils.toString(object, "UTF-8");
319
          localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
320
          // register the newPid and the generated localId
321
          if ( newPid != null ) {
322
            im.createMapping(newPid.getValue(), localId);
323
            
324
          }
325
          
326
        } catch (IOException e) {
327
          String msg = "The Node is unable to create the object. " +
328
          "There was a problem converting the object to XML";
329
          logMetacat.info(msg);
330
          throw new ServiceFailure("1310", msg + ": " + e.getMessage());
331
        
332
        }
333
        
334
      } else {
335
        
336
        // update the data object
337
        localId = insertDataObject(object, newPid, session);
338
        // register the newPid and the generated localId
339
        if ( newPid != null ) {
340
          im.createMapping(newPid.getValue(), localId);
341
          
342
        }
343
       
344
      }
345
      // log the update event
346
      EventLog.getInstance().log(metacatUrl, subject.getValue(), localId, "update");
347

    
348
    } else {
349
      throw new NotAuthorized("1200", "The provided identity does not have " +
350
      "permission to UPDATE the object identified by " +
351
      pid.getValue() + " on the Member Node.");
352
      
353
    }
354
    
355
    return pid;
356
  }
357

    
358
  /**
359
   * Called by a Coordinating Node to request that the Member Node create a 
360
   * copy of the specified object by retrieving it from another Member 
361
   * Node and storing it locally so that it can be made accessible to 
362
   * the DataONE system.
363
   * 
364
   * @param session - the Session object containing the credentials for the Subject
365
   * @param sysmeta - Copy of the CN held system metadata for the object
366
   * @param sourceNode - A reference to node from which the content should be 
367
   *                     retrieved. The reference should be resolved by 
368
   *                     checking the CN node registry.
369
   * 
370
   * @return true if the replication succeeds
371
   * 
372
   * @throws ServiceFailure
373
   * @throws NotAuthorized
374
   * @throws NotImplemented
375
   * @throws UnsupportedType
376
   * @throws InsufficientResources
377
   * @throws InvalidRequest
378
   */
379
  @Override
380
  public boolean replicate(Session session, SystemMetadata sysmeta, 
381
    NodeReference sourceNode)
382
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
383
    InsufficientResources, UnsupportedType {
384

    
385
    return false;
386
  }
387

    
388
  /**
389
   * This method provides a lighter weight mechanism than 
390
   * MN_read.getSystemMetadata() for a client to determine basic 
391
   * properties of the referenced object.
392
   * 
393
   * @param session - the Session object containing the credentials for the Subject
394
   * @param pid - the identifier of the object to be described
395
   * 
396
   * @return describeResponse - A set of values providing a basic description 
397
   *                            of the object.
398
   * 
399
   * @throws InvalidToken
400
   * @throws ServiceFailure
401
   * @throws NotAuthorized
402
   * @throws NotFound
403
   * @throws NotImplemented
404
   * @throws InvalidRequest
405
   */
406
  @Override
407
  public DescribeResponse describe(Session session, Identifier pid)
408
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
409
    NotImplemented, InvalidRequest {
410
    
411
    if(session == null) {
412
      throw new InvalidToken("1370", "The session object is null");
413
      
414
    }
415
    
416
    if(pid == null || pid.getValue().trim().equals(""))
417
    {
418
      throw new InvalidRequest("1362", "The object identifier is null. " +
419
        "A valid identifier is required.");
420
        
421
    }
422
    
423
    SystemMetadata sysmeta = getSystemMetadata(session, pid);
424
    DescribeResponse describeResponse = 
425
      new DescribeResponse(sysmeta.getObjectFormat(), 
426
      sysmeta.getSize(), sysmeta.getDateSysMetadataModified(), sysmeta.getChecksum());
427
    
428
    return describeResponse;
429

    
430
  }
431

    
432
  /**
433
   * Return the object identified by the given object identifier
434
   * 
435
   * @param session - the Session object containing the credentials for the Subject
436
   * @param pid - the object identifier for the given object
437
   * 
438
   * @return inputStream - the input stream of the given object
439
   * 
440
   * @throws InvalidToken
441
   * @throws ServiceFailure
442
   * @throws NotAuthorized
443
   * @throws InvalidRequest
444
   * @throws NotImplemented
445
   */
446
  @Override
447
  public InputStream get(Session session, Identifier pid) 
448
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
449
    NotImplemented, InvalidRequest {
450
    
451
    return super.get(session, pid);
452
    
453
  }
454

    
455
  /**
456
   * Returns a Checksum for the specified object using an accepted hashing algorithm
457
   * 
458
   * @param session - the Session object containing the credentials for the Subject
459
   * @param pid - the object identifier for the given object
460
   * @param algorithm -  the name of an algorithm that will be used to compute 
461
   *                     a checksum of the bytes of the object
462
   * 
463
   * @return checksum - the checksum of the given object
464
   * 
465
   * @throws InvalidToken
466
   * @throws ServiceFailure
467
   * @throws NotAuthorized
468
   * @throws NotFound
469
   * @throws InvalidRequest
470
   * @throws NotImplemented
471
   */
472
  @Override
473
  public Checksum getChecksum(Session session, Identifier pid, String algorithm)
474
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
475
    InvalidRequest, NotImplemented {
476

    
477
    Checksum checksum = null;
478
    
479
    InputStream inputStream = get(session, pid);
480
    
481
    try {
482
      checksum = 
483
        ServiceTypeUtil.checksum(inputStream, ChecksumAlgorithm.convert(algorithm));
484
    
485
    } catch (NoSuchAlgorithmException e) {
486
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
487
        pid.getValue() +
488
        "could not be returned due to an internal error: " +
489
        e.getMessage());
490
      
491
    } catch (IOException e) {
492
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
493
        pid.getValue() +
494
        "could not be returned due to an internal error: " +
495
        e.getMessage());
496
      
497
    }
498
    
499
    if ( checksum == null ) {
500
      throw new ServiceFailure("1410", "The checksum for the object specified by " + 
501
        pid.getValue() +
502
        "could not be returned.");
503
      
504
    }
505
    
506
    return checksum;
507
  }
508

    
509
  /**
510
   * Return the system metadata for a given object
511
   * 
512
   * @param session - the Session object containing the credentials for the Subject
513
   * @param pid - the object identifier for the given object
514
   * 
515
   * @return inputStream - the input stream of the given system metadata object
516
   * 
517
   * @throws InvalidToken
518
   * @throws ServiceFailure
519
   * @throws NotAuthorized
520
   * @throws NotFound
521
   * @throws InvalidRequest
522
   * @throws NotImplemented
523
   */
524
  @Override
525
  public SystemMetadata getSystemMetadata(Session session, Identifier pid)
526
      throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
527
      InvalidRequest, NotImplemented {
528

    
529
    return super.getSystemMetadata(session, pid);
530
  }
531

    
532
  /**
533
   * Retrieve the list of objects present on the MN that match the calling parameters
534
   * 
535
   * @param session - the Session object containing the credentials for the Subject
536
   * @param startTime - Specifies the beginning of the time range from which 
537
   *                    to return object (>=)
538
   * @param endTime - Specifies the beginning of the time range from which 
539
   *                  to return object (>=)
540
   * @param objectFormat - Restrict results to the specified object format
541
   * @param replicaStatus - Indicates if replicated objects should be returned in the list
542
   * @param start - The zero-based index of the first value, relative to the 
543
   *                first record of the resultset that matches the parameters.
544
   * @param count - The maximum number of entries that should be returned in 
545
   *                the response. The Member Node may return less entries 
546
   *                than specified in this value.
547
   * 
548
   * @return objectList - the list of objects matching the criteria
549
   * 
550
   * @throws InvalidToken
551
   * @throws ServiceFailure
552
   * @throws NotAuthorized
553
   * @throws InvalidRequest
554
   * @throws NotImplemented
555
   */
556
  @Override
557
  public ObjectList listObjects(Session session, Date startTime, Date endTime,
558
    ObjectFormat objectFormat, Boolean replicaStatus, Integer start, Integer count)
559
    throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure,
560
    InvalidToken {
561

    
562
    ObjectList objectList = null;
563
    
564
    objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
565
        objectFormat, replicaStatus, start, count);
566
    
567
    if ( objectList == null ) {
568
      throw new ServiceFailure("1580", "The object list was null.");
569
    }
570
    
571
    return objectList;
572
  }
573

    
574
  /**
575
   * Retrieve the list of objects present on the MN that match the calling parameters
576
   * 
577
   * @return node - the technical capabilities of the Member Node
578
   * 
579
   * @throws ServiceFailure
580
   * @throws NotAuthorized
581
   * @throws InvalidRequest
582
   * @throws NotImplemented
583
   */
584
  @Override
585
  public Node getCapabilities() throws NotImplemented, NotAuthorized,
586
      ServiceFailure, InvalidRequest {
587

    
588
		throw new NotImplemented("2160", "getCapabilities() not yet implemented");
589
  }
590

    
591
  /**
592
   * Returns the number of operations that have been serviced by the node 
593
   * over time periods of one and 24 hours.
594
   * 
595
   * @param session - the Session object containing the credentials for the Subject
596
   * @param period - An ISO8601 compatible DateTime range specifying the time 
597
   *                 range for which to return operation statistics.
598
   * @param requestor - Limit to operations performed by given requestor identity.
599
   * @param event -  Enumerated value indicating the type of event being examined
600
   * @param format - Limit to events involving objects of the specified format
601
   * 
602
   * @return the desired log records
603
   * 
604
   * @throws InvalidToken
605
   * @throws ServiceFailure
606
   * @throws NotAuthorized
607
   * @throws InvalidRequest
608
   * @throws NotImplemented
609
   */
610
  @Override
611
  public MonitorList getOperationStatistics(Session session, Integer period,
612
    Subject requestor, Event event, ObjectFormat format) 
613
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
614
    InsufficientResources, UnsupportedType {
615

    
616
		throw new NotImplemented("2080", "getOperationsStatistics not yet implemented");
617
  }
618

    
619
  /**
620
   * Low level “are you alive” operation. A valid ping response is 
621
   * indicated by a HTTP status of 200.
622
   * 
623
   * @return true if the service is alive
624
   * 
625
   * @throws InvalidToken
626
   * @throws ServiceFailure
627
   * @throws NotAuthorized
628
   * @throws InvalidRequest
629
   * @throws NotImplemented
630
   */
631
  @Override
632
  public boolean ping() 
633
    throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
634
    InsufficientResources, UnsupportedType {
635

    
636
    // test if we can get a database connection
637
    boolean alive = false;
638
    int serialNumber = -1;
639
    DBConnection dbConn = null;
640
    try {
641
      dbConn = DBConnectionPool
642
      .getDBConnection("MNodeService.ping");
643
      serialNumber = dbConn.getCheckOutSerialNumber();
644
      alive = true;
645
      
646
    } catch (SQLException e) {
647
      return alive;
648
      
649
    } finally {
650
      // Return the database connection
651
      DBConnectionPool.returnDBConnection(dbConn, serialNumber);
652
    
653
    }
654

    
655
    return alive;
656
  }
657

    
658
  /**
659
   * A callback method used by a CN to indicate to a MN that it cannot 
660
   * complete synchronization of the science metadata identified by pid.  Log
661
   * the event in the metacat event log.
662
   * 
663
   * @param session
664
   * @param syncFailed
665
   * 
666
   * @throws ServiceFailure
667
   * @throws NotAuthorized
668
   * @throws InvalidRequest
669
   * @throws NotImplemented
670
   */
671
  @Override
672
  public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
673
      throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest {
674

    
675
    String localId;
676
    
677
    try {
678
      localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid().getValue());
679
    } catch (McdbDocNotFoundException e) {
680
      throw new ServiceFailure("2161", "The identifier specified by " +
681
          syncFailed.getPid().getValue() + 
682
          " was not found on this node.");
683
      
684
    }
685
    // TODO: update the CN URL below when the CNRead.SynchronizationFailed
686
    // method is changed to include the URL as a parameter
687
    logMetacat.debug("Synchronization for the object identified by " +
688
      syncFailed.getPid().getValue() + 
689
      " failed from " +
690
      "CN URL WILL GO HERE." +
691
      " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
692
    EventLog.getInstance().log("CN URL WILL GO HERE", 
693
        session.getSubject().getValue(), localId, "syncFailed");
694

    
695
  }
696

    
697
}
(5-5/8)