All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebeaninternal.api.ScopeTrans Maven / Gradle / Ivy

There is a newer version: 8.1.1
Show newest version
package com.avaje.ebeaninternal.api;

import java.util.ArrayList;

import com.avaje.ebean.TxScope;
import com.avaje.ebean.config.PersistBatch;

/**
 * Used internally to handle the scoping of transactions for methods.
 */
public class ScopeTrans implements Thread.UncaughtExceptionHandler {

	private static final int OPCODE_ATHROW = 191;

	private final SpiTransactionScopeManager scopeMgr;

	/**
	 * The suspended transaction (can be null).
	 */
	private final SpiTransaction suspendedTransaction;

	/**
	 * The transaction in scope (can be null).
	 */
	private final SpiTransaction transaction;

	/**
	 * If true by default rollback on Checked exceptions.
	 */
	private final boolean rollbackOnChecked;

	/**
	 * True if the transaction was created and hence should be committed
	 * on finally if it hasn't already been rolled back.
	 */
	private final boolean created;

	/**
	 * Explicit set of Exceptions that DO NOT cause a rollback to occur.
	 */
	private final ArrayList> noRollbackFor;

	/**
	 * Explicit set of Exceptions that DO cause a rollback to occur.
	 */
	private final ArrayList> rollbackFor;

  private PersistBatch restoreBatch;

  private PersistBatch restoreBatchOnCascade;

  private int restoreBatchSize;

  /**
	 * Flag set when a rollback has occurred.
	 */
	private boolean rolledBack;
	
	
	public ScopeTrans(boolean rollbackOnChecked, boolean created, SpiTransaction transaction, TxScope txScope,
			SpiTransaction suspendedTransaction,  SpiTransactionScopeManager scopeMgr) {

		this.rollbackOnChecked = rollbackOnChecked;
		this.created = created;
		this.transaction = transaction;
		this.suspendedTransaction = suspendedTransaction;
		this.scopeMgr = scopeMgr;
		
		this.noRollbackFor = txScope.getNoRollbackFor();
		this.rollbackFor = txScope.getRollbackFor();

    if (transaction != null) {
      if (!created && txScope.isBatchSet() || txScope.isBatchOnCascadeSet() || txScope.isBatchSizeSet()) {
        restoreBatch = transaction.getBatch();
        restoreBatchOnCascade = transaction.getBatchOnCascade();
        restoreBatchSize = transaction.getBatchSize();
      }
      if (txScope.isBatchSet()) {
        transaction.setBatch(txScope.getBatch());
      }
      if (txScope.isBatchOnCascadeSet()) {
        transaction.setBatchOnCascade(txScope.getBatchOnCascade());
      }
      if (txScope.isBatchSizeSet()) {
        transaction.setBatchSize(txScope.getBatchSize());
      }
    }

	}

	/**
	 * Return the current/active transaction.
	 */
	protected SpiTransaction getTransaction() {
		return transaction;
	}

	/**
	 * Called when the Thread catches any uncaught exception.
	 * For example, an unexpected NullPointerException or Error.
	 */
	public void uncaughtException(Thread thread, Throwable e) {
		
		// rollback transaction if required
		caughtThrowable(e);
		
		// reinstate suspended transaction
		onFinally();
	}
	
	/**
	 * Returned via RETURN or expected Exception from the method.
	 * @param returnOrThrowable the return value or Throwable
	 * @param opCode indicates 
	 */
	public void onExit(Object returnOrThrowable, int opCode) {
		
		if (opCode == OPCODE_ATHROW){
			// exited with a Throwable
			caughtThrowable((Throwable)returnOrThrowable);
		}
		onFinally();
	}
	
	
	/**
	 * Commit if the transaction exists and has not already been rolled back.
	 * Also reinstate the suspended transaction if there was one.
	 */
	public void onFinally() {

		try {		
			if (!rolledBack) {
				commitTransaction();
			}
		} finally {
			restoreSuspended();
		}
	}

	protected void restoreSuspended() {
		if (suspendedTransaction != null){
      // put the previously suspended transaction
      // back onto the ThreadLocal or equivalent
      scopeMgr.replace(suspendedTransaction);
    }
	}

	protected void commitTransaction() {
		if (created) {
      transaction.commit();
    } else {
      if (restoreBatch != null) {
        transaction.setBatch(restoreBatch);
      }
      if (restoreBatchOnCascade != null) {
        transaction.setBatchOnCascade(restoreBatchOnCascade);
      }
      if (restoreBatchSize > 0) {
        transaction.setBatchSize(restoreBatchSize);
      }
    }
	}

	/**
	 * An Error was caught and this ALWAYS causes a rollback to occur.
	 * Returns the error and this should be thrown by the calling code.
	 */
	public Error caughtError(Error e) {
		rollback(e);
		return e;
	}
	
	/**
	 * An Exception was caught and may or may not cause a rollback to occur.
	 * Returns the exception and this should be thrown by the calling code.
	 */
	public  T caughtThrowable(T e) {
		
		if (isRollbackThrowable(e)) {
			rollback(e);
		}
		return e;
	}

	protected void rollback(Throwable e) {
		if (transaction != null && transaction.isActive()) {
			// transaction is null for NOT_SUPPORTED and sometimes SUPPORTS
			// and Inactive (already rolled back) if nested REQUIRED
			transaction.rollback(e);
		}
		rolledBack = true;
	}

	/**
	 * Return true if this throwable should cause a rollback to occur.
	 */
	private boolean isRollbackThrowable(Throwable e) {

		if (e instanceof Error){
			return true;
		}
		
		if (noRollbackFor != null){
			for (int i = 0; i < noRollbackFor.size(); i++) {
				if (noRollbackFor.get(i).equals(e.getClass())) {
					
					// explicit no rollback for this one
					return false;
				}
			}
		}

		if (rollbackFor != null){
			for (int i = 0; i < rollbackFor.size(); i++) {
				if (rollbackFor.get(i).equals(e.getClass())) {
					// explicit rollback for this one
					return true;
				}
			}
		}
		
		
		if (e instanceof RuntimeException) {
			return true;
			
		} else {
			// checked exceptions...
			// EJB defaults this to false which is not intuitive IMO
			// Ebean makes this configurable (default to true)
			return rollbackOnChecked;
		}
	}
	

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy