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

    
35
import javax.servlet.http.HttpServletRequest;
36

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

    
92
import edu.ucsb.nceas.metacat.DocumentImpl;
93
import edu.ucsb.nceas.metacat.EventLog;
94
import edu.ucsb.nceas.metacat.IdentifierManager;
95
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
96
import edu.ucsb.nceas.metacat.MetacatHandler;
97
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
98
import edu.ucsb.nceas.metacat.database.DBConnection;
99
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
100
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
101
import edu.ucsb.nceas.metacat.properties.PropertyService;
102
import edu.ucsb.nceas.metacat.util.SystemUtil;
103
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
104

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

    
135
    /* the logger instance */
136
    private Logger logMetacat = null;
137
    
138
    /* A reference to a remote Memeber Node */
139
    private MNode mn;
140
    
141
    /* A reference to a Coordinating Node */
142
    private CNode cn;
143

    
144

    
145
    /**
146
     * Singleton accessor to get an instance of MNodeService.
147
     * 
148
     * @return instance - the instance of MNodeService
149
     */
150
    public static MNodeService getInstance(HttpServletRequest request) {
151
        return new MNodeService(request);
152
    }
153

    
154
    /**
155
     * Constructor, private for singleton access
156
     */
157
    private MNodeService(HttpServletRequest request) {
158
        super(request);
159
        logMetacat = Logger.getLogger(MNodeService.class);
160
        
161
        // set the Member Node certificate file location
162
        CertificateManager.getInstance().setCertificateLocation(Settings.getConfiguration().getString("D1Client.certificate.file"));
163
    }
164

    
165
    /**
166
     * Deletes an object from the Member Node, where the object is either a 
167
     * data object or a science metadata object.
168
     * 
169
     * @param session - the Session object containing the credentials for the Subject
170
     * @param pid - The object identifier to be deleted
171
     * 
172
     * @return pid - the identifier of the object used for the deletion
173
     * 
174
     * @throws InvalidToken
175
     * @throws ServiceFailure
176
     * @throws NotAuthorized
177
     * @throws NotFound
178
     * @throws NotImplemented
179
     * @throws InvalidRequest
180
     */
181
    @Override
182
    public Identifier delete(Session session, Identifier pid) 
183
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
184

    
185
        String localId = null;
186
        boolean allowed = false;
187
        String username = Constants.SUBJECT_PUBLIC;
188
        String[] groupnames = null;
189
        if (session == null) {
190
        	throw new InvalidToken("1330", "No session has been provided");
191
        } else {
192
            username = session.getSubject().getValue();
193
            if (session.getSubjectInfo() != null) {
194
                List<Group> groupList = session.getSubjectInfo().getGroupList();
195
                if (groupList != null) {
196
                    groupnames = new String[groupList.size()];
197
                    for (int i = 0; i > groupList.size(); i++) {
198
                        groupnames[i] = groupList.get(i).getGroupName();
199
                    }
200
                }
201
            }
202
        }
203

    
204
        // do we have a valid pid?
205
        if (pid == null || pid.getValue().trim().equals("")) {
206
            throw new ServiceFailure("1350", "The provided identifier was invalid.");
207
        }
208

    
209
        // check for the existing identifier
210
        try {
211
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
212
        } catch (McdbDocNotFoundException e) {
213
            throw new NotFound("1340", "The object with the provided " + "identifier was not found.");
214
        }
215

    
216
        // does the subject have DELETE (a D1 CHANGE_PERMISSION level) priveleges on the pid?
217
        allowed = isAuthorized(session, pid, Permission.CHANGE_PERMISSION);
218
            
219

    
220
        if (allowed) {
221
            try {
222
                // delete the document
223
                DocumentImpl.delete(localId, username, groupnames, null);
224
                EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, localId, Event.DELETE.xmlValue());
225

    
226
                // archive it
227
                SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
228
                sysMeta.setArchived(true);
229
                HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
230
                
231
                // remove the system metadata for it
232
                //HazelcastService.getInstance().getSystemMetadataMap().remove(pid);
233
                
234
            } catch (McdbDocNotFoundException e) {
235
                throw new NotFound("1340", "The provided identifier was invalid.");
236

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

    
240
            } catch (InsufficientKarmaException e) {
241
                throw new NotAuthorized("1320", "The provided identity does not have " + "permission to DELETE objects on the Member Node.");
242

    
243
            } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
244
                throw new ServiceFailure("1350", "There was a problem deleting the object." + "The error message was: " + e.getMessage());
245
            }
246

    
247
        } else {
248
            throw new NotAuthorized("1320", "The provided identity does not have " + "permission to DELETE objects on the Member Node.");
249
        }
250

    
251
        return pid;
252
    }
253

    
254
    /**
255
     * Updates an existing object by creating a new object identified by 
256
     * newPid on the Member Node which explicitly obsoletes the object 
257
     * identified by pid through appropriate changes to the SystemMetadata 
258
     * of pid and newPid
259
     * 
260
     * @param session - the Session object containing the credentials for the Subject
261
     * @param pid - The identifier of the object to be updated
262
     * @param object - the new object bytes
263
     * @param sysmeta - the new system metadata describing the object
264
     * 
265
     * @return newPid - the identifier of the new object
266
     * 
267
     * @throws InvalidToken
268
     * @throws ServiceFailure
269
     * @throws NotAuthorized
270
     * @throws NotFound
271
     * @throws NotImplemented
272
     * @throws IdentifierNotUnique
273
     * @throws UnsupportedType
274
     * @throws InsufficientResources
275
     * @throws InvalidSystemMetadata
276
     * @throws InvalidRequest
277
     */
278
    @Override
279
    public Identifier update(Session session, Identifier pid, InputStream object, 
280
        Identifier newPid, SystemMetadata sysmeta) 
281
        throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
282
        UnsupportedType, InsufficientResources, NotFound, 
283
        InvalidSystemMetadata, NotImplemented, InvalidRequest {
284

    
285
        String localId = null;
286
        boolean allowed = false;
287
        boolean isScienceMetadata = false;
288
        
289
        if (session == null) {
290
        	throw new InvalidToken("1210", "No session has been provided");
291
        }
292
        Subject subject = session.getSubject();
293

    
294
        // do we have a valid pid?
295
        if (pid == null || pid.getValue().trim().equals("")) {
296
            throw new InvalidRequest("1202", "The provided identifier was invalid.");
297
            
298
        }
299

    
300
        // check for the existing identifier
301
        try {
302
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
303
            
304
        } catch (McdbDocNotFoundException e) {
305
            throw new InvalidRequest("1202", "The object with the provided " + 
306
                "identifier was not found.");
307
            
308
        }
309
        
310
        // set the originating node
311
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
312
        sysmeta.setOriginMemberNode(originMemberNode);
313
        
314
        // set the submitter to match the certificate
315
        sysmeta.setSubmitter(subject);
316
        // set the dates
317
        Date now = Calendar.getInstance().getTime();
318
        sysmeta.setDateSysMetadataModified(now);
319
        sysmeta.setDateUploaded(now);
320

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

    
324
        if (allowed) {
325
        	
326
        	// check quality of SM
327
        	if (sysmeta.getObsoletedBy() != null) {
328
        		throw new InvalidSystemMetadata("1300", "Cannot include obsoletedBy when updating object");
329
        	}
330
        	if (sysmeta.getObsoletes() != null && !sysmeta.getObsoletes().getValue().equals(pid.getValue())) {
331
        		throw new InvalidSystemMetadata("1300", "The identifier provided in obsoletes does not match old Identifier");
332
        	}
333

    
334
            // get the existing system metadata for the object
335
            SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
336

    
337
            // add the newPid to the obsoletedBy list for the existing sysmeta
338
            existingSysMeta.setObsoletedBy(newPid);
339

    
340
            // then update the existing system metadata
341
            updateSystemMetadata(existingSysMeta);
342

    
343
            // prep the new system metadata, add pid to the affected lists
344
            sysmeta.setObsoletes(pid);
345
            //sysmeta.addDerivedFrom(pid);
346

    
347
            isScienceMetadata = isScienceMetadata(sysmeta);
348

    
349
            // do we have XML metadata or a data object?
350
            if (isScienceMetadata) {
351

    
352
                // update the science metadata XML document
353
                // TODO: handle non-XML metadata/data documents (like netCDF)
354
                // TODO: don't put objects into memory using stream to string
355
                String objectAsXML = "";
356
                try {
357
                    objectAsXML = IOUtils.toString(object, "UTF-8");
358
                    localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
359
                    // register the newPid and the generated localId
360
                    if (newPid != null) {
361
                        IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
362

    
363
                    }
364

    
365
                } catch (IOException e) {
366
                    String msg = "The Node is unable to create the object. " + "There was a problem converting the object to XML";
367
                    logMetacat.info(msg);
368
                    throw new ServiceFailure("1310", msg + ": " + e.getMessage());
369

    
370
                }
371

    
372
            } else {
373

    
374
                // update the data object
375
                localId = insertDataObject(object, newPid, session);
376

    
377
            }
378

    
379
            // and insert the new system metadata
380
            insertSystemMetadata(sysmeta);
381

    
382
            // log the update event
383
            EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), subject.getValue(), localId, Event.UPDATE.toString());
384

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

    
390
        return newPid;
391
    }
392

    
393
    public Identifier create(Session session, Identifier pid, InputStream object, SystemMetadata sysmeta) throws InvalidToken, ServiceFailure, NotAuthorized,
394
            IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, InvalidRequest {
395

    
396
      // check for null session
397
        if (session == null) {
398
          throw new InvalidToken("1110", "Session is required to WRITE to the Node.");
399
        }
400
        // set the submitter to match the certificate
401
        sysmeta.setSubmitter(session.getSubject());
402
        // set the originating node
403
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
404
        sysmeta.setOriginMemberNode(originMemberNode);
405
        // set the dates
406
        Date now = Calendar.getInstance().getTime();
407
    sysmeta.setDateSysMetadataModified(now);
408
    sysmeta.setDateUploaded(now);
409
        // call the shared impl
410
        return super.create(session, pid, object, sysmeta);
411
    }
412

    
413
    /**
414
     * Called by a Coordinating Node to request that the Member Node create a 
415
     * copy of the specified object by retrieving it from another Member 
416
     * Node and storing it locally so that it can be made accessible to 
417
     * the DataONE system.
418
     * 
419
     * @param session - the Session object containing the credentials for the Subject
420
     * @param sysmeta - Copy of the CN held system metadata for the object
421
     * @param sourceNode - A reference to node from which the content should be 
422
     *                     retrieved. The reference should be resolved by 
423
     *                     checking the CN node registry.
424
     * 
425
     * @return true if the replication succeeds
426
     * 
427
     * @throws ServiceFailure
428
     * @throws NotAuthorized
429
     * @throws NotImplemented
430
     * @throws UnsupportedType
431
     * @throws InsufficientResources
432
     * @throws InvalidRequest
433
     */
434
    @Override
435
    public boolean replicate(Session session, SystemMetadata sysmeta,
436
            NodeReference sourceNode) throws NotImplemented, ServiceFailure,
437
            NotAuthorized, InvalidRequest, InsufficientResources,
438
            UnsupportedType {
439

    
440
        logMetacat.info("MNodeService.replicate() called with parameters: \n"
441
                + "\tSession.Subject      = " + session.getSubject().getValue()
442
                + "\n" + "\tSystemMetadata       = " + sysmeta.toString()
443
                + "\n" + "\tSource NodeReference =" + sourceNode.getValue());
444

    
445
        boolean result = false;
446
        String nodeIdStr = null;
447
        NodeReference nodeId = null;
448

    
449
        // get the referenced object
450
        Identifier pid = sysmeta.getIdentifier();
451

    
452
        // get from the membernode
453
        // TODO: switch credentials for the server retrieval?
454
        this.mn = D1Client.getMN(sourceNode);
455
        this.cn = D1Client.getCN();
456
        InputStream object = null;
457
        Session thisNodeSession = null;
458
        SystemMetadata localSystemMetadata = null;
459
        BaseException failure = null;
460

    
461
        // TODO: check credentials
462
        // cannot be called by public
463
        if (session == null) {
464
            String msg = "No session was provided.";
465
            failure = new NotAuthorized("2152", msg);
466
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
467
            logMetacat.info(msg);
468
            return true;
469
        }
470

    
471

    
472
        // get the local node id
473
        try {
474
            nodeIdStr = PropertyService.getProperty("dataone.memberNodeId");
475
            nodeId = new NodeReference();
476
            nodeId.setValue(nodeIdStr);
477

    
478
        } catch (PropertyNotFoundException e1) {
479
            String msg = "Couldn't get dataone.memberNodeId property: " + e1.getMessage();
480
            failure = new ServiceFailure("2151", msg);
481
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
482
            logMetacat.error(msg);
483
            return true;
484

    
485
        }
486
        
487

    
488
        try {
489
            // do we already have a replica?
490
            try {
491
                localSystemMetadata = 
492
                    HazelcastService.getInstance().getSystemMetadataMap().get(pid);
493
                object = getReplica(thisNodeSession, pid);
494
                
495
            } catch (RuntimeException e) {
496
                String msg = "An error occurred getting system metadata for " +
497
                    pid.getValue();
498
                failure = new ServiceFailure("2151", msg);
499
                setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
500
                logMetacat.error(msg);
501
                return true;
502

    
503
            }
504

    
505
            // no local replica, get a replica
506
            if (localSystemMetadata == null) {
507
                // session should be null to use the default certificate
508
                // location set in the Certificate manager
509
                object = mn.getReplica(thisNodeSession, pid);
510
                logMetacat.info("MNodeService.replicate() called for identifier "
511
                                + pid.getValue());
512

    
513
            }
514

    
515
        } catch (InvalidToken e) {            
516
            String msg = "Could not retrieve object to replicate (InvalidToken): "+ e.getMessage();
517
            failure = new ServiceFailure("2151", msg);
518
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
519
            logMetacat.error(msg);
520
            throw new ServiceFailure("2151", msg);
521

    
522
        } catch (NotFound e) {
523
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
524
            failure = new ServiceFailure("2151", msg);
525
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
526
            logMetacat.error(msg);
527
            throw new ServiceFailure("2151", msg);
528

    
529
        }
530

    
531
        // verify checksum on the object, if supported
532
        if (object.markSupported()) {
533
            Checksum givenChecksum = sysmeta.getChecksum();
534
            Checksum computedChecksum = null;
535
            try {
536
                computedChecksum = ChecksumUtil.checksum(object,
537
                        givenChecksum.getAlgorithm());
538
                object.reset();
539
            } catch (Exception e) {
540
                String msg = "Error computing checksum on replica: "
541
                        + e.getMessage();
542
                ServiceFailure sf = new ServiceFailure("2151", msg);
543
                sf.initCause(e);
544
                throw sf;
545
            }
546
            if (!givenChecksum.getValue().equals(computedChecksum)) {
547
                throw new ServiceFailure("2151",
548
                        "Computed checksum does not match declared checksum");
549
            }
550
        }
551

    
552
        // add it to local store
553
        Identifier retPid;
554
        try {
555
            // skip the MN.create -- this mutates the system metadata and we
556
            // dont want it to
557
            retPid = super.create(session, pid, object, sysmeta);
558
            result = (retPid.getValue().equals(pid.getValue()));
559
            
560
        } catch (InvalidToken e) {
561
            String msg = "Could not save object to local store (InvalidToken): " + e.getMessage();
562
            failure = new ServiceFailure("2151", msg);
563
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
564
            logMetacat.error(msg);
565
            throw new ServiceFailure("2151", msg);
566
        
567
        } catch (IdentifierNotUnique e) {
568
            String msg = "Could not save object to local store (IdentifierNotUnique): " + e.getMessage();
569
            failure = new ServiceFailure("2151", msg);
570
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
571
            logMetacat.error(msg);
572
            throw new ServiceFailure("2151", msg);
573
        
574
        } catch (InvalidSystemMetadata e) {
575
            String msg = "Could not save object to local store (InvalidSystemMetadata): " + e.getMessage();
576
            failure = new ServiceFailure("2151", msg);
577
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
578
            logMetacat.error(msg);
579
            throw new ServiceFailure("2151", msg);
580
            
581
        }
582

    
583
        // finish by setting the replication status
584
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
585
        return result;
586

    
587
    }
588

    
589
    /**
590
     * This method provides a lighter weight mechanism than 
591
     * MN_read.getSystemMetadata() for a client to determine basic 
592
     * properties of the referenced object.
593
     * 
594
     * @param session - the Session object containing the credentials for the Subject
595
     * @param pid - the identifier of the object to be described
596
     * 
597
     * @return describeResponse - A set of values providing a basic description 
598
     *                            of the object.
599
     * 
600
     * @throws InvalidToken
601
     * @throws ServiceFailure
602
     * @throws NotAuthorized
603
     * @throws NotFound
604
     * @throws NotImplemented
605
     * @throws InvalidRequest
606
     */
607
    @Override
608
    public DescribeResponse describe(Session session, Identifier pid) 
609
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
610

    
611
      // get system metadata and construct the describe response
612
        SystemMetadata sysmeta = getSystemMetadata(session, pid);
613
        DescribeResponse describeResponse = 
614
        	new DescribeResponse(sysmeta.getFormatId(), sysmeta.getSize(), 
615
        			sysmeta.getDateSysMetadataModified(),
616
        			sysmeta.getChecksum(), sysmeta.getSerialVersion());
617

    
618
        return describeResponse;
619

    
620
    }
621

    
622
    /**
623
     * Return the object identified by the given object identifier
624
     * 
625
     * @param session - the Session object containing the credentials for the Subject
626
     * @param pid - the object identifier for the given object
627
     * 
628
     * @return inputStream - the input stream of the given object
629
     * 
630
     * @throws InvalidToken
631
     * @throws ServiceFailure
632
     * @throws NotAuthorized
633
     * @throws InvalidRequest
634
     * @throws NotImplemented
635
     */
636
    @Override
637
    public InputStream get(Session session, Identifier pid) 
638
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
639

    
640
        return super.get(session, pid);
641

    
642
    }
643

    
644
    /**
645
     * Returns a Checksum for the specified object using an accepted hashing algorithm
646
     * 
647
     * @param session - the Session object containing the credentials for the Subject
648
     * @param pid - the object identifier for the given object
649
     * @param algorithm -  the name of an algorithm that will be used to compute 
650
     *                     a checksum of the bytes of the object
651
     * 
652
     * @return checksum - the checksum of the given object
653
     * 
654
     * @throws InvalidToken
655
     * @throws ServiceFailure
656
     * @throws NotAuthorized
657
     * @throws NotFound
658
     * @throws InvalidRequest
659
     * @throws NotImplemented
660
     */
661
    @Override
662
    public Checksum getChecksum(Session session, Identifier pid, String algorithm) 
663
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
664
        InvalidRequest, NotImplemented {
665

    
666
        Checksum checksum = null;
667

    
668
        InputStream inputStream = get(session, pid);
669

    
670
        try {
671
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
672

    
673
        } catch (NoSuchAlgorithmException e) {
674
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
675
                    + e.getMessage());
676
        } catch (IOException e) {
677
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
678
                    + e.getMessage());
679
        }
680

    
681
        if (checksum == null) {
682
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
683
        }
684

    
685
        return checksum;
686
    }
687

    
688
    /**
689
     * Return the system metadata for a given object
690
     * 
691
     * @param session - the Session object containing the credentials for the Subject
692
     * @param pid - the object identifier for the given object
693
     * 
694
     * @return inputStream - the input stream of the given system metadata object
695
     * 
696
     * @throws InvalidToken
697
     * @throws ServiceFailure
698
     * @throws NotAuthorized
699
     * @throws NotFound
700
     * @throws InvalidRequest
701
     * @throws NotImplemented
702
     */
703
    @Override
704
    public SystemMetadata getSystemMetadata(Session session, Identifier pid) 
705
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
706
        NotImplemented {
707

    
708
        return super.getSystemMetadata(session, pid);
709
    }
710

    
711
    /**
712
     * Retrieve the list of objects present on the MN that match the calling parameters
713
     * 
714
     * @param session - the Session object containing the credentials for the Subject
715
     * @param startTime - Specifies the beginning of the time range from which 
716
     *                    to return object (>=)
717
     * @param endTime - Specifies the beginning of the time range from which 
718
     *                  to return object (>=)
719
     * @param objectFormat - Restrict results to the specified object format
720
     * @param replicaStatus - Indicates if replicated objects should be returned in the list
721
     * @param start - The zero-based index of the first value, relative to the 
722
     *                first record of the resultset that matches the parameters.
723
     * @param count - The maximum number of entries that should be returned in 
724
     *                the response. The Member Node may return less entries 
725
     *                than specified in this value.
726
     * 
727
     * @return objectList - the list of objects matching the criteria
728
     * 
729
     * @throws InvalidToken
730
     * @throws ServiceFailure
731
     * @throws NotAuthorized
732
     * @throws InvalidRequest
733
     * @throws NotImplemented
734
     */
735
    @Override
736
    public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
737
            Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
738

    
739
        ObjectList objectList = null;
740

    
741
        try {
742
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
743
        } catch (Exception e) {
744
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
745
        }
746

    
747
        return objectList;
748
    }
749

    
750
    /**
751
     * Return a description of the node's capabilities and services.
752
     * 
753
     * @return node - the technical capabilities of the Member Node
754
     * 
755
     * @throws ServiceFailure
756
     * @throws NotAuthorized
757
     * @throws InvalidRequest
758
     * @throws NotImplemented
759
     */
760
    @Override
761
    public Node getCapabilities() 
762
        throws NotImplemented, ServiceFailure {
763

    
764
        String nodeName = null;
765
        String nodeId = null;
766
        String subject = null;
767
        String nodeDesc = null;
768
        String nodeTypeString = null;
769
        NodeType nodeType = null;
770
        String mnCoreServiceVersion = null;
771
        String mnReadServiceVersion = null;
772
        String mnAuthorizationServiceVersion = null;
773
        String mnStorageServiceVersion = null;
774
        String mnReplicationServiceVersion = null;
775

    
776
        boolean nodeSynchronize = false;
777
        boolean nodeReplicate = false;
778
        boolean mnCoreServiceAvailable = false;
779
        boolean mnReadServiceAvailable = false;
780
        boolean mnAuthorizationServiceAvailable = false;
781
        boolean mnStorageServiceAvailable = false;
782
        boolean mnReplicationServiceAvailable = false;
783

    
784
        try {
785
            // get the properties of the node based on configuration information
786
            nodeName = PropertyService.getProperty("dataone.nodeName");
787
            nodeId = PropertyService.getProperty("dataone.memberNodeId");
788
            subject = PropertyService.getProperty("dataone.subject");
789
            nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
790
            nodeTypeString = PropertyService.getProperty("dataone.nodeType");
791
            nodeType = NodeType.convert(nodeTypeString);
792
            nodeSynchronize = new Boolean(PropertyService.getProperty("dataone.nodeSynchronize")).booleanValue();
793
            nodeReplicate = new Boolean(PropertyService.getProperty("dataone.nodeReplicate")).booleanValue();
794

    
795
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
796
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
797
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
798
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
799
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
800

    
801
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
802
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
803
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
804
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
805
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
806

    
807
            // Set the properties of the node based on configuration information and
808
            // calls to current status methods
809
            String serviceName = SystemUtil.getContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
810
            Node node = new Node();
811
            node.setBaseURL(serviceName + "/" + nodeTypeString);
812
            node.setDescription(nodeDesc);
813

    
814
            // set the node's health information
815
            node.setState(NodeState.UP);
816
            
817
            // set the ping response to the current value
818
            Ping canPing = new Ping();
819
            canPing.setSuccess(false);
820
            try {
821
                canPing.setSuccess(ping());
822
            } catch (InsufficientResources e) {
823
                e.printStackTrace();
824
            }
825
            
826
            node.setPing(canPing);
827

    
828
            NodeReference identifier = new NodeReference();
829
            identifier.setValue(nodeId);
830
            node.setIdentifier(identifier);
831
            Subject s = new Subject();
832
            s.setValue(subject);
833
            node.addSubject(s);
834
            node.setName(nodeName);
835
            node.setReplicate(nodeReplicate);
836
            node.setSynchronize(nodeSynchronize);
837

    
838
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
839
            Services services = new Services();
840

    
841
            Service sMNCore = new Service();
842
            sMNCore.setName("MNCore");
843
            sMNCore.setVersion(mnCoreServiceVersion);
844
            sMNCore.setAvailable(mnCoreServiceAvailable);
845

    
846
            Service sMNRead = new Service();
847
            sMNRead.setName("MNRead");
848
            sMNRead.setVersion(mnReadServiceVersion);
849
            sMNRead.setAvailable(mnReadServiceAvailable);
850

    
851
            Service sMNAuthorization = new Service();
852
            sMNAuthorization.setName("MNAuthorization");
853
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
854
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
855

    
856
            Service sMNStorage = new Service();
857
            sMNStorage.setName("MNStorage");
858
            sMNStorage.setVersion(mnStorageServiceVersion);
859
            sMNStorage.setAvailable(mnStorageServiceAvailable);
860

    
861
            Service sMNReplication = new Service();
862
            sMNReplication.setName("MNReplication");
863
            sMNReplication.setVersion(mnReplicationServiceVersion);
864
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
865

    
866
            services.addService(sMNRead);
867
            services.addService(sMNCore);
868
            services.addService(sMNAuthorization);
869
            services.addService(sMNStorage);
870
            services.addService(sMNReplication);
871
            node.setServices(services);
872

    
873
            // Set the schedule for synchronization
874
            Synchronization synchronization = new Synchronization();
875
            Schedule schedule = new Schedule();
876
            Date now = new Date();
877
            schedule.setYear(PropertyService.getProperty("dataone.nodeSynchronization.schedule.year"));
878
            schedule.setMon(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mon"));
879
            schedule.setMday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mday"));
880
            schedule.setWday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.wday"));
881
            schedule.setHour(PropertyService.getProperty("dataone.nodeSynchronization.schedule.hour"));
882
            schedule.setMin(PropertyService.getProperty("dataone.nodeSynchronization.schedule.min"));
883
            schedule.setSec(PropertyService.getProperty("dataone.nodeSynchronization.schedule.sec"));
884
            synchronization.setSchedule(schedule);
885
            synchronization.setLastHarvested(now);
886
            synchronization.setLastCompleteHarvest(now);
887
            node.setSynchronization(synchronization);
888

    
889
            node.setType(nodeType);
890
            return node;
891

    
892
        } catch (PropertyNotFoundException pnfe) {
893
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
894
            logMetacat.error(msg);
895
            throw new ServiceFailure("2162", msg);
896
        }
897
    }
898

    
899
    /**
900
     * Returns the number of operations that have been serviced by the node 
901
     * over time periods of one and 24 hours.
902
     * 
903
     * @param session - the Session object containing the credentials for the Subject
904
     * @param period - An ISO8601 compatible DateTime range specifying the time 
905
     *                 range for which to return operation statistics.
906
     * @param requestor - Limit to operations performed by given requestor identity.
907
     * @param event -  Enumerated value indicating the type of event being examined
908
     * @param format - Limit to events involving objects of the specified format
909
     * 
910
     * @return the desired log records
911
     * 
912
     * @throws InvalidToken
913
     * @throws ServiceFailure
914
     * @throws NotAuthorized
915
     * @throws InvalidRequest
916
     * @throws NotImplemented
917
     */
918
    public MonitorList getOperationStatistics(Session session, Date startTime, 
919
        Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
920
        throws NotImplemented, ServiceFailure, NotAuthorized, InsufficientResources, UnsupportedType {
921

    
922
        MonitorList monitorList = new MonitorList();
923

    
924
        try {
925

    
926
            // get log records first
927
            Log logs = getLogRecords(session, startTime, endTime, event, 0, null);
928

    
929
            // TODO: aggregate by day or hour -- needs clarification
930
            int count = 1;
931
            for (LogEntry logEntry : logs.getLogEntryList()) {
932
                Identifier pid = logEntry.getIdentifier();
933
                Date logDate = logEntry.getDateLogged();
934
                // if we are filtering by format
935
                if (formatId != null) {
936
                    SystemMetadata sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
937
                    if (!sysmeta.getFormatId().getValue().equals(formatId.getValue())) {
938
                        // does not match
939
                        continue;
940
                    }
941
                }
942
                MonitorInfo item = new MonitorInfo();
943
                item.setCount(count);
944
                item.setDate(new java.sql.Date(logDate.getTime()));
945
                monitorList.addMonitorInfo(item);
946

    
947
            }
948
        } catch (Exception e) {
949
            e.printStackTrace();
950
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
951
        }
952

    
953
        return monitorList;
954

    
955
    }
956

    
957
    /**
958
     * Low level “are you alive” operation. A valid ping response is 
959
     * indicated by a HTTP status of 200.
960
     * 
961
     * @return true if the service is alive
962
     * 
963
     * @throws InvalidToken
964
     * @throws ServiceFailure
965
     * @throws NotImplemented
966
     */
967
    @Override
968
    public boolean ping() 
969
        throws NotImplemented, ServiceFailure, InsufficientResources {
970

    
971
        // test if we can get a database connection
972
        boolean alive = false;
973
        int serialNumber = -1;
974
        DBConnection dbConn = null;
975
        try {
976
            dbConn = DBConnectionPool.getDBConnection("MNodeService.ping");
977
            serialNumber = dbConn.getCheckOutSerialNumber();
978
            alive = true;
979
        } catch (SQLException e) {
980
            return alive;
981
        } finally {
982
            // Return the database connection
983
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
984
        }
985

    
986
        return alive;
987
    }
988

    
989
    /**
990
     * A callback method used by a CN to indicate to a MN that it cannot 
991
     * complete synchronization of the science metadata identified by pid.  Log
992
     * the event in the metacat event log.
993
     * 
994
     * @param session
995
     * @param syncFailed
996
     * 
997
     * @throws ServiceFailure
998
     * @throws NotAuthorized
999
     * @throws NotImplemented
1000
     */
1001
    @Override
1002
    public void synchronizationFailed(Session session, SynchronizationFailed syncFailed) 
1003
        throws NotImplemented, ServiceFailure, NotAuthorized {
1004

    
1005
        String localId;
1006

    
1007
        try {
1008
            localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid());
1009
        } catch (McdbDocNotFoundException e) {
1010
            throw new ServiceFailure("2161", "The identifier specified by " + syncFailed.getPid() + " was not found on this node.");
1011

    
1012
        }
1013
        // TODO: update the CN URL below when the CNRead.SynchronizationFailed
1014
        // method is changed to include the URL as a parameter
1015
        logMetacat.debug("Synchronization for the object identified by " + syncFailed.getPid() + " failed from " + syncFailed.getNodeId()
1016
                + " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
1017
        // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
1018
        String principal = Constants.SUBJECT_PUBLIC;
1019
        if (session != null && session.getSubject() != null) {
1020
          principal = session.getSubject().getValue();
1021
        }
1022
        try {
1023
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "synchronization_failed");
1024
        } catch (Exception e) {
1025
            throw new ServiceFailure("2161", "Could not log the error for: " + syncFailed.getPid());
1026
    }
1027
        //EventLog.getInstance().log("CN URL WILL GO HERE", 
1028
        //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
1029

    
1030
    }
1031

    
1032
    /**
1033
     * Essentially a get() but with different logging behavior
1034
     */
1035
    @Override
1036
    public InputStream getReplica(Session session, Identifier pid) 
1037
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
1038

    
1039
        logMetacat.info("MNodeService.getReplica() called.");
1040

    
1041
        // cannot be called by public
1042
        if (session == null) {
1043
        	throw new InvalidToken("2183", "No session was provided.");
1044
        }
1045
        
1046
        logMetacat.info("MNodeService.getReplica() called with parameters: \n" +
1047
             "\tSession.Subject      = " + session.getSubject().getValue() + "\n" +
1048
             "\tIdentifier           = " + pid.getValue());
1049

    
1050
        InputStream inputStream = null; // bytes to be returned
1051
        handler = new MetacatHandler(new Timer());
1052
        boolean allowed = false;
1053
        String localId; // the metacat docid for the pid
1054

    
1055
        // get the local docid from Metacat
1056
        try {
1057
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1058
        } catch (McdbDocNotFoundException e) {
1059
            throw new ServiceFailure("2181", "The object specified by " + 
1060
                    pid.getValue() + " does not exist at this node.");
1061
            
1062
        }
1063

    
1064
        Subject targetNodeSubject = session.getSubject();
1065

    
1066
        // check for authorization to replicate, null session to act as this source MN
1067
        try {
1068
            allowed = D1Client.getCN().isNodeAuthorized(null, targetNodeSubject, pid);
1069
        } catch (InvalidToken e1) {
1070
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1071
                + e1.getMessage());
1072
            
1073
        } catch (NotFound e1) {
1074
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1075
                    + e1.getMessage());
1076

    
1077
        } catch (InvalidRequest e1) {
1078
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1079
                    + e1.getMessage());
1080

    
1081
        }
1082

    
1083
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1084
            " for identifier " + pid.getValue());
1085

    
1086
        // if the person is authorized, perform the read
1087
        if (allowed) {
1088
            try {
1089
                inputStream = handler.read(localId);
1090
            } catch (Exception e) {
1091
                throw new ServiceFailure("1020", "The object specified by " + 
1092
                    pid.getValue() + "could not be returned due to error: " + e.getMessage());
1093
            }
1094
        }
1095

    
1096
        // if we fail to set the input stream
1097
        if (inputStream == null) {
1098
            throw new ServiceFailure("2181", "The object specified by " + 
1099
                pid.getValue() + "does not exist at this node.");
1100
        }
1101

    
1102
        // log the replica event
1103
        String principal = null;
1104
        if (session.getSubject() != null) {
1105
            principal = session.getSubject().getValue();
1106
        }
1107
        EventLog.getInstance().log(request.getRemoteAddr(), 
1108
            request.getHeader("User-Agent"), principal, localId, "replicate");
1109

    
1110
        return inputStream;
1111
    }
1112

    
1113
    /**
1114
     * Set the access policy
1115
     */
1116
    @Deprecated
1117
    @Override
1118
    public boolean setAccessPolicy(Session session, Identifier pid,
1119
        AccessPolicy policy) 
1120
        throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1121
        NotImplemented, InvalidRequest {
1122
        
1123
        throw new NotImplemented("4401", "This method is deprecated for Member Nodes.");
1124
        
1125
    }
1126

    
1127
    /**
1128
     * A method to notify the Member Node that the authoritative copy of 
1129
     * system metadata on the Coordinating Nodes has changed.
1130
     * 
1131
     * @param session   Session information that contains the identity of the 
1132
     *                  calling user as retrieved from the X.509 certificate 
1133
     *                  which must be traceable to the CILogon service.
1134
     * @param serialVersion   The serialVersion of the system metadata
1135
     * @param dateSysMetaLastModified  The time stamp for when the system metadata was changed
1136
     * @throws NotImplemented
1137
     * @throws ServiceFailure
1138
     * @throws NotAuthorized
1139
     * @throws InvalidRequest
1140
     * @throws InvalidToken
1141
     */
1142
    public void systemMetadataChanged(Session session, Identifier pid,
1143
        long serialVersion, Date dateSysMetaLastModified) 
1144
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
1145
        InvalidToken {
1146
        
1147
        SystemMetadata currentLocalSysMeta = null;
1148
        SystemMetadata newSysMeta = null;
1149
        CNode cn = D1Client.getCN();
1150
        NodeList nodeList = null;
1151
        Subject callingSubject = null;
1152
        boolean allowed = false;
1153
        
1154
        // are we allowed to call this?
1155
        callingSubject = session.getSubject();
1156
        nodeList = cn.listNodes();
1157
        
1158
        for(Node node : nodeList.getNodeList()) {
1159
            // must be a CN
1160
            if ( node.getType().equals(NodeType.CN)) {
1161
               List<Subject> subjectList = node.getSubjectList();
1162
               // the calling subject must be in the subject list
1163
               if ( subjectList.contains(callingSubject)) {
1164
                   allowed = true;
1165
                   
1166
               }
1167
               
1168
            }
1169
        }
1170
        
1171
        if (!allowed ) {
1172
            String msg = "The subject identified by " + callingSubject.getValue() +
1173
              " is not authorized to call this service.";
1174
            throw new NotAuthorized("1331", msg);
1175
            
1176
        }
1177
        
1178
        // compare what we have locally to what is sent in the change notification
1179
        try {
1180
            currentLocalSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1181
             
1182
        } catch (RuntimeException e) {
1183
            String msg = "SystemMetadata for pid " + pid.getValue() +
1184
              " couldn't be updated because it couldn't be found locally: " +
1185
              e.getMessage();
1186
            logMetacat.error(msg);
1187
            ServiceFailure sf = new ServiceFailure("1333", msg);
1188
            sf.initCause(e);
1189
            throw sf; 
1190
        }
1191
        
1192
        if (currentLocalSysMeta.getSerialVersion().longValue() < serialVersion ) {
1193
            try {
1194
                newSysMeta = cn.getSystemMetadata(null, pid);
1195
            } catch (NotFound e) {
1196
                // huh? you just said you had it
1197
            	String msg = "On updating the local copy of system metadata " + 
1198
                "for pid " + pid.getValue() +", the CN reports it is not found." +
1199
                " The error message was: " + e.getMessage();
1200
                logMetacat.error(msg);
1201
                ServiceFailure sf = new ServiceFailure("1333", msg);
1202
                sf.initCause(e);
1203
                throw sf;
1204
            }
1205
            
1206
            // update the local copy of system metadata for the pid
1207
            try {
1208
                HazelcastService.getInstance().getSystemMetadataMap().put(newSysMeta.getIdentifier(), newSysMeta);
1209
                logMetacat.info("Updated local copy of system metadata for pid " +
1210
                    pid.getValue() + " after change notification from the CN.");
1211
                
1212
            } catch (RuntimeException e) {
1213
                String msg = "SystemMetadata for pid " + pid.getValue() +
1214
                  " couldn't be updated: " +
1215
                  e.getMessage();
1216
                logMetacat.error(msg);
1217
                ServiceFailure sf = new ServiceFailure("1333", msg);
1218
                sf.initCause(e);
1219
                throw sf;
1220
            }
1221
        }
1222
        
1223
    }
1224
    
1225
    /*
1226
     * Set the replication status for the object on the Coordinating Node
1227
     * 
1228
     * @param session - the session for the this target node
1229
     * @param pid - the identifier of the object being updated
1230
     * @param nodeId - the identifier of this target node
1231
     * @param status - the replication status to set
1232
     * @param failure - the exception to include, if any
1233
     */
1234
    private void setReplicationStatus(Session session, Identifier pid, 
1235
        NodeReference nodeId, ReplicationStatus status, BaseException failure) 
1236
        throws ServiceFailure, NotImplemented, NotAuthorized, 
1237
        InvalidRequest {
1238
        
1239
        // call the CN as the MN to set the replication status
1240
        try {
1241
            this.cn = D1Client.getCN();
1242
            this.cn.setReplicationStatus(session, pid, nodeId,
1243
                    status, failure);
1244
            
1245
        } catch (InvalidToken e) {
1246
            throw new ServiceFailure("2151",
1247
                    "Could not set the replication status on the CN (InvalidToken): " + 
1248
                    e.getMessage());
1249
            
1250
        } catch (NotFound e) {
1251
            throw new ServiceFailure("2151",
1252
                    "Could not set the replication status on the CN (NotFound): " + 
1253
                    e.getMessage());
1254
            
1255
        } catch (VersionMismatch e) {
1256
            throw new ServiceFailure("2151",
1257
                    "Could not set the replication status on the CN (VersionMismatch): " + 
1258
                    e.getMessage());
1259
            
1260
        }
1261

    
1262

    
1263
    }
1264
    
1265
}
(3-3/5)