001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.transform.http;
017
018import static javax.jcr.nodetype.NodeType.NT_BASE;
019import static javax.jcr.nodetype.NodeType.NT_FILE;
020import static javax.jcr.nodetype.NodeType.NT_FOLDER;
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.getNodeTypeTransform;
037import static org.slf4j.LoggerFactory.getLogger;
038
039import java.io.IOException;
040import java.io.InputStream;
041
042import javax.annotation.PostConstruct;
043import javax.inject.Inject;
044import javax.jcr.Node;
045import javax.jcr.RepositoryException;
046import javax.jcr.Session;
047import javax.ws.rs.Consumes;
048import javax.ws.rs.GET;
049import javax.ws.rs.HeaderParam;
050import javax.ws.rs.POST;
051import javax.ws.rs.Path;
052import javax.ws.rs.PathParam;
053import javax.ws.rs.Produces;
054import javax.ws.rs.core.MediaType;
055
056import org.fcrepo.http.api.ContentExposingResource;
057import org.fcrepo.kernel.models.FedoraResource;
058import org.fcrepo.kernel.utils.iterators.RdfStream;
059import org.fcrepo.transform.TransformationFactory;
060import org.jvnet.hk2.annotations.Optional;
061import org.modeshape.jcr.api.JcrTools;
062import org.slf4j.Logger;
063import org.springframework.context.annotation.Scope;
064
065import com.codahale.metrics.annotation.Timed;
066import com.google.common.annotations.VisibleForTesting;
067
068/**
069 * Endpoint for transforming object properties using stored
070 * or POSTed transformations.
071 *
072 * @author cbeer
073 */
074@Scope("request")
075@Path("/{path: .*}/fcr:transform")
076public class FedoraTransform extends ContentExposingResource {
077
078    @Inject
079    protected Session session;
080
081    private static final Logger LOGGER = getLogger(FedoraTransform.class);
082
083    @Inject
084    @Optional
085    private TransformationFactory transformationFactory;
086
087    @PathParam("path") protected String externalPath;
088
089    /**
090     * Default entry point
091     */
092    public FedoraTransform() { }
093
094    /**
095     * Create a new FedoraNodes instance for a given path
096     * @param externalPath the external path
097     */
098    @VisibleForTesting
099    public FedoraTransform(final String externalPath) {
100        this.externalPath = externalPath;
101    }
102
103
104    /**
105     * Register the LDPath configuration tree in JCR
106     *
107     * @throws RepositoryException if repository exception occurred
108     * @throws java.io.IOException if IO exception occurred
109     * @throws SecurityException if security exception occurred
110     */
111    @PostConstruct
112    public void setUpRepositoryConfiguration() throws RepositoryException, IOException {
113
114        final JcrTools jcrTools = new JcrTools(true);
115        final Session internalSession = sessions.getInternalSession();
116        try {
117            // register our CND
118            jcrTools.registerNodeTypes(internalSession, "ldpath.cnd");
119
120            // create the configuration base path
121            jcrTools.findOrCreateNode(internalSession, "/fedora:system/fedora:transform", "fedora:Configuration",
122                    "fedora:NodeTypeConfiguration");
123            final Node node =
124                jcrTools.findOrCreateNode(internalSession, CONFIGURATION_FOLDER + "default", NT_FOLDER, NT_FOLDER);
125            LOGGER.debug("Transforming node: {}", node.getPath());
126
127            // register an initial default program
128            if (!node.hasNode(NT_BASE)) {
129                final Node baseConfig = node.addNode(NT_BASE, NT_FILE);
130                jcrTools.uploadFile(internalSession, baseConfig.getPath(), getClass().getResourceAsStream(
131                        "/ldpath/default/nt_base_ldpath_program.txt"));
132            }
133            internalSession.save();
134        } finally {
135            internalSession.logout();
136        }
137    }
138
139    /**
140     * Execute an LDpath program transform
141     *
142     * @param program the LDpath program
143     * @return Binary blob
144     * @throws RepositoryException if repository exception occurred
145     */
146    @GET
147    @Path("{program}")
148    @Produces({APPLICATION_JSON})
149    @Timed
150    public Object evaluateLdpathProgram(@PathParam("program") final String program)
151            throws RepositoryException {
152        LOGGER.info("GET transform, '{}', for '{}'", program, externalPath);
153
154        final RdfStream rdfStream = getResourceTriples().session(session)
155                .topic(translator().reverse().convert(resource()).asNode());
156
157        return getNodeTypeTransform(resource().getNode(), program).apply(rdfStream);
158
159    }
160
161    /**
162     * Get the LDPath output as a JSON stream appropriate for e.g. Solr
163     *
164     * @param contentType the content type
165     * @param requestBodyStream the request body stream
166     * @return LDPath as a JSON stream
167     */
168    @POST
169    @Consumes({APPLICATION_RDF_LDPATH, contentTypeSPARQLQuery})
170    @Produces({APPLICATION_JSON, contentTypeTextTSV, contentTypeTextCSV,
171            contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON,
172            contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle,
173            contentTypeN3, contentTypeNTriples, contentTypeRDFXML})
174    @Timed
175    public Object evaluateTransform(@HeaderParam("Content-Type") final MediaType contentType,
176                                    final InputStream requestBodyStream) {
177
178        if (transformationFactory == null) {
179            transformationFactory = new TransformationFactory();
180        }
181        LOGGER.info("POST transform for '{}'", externalPath);
182
183        final RdfStream rdfStream = getResourceTriples().session(session)
184                .topic(translator().reverse().convert(resource()).asNode());
185
186        return transformationFactory.getTransform(contentType, requestBodyStream).apply(rdfStream);
187
188    }
189
190    @Override
191    protected Session session() {
192        return session;
193    }
194
195    @Override
196    protected String externalPath() {
197        return externalPath;
198    }
199
200    @Override
201    protected void addResourceHttpHeaders(final FedoraResource resource) {
202        throw new UnsupportedOperationException();
203    }
204}