001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.transform.http; 019 020import static com.google.common.collect.ImmutableMap.of; 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.getResourceTransform; 037import static org.fcrepo.transform.transformations.LDPathTransform.DEFAULT_TRANSFORM_RESOURCE; 038import static org.slf4j.LoggerFactory.getLogger; 039 040import java.io.IOException; 041import java.io.InputStream; 042import java.util.Map; 043import java.util.stream.Stream; 044 045import javax.annotation.PostConstruct; 046import javax.inject.Inject; 047import javax.jcr.RepositoryException; 048import javax.jcr.Session; 049import javax.ws.rs.Consumes; 050import javax.ws.rs.GET; 051import javax.ws.rs.HeaderParam; 052import javax.ws.rs.POST; 053import javax.ws.rs.Path; 054import javax.ws.rs.PathParam; 055import javax.ws.rs.Produces; 056import javax.ws.rs.core.MediaType; 057 058import org.fcrepo.http.api.ContentExposingResource; 059import org.fcrepo.kernel.api.exception.InvalidChecksumException; 060import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 061import org.fcrepo.kernel.api.models.FedoraBinary; 062import org.fcrepo.kernel.api.models.FedoraResource; 063import org.fcrepo.transform.TransformationFactory; 064import org.jvnet.hk2.annotations.Optional; 065import org.slf4j.Logger; 066import org.springframework.context.annotation.Scope; 067 068import com.codahale.metrics.annotation.Timed; 069import com.google.common.annotations.VisibleForTesting; 070 071/** 072 * Endpoint for transforming object properties using stored 073 * or POSTed transformations. 074 * 075 * @author cbeer 076 */ 077@Scope("request") 078@Path("/{path: .*}/fcr:transform") 079public class FedoraTransform extends ContentExposingResource { 080 081 @Inject 082 protected Session session; 083 084 private static final Logger LOGGER = getLogger(FedoraTransform.class); 085 086 @Inject 087 @Optional 088 private TransformationFactory transformationFactory; 089 090 @PathParam("path") protected String externalPath; 091 092 /** 093 * Default entry point 094 */ 095 public FedoraTransform() { } 096 097 /** 098 * Create a new FedoraNodes instance for a given path 099 * @param externalPath the external path 100 */ 101 @VisibleForTesting 102 public FedoraTransform(final String externalPath) { 103 this.externalPath = externalPath; 104 } 105 106 107 /** 108 * Register the LDPath configuration tree in JCR 109 * 110 * @throws RepositoryException if repository exception occurred 111 * @throws java.io.IOException if IO exception occurred 112 */ 113 @PostConstruct 114 public void setUpRepositoryConfiguration() throws RepositoryException, IOException { 115 116 final Session internalSession = sessions.getInternalSession(); 117 try { 118 119 // Create this resource or it becomes a PairTree which is not referenceable. 120 containerService.findOrCreate(internalSession, "/fedora:system/fedora:transform"); 121 122 final Map<String, String> transformations = of( 123 "default", "/ldpath/default/ldpath_program.txt", 124 "deluxe", "/ldpath/deluxe/ldpath_program.txt"); 125 transformations.forEach((key, value) -> { 126 127 final FedoraResource resource = 128 containerService.findOrCreate(internalSession, CONFIGURATION_FOLDER + key); 129 LOGGER.debug("Transformation default resource: {}", resource.getPath()); 130 131 final Stream<FedoraResource> children = resource.getChildren(); 132 children.forEach(child -> LOGGER.debug("Child is {}", child.getPath())); 133 final String uploadPath = CONFIGURATION_FOLDER + key + "/" + DEFAULT_TRANSFORM_RESOURCE; 134 if (!resource.getChildren().anyMatch(child -> child.getPath().equalsIgnoreCase(uploadPath))) { 135 LOGGER.debug("Uploading the stream to {}", uploadPath); 136 final FedoraBinary base = binaryService.findOrCreate(internalSession, uploadPath); 137 try { 138 base.setContent(getClass().getResourceAsStream(value), null, null, null, null); 139 } catch (final InvalidChecksumException e) { 140 throw new RepositoryRuntimeException(e); 141 } 142 } 143 }); 144 145 internalSession.save(); 146 } finally { 147 internalSession.logout(); 148 } 149 } 150 151 /** 152 * Execute an LDpath program transform 153 * 154 * @param program the LDpath program 155 * @return Binary blob 156 * @throws RepositoryException if repository exception occurred 157 */ 158 @GET 159 @Path("{program}") 160 @Produces({APPLICATION_JSON}) 161 @Timed 162 public Object evaluateLdpathProgram(@PathParam("program") final String program) 163 throws RepositoryException { 164 LOGGER.info("GET transform, '{}', for '{}'", program, externalPath); 165 166 return getResourceTransform(resource(), session, nodeService, program).apply(getResourceTriples()); 167 168 } 169 170 /** 171 * Get the LDPath output as a JSON stream appropriate for e.g. Solr 172 * 173 * @param contentType the content type 174 * @param requestBodyStream the request body stream 175 * @return LDPath as a JSON stream 176 */ 177 @POST 178 @Consumes({APPLICATION_RDF_LDPATH, contentTypeSPARQLQuery}) 179 @Produces({APPLICATION_JSON, contentTypeTextTSV, contentTypeTextCSV, 180 contentTypeSSE, contentTypeTextPlain, contentTypeResultsJSON, 181 contentTypeResultsXML, contentTypeResultsBIO, contentTypeTurtle, 182 contentTypeN3, contentTypeNTriples, contentTypeRDFXML}) 183 @Timed 184 public Object evaluateTransform(@HeaderParam("Content-Type") final MediaType contentType, 185 final InputStream requestBodyStream) { 186 187 if (transformationFactory == null) { 188 transformationFactory = new TransformationFactory(); 189 } 190 LOGGER.info("POST transform for '{}'", externalPath); 191 192 return transformationFactory.getTransform(contentType, requestBodyStream).apply(getResourceTriples()); 193 194 } 195 196 @Override 197 protected Session session() { 198 return session; 199 } 200 201 @Override 202 protected String externalPath() { 203 return externalPath; 204 } 205 206 @Override 207 protected void addResourceHttpHeaders(final FedoraResource resource) { 208 throw new UnsupportedOperationException(); 209 } 210}