org.trellisldp.http.impl.PostHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.trellisldp.http.impl.PostHandler.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.trellisldp.http.impl;

import static java.net.URI.create;
import static java.time.Instant.now;
import static java.util.Collections.singletonMap;
import static java.util.Objects.nonNull;
import static java.util.Optional.ofNullable;
import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.serverError;
import static javax.ws.rs.core.Response.status;
import static org.apache.commons.rdf.api.RDFSyntax.TURTLE;
import static org.slf4j.LoggerFactory.getLogger;
import static org.trellisldp.api.RDFUtils.TRELLIS_PREFIX;
import static org.trellisldp.http.impl.RdfUtils.ldpResourceTypes;
import static org.trellisldp.http.impl.RdfUtils.skolemizeQuads;
import static org.trellisldp.vocabulary.Trellis.PreferServerManaged;
import static org.trellisldp.vocabulary.Trellis.PreferUserManaged;

import java.io.File;
import java.net.URI;
import java.util.Map;
import java.util.Optional;

import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.rdf.api.IRI;
import org.apache.commons.rdf.api.RDFSyntax;

import org.slf4j.Logger;
import org.trellisldp.api.BinaryService;
import org.trellisldp.api.IOService;
import org.trellisldp.api.ResourceService;
import org.trellisldp.api.Session;
import org.trellisldp.http.domain.Digest;
import org.trellisldp.http.domain.LdpRequest;
import org.trellisldp.vocabulary.DC;
import org.trellisldp.vocabulary.LDP;
import org.trellisldp.vocabulary.RDF;
import org.trellisldp.vocabulary.XSD;

/**
 * The POST response handler
 *
 * @author acoburn
 */
public class PostHandler extends ContentBearingHandler {

    private static final Logger LOGGER = getLogger(PostHandler.class);

    private final String id;

    /**
     * Create a builder for an LDP POST response
     * @param req the LDP request
     * @param id the new resource's identifier
     * @param entity the entity
     * @param resourceService the resource service
     * @param ioService the serialization service
     * @param binaryService the datastream service
     * @param baseUrl the base URL
     */
    public PostHandler(final LdpRequest req, final String id, final File entity,
            final ResourceService resourceService, final IOService ioService, final BinaryService binaryService,
            final String baseUrl) {
        super(req, entity, resourceService, ioService, binaryService, baseUrl);
        this.id = id;
    }

    /**
     * Create a new resource
     * @return the response builder
     */
    public ResponseBuilder createResource() {
        final String baseUrl = getBaseUrl();
        final String identifier = baseUrl + req.getPartition() + req.getPath() + id;
        final String contentType = req.getContentType();
        final Session session = ofNullable(req.getSession()).orElseGet(HttpSession::new);

        LOGGER.info("Creating resource as {}", identifier);

        final Optional<RDFSyntax> rdfSyntax = ofNullable(contentType).flatMap(RDFSyntax::byMediaType)
                .filter(SUPPORTED_RDF_TYPES::contains);

        final IRI defaultType = nonNull(contentType) && !rdfSyntax.isPresent() ? LDP.NonRDFSource : LDP.RDFSource;
        final IRI internalId = rdf.createIRI(TRELLIS_PREFIX + req.getPartition() + req.getPath() + id);

        // Add LDP type (ldp:Resource results in the defaultType)
        final IRI ldpType = ofNullable(req.getLink()).filter(l -> "type".equals(l.getRel())).map(Link::getUri)
                .map(URI::toString).filter(l -> l.startsWith(LDP.URI)).map(rdf::createIRI)
                .filter(l -> !LDP.Resource.equals(l)).orElse(defaultType);

        if (ldpType.equals(LDP.NonRDFSource) && rdfSyntax.isPresent()) {
            return status(BAD_REQUEST).type(TEXT_PLAIN).entity("Cannot save a NonRDFSource with RDF syntax");
        }

        try (final TrellisDataset dataset = TrellisDataset.createDataset()) {

            // Add Audit quads
            audit.ifPresent(svc -> svc.creation(internalId, session).stream()
                    .map(skolemizeQuads(resourceService, baseUrl)).forEachOrdered(dataset::add));

            dataset.add(rdf.createQuad(PreferServerManaged, internalId, RDF.type, ldpType));

            // Add user-supplied data
            if (ldpType.equals(LDP.NonRDFSource)) {
                // Check the expected digest value
                final Digest digest = req.getDigest();
                if (nonNull(digest) && !getDigestForEntity(digest).equals(digest.getDigest())) {
                    return status(BAD_REQUEST);
                }

                final Map<String, String> metadata = singletonMap(CONTENT_TYPE,
                        ofNullable(contentType).orElse(APPLICATION_OCTET_STREAM));
                final IRI binaryLocation = rdf
                        .createIRI(binaryService.getIdentifierSupplier(req.getPartition()).get());
                dataset.add(rdf.createQuad(PreferServerManaged, internalId, DC.hasPart, binaryLocation));
                dataset.add(rdf.createQuad(PreferServerManaged, binaryLocation, DC.modified,
                        rdf.createLiteral(now().toString(), XSD.dateTime)));
                dataset.add(rdf.createQuad(PreferServerManaged, binaryLocation, DC.format,
                        rdf.createLiteral(ofNullable(contentType).orElse(APPLICATION_OCTET_STREAM))));
                dataset.add(rdf.createQuad(PreferServerManaged, binaryLocation, DC.extent,
                        rdf.createLiteral(Long.toString(entity.length()), XSD.long_)));

                // Persist the content
                persistContent(binaryLocation, metadata);
            } else {
                readEntityIntoDataset(identifier, baseUrl, PreferUserManaged, rdfSyntax.orElse(TURTLE), dataset);

                // Check for any constraints
                checkConstraint(dataset, PreferUserManaged, ldpType, TRELLIS_PREFIX + req.getPartition(),
                        rdfSyntax.orElse(TURTLE));
            }

            if (resourceService.put(internalId, dataset.asDataset())) {
                final ResponseBuilder builder = status(CREATED).location(create(identifier));

                // Add LDP types
                ldpResourceTypes(ldpType).map(IRI::getIRIString).forEach(type -> builder.link(type, "type"));

                return builder;
            }
        }

        LOGGER.error("Unable to persist data to location at {}", internalId.getIRIString());
        return serverError().type(TEXT_PLAIN)
                .entity("Unable to persist data. Please consult the logs for more information");
    }
}