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