Project

General

Profile

1
/**
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
import java.io.ByteArrayInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.math.BigInteger;
30
import java.security.NoSuchAlgorithmException;
31
import java.util.ArrayList;
32
import java.util.Calendar;
33
import java.util.Date;
34
import java.util.List;
35
import java.util.Set;
36
import java.util.Timer;
37

    
38
import javax.servlet.http.HttpServletRequest;
39

    
40
import org.apache.commons.io.IOUtils;
41
import org.apache.log4j.Logger;
42
import org.dataone.client.CNode;
43
import org.dataone.client.D1Client;
44
import org.dataone.client.MNode;
45
import org.dataone.client.auth.CertificateManager;
46
import org.dataone.configuration.Settings;
47
import org.dataone.service.exceptions.BaseException;
48
import org.dataone.service.exceptions.IdentifierNotUnique;
49
import org.dataone.service.exceptions.InsufficientResources;
50
import org.dataone.service.exceptions.InvalidRequest;
51
import org.dataone.service.exceptions.InvalidSystemMetadata;
52
import org.dataone.service.exceptions.InvalidToken;
53
import org.dataone.service.exceptions.NotAuthorized;
54
import org.dataone.service.exceptions.NotFound;
55
import org.dataone.service.exceptions.NotImplemented;
56
import org.dataone.service.exceptions.ServiceFailure;
57
import org.dataone.service.exceptions.SynchronizationFailed;
58
import org.dataone.service.exceptions.UnsupportedType;
59
import org.dataone.service.mn.tier1.v1.MNCore;
60
import org.dataone.service.mn.tier1.v1.MNRead;
61
import org.dataone.service.mn.tier2.v1.MNAuthorization;
62
import org.dataone.service.mn.tier3.v1.MNStorage;
63
import org.dataone.service.mn.tier4.v1.MNReplication;
64
import org.dataone.service.mn.v1.MNQuery;
65
import org.dataone.service.types.v1.Checksum;
66
import org.dataone.service.types.v1.DescribeResponse;
67
import org.dataone.service.types.v1.Event;
68
import org.dataone.service.types.v1.Identifier;
69
import org.dataone.service.types.v1.Log;
70
import org.dataone.service.types.v1.LogEntry;
71
import org.dataone.service.types.v1.MonitorInfo;
72
import org.dataone.service.types.v1.MonitorList;
73
import org.dataone.service.types.v1.Node;
74
import org.dataone.service.types.v1.NodeList;
75
import org.dataone.service.types.v1.NodeReference;
76
import org.dataone.service.types.v1.NodeState;
77
import org.dataone.service.types.v1.NodeType;
78
import org.dataone.service.types.v1.ObjectFormatIdentifier;
79
import org.dataone.service.types.v1.ObjectList;
80
import org.dataone.service.types.v1.Permission;
81
import org.dataone.service.types.v1.Ping;
82
import org.dataone.service.types.v1.ReplicationStatus;
83
import org.dataone.service.types.v1.Schedule;
84
import org.dataone.service.types.v1.Service;
85
import org.dataone.service.types.v1.Services;
86
import org.dataone.service.types.v1.Session;
87
import org.dataone.service.types.v1.Subject;
88
import org.dataone.service.types.v1.Synchronization;
89
import org.dataone.service.types.v1.SystemMetadata;
90
import org.dataone.service.types.v1.util.AuthUtils;
91
import org.dataone.service.types.v1.util.ChecksumUtil;
92
import org.dataone.service.types.v1_1.QueryEngineDescription;
93
import org.dataone.service.types.v1_1.QueryEngineList;
94
import org.dataone.service.util.Constants;
95

    
96
import edu.ucsb.nceas.metacat.DBQuery;
97
import edu.ucsb.nceas.metacat.EventLog;
98
import edu.ucsb.nceas.metacat.IdentifierManager;
99
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
100
import edu.ucsb.nceas.metacat.MetaCatServlet;
101
import edu.ucsb.nceas.metacat.MetacatHandler;
102
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
103
import edu.ucsb.nceas.metacat.properties.PropertyService;
104
import edu.ucsb.nceas.metacat.util.SystemUtil;
105
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
106

    
107
/**
108
 * Represents Metacat's implementation of the DataONE Member Node 
109
 * service API. Methods implement the various MN* interfaces, and methods common
110
 * to both Member Node and Coordinating Node interfaces are found in the
111
 * D1NodeService base class.
112
 * 
113
 * Implements:
114
 * MNCore.ping()
115
 * MNCore.getLogRecords()
116
 * MNCore.getObjectStatistics()
117
 * MNCore.getOperationStatistics()
118
 * MNCore.getStatus()
119
 * MNCore.getCapabilities()
120
 * MNRead.get()
121
 * MNRead.getSystemMetadata()
122
 * MNRead.describe()
123
 * MNRead.getChecksum()
124
 * MNRead.listObjects()
125
 * MNRead.synchronizationFailed()
126
 * MNAuthorization.isAuthorized()
127
 * MNAuthorization.setAccessPolicy()
128
 * MNStorage.create()
129
 * MNStorage.update()
130
 * MNStorage.delete()
131
 * MNReplication.replicate()
132
 * 
133
 */
134
public class MNodeService extends D1NodeService 
135
    implements MNAuthorization, MNCore, MNRead, MNReplication, MNStorage, MNQuery {
136

    
137
    /* the logger instance */
138
    private Logger logMetacat = null;
139
    
140
    /* A reference to a remote Memeber Node */
141
    private MNode mn;
142
    
143
    /* A reference to a Coordinating Node */
144
    private CNode cn;
145

    
146

    
147
    /**
148
     * Singleton accessor to get an instance of MNodeService.
149
     * 
150
     * @return instance - the instance of MNodeService
151
     */
152
    public static MNodeService getInstance(HttpServletRequest request) {
153
        return new MNodeService(request);
154
    }
155

    
156
    /**
157
     * Constructor, private for singleton access
158
     */
159
    private MNodeService(HttpServletRequest request) {
160
        super(request);
161
        logMetacat = Logger.getLogger(MNodeService.class);
162
        
163
        // set the Member Node certificate file location
164
        CertificateManager.getInstance().setCertificateLocation(Settings.getConfiguration().getString("D1Client.certificate.file"));
165
    }
166

    
167
    /**
168
     * Deletes an object from the Member Node, where the object is either a 
169
     * data object or a science metadata object.
170
     * 
171
     * @param session - the Session object containing the credentials for the Subject
172
     * @param pid - The object identifier to be deleted
173
     * 
174
     * @return pid - the identifier of the object used for the deletion
175
     * 
176
     * @throws InvalidToken
177
     * @throws ServiceFailure
178
     * @throws NotAuthorized
179
     * @throws NotFound
180
     * @throws NotImplemented
181
     * @throws InvalidRequest
182
     */
183
    @Override
184
    public Identifier delete(Session session, Identifier pid) 
185
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
186

    
187
    	// only admin of  the MN or the CN is allowed a full delete
188
        boolean allowed = false;
189
        allowed = isAdminAuthorized(session);
190
        if (!allowed) { 
191
            throw new NotAuthorized("1320", "The provided identity does not have " + "permission to delete objects on the Node.");
192
        }
193
    	
194
    	// defer to superclass implementation
195
        return super.delete(session, pid);
196
    }
197

    
198
    /**
199
     * Updates an existing object by creating a new object identified by 
200
     * newPid on the Member Node which explicitly obsoletes the object 
201
     * identified by pid through appropriate changes to the SystemMetadata 
202
     * of pid and newPid
203
     * 
204
     * @param session - the Session object containing the credentials for the Subject
205
     * @param pid - The identifier of the object to be updated
206
     * @param object - the new object bytes
207
     * @param sysmeta - the new system metadata describing the object
208
     * 
209
     * @return newPid - the identifier of the new object
210
     * 
211
     * @throws InvalidToken
212
     * @throws ServiceFailure
213
     * @throws NotAuthorized
214
     * @throws NotFound
215
     * @throws NotImplemented
216
     * @throws IdentifierNotUnique
217
     * @throws UnsupportedType
218
     * @throws InsufficientResources
219
     * @throws InvalidSystemMetadata
220
     * @throws InvalidRequest
221
     */
222
    @Override
223
    public Identifier update(Session session, Identifier pid, InputStream object, 
224
        Identifier newPid, SystemMetadata sysmeta) 
225
        throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
226
        UnsupportedType, InsufficientResources, NotFound, 
227
        InvalidSystemMetadata, NotImplemented, InvalidRequest {
228

    
229
        String localId = null;
230
        boolean allowed = false;
231
        boolean isScienceMetadata = false;
232
        
233
        if (session == null) {
234
        	throw new InvalidToken("1210", "No session has been provided");
235
        }
236
        Subject subject = session.getSubject();
237

    
238
        // verify the pid is valid format
239
        if (!isValidIdentifier(pid)) {
240
        	throw new InvalidRequest("1202", "The provided identifier is invalid.");
241
        }
242

    
243
        // check for the existing identifier
244
        try {
245
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
246
            
247
        } catch (McdbDocNotFoundException e) {
248
            throw new InvalidRequest("1202", "The object with the provided " + 
249
                "identifier was not found.");
250
            
251
        }
252
        
253
        // set the originating node
254
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
255
        sysmeta.setOriginMemberNode(originMemberNode);
256
        
257
        // set the submitter to match the certificate
258
        sysmeta.setSubmitter(subject);
259
        // set the dates
260
        Date now = Calendar.getInstance().getTime();
261
        sysmeta.setDateSysMetadataModified(now);
262
        sysmeta.setDateUploaded(now);
263

    
264
        // does the subject have WRITE ( == update) priveleges on the pid?
265
        allowed = isAuthorized(session, pid, Permission.WRITE);
266

    
267
        if (allowed) {
268
        	
269
        	// check quality of SM
270
        	if (sysmeta.getObsoletedBy() != null) {
271
        		throw new InvalidSystemMetadata("1300", "Cannot include obsoletedBy when updating object");
272
        	}
273
        	if (sysmeta.getObsoletes() != null && !sysmeta.getObsoletes().getValue().equals(pid.getValue())) {
274
        		throw new InvalidSystemMetadata("1300", "The identifier provided in obsoletes does not match old Identifier");
275
        	}
276

    
277
            // get the existing system metadata for the object
278
            SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
279

    
280
            // check for previous update
281
            // see: https://redmine.dataone.org/issues/3336
282
            Identifier existingObsoletedBy = existingSysMeta.getObsoletedBy();
283
            if (existingObsoletedBy != null) {
284
            	throw new InvalidRequest("1202", 
285
            			"The previous identifier has already been made obsolete by: " + existingObsoletedBy.getValue());
286
            }
287
            
288
            // add the newPid to the obsoletedBy list for the existing sysmeta
289
            existingSysMeta.setObsoletedBy(newPid);
290

    
291
            // then update the existing system metadata
292
            updateSystemMetadata(existingSysMeta);
293

    
294
            // prep the new system metadata, add pid to the affected lists
295
            sysmeta.setObsoletes(pid);
296
            //sysmeta.addDerivedFrom(pid);
297

    
298
            isScienceMetadata = isScienceMetadata(sysmeta);
299

    
300
            // do we have XML metadata or a data object?
301
            if (isScienceMetadata) {
302

    
303
                // update the science metadata XML document
304
                // TODO: handle non-XML metadata/data documents (like netCDF)
305
                // TODO: don't put objects into memory using stream to string
306
                String objectAsXML = "";
307
                try {
308
                    objectAsXML = IOUtils.toString(object, "UTF-8");
309
                    localId = insertOrUpdateDocument(objectAsXML, newPid, session, "update");
310
                    // register the newPid and the generated localId
311
                    if (newPid != null) {
312
                        IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
313

    
314
                    }
315

    
316
                } catch (IOException e) {
317
                    String msg = "The Node is unable to create the object. " + "There was a problem converting the object to XML";
318
                    logMetacat.info(msg);
319
                    throw new ServiceFailure("1310", msg + ": " + e.getMessage());
320

    
321
                }
322

    
323
            } else {
324

    
325
                // update the data object
326
                localId = insertDataObject(object, newPid, session);
327

    
328
            }
329

    
330
            // and insert the new system metadata
331
            insertSystemMetadata(sysmeta);
332

    
333
            // log the update event
334
            EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), subject.getValue(), localId, Event.UPDATE.toString());
335

    
336
        } else {
337
            throw new NotAuthorized("1200", "The provided identity does not have " + "permission to UPDATE the object identified by " + pid.getValue()
338
                    + " on the Member Node.");
339
        }
340

    
341
        return newPid;
342
    }
343

    
344
    public Identifier create(Session session, Identifier pid, InputStream object, SystemMetadata sysmeta) throws InvalidToken, ServiceFailure, NotAuthorized,
345
            IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, InvalidRequest {
346

    
347
        // check for null session
348
        if (session == null) {
349
          throw new InvalidToken("1110", "Session is required to WRITE to the Node.");
350
        }
351
        // set the submitter to match the certificate
352
        sysmeta.setSubmitter(session.getSubject());
353
        // set the originating node
354
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
355
        sysmeta.setOriginMemberNode(originMemberNode);
356
        sysmeta.setArchived(false);
357

    
358
        // set the dates
359
        Date now = Calendar.getInstance().getTime();
360
        sysmeta.setDateSysMetadataModified(now);
361
        sysmeta.setDateUploaded(now);
362
        
363
        // set the serial version
364
        sysmeta.setSerialVersion(BigInteger.ZERO);
365

    
366
        // check that we are not attempting to subvert versioning
367
        if (sysmeta.getObsoletes() != null && sysmeta.getObsoletes().getValue() != null) {
368
            throw new InvalidSystemMetadata("1180", 
369
              "The supplied system metadata is invalid. " +
370
              "The obsoletes field cannot have a value when creating entries.");
371
        }
372
        
373
        if (sysmeta.getObsoletedBy() != null && sysmeta.getObsoletedBy().getValue() != null) {
374
            throw new InvalidSystemMetadata("1180", 
375
              "The supplied system metadata is invalid. " +
376
              "The obsoletedBy field cannot have a value when creating entries.");
377
        }
378
        
379

    
380
        // call the shared impl
381
        return super.create(session, pid, object, sysmeta);
382
    }
383

    
384
    /**
385
     * Called by a Coordinating Node to request that the Member Node create a 
386
     * copy of the specified object by retrieving it from another Member 
387
     * Node and storing it locally so that it can be made accessible to 
388
     * the DataONE system.
389
     * 
390
     * @param session - the Session object containing the credentials for the Subject
391
     * @param sysmeta - Copy of the CN held system metadata for the object
392
     * @param sourceNode - A reference to node from which the content should be 
393
     *                     retrieved. The reference should be resolved by 
394
     *                     checking the CN node registry.
395
     * 
396
     * @return true if the replication succeeds
397
     * 
398
     * @throws ServiceFailure
399
     * @throws NotAuthorized
400
     * @throws NotImplemented
401
     * @throws UnsupportedType
402
     * @throws InsufficientResources
403
     * @throws InvalidRequest
404
     */
405
    @Override
406
    public boolean replicate(Session session, SystemMetadata sysmeta,
407
            NodeReference sourceNode) throws NotImplemented, ServiceFailure,
408
            NotAuthorized, InvalidRequest, InsufficientResources,
409
            UnsupportedType {
410

    
411
        if (session != null && sysmeta != null && sourceNode != null) {
412
            logMetacat.info("MNodeService.replicate() called with parameters: \n" +
413
                            "\tSession.Subject      = "                           +
414
                            session.getSubject().getValue() + "\n"                +
415
                            "\tidentifier           = "                           + 
416
                            sysmeta.getIdentifier().getValue()                    +
417
                            "\n" + "\tSource NodeReference ="                     +
418
                            sourceNode.getValue());
419
        }
420
        boolean result = false;
421
        String nodeIdStr = null;
422
        NodeReference nodeId = null;
423

    
424
        // get the referenced object
425
        Identifier pid = sysmeta.getIdentifier();
426

    
427
        // get from the membernode
428
        // TODO: switch credentials for the server retrieval?
429
        this.mn = D1Client.getMN(sourceNode);
430
        this.cn = D1Client.getCN();
431
        InputStream object = null;
432
        Session thisNodeSession = null;
433
        SystemMetadata localSystemMetadata = null;
434
        BaseException failure = null;
435
        String localId = null;
436
        
437
        // TODO: check credentials
438
        // cannot be called by public
439
        if (session == null || session.getSubject() == null) {
440
            String msg = "No session was provided to replicate identifier " +
441
            sysmeta.getIdentifier().getValue();
442
            logMetacat.info(msg);
443
            throw new NotAuthorized("2152", msg);
444
            
445
        }
446

    
447

    
448
        // get the local node id
449
        try {
450
            nodeIdStr = PropertyService.getProperty("dataone.nodeId");
451
            nodeId = new NodeReference();
452
            nodeId.setValue(nodeIdStr);
453

    
454
        } catch (PropertyNotFoundException e1) {
455
            String msg = "Couldn't get dataone.nodeId property: " + e1.getMessage();
456
            failure = new ServiceFailure("2151", msg);
457
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
458
            logMetacat.error(msg);
459
            return true;
460

    
461
        }
462
        
463

    
464
        try {
465
            // do we already have a replica?
466
            try {
467
                localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
468
                // if we have a local id, get the local object
469
                try {
470
                    object = MetacatHandler.read(localId);
471
                } catch (Exception e) {
472
                	// NOTE: we may already know about this ID because it could be a data file described by a metadata file
473
                	// https://redmine.dataone.org/issues/2572
474
                	// TODO: fix this so that we don't prevent ourselves from getting replicas
475
                	
476
                    // let the CN know that the replication failed
477
                	logMetacat.warn("Object content not found on this node despite having localId: " + localId);
478
                	String msg = "Can't read the object bytes properly, replica is invalid.";
479
                    ServiceFailure serviceFailure = new ServiceFailure("2151", msg);
480
                    setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, serviceFailure);
481
                    logMetacat.warn(msg);
482
                    throw serviceFailure;
483
                    
484
                }
485

    
486
            } catch (McdbDocNotFoundException e) {
487
                logMetacat.info("No replica found. Continuing.");
488
                
489
            }
490
            
491
            // no local replica, get a replica
492
            if ( object == null ) {
493
                // session should be null to use the default certificate
494
                // location set in the Certificate manager
495
                object = mn.getReplica(thisNodeSession, pid);
496
                logMetacat.info("MNodeService.getReplica() called for identifier "
497
                                + pid.getValue());
498

    
499
            }
500

    
501
        } catch (InvalidToken e) {            
502
            String msg = "Could not retrieve object to replicate (InvalidToken): "+ e.getMessage();
503
            failure = new ServiceFailure("2151", msg);
504
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
505
            logMetacat.error(msg);
506
            throw new ServiceFailure("2151", msg);
507

    
508
        } catch (NotFound e) {
509
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
510
            failure = new ServiceFailure("2151", msg);
511
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
512
            logMetacat.error(msg);
513
            throw new ServiceFailure("2151", msg);
514

    
515
        }
516

    
517
        // verify checksum on the object, if supported
518
        if (object.markSupported()) {
519
            Checksum givenChecksum = sysmeta.getChecksum();
520
            Checksum computedChecksum = null;
521
            try {
522
                computedChecksum = ChecksumUtil.checksum(object, givenChecksum.getAlgorithm());
523
                object.reset();
524

    
525
            } catch (Exception e) {
526
                String msg = "Error computing checksum on replica: " + e.getMessage();
527
                logMetacat.error(msg);
528
                ServiceFailure sf = new ServiceFailure("2151", msg);
529
                sf.initCause(e);
530
                throw sf;
531
            }
532
            if (!givenChecksum.getValue().equals(computedChecksum.getValue())) {
533
                logMetacat.error("Given    checksum for " + pid.getValue() + 
534
                    "is " + givenChecksum.getValue());
535
                logMetacat.error("Computed checksum for " + pid.getValue() + 
536
                    "is " + computedChecksum.getValue());
537
                throw new ServiceFailure("2151",
538
                        "Computed checksum does not match declared checksum");
539
            }
540
        }
541

    
542
        // add it to local store
543
        Identifier retPid;
544
        try {
545
            // skip the MN.create -- this mutates the system metadata and we don't want it to
546
            if ( localId == null ) {
547
                // TODO: this will fail if we already "know" about the identifier
548
            	// FIXME: see https://redmine.dataone.org/issues/2572
549
                retPid = super.create(session, pid, object, sysmeta);
550
                result = (retPid.getValue().equals(pid.getValue()));
551
            }
552
            
553
        } catch (Exception e) {
554
            String msg = "Could not save object to local store (" + e.getClass().getName() + "): " + e.getMessage();
555
            failure = new ServiceFailure("2151", msg);
556
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
557
            logMetacat.error(msg);
558
            throw new ServiceFailure("2151", msg);
559
            
560
        }
561

    
562
        // finish by setting the replication status
563
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
564
        return result;
565

    
566
    }
567

    
568
    /**
569
     * Return the object identified by the given object identifier
570
     * 
571
     * @param session - the Session object containing the credentials for the Subject
572
     * @param pid - the object identifier for the given object
573
     * 
574
     * @return inputStream - the input stream of the given object
575
     * 
576
     * @throws InvalidToken
577
     * @throws ServiceFailure
578
     * @throws NotAuthorized
579
     * @throws InvalidRequest
580
     * @throws NotImplemented
581
     */
582
    @Override
583
    public InputStream get(Session session, Identifier pid) 
584
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
585

    
586
        return super.get(session, pid);
587

    
588
    }
589

    
590
    /**
591
     * Returns a Checksum for the specified object using an accepted hashing algorithm
592
     * 
593
     * @param session - the Session object containing the credentials for the Subject
594
     * @param pid - the object identifier for the given object
595
     * @param algorithm -  the name of an algorithm that will be used to compute 
596
     *                     a checksum of the bytes of the object
597
     * 
598
     * @return checksum - the checksum of the given object
599
     * 
600
     * @throws InvalidToken
601
     * @throws ServiceFailure
602
     * @throws NotAuthorized
603
     * @throws NotFound
604
     * @throws InvalidRequest
605
     * @throws NotImplemented
606
     */
607
    @Override
608
    public Checksum getChecksum(Session session, Identifier pid, String algorithm) 
609
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
610
        InvalidRequest, NotImplemented {
611

    
612
        Checksum checksum = null;
613

    
614
        InputStream inputStream = get(session, pid);
615

    
616
        try {
617
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
618

    
619
        } catch (NoSuchAlgorithmException e) {
620
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
621
                    + e.getMessage());
622
        } catch (IOException e) {
623
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
624
                    + e.getMessage());
625
        }
626

    
627
        if (checksum == null) {
628
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
629
        }
630

    
631
        return checksum;
632
    }
633

    
634
    /**
635
     * Return the system metadata for a given object
636
     * 
637
     * @param session - the Session object containing the credentials for the Subject
638
     * @param pid - the object identifier for the given object
639
     * 
640
     * @return inputStream - the input stream of the given system metadata object
641
     * 
642
     * @throws InvalidToken
643
     * @throws ServiceFailure
644
     * @throws NotAuthorized
645
     * @throws NotFound
646
     * @throws InvalidRequest
647
     * @throws NotImplemented
648
     */
649
    @Override
650
    public SystemMetadata getSystemMetadata(Session session, Identifier pid) 
651
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
652
        NotImplemented {
653

    
654
        return super.getSystemMetadata(session, pid);
655
    }
656

    
657
    /**
658
     * Retrieve the list of objects present on the MN that match the calling parameters
659
     * 
660
     * @param session - the Session object containing the credentials for the Subject
661
     * @param startTime - Specifies the beginning of the time range from which 
662
     *                    to return object (>=)
663
     * @param endTime - Specifies the beginning of the time range from which 
664
     *                  to return object (>=)
665
     * @param objectFormat - Restrict results to the specified object format
666
     * @param replicaStatus - Indicates if replicated objects should be returned in the list
667
     * @param start - The zero-based index of the first value, relative to the 
668
     *                first record of the resultset that matches the parameters.
669
     * @param count - The maximum number of entries that should be returned in 
670
     *                the response. The Member Node may return less entries 
671
     *                than specified in this value.
672
     * 
673
     * @return objectList - the list of objects matching the criteria
674
     * 
675
     * @throws InvalidToken
676
     * @throws ServiceFailure
677
     * @throws NotAuthorized
678
     * @throws InvalidRequest
679
     * @throws NotImplemented
680
     */
681
    @Override
682
    public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
683
            Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
684

    
685
        ObjectList objectList = null;
686

    
687
        try {
688
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
689
        } catch (Exception e) {
690
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
691
        }
692

    
693
        return objectList;
694
    }
695

    
696
    /**
697
     * Return a description of the node's capabilities and services.
698
     * 
699
     * @return node - the technical capabilities of the Member Node
700
     * 
701
     * @throws ServiceFailure
702
     * @throws NotAuthorized
703
     * @throws InvalidRequest
704
     * @throws NotImplemented
705
     */
706
    @Override
707
    public Node getCapabilities() 
708
        throws NotImplemented, ServiceFailure {
709

    
710
        String nodeName = null;
711
        String nodeId = null;
712
        String subject = null;
713
        String contactSubject = null;
714
        String nodeDesc = null;
715
        String nodeTypeString = null;
716
        NodeType nodeType = null;
717
        String mnCoreServiceVersion = null;
718
        String mnReadServiceVersion = null;
719
        String mnAuthorizationServiceVersion = null;
720
        String mnStorageServiceVersion = null;
721
        String mnReplicationServiceVersion = null;
722

    
723
        boolean nodeSynchronize = false;
724
        boolean nodeReplicate = false;
725
        boolean mnCoreServiceAvailable = false;
726
        boolean mnReadServiceAvailable = false;
727
        boolean mnAuthorizationServiceAvailable = false;
728
        boolean mnStorageServiceAvailable = false;
729
        boolean mnReplicationServiceAvailable = false;
730

    
731
        try {
732
            // get the properties of the node based on configuration information
733
            nodeName = PropertyService.getProperty("dataone.nodeName");
734
            nodeId = PropertyService.getProperty("dataone.nodeId");
735
            subject = PropertyService.getProperty("dataone.subject");
736
            contactSubject = PropertyService.getProperty("dataone.contactSubject");
737
            nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
738
            nodeTypeString = PropertyService.getProperty("dataone.nodeType");
739
            nodeType = NodeType.convert(nodeTypeString);
740
            nodeSynchronize = new Boolean(PropertyService.getProperty("dataone.nodeSynchronize")).booleanValue();
741
            nodeReplicate = new Boolean(PropertyService.getProperty("dataone.nodeReplicate")).booleanValue();
742

    
743
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
744
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
745
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
746
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
747
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
748

    
749
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
750
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
751
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
752
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
753
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
754

    
755
            // Set the properties of the node based on configuration information and
756
            // calls to current status methods
757
            String serviceName = SystemUtil.getSecureContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
758
            Node node = new Node();
759
            node.setBaseURL(serviceName + "/" + nodeTypeString);
760
            node.setDescription(nodeDesc);
761

    
762
            // set the node's health information
763
            node.setState(NodeState.UP);
764
            
765
            // set the ping response to the current value
766
            Ping canPing = new Ping();
767
            canPing.setSuccess(false);
768
            try {
769
            	Date pingDate = ping();
770
                canPing.setSuccess(pingDate != null);
771
            } catch (BaseException e) {
772
                e.printStackTrace();
773
                // guess it can't be pinged
774
            }
775
            
776
            node.setPing(canPing);
777

    
778
            NodeReference identifier = new NodeReference();
779
            identifier.setValue(nodeId);
780
            node.setIdentifier(identifier);
781
            Subject s = new Subject();
782
            s.setValue(subject);
783
            node.addSubject(s);
784
            Subject contact = new Subject();
785
            contact.setValue(contactSubject);
786
            node.addContactSubject(contact);
787
            node.setName(nodeName);
788
            node.setReplicate(nodeReplicate);
789
            node.setSynchronize(nodeSynchronize);
790

    
791
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
792
            Services services = new Services();
793

    
794
            Service sMNCore = new Service();
795
            sMNCore.setName("MNCore");
796
            sMNCore.setVersion(mnCoreServiceVersion);
797
            sMNCore.setAvailable(mnCoreServiceAvailable);
798

    
799
            Service sMNRead = new Service();
800
            sMNRead.setName("MNRead");
801
            sMNRead.setVersion(mnReadServiceVersion);
802
            sMNRead.setAvailable(mnReadServiceAvailable);
803

    
804
            Service sMNAuthorization = new Service();
805
            sMNAuthorization.setName("MNAuthorization");
806
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
807
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
808

    
809
            Service sMNStorage = new Service();
810
            sMNStorage.setName("MNStorage");
811
            sMNStorage.setVersion(mnStorageServiceVersion);
812
            sMNStorage.setAvailable(mnStorageServiceAvailable);
813

    
814
            Service sMNReplication = new Service();
815
            sMNReplication.setName("MNReplication");
816
            sMNReplication.setVersion(mnReplicationServiceVersion);
817
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
818

    
819
            services.addService(sMNRead);
820
            services.addService(sMNCore);
821
            services.addService(sMNAuthorization);
822
            services.addService(sMNStorage);
823
            services.addService(sMNReplication);
824
            node.setServices(services);
825

    
826
            // Set the schedule for synchronization
827
            Synchronization synchronization = new Synchronization();
828
            Schedule schedule = new Schedule();
829
            Date now = new Date();
830
            schedule.setYear(PropertyService.getProperty("dataone.nodeSynchronization.schedule.year"));
831
            schedule.setMon(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mon"));
832
            schedule.setMday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mday"));
833
            schedule.setWday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.wday"));
834
            schedule.setHour(PropertyService.getProperty("dataone.nodeSynchronization.schedule.hour"));
835
            schedule.setMin(PropertyService.getProperty("dataone.nodeSynchronization.schedule.min"));
836
            schedule.setSec(PropertyService.getProperty("dataone.nodeSynchronization.schedule.sec"));
837
            synchronization.setSchedule(schedule);
838
            synchronization.setLastHarvested(now);
839
            synchronization.setLastCompleteHarvest(now);
840
            node.setSynchronization(synchronization);
841

    
842
            node.setType(nodeType);
843
            return node;
844

    
845
        } catch (PropertyNotFoundException pnfe) {
846
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
847
            logMetacat.error(msg);
848
            throw new ServiceFailure("2162", msg);
849
        }
850
    }
851

    
852
    /**
853
     * Returns the number of operations that have been serviced by the node 
854
     * over time periods of one and 24 hours.
855
     * 
856
     * @param session - the Session object containing the credentials for the Subject
857
     * @param period - An ISO8601 compatible DateTime range specifying the time 
858
     *                 range for which to return operation statistics.
859
     * @param requestor - Limit to operations performed by given requestor identity.
860
     * @param event -  Enumerated value indicating the type of event being examined
861
     * @param format - Limit to events involving objects of the specified format
862
     * 
863
     * @return the desired log records
864
     * 
865
     * @throws InvalidToken
866
     * @throws ServiceFailure
867
     * @throws NotAuthorized
868
     * @throws InvalidRequest
869
     * @throws NotImplemented
870
     */
871
    public MonitorList getOperationStatistics(Session session, Date startTime, 
872
        Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
873
        throws NotImplemented, ServiceFailure, NotAuthorized, InsufficientResources, UnsupportedType {
874

    
875
        MonitorList monitorList = new MonitorList();
876

    
877
        try {
878

    
879
            // get log records first
880
            Log logs = getLogRecords(session, startTime, endTime, event, null, 0, null);
881

    
882
            // TODO: aggregate by day or hour -- needs clarification
883
            int count = 1;
884
            for (LogEntry logEntry : logs.getLogEntryList()) {
885
                Identifier pid = logEntry.getIdentifier();
886
                Date logDate = logEntry.getDateLogged();
887
                // if we are filtering by format
888
                if (formatId != null) {
889
                    SystemMetadata sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
890
                    if (!sysmeta.getFormatId().getValue().equals(formatId.getValue())) {
891
                        // does not match
892
                        continue;
893
                    }
894
                }
895
                MonitorInfo item = new MonitorInfo();
896
                item.setCount(count);
897
                item.setDate(new java.sql.Date(logDate.getTime()));
898
                monitorList.addMonitorInfo(item);
899

    
900
            }
901
        } catch (Exception e) {
902
            e.printStackTrace();
903
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
904
        }
905

    
906
        return monitorList;
907

    
908
    }
909

    
910
    /**
911
     * A callback method used by a CN to indicate to a MN that it cannot 
912
     * complete synchronization of the science metadata identified by pid.  Log
913
     * the event in the metacat event log.
914
     * 
915
     * @param session
916
     * @param syncFailed
917
     * 
918
     * @throws ServiceFailure
919
     * @throws NotAuthorized
920
     * @throws NotImplemented
921
     */
922
    @Override
923
    public boolean synchronizationFailed(Session session, SynchronizationFailed syncFailed) 
924
        throws NotImplemented, ServiceFailure, NotAuthorized {
925

    
926
        String localId;
927
        Identifier pid;
928
        if ( syncFailed.getPid() != null ) {
929
            pid = new Identifier();
930
            pid.setValue(syncFailed.getPid());
931
            boolean allowed;
932
            
933
            //are we allowed? only CNs
934
            try {
935
                allowed = isAdminAuthorized(session);
936
                if ( !allowed ){
937
                    throw new NotAuthorized("2162", 
938
                            "Not allowed to call synchronizationFailed() on this node.");
939
                }
940
            } catch (InvalidToken e) {
941
                throw new NotAuthorized("2162", 
942
                        "Not allowed to call synchronizationFailed() on this node.");
943

    
944
            }
945
            
946
        } else {
947
            throw new ServiceFailure("2161", "The identifier cannot be null.");
948

    
949
        }
950
        
951
        try {
952
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
953
        } catch (McdbDocNotFoundException e) {
954
            throw new ServiceFailure("2161", "The identifier specified by " + 
955
                    syncFailed.getPid() + " was not found on this node.");
956

    
957
        }
958
        // TODO: update the CN URL below when the CNRead.SynchronizationFailed
959
        // method is changed to include the URL as a parameter
960
        logMetacat.debug("Synchronization for the object identified by " + 
961
                pid.getValue() + " failed from " + syncFailed.getNodeId() + 
962
                " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
963
        // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
964
        String principal = Constants.SUBJECT_PUBLIC;
965
        if (session != null && session.getSubject() != null) {
966
          principal = session.getSubject().getValue();
967
        }
968
        try {
969
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "synchronization_failed");
970
        } catch (Exception e) {
971
            throw new ServiceFailure("2161", "Could not log the error for: " + pid.getValue());
972
        }
973
        //EventLog.getInstance().log("CN URL WILL GO HERE", 
974
        //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
975
        return true;
976

    
977
    }
978

    
979
    /**
980
     * Essentially a get() but with different logging behavior
981
     */
982
    @Override
983
    public InputStream getReplica(Session session, Identifier pid) 
984
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
985

    
986
        logMetacat.info("MNodeService.getReplica() called.");
987

    
988
        // cannot be called by public
989
        if (session == null) {
990
        	throw new InvalidToken("2183", "No session was provided.");
991
        }
992
        
993
        logMetacat.info("MNodeService.getReplica() called with parameters: \n" +
994
             "\tSession.Subject      = " + session.getSubject().getValue() + "\n" +
995
             "\tIdentifier           = " + pid.getValue());
996

    
997
        InputStream inputStream = null; // bytes to be returned
998
        handler = new MetacatHandler(new Timer());
999
        boolean allowed = false;
1000
        String localId; // the metacat docid for the pid
1001

    
1002
        // get the local docid from Metacat
1003
        try {
1004
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1005
        } catch (McdbDocNotFoundException e) {
1006
            throw new ServiceFailure("2181", "The object specified by " + 
1007
                    pid.getValue() + " does not exist at this node.");
1008
            
1009
        }
1010

    
1011
        Subject targetNodeSubject = session.getSubject();
1012

    
1013
        // check for authorization to replicate, null session to act as this source MN
1014
        try {
1015
            allowed = D1Client.getCN().isNodeAuthorized(null, targetNodeSubject, pid);
1016
        } catch (InvalidToken e1) {
1017
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1018
                + e1.getMessage());
1019
            
1020
        } catch (NotFound e1) {
1021
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1022
                    + e1.getMessage());
1023

    
1024
        } catch (InvalidRequest e1) {
1025
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1026
                    + e1.getMessage());
1027

    
1028
        }
1029

    
1030
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1031
            " for identifier " + pid.getValue());
1032

    
1033
        // if the person is authorized, perform the read
1034
        if (allowed) {
1035
            try {
1036
                inputStream = MetacatHandler.read(localId);
1037
            } catch (Exception e) {
1038
                throw new ServiceFailure("1020", "The object specified by " + 
1039
                    pid.getValue() + "could not be returned due to error: " + e.getMessage());
1040
            }
1041
        }
1042

    
1043
        // if we fail to set the input stream
1044
        if (inputStream == null) {
1045
            throw new ServiceFailure("2181", "The object specified by " + 
1046
                pid.getValue() + "does not exist at this node.");
1047
        }
1048

    
1049
        // log the replica event
1050
        String principal = null;
1051
        if (session.getSubject() != null) {
1052
            principal = session.getSubject().getValue();
1053
        }
1054
        EventLog.getInstance().log(request.getRemoteAddr(), 
1055
            request.getHeader("User-Agent"), principal, localId, "replicate");
1056

    
1057
        return inputStream;
1058
    }
1059

    
1060
    /**
1061
     * A method to notify the Member Node that the authoritative copy of 
1062
     * system metadata on the Coordinating Nodes has changed.
1063
     * 
1064
     * @param session   Session information that contains the identity of the 
1065
     *                  calling user as retrieved from the X.509 certificate 
1066
     *                  which must be traceable to the CILogon service.
1067
     * @param serialVersion   The serialVersion of the system metadata
1068
     * @param dateSysMetaLastModified  The time stamp for when the system metadata was changed
1069
     * @throws NotImplemented
1070
     * @throws ServiceFailure
1071
     * @throws NotAuthorized
1072
     * @throws InvalidRequest
1073
     * @throws InvalidToken
1074
     */
1075
    public boolean systemMetadataChanged(Session session, Identifier pid,
1076
        long serialVersion, Date dateSysMetaLastModified) 
1077
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
1078
        InvalidToken {
1079
        
1080
        SystemMetadata currentLocalSysMeta = null;
1081
        SystemMetadata newSysMeta = null;
1082
        CNode cn = D1Client.getCN();
1083
        NodeList nodeList = null;
1084
        Subject callingSubject = null;
1085
        boolean allowed = false;
1086
        
1087
        // are we allowed to call this?
1088
        callingSubject = session.getSubject();
1089
        nodeList = cn.listNodes();
1090
        
1091
        for(Node node : nodeList.getNodeList()) {
1092
            // must be a CN
1093
            if ( node.getType().equals(NodeType.CN)) {
1094
               List<Subject> subjectList = node.getSubjectList();
1095
               // the calling subject must be in the subject list
1096
               if ( subjectList.contains(callingSubject)) {
1097
                   allowed = true;
1098
                   
1099
               }
1100
               
1101
            }
1102
        }
1103
        
1104
        if (!allowed ) {
1105
            String msg = "The subject identified by " + callingSubject.getValue() +
1106
              " is not authorized to call this service.";
1107
            throw new NotAuthorized("1331", msg);
1108
            
1109
        }
1110
        
1111
        // compare what we have locally to what is sent in the change notification
1112
        try {
1113
            currentLocalSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1114
             
1115
        } catch (RuntimeException e) {
1116
            String msg = "SystemMetadata for pid " + pid.getValue() +
1117
              " couldn't be updated because it couldn't be found locally: " +
1118
              e.getMessage();
1119
            logMetacat.error(msg);
1120
            ServiceFailure sf = new ServiceFailure("1333", msg);
1121
            sf.initCause(e);
1122
            throw sf; 
1123
        }
1124
        
1125
        if (currentLocalSysMeta.getSerialVersion().longValue() < serialVersion ) {
1126
            try {
1127
                newSysMeta = cn.getSystemMetadata(null, pid);
1128
            } catch (NotFound e) {
1129
                // huh? you just said you had it
1130
            	String msg = "On updating the local copy of system metadata " + 
1131
                "for pid " + pid.getValue() +", the CN reports it is not found." +
1132
                " The error message was: " + e.getMessage();
1133
                logMetacat.error(msg);
1134
                ServiceFailure sf = new ServiceFailure("1333", msg);
1135
                sf.initCause(e);
1136
                throw sf;
1137
            }
1138
            
1139
            // update the local copy of system metadata for the pid
1140
            try {
1141
                HazelcastService.getInstance().getSystemMetadataMap().put(newSysMeta.getIdentifier(), newSysMeta);
1142
                logMetacat.info("Updated local copy of system metadata for pid " +
1143
                    pid.getValue() + " after change notification from the CN.");
1144
                
1145
            } catch (RuntimeException e) {
1146
                String msg = "SystemMetadata for pid " + pid.getValue() +
1147
                  " couldn't be updated: " +
1148
                  e.getMessage();
1149
                logMetacat.error(msg);
1150
                ServiceFailure sf = new ServiceFailure("1333", msg);
1151
                sf.initCause(e);
1152
                throw sf;
1153
            }
1154
        }
1155
        
1156
        return true;
1157
        
1158
    }
1159
    
1160
    /*
1161
     * Set the replication status for the object on the Coordinating Node
1162
     * 
1163
     * @param session - the session for the this target node
1164
     * @param pid - the identifier of the object being updated
1165
     * @param nodeId - the identifier of this target node
1166
     * @param status - the replication status to set
1167
     * @param failure - the exception to include, if any
1168
     */
1169
    private void setReplicationStatus(Session session, Identifier pid, 
1170
        NodeReference nodeId, ReplicationStatus status, BaseException failure) 
1171
        throws ServiceFailure, NotImplemented, NotAuthorized, 
1172
        InvalidRequest {
1173
        
1174
        // call the CN as the MN to set the replication status
1175
        try {
1176
            this.cn = D1Client.getCN();
1177
            this.cn.setReplicationStatus(session, pid, nodeId,
1178
                    status, failure);
1179
            
1180
        } catch (InvalidToken e) {
1181
        	String msg = "Could not set the replication status for " + pid.getValue() + " on the CN (InvalidToken): " + e.getMessage();
1182
            logMetacat.error(msg);
1183
        	throw new ServiceFailure("2151",
1184
                    msg);
1185
            
1186
        } catch (NotFound e) {
1187
        	String msg = "Could not set the replication status for " + pid.getValue() + " on the CN (NotFound): " + e.getMessage();
1188
            logMetacat.error(msg);
1189
        	throw new ServiceFailure("2151",
1190
                    msg);
1191
            
1192
        }
1193

    
1194

    
1195
    }
1196

    
1197
	@Override
1198
	public Identifier generateIdentifier(Session arg0, String arg1, String arg2)
1199
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1200
			InvalidRequest {
1201
		throw new NotImplemented("2194", "Member Node does not implement generateIdentifier method");
1202
	}
1203

    
1204
	@Override
1205
	public boolean isAuthorized(Identifier pid, Permission permission)
1206
			throws ServiceFailure, InvalidRequest, InvalidToken, NotFound,
1207
			NotAuthorized, NotImplemented {
1208

    
1209
		return isAuthorized(null, pid, permission);
1210
	}
1211

    
1212
	@Override
1213
	public boolean systemMetadataChanged(Identifier pid, long serialVersion, Date dateSysMetaLastModified)
1214
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1215
			InvalidRequest {
1216

    
1217
		return systemMetadataChanged(null, pid, serialVersion, dateSysMetaLastModified);
1218
	}
1219

    
1220
	@Override
1221
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1222
			Integer start, Integer count) throws InvalidRequest, InvalidToken,
1223
			NotAuthorized, NotImplemented, ServiceFailure {
1224

    
1225
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1226
	}
1227

    
1228
	@Override
1229
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1230
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1231

    
1232
		return describe(null, pid);
1233
	}
1234

    
1235
	@Override
1236
	public InputStream get(Identifier pid) throws InvalidToken, NotAuthorized,
1237
			NotImplemented, ServiceFailure, NotFound, InsufficientResources {
1238

    
1239
		return get(null, pid);
1240
	}
1241

    
1242
	@Override
1243
	public Checksum getChecksum(Identifier pid, String algorithm)
1244
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1245
			ServiceFailure, NotFound {
1246

    
1247
		return getChecksum(null, pid, algorithm);
1248
	}
1249

    
1250
	@Override
1251
	public SystemMetadata getSystemMetadata(Identifier pid)
1252
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1253
			NotFound {
1254

    
1255
		return getSystemMetadata(null, pid);
1256
	}
1257

    
1258
	@Override
1259
	public ObjectList listObjects(Date startTime, Date endTime,
1260
			ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
1261
			Integer count) throws InvalidRequest, InvalidToken, NotAuthorized,
1262
			NotImplemented, ServiceFailure {
1263

    
1264
		return listObjects(null, startTime, endTime, objectFormatId, replicaStatus, start, count);
1265
	}
1266

    
1267
	@Override
1268
	public boolean synchronizationFailed(SynchronizationFailed syncFailed)
1269
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure {
1270

    
1271
		return synchronizationFailed(null, syncFailed);
1272
	}
1273

    
1274
	@Override
1275
	public InputStream getReplica(Identifier pid) throws InvalidToken,
1276
			NotAuthorized, NotImplemented, ServiceFailure, NotFound,
1277
			InsufficientResources {
1278

    
1279
		return getReplica(null, pid);
1280
	}
1281

    
1282
	@Override
1283
	public boolean replicate(SystemMetadata sysmeta, NodeReference sourceNode)
1284
			throws NotImplemented, ServiceFailure, NotAuthorized,
1285
			InvalidRequest, InvalidToken, InsufficientResources,
1286
			UnsupportedType {
1287

    
1288
		return replicate(null, sysmeta, sourceNode);
1289
	}
1290

    
1291
	@Override
1292
	public Identifier create(Identifier pid, InputStream object,
1293
			SystemMetadata sysmeta) throws IdentifierNotUnique,
1294
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1295
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1296
			UnsupportedType {
1297

    
1298
		return create(null, pid, object, sysmeta);
1299
	}
1300

    
1301
	@Override
1302
	public Identifier delete(Identifier pid) throws InvalidToken,
1303
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1304

    
1305
		return delete(null, pid);
1306
	}
1307

    
1308
	@Override
1309
	public Identifier generateIdentifier(String arg0, String arg1)
1310
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1311
			InvalidRequest {
1312

    
1313
		return generateIdentifier(null, arg0, arg1);
1314
	}
1315

    
1316
	@Override
1317
	public Identifier update(Identifier pid, InputStream object,
1318
			Identifier newPid, SystemMetadata sysmeta) throws IdentifierNotUnique,
1319
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1320
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1321
			UnsupportedType, NotFound {
1322

    
1323
		return update(null, pid, object, newPid, sysmeta);
1324
	}
1325

    
1326
	@Override
1327
	public QueryEngineDescription getQueryEngineDescription(String engine)
1328
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1329
			NotFound {
1330
		// TODO: implement
1331
		throw new NotImplemented("", "Metacat has not implemented this method");
1332
	}
1333

    
1334
	@Override
1335
	public QueryEngineList listQueryEngines() throws InvalidToken,
1336
			ServiceFailure, NotAuthorized, NotImplemented {
1337
		QueryEngineList qel = new QueryEngineList();
1338
		// support pathquery initially
1339
		qel.addQueryEngine("pathquery");
1340
		// TODO: implement solr-based query
1341
		//qel.addQueryEngine("solr");
1342
		return qel;
1343
	}
1344

    
1345
	@Override
1346
	public InputStream query(String engine, String query) throws InvalidToken,
1347
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented,
1348
			NotFound {
1349
		if (engine.equals("pathquery")) {
1350
			try {
1351
				DBQuery queryobj = new DBQuery();
1352
				String user = Constants.SUBJECT_PUBLIC;
1353
				String[] groups= null;
1354
				if (session != null) {
1355
					user = session.getSubject().getValue();
1356
					Set<Subject> subjects = AuthUtils.authorizedClientSubjects(session);
1357
					if (subjects != null) {
1358
						List<String> groupList = new ArrayList<String>();
1359
						for (Subject subject: subjects) {
1360
							groupList.add(subject.getValue());
1361
						}
1362
						groups = groupList.toArray(new String[0]);
1363
					}
1364
				}
1365
				String results = queryobj.performPathquery(query, user, groups);
1366
				return new ByteArrayInputStream(results.getBytes(MetaCatServlet.DEFAULT_ENCODING));
1367

    
1368
			} catch (Exception e) {
1369
				
1370
			}
1371
			
1372
		}
1373
		return null;
1374
	}
1375
    
1376
}
(3-3/5)