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
        logMetacat.info("MNodeService.replicate() called with parameters: \n"
444
                + "\tSession.Subject      = " + session.getSubject().getValue()
445
                + "\n" + "\tSystemMetadata       = " + sysmeta.toString()
446
                + "\n" + "\tSource NodeReference =" + sourceNode.getValue());
447
448 6475 jones
        boolean result = false;
449 6786 cjones
        String nodeIdStr = null;
450 6651 cjones
        NodeReference nodeId = null;
451 6786 cjones
452 6795 cjones
        // get the referenced object
453
        Identifier pid = sysmeta.getIdentifier();
454
455
        // get from the membernode
456
        // TODO: switch credentials for the server retrieval?
457
        this.mn = D1Client.getMN(sourceNode);
458
        this.cn = D1Client.getCN();
459
        InputStream object = null;
460
        Session thisNodeSession = null;
461
        SystemMetadata localSystemMetadata = null;
462
        BaseException failure = null;
463
464
        // TODO: check credentials
465
        // cannot be called by public
466
        if (session == null) {
467
            String msg = "No session was provided.";
468
            failure = new NotAuthorized("2152", msg);
469
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
470
            logMetacat.info(msg);
471
            return true;
472
        }
473
474
475 6651 cjones
        // get the local node id
476
        try {
477
            nodeIdStr = PropertyService.getProperty("dataone.memberNodeId");
478
            nodeId = new NodeReference();
479
            nodeId.setValue(nodeIdStr);
480 6786 cjones
481 6651 cjones
        } catch (PropertyNotFoundException e1) {
482 6795 cjones
            String msg = "Couldn't get dataone.memberNodeId property: " + e1.getMessage();
483
            failure = new ServiceFailure("2151", msg);
484
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
485
            logMetacat.error(msg);
486
            return true;
487 6786 cjones
488 6651 cjones
        }
489 6795 cjones
490 6475 jones
491
        try {
492 6786 cjones
            // do we already have a replica?
493
            try {
494
                localSystemMetadata =
495
                    HazelcastService.getInstance().getSystemMetadataMap().get(pid);
496 6817 cjones
                String localId = IdentifierManager.getInstance().getLocalId(sysmeta.getIdentifier().getValue());
497
                object = MetacatHandler.read(localId);
498
499 6786 cjones
            } catch (RuntimeException e) {
500 6795 cjones
                String msg = "An error occurred getting system metadata for " +
501
                    pid.getValue();
502
                failure = new ServiceFailure("2151", msg);
503
                setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
504
                logMetacat.error(msg);
505
                return true;
506 6540 cjones
507 6817 cjones
            } catch (McdbDocNotFoundException e) {
508
                // TODO Auto-generated catch block
509
                e.printStackTrace();
510
511
            } catch (PropertyNotFoundException e) {
512
                // TODO Auto-generated catch block
513
                e.printStackTrace();
514
515
            } catch (ParseLSIDException e) {
516
                // TODO Auto-generated catch block
517
                e.printStackTrace();
518
519
            } catch (McdbException e) {
520
                // TODO Auto-generated catch block
521
                e.printStackTrace();
522
523
            } catch (SQLException e) {
524
                // TODO Auto-generated catch block
525
                e.printStackTrace();
526
            } catch (ClassNotFoundException e) {
527
                // TODO Auto-generated catch block
528
                e.printStackTrace();
529
            } catch (IOException e) {
530
                // TODO Auto-generated catch block
531
                e.printStackTrace();
532 6786 cjones
            }
533
534
            // no local replica, get a replica
535
            if (localSystemMetadata == null) {
536
                // session should be null to use the default certificate
537
                // location set in the Certificate manager
538
                object = mn.getReplica(thisNodeSession, pid);
539
                logMetacat.info("MNodeService.replicate() called for identifier "
540
                                + pid.getValue());
541
542
            }
543
544 6795 cjones
        } catch (InvalidToken e) {
545
            String msg = "Could not retrieve object to replicate (InvalidToken): "+ e.getMessage();
546
            failure = new ServiceFailure("2151", msg);
547
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
548
            logMetacat.error(msg);
549
            throw new ServiceFailure("2151", msg);
550 6786 cjones
551 6475 jones
        } catch (NotFound e) {
552 6795 cjones
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
553
            failure = new ServiceFailure("2151", msg);
554
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
555
            logMetacat.error(msg);
556
            throw new ServiceFailure("2151", msg);
557 6786 cjones
558 6475 jones
        }
559
560 6693 leinfelder
        // verify checksum on the object, if supported
561
        if (object.markSupported()) {
562 6786 cjones
            Checksum givenChecksum = sysmeta.getChecksum();
563
            Checksum computedChecksum = null;
564
            try {
565
                computedChecksum = ChecksumUtil.checksum(object,
566
                        givenChecksum.getAlgorithm());
567
                object.reset();
568
            } catch (Exception e) {
569
                String msg = "Error computing checksum on replica: "
570
                        + e.getMessage();
571
                ServiceFailure sf = new ServiceFailure("2151", msg);
572
                sf.initCause(e);
573
                throw sf;
574
            }
575
            if (!givenChecksum.getValue().equals(computedChecksum)) {
576
                throw new ServiceFailure("2151",
577
                        "Computed checksum does not match declared checksum");
578
            }
579 6693 leinfelder
        }
580 6786 cjones
581 6475 jones
        // add it to local store
582
        Identifier retPid;
583
        try {
584 6786 cjones
            // skip the MN.create -- this mutates the system metadata and we
585
            // dont want it to
586 6552 leinfelder
            retPid = super.create(session, pid, object, sysmeta);
587 6475 jones
            result = (retPid.getValue().equals(pid.getValue()));
588 6795 cjones
589 6475 jones
        } catch (InvalidToken e) {
590 6795 cjones
            String msg = "Could not save object to local store (InvalidToken): " + e.getMessage();
591
            failure = new ServiceFailure("2151", msg);
592
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
593
            logMetacat.error(msg);
594
            throw new ServiceFailure("2151", msg);
595
596 6475 jones
        } catch (IdentifierNotUnique e) {
597 6795 cjones
            String msg = "Could not save object to local store (IdentifierNotUnique): " + e.getMessage();
598
            failure = new ServiceFailure("2151", msg);
599
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
600
            logMetacat.error(msg);
601
            throw new ServiceFailure("2151", msg);
602
603 6475 jones
        } catch (InvalidSystemMetadata e) {
604 6795 cjones
            String msg = "Could not save object to local store (InvalidSystemMetadata): " + e.getMessage();
605
            failure = new ServiceFailure("2151", msg);
606
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
607
            logMetacat.error(msg);
608
            throw new ServiceFailure("2151", msg);
609
610 6475 jones
        }
611
612 6795 cjones
        // finish by setting the replication status
613
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
614 6475 jones
        return result;
615
616 6250 cjones
    }
617 6179 cjones
618 6475 jones
    /**
619
     * Return the object identified by the given object identifier
620
     *
621
     * @param session - the Session object containing the credentials for the Subject
622
     * @param pid - the object identifier for the given object
623
     *
624
     * @return inputStream - the input stream of the given object
625
     *
626
     * @throws InvalidToken
627
     * @throws ServiceFailure
628
     * @throws NotAuthorized
629
     * @throws InvalidRequest
630
     * @throws NotImplemented
631
     */
632
    @Override
633 6610 cjones
    public InputStream get(Session session, Identifier pid)
634
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
635 6258 cjones
636 6475 jones
        return super.get(session, pid);
637 6258 cjones
638 6259 cjones
    }
639 6258 cjones
640 6475 jones
    /**
641
     * Returns a Checksum for the specified object using an accepted hashing algorithm
642
     *
643
     * @param session - the Session object containing the credentials for the Subject
644
     * @param pid - the object identifier for the given object
645
     * @param algorithm -  the name of an algorithm that will be used to compute
646
     *                     a checksum of the bytes of the object
647
     *
648
     * @return checksum - the checksum of the given object
649
     *
650
     * @throws InvalidToken
651
     * @throws ServiceFailure
652
     * @throws NotAuthorized
653
     * @throws NotFound
654
     * @throws InvalidRequest
655
     * @throws NotImplemented
656
     */
657
    @Override
658 6610 cjones
    public Checksum getChecksum(Session session, Identifier pid, String algorithm)
659
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
660
        InvalidRequest, NotImplemented {
661 6258 cjones
662 6475 jones
        Checksum checksum = null;
663
664
        InputStream inputStream = get(session, pid);
665
666 6259 cjones
        try {
667 6475 jones
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
668
669
        } catch (NoSuchAlgorithmException e) {
670
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
671
                    + e.getMessage());
672 6259 cjones
        } catch (IOException e) {
673 6475 jones
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
674
                    + e.getMessage());
675 6259 cjones
        }
676 6382 cjones
677 6475 jones
        if (checksum == null) {
678
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
679
        }
680 6258 cjones
681 6475 jones
        return checksum;
682 6259 cjones
    }
683 6179 cjones
684 6475 jones
    /**
685
     * Return the system metadata for a given object
686
     *
687
     * @param session - the Session object containing the credentials for the Subject
688
     * @param pid - the object identifier for the given object
689
     *
690
     * @return inputStream - the input stream of the given system metadata object
691
     *
692
     * @throws InvalidToken
693
     * @throws ServiceFailure
694
     * @throws NotAuthorized
695
     * @throws NotFound
696
     * @throws InvalidRequest
697
     * @throws NotImplemented
698
     */
699
    @Override
700 6610 cjones
    public SystemMetadata getSystemMetadata(Session session, Identifier pid)
701
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
702
        NotImplemented {
703 6341 leinfelder
704 6475 jones
        return super.getSystemMetadata(session, pid);
705
    }
706 6341 leinfelder
707 6475 jones
    /**
708
     * Retrieve the list of objects present on the MN that match the calling parameters
709
     *
710
     * @param session - the Session object containing the credentials for the Subject
711
     * @param startTime - Specifies the beginning of the time range from which
712
     *                    to return object (>=)
713
     * @param endTime - Specifies the beginning of the time range from which
714
     *                  to return object (>=)
715
     * @param objectFormat - Restrict results to the specified object format
716
     * @param replicaStatus - Indicates if replicated objects should be returned in the list
717
     * @param start - The zero-based index of the first value, relative to the
718
     *                first record of the resultset that matches the parameters.
719
     * @param count - The maximum number of entries that should be returned in
720
     *                the response. The Member Node may return less entries
721
     *                than specified in this value.
722
     *
723
     * @return objectList - the list of objects matching the criteria
724
     *
725
     * @throws InvalidToken
726
     * @throws ServiceFailure
727
     * @throws NotAuthorized
728
     * @throws InvalidRequest
729
     * @throws NotImplemented
730
     */
731
    @Override
732
    public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
733
            Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
734 6179 cjones
735 6475 jones
        ObjectList objectList = null;
736 6332 leinfelder
737 6475 jones
        try {
738
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
739
        } catch (Exception e) {
740
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
741
        }
742 6332 leinfelder
743 6475 jones
        return objectList;
744 6229 cjones
    }
745 6179 cjones
746 6475 jones
    /**
747 6476 jones
     * Return a description of the node's capabilities and services.
748 6475 jones
     *
749
     * @return node - the technical capabilities of the Member Node
750
     *
751
     * @throws ServiceFailure
752
     * @throws NotAuthorized
753
     * @throws InvalidRequest
754
     * @throws NotImplemented
755
     */
756
    @Override
757 6610 cjones
    public Node getCapabilities()
758
        throws NotImplemented, ServiceFailure {
759 6179 cjones
760 6475 jones
        String nodeName = null;
761
        String nodeId = null;
762 6492 jones
        String subject = null;
763 6475 jones
        String nodeDesc = null;
764 6476 jones
        String nodeTypeString = null;
765
        NodeType nodeType = null;
766 6475 jones
        String mnCoreServiceVersion = null;
767
        String mnReadServiceVersion = null;
768
        String mnAuthorizationServiceVersion = null;
769
        String mnStorageServiceVersion = null;
770
        String mnReplicationServiceVersion = null;
771 6179 cjones
772 6475 jones
        boolean nodeSynchronize = false;
773
        boolean nodeReplicate = false;
774
        boolean mnCoreServiceAvailable = false;
775
        boolean mnReadServiceAvailable = false;
776
        boolean mnAuthorizationServiceAvailable = false;
777
        boolean mnStorageServiceAvailable = false;
778
        boolean mnReplicationServiceAvailable = false;
779 6179 cjones
780 6475 jones
        try {
781
            // get the properties of the node based on configuration information
782 6492 jones
            nodeName = PropertyService.getProperty("dataone.nodeName");
783 6475 jones
            nodeId = PropertyService.getProperty("dataone.memberNodeId");
784 6492 jones
            subject = PropertyService.getProperty("dataone.subject");
785 6475 jones
            nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
786 6476 jones
            nodeTypeString = PropertyService.getProperty("dataone.nodeType");
787
            nodeType = NodeType.convert(nodeTypeString);
788 6475 jones
            nodeSynchronize = new Boolean(PropertyService.getProperty("dataone.nodeSynchronize")).booleanValue();
789
            nodeReplicate = new Boolean(PropertyService.getProperty("dataone.nodeReplicate")).booleanValue();
790
791
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
792
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
793
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
794
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
795
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
796
797
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
798
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
799
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
800
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
801
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
802
803 6476 jones
            // Set the properties of the node based on configuration information and
804
            // calls to current status methods
805 6542 leinfelder
            String serviceName = SystemUtil.getContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
806 6476 jones
            Node node = new Node();
807 6542 leinfelder
            node.setBaseURL(serviceName + "/" + nodeTypeString);
808 6476 jones
            node.setDescription(nodeDesc);
809 6475 jones
810 6476 jones
            // set the node's health information
811
            node.setState(NodeState.UP);
812
813
            // set the ping response to the current value
814
            Ping canPing = new Ping();
815
            canPing.setSuccess(false);
816
            try {
817 6803 leinfelder
            	Date pingDate = ping();
818
                canPing.setSuccess(pingDate != null);
819
            } catch (BaseException e) {
820 6476 jones
                e.printStackTrace();
821 6803 leinfelder
                // guess it can't be pinged
822 6476 jones
            }
823 6610 cjones
824 6476 jones
            node.setPing(canPing);
825 6475 jones
826 6476 jones
            NodeReference identifier = new NodeReference();
827
            identifier.setValue(nodeId);
828
            node.setIdentifier(identifier);
829 6492 jones
            Subject s = new Subject();
830
            s.setValue(subject);
831
            node.addSubject(s);
832 6476 jones
            node.setName(nodeName);
833
            node.setReplicate(nodeReplicate);
834
            node.setSynchronize(nodeSynchronize);
835 6475 jones
836 6476 jones
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
837
            Services services = new Services();
838 6475 jones
839 6476 jones
            Service sMNCore = new Service();
840
            sMNCore.setName("MNCore");
841
            sMNCore.setVersion(mnCoreServiceVersion);
842
            sMNCore.setAvailable(mnCoreServiceAvailable);
843 6475 jones
844 6476 jones
            Service sMNRead = new Service();
845
            sMNRead.setName("MNRead");
846
            sMNRead.setVersion(mnReadServiceVersion);
847
            sMNRead.setAvailable(mnReadServiceAvailable);
848 6475 jones
849 6476 jones
            Service sMNAuthorization = new Service();
850
            sMNAuthorization.setName("MNAuthorization");
851
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
852
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
853 6475 jones
854 6476 jones
            Service sMNStorage = new Service();
855
            sMNStorage.setName("MNStorage");
856
            sMNStorage.setVersion(mnStorageServiceVersion);
857
            sMNStorage.setAvailable(mnStorageServiceAvailable);
858 6475 jones
859 6476 jones
            Service sMNReplication = new Service();
860
            sMNReplication.setName("MNReplication");
861
            sMNReplication.setVersion(mnReplicationServiceVersion);
862
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
863 6475 jones
864 6476 jones
            services.addService(sMNRead);
865
            services.addService(sMNCore);
866
            services.addService(sMNAuthorization);
867
            services.addService(sMNStorage);
868
            services.addService(sMNReplication);
869
            node.setServices(services);
870 6475 jones
871 6476 jones
            // Set the schedule for synchronization
872
            Synchronization synchronization = new Synchronization();
873
            Schedule schedule = new Schedule();
874
            Date now = new Date();
875 6689 leinfelder
            schedule.setYear(PropertyService.getProperty("dataone.nodeSynchronization.schedule.year"));
876
            schedule.setMon(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mon"));
877
            schedule.setMday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mday"));
878
            schedule.setWday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.wday"));
879
            schedule.setHour(PropertyService.getProperty("dataone.nodeSynchronization.schedule.hour"));
880
            schedule.setMin(PropertyService.getProperty("dataone.nodeSynchronization.schedule.min"));
881
            schedule.setSec(PropertyService.getProperty("dataone.nodeSynchronization.schedule.sec"));
882 6476 jones
            synchronization.setSchedule(schedule);
883
            synchronization.setLastHarvested(now);
884
            synchronization.setLastCompleteHarvest(now);
885
            node.setSynchronization(synchronization);
886 6475 jones
887 6476 jones
            node.setType(nodeType);
888
            return node;
889 6475 jones
890 6476 jones
        } catch (PropertyNotFoundException pnfe) {
891
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
892
            logMetacat.error(msg);
893
            throw new ServiceFailure("2162", msg);
894
        }
895 6228 cjones
    }
896 6179 cjones
897 6475 jones
    /**
898
     * Returns the number of operations that have been serviced by the node
899
     * over time periods of one and 24 hours.
900
     *
901
     * @param session - the Session object containing the credentials for the Subject
902
     * @param period - An ISO8601 compatible DateTime range specifying the time
903
     *                 range for which to return operation statistics.
904
     * @param requestor - Limit to operations performed by given requestor identity.
905
     * @param event -  Enumerated value indicating the type of event being examined
906
     * @param format - Limit to events involving objects of the specified format
907
     *
908
     * @return the desired log records
909
     *
910
     * @throws InvalidToken
911
     * @throws ServiceFailure
912
     * @throws NotAuthorized
913
     * @throws InvalidRequest
914
     * @throws NotImplemented
915
     */
916 6610 cjones
    public MonitorList getOperationStatistics(Session session, Date startTime,
917
        Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
918
        throws NotImplemented, ServiceFailure, NotAuthorized, InsufficientResources, UnsupportedType {
919 6179 cjones
920 6475 jones
        MonitorList monitorList = new MonitorList();
921 6179 cjones
922 6475 jones
        try {
923 6179 cjones
924 6475 jones
            // get log records first
925
            Log logs = getLogRecords(session, startTime, endTime, event, 0, null);
926 6179 cjones
927 6475 jones
            // TODO: aggregate by day or hour -- needs clarification
928
            int count = 1;
929
            for (LogEntry logEntry : logs.getLogEntryList()) {
930
                Identifier pid = logEntry.getIdentifier();
931
                Date logDate = logEntry.getDateLogged();
932
                // if we are filtering by format
933
                if (formatId != null) {
934 6692 leinfelder
                    SystemMetadata sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
935 6561 leinfelder
                    if (!sysmeta.getFormatId().getValue().equals(formatId.getValue())) {
936 6475 jones
                        // does not match
937
                        continue;
938
                    }
939
                }
940
                MonitorInfo item = new MonitorInfo();
941
                item.setCount(count);
942
                item.setDate(new java.sql.Date(logDate.getTime()));
943
                monitorList.addMonitorInfo(item);
944 6179 cjones
945 6475 jones
            }
946
        } catch (Exception e) {
947
            e.printStackTrace();
948
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
949
        }
950 6345 cjones
951 6475 jones
        return monitorList;
952 6345 cjones
953 6340 cjones
    }
954
955 6475 jones
    /**
956
     * A callback method used by a CN to indicate to a MN that it cannot
957
     * complete synchronization of the science metadata identified by pid.  Log
958
     * the event in the metacat event log.
959
     *
960
     * @param session
961
     * @param syncFailed
962
     *
963
     * @throws ServiceFailure
964
     * @throws NotAuthorized
965
     * @throws NotImplemented
966
     */
967
    @Override
968 6610 cjones
    public void synchronizationFailed(Session session, SynchronizationFailed syncFailed)
969
        throws NotImplemented, ServiceFailure, NotAuthorized {
970 6179 cjones
971 6475 jones
        String localId;
972 6331 leinfelder
973 6475 jones
        try {
974
            localId = IdentifierManager.getInstance().getLocalId(syncFailed.getPid());
975
        } catch (McdbDocNotFoundException e) {
976 6610 cjones
            throw new ServiceFailure("2161", "The identifier specified by " + syncFailed.getPid() + " was not found on this node.");
977 6179 cjones
978 6475 jones
        }
979
        // TODO: update the CN URL below when the CNRead.SynchronizationFailed
980
        // method is changed to include the URL as a parameter
981
        logMetacat.debug("Synchronization for the object identified by " + syncFailed.getPid() + " failed from " + syncFailed.getNodeId()
982
                + " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
983
        // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
984 6532 leinfelder
        String principal = Constants.SUBJECT_PUBLIC;
985 6506 leinfelder
        if (session != null && session.getSubject() != null) {
986 6575 cjones
          principal = session.getSubject().getValue();
987 6506 leinfelder
        }
988
        try {
989 6575 cjones
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "synchronization_failed");
990 6506 leinfelder
        } catch (Exception e) {
991
            throw new ServiceFailure("2161", "Could not log the error for: " + syncFailed.getPid());
992 6575 cjones
    }
993 6475 jones
        //EventLog.getInstance().log("CN URL WILL GO HERE",
994
        //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
995 6179 cjones
996 6260 cjones
    }
997
998 6475 jones
    /**
999
     * Essentially a get() but with different logging behavior
1000
     */
1001
    @Override
1002 6540 cjones
    public InputStream getReplica(Session session, Identifier pid)
1003 6653 leinfelder
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
1004 6179 cjones
1005 6540 cjones
        logMetacat.info("MNodeService.getReplica() called.");
1006
1007 6653 leinfelder
        // cannot be called by public
1008
        if (session == null) {
1009
        	throw new InvalidToken("2183", "No session was provided.");
1010
        }
1011
1012 6631 cjones
        logMetacat.info("MNodeService.getReplica() called with parameters: \n" +
1013
             "\tSession.Subject      = " + session.getSubject().getValue() + "\n" +
1014
             "\tIdentifier           = " + pid.getValue());
1015
1016 6475 jones
        InputStream inputStream = null; // bytes to be returned
1017
        handler = new MetacatHandler(new Timer());
1018
        boolean allowed = false;
1019
        String localId; // the metacat docid for the pid
1020 6179 cjones
1021 6475 jones
        // get the local docid from Metacat
1022
        try {
1023
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1024
        } catch (McdbDocNotFoundException e) {
1025 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
1026
                    pid.getValue() + " does not exist at this node.");
1027
1028 6475 jones
        }
1029 6234 cjones
1030 6552 leinfelder
        Subject targetNodeSubject = session.getSubject();
1031 6185 leinfelder
1032 6552 leinfelder
        // check for authorization to replicate, null session to act as this source MN
1033 6610 cjones
        try {
1034 6777 leinfelder
            allowed = D1Client.getCN().isNodeAuthorized(null, targetNodeSubject, pid);
1035 6610 cjones
        } catch (InvalidToken e1) {
1036
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1037
                + e1.getMessage());
1038
1039
        } catch (NotFound e1) {
1040
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1041
                    + e1.getMessage());
1042 6384 cjones
1043 6610 cjones
        } catch (InvalidRequest e1) {
1044
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1045
                    + e1.getMessage());
1046
1047
        }
1048
1049 6540 cjones
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1050
            " for identifier " + pid.getValue());
1051
1052 6475 jones
        // if the person is authorized, perform the read
1053
        if (allowed) {
1054
            try {
1055
                inputStream = handler.read(localId);
1056
            } catch (Exception e) {
1057 6610 cjones
                throw new ServiceFailure("1020", "The object specified by " +
1058
                    pid.getValue() + "could not be returned due to error: " + e.getMessage());
1059 6475 jones
            }
1060
        }
1061 6384 cjones
1062 6475 jones
        // if we fail to set the input stream
1063
        if (inputStream == null) {
1064 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
1065
                pid.getValue() + "does not exist at this node.");
1066 6475 jones
        }
1067
1068
        // log the replica event
1069
        String principal = null;
1070
        if (session.getSubject() != null) {
1071
            principal = session.getSubject().getValue();
1072
        }
1073 6576 cjones
        EventLog.getInstance().log(request.getRemoteAddr(),
1074
            request.getHeader("User-Agent"), principal, localId, "replicate");
1075 6475 jones
1076
        return inputStream;
1077
    }
1078
1079 6573 cjones
    /**
1080 6600 cjones
     * A method to notify the Member Node that the authoritative copy of
1081 6599 cjones
     * system metadata on the Coordinating Nodes has changed.
1082
     *
1083
     * @param session   Session information that contains the identity of the
1084
     *                  calling user as retrieved from the X.509 certificate
1085
     *                  which must be traceable to the CILogon service.
1086
     * @param serialVersion   The serialVersion of the system metadata
1087
     * @param dateSysMetaLastModified  The time stamp for when the system metadata was changed
1088
     * @throws NotImplemented
1089
     * @throws ServiceFailure
1090
     * @throws NotAuthorized
1091
     * @throws InvalidRequest
1092
     * @throws InvalidToken
1093
     */
1094
    public void systemMetadataChanged(Session session, Identifier pid,
1095
        long serialVersion, Date dateSysMetaLastModified)
1096
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
1097
        InvalidToken {
1098
1099 6600 cjones
        SystemMetadata currentLocalSysMeta = null;
1100
        SystemMetadata newSysMeta = null;
1101
        CNode cn = D1Client.getCN();
1102
        NodeList nodeList = null;
1103
        Subject callingSubject = null;
1104
        boolean allowed = false;
1105
1106
        // are we allowed to call this?
1107
        callingSubject = session.getSubject();
1108
        nodeList = cn.listNodes();
1109
1110
        for(Node node : nodeList.getNodeList()) {
1111
            // must be a CN
1112
            if ( node.getType().equals(NodeType.CN)) {
1113
               List<Subject> subjectList = node.getSubjectList();
1114
               // the calling subject must be in the subject list
1115
               if ( subjectList.contains(callingSubject)) {
1116
                   allowed = true;
1117
1118
               }
1119
1120
            }
1121
        }
1122
1123
        if (!allowed ) {
1124
            String msg = "The subject identified by " + callingSubject.getValue() +
1125
              " is not authorized to call this service.";
1126
            throw new NotAuthorized("1331", msg);
1127
1128
        }
1129
1130
        // compare what we have locally to what is sent in the change notification
1131
        try {
1132 6692 leinfelder
            currentLocalSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1133
1134
        } catch (RuntimeException e) {
1135 6600 cjones
            String msg = "SystemMetadata for pid " + pid.getValue() +
1136 6692 leinfelder
              " couldn't be updated because it couldn't be found locally: " +
1137 6600 cjones
              e.getMessage();
1138 6692 leinfelder
            logMetacat.error(msg);
1139
            ServiceFailure sf = new ServiceFailure("1333", msg);
1140
            sf.initCause(e);
1141
            throw sf;
1142 6600 cjones
        }
1143
1144
        if (currentLocalSysMeta.getSerialVersion().longValue() < serialVersion ) {
1145
            try {
1146
                newSysMeta = cn.getSystemMetadata(null, pid);
1147
            } catch (NotFound e) {
1148
                // huh? you just said you had it
1149 6692 leinfelder
            	String msg = "On updating the local copy of system metadata " +
1150
                "for pid " + pid.getValue() +", the CN reports it is not found." +
1151
                " The error message was: " + e.getMessage();
1152
                logMetacat.error(msg);
1153
                ServiceFailure sf = new ServiceFailure("1333", msg);
1154
                sf.initCause(e);
1155
                throw sf;
1156 6600 cjones
            }
1157 6692 leinfelder
1158 6600 cjones
            // update the local copy of system metadata for the pid
1159
            try {
1160 6692 leinfelder
                HazelcastService.getInstance().getSystemMetadataMap().put(newSysMeta.getIdentifier(), newSysMeta);
1161 6600 cjones
                logMetacat.info("Updated local copy of system metadata for pid " +
1162
                    pid.getValue() + " after change notification from the CN.");
1163
1164 6692 leinfelder
            } catch (RuntimeException e) {
1165 6600 cjones
                String msg = "SystemMetadata for pid " + pid.getValue() +
1166 6692 leinfelder
                  " couldn't be updated: " +
1167 6600 cjones
                  e.getMessage();
1168 6692 leinfelder
                logMetacat.error(msg);
1169
                ServiceFailure sf = new ServiceFailure("1333", msg);
1170
                sf.initCause(e);
1171
                throw sf;
1172 6600 cjones
            }
1173
        }
1174
1175 6599 cjones
    }
1176
1177 6795 cjones
    /*
1178
     * Set the replication status for the object on the Coordinating Node
1179
     *
1180
     * @param session - the session for the this target node
1181
     * @param pid - the identifier of the object being updated
1182
     * @param nodeId - the identifier of this target node
1183
     * @param status - the replication status to set
1184
     * @param failure - the exception to include, if any
1185
     */
1186
    private void setReplicationStatus(Session session, Identifier pid,
1187
        NodeReference nodeId, ReplicationStatus status, BaseException failure)
1188
        throws ServiceFailure, NotImplemented, NotAuthorized,
1189
        InvalidRequest {
1190
1191
        // call the CN as the MN to set the replication status
1192
        try {
1193
            this.cn = D1Client.getCN();
1194
            this.cn.setReplicationStatus(session, pid, nodeId,
1195
                    status, failure);
1196
1197
        } catch (InvalidToken e) {
1198
            throw new ServiceFailure("2151",
1199
                    "Could not set the replication status on the CN (InvalidToken): " +
1200
                    e.getMessage());
1201
1202
        } catch (NotFound e) {
1203
            throw new ServiceFailure("2151",
1204
                    "Could not set the replication status on the CN (NotFound): " +
1205
                    e.getMessage());
1206
1207
        } catch (VersionMismatch e) {
1208
            throw new ServiceFailure("2151",
1209
                    "Could not set the replication status on the CN (VersionMismatch): " +
1210
                    e.getMessage());
1211
1212
        }
1213
1214
1215
    }
1216
1217 6179 cjones
}