package play.api.db
import javax.inject.{ Inject, Singleton }
import javax.sql.DataSource
import com.typesafe.config.Config
import play.api.libs.JNDI
import play.api.inject.Module
import play.api._
import scala.concurrent.duration.{ FiniteDuration, Duration }
import scala.util.{ Success, Try, Failure }
import com.zaxxer.hikari.{ HikariDataSource, HikariConfig }
class HikariCPModule extends Module {
def bindings(environment: Environment, configuration: Configuration) = {
Seq(
bind[ConnectionPool].to[HikariCPConnectionPool]
)
}
}
trait HikariCPComponents {
def environment: Environment
lazy val connectionPool: ConnectionPool = new HikariCPConnectionPool(environment)
}
@Singleton
class HikariCPConnectionPool @Inject() (environment: Environment) extends ConnectionPool {
import HikariCPConnectionPool._
override def create(name: String, dbConfig: DatabaseConfig, configuration: Config): DataSource = {
val config = PlayConfig(configuration)
Try {
Logger.info(s"Creating Pool for datasource '$name'")
val hikariConfig = new HikariCPConfig(dbConfig, config).toHikariConfig
val datasource = new HikariDataSource(hikariConfig)
dbConfig.jndiName.foreach { name =>
JNDI.initialContext.rebind(name, datasource)
val visibleUrl = datasource.getJdbcUrl
logger.info(s"""datasource [$visibleUrl] bound to JNDI as $name""")
}
datasource
} match {
case Success(datasource) => datasource
case Failure(ex) => throw config.reportError(name, ex.getMessage, Some(ex))
}
}
override def close(dataSource: DataSource) = {
Logger.info("Shutting down connection pool.")
dataSource match {
case ds: HikariDataSource => ds.shutdown()
case _ => sys.error("Unable to close data source: not a HikariDataSource")
}
}
}
class HikariCPConfig(dbConfig: DatabaseConfig, configuration: PlayConfig) {
def toHikariConfig: HikariConfig = {
val hikariConfig = new HikariConfig()
val config = configuration.get[PlayConfig]("hikaricp")
config.getOptional[String]("dataSourceClassName").foreach(hikariConfig.setDataSourceClassName)
dbConfig.url.foreach(hikariConfig.setJdbcUrl)
dbConfig.driver.foreach(hikariConfig.setDriverClassName)
dbConfig.username.foreach(hikariConfig.setUsername)
dbConfig.password.foreach(hikariConfig.setPassword)
import scala.collection.JavaConverters._
val dataSourceConfig = config.get[PlayConfig]("dataSource")
dataSourceConfig.underlying.root().keySet().asScala.foreach { key =>
hikariConfig.addDataSourceProperty(key, dataSourceConfig.get[String](key))
}
def toMillis(duration: Duration) = {
if (duration.isFinite()) duration.toMillis
else 0l
}
hikariConfig.setAutoCommit(config.get[Boolean]("autoCommit"))
hikariConfig.setConnectionTimeout(toMillis(config.get[Duration]("connectionTimeout")))
hikariConfig.setIdleTimeout(toMillis(config.get[Duration]("idleTimeout")))
hikariConfig.setMaxLifetime(toMillis(config.get[Duration]("maxLifetime")))
config.getOptional[String]("connectionTestQuery").foreach(hikariConfig.setConnectionTestQuery)
config.getOptional[Int]("minimumIdle").foreach(hikariConfig.setMinimumIdle)
hikariConfig.setMaximumPoolSize(config.get[Int]("maximumPoolSize"))
config.getOptional[String]("poolName").foreach(hikariConfig.setPoolName)
hikariConfig.setInitializationFailFast(config.get[Boolean]("initializationFailFast"))
hikariConfig.setIsolateInternalQueries(config.get[Boolean]("isolateInternalQueries"))
hikariConfig.setAllowPoolSuspension(config.get[Boolean]("allowPoolSuspension"))
hikariConfig.setReadOnly(config.get[Boolean]("readOnly"))
hikariConfig.setRegisterMbeans(config.get[Boolean]("registerMbeans"))
config.getOptional[String]("catalog").foreach(hikariConfig.setCatalog)
config.getOptional[String]("transactionIsolation").foreach(hikariConfig.setTransactionIsolation)
hikariConfig.setValidationTimeout(config.get[FiniteDuration]("validationTimeout").toMillis)
hikariConfig.setLeakDetectionThreshold(toMillis(config.get[Duration]("leakDetectionThreshold")))
hikariConfig.validate()
hikariConfig
}
}
object HikariCPConnectionPool {
private val logger = Logger(classOf[HikariCPConnectionPool])
}