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