Project

General

Profile

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