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.kernel.api.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}