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}