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
import java.util.Vector;
38

    
39
import javax.servlet.http.HttpServletRequest;
40

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

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

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

    
140
    private static final String PATHQUERY = "pathquery";
141

    
142
	/* the logger instance */
143
    private Logger logMetacat = null;
144
    
145
    /* A reference to a remote Memeber Node */
146
    private MNode mn;
147
    
148
    /* A reference to a Coordinating Node */
149
    private CNode cn;
150

    
151

    
152
    /**
153
     * Singleton accessor to get an instance of MNodeService.
154
     * 
155
     * @return instance - the instance of MNodeService
156
     */
157
    public static MNodeService getInstance(HttpServletRequest request) {
158
        return new MNodeService(request);
159
    }
160

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

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

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

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

    
234
        String localId = null;
235
        boolean allowed = false;
236
        boolean isScienceMetadata = false;
237
        
238
        if (session == null) {
239
        	throw new InvalidToken("1210", "No session has been provided");
240
        }
241
        Subject subject = session.getSubject();
242

    
243
        // verify the pid is valid format
244
        if (!isValidIdentifier(pid)) {
245
        	throw new InvalidRequest("1202", "The provided identifier is invalid.");
246
        }
247

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

    
269
        // does the subject have WRITE ( == update) priveleges on the pid?
270
        allowed = isAuthorized(session, pid, Permission.WRITE);
271

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

    
282
            // get the existing system metadata for the object
283
            SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
284

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

    
296
            // then update the existing system metadata
297
            updateSystemMetadata(existingSysMeta);
298

    
299
            // prep the new system metadata, add pid to the affected lists
300
            sysmeta.setObsoletes(pid);
301
            //sysmeta.addDerivedFrom(pid);
302

    
303
            isScienceMetadata = isScienceMetadata(sysmeta);
304

    
305
            // do we have XML metadata or a data object?
306
            if (isScienceMetadata) {
307

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

    
319
                    }
320

    
321
                } catch (IOException e) {
322
                    String msg = "The Node is unable to create the object. " + "There was a problem converting the object to XML";
323
                    logMetacat.info(msg);
324
                    throw new ServiceFailure("1310", msg + ": " + e.getMessage());
325

    
326
                }
327

    
328
            } else {
329

    
330
                // update the data object
331
                localId = insertDataObject(object, newPid, session);
332

    
333
            }
334

    
335
            // and insert the new system metadata
336
            insertSystemMetadata(sysmeta);
337

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

    
341
        } else {
342
            throw new NotAuthorized("1200", "The provided identity does not have " + "permission to UPDATE the object identified by " + pid.getValue()
343
                    + " on the Member Node.");
344
        }
345

    
346
        return newPid;
347
    }
348

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

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

    
363
        // set the dates
364
        Date now = Calendar.getInstance().getTime();
365
        sysmeta.setDateSysMetadataModified(now);
366
        sysmeta.setDateUploaded(now);
367
        
368
        // set the serial version
369
        sysmeta.setSerialVersion(BigInteger.ZERO);
370

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

    
385
        // call the shared impl
386
        return super.create(session, pid, object, sysmeta);
387
    }
388

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

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

    
429
        // get the referenced object
430
        Identifier pid = sysmeta.getIdentifier();
431

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

    
452

    
453
        // get the local node id
454
        try {
455
            nodeIdStr = PropertyService.getProperty("dataone.nodeId");
456
            nodeId = new NodeReference();
457
            nodeId.setValue(nodeIdStr);
458

    
459
        } catch (PropertyNotFoundException e1) {
460
            String msg = "Couldn't get dataone.nodeId property: " + e1.getMessage();
461
            failure = new ServiceFailure("2151", msg);
462
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
463
            logMetacat.error(msg);
464
            return true;
465

    
466
        }
467
        
468

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

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

    
504
            }
505

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

    
513
        } catch (NotFound e) {
514
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
515
            failure = new ServiceFailure("2151", msg);
516
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
517
            logMetacat.error(msg);
518
            throw new ServiceFailure("2151", msg);
519

    
520
        }
521

    
522
        // verify checksum on the object, if supported
523
        if (object.markSupported()) {
524
            Checksum givenChecksum = sysmeta.getChecksum();
525
            Checksum computedChecksum = null;
526
            try {
527
                computedChecksum = ChecksumUtil.checksum(object, givenChecksum.getAlgorithm());
528
                object.reset();
529

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

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

    
567
        // finish by setting the replication status
568
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
569
        return result;
570

    
571
    }
572

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

    
591
        return super.get(session, pid);
592

    
593
    }
594

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

    
617
        Checksum checksum = null;
618

    
619
        InputStream inputStream = get(session, pid);
620

    
621
        try {
622
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
623

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

    
632
        if (checksum == null) {
633
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
634
        }
635

    
636
        return checksum;
637
    }
638

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

    
659
        return super.getSystemMetadata(session, pid);
660
    }
661

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

    
690
        ObjectList objectList = null;
691

    
692
        try {
693
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
694
        } catch (Exception e) {
695
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
696
        }
697

    
698
        return objectList;
699
    }
700

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

    
715
        String nodeName = null;
716
        String nodeId = null;
717
        String subject = null;
718
        String contactSubject = null;
719
        String nodeDesc = null;
720
        String nodeTypeString = null;
721
        NodeType nodeType = null;
722
        String mnCoreServiceVersion = null;
723
        String mnReadServiceVersion = null;
724
        String mnAuthorizationServiceVersion = null;
725
        String mnStorageServiceVersion = null;
726
        String mnReplicationServiceVersion = null;
727

    
728
        boolean nodeSynchronize = false;
729
        boolean nodeReplicate = false;
730
        boolean mnCoreServiceAvailable = false;
731
        boolean mnReadServiceAvailable = false;
732
        boolean mnAuthorizationServiceAvailable = false;
733
        boolean mnStorageServiceAvailable = false;
734
        boolean mnReplicationServiceAvailable = false;
735

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

    
748
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
749
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
750
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
751
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
752
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
753

    
754
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
755
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
756
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
757
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
758
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
759

    
760
            // Set the properties of the node based on configuration information and
761
            // calls to current status methods
762
            String serviceName = SystemUtil.getSecureContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
763
            Node node = new Node();
764
            node.setBaseURL(serviceName + "/" + nodeTypeString);
765
            node.setDescription(nodeDesc);
766

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

    
783
            NodeReference identifier = new NodeReference();
784
            identifier.setValue(nodeId);
785
            node.setIdentifier(identifier);
786
            Subject s = new Subject();
787
            s.setValue(subject);
788
            node.addSubject(s);
789
            Subject contact = new Subject();
790
            contact.setValue(contactSubject);
791
            node.addContactSubject(contact);
792
            node.setName(nodeName);
793
            node.setReplicate(nodeReplicate);
794
            node.setSynchronize(nodeSynchronize);
795

    
796
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
797
            Services services = new Services();
798

    
799
            Service sMNCore = new Service();
800
            sMNCore.setName("MNCore");
801
            sMNCore.setVersion(mnCoreServiceVersion);
802
            sMNCore.setAvailable(mnCoreServiceAvailable);
803

    
804
            Service sMNRead = new Service();
805
            sMNRead.setName("MNRead");
806
            sMNRead.setVersion(mnReadServiceVersion);
807
            sMNRead.setAvailable(mnReadServiceAvailable);
808

    
809
            Service sMNAuthorization = new Service();
810
            sMNAuthorization.setName("MNAuthorization");
811
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
812
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
813

    
814
            Service sMNStorage = new Service();
815
            sMNStorage.setName("MNStorage");
816
            sMNStorage.setVersion(mnStorageServiceVersion);
817
            sMNStorage.setAvailable(mnStorageServiceAvailable);
818

    
819
            Service sMNReplication = new Service();
820
            sMNReplication.setName("MNReplication");
821
            sMNReplication.setVersion(mnReplicationServiceVersion);
822
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
823

    
824
            services.addService(sMNRead);
825
            services.addService(sMNCore);
826
            services.addService(sMNAuthorization);
827
            services.addService(sMNStorage);
828
            services.addService(sMNReplication);
829
            node.setServices(services);
830

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

    
847
            node.setType(nodeType);
848
            return node;
849

    
850
        } catch (PropertyNotFoundException pnfe) {
851
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
852
            logMetacat.error(msg);
853
            throw new ServiceFailure("2162", msg);
854
        }
855
    }
856

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

    
880
        MonitorList monitorList = new MonitorList();
881

    
882
        try {
883

    
884
            // get log records first
885
            Log logs = getLogRecords(session, startTime, endTime, event, null, 0, null);
886

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

    
905
            }
906
        } catch (Exception e) {
907
            e.printStackTrace();
908
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
909
        }
910

    
911
        return monitorList;
912

    
913
    }
914

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

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

    
949
            }
950
            
951
        } else {
952
            throw new ServiceFailure("2161", "The identifier cannot be null.");
953

    
954
        }
955
        
956
        try {
957
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
958
        } catch (McdbDocNotFoundException e) {
959
            throw new ServiceFailure("2161", "The identifier specified by " + 
960
                    syncFailed.getPid() + " was not found on this node.");
961

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

    
982
    }
983

    
984
    /**
985
     * Essentially a get() but with different logging behavior
986
     */
987
    @Override
988
    public InputStream getReplica(Session session, Identifier pid) 
989
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
990

    
991
        logMetacat.info("MNodeService.getReplica() called.");
992

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

    
1002
        InputStream inputStream = null; // bytes to be returned
1003
        handler = new MetacatHandler(new Timer());
1004
        boolean allowed = false;
1005
        String localId; // the metacat docid for the pid
1006

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

    
1016
        Subject targetNodeSubject = session.getSubject();
1017

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

    
1029
        } catch (InvalidRequest e1) {
1030
            throw new ServiceFailure("2181", "Could not determine if node is authorized: " 
1031
                    + e1.getMessage());
1032

    
1033
        }
1034

    
1035
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1036
            " for identifier " + pid.getValue());
1037

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

    
1048
        // if we fail to set the input stream
1049
        if (inputStream == null) {
1050
            throw new ServiceFailure("2181", "The object specified by " + 
1051
                pid.getValue() + "does not exist at this node.");
1052
        }
1053

    
1054
        // log the replica event
1055
        String principal = null;
1056
        if (session.getSubject() != null) {
1057
            principal = session.getSubject().getValue();
1058
        }
1059
        EventLog.getInstance().log(request.getRemoteAddr(), 
1060
            request.getHeader("User-Agent"), principal, localId, "replicate");
1061

    
1062
        return inputStream;
1063
    }
1064

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

    
1199

    
1200
    }
1201

    
1202
	@Override
1203
	public Identifier generateIdentifier(Session arg0, String arg1, String arg2)
1204
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1205
			InvalidRequest {
1206
		throw new NotImplemented("2194", "Member Node does not implement generateIdentifier method");
1207
	}
1208

    
1209
	@Override
1210
	public boolean isAuthorized(Identifier pid, Permission permission)
1211
			throws ServiceFailure, InvalidRequest, InvalidToken, NotFound,
1212
			NotAuthorized, NotImplemented {
1213

    
1214
		return isAuthorized(null, pid, permission);
1215
	}
1216

    
1217
	@Override
1218
	public boolean systemMetadataChanged(Identifier pid, long serialVersion, Date dateSysMetaLastModified)
1219
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1220
			InvalidRequest {
1221

    
1222
		return systemMetadataChanged(null, pid, serialVersion, dateSysMetaLastModified);
1223
	}
1224

    
1225
	@Override
1226
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1227
			Integer start, Integer count) throws InvalidRequest, InvalidToken,
1228
			NotAuthorized, NotImplemented, ServiceFailure {
1229

    
1230
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1231
	}
1232

    
1233
	@Override
1234
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1235
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1236

    
1237
		return describe(null, pid);
1238
	}
1239

    
1240
	@Override
1241
	public InputStream get(Identifier pid) throws InvalidToken, NotAuthorized,
1242
			NotImplemented, ServiceFailure, NotFound, InsufficientResources {
1243

    
1244
		return get(null, pid);
1245
	}
1246

    
1247
	@Override
1248
	public Checksum getChecksum(Identifier pid, String algorithm)
1249
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1250
			ServiceFailure, NotFound {
1251

    
1252
		return getChecksum(null, pid, algorithm);
1253
	}
1254

    
1255
	@Override
1256
	public SystemMetadata getSystemMetadata(Identifier pid)
1257
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1258
			NotFound {
1259

    
1260
		return getSystemMetadata(null, pid);
1261
	}
1262

    
1263
	@Override
1264
	public ObjectList listObjects(Date startTime, Date endTime,
1265
			ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
1266
			Integer count) throws InvalidRequest, InvalidToken, NotAuthorized,
1267
			NotImplemented, ServiceFailure {
1268

    
1269
		return listObjects(null, startTime, endTime, objectFormatId, replicaStatus, start, count);
1270
	}
1271

    
1272
	@Override
1273
	public boolean synchronizationFailed(SynchronizationFailed syncFailed)
1274
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure {
1275

    
1276
		return synchronizationFailed(null, syncFailed);
1277
	}
1278

    
1279
	@Override
1280
	public InputStream getReplica(Identifier pid) throws InvalidToken,
1281
			NotAuthorized, NotImplemented, ServiceFailure, NotFound,
1282
			InsufficientResources {
1283

    
1284
		return getReplica(null, pid);
1285
	}
1286

    
1287
	@Override
1288
	public boolean replicate(SystemMetadata sysmeta, NodeReference sourceNode)
1289
			throws NotImplemented, ServiceFailure, NotAuthorized,
1290
			InvalidRequest, InvalidToken, InsufficientResources,
1291
			UnsupportedType {
1292

    
1293
		return replicate(null, sysmeta, sourceNode);
1294
	}
1295

    
1296
	@Override
1297
	public Identifier create(Identifier pid, InputStream object,
1298
			SystemMetadata sysmeta) throws IdentifierNotUnique,
1299
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1300
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1301
			UnsupportedType {
1302

    
1303
		return create(null, pid, object, sysmeta);
1304
	}
1305

    
1306
	@Override
1307
	public Identifier delete(Identifier pid) throws InvalidToken,
1308
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1309

    
1310
		return delete(null, pid);
1311
	}
1312

    
1313
	@Override
1314
	public Identifier generateIdentifier(String arg0, String arg1)
1315
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1316
			InvalidRequest {
1317

    
1318
		return generateIdentifier(null, arg0, arg1);
1319
	}
1320

    
1321
	@Override
1322
	public Identifier update(Identifier pid, InputStream object,
1323
			Identifier newPid, SystemMetadata sysmeta) throws IdentifierNotUnique,
1324
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1325
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1326
			UnsupportedType, NotFound {
1327

    
1328
		return update(null, pid, object, newPid, sysmeta);
1329
	}
1330

    
1331
	@Override
1332
	public QueryEngineDescription getQueryEngineDescription(String engine)
1333
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1334
			NotFound {
1335
		QueryEngineDescription qed = new QueryEngineDescription();
1336
		qed.setName(PATHQUERY);
1337
		qed.setQueryEngineVersion("1.0");
1338
		qed.addAdditionalInfo("This is the traditional structured query for Metacat");
1339
		Vector<String> pathsForIndexing = null;
1340
		try {
1341
			pathsForIndexing = SystemUtil.getPathsForIndexing();
1342
		} catch (MetacatUtilException e) {
1343
			logMetacat.warn("Could not get index paths", e);
1344
		}
1345
		for (String fieldName: pathsForIndexing) {
1346
			QueryField field = new QueryField();
1347
			field.addDescription("Indexed field for path '" + fieldName + "'");
1348
			field.setName(fieldName);
1349
			field.setReturnable(true);
1350
			field.setSearchable(true);
1351
			field.setSortable(false);
1352
			// TODO: determine type and multivaluedness
1353
			field.setType(String.class.getName());
1354
			//field.setMultivalued(true);
1355
			qed.addQueryField(field);
1356
		}
1357
		return qed;
1358
	}
1359

    
1360
	@Override
1361
	public QueryEngineList listQueryEngines() throws InvalidToken,
1362
			ServiceFailure, NotAuthorized, NotImplemented {
1363
		QueryEngineList qel = new QueryEngineList();
1364
		// support pathquery initially
1365
		qel.addQueryEngine(PATHQUERY);
1366
		// TODO: implement solr-based query
1367
		//qel.addQueryEngine("solr");
1368
		return qel;
1369
	}
1370

    
1371
	@Override
1372
	public InputStream query(String engine, String query) throws InvalidToken,
1373
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented,
1374
			NotFound {
1375
		if (engine.equals(PATHQUERY)) {
1376
			try {
1377
				DBQuery queryobj = new DBQuery();
1378
				String user = Constants.SUBJECT_PUBLIC;
1379
				String[] groups= null;
1380
				if (session != null) {
1381
					user = session.getSubject().getValue();
1382
					Set<Subject> subjects = AuthUtils.authorizedClientSubjects(session);
1383
					if (subjects != null) {
1384
						List<String> groupList = new ArrayList<String>();
1385
						for (Subject subject: subjects) {
1386
							groupList.add(subject.getValue());
1387
						}
1388
						groups = groupList.toArray(new String[0]);
1389
					}
1390
				}
1391
				String results = queryobj.performPathquery(query, user, groups);
1392
				return new ByteArrayInputStream(results.getBytes(MetaCatServlet.DEFAULT_ENCODING));
1393

    
1394
			} catch (Exception e) {
1395
				
1396
			}
1397
			
1398
		}
1399
		return null;
1400
	}
1401
    
1402
}
(3-3/5)