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}