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 static javax.ws.rs.core.Response.Status.BAD_GATEWAY;
021import static javax.ws.rs.core.Response.Status.CONFLICT;
022import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED;
023import static javax.ws.rs.core.Response.created;
024import static org.slf4j.LoggerFactory.getLogger;
025
026import java.net.URI;
027import java.net.URISyntaxException;
028
029import javax.annotation.PostConstruct;
030import javax.jcr.ItemExistsException;
031import javax.jcr.PathNotFoundException;
032import javax.jcr.RepositoryException;
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    private static final Logger LOGGER = getLogger(FedoraNodes.class);
065
066    @Context protected Request request;
067    @Context protected HttpServletResponse servletResponse;
068    @Context protected UriInfo uriInfo;
069
070    @PathParam("path") protected String externalPath;
071
072    protected FedoraResource resource;
073
074    /**
075     * Default JAX-RS entry point
076     */
077    public FedoraNodes() {
078        super();
079    }
080
081    /**
082     * Create a new FedoraNodes instance for a given path
083     * @param externalPath the external path
084     */
085    @VisibleForTesting
086    public FedoraNodes(final String externalPath) {
087        this.externalPath = externalPath;
088    }
089
090
091    /**
092     * Run these actions after initializing this resource
093     */
094    @PostConstruct
095    public void postConstruct() {
096        setUpJMSInfo(uriInfo, headers);
097    }
098
099    /**
100     * Copies an object from one path to another
101     * @param destinationUri the destination uri
102     * @throws URISyntaxException if uri syntax exception occurred
103     * @return the response
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     * @param destinationUri the destination uri
154     * @throws URISyntaxException if uri syntax exception occurred
155     * @return the response
156     */
157    @MOVE
158    @Timed
159    public Response moveObject(@HeaderParam("Destination") final String destinationUri)
160            throws URISyntaxException {
161
162        try {
163
164            final String source = toPath(translator(), externalPath);
165
166            if (!nodeService.exists(session, source)) {
167                throw new ClientErrorException("The source path does not exist", CONFLICT);
168            }
169
170
171            evaluateRequestPreconditions(request, servletResponse, resource(), session);
172
173            final String destination = translator().asString(ResourceFactory.createResource(destinationUri));
174
175            if (destination == null) {
176                throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY);
177            } else if (nodeService.exists(session, destination)) {
178                throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED);
179            }
180
181            LOGGER.info("Move from '{}' to '{}'", source, destination);
182            nodeService.moveObject(session, source, destination);
183            session.save();
184            return created(new URI(destinationUri)).build();
185        } catch (final RepositoryRuntimeException e) {
186            final Throwable cause = e.getCause();
187
188            if (cause instanceof ItemExistsException) {
189                throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e);
190            } else if (cause instanceof PathNotFoundException) {
191                throw new ClientErrorException("There is no node that will serve as the parent of the moved item",
192                        CONFLICT, e);
193            } else {
194                throw e;
195            }
196        } catch (final RepositoryException e) {
197            throw new RepositoryRuntimeException(e);
198        }
199    }
200
201    @Override
202    protected void addResourceHttpHeaders(final FedoraResource resource) {
203        throw new UnsupportedOperationException();
204    }
205
206    @Override
207    protected String externalPath() {
208        return externalPath;
209    }
210
211}