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.models.FedoraResource; 047import org.fcrepo.kernel.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 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 setUpJMSBaseURIs(uriInfo); 100 } 101 102 /** 103 * Copies an object from one path to another 104 */ 105 @COPY 106 @Timed 107 public Response copyObject(@HeaderParam("Destination") final String destinationUri) 108 throws URISyntaxException { 109 110 try { 111 final String source = translator().asString(translator().toDomain(externalPath)); 112 113 if (!nodeService.exists(session, source)) { 114 throw new ClientErrorException("The source path does not exist", CONFLICT); 115 } 116 117 final String destination = translator().asString(ResourceFactory.createResource(destinationUri)); 118 119 if (destination == null) { 120 throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY); 121 } else if (nodeService.exists(session, destination)) { 122 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED); 123 } 124 125 LOGGER.info("Copy from '{}' to '{}'", source, destination); 126 nodeService.copyObject(session, source, destination); 127 128 session.save(); 129 130 return created(new URI(destinationUri)).build(); 131 } catch (final RepositoryRuntimeException e) { 132 final Throwable cause = e.getCause(); 133 134 if (cause instanceof ItemExistsException) { 135 136 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e); 137 138 } else if (cause instanceof PathNotFoundException) { 139 140 throw new ClientErrorException("There is no node that will serve as the parent of the copied item", 141 CONFLICT, e); 142 } else { 143 throw e; 144 } 145 } catch (final RepositoryException e) { 146 throw new RepositoryRuntimeException(e); 147 } 148 149 } 150 151 /** 152 * Copies an object from one path to another 153 */ 154 @MOVE 155 @Timed 156 public Response moveObject(@HeaderParam("Destination") final String destinationUri) 157 throws URISyntaxException { 158 159 try { 160 161 final String source = toPath(translator(), externalPath); 162 163 if (!nodeService.exists(session, source)) { 164 throw new ClientErrorException("The source path does not exist", CONFLICT); 165 } 166 167 168 evaluateRequestPreconditions(request, servletResponse, resource(), session); 169 170 final String destination = translator().asString(ResourceFactory.createResource(destinationUri)); 171 172 if (destination == null) { 173 throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY); 174 } else if (nodeService.exists(session, destination)) { 175 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED); 176 } 177 178 LOGGER.info("Move from '{}' to '{}'", source, destination); 179 nodeService.moveObject(session, resource().getPath(), destination); 180 session.save(); 181 return created(new URI(destinationUri)).build(); 182 } catch (final RepositoryRuntimeException e) { 183 final Throwable cause = e.getCause(); 184 185 if (cause instanceof ItemExistsException) { 186 throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e); 187 } else if (cause instanceof PathNotFoundException) { 188 throw new ClientErrorException("There is no node that will serve as the parent of the moved item", 189 CONFLICT, e); 190 } else { 191 throw e; 192 } 193 } catch (final RepositoryException e) { 194 throw new RepositoryRuntimeException(e); 195 } 196 } 197 198 @Override 199 protected Session session() { 200 return session; 201 } 202 203 @Override 204 protected void addResourceHttpHeaders(final FedoraResource resource) { 205 206 } 207 208 @Override 209 protected String externalPath() { 210 return externalPath; 211 } 212 213}