Project

General

Profile

1 6179 cjones
/**
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 6228 cjones
import java.io.IOException;
27 6179 cjones
import java.io.InputStream;
28 6228 cjones
import java.security.NoSuchAlgorithmException;
29 6250 cjones
import java.sql.SQLException;
30 6525 leinfelder
import java.util.Calendar;
31 6179 cjones
import java.util.Date;
32 6250 cjones
import java.util.List;
33 6389 leinfelder
import java.util.Timer;
34 6179 cjones
35 6542 leinfelder
import javax.servlet.http.HttpServletRequest;
36
37 6258 cjones
import org.apache.commons.io.IOUtils;
38 6179 cjones
import org.apache.log4j.Logger;
39 6528 cjones
import org.dataone.client.CNode;
40 6332 leinfelder
import org.dataone.client.D1Client;
41
import org.dataone.client.MNode;
42 6552 leinfelder
import org.dataone.client.auth.CertificateManager;
43
import org.dataone.configuration.Settings;
44 6795 cjones
import org.dataone.service.exceptions.BaseException;
45 6179 cjones
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 6185 leinfelder
import org.dataone.service.exceptions.SynchronizationFailed;
55 6179 cjones
import org.dataone.service.exceptions.UnsupportedType;
56 6622 leinfelder
import org.dataone.service.exceptions.VersionMismatch;
57 6366 leinfelder
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 6573 cjones
import org.dataone.service.types.v1.AccessPolicy;
63 6366 leinfelder
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 6600 cjones
import org.dataone.service.types.v1.NodeList;
74 6366 leinfelder
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 6528 cjones
import org.dataone.service.types.v1.ReplicationStatus;
82 6366 leinfelder
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 6476 jones
import org.dataone.service.util.Constants;
91 6179 cjones
92 6250 cjones
import edu.ucsb.nceas.metacat.DocumentImpl;
93 6234 cjones
import edu.ucsb.nceas.metacat.EventLog;
94 6230 cjones
import edu.ucsb.nceas.metacat.IdentifierManager;
95 6234 cjones
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
96 6389 leinfelder
import edu.ucsb.nceas.metacat.MetacatHandler;
97 6250 cjones
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
98 6260 cjones
import edu.ucsb.nceas.metacat.database.DBConnection;
99
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
100 6648 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
101 6340 cjones
import edu.ucsb.nceas.metacat.properties.PropertyService;
102 6803 leinfelder
import edu.ucsb.nceas.metacat.shared.ServiceException;
103 6542 leinfelder
import edu.ucsb.nceas.metacat.util.SystemUtil;
104 6340 cjones
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
105 6230 cjones
106 6179 cjones
/**
107
 * Represents Metacat's implementation of the DataONE Member Node
108
 * service API. Methods implement the various MN* interfaces, and methods common
109
 * to both Member Node and Coordinating Node interfaces are found in the
110
 * D1NodeService base class.
111 6288 cjones
 *
112
 * Implements:
113
 * MNCore.ping()
114
 * MNCore.getLogRecords()
115
 * MNCore.getObjectStatistics()
116
 * MNCore.getOperationStatistics()
117
 * MNCore.getStatus()
118
 * MNCore.getCapabilities()
119
 * MNRead.get()
120
 * MNRead.getSystemMetadata()
121
 * MNRead.describe()
122
 * MNRead.getChecksum()
123
 * MNRead.listObjects()
124
 * MNRead.synchronizationFailed()
125
 * MNAuthorization.isAuthorized()
126
 * MNAuthorization.setAccessPolicy()
127
 * MNStorage.create()
128
 * MNStorage.update()
129
 * MNStorage.delete()
130
 * MNReplication.replicate()
131
 *
132 6179 cjones
 */
133 6599 cjones
public class MNodeService extends D1NodeService
134
    implements MNAuthorization, MNCore, MNRead, MNReplication, MNStorage {
135 6179 cjones
136 6475 jones
    /* the logger instance */
137
    private Logger logMetacat = null;
138 6795 cjones
139
    /* A reference to a remote Memeber Node */
140
    private MNode mn;
141
142
    /* A reference to a Coordinating Node */
143
    private CNode cn;
144 6241 cjones
145 6795 cjones
146 6475 jones
    /**
147
     * Singleton accessor to get an instance of MNodeService.
148
     *
149
     * @return instance - the instance of MNodeService
150
     */
151 6542 leinfelder
    public static MNodeService getInstance(HttpServletRequest request) {
152
        return new MNodeService(request);
153 6179 cjones
    }
154
155 6475 jones
    /**
156
     * Constructor, private for singleton access
157
     */
158 6542 leinfelder
    private MNodeService(HttpServletRequest request) {
159
        super(request);
160 6475 jones
        logMetacat = Logger.getLogger(MNodeService.class);
161 6552 leinfelder
162
        // set the Member Node certificate file location
163
        CertificateManager.getInstance().setCertificateLocation(Settings.getConfiguration().getString("D1Client.certificate.file"));
164 6310 cjones
    }
165 6475 jones
166
    /**
167
     * Deletes an object from the Member Node, where the object is either a
168
     * data object or a science metadata object.
169
     *
170
     * @param session - the Session object containing the credentials for the Subject
171
     * @param pid - The object identifier to be deleted
172
     *
173
     * @return pid - the identifier of the object used for the deletion
174
     *
175
     * @throws InvalidToken
176
     * @throws ServiceFailure
177
     * @throws NotAuthorized
178
     * @throws NotFound
179
     * @throws NotImplemented
180
     * @throws InvalidRequest
181
     */
182
    @Override
183
    public Identifier delete(Session session, Identifier pid)
184 6610 cjones
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
185 6475 jones
186
        String localId = null;
187
        boolean allowed = false;
188 6532 leinfelder
        String username = Constants.SUBJECT_PUBLIC;
189 6475 jones
        String[] groupnames = null;
190 6645 leinfelder
        if (session == null) {
191
        	throw new InvalidToken("1330", "No session has been provided");
192
        } else {
193 6475 jones
            username = session.getSubject().getValue();
194 6532 leinfelder
            if (session.getSubjectInfo() != null) {
195
                List<Group> groupList = session.getSubjectInfo().getGroupList();
196 6475 jones
                if (groupList != null) {
197
                    groupnames = new String[groupList.size()];
198
                    for (int i = 0; i > groupList.size(); i++) {
199
                        groupnames[i] = groupList.get(i).getGroupName();
200
                    }
201
                }
202
            }
203
        }
204
205
        // do we have a valid pid?
206
        if (pid == null || pid.getValue().trim().equals("")) {
207 6610 cjones
            throw new ServiceFailure("1350", "The provided identifier was invalid.");
208 6475 jones
        }
209
210
        // check for the existing identifier
211
        try {
212
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
213
        } catch (McdbDocNotFoundException e) {
214 6610 cjones
            throw new NotFound("1340", "The object with the provided " + "identifier was not found.");
215 6475 jones
        }
216
217
        // does the subject have DELETE (a D1 CHANGE_PERMISSION level) priveleges on the pid?
218
        allowed = isAuthorized(session, pid, Permission.CHANGE_PERMISSION);
219 6610 cjones
220 6475 jones
221
        if (allowed) {
222
            try {
223
                // delete the document
224
                DocumentImpl.delete(localId, username, groupnames, null);
225 6542 leinfelder
                EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), username, localId, Event.DELETE.xmlValue());
226 6475 jones
227 6787 leinfelder
                // archive it
228
                SystemMetadata sysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
229
                sysMeta.setArchived(true);
230
                HazelcastService.getInstance().getSystemMetadataMap().put(pid, sysMeta);
231
232 6648 leinfelder
                // remove the system metadata for it
233 6787 leinfelder
                //HazelcastService.getInstance().getSystemMetadataMap().remove(pid);
234 6648 leinfelder
235 6475 jones
            } catch (McdbDocNotFoundException e) {
236 6610 cjones
                throw new NotFound("1340", "The provided identifier was invalid.");
237 6475 jones
238
            } catch (SQLException e) {
239
                throw new ServiceFailure("1350", "There was a problem deleting the object." + "The error message was: " + e.getMessage());
240
241
            } catch (InsufficientKarmaException e) {
242
                throw new NotAuthorized("1320", "The provided identity does not have " + "permission to DELETE objects on the Member Node.");
243
244
            } catch (Exception e) { // for some reason DocumentImpl throws a general Exception
245
                throw new ServiceFailure("1350", "There was a problem deleting the object." + "The error message was: " + e.getMessage());
246
            }
247
248
        } else {
249
            throw new NotAuthorized("1320", "The provided identity does not have " + "permission to DELETE objects on the Member Node.");
250
        }
251
252
        return pid;
253 6250 cjones
    }
254
255 6475 jones
    /**
256
     * Updates an existing object by creating a new object identified by
257
     * newPid on the Member Node which explicitly obsoletes the object
258
     * identified by pid through appropriate changes to the SystemMetadata
259
     * of pid and newPid
260
     *
261
     * @param session - the Session object containing the credentials for the Subject
262
     * @param pid - The identifier of the object to be updated
263
     * @param object - the new object bytes
264
     * @param sysmeta - the new system metadata describing the object
265
     *
266
     * @return newPid - the identifier of the new object
267
     *
268
     * @throws InvalidToken
269
     * @throws ServiceFailure
270
     * @throws NotAuthorized
271
     * @throws NotFound
272
     * @throws NotImplemented
273
     * @throws IdentifierNotUnique
274
     * @throws UnsupportedType
275
     * @throws InsufficientResources
276
     * @throws InvalidSystemMetadata
277
     * @throws InvalidRequest
278
     */
279
    @Override
280 6575 cjones
    public Identifier update(Session session, Identifier pid, InputStream object,
281
        Identifier newPid, SystemMetadata sysmeta)
282
        throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
283
        UnsupportedType, InsufficientResources, NotFound,
284
        InvalidSystemMetadata, NotImplemented, InvalidRequest {
285 6250 cjones
286 6475 jones
        String localId = null;
287
        boolean allowed = false;
288
        boolean isScienceMetadata = false;
289 6645 leinfelder
290
        if (session == null) {
291
        	throw new InvalidToken("1210", "No session has been provided");
292
        }
293 6475 jones
        Subject subject = session.getSubject();
294
295
        // do we have a valid pid?
296
        if (pid == null || pid.getValue().trim().equals("")) {
297
            throw new InvalidRequest("1202", "The provided identifier was invalid.");
298 6575 cjones
299 6475 jones
        }
300
301
        // check for the existing identifier
302
        try {
303
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
304 6575 cjones
305 6475 jones
        } catch (McdbDocNotFoundException e) {
306 6575 cjones
            throw new InvalidRequest("1202", "The object with the provided " +
307
                "identifier was not found.");
308
309 6475 jones
        }
310 6518 leinfelder
311 6521 leinfelder
        // set the originating node
312
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
313
        sysmeta.setOriginMemberNode(originMemberNode);
314
315 6518 leinfelder
        // set the submitter to match the certificate
316
        sysmeta.setSubmitter(subject);
317 6525 leinfelder
        // set the dates
318
        Date now = Calendar.getInstance().getTime();
319 6575 cjones
        sysmeta.setDateSysMetadataModified(now);
320
        sysmeta.setDateUploaded(now);
321 6475 jones
322
        // does the subject have WRITE ( == update) priveleges on the pid?
323
        allowed = isAuthorized(session, pid, Permission.WRITE);
324
325
        if (allowed) {
326 6649 leinfelder
327
        	// check quality of SM
328
        	if (sysmeta.getObsoletedBy() != null) {
329
        		throw new InvalidSystemMetadata("1300", "Cannot include obsoletedBy when updating object");
330
        	}
331
        	if (sysmeta.getObsoletes() != null && !sysmeta.getObsoletes().getValue().equals(pid.getValue())) {
332
        		throw new InvalidSystemMetadata("1300", "The identifier provided in obsoletes does not match old Identifier");
333
        	}
334 6475 jones
335
            // get the existing system metadata for the object
336
            SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
337
338
            // add the newPid to the obsoletedBy list for the existing sysmeta
339
            existingSysMeta.setObsoletedBy(newPid);
340
341
            // then update the existing system metadata
342
            updateSystemMetadata(existingSysMeta);
343
344
            // prep the new system metadata, add pid to the affected lists
345
            sysmeta.setObsoletes(pid);
346
            //sysmeta.addDerivedFrom(pid);
347
348
            isScienceMetadata = isScienceMetadata(sysmeta);
349
350
            // do we have XML metadata or a data object?
351
            if (isScienceMetadata) {
352
353
                // update the science metadata XML document
354
                // TODO: handle non-XML metadata/data documents (like netCDF)
355
                // TODO: don't put objects into memory using stream to string
356
                String objectAsXML = "";
357
                try {
358
                    objectAsXML = IOUtils.toString(object, "UTF-8");
359
                    localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
360
                    // register the newPid and the generated localId
361
                    if (newPid != null) {
362
                        IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
363
364
                    }
365
366
                } catch (IOException e) {
367
                    String msg = "The Node is unable to create the object. " + "There was a problem converting the object to XML";
368
                    logMetacat.info(msg);
369
                    throw new ServiceFailure("1310", msg + ": " + e.getMessage());
370
371
                }
372
373
            } else {
374
375
                // update the data object
376
                localId = insertDataObject(object, newPid, session);
377
378
            }
379
380
            // and insert the new system metadata
381
            insertSystemMetadata(sysmeta);
382
383
            // log the update event
384 6542 leinfelder
            EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), subject.getValue(), localId, Event.UPDATE.toString());
385 6475 jones
386
        } else {
387
            throw new NotAuthorized("1200", "The provided identity does not have " + "permission to UPDATE the object identified by " + pid.getValue()
388
                    + " on the Member Node.");
389
        }
390
391
        return newPid;
392 6250 cjones
    }
393 6254 cjones
394 6475 jones
    public Identifier create(Session session, Identifier pid, InputStream object, SystemMetadata sysmeta) throws InvalidToken, ServiceFailure, NotAuthorized,
395
            IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, InvalidRequest {
396 6250 cjones
397 6575 cjones
      // check for null session
398 6530 leinfelder
        if (session == null) {
399 6575 cjones
          throw new InvalidToken("1110", "Session is required to WRITE to the Node.");
400 6530 leinfelder
        }
401 6518 leinfelder
        // set the submitter to match the certificate
402
        sysmeta.setSubmitter(session.getSubject());
403 6520 leinfelder
        // set the originating node
404
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
405
        sysmeta.setOriginMemberNode(originMemberNode);
406 6525 leinfelder
        // set the dates
407
        Date now = Calendar.getInstance().getTime();
408 6575 cjones
    sysmeta.setDateSysMetadataModified(now);
409
    sysmeta.setDateUploaded(now);
410 6518 leinfelder
        // call the shared impl
411 6475 jones
        return super.create(session, pid, object, sysmeta);
412
    }
413 6250 cjones
414 6475 jones
    /**
415
     * Called by a Coordinating Node to request that the Member Node create a
416
     * copy of the specified object by retrieving it from another Member
417
     * Node and storing it locally so that it can be made accessible to
418
     * the DataONE system.
419
     *
420
     * @param session - the Session object containing the credentials for the Subject
421
     * @param sysmeta - Copy of the CN held system metadata for the object
422
     * @param sourceNode - A reference to node from which the content should be
423
     *                     retrieved. The reference should be resolved by
424
     *                     checking the CN node registry.
425
     *
426
     * @return true if the replication succeeds
427
     *
428
     * @throws ServiceFailure
429
     * @throws NotAuthorized
430
     * @throws NotImplemented
431
     * @throws UnsupportedType
432
     * @throws InsufficientResources
433
     * @throws InvalidRequest
434
     */
435
    @Override
436 6786 cjones
    public boolean replicate(Session session, SystemMetadata sysmeta,
437
            NodeReference sourceNode) throws NotImplemented, ServiceFailure,
438
            NotAuthorized, InvalidRequest, InsufficientResources,
439
            UnsupportedType {
440
441
        logMetacat.info("MNodeService.replicate() called with parameters: \n"
442
                + "\tSession.Subject      = " + session.getSubject().getValue()
443
                + "\n" + "\tSystemMetadata       = " + sysmeta.toString()
444
                + "\n" + "\tSource NodeReference =" + sourceNode.getValue());
445
446 6475 jones
        boolean result = false;
447 6786 cjones
        String nodeIdStr = null;
448 6651 cjones
        NodeReference nodeId = null;
449 6786 cjones
450 6795 cjones
        // get the referenced object
451
        Identifier pid = sysmeta.getIdentifier();
452
453
        // get from the membernode
454
        // TODO: switch credentials for the server retrieval?
455
        this.mn = D1Client.getMN(sourceNode);
456
        this.cn = D1Client.getCN();
457
        InputStream object = null;
458
        Session thisNodeSession = null;
459
        SystemMetadata localSystemMetadata = null;
460
        BaseException failure = null;
461
462
        // TODO: check credentials
463
        // cannot be called by public
464
        if (session == null) {
465
            String msg = "No session was provided.";
466
            failure = new NotAuthorized("2152", msg);
467
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
468
            logMetacat.info(msg);
469
            return true;
470
        }
471
472
473 6651 cjones
        // get the local node id
474
        try {
475
            nodeIdStr = PropertyService.getProperty("dataone.memberNodeId");
476
            nodeId = new NodeReference();
477
            nodeId.setValue(nodeIdStr);
478 6786 cjones
479 6651 cjones
        } catch (PropertyNotFoundException e1) {
480 6795 cjones
            String msg = "Couldn't get dataone.memberNodeId property: " + e1.getMessage();
481
            failure = new ServiceFailure("2151", msg);
482
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
483
            logMetacat.error(msg);
484
            return true;
485 6786 cjones
486 6651 cjones
        }
487 6795 cjones
488 6475 jones
489
        try {
490 6786 cjones
            // do we already have a replica?
491
            try {
492
                localSystemMetadata =
493
                    HazelcastService.getInstance().getSystemMetadataMap().get(pid);
494
                object = getReplica(thisNodeSession, pid);
495
496
            } catch (RuntimeException e) {
497 6795 cjones
                String msg = "An error occurred getting system metadata for " +
498
                    pid.getValue();
499
                failure = new ServiceFailure("2151", msg);
500
                setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
501
                logMetacat.error(msg);
502
                return true;
503 6540 cjones
504 6786 cjones
            }
505
506
            // no local replica, get a replica
507
            if (localSystemMetadata == null) {
508
                // session should be null to use the default certificate
509
                // location set in the Certificate manager
510
                object = mn.getReplica(thisNodeSession, pid);
511
                logMetacat.info("MNodeService.replicate() called for identifier "
512
                                + pid.getValue());
513
514
            }
515
516 6795 cjones
        } catch (InvalidToken e) {
517
            String msg = "Could not retrieve object to replicate (InvalidToken): "+ e.getMessage();
518
            failure = new ServiceFailure("2151", msg);
519
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
520
            logMetacat.error(msg);
521
            throw new ServiceFailure("2151", msg);
522 6786 cjones
523 6475 jones
        } catch (NotFound e) {
524 6795 cjones
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
525
            failure = new ServiceFailure("2151", msg);
526
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
527
            logMetacat.error(msg);
528
            throw new ServiceFailure("2151", msg);
529 6786 cjones
530 6475 jones
        }
531
532 6693 leinfelder
        // verify checksum on the object, if supported
533
        if (object.markSupported()) {
534 6786 cjones
            Checksum givenChecksum = sysmeta.getChecksum();
535
            Checksum computedChecksum = null;
536
            try {
537
                computedChecksum = ChecksumUtil.checksum(object,
538
                        givenChecksum.getAlgorithm());
539
                object.reset();
540
            } catch (Exception e) {
541
                String msg = "Error computing checksum on replica: "
542
                        + e.getMessage();
543
                ServiceFailure sf = new ServiceFailure("2151", msg);
544
                sf.initCause(e);
545
                throw sf;
546
            }
547
            if (!givenChecksum.getValue().equals(computedChecksum)) {
548
                throw new ServiceFailure("2151",
549
                        "Computed checksum does not match declared checksum");
550
            }
551 6693 leinfelder
        }
552 6786 cjones
553 6475 jones
        // add it to local store
554
        Identifier retPid;
555
        try {
556 6786 cjones
            // skip the MN.create -- this mutates the system metadata and we
557
            // dont want it to
558 6552 leinfelder
            retPid = super.create(session, pid, object, sysmeta);
559 6475 jones
            result = (retPid.getValue().equals(pid.getValue()));
560 6795 cjones
561 6475 jones
        } catch (InvalidToken e) {
562 6795 cjones
            String msg = "Could not save object to local store (InvalidToken): " + e.getMessage();
563
            failure = new ServiceFailure("2151", msg);
564
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
565
            logMetacat.error(msg);
566
            throw new ServiceFailure("2151", msg);
567
568 6475 jones
        } catch (IdentifierNotUnique e) {
569 6795 cjones
            String msg = "Could not save object to local store (IdentifierNotUnique): " + e.getMessage();
570
            failure = new ServiceFailure("2151", msg);
571
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
572
            logMetacat.error(msg);
573
            throw new ServiceFailure("2151", msg);
574
575 6475 jones
        } catch (InvalidSystemMetadata e) {
576 6795 cjones
            String msg = "Could not save object to local store (InvalidSystemMetadata): " + e.getMessage();
577
            failure = new ServiceFailure("2151", msg);
578
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
579
            logMetacat.error(msg);
580
            throw new ServiceFailure("2151", msg);
581
582 6475 jones
        }
583
584 6795 cjones
        // finish by setting the replication status
585
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
586 6475 jones
        return result;
587
588 6250 cjones
    }
589 6179 cjones
590 6475 jones
    /**
591
     * Return the object identified by the given object identifier
592
     *
593
     * @param session - the Session object containing the credentials for the Subject
594
     * @param pid - the object identifier for the given object
595
     *
596
     * @return inputStream - the input stream of the given object
597
     *
598
     * @throws InvalidToken
599
     * @throws ServiceFailure
600
     * @throws NotAuthorized
601
     * @throws InvalidRequest
602
     * @throws NotImplemented
603
     */
604
    @Override
605 6610 cjones
    public InputStream get(Session session, Identifier pid)
606
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
607 6258 cjones
608 6475 jones
        return super.get(session, pid);
609 6258 cjones
610 6259 cjones
    }
611 6258 cjones
612 6475 jones
    /**
613
     * Returns a Checksum for the specified object using an accepted hashing algorithm
614
     *
615
     * @param session - the Session object containing the credentials for the Subject
616
     * @param pid - the object identifier for the given object
617
     * @param algorithm -  the name of an algorithm that will be used to compute
618
     *                     a checksum of the bytes of the object
619
     *
620
     * @return checksum - the checksum of the given object
621
     *
622
     * @throws InvalidToken
623
     * @throws ServiceFailure
624
     * @throws NotAuthorized
625
     * @throws NotFound
626
     * @throws InvalidRequest
627
     * @throws NotImplemented
628
     */
629
    @Override
630 6610 cjones
    public Checksum getChecksum(Session session, Identifier pid, String algorithm)
631
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
632
        InvalidRequest, NotImplemented {
633 6258 cjones
634 6475 jones
        Checksum checksum = null;
635
636
        InputStream inputStream = get(session, pid);
637
638 6259 cjones
        try {
639 6475 jones
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
640
641
        } catch (NoSuchAlgorithmException e) {
642
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
643
                    + e.getMessage());
644 6259 cjones
        } catch (IOException e) {
645 6475 jones
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
646
                    + e.getMessage());
647 6259 cjones
        }
648 6382 cjones
649 6475 jones
        if (checksum == null) {
650
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
651
        }
652 6258 cjones
653 6475 jones
        return checksum;
654 6259 cjones
    }
655 6179 cjones
656 6475 jones
    /**
657
     * Return the system metadata for a given object
658
     *
659
     * @param session - the Session object containing the credentials for the Subject
660
     * @param pid - the object identifier for the given object
661
     *
662
     * @return inputStream - the input stream of the given system metadata object
663
     *
664
     * @throws InvalidToken
665
     * @throws ServiceFailure
666
     * @throws NotAuthorized
667
     * @throws NotFound
668
     * @throws InvalidRequest
669
     * @throws NotImplemented
670
     */
671
    @Override
672 6610 cjones
    public SystemMetadata getSystemMetadata(Session session, Identifier pid)
673
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
674
        NotImplemented {
675 6341 leinfelder
676 6475 jones
        return super.getSystemMetadata(session, pid);
677
    }
678 6341 leinfelder
679 6475 jones
    /**
680
     * Retrieve the list of objects present on the MN that match the calling parameters
681
     *
682
     * @param session - the Session object containing the credentials for the Subject
683
     * @param startTime - Specifies the beginning of the time range from which
684
     *                    to return object (>=)
685
     * @param endTime - Specifies the beginning of the time range from which
686
     *                  to return object (>=)
687
     * @param objectFormat - Restrict results to the specified object format
688
     * @param replicaStatus - Indicates if replicated objects should be returned in the list
689
     * @param start - The zero-based index of the first value, relative to the
690
     *                first record of the resultset that matches the parameters.
691
     * @param count - The maximum number of entries that should be returned in
692
     *                the response. The Member Node may return less entries
693
     *                than specified in this value.
694
     *
695
     * @return objectList - the list of objects matching the criteria
696
     *
697
     * @throws InvalidToken
698
     * @throws ServiceFailure
699
     * @throws NotAuthorized
700
     * @throws InvalidRequest
701
     * @throws NotImplemented
702
     */
703
    @Override
704
    public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
705
            Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
706 6179 cjones
707 6475 jones
        ObjectList objectList = null;
708 6332 leinfelder
709 6475 jones
        try {
710
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
711
        } catch (Exception e) {
712
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
713
        }
714 6332 leinfelder
715 6475 jones
        return objectList;
716 6229 cjones
    }
717 6179 cjones
718 6475 jones
    /**
719 6476 jones
     * Return a description of the node's capabilities and services.
720 6475 jones
     *
721
     * @return node - the technical capabilities of the Member Node
722
     *
723
     * @throws ServiceFailure
724
     * @throws NotAuthorized
725
     * @throws InvalidRequest
726
     * @throws NotImplemented
727
     */
728
    @Override
729 6610 cjones
    public Node getCapabilities()
730
        throws NotImplemented, ServiceFailure {
731 6179 cjones
732 6475 jones
        String nodeName = null;
733
        String nodeId = null;
734 6492 jones
        String subject = null;
735 6475 jones
        String nodeDesc = null;
736 6476 jones
        String nodeTypeString = null;
737
        NodeType nodeType = null;
738 6475 jones
        String mnCoreServiceVersion = null;
739
        String mnReadServiceVersion = null;
740
        String mnAuthorizationServiceVersion = null;
741
        String mnStorageServiceVersion = null;
742
        String mnReplicationServiceVersion = null;
743 6179 cjones
744 6475 jones
        boolean nodeSynchronize = false;
745
        boolean nodeReplicate = false;
746
        boolean mnCoreServiceAvailable = false;
747
        boolean mnReadServiceAvailable = false;
748
        boolean mnAuthorizationServiceAvailable = false;
749
        boolean mnStorageServiceAvailable = false;
750
        boolean mnReplicationServiceAvailable = false;
751 6179 cjones
752 6475 jones
        try {
753
            // get the properties of the node based on configuration information
754 6492 jones
            nodeName = PropertyService.getProperty("dataone.nodeName");
755 6475 jones
            nodeId = PropertyService.getProperty("dataone.memberNodeId");
756 6492 jones
            subject = PropertyService.getProperty("dataone.subject");
757 6475 jones
            nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
758 6476 jones
            nodeTypeString = PropertyService.getProperty("dataone.nodeType");
759
            nodeType = NodeType.convert(nodeTypeString);
760 6475 jones
            nodeSynchronize = new Boolean(PropertyService.getProperty("dataone.nodeSynchronize")).booleanValue();
761
            nodeReplicate = new Boolean(PropertyService.getProperty("dataone.nodeReplicate")).booleanValue();
762
763
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
764
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
765
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
766
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
767
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
768
769
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
770
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
771
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
772
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
773
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
774
775 6476 jones
            // Set the properties of the node based on configuration information and
776
            // calls to current status methods
777 6542 leinfelder
            String serviceName = SystemUtil.getContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
778 6476 jones
            Node node = new Node();
779 6542 leinfelder
            node.setBaseURL(serviceName + "/" + nodeTypeString);
780 6476 jones
            node.setDescription(nodeDesc);
781 6475 jones
782 6476 jones
            // set the node's health information
783
            node.setState(NodeState.UP);
784
785
            // set the ping response to the current value
786
            Ping canPing = new Ping();
787
            canPing.setSuccess(false);
788
            try {
789 6803 leinfelder
            	Date pingDate = ping();
790
                canPing.setSuccess(pingDate != null);
791
            } catch (BaseException e) {
792 6476 jones
                e.printStackTrace();
793 6803 leinfelder
                // guess it can't be pinged
794 6476 jones
            }
795 6610 cjones
796 6476 jones
            node.setPing(canPing);
797 6475 jones
798 6476 jones
            NodeReference identifier = new NodeReference();
799
            identifier.setValue(nodeId);
800
            node.setIdentifier(identifier);
801 6492 jones
            Subject s = new Subject();
802
            s.setValue(subject);
803
            node.addSubject(s);
804 6476 jones
            node.setName(nodeName);
805
            node.setReplicate(nodeReplicate);
806
            node.setSynchronize(nodeSynchronize);
807 6475 jones
808 6476 jones
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
809
            Services services = new Services();
810 6475 jones
811 6476 jones
            Service sMNCore = new Service();
812
            sMNCore.setName("MNCore");
813
            sMNCore.setVersion(mnCoreServiceVersion);
814
            sMNCore.setAvailable(mnCoreServiceAvailable);
815 6475 jones
816 6476 jones
            Service sMNRead = new Service();
817
            sMNRead.setName("MNRead");
818
            sMNRead.setVersion(mnReadServiceVersion);
819
            sMNRead.setAvailable(mnReadServiceAvailable);
820 6475 jones
821 6476 jones
            Service sMNAuthorization = new Service();
822
            sMNAuthorization.setName("MNAuthorization");
823
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
824
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
825 6475 jones
826 6476 jones
            Service sMNStorage = new Service();
827
            sMNStorage.setName("MNStorage");
828
            sMNStorage.setVersion(mnStorageServiceVersion);
829
            sMNStorage.setAvailable(mnStorageServiceAvailable);
830 6475 jones
831 6476 jones
            Service sMNReplication = new Service();
832
            sMNReplication.setName("MNReplication");
833
            sMNReplication.setVersion(mnReplicationServiceVersion);
834
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
835 6475 jones
836 6476 jones
            services.addService(sMNRead);
837
            services.addService(sMNCore);
838
            services.addService(sMNAuthorization);
839
            services.addService(sMNStorage);
840
            services.addService(sMNReplication);
841
            node.setServices(services);
842 6475 jones
843 6476 jones
            // Set the schedule for synchronization
844
            Synchronization synchronization = new Synchronization();
845
            Schedule schedule = new Schedule();
846
            Date now = new Date();
847 6689 leinfelder
            schedule.setYear(PropertyService.getProperty("dataone.nodeSynchronization.schedule.year"));
848
            schedule.setMon(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mon"));
849
            schedule.setMday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mday"));
850
            schedule.setWday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.wday"));
851
            schedule.setHour(PropertyService.getProperty("dataone.nodeSynchronization.schedule.hour"));
852
            schedule.setMin(PropertyService.getProperty("dataone.nodeSynchronization.schedule.min"));
853
            schedule.setSec(PropertyService.getProperty("dataone.nodeSynchronization.schedule.sec"));
854 6476 jones
            synchronization.setSchedule(schedule);
855
            synchronization.setLastHarvested(now);
856
            synchronization.setLastCompleteHarvest(now);
857
            node.setSynchronization(synchronization);
858 6475 jones
859 6476 jones
            node.setType(nodeType);
860
            return node;
861 6475 jones
862 6476 jones
        } catch (PropertyNotFoundException pnfe) {
863
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
864
            logMetacat.error(msg);
865
            throw new ServiceFailure("2162", msg);
866
        }
867 6228 cjones
    }
868 6179 cjones
869 6475 jones
    /**
870
     * Returns the number of operations that have been serviced by the node
871
     * over time periods of one and 24 hours.
872
     *
873
     * @param session - the Session object containing the credentials for the Subject
874
     * @param period - An ISO8601 compatible DateTime range specifying the time
875
     *                 range for which to return operation statistics.
876
     * @param requestor - Limit to operations performed by given requestor identity.
877
     * @param event -  Enumerated value indicating the type of event being examined
878
     * @param format - Limit to events involving objects of the specified format
879
     *
880
     * @return the desired log records
881
     *
882
     * @throws InvalidToken
883
     * @throws ServiceFailure
884
     * @throws NotAuthorized
885
     * @throws InvalidRequest
886
     * @throws NotImplemented
887
     */
888 6610 cjones
    public MonitorList getOperationStatistics(Session session, Date startTime,
889
        Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
890
        throws NotImplemented, ServiceFailure, NotAuthorized, InsufficientResources, UnsupportedType {
891 6179 cjones
892 6475 jones
        MonitorList monitorList = new MonitorList();
893 6179 cjones
894 6475 jones
        try {
895 6179 cjones
896 6475 jones
            // get log records first
897
            Log logs = getLogRecords(session, startTime, endTime, event, 0, null);
898 6179 cjones
899 6475 jones
            // TODO: aggregate by day or hour -- needs clarification
900
            int count = 1;
901
            for (LogEntry logEntry : logs.getLogEntryList()) {
902
                Identifier pid = logEntry.getIdentifier();
903
                Date logDate = logEntry.getDateLogged();
904
                // if we are filtering by format
905
                if (formatId != null) {
906 6692 leinfelder
                    SystemMetadata sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
907 6561 leinfelder
                    if (!sysmeta.getFormatId().getValue().equals(formatId.getValue())) {
908 6475 jones
                        // does not match
909
                        continue;
910
                    }
911
                }
912
                MonitorInfo item = new MonitorInfo();
913
                item.setCount(count);
914
                item.setDate(new java.sql.Date(logDate.getTime()));
915
                monitorList.addMonitorInfo(item);
916 6179 cjones
917 6475 jones
            }
918
        } catch (Exception e) {
919
            e.printStackTrace();
920
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
921
        }
922 6345 cjones
923 6475 jones
        return monitorList;
924 6345 cjones
925 6340 cjones
    }
926
927 6475 jones
    /**
928
     * A callback method used by a CN to indicate to a MN that it cannot
929
     * complete synchronization of the science metadata identified by pid.  Log
930
     * the event in the metacat event log.
931
     *
932
     * @param session
933
     * @param syncFailed
934
     *
935
     * @throws ServiceFailure
936
     * @throws NotAuthorized
937
     * @throws NotImplemented
938
     */
939
    @Override
940 6610 cjones
    public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
941
        throws NotImplemented, ServiceFailure, NotAuthorized {
942 6179 cjones
943 6475 jones
        String localId;
944 6331 leinfelder
945 6475 jones
        try {
946
            localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid());
947
        } catch (McdbDocNotFoundException e) {
948 6610 cjones
            throw new ServiceFailure("2161", "The identifier specified by " + syncFailed.getPid() + " was not found on this node.");
949 6179 cjones
950 6475 jones
        }
951
        // TODO: update the CN URL below when the CNRead.SynchronizationFailed
952
        // method is changed to include the URL as a parameter
953
        logMetacat.debug("Synchronization for the object identified by " + syncFailed.getPid() + " failed from " + syncFailed.getNodeId()
954
                + " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
955
        // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
956 6532 leinfelder
        String principal = Constants.SUBJECT_PUBLIC;
957 6506 leinfelder
        if (session != null && session.getSubject() != null) {
958 6575 cjones
          principal = session.getSubject().getValue();
959 6506 leinfelder
        }
960
        try {
961 6575 cjones
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "synchronization_failed");
962 6506 leinfelder
        } catch (Exception e) {
963
            throw new ServiceFailure("2161", "Could not log the error for: " + syncFailed.getPid());
964 6575 cjones
    }
965 6475 jones
        //EventLog.getInstance().log("CN URL WILL GO HERE",
966
        //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
967 6179 cjones
968 6260 cjones
    }
969
970 6475 jones
    /**
971
     * Essentially a get() but with different logging behavior
972
     */
973
    @Override
974 6540 cjones
    public InputStream getReplica(Session session, Identifier pid)
975 6653 leinfelder
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
976 6179 cjones
977 6540 cjones
        logMetacat.info("MNodeService.getReplica() called.");
978
979 6653 leinfelder
        // cannot be called by public
980
        if (session == null) {
981
        	throw new InvalidToken("2183", "No session was provided.");
982
        }
983
984 6631 cjones
        logMetacat.info("MNodeService.getReplica() called with parameters: \n" +
985
             "\tSession.Subject      = " + session.getSubject().getValue() + "\n" +
986
             "\tIdentifier           = " + pid.getValue());
987
988 6475 jones
        InputStream inputStream = null; // bytes to be returned
989
        handler = new MetacatHandler(new Timer());
990
        boolean allowed = false;
991
        String localId; // the metacat docid for the pid
992 6179 cjones
993 6475 jones
        // get the local docid from Metacat
994
        try {
995
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
996
        } catch (McdbDocNotFoundException e) {
997 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
998
                    pid.getValue() + " does not exist at this node.");
999
1000 6475 jones
        }
1001 6234 cjones
1002 6552 leinfelder
        Subject targetNodeSubject = session.getSubject();
1003 6185 leinfelder
1004 6552 leinfelder
        // check for authorization to replicate, null session to act as this source MN
1005 6610 cjones
        try {
1006 6777 leinfelder
            allowed = D1Client.getCN().isNodeAuthorized(null, targetNodeSubject, pid);
1007 6610 cjones
        } catch (InvalidToken e1) {
1008
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1009
                + e1.getMessage());
1010
1011
        } catch (NotFound e1) {
1012
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1013
                    + e1.getMessage());
1014 6384 cjones
1015 6610 cjones
        } catch (InvalidRequest e1) {
1016
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1017
                    + e1.getMessage());
1018
1019
        }
1020
1021 6540 cjones
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1022
            " for identifier " + pid.getValue());
1023
1024 6475 jones
        // if the person is authorized, perform the read
1025
        if (allowed) {
1026
            try {
1027
                inputStream = handler.read(localId);
1028
            } catch (Exception e) {
1029 6610 cjones
                throw new ServiceFailure("1020", "The object specified by " +
1030
                    pid.getValue() + "could not be returned due to error: " + e.getMessage());
1031 6475 jones
            }
1032
        }
1033 6384 cjones
1034 6475 jones
        // if we fail to set the input stream
1035
        if (inputStream == null) {
1036 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
1037
                pid.getValue() + "does not exist at this node.");
1038 6475 jones
        }
1039
1040
        // log the replica event
1041
        String principal = null;
1042
        if (session.getSubject() != null) {
1043
            principal = session.getSubject().getValue();
1044
        }
1045 6576 cjones
        EventLog.getInstance().log(request.getRemoteAddr(),
1046
            request.getHeader("User-Agent"), principal, localId, "replicate");
1047 6475 jones
1048
        return inputStream;
1049
    }
1050
1051 6573 cjones
    /**
1052 6600 cjones
     * A method to notify the Member Node that the authoritative copy of
1053 6599 cjones
     * system metadata on the Coordinating Nodes has changed.
1054
     *
1055
     * @param session   Session information that contains the identity of the
1056
     *                  calling user as retrieved from the X.509 certificate
1057
     *                  which must be traceable to the CILogon service.
1058
     * @param serialVersion   The serialVersion of the system metadata
1059
     * @param dateSysMetaLastModified  The time stamp for when the system metadata was changed
1060
     * @throws NotImplemented
1061
     * @throws ServiceFailure
1062
     * @throws NotAuthorized
1063
     * @throws InvalidRequest
1064
     * @throws InvalidToken
1065
     */
1066
    public void systemMetadataChanged(Session session, Identifier pid,
1067
        long serialVersion, Date dateSysMetaLastModified)
1068
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
1069
        InvalidToken {
1070
1071 6600 cjones
        SystemMetadata currentLocalSysMeta = null;
1072
        SystemMetadata newSysMeta = null;
1073
        CNode cn = D1Client.getCN();
1074
        NodeList nodeList = null;
1075
        Subject callingSubject = null;
1076
        boolean allowed = false;
1077
1078
        // are we allowed to call this?
1079
        callingSubject = session.getSubject();
1080
        nodeList = cn.listNodes();
1081
1082
        for(Node node : nodeList.getNodeList()) {
1083
            // must be a CN
1084
            if ( node.getType().equals(NodeType.CN)) {
1085
               List<Subject> subjectList = node.getSubjectList();
1086
               // the calling subject must be in the subject list
1087
               if ( subjectList.contains(callingSubject)) {
1088
                   allowed = true;
1089
1090
               }
1091
1092
            }
1093
        }
1094
1095
        if (!allowed ) {
1096
            String msg = "The subject identified by " + callingSubject.getValue() +
1097
              " is not authorized to call this service.";
1098
            throw new NotAuthorized("1331", msg);
1099
1100
        }
1101
1102
        // compare what we have locally to what is sent in the change notification
1103
        try {
1104 6692 leinfelder
            currentLocalSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1105
1106
        } catch (RuntimeException e) {
1107 6600 cjones
            String msg = "SystemMetadata for pid " + pid.getValue() +
1108 6692 leinfelder
              " couldn't be updated because it couldn't be found locally: " +
1109 6600 cjones
              e.getMessage();
1110 6692 leinfelder
            logMetacat.error(msg);
1111
            ServiceFailure sf = new ServiceFailure("1333", msg);
1112
            sf.initCause(e);
1113
            throw sf;
1114 6600 cjones
        }
1115
1116
        if (currentLocalSysMeta.getSerialVersion().longValue() < serialVersion ) {
1117
            try {
1118
                newSysMeta = cn.getSystemMetadata(null, pid);
1119
            } catch (NotFound e) {
1120
                // huh? you just said you had it
1121 6692 leinfelder
            	String msg = "On updating the local copy of system metadata " +
1122
                "for pid " + pid.getValue() +", the CN reports it is not found." +
1123
                " The error message was: " + e.getMessage();
1124
                logMetacat.error(msg);
1125
                ServiceFailure sf = new ServiceFailure("1333", msg);
1126
                sf.initCause(e);
1127
                throw sf;
1128 6600 cjones
            }
1129 6692 leinfelder
1130 6600 cjones
            // update the local copy of system metadata for the pid
1131
            try {
1132 6692 leinfelder
                HazelcastService.getInstance().getSystemMetadataMap().put(newSysMeta.getIdentifier(), newSysMeta);
1133 6600 cjones
                logMetacat.info("Updated local copy of system metadata for pid " +
1134
                    pid.getValue() + " after change notification from the CN.");
1135
1136 6692 leinfelder
            } catch (RuntimeException e) {
1137 6600 cjones
                String msg = "SystemMetadata for pid " + pid.getValue() +
1138 6692 leinfelder
                  " couldn't be updated: " +
1139 6600 cjones
                  e.getMessage();
1140 6692 leinfelder
                logMetacat.error(msg);
1141
                ServiceFailure sf = new ServiceFailure("1333", msg);
1142
                sf.initCause(e);
1143
                throw sf;
1144 6600 cjones
            }
1145
        }
1146
1147 6599 cjones
    }
1148
1149 6795 cjones
    /*
1150
     * Set the replication status for the object on the Coordinating Node
1151
     *
1152
     * @param session - the session for the this target node
1153
     * @param pid - the identifier of the object being updated
1154
     * @param nodeId - the identifier of this target node
1155
     * @param status - the replication status to set
1156
     * @param failure - the exception to include, if any
1157
     */
1158
    private void setReplicationStatus(Session session, Identifier pid,
1159
        NodeReference nodeId, ReplicationStatus status, BaseException failure)
1160
        throws ServiceFailure, NotImplemented, NotAuthorized,
1161
        InvalidRequest {
1162
1163
        // call the CN as the MN to set the replication status
1164
        try {
1165
            this.cn = D1Client.getCN();
1166
            this.cn.setReplicationStatus(session, pid, nodeId,
1167
                    status, failure);
1168
1169
        } catch (InvalidToken e) {
1170
            throw new ServiceFailure("2151",
1171
                    "Could not set the replication status on the CN (InvalidToken): " +
1172
                    e.getMessage());
1173
1174
        } catch (NotFound e) {
1175
            throw new ServiceFailure("2151",
1176
                    "Could not set the replication status on the CN (NotFound): " +
1177
                    e.getMessage());
1178
1179
        } catch (VersionMismatch e) {
1180
            throw new ServiceFailure("2151",
1181
                    "Could not set the replication status on the CN (VersionMismatch): " +
1182
                    e.getMessage());
1183
1184
        }
1185
1186
1187
    }
1188
1189 6179 cjones
}