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

net.liftmodules.transaction.TransactionContext.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2009-2013 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftmodules
package transaction

import javax.persistence.EntityManager
import javax.transaction.{Transaction, Status, TransactionManager}

import org.scala_libs.jpa.{ScalaEntityManager, ScalaEMFactory}

import net.liftweb.common.Loggable


/**
 * Base monad for the transaction monad implementations.
 *
 * @author Jonas Bonér
 */
trait TransactionMonad {

  // -----------------------------
  // Monadic definitions
  // -----------------------------

  def map[T](f: TransactionMonad => T): T
  def flatMap[T](f: TransactionMonad => T): T
  def foreach(f: TransactionMonad => Unit): Unit
  def filter(f: TransactionMonad => Boolean): TransactionMonad =
    if (f(this)) this else TransactionContext.NoOpTransactionMonad

  // -----------------------------
  // JTA Transaction definitions
  // -----------------------------

  /**
   * Returns the current Transaction.
   */
  def getTransaction: Transaction = TransactionContext.getTransactionManager.getTransaction

  /**
   * Marks the current transaction as doomed.
   */
  def setRollbackOnly = TransactionContext.setRollbackOnly

  /**
   * Marks the current transaction as doomed.
   */
  def doom = TransactionContext.setRollbackOnly

  /**
   * Checks if the current transaction is doomed.
   */
  def isRollbackOnly = TransactionContext.isRollbackOnly

  /**
   * Checks that the current transaction is NOT doomed.
   */
  def isNotDoomed = !TransactionContext.isRollbackOnly

  // -----------------------------
  // JPA EntityManager definitions
  // -----------------------------

  /**
   * Returns the current EntityManager.
   */
  def getEntityManager: EntityManager = TransactionContext.getEntityManager

  /**
   * Checks if an EntityManager exists in current context.
   */
  //def hasEntityManager: Boolean = TransactionContext.hasEntityManager

  /**
   * Closes and removes the current EntityManager.
   * 

* IMPORTANT: This method must always be used to close the EntityManager, never use em.close directly. */ def closeEntityManager = TransactionContext.closeEntityManager } /** * Manages a thread-local stack of TransactionContexts. *

* Choose TransactionService implementation by implicit definition of the implementation of choice, * e.g. implicit val txService = TransactionServices.AtomikosTransactionService. *

* Example usage 1: *

 * for {
 *   ctx <- TransactionContext.Required
 *   entity <- updatedEntities
 *   if !ctx.isRollbackOnly
 * } {
 *   // transactional stuff
 *   ctx.getEntityManager.merge(entity)
 * }
 * 
* Example usage 2: *
 * val users = for {
 *   ctx <- TransactionContext.Required
 *   name <- userNames
 * } yield {
 *   // transactional stuff
 *   val query = ctx.getEntityManager.createNamedQuery("findUserByName")
 *   query.setParameter("userName", name)
 *   query.getSingleResult
 * }
 * 
* * @author Jonas Bonér */ object TransactionContext extends TransactionProtocol with Loggable { // FIXME: make configurable private implicit val defaultTransactionService = atomikos.AtomikosTransactionService private[TransactionContext] val stack = new scala.util.DynamicVariable(new TransactionContext) object Required extends TransactionMonad { def map[T](f: TransactionMonad => T): T = withTxRequired { f(this) } def flatMap[T](f: TransactionMonad => T): T = withTxRequired { f(this) } def foreach(f: TransactionMonad => Unit): Unit = withTxRequired { f(this) } } object RequiresNew extends TransactionMonad { def map[T](f: TransactionMonad => T): T = withTxRequiresNew { f(this) } def flatMap[T](f: TransactionMonad => T): T = withTxRequiresNew { f(this) } def foreach(f: TransactionMonad => Unit): Unit = withTxRequiresNew { f(this) } } object Supports extends TransactionMonad { def map[T](f: TransactionMonad => T): T = withTxSupports { f(this) } def flatMap[T](f: TransactionMonad => T): T = withTxSupports { f(this) } def foreach(f: TransactionMonad => Unit): Unit = withTxSupports { f(this) } } object Mandatory extends TransactionMonad { def map[T](f: TransactionMonad => T): T = withTxMandatory { f(this) } def flatMap[T](f: TransactionMonad => T): T = withTxMandatory { f(this) } def foreach(f: TransactionMonad => Unit): Unit = withTxMandatory { f(this) } } object Never extends TransactionMonad { def map[T](f: TransactionMonad => T): T = withTxNever { f(this) } def flatMap[T](f: TransactionMonad => T): T = withTxNever { f(this) } def foreach(f: TransactionMonad => Unit): Unit = withTxNever { f(this) } } object NoOpTransactionMonad extends TransactionMonad { def map[T](f: TransactionMonad => T): T = f(this) def flatMap[T](f: TransactionMonad => T): T = f(this) def foreach(f: TransactionMonad => Unit): Unit = f(this) override def filter(f: TransactionMonad => Boolean): TransactionMonad = this } private[transaction] def setRollbackOnly = current.setRollbackOnly private[transaction] def isRollbackOnly = current.isRollbackOnly private[transaction] def getTransactionManager: TransactionManager = current.getTransactionManager private[transaction] def getTransaction: Transaction = current.getTransactionManager.getTransaction private[transaction] def getEntityManager: EntityManager = current.getEntityManager private[transaction] def closeEntityManager = current.closeEntityManager private[this] def current = stack.value /** * Continues with the invocation defined in 'body' with the brand new context define in 'newCtx', the old * one is put on the stack and will automatically come back in scope when the method exits. *

* Suspends and resumes the current JTA transaction. */ private[transaction] def withNewContext[T](body: => T): T = { val suspendedTx: Option[Transaction] = if (isInExistingTransaction(getTransactionManager)) { logger.debug("Suspending TX") Some(getTransactionManager.suspend) } else None val result = stack.withValue(new TransactionContext) { body } if (suspendedTx.isDefined) { logger.debug("Resuming TX") getTransactionManager.resume(suspendedTx.get) } result } } /** * Transaction context, holds the EntityManager and the TransactionManager. * * @author Jonas Bonér */ class TransactionContext(private implicit val transactionService: TransactionService) extends ScalaEntityManager with ScalaEMFactory { val em: EntityManager = transactionService.entityManagerFactory.createEntityManager val tm: TransactionManager = transactionService.transactionManager private def setRollbackOnly = tm.setRollbackOnly protected def getUnitName = "N/A" private def isRollbackOnly: Boolean = tm.getStatus == Status.STATUS_MARKED_ROLLBACK private def getTransactionManager: TransactionManager = tm private def getEntityManager: EntityManager = em private def closeEntityManager = em.close // --------------------------------- // To make ScalaEMFactory happy val factory = this def openEM: javax.persistence.EntityManager = em def closeEM(e: javax.persistence.EntityManager) = closeEntityManager }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy