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.http.api; 019 020import com.google.common.annotations.VisibleForTesting; 021import io.micrometer.core.annotation.Timed; 022 023import org.fcrepo.kernel.api.exception.PathNotFoundException; 024import org.fcrepo.kernel.api.exception.PathNotFoundRuntimeException; 025import org.fcrepo.kernel.api.identifiers.FedoraId; 026import org.fcrepo.kernel.api.models.FedoraResource; 027import org.fcrepo.kernel.api.models.Tombstone; 028import org.fcrepo.kernel.api.services.PurgeResourceService; 029import org.slf4j.Logger; 030import org.springframework.context.annotation.Scope; 031 032import javax.inject.Inject; 033import javax.ws.rs.DELETE; 034import javax.ws.rs.GET; 035import javax.ws.rs.OPTIONS; 036import javax.ws.rs.POST; 037import javax.ws.rs.PUT; 038import javax.ws.rs.Path; 039import javax.ws.rs.PathParam; 040import javax.ws.rs.core.Response; 041 042import static javax.ws.rs.core.HttpHeaders.ALLOW; 043import static javax.ws.rs.core.Response.noContent; 044import static org.slf4j.LoggerFactory.getLogger; 045 046/** 047 * CRUD operations on Fedora tombstones 048 * 049 * @author cbeer 050 */ 051@Timed 052@Scope("request") 053@Path("/{path: .*}/fcr:tombstone") 054public class FedoraTombstones extends ContentExposingResource { 055 056 private static final Logger LOGGER = getLogger(FedoraTombstones.class); 057 058 @PathParam("path") protected String externalPath; 059 060 @Inject 061 private PurgeResourceService purgeResourceService; 062 063 /** 064 * Default JAX-RS entry point 065 */ 066 public FedoraTombstones() { 067 super(); 068 } 069 070 /** 071 * Create a new FedoraTombstones instance for a given path 072 * @param externalPath the external path 073 */ 074 @VisibleForTesting 075 public FedoraTombstones(final String externalPath) { 076 this.externalPath = externalPath; 077 } 078 079 /** 080 * Delete a tombstone resource (freeing the original resource to be reused) 081 * @return the free resource 082 */ 083 @DELETE 084 public Response delete() { 085 final FedoraResource resource = resource(); 086 if (!(resource instanceof Tombstone)) { 087 // If the resource is not deleted there is no tombstone. 088 return Response.status(Response.Status.NOT_FOUND).build(); 089 } 090 try { 091 final Tombstone tombstone = (Tombstone) resource; 092 LOGGER.info("Delete tombstone: {}", resource.getFedoraId()); 093 doInDbTxWithRetry(() -> { 094 purgeResourceService.perform(transaction(), tombstone.getDeletedObject(), getUserPrincipal()); 095 transaction().commitIfShortLived(); 096 }); 097 return noContent().build(); 098 } finally { 099 transaction().releaseResourceLocksIfShortLived(); 100 } 101 } 102 103 /* 104 * These methods are disallowed, but need to exist here or the path gets caught by the FedoraLdp path matcher. 105 */ 106 @GET 107 public Response get() { 108 return methodNotAllowed(); 109 } 110 111 @POST 112 public Response post() { 113 return methodNotAllowed(); 114 } 115 @PUT 116 public Response put() { 117 return methodNotAllowed(); 118 } 119 120 @OPTIONS 121 public Response options() { 122 return Response.ok().header(ALLOW, "DELETE").build(); 123 } 124 125 @Override 126 protected FedoraResource resource() { 127 final FedoraId resourceId = identifierConverter().pathToInternalId(externalPath); 128 try { 129 return getFedoraResource(transaction(), resourceId); 130 } catch (final PathNotFoundException e) { 131 throw new PathNotFoundRuntimeException(e.getMessage(), e); 132 } 133 } 134 135 @Override 136 protected String externalPath() { 137 return null; 138 } 139 140 private Response methodNotAllowed() { 141 return Response.status(Response.Status.METHOD_NOT_ALLOWED).header(ALLOW, "DELETE").build(); 142 } 143}