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