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}