001/**
002 * Copyright 2014 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.lang.StringUtils.isBlank;
027import static org.fcrepo.http.commons.domain.RDFMediaType.JSON_LD;
028import static org.fcrepo.http.commons.domain.RDFMediaType.N3;
029import static org.fcrepo.http.commons.domain.RDFMediaType.N3_ALT2;
030import static org.fcrepo.http.commons.domain.RDFMediaType.NTRIPLES;
031import static org.fcrepo.http.commons.domain.RDFMediaType.RDF_XML;
032import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE;
033import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_X;
034import static org.slf4j.LoggerFactory.getLogger;
035
036import javax.inject.Inject;
037import javax.jcr.RepositoryException;
038import javax.jcr.Session;
039import javax.servlet.http.HttpServletResponse;
040import javax.ws.rs.DELETE;
041import javax.ws.rs.GET;
042import javax.ws.rs.HeaderParam;
043import javax.ws.rs.POST;
044import javax.ws.rs.PUT;
045import javax.ws.rs.Path;
046import javax.ws.rs.PathParam;
047import javax.ws.rs.Produces;
048import javax.ws.rs.core.Context;
049import javax.ws.rs.core.Request;
050import javax.ws.rs.core.Response;
051import javax.ws.rs.core.UriInfo;
052
053import org.fcrepo.http.commons.responses.HtmlTemplate;
054import org.fcrepo.kernel.models.FedoraResource;
055import org.fcrepo.kernel.exception.RepositoryRuntimeException;
056import org.fcrepo.kernel.exception.RepositoryVersionRuntimeException;
057import org.fcrepo.kernel.impl.rdf.impl.VersionsRdfContext;
058import org.fcrepo.kernel.utils.iterators.RdfStream;
059import org.slf4j.Logger;
060import org.springframework.context.annotation.Scope;
061
062import com.google.common.annotations.VisibleForTesting;
063
064/**
065 * @author cabeer
066 * @since 9/25/14
067 */
068@Scope("request")
069@Path("/{path: .*}/fcr:versions")
070public class FedoraVersioning extends FedoraBaseResource {
071
072    @Inject
073    protected Session session;
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
097     */
098    @VisibleForTesting
099    public FedoraVersioning(final String externalPath) {
100        this.externalPath = externalPath;
101    }
102
103
104    /**
105     * Enable versioning
106     * @return
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
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     * @return response
145     * @throws RepositoryException
146     */
147    @POST
148    public Response addVersion(@HeaderParam("Slug") final String slug) throws RepositoryException {
149        if (!isBlank(slug)) {
150            LOGGER.info("Add version {} for '{}'", slug, externalPath);
151            final String path = toPath(translator(), externalPath);
152            versionService.createVersion(session, path);
153            resource().addVersionLabel(slug);
154            return noContent().header("Location", uriInfo.getRequestUri() + "/" + slug).build();
155        }
156        return status(BAD_REQUEST).entity("Specify label for version").build();
157    }
158
159
160    /**
161     * Get the list of versions for the object
162     *
163     * @return List of versions for the object as RDF
164     * @throws RepositoryException
165     */
166    @GET
167    @HtmlTemplate(value = "fcr:versions")
168    @Produces({TURTLE + ";qs=10", JSON_LD + ";qs=8", N3, N3_ALT2, RDF_XML, NTRIPLES, APPLICATION_XML, TEXT_PLAIN,
169            TURTLE_X, TEXT_HTML, APPLICATION_XHTML_XML, "*/*"})
170    public RdfStream getVersionList() {
171        if (!resource().isVersioned()) {
172            throw new RepositoryVersionRuntimeException("This operation requires that the node be versionable");
173        }
174
175        return resource().getTriples(translator(), VersionsRdfContext.class)
176                .session(session)
177                .topic(translator().reverse().convert(resource()).asNode());
178    }
179
180    protected FedoraResource resource() {
181        if (resource == null) {
182            resource = getResourceFromPath(externalPath);
183        }
184
185        return resource;
186    }
187
188    @Override
189    protected Session session() {
190        return session;
191    }
192}