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