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