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

com.edulify.play.hikaricp.HikariCPConfig.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Edulify.com
 *
 * 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 com.edulify.play.hikaricp

import com.zaxxer.hikari.HikariConfig
import play.api.{Configuration, Logger}

import java.io._
import java.util.Properties

import scala.collection.JavaConversions._

object HikariCPConfig {
  lazy val DEFAULT_DATASOURCE_NAME = "default"
  lazy val HIKARI_CP_PROPERTIES_FILE = "hikaricp.properties"

  def getHikariConfig(dbConfig: Configuration) = {
    val file = new File(HIKARI_CP_PROPERTIES_FILE)
    if(file.exists()) new HikariConfig(props(file))
    else new HikariConfig(HikariCPConfig.mapFromPlayConfiguration(dbConfig))
  }

  private def props(file: File): Properties = {
    if (!file.exists()) {
      throw new IllegalStateException(s"Hikari configuration file ${file.getAbsolutePath} doesn't exist.")
    }

    Logger.info("Loading Hikari configuration from " + file)

    val properties = loadProperties(file)

    logProperties(properties)
    properties
  }

  private def loadProperties(file: File): Properties = {
    val props = new Properties()
    val reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))
    try { props.load(reader) } finally { reader.close() }
    props
  }

  private def mapFromPlayConfiguration(dbConfig: Configuration): Properties = {
    Logger.info("Loading Hikari configuration from Play configuration.")

    val configFile = dbConfig.getString("hikaricp.file")
    if(configFile.nonEmpty) {
      Logger.info("Loading from file configured by db.default.hikaricp.file that is " + configFile)
      return props(new File(configFile.get))
    }

    val properties = new ConfigProperties(dbConfig)
    properties.setPropertyFromConfig("driverClassName", "driver")
    properties.setPropertyFromConfig("jdbcUrl",         "url")
    properties.setPropertyFromConfig("username",        "user")
    properties.setPropertyFromConfig("password",        "password")

    properties.setProperty("maximumPoolSize",        maxPoolSize(dbConfig))
    properties.setProperty("minimumIdle",            minPoolSize(dbConfig))
    properties.setProperty("maxLifetime",            maxLifetime(dbConfig))
    properties.setProperty("idleTimeout",            idleTimeout(dbConfig))
    properties.setProperty("connectionTimeout",      connectionTimeout(dbConfig))
    properties.setProperty("leakDetectionThreshold", leakDetectionThreshold(dbConfig))

    properties.setPropertyFromConfig("catalog",             "defaultCatalog")
    properties.setPropertyFromConfig("autoCommit",          "defaultAutoCommit", "true")
    properties.setPropertyFromConfig("connectionTestQuery", "connectionTestStatement")
    properties.setProperty("jdbc4ConnectionTest", (properties.getProperty("connectionTestQuery") == null).toString)
    properties.setPropertyFromConfig("transactionIsolation", "defaultTransactionIsolation")
    properties.setPropertyFromConfig("readOnly",             "defaultReadOnly", "false")

    properties.setPropertyFromConfig("registerMbeans",       "statisticsEnabled", "false")
    properties.setPropertyFromConfig("connectionInitSql",    "initSQL")

    logProperties(properties)
    properties
  }

  private def logProperties(properties: Properties): Unit = {
    // Log the properties that are used, but don't print out the raw password for security-sake
    Logger.info("Properties: " + properties.map { case (name: String, value: String) =>
      if (name contains "password") {
        "%s=%.1s%s" format(name, value, value.substring(value.length).padTo(value.length - 1, "*").mkString)
      } else "%s=%s" format(name, value)
    }.mkString(", "))
  }

  private def maxPoolSize(config: Configuration) = {
    val partitionCount = config.getInt("partitionCount").getOrElse(1)
    val maxConnectionsPerPartition = config.getInt("maxConnectionsPerPartition").getOrElse(30)
    (partitionCount * maxConnectionsPerPartition).toString
  }

  private def minPoolSize(config: Configuration) = {
    val partitionCount = config.getInt("partitionCount").getOrElse(1)
    val maxConnectionsPerPartition = config.getInt("minConnectionsPerPartition").getOrElse(5)
    (partitionCount * maxConnectionsPerPartition).toString
  }

  private def maxLifetime(config: Configuration) = {
    var maxLife = config.getLong("maxConnectionAgeInMinutes").getOrElse(30L)
    maxLife     = config.getLong("maxConnectionAgeInSeconds").getOrElse(maxLife * 60)
    maxLife     = config.getMilliseconds("maxConnectionAge").getOrElse(maxLife * 1000)
    maxLife.toString
  }

  private def idleTimeout(config: Configuration) = {
    var idleMaxAge = config.getLong("idleMaxAgeInMinutes").getOrElse(10L)
    idleMaxAge     = config.getLong("idleMaxAgeInSeconds").getOrElse(idleMaxAge) * 60
    idleMaxAge     = config.getMilliseconds("idleMaxAge").getOrElse(idleMaxAge) * 1000
    idleMaxAge.toString
  }

  private def connectionTimeout(config: Configuration) = {
    var timeout = config.getLong("connectionTimeoutInMs").getOrElse(30 * 1000L)
    timeout     = config.getMilliseconds("connectionTimeout").getOrElse(timeout)
    timeout.toString
  }

  private def leakDetectionThreshold(config: Configuration) = {
    var threshold = config.getLong("closeConnectionWatchTimeoutInMs").getOrElse(0)
    threshold     = config.getMilliseconds("closeConnectionWatchTimeout").getOrElse(threshold)
    threshold.toString
  }

  private class ConfigProperties(config: Configuration) extends Properties {
    // Keep track of the required fields which if not set cause the startup to fail
    val playRequired : Set[String] = Set("driver", "url", "user", "password")

    def setPropertyFromConfig(poolStr: String, playStr: String, default: String): Unit = {
      setProperty(poolStr, config.getString(playStr).getOrElse(default))
    }

    def setPropertyFromConfig(poolStr: String, playStr: String, required: Boolean = false): Unit = {
      val prop = config.getString(playStr)
      if (prop.nonEmpty) {
        setProperty(poolStr, prop.get)
      } else if(playRequired contains playStr) {
        throw config.reportError("Play Config", "Required property not found: '" + playStr + "'")
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy