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
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
108     * @throws java.io.IOException
109     * @throws SecurityException
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     * @return Binary blob
143     * @throws RepositoryException
144     */
145    @GET
146    @Path("{program}")
147    @Produces({APPLICATION_JSON})
148    @Timed
149    public Object evaluateLdpathProgram(@PathParam("program") final String program)
150            throws RepositoryException {
151        LOGGER.info("GET transform, '{}', for '{}'", program, externalPath);
152
153        final RdfStream rdfStream = getResourceTriples().session(session)
154                .topic(translator().reverse().convert(resource()).asNode());
155
156        return getNodeTypeTransform(resource().getNode(), program).apply(rdfStream);
157
158    }
159
160    /**
161     * Get the LDPath output as a JSON stream appropriate for e.g. Solr
162     *
163     * @param requestBodyStream
164     * @return LDPath as a JSON stream
165     */
166    @POST
167    @Consumes({APPLICATION_RDF_LDPATH, contentTypeSPARQLQuery})
168    @Produces({APPLICATION_JSON, contentTypeTextTSV, contentTypeTextCSV,
169            contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON,
170            contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle,
171            contentTypeN3, contentTypeNTriples, contentTypeRDFXML})
172    @Timed
173    public Object evaluateTransform(@HeaderParam("Content-Type") final MediaType contentType,
174                                    final InputStream requestBodyStream) {
175
176        if (transformationFactory == null) {
177            transformationFactory = new TransformationFactory();
178        }
179        LOGGER.info("POST transform for '{}'", externalPath);
180
181        final RdfStream rdfStream = getResourceTriples().session(session)
182                .topic(translator().reverse().convert(resource()).asNode());
183
184        return transformationFactory.getTransform(contentType, requestBodyStream).apply(rdfStream);
185
186    }
187
188    @Override
189    protected Session session() {
190        return session;
191    }
192
193    @Override
194    protected String externalPath() {
195        return externalPath;
196    }
197
198    @Override
199    protected void addResourceHttpHeaders(final FedoraResource resource) {
200
201    }
202}