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.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.fcrepo.kernel.impl.identifiers.NodeResourceConverter.nodeToResource; 035import static org.slf4j.LoggerFactory.getLogger; 036 037import javax.inject.Inject; 038import javax.jcr.RepositoryException; 039import javax.jcr.Session; 040import javax.servlet.http.HttpServletResponse; 041import javax.ws.rs.DELETE; 042import javax.ws.rs.GET; 043import javax.ws.rs.HeaderParam; 044import javax.ws.rs.POST; 045import javax.ws.rs.PUT; 046import javax.ws.rs.Path; 047import javax.ws.rs.PathParam; 048import javax.ws.rs.Produces; 049import javax.ws.rs.core.Context; 050import javax.ws.rs.core.Request; 051import javax.ws.rs.core.Response; 052import javax.ws.rs.core.UriInfo; 053 054import org.fcrepo.http.commons.responses.HtmlTemplate; 055import org.fcrepo.kernel.models.FedoraResource; 056import org.fcrepo.kernel.exception.RepositoryRuntimeException; 057import org.fcrepo.kernel.exception.RepositoryVersionRuntimeException; 058import org.fcrepo.kernel.impl.rdf.impl.VersionsRdfContext; 059import org.fcrepo.kernel.utils.iterators.RdfStream; 060import org.slf4j.Logger; 061import org.springframework.context.annotation.Scope; 062 063import com.google.common.annotations.VisibleForTesting; 064 065/** 066 * @author cabeer 067 * @since 9/25/14 068 */ 069@Scope("request") 070@Path("/{path: .*}/fcr:versions") 071public class FedoraVersioning extends FedoraBaseResource { 072 073 @Inject 074 protected Session session; 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 noContent().header("Location", 156 nodeToResource(translator()).convert( 157 resource().getBaseVersion().getFrozenNode()).getURI()).build(); 158 } 159 return status(BAD_REQUEST).entity("Specify label for version").build(); 160 } 161 162 163 /** 164 * Get the list of versions for the object 165 * 166 * @return List of versions for the object as RDF 167 */ 168 @GET 169 @HtmlTemplate(value = "fcr:versions") 170 @Produces({TURTLE + ";qs=10", JSON_LD + ";qs=8", N3, N3_ALT2, RDF_XML, NTRIPLES, APPLICATION_XML, TEXT_PLAIN, 171 TURTLE_X, TEXT_HTML, APPLICATION_XHTML_XML, "*/*"}) 172 public RdfStream getVersionList() { 173 if (!resource().isVersioned()) { 174 throw new RepositoryVersionRuntimeException("This operation requires that the node be versionable"); 175 } 176 177 return resource().getTriples(translator(), VersionsRdfContext.class) 178 .session(session) 179 .topic(translator().reverse().convert(resource()).asNode()); 180 } 181 182 protected FedoraResource resource() { 183 if (resource == null) { 184 resource = getResourceFromPath(externalPath); 185 } 186 187 return resource; 188 } 189 190 @Override 191 protected Session session() { 192 return session; 193 } 194}