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