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