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