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