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