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