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.http.api; 017 018import static javax.ws.rs.core.Response.Status.BAD_GATEWAY; 019import static javax.ws.rs.core.Response.Status.CONFLICT; 020import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED; 021import static javax.ws.rs.core.Response.created; 022import static org.slf4j.LoggerFactory.getLogger; 023 024import java.net.URI; 025import java.net.URISyntaxException; 026 027import javax.annotation.PostConstruct; 028import javax.inject.Inject; 029import javax.jcr.ItemExistsException; 030import javax.jcr.PathNotFoundException; 031import javax.jcr.RepositoryException; 032import javax.jcr.Session; 033import javax.servlet.http.HttpServletResponse; 034import javax.ws.rs.ClientErrorException; 035import javax.ws.rs.HeaderParam; 036import javax.ws.rs.Path; 037import javax.ws.rs.PathParam; 038import javax.ws.rs.ServerErrorException; 039import javax.ws.rs.core.Context; 040import javax.ws.rs.core.Request; 041import javax.ws.rs.core.Response; 042import javax.ws.rs.core.UriInfo; 043 044import org.fcrepo.http.commons.domain.COPY; 045import org.fcrepo.http.commons.domain.MOVE; 046import org.fcrepo.kernel.api.models.FedoraResource; 047import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 048import org.slf4j.Logger; 049import org.springframework.context.annotation.Scope; 050 051import com.codahale.metrics.annotation.Timed; 052import com.google.common.annotations.VisibleForTesting; 053import com.hp.hpl.jena.rdf.model.ResourceFactory; 054 055/** 056 * CRUD operations on Fedora Nodes 057 * 058 * @author cbeer 059 */ 060@Scope("request") 061@Path("/{path: .*}") 062public class FedoraNodes extends ContentExposingResource { 063 064 @Inject 065 protected Session session; 066 067 private static final Logger LOGGER = getLogger(FedoraNodes.class); 068 069 @Context protected Request request; 070 @Context protected HttpServletResponse servletResponse; 071 @Context protected UriInfo uriInfo; 072 073 @PathParam("path") protected String externalPath; 074 075 protected FedoraResource resource; 076 077 /** 078 * Default JAX-RS entry point 079 */ 080 public FedoraNodes() { 081 super(); 082 } 083 084 /** 085 * Create a new FedoraNodes instance for a given path 086 * @param externalPath the external path 087 */ 088 @VisibleForTesting 089 public FedoraNodes(final String externalPath) { 090 this.externalPath = externalPath; 091 } 092 093 094 /** 095 * Run these actions after initializing this resource 096 */ 097 @PostConstruct 098 public void postConstruct() { 099 setUpJMSInfo(uriInfo, headers); 100 } 101 102 /** 103 * Copies an object from one path to another 104 * @param destinationUri the destination uri 105 * @throws URISyntaxException if uri syntax exception occurred 106 * @return the response 107 */ 108 @COPY 109 @Timed 110 public Response copyObject(@HeaderParam("Destination") final String destinationUri) 111 throws URISyntaxException { 112 113 try { 114 final String source = translator().asString(translator().toDomain(externalPath)); 115 116 if (!nodeService.exists(session, source)) { 117 throw new ClientErrorException("The source path does not exist", CONFLICT); 118 } 119 120 final String destination = translator().asString(ResourceFactory.createResource(destinationUri)); 121 122 if (destination == null) { 123 throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY); 124 } else if (nodeService.exists(session, destination)) { 125 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED); 126 } 127 128 LOGGER.info("Copy from '{}' to '{}'", source, destination); 129 nodeService.copyObject(session, source, destination); 130 131 session.save(); 132 133 return created(new URI(destinationUri)).build(); 134 } catch (final RepositoryRuntimeException e) { 135 final Throwable cause = e.getCause(); 136 137 if (cause instanceof ItemExistsException) { 138 139 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e); 140 141 } else if (cause instanceof PathNotFoundException) { 142 143 throw new ClientErrorException("There is no node that will serve as the parent of the copied item", 144 CONFLICT, e); 145 } else { 146 throw e; 147 } 148 } catch (final RepositoryException e) { 149 throw new RepositoryRuntimeException(e); 150 } 151 152 } 153 154 /** 155 * Copies an object from one path to another 156 * @param destinationUri the destination uri 157 * @throws URISyntaxException if uri syntax exception occurred 158 * @return the response 159 */ 160 @MOVE 161 @Timed 162 public Response moveObject(@HeaderParam("Destination") final String destinationUri) 163 throws URISyntaxException { 164 165 try { 166 167 final String source = toPath(translator(), externalPath); 168 169 if (!nodeService.exists(session, source)) { 170 throw new ClientErrorException("The source path does not exist", CONFLICT); 171 } 172 173 174 evaluateRequestPreconditions(request, servletResponse, resource(), session); 175 176 final String destination = translator().asString(ResourceFactory.createResource(destinationUri)); 177 178 if (destination == null) { 179 throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY); 180 } else if (nodeService.exists(session, destination)) { 181 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED); 182 } 183 184 LOGGER.info("Move from '{}' to '{}'", source, destination); 185 nodeService.moveObject(session, resource().getPath(), destination); 186 session.save(); 187 return created(new URI(destinationUri)).build(); 188 } catch (final RepositoryRuntimeException e) { 189 final Throwable cause = e.getCause(); 190 191 if (cause instanceof ItemExistsException) { 192 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e); 193 } else if (cause instanceof PathNotFoundException) { 194 throw new ClientErrorException("There is no node that will serve as the parent of the moved item", 195 CONFLICT, e); 196 } else { 197 throw e; 198 } 199 } catch (final RepositoryException e) { 200 throw new RepositoryRuntimeException(e); 201 } 202 } 203 204 @Override 205 protected Session session() { 206 return session; 207 } 208 209 @Override 210 protected void addResourceHttpHeaders(final FedoraResource resource) { 211 throw new UnsupportedOperationException(); 212 } 213 214 @Override 215 protected String externalPath() { 216 return externalPath; 217 } 218 219}