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.api.models.FedoraResource;
058import org.fcrepo.transform.TransformationFactory;
059import org.jvnet.hk2.annotations.Optional;
060import org.modeshape.jcr.api.JcrTools;
061import org.slf4j.Logger;
062import org.springframework.context.annotation.Scope;
063
064import com.codahale.metrics.annotation.Timed;
065import com.google.common.annotations.VisibleForTesting;
066
067/**
068 * Endpoint for transforming object properties using stored
069 * or POSTed transformations.
070 *
071 * @author cbeer
072 */
073@Scope("request")
074@Path("/{path: .*}/fcr:transform")
075public class FedoraTransform extends ContentExposingResource {
076
077    @Inject
078    protected Session session;
079
080    private static final Logger LOGGER = getLogger(FedoraTransform.class);
081
082    @Inject
083    @Optional
084    private TransformationFactory transformationFactory;
085
086    @PathParam("path") protected String externalPath;
087
088    /**
089     * Default entry point
090     */
091    public FedoraTransform() { }
092
093    /**
094     * Create a new FedoraNodes instance for a given path
095     * @param externalPath the external path
096     */
097    @VisibleForTesting
098    public FedoraTransform(final String externalPath) {
099        this.externalPath = externalPath;
100    }
101
102
103    /**
104     * Register the LDPath configuration tree in JCR
105     *
106     * @throws RepositoryException if repository exception occurred
107     * @throws java.io.IOException if IO exception occurred
108     * @throws SecurityException if security exception occurred
109     */
110    @PostConstruct
111    public void setUpRepositoryConfiguration() throws RepositoryException, IOException {
112
113        final JcrTools jcrTools = new JcrTools(true);
114        final Session internalSession = sessions.getInternalSession();
115        try {
116            // register our CND
117            jcrTools.registerNodeTypes(internalSession, "ldpath.cnd");
118
119            // create the configuration base path
120            jcrTools.findOrCreateNode(internalSession, "/fedora:system/fedora:transform", "fedora:Configuration",
121                    "fedora:NodeTypeConfiguration");
122            final Node node =
123                jcrTools.findOrCreateNode(internalSession, CONFIGURATION_FOLDER + "default", NT_FOLDER, NT_FOLDER);
124            LOGGER.debug("Transforming node: {}", node.getPath());
125
126            // register an initial default program
127            if (!node.hasNode(NT_BASE)) {
128                final Node baseConfig = node.addNode(NT_BASE, NT_FILE);
129                jcrTools.uploadFile(internalSession, baseConfig.getPath(), getClass().getResourceAsStream(
130                        "/ldpath/default/nt_base_ldpath_program.txt"));
131            }
132            internalSession.save();
133        } finally {
134            internalSession.logout();
135        }
136    }
137
138    /**
139     * Execute an LDpath program transform
140     *
141     * @param program the LDpath program
142     * @return Binary blob
143     * @throws RepositoryException if repository exception occurred
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        return getNodeTypeTransform(resource().getNode(), program).apply(getResourceTriples());
154
155    }
156
157    /**
158     * Get the LDPath output as a JSON stream appropriate for e.g. Solr
159     *
160     * @param contentType the content type
161     * @param requestBodyStream the request body stream
162     * @return LDPath as a JSON stream
163     */
164    @POST
165    @Consumes({APPLICATION_RDF_LDPATH, contentTypeSPARQLQuery})
166    @Produces({APPLICATION_JSON, contentTypeTextTSV, contentTypeTextCSV,
167            contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON,
168            contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle,
169            contentTypeN3, contentTypeNTriples, contentTypeRDFXML})
170    @Timed
171    public Object evaluateTransform(@HeaderParam("Content-Type") final MediaType contentType,
172                                    final InputStream requestBodyStream) {
173
174        if (transformationFactory == null) {
175            transformationFactory = new TransformationFactory();
176        }
177        LOGGER.info("POST transform for '{}'", externalPath);
178
179        return transformationFactory.getTransform(contentType, requestBodyStream).apply(getResourceTriples());
180
181    }
182
183    @Override
184    protected Session session() {
185        return session;
186    }
187
188    @Override
189    protected String externalPath() {
190        return externalPath;
191    }
192
193    @Override
194    protected void addResourceHttpHeaders(final FedoraResource resource) {
195        throw new UnsupportedOperationException();
196    }
197}