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
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     */
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     */
154    @MOVE
155    @Timed
156    public Response moveObject(@HeaderParam("Destination") final String destinationUri)
157            throws URISyntaxException {
158
159        try {
160
161            final String source = toPath(translator(), externalPath);
162
163            if (!nodeService.exists(session, source)) {
164                throw new ClientErrorException("The source path does not exist", CONFLICT);
165            }
166
167
168            evaluateRequestPreconditions(request, servletResponse, resource(), session);
169
170            final String destination = translator().asString(ResourceFactory.createResource(destinationUri));
171
172            if (destination == null) {
173                throw new ServerErrorException("Destination was not a valid resource path", BAD_GATEWAY);
174            } else if (nodeService.exists(session, destination)) {
175                throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED);
176            }
177
178            LOGGER.info("Move from '{}' to '{}'", source, destination);
179            nodeService.moveObject(session, resource().getPath(), destination);
180            session.save();
181            return created(new URI(destinationUri)).build();
182        } catch (final RepositoryRuntimeException e) {
183            final Throwable cause = e.getCause();
184
185            if (cause instanceof ItemExistsException) {
186                throw new ClientErrorException("Destination resource already exists", PRECONDITION_FAILED, e);
187            } else if (cause instanceof PathNotFoundException) {
188                throw new ClientErrorException("There is no node that will serve as the parent of the moved item",
189                        CONFLICT, e);
190            } else {
191                throw e;
192            }
193        } catch (final RepositoryException e) {
194            throw new RepositoryRuntimeException(e);
195        }
196    }
197
198    @Override
199    protected Session session() {
200        return session;
201    }
202
203    @Override
204    protected void addResourceHttpHeaders(final FedoraResource resource) {
205
206    }
207
208    @Override
209    protected String externalPath() {
210        return externalPath;
211    }
212
213}