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.Response.created;
019import static javax.ws.rs.core.Response.noContent;
020import static javax.ws.rs.core.Response.status;
021import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
022import static org.slf4j.LoggerFactory.getLogger;
023
024import java.net.URI;
025import java.net.URISyntaxException;
026import java.security.Principal;
027
028import javax.servlet.http.HttpServletRequest;
029import javax.ws.rs.POST;
030import javax.ws.rs.Path;
031import javax.ws.rs.PathParam;
032import javax.ws.rs.core.Context;
033import javax.ws.rs.core.Response;
034
035import org.fcrepo.kernel.api.Transaction;
036import org.fcrepo.kernel.api.TxSession;
037import org.fcrepo.kernel.api.services.TransactionService;
038import org.slf4j.Logger;
039import org.springframework.beans.factory.annotation.Autowired;
040import org.springframework.context.annotation.Scope;
041
042/**
043 * Transactions over REST
044 *
045 * @author awoods
046 * @author gregjan
047 */
048@Scope("prototype")
049@Path("/{path: .*}/fcr:tx")
050public class FedoraTransactions extends FedoraBaseResource {
051
052    private static final Logger LOGGER = getLogger(FedoraTransactions.class);
053
054    @Autowired
055    private TransactionService txService;
056
057    /**
058     * Create a new transaction resource and add it to the registry
059     *
060     * @param externalPath the external path
061     * @param req the http servlet request
062     * @return 201 with the transaction id and expiration date
063     * @throws URISyntaxException if URI syntax exception occurred
064     */
065    @POST
066    public Response createTransaction(@PathParam("path") final String externalPath,
067                                      @Context final HttpServletRequest req) throws URISyntaxException {
068
069        if (session instanceof TxSession) {
070            final Transaction t = txService.getTransaction(session);
071            LOGGER.debug("renewing transaction {}", t.getId());
072            t.updateExpiryDate();
073            return noContent().expires(t.getExpires()).build();
074        }
075
076        if (externalPath != null && !externalPath.isEmpty()) {
077            return status(BAD_REQUEST).build();
078        }
079
080        final Principal userPrincipal = req.getUserPrincipal();
081        String userName = null;
082        if (userPrincipal != null) {
083            userName = userPrincipal.getName();
084        }
085
086        final Transaction t = txService.beginTransaction(session, userName);
087        LOGGER.info("Created transaction '{}'", t.getId());
088
089        return created(new URI(translator().toDomain("/tx:" + t.getId()).toString())).expires(
090                t.getExpires()).build();
091    }
092
093    /**
094     * Commit a transaction resource
095     *
096     * @param externalPath the external path
097     * @return 204
098     */
099    @POST
100    @Path("fcr:commit")
101    public Response commit(@PathParam("path") final String externalPath) {
102        LOGGER.info("Commit transaction '{}'", externalPath);
103        return finalizeTransaction(externalPath, true);
104
105    }
106
107    /**
108     * Rollback a transaction
109     *
110     * @param externalPath the external path
111     * @return 204
112     */
113    @POST
114    @Path("fcr:rollback")
115    public Response rollback(@PathParam("path") final String externalPath) {
116        LOGGER.info("Rollback transaction '{}'", externalPath);
117        return finalizeTransaction(externalPath, false);
118    }
119
120    private Response finalizeTransaction(@PathParam("path")
121        final String externalPath, final boolean commit) {
122
123        final String path = toPath(translator(), externalPath);
124        if (!path.equals("/")) {
125            return status(BAD_REQUEST).build();
126        }
127
128        final String txId;
129        if (session instanceof TxSession) {
130            txId = ((TxSession) session).getTxId();
131        } else {
132            txId = "";
133        }
134
135        if (txId.isEmpty()) {
136            LOGGER.debug("cannot finalize an empty tx id {} at path {}",
137                    txId, path);
138            return status(BAD_REQUEST).build();
139        }
140
141        if (commit) {
142            LOGGER.debug("commiting transaction {} at path {}", txId, path);
143            txService.commit(txId);
144
145        } else {
146            LOGGER.debug("rolling back transaction {} at path {}", txId,
147                    path);
148            txService.rollback(txId);
149        }
150        return noContent().build();
151    }
152}