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 org.fcrepo.common.db.DbTransactionExecutor;
021import org.fcrepo.http.commons.AbstractResource;
022import org.fcrepo.http.commons.api.rdf.HttpIdentifierConverter;
023import org.fcrepo.http.commons.session.TransactionProvider;
024import org.fcrepo.kernel.api.Transaction;
025import org.fcrepo.kernel.api.TransactionManager;
026import org.fcrepo.kernel.api.exception.PathNotFoundException;
027import org.fcrepo.kernel.api.identifiers.FedoraId;
028import org.fcrepo.kernel.api.models.FedoraResource;
029import org.fcrepo.kernel.api.models.ResourceFactory;
030import org.fcrepo.kernel.api.models.ResourceHelper;
031import org.slf4j.Logger;
032
033import javax.inject.Inject;
034import javax.servlet.http.HttpServletRequest;
035import javax.ws.rs.core.Context;
036import javax.ws.rs.core.SecurityContext;
037import java.security.Principal;
038
039import static org.slf4j.LoggerFactory.getLogger;
040
041/**
042 * @author cabeer
043 * @since 10/5/14
044 */
045abstract public class FedoraBaseResource extends AbstractResource {
046
047    private static final Logger LOGGER = getLogger(FedoraBaseResource.class);
048
049    @Context
050    protected SecurityContext securityContext;
051
052    @Inject
053    protected ResourceFactory resourceFactory;
054
055    @Inject
056    protected ResourceHelper resourceHelper;
057
058    @Context protected HttpServletRequest servletRequest;
059
060    @Inject
061    protected TransactionManager txManager;
062
063    @Inject
064    protected DbTransactionExecutor dbTransactionExecutor;
065
066    private TransactionProvider txProvider;
067
068    private Transaction transaction;
069
070    protected HttpIdentifierConverter identifierConverter;
071
072    protected HttpIdentifierConverter identifierConverter() {
073        if (identifierConverter == null) {
074            identifierConverter = new HttpIdentifierConverter(
075                    uriInfo.getBaseUriBuilder().clone().path(FedoraLdp.class));
076        }
077
078        return identifierConverter;
079    }
080
081    /**
082     * Gets a fedora resource by id. Uses the provided transaction if it is uncommitted,
083     * or uses a new transaction.
084     *
085     * @param transaction the fedora transaction
086     * @param fedoraId    identifier of the resource
087     * @return the requested FedoraResource
088     */
089    protected FedoraResource getFedoraResource(final Transaction transaction, final FedoraId fedoraId)
090            throws PathNotFoundException {
091        return resourceFactory.getResource(transaction, fedoraId);
092    }
093
094    /**
095     * @param transaction the transaction in which to check
096     * @param fedoraId identifier of the object to check
097     * @param includeDeleted Whether to check for deleted resources too.
098     * @return Returns true if an object with the provided id exists
099     */
100    protected boolean doesResourceExist(final Transaction transaction, final FedoraId fedoraId,
101                                        final boolean includeDeleted) {
102        return resourceHelper.doesResourceExist(transaction, fedoraId, includeDeleted);
103    }
104
105    /**
106     *
107     * @param transaction the transaction in which to check
108     * @param fedoraId identifier of the object to check
109     * @return Returns true if object does not exist but whose ID starts other resources that do exist.
110     */
111    protected boolean isGhostNode(final Transaction transaction, final FedoraId fedoraId) {
112        return resourceHelper.isGhostNode(transaction, fedoraId);
113    }
114
115    protected String getUserPrincipal() {
116        final Principal p = securityContext.getUserPrincipal();
117        return p == null ? null : p.getName();
118    }
119
120    protected Transaction transaction() {
121        if (transaction == null) {
122            txProvider = new TransactionProvider(txManager, servletRequest,
123                    uriInfo.getBaseUri(), fedoraPropsConfig.getJmsBaseUrl());
124            transaction = txProvider.provide();
125        }
126        return transaction;
127    }
128
129    /**
130     * Executes the runnable within a DB transaction that will retry entire block on MySQL deadlock exceptions.
131     * If the runnable throws an exception, after completing any retires, then the Fedora transaction is marked
132     * as failed.
133     *
134     * @param action the code to execute
135     */
136    protected void doInDbTxWithRetry(final Runnable action) {
137        try {
138            dbTransactionExecutor.doInTxWithRetry(action);
139        } catch (RuntimeException e) {
140            transaction().fail();
141            throw e;
142        }
143    }
144
145    /**
146     * Executes the runnable within a DB transaction. The block is NOT retried on MySQL deadlock exceptions.
147     * If the runnable throws an exception, then the Fedora transaction is marked as failed.
148     *
149     * @param action the code to execute
150     */
151    protected void doInDbTx(final Runnable action) {
152        try {
153            dbTransactionExecutor.doInTx(action);
154        } catch (RuntimeException e) {
155            transaction().fail();
156            throw e;
157        }
158    }
159
160}