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