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