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.MediaType.APPLICATION_XHTML_XML;
019import static javax.ws.rs.core.MediaType.APPLICATION_XML;
020import static javax.ws.rs.core.MediaType.TEXT_HTML;
021import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
022import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
023import static javax.ws.rs.core.Response.created;
024import static javax.ws.rs.core.Response.noContent;
025import static javax.ws.rs.core.Response.status;
026import static org.apache.commons.lang3.StringUtils.isBlank;
027import static org.fcrepo.kernel.api.RequiredRdfContext.VERSIONS;
028import static org.fcrepo.http.commons.domain.RDFMediaType.JSON_LD;
029import static org.fcrepo.http.commons.domain.RDFMediaType.N3;
030import static org.fcrepo.http.commons.domain.RDFMediaType.N3_ALT2;
031import static org.fcrepo.http.commons.domain.RDFMediaType.NTRIPLES;
032import static org.fcrepo.http.commons.domain.RDFMediaType.RDF_XML;
033import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE;
034import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_X;
035import static org.fcrepo.kernel.modeshape.identifiers.NodeResourceConverter.nodeToResource;
036import static org.fcrepo.kernel.modeshape.utils.NamespaceTools.getNamespaces;
037import static org.slf4j.LoggerFactory.getLogger;
038
039import java.net.URI;
040
041import javax.jcr.RepositoryException;
042import javax.servlet.http.HttpServletResponse;
043import javax.ws.rs.DELETE;
044import javax.ws.rs.GET;
045import javax.ws.rs.HeaderParam;
046import javax.ws.rs.POST;
047import javax.ws.rs.PUT;
048import javax.ws.rs.Path;
049import javax.ws.rs.PathParam;
050import javax.ws.rs.Produces;
051import javax.ws.rs.core.Context;
052import javax.ws.rs.core.Request;
053import javax.ws.rs.core.Response;
054import javax.ws.rs.core.UriInfo;
055
056import org.fcrepo.http.commons.responses.HtmlTemplate;
057import org.fcrepo.http.commons.responses.RdfNamespacedStream;
058import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
059import org.fcrepo.kernel.api.exception.RepositoryVersionRuntimeException;
060import org.fcrepo.kernel.api.models.FedoraResource;
061import org.fcrepo.kernel.api.rdf.DefaultRdfStream;
062import org.slf4j.Logger;
063import org.springframework.context.annotation.Scope;
064
065import com.google.common.annotations.VisibleForTesting;
066
067/**
068 * @author cabeer
069 * @since 9/25/14
070 */
071@Scope("request")
072@Path("/{path: .*}/fcr:versions")
073public class FedoraVersioning extends FedoraBaseResource {
074
075    private static final Logger LOGGER = getLogger(FedoraVersioning.class);
076
077
078    @Context protected Request request;
079    @Context protected HttpServletResponse servletResponse;
080    @Context protected UriInfo uriInfo;
081
082    @PathParam("path") protected String externalPath;
083
084    protected FedoraResource resource;
085
086
087    /**
088     * Default JAX-RS entry point
089     */
090    public FedoraVersioning() {
091        super();
092    }
093
094    /**
095     * Create a new FedoraNodes instance for a given path
096     * @param externalPath the external path
097     */
098    @VisibleForTesting
099    public FedoraVersioning(final String externalPath) {
100        this.externalPath = externalPath;
101    }
102
103
104    /**
105     * Enable versioning
106     * @return the response
107     */
108    @PUT
109    public Response enableVersioning() {
110        LOGGER.info("Enable versioning for '{}'", externalPath);
111        resource().enableVersioning();
112
113        try {
114            session.save();
115        } catch (final RepositoryException e) {
116            throw new RepositoryRuntimeException(e);
117        }
118        return created(uriInfo.getRequestUri()).build();
119    }
120
121    /**
122     * Disable versioning
123     * @return the response
124     */
125    @DELETE
126    public Response disableVersioning() {
127        LOGGER.info("Disable versioning for '{}'", externalPath);
128        resource().disableVersioning();
129
130        try {
131            session.save();
132        } catch (final RepositoryException e) {
133            throw new RepositoryRuntimeException(e);
134        }
135
136        return noContent().build();
137    }
138
139    /**
140     * Create a new version checkpoint and tag it with the given label.  If
141     * that label already describes another version it will silently be
142     * reassigned to describe this version.
143     *
144     * @param slug the value of slug
145     * @return response
146     * @throws RepositoryException if repository exception occurred
147     */
148    @POST
149    public Response addVersion(@HeaderParam("Slug") final String slug) throws RepositoryException {
150        if (!isBlank(slug)) {
151            LOGGER.info("Request to add version '{}' for '{}'", slug, externalPath);
152            final String path = toPath(translator(), externalPath);
153            versionService.createVersion(session, path, slug);
154            return created(URI.create(nodeToResource(translator()).convert(
155                            resource().getBaseVersion().getFrozenNode()).getURI())).build();
156        }
157        return status(BAD_REQUEST).entity("Specify label for version").build();
158    }
159
160
161    /**
162     * Get the list of versions for the object
163     *
164     * @return List of versions for the object as RDF
165     */
166    @SuppressWarnings("resource")
167    @GET
168    @HtmlTemplate(value = "fcr:versions")
169    @Produces({TURTLE + ";qs=10", JSON_LD + ";qs=8", N3, N3_ALT2, RDF_XML, NTRIPLES, APPLICATION_XML, TEXT_PLAIN,
170            TURTLE_X, TEXT_HTML, APPLICATION_XHTML_XML, "*/*"})
171    public RdfNamespacedStream getVersionList() {
172        if (!resource().isVersioned()) {
173            throw new RepositoryVersionRuntimeException("This operation requires that the node be versionable");
174        }
175
176        return new RdfNamespacedStream(new DefaultRdfStream(
177                asNode(resource()),
178                resource().getTriples(translator(), VERSIONS)),
179                getNamespaces(session()));
180    }
181
182    protected FedoraResource resource() {
183        if (resource == null) {
184            resource = getResourceFromPath(externalPath);
185        }
186
187        return resource;
188    }
189
190}