001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.transform.http;
019
020import static com.google.common.collect.ImmutableMap.of;
021import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
022import static org.apache.jena.riot.WebContent.contentTypeN3;
023import static org.apache.jena.riot.WebContent.contentTypeNTriples;
024import static org.apache.jena.riot.WebContent.contentTypeRDFXML;
025import static org.apache.jena.riot.WebContent.contentTypeResultsBIO;
026import static org.apache.jena.riot.WebContent.contentTypeResultsJSON;
027import static org.apache.jena.riot.WebContent.contentTypeResultsXML;
028import static org.apache.jena.riot.WebContent.contentTypeSPARQLQuery;
029import static org.apache.jena.riot.WebContent.contentTypeSSE;
030import static org.apache.jena.riot.WebContent.contentTypeTextCSV;
031import static org.apache.jena.riot.WebContent.contentTypeTextPlain;
032import static org.apache.jena.riot.WebContent.contentTypeTextTSV;
033import static org.apache.jena.riot.WebContent.contentTypeTurtle;
034import static org.fcrepo.transform.transformations.LDPathTransform.APPLICATION_RDF_LDPATH;
035import static org.fcrepo.transform.transformations.LDPathTransform.CONFIGURATION_FOLDER;
036import static org.fcrepo.transform.transformations.LDPathTransform.getResourceTransform;
037import static org.fcrepo.transform.transformations.LDPathTransform.DEFAULT_TRANSFORM_RESOURCE;
038import static org.slf4j.LoggerFactory.getLogger;
039
040import java.io.IOException;
041import java.io.InputStream;
042import java.util.Map;
043import java.util.stream.Stream;
044
045import javax.annotation.PostConstruct;
046import javax.inject.Inject;
047import javax.jcr.RepositoryException;
048import javax.jcr.Session;
049import javax.ws.rs.Consumes;
050import javax.ws.rs.GET;
051import javax.ws.rs.HeaderParam;
052import javax.ws.rs.POST;
053import javax.ws.rs.Path;
054import javax.ws.rs.PathParam;
055import javax.ws.rs.Produces;
056import javax.ws.rs.core.MediaType;
057
058import org.fcrepo.http.api.ContentExposingResource;
059import org.fcrepo.kernel.api.exception.InvalidChecksumException;
060import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
061import org.fcrepo.kernel.api.models.FedoraBinary;
062import org.fcrepo.kernel.api.models.FedoraResource;
063import org.fcrepo.transform.TransformationFactory;
064import org.jvnet.hk2.annotations.Optional;
065import org.slf4j.Logger;
066import org.springframework.context.annotation.Scope;
067
068import com.codahale.metrics.annotation.Timed;
069import com.google.common.annotations.VisibleForTesting;
070
071/**
072 * Endpoint for transforming object properties using stored
073 * or POSTed transformations.
074 *
075 * @author cbeer
076 */
077@Scope("request")
078@Path("/{path: .*}/fcr:transform")
079public class FedoraTransform extends ContentExposingResource {
080
081    @Inject
082    protected Session session;
083
084    private static final Logger LOGGER = getLogger(FedoraTransform.class);
085
086    @Inject
087    @Optional
088    private TransformationFactory transformationFactory;
089
090    @PathParam("path") protected String externalPath;
091
092    /**
093     * Default entry point
094     */
095    public FedoraTransform() { }
096
097    /**
098     * Create a new FedoraNodes instance for a given path
099     * @param externalPath the external path
100     */
101    @VisibleForTesting
102    public FedoraTransform(final String externalPath) {
103        this.externalPath = externalPath;
104    }
105
106
107    /**
108     * Register the LDPath configuration tree in JCR
109     *
110     * @throws RepositoryException if repository exception occurred
111     * @throws java.io.IOException if IO exception occurred
112     */
113    @PostConstruct
114    public void setUpRepositoryConfiguration() throws RepositoryException, IOException {
115
116        final Session internalSession = sessions.getInternalSession();
117        try {
118
119            // Create this resource or it becomes a PairTree which is not referenceable.
120            containerService.findOrCreate(internalSession, "/fedora:system/fedora:transform");
121
122            final Map<String, String> transformations = of(
123                    "default", "/ldpath/default/ldpath_program.txt",
124                    "deluxe", "/ldpath/deluxe/ldpath_program.txt");
125            transformations.forEach((key, value) -> {
126
127                final FedoraResource resource =
128                        containerService.findOrCreate(internalSession, CONFIGURATION_FOLDER + key);
129                LOGGER.debug("Transformation default resource: {}", resource.getPath());
130
131                final Stream<FedoraResource> children = resource.getChildren();
132                children.forEach(child -> LOGGER.debug("Child is {}", child.getPath()));
133                final String uploadPath = CONFIGURATION_FOLDER + key + "/" + DEFAULT_TRANSFORM_RESOURCE;
134                if (!resource.getChildren().anyMatch(child -> child.getPath().equalsIgnoreCase(uploadPath))) {
135                    LOGGER.debug("Uploading the stream to {}", uploadPath);
136                    final FedoraBinary base = binaryService.findOrCreate(internalSession, uploadPath);
137                    try {
138                        base.setContent(getClass().getResourceAsStream(value), null, null, null, null);
139                    } catch (final InvalidChecksumException e) {
140                        throw new RepositoryRuntimeException(e);
141                    }
142                }
143            });
144
145            internalSession.save();
146        } finally {
147            internalSession.logout();
148        }
149    }
150
151    /**
152     * Execute an LDpath program transform
153     *
154     * @param program the LDpath program
155     * @return Binary blob
156     * @throws RepositoryException if repository exception occurred
157     */
158    @GET
159    @Path("{program}")
160    @Produces({APPLICATION_JSON})
161    @Timed
162    public Object evaluateLdpathProgram(@PathParam("program") final String program)
163            throws RepositoryException {
164        LOGGER.info("GET transform, '{}', for '{}'", program, externalPath);
165
166        return getResourceTransform(resource(), session, nodeService, program).apply(getResourceTriples());
167
168    }
169
170    /**
171     * Get the LDPath output as a JSON stream appropriate for e.g. Solr
172     *
173     * @param contentType the content type
174     * @param requestBodyStream the request body stream
175     * @return LDPath as a JSON stream
176     */
177    @POST
178    @Consumes({APPLICATION_RDF_LDPATH, contentTypeSPARQLQuery})
179    @Produces({APPLICATION_JSON, contentTypeTextTSV, contentTypeTextCSV,
180            contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON,
181            contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle,
182            contentTypeN3, contentTypeNTriples, contentTypeRDFXML})
183    @Timed
184    public Object evaluateTransform(@HeaderParam("Content-Type") final MediaType contentType,
185                                    final InputStream requestBodyStream) {
186
187        if (transformationFactory == null) {
188            transformationFactory = new TransformationFactory();
189        }
190        LOGGER.info("POST transform for '{}'", externalPath);
191
192        return transformationFactory.getTransform(contentType, requestBodyStream).apply(getResourceTriples());
193
194    }
195
196    @Override
197    protected Session session() {
198        return session;
199    }
200
201    @Override
202    protected String externalPath() {
203        return externalPath;
204    }
205
206    @Override
207    protected void addResourceHttpHeaders(final FedoraResource resource) {
208        throw new UnsupportedOperationException();
209    }
210}