name.livitski.tools.persista.TransactionalWork.java Source code

Java tutorial

Introduction

Here is the source code for name.livitski.tools.persista.TransactionalWork.java

Source

/*
 *  This file is part of Persista.
 *  Copyright  2013, 2014 Konstantin "Stan" Livitski
 *
 *  Persista is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Additional permissions under GNU Affero GPL version 3 section 7:
 *
 *  1. If you modify this Program, or any covered work, by linking or combining
 *  it with any library or component covered by the terms of Eclipse Public
 *  License version 1.0 and/or Eclipse Distribution License version 1.0, the
 *  licensors of this Program grant you additional permission to convey the
 *  resulting work. Corresponding Source for a non-source form of such a
 *  combination shall include the source code for the aforementioned library or
 *  component as well as that of the covered work.
 *
 *  2. If you modify this Program, or any covered work, by linking or combining
 *  it with the Java Server Pages Expression Language API library (or a
 *  modified version of that library), containing parts covered by the terms of
 *  JavaServer Pages Specification License, the licensors of this Program grant
 *  you additional permission to convey the resulting work.
 *
 ******************************************************************************/
package name.livitski.tools.persista;

import javax.persistence.EntityTransaction;

import org.apache.commons.logging.Log;

import name.livitski.tools.persista.diagn.AbstractStorageException;
import name.livitski.tools.springlet.Logging;

/**
 * Encloses the implementors' code block in JPA transactional brackets.
 */
public abstract class TransactionalWork extends Logging {
    /**
     * Subclasses should place their code that updates the database
     * in this method's implementation.
     * @param db the entity manager the implementor should work with
     * @return <code>true</code> upon successful update indicating
     * that the transaction can be committed, <code>false</code> if
     * the transaction must be rolled back. Implementors SHOULD log
     * an explanation message at the {@link Log#debug(Object) DEBUG}
     * or a higher level with a word "rollback" when they request
     * a rollback without throwing an exception.
     * @throws AbstractStorageException implementors may throw objects
     * of any subclass thereof to indicate an error and roll back the
     * transaction  
     * @throws RuntimeException unchecked exceptions thrown by this
     * method also roll back the transaction
     */
    protected abstract boolean code(EntityManager db) throws AbstractStorageException;

    /**
     * Performs the {@link #code database operations} provided by
     * the subclass in JPA transactional brackets. If there is currently
     * no active transaction at the entity manager, a new transaction
     * is started.
     * If the code finishes normally and does not request the rollback,
     * the transaction is committed if it was started by this method
     * (local transaction).
     * If the implementor's code throws an exception or requests the
     * rollback, the local transaction is rolled back and any ongoing
     * transaction is marked
     * {@link EntityTransaction#setRollbackOnly() rollback-only}.
     * @param db the entity manager the operation will be performed with
     * @return <code>true</code> if the transaction has or may still
     * be committed
     * @throws AbstractStorageException indicates an error during
     * the operation
     * @throws RuntimeException any unchecked implementor's exceptions
     * will be rethrown
     * @see EntityManager#getTransaction()
     */
    public boolean exec(final EntityManager db) throws AbstractStorageException {
        final EntityTransaction transaction = db.getTransaction();
        boolean commit = false;
        Exception status = null;
        boolean localTransaction;
        if (transaction.isActive())
            localTransaction = false;
        else {
            transaction.begin();
            localTransaction = true;
        }
        try {
            commit = code(db);
            return commit;
        } catch (AbstractStorageException fault) {
            status = fault;
            throw fault;
        } catch (RuntimeException fault) {
            status = fault;
            throw fault;
        } finally {
            if (!localTransaction) {
                if (commit)
                    db.flush();
                else
                    transaction.setRollbackOnly();
            } else if (!commit || transaction.getRollbackOnly()) {
                try {
                    transaction.rollback();
                } catch (RuntimeException fault) {
                    if (null != status)
                        log().error("Transaction rollback failed", fault);
                    else
                        throw fault;
                }
            } else // commit local transaction
            {
                try {
                    transaction.commit();
                } catch (RuntimeException fault) {
                    throw fault;
                }
            }
        }
    }
}