Project

General

Profile

1 6179 cjones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000-2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author:  $'
7
 *     '$Date:  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
24
package edu.ucsb.nceas.metacat.dataone;
25
26 7417 leinfelder
import java.io.ByteArrayInputStream;
27 7860 leinfelder
import java.io.ByteArrayOutputStream;
28 7850 leinfelder
import java.io.File;
29
import java.io.FileOutputStream;
30 6228 cjones
import java.io.IOException;
31 6179 cjones
import java.io.InputStream;
32 8190 leinfelder
import java.io.InputStreamReader;
33 7860 leinfelder
import java.io.OutputStreamWriter;
34 7849 leinfelder
import java.io.UnsupportedEncodingException;
35 7860 leinfelder
import java.io.Writer;
36 7021 leinfelder
import java.math.BigInteger;
37 7849 leinfelder
import java.net.URISyntaxException;
38 6228 cjones
import java.security.NoSuchAlgorithmException;
39 7860 leinfelder
import java.sql.SQLException;
40 7417 leinfelder
import java.util.ArrayList;
41 6525 leinfelder
import java.util.Calendar;
42 6179 cjones
import java.util.Date;
43 8437 walker
import java.util.HashMap;
44 7680 tao
import java.util.HashSet;
45 7860 leinfelder
import java.util.Hashtable;
46 6250 cjones
import java.util.List;
47 7849 leinfelder
import java.util.Map;
48 7417 leinfelder
import java.util.Set;
49 6389 leinfelder
import java.util.Timer;
50 7489 leinfelder
import java.util.UUID;
51 7418 leinfelder
import java.util.Vector;
52 6179 cjones
53 6542 leinfelder
import javax.servlet.http.HttpServletRequest;
54
55 8141 leinfelder
import org.apache.commons.beanutils.BeanUtils;
56 6258 cjones
import org.apache.commons.io.IOUtils;
57 6179 cjones
import org.apache.log4j.Logger;
58 6528 cjones
import org.dataone.client.CNode;
59 6332 leinfelder
import org.dataone.client.D1Client;
60
import org.dataone.client.MNode;
61 8013 leinfelder
import org.dataone.client.ObjectFormatCache;
62 6552 leinfelder
import org.dataone.client.auth.CertificateManager;
63 7850 leinfelder
import org.dataone.client.formats.ObjectFormatInfo;
64 6552 leinfelder
import org.dataone.configuration.Settings;
65 7849 leinfelder
import org.dataone.ore.ResourceMapFactory;
66 6795 cjones
import org.dataone.service.exceptions.BaseException;
67 6179 cjones
import org.dataone.service.exceptions.IdentifierNotUnique;
68
import org.dataone.service.exceptions.InsufficientResources;
69
import org.dataone.service.exceptions.InvalidRequest;
70
import org.dataone.service.exceptions.InvalidSystemMetadata;
71
import org.dataone.service.exceptions.InvalidToken;
72
import org.dataone.service.exceptions.NotAuthorized;
73
import org.dataone.service.exceptions.NotFound;
74
import org.dataone.service.exceptions.NotImplemented;
75
import org.dataone.service.exceptions.ServiceFailure;
76 6185 leinfelder
import org.dataone.service.exceptions.SynchronizationFailed;
77 6179 cjones
import org.dataone.service.exceptions.UnsupportedType;
78 6366 leinfelder
import org.dataone.service.mn.tier1.v1.MNCore;
79
import org.dataone.service.mn.tier1.v1.MNRead;
80
import org.dataone.service.mn.tier2.v1.MNAuthorization;
81
import org.dataone.service.mn.tier3.v1.MNStorage;
82
import org.dataone.service.mn.tier4.v1.MNReplication;
83 7417 leinfelder
import org.dataone.service.mn.v1.MNQuery;
84 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
85 7144 leinfelder
import org.dataone.service.types.v1.DescribeResponse;
86 6366 leinfelder
import org.dataone.service.types.v1.Event;
87
import org.dataone.service.types.v1.Identifier;
88
import org.dataone.service.types.v1.Log;
89
import org.dataone.service.types.v1.LogEntry;
90
import org.dataone.service.types.v1.MonitorInfo;
91
import org.dataone.service.types.v1.MonitorList;
92
import org.dataone.service.types.v1.Node;
93 6600 cjones
import org.dataone.service.types.v1.NodeList;
94 6366 leinfelder
import org.dataone.service.types.v1.NodeReference;
95
import org.dataone.service.types.v1.NodeState;
96
import org.dataone.service.types.v1.NodeType;
97 7860 leinfelder
import org.dataone.service.types.v1.ObjectFormat;
98 6366 leinfelder
import org.dataone.service.types.v1.ObjectFormatIdentifier;
99
import org.dataone.service.types.v1.ObjectList;
100
import org.dataone.service.types.v1.Permission;
101
import org.dataone.service.types.v1.Ping;
102 6528 cjones
import org.dataone.service.types.v1.ReplicationStatus;
103 6366 leinfelder
import org.dataone.service.types.v1.Schedule;
104
import org.dataone.service.types.v1.Service;
105
import org.dataone.service.types.v1.Services;
106
import org.dataone.service.types.v1.Session;
107
import org.dataone.service.types.v1.Subject;
108
import org.dataone.service.types.v1.Synchronization;
109
import org.dataone.service.types.v1.SystemMetadata;
110 7417 leinfelder
import org.dataone.service.types.v1.util.AuthUtils;
111 6366 leinfelder
import org.dataone.service.types.v1.util.ChecksumUtil;
112 7417 leinfelder
import org.dataone.service.types.v1_1.QueryEngineDescription;
113
import org.dataone.service.types.v1_1.QueryEngineList;
114 7418 leinfelder
import org.dataone.service.types.v1_1.QueryField;
115 6476 jones
import org.dataone.service.util.Constants;
116 7849 leinfelder
import org.dspace.foresite.OREException;
117
import org.dspace.foresite.OREParserException;
118
import org.dspace.foresite.ORESerialiserException;
119
import org.dspace.foresite.ResourceMap;
120 8437 walker
import org.ecoinformatics.datamanager.parser.DataPackage;
121
import org.ecoinformatics.datamanager.parser.Entity;
122
import org.ecoinformatics.datamanager.parser.generic.DataPackageParserInterface;
123
import org.ecoinformatics.datamanager.parser.generic.Eml200DataPackageParser;
124 6179 cjones
125 7448 leinfelder
import edu.ucsb.nceas.ezid.EZIDException;
126 7417 leinfelder
import edu.ucsb.nceas.metacat.DBQuery;
127 7860 leinfelder
import edu.ucsb.nceas.metacat.DBTransform;
128 6234 cjones
import edu.ucsb.nceas.metacat.EventLog;
129 6230 cjones
import edu.ucsb.nceas.metacat.IdentifierManager;
130 6234 cjones
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
131 7417 leinfelder
import edu.ucsb.nceas.metacat.MetaCatServlet;
132 6389 leinfelder
import edu.ucsb.nceas.metacat.MetacatHandler;
133 7772 tao
import edu.ucsb.nceas.metacat.common.query.EnabledQueryEngines;
134 7757 leinfelder
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeByteArrayInputStream;
135 6648 leinfelder
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
136 7662 tao
import edu.ucsb.nceas.metacat.index.MetacatSolrEngineDescriptionHandler;
137 7620 tao
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
138 6340 cjones
import edu.ucsb.nceas.metacat.properties.PropertyService;
139 7418 leinfelder
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
140 8026 leinfelder
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
141 7441 leinfelder
import edu.ucsb.nceas.metacat.util.DocumentUtil;
142 6542 leinfelder
import edu.ucsb.nceas.metacat.util.SystemUtil;
143 6340 cjones
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
144 8190 leinfelder
import edu.ucsb.nceas.utilities.XMLUtilities;
145 7850 leinfelder
import gov.loc.repository.bagit.Bag;
146
import gov.loc.repository.bagit.BagFactory;
147
import gov.loc.repository.bagit.writer.impl.ZipWriter;
148 6230 cjones
149 6179 cjones
/**
150
 * Represents Metacat's implementation of the DataONE Member Node
151
 * service API. Methods implement the various MN* interfaces, and methods common
152
 * to both Member Node and Coordinating Node interfaces are found in the
153
 * D1NodeService base class.
154 6288 cjones
 *
155
 * Implements:
156
 * MNCore.ping()
157
 * MNCore.getLogRecords()
158
 * MNCore.getObjectStatistics()
159
 * MNCore.getOperationStatistics()
160
 * MNCore.getStatus()
161
 * MNCore.getCapabilities()
162
 * MNRead.get()
163
 * MNRead.getSystemMetadata()
164
 * MNRead.describe()
165
 * MNRead.getChecksum()
166
 * MNRead.listObjects()
167
 * MNRead.synchronizationFailed()
168
 * MNAuthorization.isAuthorized()
169
 * MNAuthorization.setAccessPolicy()
170
 * MNStorage.create()
171
 * MNStorage.update()
172
 * MNStorage.delete()
173
 * MNReplication.replicate()
174
 *
175 6179 cjones
 */
176 6599 cjones
public class MNodeService extends D1NodeService
177 7417 leinfelder
    implements MNAuthorization, MNCore, MNRead, MNReplication, MNStorage, MNQuery {
178 6179 cjones
179 7772 tao
    //private static final String PATHQUERY = "pathquery";
180 7849 leinfelder
	public static final String UUID_SCHEME = "UUID";
181
	public static final String DOI_SCHEME = "DOI";
182 7489 leinfelder
	private static final String UUID_PREFIX = "urn:uuid:";
183 7418 leinfelder
184
	/* the logger instance */
185 6475 jones
    private Logger logMetacat = null;
186 6795 cjones
187
    /* A reference to a remote Memeber Node */
188
    private MNode mn;
189
190
    /* A reference to a Coordinating Node */
191
    private CNode cn;
192 6241 cjones
193 6795 cjones
194 6475 jones
    /**
195
     * Singleton accessor to get an instance of MNodeService.
196
     *
197
     * @return instance - the instance of MNodeService
198
     */
199 6542 leinfelder
    public static MNodeService getInstance(HttpServletRequest request) {
200
        return new MNodeService(request);
201 6179 cjones
    }
202
203 6475 jones
    /**
204
     * Constructor, private for singleton access
205
     */
206 6542 leinfelder
    private MNodeService(HttpServletRequest request) {
207
        super(request);
208 6475 jones
        logMetacat = Logger.getLogger(MNodeService.class);
209 6552 leinfelder
210
        // set the Member Node certificate file location
211
        CertificateManager.getInstance().setCertificateLocation(Settings.getConfiguration().getString("D1Client.certificate.file"));
212 6310 cjones
    }
213 6475 jones
214
    /**
215
     * Deletes an object from the Member Node, where the object is either a
216
     * data object or a science metadata object.
217
     *
218
     * @param session - the Session object containing the credentials for the Subject
219
     * @param pid - The object identifier to be deleted
220
     *
221
     * @return pid - the identifier of the object used for the deletion
222
     *
223
     * @throws InvalidToken
224
     * @throws ServiceFailure
225
     * @throws NotAuthorized
226
     * @throws NotFound
227
     * @throws NotImplemented
228
     * @throws InvalidRequest
229
     */
230
    @Override
231
    public Identifier delete(Session session, Identifier pid)
232 6610 cjones
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
233 6475 jones
234 7162 leinfelder
    	// only admin of  the MN or the CN is allowed a full delete
235
        boolean allowed = false;
236 7330 leinfelder
        allowed = isAdminAuthorized(session);
237 8360 tao
238
        //check if it is the authoritative member node
239
        if(!allowed) {
240
            allowed = isAuthoritativeMNodeAdmin(session, pid);
241
        }
242
243 7162 leinfelder
        if (!allowed) {
244 7245 cjones
            throw new NotAuthorized("1320", "The provided identity does not have " + "permission to delete objects on the Node.");
245 7162 leinfelder
        }
246
247 7077 leinfelder
    	// defer to superclass implementation
248
        return super.delete(session, pid);
249 6250 cjones
    }
250
251 6475 jones
    /**
252
     * Updates an existing object by creating a new object identified by
253
     * newPid on the Member Node which explicitly obsoletes the object
254
     * identified by pid through appropriate changes to the SystemMetadata
255
     * of pid and newPid
256
     *
257
     * @param session - the Session object containing the credentials for the Subject
258
     * @param pid - The identifier of the object to be updated
259
     * @param object - the new object bytes
260
     * @param sysmeta - the new system metadata describing the object
261
     *
262
     * @return newPid - the identifier of the new object
263
     *
264
     * @throws InvalidToken
265
     * @throws ServiceFailure
266
     * @throws NotAuthorized
267
     * @throws NotFound
268
     * @throws NotImplemented
269
     * @throws IdentifierNotUnique
270
     * @throws UnsupportedType
271
     * @throws InsufficientResources
272
     * @throws InvalidSystemMetadata
273
     * @throws InvalidRequest
274
     */
275
    @Override
276 6575 cjones
    public Identifier update(Session session, Identifier pid, InputStream object,
277
        Identifier newPid, SystemMetadata sysmeta)
278
        throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
279
        UnsupportedType, InsufficientResources, NotFound,
280
        InvalidSystemMetadata, NotImplemented, InvalidRequest {
281 6250 cjones
282 6475 jones
        String localId = null;
283
        boolean allowed = false;
284
        boolean isScienceMetadata = false;
285 6645 leinfelder
286
        if (session == null) {
287
        	throw new InvalidToken("1210", "No session has been provided");
288
        }
289 6475 jones
        Subject subject = session.getSubject();
290
291 7315 leinfelder
        // verify the pid is valid format
292 7318 leinfelder
        if (!isValidIdentifier(pid)) {
293 7315 leinfelder
        	throw new InvalidRequest("1202", "The provided identifier is invalid.");
294 6475 jones
        }
295
296
        // check for the existing identifier
297
        try {
298
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
299 6575 cjones
300 6475 jones
        } catch (McdbDocNotFoundException e) {
301 6575 cjones
            throw new InvalidRequest("1202", "The object with the provided " +
302
                "identifier was not found.");
303
304 6475 jones
        }
305 6518 leinfelder
306 6521 leinfelder
        // set the originating node
307
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
308
        sysmeta.setOriginMemberNode(originMemberNode);
309
310 6518 leinfelder
        // set the submitter to match the certificate
311
        sysmeta.setSubmitter(subject);
312 6525 leinfelder
        // set the dates
313
        Date now = Calendar.getInstance().getTime();
314 6575 cjones
        sysmeta.setDateSysMetadataModified(now);
315
        sysmeta.setDateUploaded(now);
316 7486 leinfelder
317
        // make sure serial version is set to something
318
        BigInteger serialVersion = sysmeta.getSerialVersion();
319
        if (serialVersion == null) {
320
        	sysmeta.setSerialVersion(BigInteger.ZERO);
321
        }
322 6475 jones
323
        // does the subject have WRITE ( == update) priveleges on the pid?
324
        allowed = isAuthorized(session, pid, Permission.WRITE);
325
326
        if (allowed) {
327 6649 leinfelder
328
        	// check quality of SM
329
        	if (sysmeta.getObsoletedBy() != null) {
330
        		throw new InvalidSystemMetadata("1300", "Cannot include obsoletedBy when updating object");
331
        	}
332
        	if (sysmeta.getObsoletes() != null && !sysmeta.getObsoletes().getValue().equals(pid.getValue())) {
333
        		throw new InvalidSystemMetadata("1300", "The identifier provided in obsoletes does not match old Identifier");
334
        	}
335 6475 jones
336
            // get the existing system metadata for the object
337
            SystemMetadata existingSysMeta = getSystemMetadata(session, pid);
338
339 7400 leinfelder
            // check for previous update
340
            // see: https://redmine.dataone.org/issues/3336
341
            Identifier existingObsoletedBy = existingSysMeta.getObsoletedBy();
342
            if (existingObsoletedBy != null) {
343
            	throw new InvalidRequest("1202",
344
            			"The previous identifier has already been made obsolete by: " + existingObsoletedBy.getValue());
345
            }
346 6475 jones
347
            isScienceMetadata = isScienceMetadata(sysmeta);
348
349
            // do we have XML metadata or a data object?
350
            if (isScienceMetadata) {
351
352
                // update the science metadata XML document
353
                // TODO: handle non-XML metadata/data documents (like netCDF)
354
                // TODO: don't put objects into memory using stream to string
355
                String objectAsXML = "";
356
                try {
357
                    objectAsXML = IOUtils.toString(object, "UTF-8");
358 7443 leinfelder
                    // give the old pid so we can calculate the new local id
359
                    localId = insertOrUpdateDocument(objectAsXML, pid, session, "update");
360 6475 jones
                    // register the newPid and the generated localId
361
                    if (newPid != null) {
362
                        IdentifierManager.getInstance().createMapping(newPid.getValue(), localId);
363
364
                    }
365
366
                } catch (IOException e) {
367
                    String msg = "The Node is unable to create the object. " + "There was a problem converting the object to XML";
368
                    logMetacat.info(msg);
369
                    throw new ServiceFailure("1310", msg + ": " + e.getMessage());
370
371
                }
372
373
            } else {
374
375
                // update the data object
376
                localId = insertDataObject(object, newPid, session);
377
378
            }
379 8267 leinfelder
380
            // add the newPid to the obsoletedBy list for the existing sysmeta
381
            existingSysMeta.setObsoletedBy(newPid);
382 6475 jones
383 8267 leinfelder
            // then update the existing system metadata
384
            updateSystemMetadata(existingSysMeta);
385
386
            // prep the new system metadata, add pid to the affected lists
387
            sysmeta.setObsoletes(pid);
388
            //sysmeta.addDerivedFrom(pid);
389
390 6475 jones
            // and insert the new system metadata
391
            insertSystemMetadata(sysmeta);
392
393
            // log the update event
394 6542 leinfelder
            EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), subject.getValue(), localId, Event.UPDATE.toString());
395 7507 leinfelder
396
            // attempt to register the identifier - it checks if it is a doi
397
            try {
398 7512 leinfelder
    			DOIService.getInstance().registerDOI(sysmeta);
399 7507 leinfelder
    		} catch (EZIDException e) {
400
                throw new ServiceFailure("1190", "Could not register DOI: " + e.getMessage());
401
    		}
402 6475 jones
403
        } else {
404
            throw new NotAuthorized("1200", "The provided identity does not have " + "permission to UPDATE the object identified by " + pid.getValue()
405
                    + " on the Member Node.");
406
        }
407
408
        return newPid;
409 6250 cjones
    }
410 6254 cjones
411 6475 jones
    public Identifier create(Session session, Identifier pid, InputStream object, SystemMetadata sysmeta) throws InvalidToken, ServiceFailure, NotAuthorized,
412
            IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, InvalidRequest {
413 6250 cjones
414 6916 cjones
        // check for null session
415 6530 leinfelder
        if (session == null) {
416 6575 cjones
          throw new InvalidToken("1110", "Session is required to WRITE to the Node.");
417 6530 leinfelder
        }
418 6518 leinfelder
        // set the submitter to match the certificate
419
        sysmeta.setSubmitter(session.getSubject());
420 6520 leinfelder
        // set the originating node
421
        NodeReference originMemberNode = this.getCapabilities().getIdentifier();
422
        sysmeta.setOriginMemberNode(originMemberNode);
423 6916 cjones
        sysmeta.setArchived(false);
424
425 6525 leinfelder
        // set the dates
426
        Date now = Calendar.getInstance().getTime();
427 6916 cjones
        sysmeta.setDateSysMetadataModified(now);
428
        sysmeta.setDateUploaded(now);
429 7021 leinfelder
430
        // set the serial version
431
        sysmeta.setSerialVersion(BigInteger.ZERO);
432 7083 cjones
433
        // check that we are not attempting to subvert versioning
434
        if (sysmeta.getObsoletes() != null && sysmeta.getObsoletes().getValue() != null) {
435
            throw new InvalidSystemMetadata("1180",
436
              "The supplied system metadata is invalid. " +
437
              "The obsoletes field cannot have a value when creating entries.");
438
        }
439 7021 leinfelder
440 7083 cjones
        if (sysmeta.getObsoletedBy() != null && sysmeta.getObsoletedBy().getValue() != null) {
441
            throw new InvalidSystemMetadata("1180",
442
              "The supplied system metadata is invalid. " +
443
              "The obsoletedBy field cannot have a value when creating entries.");
444
        }
445
446 6518 leinfelder
        // call the shared impl
447 7507 leinfelder
        Identifier resultPid = super.create(session, pid, object, sysmeta);
448
449
        // attempt to register the identifier - it checks if it is a doi
450
        try {
451 7512 leinfelder
			DOIService.getInstance().registerDOI(sysmeta);
452 7507 leinfelder
		} catch (EZIDException e) {
453 7510 leinfelder
			ServiceFailure sf = new ServiceFailure("1190", "Could not register DOI: " + e.getMessage());
454
			sf.initCause(e);
455
            throw sf;
456 7507 leinfelder
		}
457
458
        // return
459
		return resultPid ;
460 6475 jones
    }
461 6250 cjones
462 6475 jones
    /**
463
     * Called by a Coordinating Node to request that the Member Node create a
464
     * copy of the specified object by retrieving it from another Member
465
     * Node and storing it locally so that it can be made accessible to
466
     * the DataONE system.
467
     *
468
     * @param session - the Session object containing the credentials for the Subject
469
     * @param sysmeta - Copy of the CN held system metadata for the object
470
     * @param sourceNode - A reference to node from which the content should be
471
     *                     retrieved. The reference should be resolved by
472
     *                     checking the CN node registry.
473
     *
474
     * @return true if the replication succeeds
475
     *
476
     * @throws ServiceFailure
477
     * @throws NotAuthorized
478
     * @throws NotImplemented
479
     * @throws UnsupportedType
480
     * @throws InsufficientResources
481
     * @throws InvalidRequest
482
     */
483
    @Override
484 6786 cjones
    public boolean replicate(Session session, SystemMetadata sysmeta,
485
            NodeReference sourceNode) throws NotImplemented, ServiceFailure,
486
            NotAuthorized, InvalidRequest, InsufficientResources,
487
            UnsupportedType {
488
489 6875 cjones
        if (session != null && sysmeta != null && sourceNode != null) {
490
            logMetacat.info("MNodeService.replicate() called with parameters: \n" +
491
                            "\tSession.Subject      = "                           +
492
                            session.getSubject().getValue() + "\n"                +
493 7082 cjones
                            "\tidentifier           = "                           +
494
                            sysmeta.getIdentifier().getValue()                    +
495 6875 cjones
                            "\n" + "\tSource NodeReference ="                     +
496
                            sourceNode.getValue());
497
        }
498 6475 jones
        boolean result = false;
499 6786 cjones
        String nodeIdStr = null;
500 6651 cjones
        NodeReference nodeId = null;
501 6786 cjones
502 6795 cjones
        // get the referenced object
503
        Identifier pid = sysmeta.getIdentifier();
504
505
        // get from the membernode
506
        // TODO: switch credentials for the server retrieval?
507
        this.mn = D1Client.getMN(sourceNode);
508
        this.cn = D1Client.getCN();
509
        InputStream object = null;
510
        Session thisNodeSession = null;
511
        SystemMetadata localSystemMetadata = null;
512
        BaseException failure = null;
513 6818 cjones
        String localId = null;
514
515 6795 cjones
        // TODO: check credentials
516
        // cannot be called by public
517 7063 leinfelder
        if (session == null || session.getSubject() == null) {
518 7082 cjones
            String msg = "No session was provided to replicate identifier " +
519
            sysmeta.getIdentifier().getValue();
520 8414 tao
            logMetacat.error(msg);
521 7192 cjones
            throw new NotAuthorized("2152", msg);
522
523 6795 cjones
        }
524
525
526 6651 cjones
        // get the local node id
527
        try {
528 7030 cjones
            nodeIdStr = PropertyService.getProperty("dataone.nodeId");
529 6651 cjones
            nodeId = new NodeReference();
530
            nodeId.setValue(nodeIdStr);
531 6786 cjones
532 6651 cjones
        } catch (PropertyNotFoundException e1) {
533 7030 cjones
            String msg = "Couldn't get dataone.nodeId property: " + e1.getMessage();
534 6795 cjones
            failure = new ServiceFailure("2151", msg);
535 8414 tao
            //setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
536 6795 cjones
            logMetacat.error(msg);
537 8414 tao
            //return true;
538
            throw new ServiceFailure("2151", msg);
539 6786 cjones
540 6651 cjones
        }
541 6795 cjones
542 6475 jones
543
        try {
544 6786 cjones
            // do we already have a replica?
545
            try {
546 6818 cjones
                localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
547 6822 cjones
                // if we have a local id, get the local object
548
                try {
549
                    object = MetacatHandler.read(localId);
550
                } catch (Exception e) {
551 7127 leinfelder
                	// NOTE: we may already know about this ID because it could be a data file described by a metadata file
552
                	// https://redmine.dataone.org/issues/2572
553
                	// TODO: fix this so that we don't prevent ourselves from getting replicas
554
555 6822 cjones
                    // let the CN know that the replication failed
556 7127 leinfelder
                	logMetacat.warn("Object content not found on this node despite having localId: " + localId);
557
                	String msg = "Can't read the object bytes properly, replica is invalid.";
558
                    ServiceFailure serviceFailure = new ServiceFailure("2151", msg);
559 7113 leinfelder
                    setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, serviceFailure);
560
                    logMetacat.warn(msg);
561 6822 cjones
                    throw serviceFailure;
562
563
                }
564
565 6817 cjones
            } catch (McdbDocNotFoundException e) {
566 6818 cjones
                logMetacat.info("No replica found. Continuing.");
567 6817 cjones
568 6819 cjones
            }
569
570 6786 cjones
            // no local replica, get a replica
571 6819 cjones
            if ( object == null ) {
572 6786 cjones
                // session should be null to use the default certificate
573
                // location set in the Certificate manager
574
                object = mn.getReplica(thisNodeSession, pid);
575 7082 cjones
                logMetacat.info("MNodeService.getReplica() called for identifier "
576 6786 cjones
                                + pid.getValue());
577
578
            }
579
580 6795 cjones
        } catch (InvalidToken e) {
581
            String msg = "Could not retrieve object to replicate (InvalidToken): "+ e.getMessage();
582
            failure = new ServiceFailure("2151", msg);
583
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
584
            logMetacat.error(msg);
585
            throw new ServiceFailure("2151", msg);
586 6786 cjones
587 6475 jones
        } catch (NotFound e) {
588 6795 cjones
            String msg = "Could not retrieve object to replicate (NotFound): "+ e.getMessage();
589
            failure = new ServiceFailure("2151", msg);
590
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
591
            logMetacat.error(msg);
592
            throw new ServiceFailure("2151", msg);
593 6786 cjones
594 8414 tao
        } catch (NotAuthorized e) {
595
            String msg = "Could not retrieve object to replicate (NotAuthorized): "+ e.getMessage();
596
            failure = new ServiceFailure("2151", msg);
597
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
598
            logMetacat.error(msg);
599
            throw new ServiceFailure("2151", msg);
600
        } catch (NotImplemented e) {
601
            String msg = "Could not retrieve object to replicate (mn.getReplica NotImplemented): "+ e.getMessage();
602
            failure = new ServiceFailure("2151", msg);
603
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
604
            logMetacat.error(msg);
605
            throw new ServiceFailure("2151", msg);
606
        } catch (ServiceFailure e) {
607
            String msg = "Could not retrieve object to replicate (ServiceFailure): "+ e.getMessage();
608
            failure = new ServiceFailure("2151", msg);
609
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
610
            logMetacat.error(msg);
611
            throw new ServiceFailure("2151", msg);
612
        } catch (InsufficientResources e) {
613
            String msg = "Could not retrieve object to replicate (InsufficientResources): "+ e.getMessage();
614
            failure = new ServiceFailure("2151", msg);
615
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
616
            logMetacat.error(msg);
617
            throw new ServiceFailure("2151", msg);
618 6475 jones
        }
619
620 6693 leinfelder
        // verify checksum on the object, if supported
621
        if (object.markSupported()) {
622 6786 cjones
            Checksum givenChecksum = sysmeta.getChecksum();
623
            Checksum computedChecksum = null;
624
            try {
625 7127 leinfelder
                computedChecksum = ChecksumUtil.checksum(object, givenChecksum.getAlgorithm());
626 6786 cjones
                object.reset();
627 6948 cjones
628 6786 cjones
            } catch (Exception e) {
629 7127 leinfelder
                String msg = "Error computing checksum on replica: " + e.getMessage();
630 7113 leinfelder
                logMetacat.error(msg);
631 6786 cjones
                ServiceFailure sf = new ServiceFailure("2151", msg);
632
                sf.initCause(e);
633 8414 tao
                setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, sf);
634 6786 cjones
                throw sf;
635
            }
636 6948 cjones
            if (!givenChecksum.getValue().equals(computedChecksum.getValue())) {
637 7113 leinfelder
                logMetacat.error("Given    checksum for " + pid.getValue() +
638 6948 cjones
                    "is " + givenChecksum.getValue());
639 7113 leinfelder
                logMetacat.error("Computed checksum for " + pid.getValue() +
640 6948 cjones
                    "is " + computedChecksum.getValue());
641 8414 tao
                String msg = "Computed checksum does not match declared checksum";
642
                failure = new ServiceFailure("2151", msg);
643
                setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
644
                throw new ServiceFailure("2151", msg);
645 6786 cjones
            }
646 6693 leinfelder
        }
647 6786 cjones
648 6475 jones
        // add it to local store
649
        Identifier retPid;
650
        try {
651 7127 leinfelder
            // skip the MN.create -- this mutates the system metadata and we don't want it to
652 6818 cjones
            if ( localId == null ) {
653 7127 leinfelder
                // TODO: this will fail if we already "know" about the identifier
654
            	// FIXME: see https://redmine.dataone.org/issues/2572
655 6818 cjones
                retPid = super.create(session, pid, object, sysmeta);
656
                result = (retPid.getValue().equals(pid.getValue()));
657
            }
658 6795 cjones
659 7125 leinfelder
        } catch (Exception e) {
660 7126 leinfelder
            String msg = "Could not save object to local store (" + e.getClass().getName() + "): " + e.getMessage();
661 7125 leinfelder
            failure = new ServiceFailure("2151", msg);
662
            setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.FAILED, failure);
663
            logMetacat.error(msg);
664
            throw new ServiceFailure("2151", msg);
665
666 6475 jones
        }
667
668 6795 cjones
        // finish by setting the replication status
669
        setReplicationStatus(thisNodeSession, pid, nodeId, ReplicationStatus.COMPLETED, null);
670 6475 jones
        return result;
671
672 6250 cjones
    }
673 6179 cjones
674 6475 jones
    /**
675
     * Return the object identified by the given object identifier
676
     *
677
     * @param session - the Session object containing the credentials for the Subject
678
     * @param pid - the object identifier for the given object
679
     *
680
     * @return inputStream - the input stream of the given object
681
     *
682
     * @throws InvalidToken
683
     * @throws ServiceFailure
684
     * @throws NotAuthorized
685
     * @throws InvalidRequest
686
     * @throws NotImplemented
687
     */
688
    @Override
689 6610 cjones
    public InputStream get(Session session, Identifier pid)
690
    throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
691 6258 cjones
692 6475 jones
        return super.get(session, pid);
693 6258 cjones
694 6259 cjones
    }
695 6258 cjones
696 6475 jones
    /**
697
     * Returns a Checksum for the specified object using an accepted hashing algorithm
698
     *
699
     * @param session - the Session object containing the credentials for the Subject
700
     * @param pid - the object identifier for the given object
701
     * @param algorithm -  the name of an algorithm that will be used to compute
702
     *                     a checksum of the bytes of the object
703
     *
704
     * @return checksum - the checksum of the given object
705
     *
706
     * @throws InvalidToken
707
     * @throws ServiceFailure
708
     * @throws NotAuthorized
709
     * @throws NotFound
710
     * @throws InvalidRequest
711
     * @throws NotImplemented
712
     */
713
    @Override
714 6610 cjones
    public Checksum getChecksum(Session session, Identifier pid, String algorithm)
715
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
716
        InvalidRequest, NotImplemented {
717 6258 cjones
718 6475 jones
        Checksum checksum = null;
719
720
        InputStream inputStream = get(session, pid);
721
722 6259 cjones
        try {
723 6475 jones
            checksum = ChecksumUtil.checksum(inputStream, algorithm);
724
725
        } catch (NoSuchAlgorithmException e) {
726
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
727
                    + e.getMessage());
728 6259 cjones
        } catch (IOException e) {
729 6475 jones
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned due to an internal error: "
730
                    + e.getMessage());
731 6259 cjones
        }
732 6382 cjones
733 6475 jones
        if (checksum == null) {
734
            throw new ServiceFailure("1410", "The checksum for the object specified by " + pid.getValue() + "could not be returned.");
735
        }
736 6258 cjones
737 6475 jones
        return checksum;
738 6259 cjones
    }
739 6179 cjones
740 6475 jones
    /**
741
     * Return the system metadata for a given object
742
     *
743
     * @param session - the Session object containing the credentials for the Subject
744
     * @param pid - the object identifier for the given object
745
     *
746
     * @return inputStream - the input stream of the given system metadata object
747
     *
748
     * @throws InvalidToken
749
     * @throws ServiceFailure
750
     * @throws NotAuthorized
751
     * @throws NotFound
752
     * @throws InvalidRequest
753
     * @throws NotImplemented
754
     */
755
    @Override
756 6610 cjones
    public SystemMetadata getSystemMetadata(Session session, Identifier pid)
757
        throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
758
        NotImplemented {
759 6341 leinfelder
760 6475 jones
        return super.getSystemMetadata(session, pid);
761
    }
762 6341 leinfelder
763 6475 jones
    /**
764
     * Retrieve the list of objects present on the MN that match the calling parameters
765
     *
766
     * @param session - the Session object containing the credentials for the Subject
767
     * @param startTime - Specifies the beginning of the time range from which
768
     *                    to return object (>=)
769
     * @param endTime - Specifies the beginning of the time range from which
770
     *                  to return object (>=)
771
     * @param objectFormat - Restrict results to the specified object format
772
     * @param replicaStatus - Indicates if replicated objects should be returned in the list
773
     * @param start - The zero-based index of the first value, relative to the
774
     *                first record of the resultset that matches the parameters.
775
     * @param count - The maximum number of entries that should be returned in
776
     *                the response. The Member Node may return less entries
777
     *                than specified in this value.
778
     *
779
     * @return objectList - the list of objects matching the criteria
780
     *
781
     * @throws InvalidToken
782
     * @throws ServiceFailure
783
     * @throws NotAuthorized
784
     * @throws InvalidRequest
785
     * @throws NotImplemented
786
     */
787
    @Override
788
    public ObjectList listObjects(Session session, Date startTime, Date endTime, ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
789
            Integer count) throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken {
790 6179 cjones
791 6475 jones
        ObjectList objectList = null;
792 6332 leinfelder
793 6475 jones
        try {
794 7439 leinfelder
        	// safeguard against large requests
795
            if (count == null || count > MAXIMUM_DB_RECORD_COUNT) {
796
            	count = MAXIMUM_DB_RECORD_COUNT;
797
            }
798 6475 jones
            objectList = IdentifierManager.getInstance().querySystemMetadata(startTime, endTime, objectFormatId, replicaStatus, start, count);
799
        } catch (Exception e) {
800
            throw new ServiceFailure("1580", "Error querying system metadata: " + e.getMessage());
801
        }
802 6332 leinfelder
803 6475 jones
        return objectList;
804 6229 cjones
    }
805 6179 cjones
806 6475 jones
    /**
807 6476 jones
     * Return a description of the node's capabilities and services.
808 6475 jones
     *
809
     * @return node - the technical capabilities of the Member Node
810
     *
811
     * @throws ServiceFailure
812
     * @throws NotAuthorized
813
     * @throws InvalidRequest
814
     * @throws NotImplemented
815
     */
816
    @Override
817 6610 cjones
    public Node getCapabilities()
818
        throws NotImplemented, ServiceFailure {
819 6179 cjones
820 6475 jones
        String nodeName = null;
821
        String nodeId = null;
822 6492 jones
        String subject = null;
823 6938 cjones
        String contactSubject = null;
824 6475 jones
        String nodeDesc = null;
825 6476 jones
        String nodeTypeString = null;
826
        NodeType nodeType = null;
827 6475 jones
        String mnCoreServiceVersion = null;
828
        String mnReadServiceVersion = null;
829
        String mnAuthorizationServiceVersion = null;
830
        String mnStorageServiceVersion = null;
831
        String mnReplicationServiceVersion = null;
832 6179 cjones
833 6475 jones
        boolean nodeSynchronize = false;
834
        boolean nodeReplicate = false;
835
        boolean mnCoreServiceAvailable = false;
836
        boolean mnReadServiceAvailable = false;
837
        boolean mnAuthorizationServiceAvailable = false;
838
        boolean mnStorageServiceAvailable = false;
839
        boolean mnReplicationServiceAvailable = false;
840 6179 cjones
841 6475 jones
        try {
842
            // get the properties of the node based on configuration information
843 6492 jones
            nodeName = PropertyService.getProperty("dataone.nodeName");
844 7030 cjones
            nodeId = PropertyService.getProperty("dataone.nodeId");
845 6492 jones
            subject = PropertyService.getProperty("dataone.subject");
846 6938 cjones
            contactSubject = PropertyService.getProperty("dataone.contactSubject");
847 6475 jones
            nodeDesc = PropertyService.getProperty("dataone.nodeDescription");
848 6476 jones
            nodeTypeString = PropertyService.getProperty("dataone.nodeType");
849
            nodeType = NodeType.convert(nodeTypeString);
850 6475 jones
            nodeSynchronize = new Boolean(PropertyService.getProperty("dataone.nodeSynchronize")).booleanValue();
851
            nodeReplicate = new Boolean(PropertyService.getProperty("dataone.nodeReplicate")).booleanValue();
852
853
            mnCoreServiceVersion = PropertyService.getProperty("dataone.mnCore.serviceVersion");
854
            mnReadServiceVersion = PropertyService.getProperty("dataone.mnRead.serviceVersion");
855
            mnAuthorizationServiceVersion = PropertyService.getProperty("dataone.mnAuthorization.serviceVersion");
856
            mnStorageServiceVersion = PropertyService.getProperty("dataone.mnStorage.serviceVersion");
857
            mnReplicationServiceVersion = PropertyService.getProperty("dataone.mnReplication.serviceVersion");
858
859
            mnCoreServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnCore.serviceAvailable")).booleanValue();
860
            mnReadServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnRead.serviceAvailable")).booleanValue();
861
            mnAuthorizationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnAuthorization.serviceAvailable")).booleanValue();
862
            mnStorageServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnStorage.serviceAvailable")).booleanValue();
863
            mnReplicationServiceAvailable = new Boolean(PropertyService.getProperty("dataone.mnReplication.serviceAvailable")).booleanValue();
864
865 6476 jones
            // Set the properties of the node based on configuration information and
866
            // calls to current status methods
867 7286 leinfelder
            String serviceName = SystemUtil.getSecureContextURL() + "/" + PropertyService.getProperty("dataone.serviceName");
868 6476 jones
            Node node = new Node();
869 6542 leinfelder
            node.setBaseURL(serviceName + "/" + nodeTypeString);
870 6476 jones
            node.setDescription(nodeDesc);
871 6475 jones
872 6476 jones
            // set the node's health information
873
            node.setState(NodeState.UP);
874
875
            // set the ping response to the current value
876
            Ping canPing = new Ping();
877
            canPing.setSuccess(false);
878
            try {
879 6803 leinfelder
            	Date pingDate = ping();
880
                canPing.setSuccess(pingDate != null);
881
            } catch (BaseException e) {
882 6476 jones
                e.printStackTrace();
883 6803 leinfelder
                // guess it can't be pinged
884 6476 jones
            }
885 6610 cjones
886 6476 jones
            node.setPing(canPing);
887 6475 jones
888 6476 jones
            NodeReference identifier = new NodeReference();
889
            identifier.setValue(nodeId);
890
            node.setIdentifier(identifier);
891 6492 jones
            Subject s = new Subject();
892
            s.setValue(subject);
893
            node.addSubject(s);
894 6938 cjones
            Subject contact = new Subject();
895
            contact.setValue(contactSubject);
896
            node.addContactSubject(contact);
897 6476 jones
            node.setName(nodeName);
898
            node.setReplicate(nodeReplicate);
899
            node.setSynchronize(nodeSynchronize);
900 6475 jones
901 6476 jones
            // services: MNAuthorization, MNCore, MNRead, MNReplication, MNStorage
902
            Services services = new Services();
903 6475 jones
904 6476 jones
            Service sMNCore = new Service();
905
            sMNCore.setName("MNCore");
906
            sMNCore.setVersion(mnCoreServiceVersion);
907
            sMNCore.setAvailable(mnCoreServiceAvailable);
908 6475 jones
909 6476 jones
            Service sMNRead = new Service();
910
            sMNRead.setName("MNRead");
911
            sMNRead.setVersion(mnReadServiceVersion);
912
            sMNRead.setAvailable(mnReadServiceAvailable);
913 6475 jones
914 6476 jones
            Service sMNAuthorization = new Service();
915
            sMNAuthorization.setName("MNAuthorization");
916
            sMNAuthorization.setVersion(mnAuthorizationServiceVersion);
917
            sMNAuthorization.setAvailable(mnAuthorizationServiceAvailable);
918 6475 jones
919 6476 jones
            Service sMNStorage = new Service();
920
            sMNStorage.setName("MNStorage");
921
            sMNStorage.setVersion(mnStorageServiceVersion);
922
            sMNStorage.setAvailable(mnStorageServiceAvailable);
923 6475 jones
924 6476 jones
            Service sMNReplication = new Service();
925
            sMNReplication.setName("MNReplication");
926
            sMNReplication.setVersion(mnReplicationServiceVersion);
927
            sMNReplication.setAvailable(mnReplicationServiceAvailable);
928 6475 jones
929 6476 jones
            services.addService(sMNRead);
930
            services.addService(sMNCore);
931
            services.addService(sMNAuthorization);
932
            services.addService(sMNStorage);
933
            services.addService(sMNReplication);
934
            node.setServices(services);
935 6475 jones
936 6476 jones
            // Set the schedule for synchronization
937
            Synchronization synchronization = new Synchronization();
938
            Schedule schedule = new Schedule();
939
            Date now = new Date();
940 6689 leinfelder
            schedule.setYear(PropertyService.getProperty("dataone.nodeSynchronization.schedule.year"));
941
            schedule.setMon(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mon"));
942
            schedule.setMday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.mday"));
943
            schedule.setWday(PropertyService.getProperty("dataone.nodeSynchronization.schedule.wday"));
944
            schedule.setHour(PropertyService.getProperty("dataone.nodeSynchronization.schedule.hour"));
945
            schedule.setMin(PropertyService.getProperty("dataone.nodeSynchronization.schedule.min"));
946
            schedule.setSec(PropertyService.getProperty("dataone.nodeSynchronization.schedule.sec"));
947 6476 jones
            synchronization.setSchedule(schedule);
948
            synchronization.setLastHarvested(now);
949
            synchronization.setLastCompleteHarvest(now);
950
            node.setSynchronization(synchronization);
951 6475 jones
952 6476 jones
            node.setType(nodeType);
953
            return node;
954 6475 jones
955 6476 jones
        } catch (PropertyNotFoundException pnfe) {
956
            String msg = "MNodeService.getCapabilities(): " + "property not found: " + pnfe.getMessage();
957
            logMetacat.error(msg);
958
            throw new ServiceFailure("2162", msg);
959
        }
960 6228 cjones
    }
961 6179 cjones
962 6475 jones
    /**
963
     * Returns the number of operations that have been serviced by the node
964
     * over time periods of one and 24 hours.
965
     *
966
     * @param session - the Session object containing the credentials for the Subject
967
     * @param period - An ISO8601 compatible DateTime range specifying the time
968
     *                 range for which to return operation statistics.
969
     * @param requestor - Limit to operations performed by given requestor identity.
970
     * @param event -  Enumerated value indicating the type of event being examined
971
     * @param format - Limit to events involving objects of the specified format
972
     *
973
     * @return the desired log records
974
     *
975
     * @throws InvalidToken
976
     * @throws ServiceFailure
977
     * @throws NotAuthorized
978
     * @throws InvalidRequest
979
     * @throws NotImplemented
980
     */
981 6610 cjones
    public MonitorList getOperationStatistics(Session session, Date startTime,
982
        Date endTime, Subject requestor, Event event, ObjectFormatIdentifier formatId)
983
        throws NotImplemented, ServiceFailure, NotAuthorized, InsufficientResources, UnsupportedType {
984 6179 cjones
985 6475 jones
        MonitorList monitorList = new MonitorList();
986 6179 cjones
987 6475 jones
        try {
988 6179 cjones
989 6475 jones
            // get log records first
990 7101 leinfelder
            Log logs = getLogRecords(session, startTime, endTime, event, null, 0, null);
991 6179 cjones
992 6475 jones
            // TODO: aggregate by day or hour -- needs clarification
993
            int count = 1;
994
            for (LogEntry logEntry : logs.getLogEntryList()) {
995
                Identifier pid = logEntry.getIdentifier();
996
                Date logDate = logEntry.getDateLogged();
997
                // if we are filtering by format
998
                if (formatId != null) {
999 6692 leinfelder
                    SystemMetadata sysmeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1000 6561 leinfelder
                    if (!sysmeta.getFormatId().getValue().equals(formatId.getValue())) {
1001 6475 jones
                        // does not match
1002
                        continue;
1003
                    }
1004
                }
1005
                MonitorInfo item = new MonitorInfo();
1006
                item.setCount(count);
1007
                item.setDate(new java.sql.Date(logDate.getTime()));
1008
                monitorList.addMonitorInfo(item);
1009 6179 cjones
1010 6475 jones
            }
1011
        } catch (Exception e) {
1012
            e.printStackTrace();
1013
            throw new ServiceFailure("2081", "Could not retrieve statistics: " + e.getMessage());
1014
        }
1015 6345 cjones
1016 6475 jones
        return monitorList;
1017 6345 cjones
1018 6340 cjones
    }
1019
1020 6475 jones
    /**
1021
     * A callback method used by a CN to indicate to a MN that it cannot
1022
     * complete synchronization of the science metadata identified by pid.  Log
1023
     * the event in the metacat event log.
1024
     *
1025
     * @param session
1026
     * @param syncFailed
1027
     *
1028
     * @throws ServiceFailure
1029
     * @throws NotAuthorized
1030
     * @throws NotImplemented
1031
     */
1032
    @Override
1033 6991 leinfelder
    public boolean synchronizationFailed(Session session, SynchronizationFailed syncFailed)
1034 6610 cjones
        throws NotImplemented, ServiceFailure, NotAuthorized {
1035 6179 cjones
1036 6475 jones
        String localId;
1037 7075 cjones
        Identifier pid;
1038
        if ( syncFailed.getPid() != null ) {
1039
            pid = new Identifier();
1040
            pid.setValue(syncFailed.getPid());
1041
            boolean allowed;
1042
1043
            //are we allowed? only CNs
1044
            try {
1045 7142 leinfelder
                allowed = isAdminAuthorized(session);
1046 7075 cjones
                if ( !allowed ){
1047
                    throw new NotAuthorized("2162",
1048
                            "Not allowed to call synchronizationFailed() on this node.");
1049
                }
1050
            } catch (InvalidToken e) {
1051
                throw new NotAuthorized("2162",
1052
                        "Not allowed to call synchronizationFailed() on this node.");
1053 6331 leinfelder
1054 7075 cjones
            }
1055
1056
        } else {
1057
            throw new ServiceFailure("2161", "The identifier cannot be null.");
1058
1059
        }
1060
1061 6475 jones
        try {
1062 7075 cjones
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1063 6475 jones
        } catch (McdbDocNotFoundException e) {
1064 7075 cjones
            throw new ServiceFailure("2161", "The identifier specified by " +
1065
                    syncFailed.getPid() + " was not found on this node.");
1066 6179 cjones
1067 6475 jones
        }
1068
        // TODO: update the CN URL below when the CNRead.SynchronizationFailed
1069
        // method is changed to include the URL as a parameter
1070 7075 cjones
        logMetacat.debug("Synchronization for the object identified by " +
1071
                pid.getValue() + " failed from " + syncFailed.getNodeId() +
1072
                " Logging the event to the Metacat EventLog as a 'syncFailed' event.");
1073 6475 jones
        // TODO: use the event type enum when the SYNCHRONIZATION_FAILED event is added
1074 6532 leinfelder
        String principal = Constants.SUBJECT_PUBLIC;
1075 6506 leinfelder
        if (session != null && session.getSubject() != null) {
1076 6575 cjones
          principal = session.getSubject().getValue();
1077 6506 leinfelder
        }
1078
        try {
1079 6575 cjones
          EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), principal, localId, "synchronization_failed");
1080 6506 leinfelder
        } catch (Exception e) {
1081 7075 cjones
            throw new ServiceFailure("2161", "Could not log the error for: " + pid.getValue());
1082 6991 leinfelder
        }
1083 6475 jones
        //EventLog.getInstance().log("CN URL WILL GO HERE",
1084
        //  session.getSubject().getValue(), localId, Event.SYNCHRONIZATION_FAILED);
1085 6991 leinfelder
        return true;
1086 6179 cjones
1087 6260 cjones
    }
1088
1089 6475 jones
    /**
1090
     * Essentially a get() but with different logging behavior
1091
     */
1092
    @Override
1093 6540 cjones
    public InputStream getReplica(Session session, Identifier pid)
1094 6653 leinfelder
        throws NotAuthorized, NotImplemented, ServiceFailure, InvalidToken {
1095 6179 cjones
1096 6540 cjones
        logMetacat.info("MNodeService.getReplica() called.");
1097
1098 6653 leinfelder
        // cannot be called by public
1099
        if (session == null) {
1100
        	throw new InvalidToken("2183", "No session was provided.");
1101
        }
1102
1103 6631 cjones
        logMetacat.info("MNodeService.getReplica() called with parameters: \n" +
1104
             "\tSession.Subject      = " + session.getSubject().getValue() + "\n" +
1105
             "\tIdentifier           = " + pid.getValue());
1106
1107 6475 jones
        InputStream inputStream = null; // bytes to be returned
1108
        handler = new MetacatHandler(new Timer());
1109
        boolean allowed = false;
1110
        String localId; // the metacat docid for the pid
1111 6179 cjones
1112 6475 jones
        // get the local docid from Metacat
1113
        try {
1114
            localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
1115
        } catch (McdbDocNotFoundException e) {
1116 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
1117
                    pid.getValue() + " does not exist at this node.");
1118
1119 6475 jones
        }
1120 6234 cjones
1121 6552 leinfelder
        Subject targetNodeSubject = session.getSubject();
1122 6185 leinfelder
1123 6552 leinfelder
        // check for authorization to replicate, null session to act as this source MN
1124 6610 cjones
        try {
1125 6777 leinfelder
            allowed = D1Client.getCN().isNodeAuthorized(null, targetNodeSubject, pid);
1126 6610 cjones
        } catch (InvalidToken e1) {
1127
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1128
                + e1.getMessage());
1129
1130
        } catch (NotFound e1) {
1131
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1132
                    + e1.getMessage());
1133 6384 cjones
1134 6610 cjones
        } catch (InvalidRequest e1) {
1135
            throw new ServiceFailure("2181", "Could not determine if node is authorized: "
1136
                    + e1.getMessage());
1137
1138
        }
1139
1140 6540 cjones
        logMetacat.info("Called D1Client.isNodeAuthorized(). Allowed = " + allowed +
1141
            " for identifier " + pid.getValue());
1142
1143 6475 jones
        // if the person is authorized, perform the read
1144
        if (allowed) {
1145
            try {
1146 6986 jones
                inputStream = MetacatHandler.read(localId);
1147 6475 jones
            } catch (Exception e) {
1148 6610 cjones
                throw new ServiceFailure("1020", "The object specified by " +
1149
                    pid.getValue() + "could not be returned due to error: " + e.getMessage());
1150 6475 jones
            }
1151
        }
1152 6384 cjones
1153 6475 jones
        // if we fail to set the input stream
1154
        if (inputStream == null) {
1155 6610 cjones
            throw new ServiceFailure("2181", "The object specified by " +
1156
                pid.getValue() + "does not exist at this node.");
1157 6475 jones
        }
1158
1159
        // log the replica event
1160
        String principal = null;
1161
        if (session.getSubject() != null) {
1162
            principal = session.getSubject().getValue();
1163
        }
1164 6576 cjones
        EventLog.getInstance().log(request.getRemoteAddr(),
1165
            request.getHeader("User-Agent"), principal, localId, "replicate");
1166 6475 jones
1167
        return inputStream;
1168
    }
1169
1170 6573 cjones
    /**
1171 6600 cjones
     * A method to notify the Member Node that the authoritative copy of
1172 6599 cjones
     * system metadata on the Coordinating Nodes has changed.
1173
     *
1174
     * @param session   Session information that contains the identity of the
1175
     *                  calling user as retrieved from the X.509 certificate
1176
     *                  which must be traceable to the CILogon service.
1177
     * @param serialVersion   The serialVersion of the system metadata
1178
     * @param dateSysMetaLastModified  The time stamp for when the system metadata was changed
1179
     * @throws NotImplemented
1180
     * @throws ServiceFailure
1181
     * @throws NotAuthorized
1182
     * @throws InvalidRequest
1183
     * @throws InvalidToken
1184
     */
1185 6991 leinfelder
    public boolean systemMetadataChanged(Session session, Identifier pid,
1186 6599 cjones
        long serialVersion, Date dateSysMetaLastModified)
1187
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
1188
        InvalidToken {
1189
1190 7600 cjones
        // cannot be called by public
1191
        if (session == null) {
1192
        	throw new InvalidToken("2183", "No session was provided.");
1193
        }
1194
1195 6600 cjones
        SystemMetadata currentLocalSysMeta = null;
1196
        SystemMetadata newSysMeta = null;
1197
        CNode cn = D1Client.getCN();
1198
        NodeList nodeList = null;
1199
        Subject callingSubject = null;
1200
        boolean allowed = false;
1201
1202
        // are we allowed to call this?
1203
        callingSubject = session.getSubject();
1204
        nodeList = cn.listNodes();
1205
1206
        for(Node node : nodeList.getNodeList()) {
1207
            // must be a CN
1208
            if ( node.getType().equals(NodeType.CN)) {
1209
               List<Subject> subjectList = node.getSubjectList();
1210
               // the calling subject must be in the subject list
1211
               if ( subjectList.contains(callingSubject)) {
1212
                   allowed = true;
1213
1214
               }
1215
1216
            }
1217
        }
1218
1219
        if (!allowed ) {
1220
            String msg = "The subject identified by " + callingSubject.getValue() +
1221
              " is not authorized to call this service.";
1222
            throw new NotAuthorized("1331", msg);
1223
1224
        }
1225
1226
        // compare what we have locally to what is sent in the change notification
1227
        try {
1228 6692 leinfelder
            currentLocalSysMeta = HazelcastService.getInstance().getSystemMetadataMap().get(pid);
1229
1230
        } catch (RuntimeException e) {
1231 6600 cjones
            String msg = "SystemMetadata for pid " + pid.getValue() +
1232 6692 leinfelder
              " couldn't be updated because it couldn't be found locally: " +
1233 6600 cjones
              e.getMessage();
1234 6692 leinfelder
            logMetacat.error(msg);
1235
            ServiceFailure sf = new ServiceFailure("1333", msg);
1236
            sf.initCause(e);
1237
            throw sf;
1238 6600 cjones
        }
1239
1240
        if (currentLocalSysMeta.getSerialVersion().longValue() < serialVersion ) {
1241
            try {
1242
                newSysMeta = cn.getSystemMetadata(null, pid);
1243
            } catch (NotFound e) {
1244
                // huh? you just said you had it
1245 6692 leinfelder
            	String msg = "On updating the local copy of system metadata " +
1246
                "for pid " + pid.getValue() +", the CN reports it is not found." +
1247
                " The error message was: " + e.getMessage();
1248
                logMetacat.error(msg);
1249
                ServiceFailure sf = new ServiceFailure("1333", msg);
1250
                sf.initCause(e);
1251
                throw sf;
1252 6600 cjones
            }
1253 6692 leinfelder
1254 6600 cjones
            // update the local copy of system metadata for the pid
1255
            try {
1256 6692 leinfelder
                HazelcastService.getInstance().getSystemMetadataMap().put(newSysMeta.getIdentifier(), newSysMeta);
1257 6600 cjones
                logMetacat.info("Updated local copy of system metadata for pid " +
1258
                    pid.getValue() + " after change notification from the CN.");
1259
1260 6692 leinfelder
            } catch (RuntimeException e) {
1261 6600 cjones
                String msg = "SystemMetadata for pid " + pid.getValue() +
1262 6692 leinfelder
                  " couldn't be updated: " +
1263 6600 cjones
                  e.getMessage();
1264 6692 leinfelder
                logMetacat.error(msg);
1265
                ServiceFailure sf = new ServiceFailure("1333", msg);
1266
                sf.initCause(e);
1267
                throw sf;
1268 6600 cjones
            }
1269 8464 leinfelder
1270
            // submit for indexing
1271
            try {
1272
				MetacatSolrIndex.getInstance().submit(newSysMeta.getIdentifier(), newSysMeta, null);
1273
			} catch (Exception e) {
1274
                logMetacat.error("Could not submit changed systemMetadata for indexing, pid: " + newSysMeta.getIdentifier().getValue(), e);
1275
			}
1276 6600 cjones
        }
1277
1278 6991 leinfelder
        return true;
1279
1280 6599 cjones
    }
1281
1282 6795 cjones
    /*
1283
     * Set the replication status for the object on the Coordinating Node
1284
     *
1285
     * @param session - the session for the this target node
1286
     * @param pid - the identifier of the object being updated
1287
     * @param nodeId - the identifier of this target node
1288
     * @param status - the replication status to set
1289
     * @param failure - the exception to include, if any
1290
     */
1291
    private void setReplicationStatus(Session session, Identifier pid,
1292
        NodeReference nodeId, ReplicationStatus status, BaseException failure)
1293
        throws ServiceFailure, NotImplemented, NotAuthorized,
1294
        InvalidRequest {
1295
1296
        // call the CN as the MN to set the replication status
1297
        try {
1298
            this.cn = D1Client.getCN();
1299
            this.cn.setReplicationStatus(session, pid, nodeId,
1300
                    status, failure);
1301
1302
        } catch (InvalidToken e) {
1303 7091 leinfelder
        	String msg = "Could not set the replication status for " + pid.getValue() + " on the CN (InvalidToken): " + e.getMessage();
1304
            logMetacat.error(msg);
1305
        	throw new ServiceFailure("2151",
1306
                    msg);
1307 6795 cjones
1308
        } catch (NotFound e) {
1309 7091 leinfelder
        	String msg = "Could not set the replication status for " + pid.getValue() + " on the CN (NotFound): " + e.getMessage();
1310
            logMetacat.error(msg);
1311
        	throw new ServiceFailure("2151",
1312
                    msg);
1313 6795 cjones
1314
        }
1315
    }
1316 7099 leinfelder
1317
	@Override
1318 7441 leinfelder
	public Identifier generateIdentifier(Session session, String scheme, String fragment)
1319 7099 leinfelder
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1320
			InvalidRequest {
1321 7448 leinfelder
1322 8210 leinfelder
		// check for null session
1323
        if (session == null) {
1324
          throw new InvalidToken("2190", "Session is required to generate an Identifier at this Node.");
1325
        }
1326
1327 7441 leinfelder
		Identifier identifier = new Identifier();
1328 7448 leinfelder
1329 7489 leinfelder
		// handle different schemes
1330
		if (scheme.equalsIgnoreCase(UUID_SCHEME)) {
1331
			// UUID
1332
			UUID uuid = UUID.randomUUID();
1333
            identifier.setValue(UUID_PREFIX + uuid.toString());
1334
		} else if (scheme.equalsIgnoreCase(DOI_SCHEME)) {
1335 7512 leinfelder
			// generate a DOI
1336 7448 leinfelder
			try {
1337 7512 leinfelder
				identifier = DOIService.getInstance().generateDOI();
1338 7448 leinfelder
			} catch (EZIDException e) {
1339 7512 leinfelder
				ServiceFailure sf = new ServiceFailure("2191", "Could not generate DOI: " + e.getMessage());
1340
				sf.initCause(e);
1341
				throw sf;
1342 7448 leinfelder
			}
1343 7489 leinfelder
		} else {
1344
			// default if we don't know the scheme
1345
			if (fragment != null) {
1346
				// for now, just autogen with fragment
1347
				String autogenId = DocumentUtil.generateDocumentId(fragment, 0);
1348
				identifier.setValue(autogenId);
1349
			} else {
1350
				// autogen with no fragment
1351
				String autogenId = DocumentUtil.generateDocumentId(0);
1352
				identifier.setValue(autogenId);
1353
			}
1354 7448 leinfelder
		}
1355
1356 7441 leinfelder
		// TODO: reserve the identifier with the CN. We can only do this when
1357
		// 1) the MN is part of a CN cluster
1358
		// 2) the request is from an authenticated user
1359
1360
		return identifier;
1361 7099 leinfelder
	}
1362 7144 leinfelder
1363
	@Override
1364
	public boolean isAuthorized(Identifier pid, Permission permission)
1365
			throws ServiceFailure, InvalidRequest, InvalidToken, NotFound,
1366
			NotAuthorized, NotImplemented {
1367
1368
		return isAuthorized(null, pid, permission);
1369
	}
1370
1371
	@Override
1372
	public boolean systemMetadataChanged(Identifier pid, long serialVersion, Date dateSysMetaLastModified)
1373
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1374
			InvalidRequest {
1375
1376
		return systemMetadataChanged(null, pid, serialVersion, dateSysMetaLastModified);
1377
	}
1378
1379
	@Override
1380
	public Log getLogRecords(Date fromDate, Date toDate, Event event, String pidFilter,
1381
			Integer start, Integer count) throws InvalidRequest, InvalidToken,
1382
			NotAuthorized, NotImplemented, ServiceFailure {
1383
1384
		return getLogRecords(null, fromDate, toDate, event, pidFilter, start, count);
1385
	}
1386
1387
	@Override
1388
	public DescribeResponse describe(Identifier pid) throws InvalidToken,
1389
			NotAuthorized, NotImplemented, ServiceFailure, NotFound {
1390
1391
		return describe(null, pid);
1392
	}
1393
1394
	@Override
1395
	public InputStream get(Identifier pid) throws InvalidToken, NotAuthorized,
1396
			NotImplemented, ServiceFailure, NotFound, InsufficientResources {
1397
1398
		return get(null, pid);
1399
	}
1400
1401
	@Override
1402
	public Checksum getChecksum(Identifier pid, String algorithm)
1403
			throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
1404
			ServiceFailure, NotFound {
1405
1406
		return getChecksum(null, pid, algorithm);
1407
	}
1408
1409
	@Override
1410
	public SystemMetadata getSystemMetadata(Identifier pid)
1411
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1412
			NotFound {
1413
1414
		return getSystemMetadata(null, pid);
1415
	}
1416
1417
	@Override
1418
	public ObjectList listObjects(Date startTime, Date endTime,
1419
			ObjectFormatIdentifier objectFormatId, Boolean replicaStatus, Integer start,
1420
			Integer count) throws InvalidRequest, InvalidToken, NotAuthorized,
1421
			NotImplemented, ServiceFailure {
1422
1423
		return listObjects(null, startTime, endTime, objectFormatId, replicaStatus, start, count);
1424
	}
1425
1426
	@Override
1427
	public boolean synchronizationFailed(SynchronizationFailed syncFailed)
1428
			throws InvalidToken, NotAuthorized, NotImplemented, ServiceFailure {
1429
1430
		return synchronizationFailed(null, syncFailed);
1431
	}
1432
1433
	@Override
1434
	public InputStream getReplica(Identifier pid) throws InvalidToken,
1435
			NotAuthorized, NotImplemented, ServiceFailure, NotFound,
1436
			InsufficientResources {
1437
1438
		return getReplica(null, pid);
1439
	}
1440
1441
	@Override
1442
	public boolean replicate(SystemMetadata sysmeta, NodeReference sourceNode)
1443
			throws NotImplemented, ServiceFailure, NotAuthorized,
1444
			InvalidRequest, InvalidToken, InsufficientResources,
1445
			UnsupportedType {
1446
1447
		return replicate(null, sysmeta, sourceNode);
1448
	}
1449
1450
	@Override
1451
	public Identifier create(Identifier pid, InputStream object,
1452
			SystemMetadata sysmeta) throws IdentifierNotUnique,
1453
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1454
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1455
			UnsupportedType {
1456
1457
		return create(null, pid, object, sysmeta);
1458
	}
1459
1460
	@Override
1461
	public Identifier delete(Identifier pid) throws InvalidToken,
1462
			ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1463
1464
		return delete(null, pid);
1465
	}
1466
1467
	@Override
1468 7441 leinfelder
	public Identifier generateIdentifier(String scheme, String fragment)
1469 7144 leinfelder
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1470
			InvalidRequest {
1471
1472 7441 leinfelder
		return generateIdentifier(null, scheme, fragment);
1473 7144 leinfelder
	}
1474
1475
	@Override
1476
	public Identifier update(Identifier pid, InputStream object,
1477
			Identifier newPid, SystemMetadata sysmeta) throws IdentifierNotUnique,
1478
			InsufficientResources, InvalidRequest, InvalidSystemMetadata,
1479
			InvalidToken, NotAuthorized, NotImplemented, ServiceFailure,
1480
			UnsupportedType, NotFound {
1481
1482
		return update(null, pid, object, newPid, sysmeta);
1483
	}
1484 7417 leinfelder
1485
	@Override
1486
	public QueryEngineDescription getQueryEngineDescription(String engine)
1487
			throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented,
1488
			NotFound {
1489 7772 tao
	    if(engine != null && engine.equals(EnabledQueryEngines.PATHQUERYENGINE)) {
1490 8162 tao
	        if(!EnabledQueryEngines.getInstance().isEnabled(EnabledQueryEngines.PATHQUERYENGINE)) {
1491
                throw new NotImplemented("0000", "MNodeService.query - the query engine "+engine +" hasn't been implemented or has been disabled.");
1492
            }
1493 7634 tao
	        QueryEngineDescription qed = new QueryEngineDescription();
1494 7772 tao
	        qed.setName(EnabledQueryEngines.PATHQUERYENGINE);
1495 7634 tao
	        qed.setQueryEngineVersion("1.0");
1496
	        qed.addAdditionalInfo("This is the traditional structured query for Metacat");
1497
	        Vector<String> pathsForIndexing = null;
1498
	        try {
1499
	            pathsForIndexing = SystemUtil.getPathsForIndexing();
1500
	        } catch (MetacatUtilException e) {
1501
	            logMetacat.warn("Could not get index paths", e);
1502
	        }
1503
	        for (String fieldName: pathsForIndexing) {
1504
	            QueryField field = new QueryField();
1505
	            field.addDescription("Indexed field for path '" + fieldName + "'");
1506
	            field.setName(fieldName);
1507
	            field.setReturnable(true);
1508
	            field.setSearchable(true);
1509
	            field.setSortable(false);
1510
	            // TODO: determine type and multivaluedness
1511
	            field.setType(String.class.getName());
1512
	            //field.setMultivalued(true);
1513
	            qed.addQueryField(field);
1514
	        }
1515
	        return qed;
1516 7772 tao
	    } else if (engine != null && engine.equals(EnabledQueryEngines.SOLRENGINE)) {
1517 7781 tao
	        if(!EnabledQueryEngines.getInstance().isEnabled(EnabledQueryEngines.SOLRENGINE)) {
1518 7772 tao
                throw new NotImplemented("0000", "MNodeService.getQueryEngineDescription - the query engine "+engine +" hasn't been implemented or has been disabled.");
1519 7781 tao
            }
1520 7634 tao
	        try {
1521 7662 tao
	            QueryEngineDescription qed = MetacatSolrEngineDescriptionHandler.getInstance().getQueryEngineDescritpion();
1522 7634 tao
	            return qed;
1523
	        } catch (Exception e) {
1524
	            e.printStackTrace();
1525
	            throw new ServiceFailure("Solr server error", e.getMessage());
1526
	        }
1527
	    } else {
1528
	        throw new NotFound("404", "The Metacat member node can't find the query engine - "+engine);
1529
	    }
1530
1531 7417 leinfelder
	}
1532
1533
	@Override
1534
	public QueryEngineList listQueryEngines() throws InvalidToken,
1535
			ServiceFailure, NotAuthorized, NotImplemented {
1536
		QueryEngineList qel = new QueryEngineList();
1537 7781 tao
		//qel.addQueryEngine(EnabledQueryEngines.PATHQUERYENGINE);
1538
		//qel.addQueryEngine(EnabledQueryEngines.SOLRENGINE);
1539
		List<String> enables = EnabledQueryEngines.getInstance().getEnabled();
1540 7772 tao
		for(String name : enables) {
1541
		    qel.addQueryEngine(name);
1542 7781 tao
		}
1543 7417 leinfelder
		return qel;
1544
	}
1545
1546
	@Override
1547
	public InputStream query(String engine, String query) throws InvalidToken,
1548
			ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented,
1549
			NotFound {
1550 7648 tao
	    String user = Constants.SUBJECT_PUBLIC;
1551
        String[] groups= null;
1552 7680 tao
        Set<Subject> subjects = null;
1553 7648 tao
        if (session != null) {
1554
            user = session.getSubject().getValue();
1555 7680 tao
            subjects = AuthUtils.authorizedClientSubjects(session);
1556 7648 tao
            if (subjects != null) {
1557
                List<String> groupList = new ArrayList<String>();
1558
                for (Subject subject: subjects) {
1559
                    groupList.add(subject.getValue());
1560
                }
1561
                groups = groupList.toArray(new String[0]);
1562
            }
1563 7680 tao
        } else {
1564
            //add the public user subject to the set
1565
            Subject subject = new Subject();
1566
            subject.setValue(Constants.SUBJECT_PUBLIC);
1567 7757 leinfelder
            subjects = new HashSet<Subject>();
1568 7680 tao
            subjects.add(subject);
1569 7648 tao
        }
1570 7680 tao
        //System.out.println("====== user is "+user);
1571
        //System.out.println("====== groups are "+groups);
1572 7772 tao
		if (engine != null && engine.equals(EnabledQueryEngines.PATHQUERYENGINE)) {
1573 8162 tao
		    if(!EnabledQueryEngines.getInstance().isEnabled(EnabledQueryEngines.PATHQUERYENGINE)) {
1574
                throw new NotImplemented("0000", "MNodeService.query - the query engine "+engine +" hasn't been implemented or has been disabled.");
1575
            }
1576 7417 leinfelder
			try {
1577
				DBQuery queryobj = new DBQuery();
1578 7648 tao
1579 7417 leinfelder
				String results = queryobj.performPathquery(query, user, groups);
1580 7757 leinfelder
				ContentTypeByteArrayInputStream ctbais = new ContentTypeByteArrayInputStream(results.getBytes(MetaCatServlet.DEFAULT_ENCODING));
1581
				ctbais.setContentType("text/xml");
1582
				return ctbais;
1583 7417 leinfelder
1584
			} catch (Exception e) {
1585 7757 leinfelder
				throw new ServiceFailure("Pathquery error", e.getMessage());
1586 7417 leinfelder
			}
1587
1588 7772 tao
		} else if (engine != null && engine.equals(EnabledQueryEngines.SOLRENGINE)) {
1589 7781 tao
		    if(!EnabledQueryEngines.getInstance().isEnabled(EnabledQueryEngines.SOLRENGINE)) {
1590 7772 tao
		        throw new NotImplemented("0000", "MNodeService.query - the query engine "+engine +" hasn't been implemented or has been disabled.");
1591 7781 tao
		    }
1592 7634 tao
		    logMetacat.info("The query is ==================================== \n"+query);
1593 7620 tao
		    try {
1594 7634 tao
1595 7680 tao
                return MetacatSolrIndex.getInstance().query(query, subjects);
1596 7620 tao
            } catch (Exception e) {
1597
                // TODO Auto-generated catch block
1598
                throw new ServiceFailure("Solr server error", e.getMessage());
1599
            }
1600 7417 leinfelder
		}
1601
		return null;
1602
	}
1603 7849 leinfelder
1604
	/**
1605
	 * Given an existing Science Metadata PID, this method mints a DOI
1606
	 * and updates the original object "publishing" the update with the DOI.
1607
	 * This includes updating the ORE map that describes the Science Metadata+data.
1608
	 * TODO: ensure all referenced objects allow public read
1609
	 *
1610
	 * @see https://projects.ecoinformatics.org/ecoinfo/issues/6014
1611
	 *
1612
	 * @param originalIdentifier
1613
	 * @param request
1614 7864 leinfelder
	 * @throws InvalidRequest
1615
	 * @throws NotImplemented
1616
	 * @throws NotAuthorized
1617
	 * @throws ServiceFailure
1618
	 * @throws InvalidToken
1619 7849 leinfelder
	 * @throws NotFound
1620 7864 leinfelder
	 * @throws InvalidSystemMetadata
1621
	 * @throws InsufficientResources
1622
	 * @throws UnsupportedType
1623
	 * @throws IdentifierNotUnique
1624 7849 leinfelder
	 */
1625 7864 leinfelder
	public Identifier publish(Session session, Identifier originalIdentifier) throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, NotFound, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1626 7849 leinfelder
1627 8141 leinfelder
1628
		// get the original SM
1629
		SystemMetadata originalSystemMetadata = this.getSystemMetadata(session, originalIdentifier);
1630
1631
		// make copy of it
1632
		SystemMetadata sysmeta = new SystemMetadata();
1633
		try {
1634
			BeanUtils.copyProperties(sysmeta, originalSystemMetadata);
1635
		} catch (Exception e) {
1636
			// report as service failure
1637
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1638
			sf.initCause(e);
1639
			throw sf;
1640
		}
1641
1642 7849 leinfelder
		// mint a DOI for the new revision
1643
		Identifier newIdentifier = this.generateIdentifier(session, MNodeService.DOI_SCHEME, null);
1644 8141 leinfelder
1645
		// set new metadata values
1646 7849 leinfelder
		sysmeta.setIdentifier(newIdentifier);
1647
		sysmeta.setObsoletes(originalIdentifier);
1648
		sysmeta.setObsoletedBy(null);
1649
1650
		// get the bytes
1651
		InputStream inputStream = this.get(session, originalIdentifier);
1652
1653
		// update the object
1654 7864 leinfelder
		this.update(session, originalIdentifier, inputStream, newIdentifier, sysmeta);
1655 7849 leinfelder
1656 7864 leinfelder
		// update ORE that references the scimeta
1657 8190 leinfelder
		// first try the naive method, then check the SOLR index
1658 7849 leinfelder
		try {
1659 7864 leinfelder
			String localId = IdentifierManager.getInstance().getLocalId(originalIdentifier.getValue());
1660 7849 leinfelder
1661 7864 leinfelder
			Identifier potentialOreIdentifier = new Identifier();
1662
			potentialOreIdentifier.setValue(SystemMetadataFactory.RESOURCE_MAP_PREFIX + localId);
1663 7849 leinfelder
1664 7864 leinfelder
			InputStream oreInputStream = null;
1665
			try {
1666
				oreInputStream = this.get(session, potentialOreIdentifier);
1667
			} catch (NotFound nf) {
1668
				// this is probably okay for many sci meta data docs
1669
				logMetacat.warn("No potential ORE map found for: " + potentialOreIdentifier.getValue());
1670 8190 leinfelder
				// try the SOLR index
1671 8200 leinfelder
				List<Identifier> potentialOreIdentifiers = this.lookupOreFor(originalIdentifier, false);
1672 8190 leinfelder
				if (potentialOreIdentifiers != null) {
1673
					potentialOreIdentifier = potentialOreIdentifiers.get(0);
1674
					try {
1675
						oreInputStream = this.get(session, potentialOreIdentifier);
1676
					} catch (NotFound nf2) {
1677
						// this is probably okay for many sci meta data docs
1678
						logMetacat.warn("No potential ORE map found for: " + potentialOreIdentifier.getValue());
1679
					}
1680
				}
1681 7864 leinfelder
			}
1682
			if (oreInputStream != null) {
1683
				Identifier newOreIdentifier = MNodeService.getInstance(request).generateIdentifier(session, MNodeService.UUID_SCHEME, null);
1684
1685
				Map<Identifier, Map<Identifier, List<Identifier>>> resourceMapStructure = ResourceMapFactory.getInstance().parseResourceMap(oreInputStream);
1686
				Map<Identifier, List<Identifier>> sciMetaMap = resourceMapStructure.get(potentialOreIdentifier);
1687
				List<Identifier> dataIdentifiers = sciMetaMap.get(originalIdentifier);
1688
1689
				// TODO: ensure all data package objects allow public read
1690
1691
				// reconstruct the ORE with the new identifiers
1692
				sciMetaMap.remove(originalIdentifier);
1693
				sciMetaMap.put(newIdentifier, dataIdentifiers);
1694
1695
				ResourceMap resourceMap = ResourceMapFactory.getInstance().createResourceMap(newOreIdentifier, sciMetaMap);
1696
				String resourceMapString = ResourceMapFactory.getInstance().serializeResourceMap(resourceMap);
1697
1698
				// get the original ORE SM and update the values
1699 8141 leinfelder
				SystemMetadata originalOreSysMeta = this.getSystemMetadata(session, potentialOreIdentifier);
1700
				SystemMetadata oreSysMeta = new SystemMetadata();
1701
				try {
1702
					BeanUtils.copyProperties(oreSysMeta, originalOreSysMeta);
1703
				} catch (Exception e) {
1704
					// report as service failure
1705
					ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1706
					sf.initCause(e);
1707
					throw sf;
1708
				}
1709
1710 7864 leinfelder
				oreSysMeta.setIdentifier(newOreIdentifier);
1711
				oreSysMeta.setObsoletes(potentialOreIdentifier);
1712
				oreSysMeta.setObsoletedBy(null);
1713
				oreSysMeta.setSize(BigInteger.valueOf(resourceMapString.getBytes("UTF-8").length));
1714
				oreSysMeta.setChecksum(ChecksumUtil.checksum(resourceMapString.getBytes("UTF-8"), oreSysMeta.getChecksum().getAlgorithm()));
1715
1716
				// save the updated ORE
1717
				this.update(
1718
						session,
1719
						potentialOreIdentifier,
1720
						new ByteArrayInputStream(resourceMapString.getBytes("UTF-8")),
1721
						newOreIdentifier,
1722
						oreSysMeta);
1723
1724 8362 leinfelder
			} else {
1725
				// create a new ORE for them
1726
				// https://projects.ecoinformatics.org/ecoinfo/issues/6194
1727
				try {
1728
					// find the local id for the NEW package.
1729
					String newLocalId = IdentifierManager.getInstance().getLocalId(newIdentifier.getValue());
1730
1731
					@SuppressWarnings("unused")
1732
					SystemMetadata extraSysMeta = SystemMetadataFactory.createSystemMetadata(newLocalId, true, false);
1733
					// should be done generating the ORE here
1734
1735
				} catch (Exception e) {
1736
					// oops, guess there was a problem - no package for you
1737
					logMetacat.error("Could not generate new ORE for published object: " + newIdentifier.getValue(), e);
1738
				}
1739 7864 leinfelder
			}
1740
		} catch (McdbDocNotFoundException e) {
1741
			// report as service failure
1742
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1743
			sf.initCause(e);
1744
			throw sf;
1745
		} catch (UnsupportedEncodingException e) {
1746
			// report as service failure
1747
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1748
			sf.initCause(e);
1749
			throw sf;
1750
		} catch (OREException e) {
1751
			// report as service failure
1752
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1753
			sf.initCause(e);
1754
			throw sf;
1755
		} catch (URISyntaxException e) {
1756
			// report as service failure
1757
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1758
			sf.initCause(e);
1759
			throw sf;
1760
		} catch (OREParserException e) {
1761
			// report as service failure
1762
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1763
			sf.initCause(e);
1764
			throw sf;
1765
		} catch (ORESerialiserException e) {
1766
			// report as service failure
1767
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1768
			sf.initCause(e);
1769
			throw sf;
1770
		} catch (NoSuchAlgorithmException e) {
1771
			// report as service failure
1772
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1773
			sf.initCause(e);
1774
			throw sf;
1775 7849 leinfelder
		}
1776
1777
		return newIdentifier;
1778
	}
1779 7850 leinfelder
1780
	/**
1781 8190 leinfelder
	 * Determines if we already have registered an ORE map for this package
1782
	 * NOTE: uses a solr query to locate OREs for the object
1783
	 * @param guid of the EML/packaging object
1784
	 * @return list of resource map identifiers for the given pid
1785
	 */
1786 8200 leinfelder
	public List<Identifier> lookupOreFor(Identifier guid, boolean includeObsolete) {
1787 8190 leinfelder
		// Search for the ORE if we can find it
1788
		String pid = guid.getValue();
1789
		List<Identifier> retList = null;
1790
		try {
1791 8200 leinfelder
			String query = "fl=id,resourceMap&wt=xml&q=-obsoletedBy:*+resourceMap:*+id:\"" + pid + "\"";;
1792
			if (includeObsolete) {
1793
				query = "fl=id,resourceMap&wt=xml&q=resourceMap:*+id:\"" + pid + "\"";
1794
			}
1795
1796 8190 leinfelder
			InputStream results = this.query("solr", query);
1797
			org.w3c.dom.Node rootNode = XMLUtilities.getXMLReaderAsDOMTreeRootNode(new InputStreamReader(results, "UTF-8"));
1798
			//String resultString = XMLUtilities.getDOMTreeAsString(rootNode);
1799
			org.w3c.dom.NodeList nodeList = XMLUtilities.getNodeListWithXPath(rootNode, "//arr[@name=\"resourceMap\"]/str");
1800
			if (nodeList != null && nodeList.getLength() > 0) {
1801
				retList = new ArrayList<Identifier>();
1802
				for (int i = 0; i < nodeList.getLength(); i++) {
1803
					String found = nodeList.item(i).getFirstChild().getNodeValue();
1804
					Identifier oreId = new Identifier();
1805
					oreId.setValue(found);
1806
					retList.add(oreId);
1807
				}
1808
			}
1809
		} catch (Exception e) {
1810
			logMetacat.error("Error checking for resourceMap[s] on pid " + pid + ". " + e.getMessage(), e);
1811
		}
1812
1813
		return retList;
1814
	}
1815
1816
	/**
1817 7850 leinfelder
	 * Packages the given package in a Bagit collection for download
1818
	 * @param pid
1819
	 * @throws NotImplemented
1820
	 * @throws NotFound
1821
	 * @throws NotAuthorized
1822
	 * @throws ServiceFailure
1823
	 * @throws InvalidToken
1824
	 */
1825 7855 leinfelder
	public InputStream getPackage(Session session, Identifier pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
1826
1827 7850 leinfelder
		InputStream bagInputStream = null;
1828
		BagFactory bagFactory = new BagFactory();
1829
		Bag bag = bagFactory.createBag();
1830
1831
		// track the temp files we use so we can delete them when finished
1832
		List<File> tempFiles = new ArrayList<File>();
1833
1834
		// the pids to include in the package
1835
		List<Identifier> packagePids = new ArrayList<Identifier>();
1836
1837 7855 leinfelder
		// catch non-D1 service errors and throw as ServiceFailures
1838
		try {
1839 8437 walker
			//Create a map of dataone ids and file names
1840
			Map<Identifier, String> fileNames = new HashMap<Identifier, String>();
1841 7855 leinfelder
1842
			// find the package contents
1843
			SystemMetadata sysMeta = this.getSystemMetadata(session, pid);
1844 8025 leinfelder
			if (ObjectFormatCache.getInstance().getFormat(sysMeta.getFormatId()).getFormatType().equals("RESOURCE")) {
1845 8437 walker
				//Get the resource map as a map of Identifiers
1846 7855 leinfelder
				InputStream oreInputStream = this.get(session, pid);
1847
				Map<Identifier, Map<Identifier, List<Identifier>>> resourceMapStructure = ResourceMapFactory.getInstance().parseResourceMap(oreInputStream);
1848
				packagePids.addAll(resourceMapStructure.keySet());
1849 8437 walker
				//Loop through each object in this resource map
1850 7855 leinfelder
				for (Map<Identifier, List<Identifier>> entries: resourceMapStructure.values()) {
1851 8437 walker
					//Loop through each metadata object in this entry
1852
					Set<Identifier> metadataIdentifiers = entries.keySet();
1853
					for(Identifier metadataID: metadataIdentifiers){
1854
						try{
1855
							//Get the system metadata for this metadata object
1856
							SystemMetadata metadataSysMeta = this.getSystemMetadata(session, metadataID);
1857
1858
							//If this is in eml format, extract the filename and GUID from each entity in its package
1859
							if (metadataSysMeta.getFormatId().getValue().startsWith("eml://")) {
1860
								//Get the package
1861
								DataPackageParserInterface parser = new Eml200DataPackageParser();
1862
								InputStream emlStream = this.get(session, metadataID);
1863
								parser.parse(emlStream);
1864
								DataPackage dataPackage = parser.getDataPackage();
1865
1866
								//Get all the entities in this package and loop through each to extract its ID and file name
1867
								Entity[] entities = dataPackage.getEntityList();
1868
								for(Entity entity: entities){
1869
									try{
1870
										//Get the file name from the metadata
1871
										String fileNameFromMetadata = entity.getName();
1872
1873
										//Get the ecogrid URL from the metadata
1874
										String ecogridIdentifier = entity.getEntityIdentifier();
1875
										//Parse the ecogrid URL to get the local id
1876
										String idFromMetadata = DocumentUtil.getAccessionNumberFromEcogridIdentifier(ecogridIdentifier);
1877
1878
										//Get the docid and rev pair
1879
										String docid = DocumentUtil.getDocIdFromString(idFromMetadata);
1880
										String rev = DocumentUtil.getRevisionStringFromString(idFromMetadata);
1881
1882
										//Get the GUID
1883
										String guid = IdentifierManager.getInstance().getGUID(docid, Integer.valueOf(rev));
1884
										Identifier dataIdentifier = new Identifier();
1885
										dataIdentifier.setValue(guid);
1886
1887
										//Add the GUID to our GUID & file name map
1888
										fileNames.put(dataIdentifier, fileNameFromMetadata);
1889
									}
1890
									catch(Exception e){
1891
										//Prevent just one entity error
1892
										e.printStackTrace();
1893
										logMetacat.debug(e.getMessage(), e);
1894
									}
1895
								}
1896
							}
1897
						}
1898
						catch(Exception e){
1899
							//Catch errors that would prevent package download
1900
							logMetacat.debug(e.toString());
1901
						}
1902
					}
1903 7855 leinfelder
					packagePids.addAll(entries.keySet());
1904
					for (List<Identifier> dataPids: entries.values()) {
1905
						packagePids.addAll(dataPids);
1906
					}
1907 7850 leinfelder
				}
1908 7855 leinfelder
			} else {
1909
				// just the lone pid in this package
1910
				packagePids.add(pid);
1911 7850 leinfelder
			}
1912 8436 walker
1913 8437 walker
			//Create a temp file, then delete it and make a directory with that name
1914
			File tempDir = File.createTempFile("temp", Long.toString(System.nanoTime()));
1915
			tempDir.delete();
1916
			tempDir = new File(tempDir.getPath() + "_dir");
1917
			tempDir.mkdir();
1918 8436 walker
			tempFiles.add(tempDir);
1919
1920 7855 leinfelder
			// track the pid-to-file mapping
1921
			StringBuffer pidMapping = new StringBuffer();
1922 8436 walker
1923 7855 leinfelder
			// loop through the package contents
1924
			for (Identifier entryPid: packagePids) {
1925 8436 walker
				//Get the system metadata for each item
1926 8437 walker
				SystemMetadata entrySysMeta = this.getSystemMetadata(session, entryPid);
1927 8436 walker
1928
				String objectFormatType = ObjectFormatCache.getInstance().getFormat(entrySysMeta.getFormatId()).getFormatType();
1929 8437 walker
				String fileName = null;
1930 8436 walker
1931 8437 walker
				//TODO: Be more specific of what characters to replace. Make sure periods arent replaced for the filename from metadata
1932
				//Our default file name is just the ID + format type (e.g. walker.1.1-DATA)
1933
				fileName = entryPid.getValue().replaceAll("[^a-zA-Z0-9\\-\\.]", "_") + "-" + objectFormatType;
1934
1935
				if(fileNames.containsKey(entryPid)){
1936
					//Let's use the file name and extension from the metadata is we have it
1937
					fileName = entryPid.getValue().replaceAll("[^a-zA-Z0-9\\-\\.]", "_") + "-" + fileNames.get(entryPid).replaceAll("[^a-zA-Z0-9\\-\\.]", "_");
1938
				}
1939
				else{
1940
					//If we couldn't find a given file name, use the system metadata extension
1941
					String extension = ObjectFormatInfo.instance().getExtension(entrySysMeta.getFormatId().getValue());
1942
					fileName += extension;
1943
				}
1944
1945 8436 walker
		        //Create a new file for this item and add to the list
1946 8437 walker
				File tempFile = new File(tempDir, fileName);
1947 7855 leinfelder
				tempFiles.add(tempFile);
1948 8436 walker
1949
				InputStream entryInputStream = this.get(session, entryPid);
1950 7855 leinfelder
				IOUtils.copy(entryInputStream, new FileOutputStream(tempFile));
1951
				bag.addFileToPayload(tempFile);
1952
				pidMapping.append(entryPid.getValue() + "\t" + "data/" + tempFile.getName() + "\n");
1953
			}
1954
1955
			//add the the pid to data file map
1956 8436 walker
			File pidMappingFile = new File(tempDir, "pid-mapping.txt");
1957 7855 leinfelder
			IOUtils.write(pidMapping.toString(), new FileOutputStream(pidMappingFile));
1958
			bag.addFileAsTag(pidMappingFile);
1959 8026 leinfelder
			tempFiles.add(pidMappingFile);
1960
1961 7855 leinfelder
			bag = bag.makeComplete();
1962 8026 leinfelder
1963 8436 walker
			///Now create the zip file
1964 8437 walker
			//Use the pid as the file name prefix, replacing all non-word characters
1965
			String zipName = pid.getValue().replaceAll("\\W", "_");
1966 8160 leinfelder
1967 8436 walker
			File bagFile = new File(tempDir, zipName+".zip");
1968
1969 7855 leinfelder
			bag.setFile(bagFile);
1970 7860 leinfelder
			ZipWriter zipWriter = new ZipWriter(bagFactory);
1971 7855 leinfelder
			bag.write(zipWriter, bagFile);
1972
			bagFile = bag.getFile();
1973 8026 leinfelder
			// use custom FIS that will delete the file when closed
1974
			bagInputStream = new DeleteOnCloseFileInputStream(bagFile);
1975
			// also mark for deletion on shutdown in case the stream is never closed
1976
			bagFile.deleteOnExit();
1977 8436 walker
			tempFiles.add(bagFile);
1978 7855 leinfelder
1979 8026 leinfelder
			// clean up other temp files
1980 8436 walker
			for(int i=tempFiles.size()-1; i>=0; i--){
1981
				File tf = new File(tempFiles.get(i).getPath());
1982 7855 leinfelder
				tf.delete();
1983
			}
1984 8436 walker
1985 7855 leinfelder
		} catch (IOException e) {
1986
			// report as service failure
1987
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1988
			sf.initCause(e);
1989
			throw sf;
1990
		} catch (OREException e) {
1991
			// report as service failure
1992
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1993
			sf.initCause(e);
1994
			throw sf;
1995
		} catch (URISyntaxException e) {
1996
			// report as service failure
1997
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
1998
			sf.initCause(e);
1999
			throw sf;
2000
		} catch (OREParserException e) {
2001
			// report as service failure
2002
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2003
			sf.initCause(e);
2004
			throw sf;
2005 7850 leinfelder
		}
2006
2007
		return bagInputStream;
2008
2009
	}
2010 7860 leinfelder
2011
	/**
2012
	 * Get a rendered view of the object identified by pid.
2013
	 * Uses the registered format given by the format parameter.
2014
	 * Typically, this is structured HTML that can be styled with CSS.
2015
	 * @param session
2016
	 * @param pid
2017
	 * @param format
2018
	 * @return
2019
	 * @throws InvalidToken
2020
	 * @throws ServiceFailure
2021
	 * @throws NotAuthorized
2022
	 * @throws NotFound
2023
	 * @throws NotImplemented
2024
	 */
2025 7861 leinfelder
	public InputStream getView(Session session, Identifier pid, String format) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented {
2026 7860 leinfelder
		InputStream resultInputStream = null;
2027
2028
		SystemMetadata sysMeta = this.getSystemMetadata(session, pid);
2029
		InputStream object = this.get(session, pid);
2030
2031
		try {
2032
			// can only transform metadata, really
2033 8013 leinfelder
			ObjectFormat objectFormat = ObjectFormatCache.getInstance().getFormat(sysMeta.getFormatId());
2034 7860 leinfelder
			if (objectFormat.getFormatType().equals("METADATA")) {
2035
				// transform
2036
				DBTransform transformer = new DBTransform();
2037
	            String documentContent = IOUtils.toString(object, "UTF-8");
2038
	            String sourceType = objectFormat.getFormatId().getValue();
2039
	            String targetType = "-//W3C//HTML//EN";
2040
	            ByteArrayOutputStream baos = new ByteArrayOutputStream();
2041
	            Writer writer = new OutputStreamWriter(baos , "UTF-8");
2042
	            // TODO: include more params?
2043
	            Hashtable<String, String[]> params = new Hashtable<String, String[]>();
2044 8014 leinfelder
	            String localId = null;
2045
				try {
2046
					localId = IdentifierManager.getInstance().getLocalId(pid.getValue());
2047
				} catch (McdbDocNotFoundException e) {
2048
					throw new NotFound("1020", e.getMessage());
2049
				}
2050
	            params.put("qformat", new String[] {format});
2051
	            params.put("docid", new String[] {localId});
2052
	            params.put("pid", new String[] {pid.getValue()});
2053 7860 leinfelder
	            transformer.transformXMLDocument(
2054
	                    documentContent ,
2055
	                    sourceType,
2056
	                    targetType ,
2057
	                    format,
2058
	                    writer,
2059
	                    params,
2060
	                    null //sessionid
2061
	                    );
2062
2063
	            // finally, get the HTML back
2064
	            resultInputStream = new ContentTypeByteArrayInputStream(baos.toByteArray());
2065
	            ((ContentTypeByteArrayInputStream) resultInputStream).setContentType("text/html");
2066
2067
			} else {
2068
				// just return the raw bytes
2069
				resultInputStream = object;
2070
			}
2071
		} catch (IOException e) {
2072
			// report as service failure
2073
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2074
			sf.initCause(e);
2075
			throw sf;
2076
		} catch (PropertyNotFoundException e) {
2077
			// report as service failure
2078
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2079
			sf.initCause(e);
2080
			throw sf;
2081
		} catch (SQLException e) {
2082
			// report as service failure
2083
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2084
			sf.initCause(e);
2085
			throw sf;
2086
		} catch (ClassNotFoundException e) {
2087
			// report as service failure
2088
			ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
2089
			sf.initCause(e);
2090
			throw sf;
2091
		}
2092
2093
		return resultInputStream;
2094
2095 8026 leinfelder
	}
2096 6795 cjones
2097 6179 cjones
}