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

dbx.api.DatabaseComponents.scala Maven / Gradle / Ivy

package dbx.api

import java.sql.Connection

import com.typesafe.config.ConfigFactory
import dbx.api.Transactional.{TransactionSettings, TransactionSettingsBuilder}
import dbx.compat.Converts
import javax.sql.DataSource
import org.springframework.jdbc.datasource.{DataSourceTransactionManager, DataSourceUtils}
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
import org.springframework.transaction.{PlatformTransactionManager, TransactionUsageException}

import scala.util.{Failure, Success, Try}

class TransactionSettingsProvider {
  def config = ConfigFactory.load("dbx")
  private lazy val settings = {
    var _settings: Map[String, TransactionSettings] = Map.empty
    Try {
      config.getConfig("dbx")
    } match {
      case Success(root) =>
        val resources = Converts.asSeq(root.getStringList("resources"))
        resources map { resource =>
          val settings = root.getConfig(resource)
          val builder = TransactionSettingsBuilder()
          builder.resource = settings.getString("resource")
          builder.readOnly = settings.getBoolean("readOnly")
          builder.isolation = Isolation.withName(settings.getString("isolation"))
          builder.propagation = Propagation.withName(settings.getString("propagation"))
          builder.timeout = settings.getInt("timeout")
          val cl = settings.getClass.getClassLoader
          builder.rollbackFor =  Converts.asSeq(settings.getStringList("rollbackFor")) map (cl.loadClass(_))
          builder.noRollbackFor  = Converts.asSeq(settings.getStringList("noRollbackFor")) map (cl.loadClass(_))
          _settings += resource -> builder.build()
        }
      case Failure(exception) =>
        logger.warn("Can't load transactionSettings, using the default settings", exception)
        val builder = TransactionSettingsBuilder()
        builder.resource = defaultSettings.resource
        builder.readOnly = defaultSettings.readOnly
        builder.isolation = defaultSettings.isolation
        builder.propagation = defaultSettings.propagation
        builder.timeout = defaultSettings.timeout
        builder.rollbackFor = defaultSettings.rollbackFor
        builder.noRollbackFor = defaultSettings.noRollbackFor
        _settings += "default" -> builder.build()
    }
    _settings
  }
  def get(resource: String): TransactionSettings = {
    settings(resource)
  }
}

trait DatabaseComponents {
  def dataSource: DataSource
  lazy val transactionManager: TransactionManagerLookup = new ProvidedDataSourceTransactionManagerLookup(Map("default" -> dataSource))
  lazy val transactionSettingsProvider: TransactionSettingsProvider = new TransactionSettingsProvider(){}
  lazy val transactional: Transactional[Connection] = new ProvidedDataSourceTransactional(Map("default" -> dataSource), transactionManager, transactionSettingsProvider)
}

class ProvidedDataSourceTransactional(dataSources: Map[String, DataSource], override val lookupTransactionManager: TransactionManagerLookup,
                                      override val settingsProvider: TransactionSettingsProvider) extends Transactional[Connection] {

  @volatile
  private var exceptionTranslators = Map.empty[String, Option[SQLErrorCodeSQLExceptionTranslator]]

  override private [api] def exceptionTranslator(resource: String): Option[SQLErrorCodeSQLExceptionTranslator] = {
    if (!exceptionTranslators.contains(resource)) {
      synchronized {
        exceptionTranslators += (resource -> dataSources.get(resource).map(new SQLErrorCodeSQLExceptionTranslator(_)))
      }
    }
    exceptionTranslators(resource)
  }

  override def obtainResource(resource: String): Resource = {
    if (!dataSources.contains(resource)) {
      throw new TransactionUsageException(s"No resource which is named [$resource] !")
    }
    DataSourceUtils.getConnection(dataSources(resource))
  }

  override protected def releaseResource(resource: String, actualResource: Resource): Unit = {
    DataSourceUtils.releaseConnection(actualResource, dataSources(resource))
  }
}

class ProvidedDataSourceTransactionManagerLookup(dataSources: Map[String, DataSource]) extends TransactionManagerLookup {

  @volatile
  private var managers = Map.empty[String, PlatformTransactionManager]

  override def lookup(resource: String): PlatformTransactionManager = {
    if (!managers.contains(resource)) {
      synchronized {
        val dataSource = dataSources.get(resource)
        if (dataSource.isEmpty)  {
          throw new TransactionUsageException(s"No resource is named [$resource] !")
        }
        managers += (resource -> new DataSourceTransactionManager(dataSource.get))
      }
    }
    managers(resource)
  }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy