Skip to content

Commit

Permalink
Refactored database configuration
Browse files Browse the repository at this point in the history
* Included default values in reference.conf
* Switch to new Play idiomatic prototype and optional value patterns
* Added ability to link to reference.conf files in documentation
  • Loading branch information
jroper committed Mar 19, 2015
1 parent cc21b5d commit 98e157f
Show file tree
Hide file tree
Showing 27 changed files with 673 additions and 385 deletions.
52 changes: 2 additions & 50 deletions documentation/manual/detailedTopics/configuration/SettingsJDBC.md
Expand Up @@ -28,54 +28,6 @@ db.default.url="postgres://user:password@localhost:port/database"

## Reference

In addition to the classical `driver`, `url`, `user`, `password` configuration properties, it also supports additional tuning parameters if you need them:
In addition to the classical `driver`, `url`, `username`, `password` configuration properties, it also supports additional tuning parameters if you need them. The `play.db.prototype` configuration from the Play JDBC `reference.conf` is used as the prototype for the configuration for all database connections. The defaults for all the available configuration options can be seen here:

```properties
# The JDBC driver to use
db.default.driver=org.h2.Driver

# The JDBC url
db.default.url="jdbc:h2:mem:play"

# User name
db.default.username=sa

# Password
db.default.password=secret

# Set a connection's default autocommit setting
db.default.autoCommit=true

# Set a connection's default isolation level
db.default.transactionIsolation=TRANSACTION_READ_COMMITTED

# The number of connections to create per pool.
db.default.maximumPoolSize=15

# This property controls the minimum number of idle connections
# that HikariCP tries to maintain in the pool.
db.default.minimumIdle=5

# How long to wait before attempting to obtain a
# connection again after a failure.
db.default.acquireRetryDelay=5 seconds

# The maximum time to wait before a call
# to getConnection is timed out.
db.default.connectionTimeout=1 second

# This property controls the maximum amount of time that a
# connection is allowed to sit idle in the pool.
db.default.idleTimeout=10 minute

# This property controls the maximum lifetime of a connection in
# the pool. When a connection reaches this timeout it will be
# retired from the pool, subject to a maximum variation of +30 seconds.
db.default.maxLifetime=5 minutes

# An initial SQL statement that is run only when
# a connection is first created.
db.default.connectionInitSql="SELECT 1"
```

For a complete list of what can be configured, please see the [HikariCP documentation](https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby). Also, see if your database implementation of `java.sql.DataSource` offers more advanced configuration. A very complete list of database specific DataSource can also be found in [HikariCP documentation](https://github.com/brettwooldridge/HikariCP#popular-datasource-class-names).
@[](/confs/play-jdbc/reference.conf)
10 changes: 8 additions & 2 deletions documentation/manual/releases/Migration24.md
Expand Up @@ -125,9 +125,15 @@ Additionally, Ebean has been upgraded to 4.2.0, which pulls in a few of the feat

## JDBC connection pool

JDBC connection pool is now provided by [HikariCP](http://brettwooldridge.github.io/HikariCP/), instead of BoneCP. Because of that, you must reconfigure your pool to use Hikari properties. You will need to rename the properties in both `.conf` files and in your tests, if you are passing specific configuration to `FakeApplication`s. Take a look at HikariCP documentation in order to see how to properly configure your pool.
The default JDBC connection pool is now provided by [HikariCP](http://brettwooldridge.github.io/HikariCP/), instead of BoneCP.

Besides of that, just two properties are still supported, which are `db.<database>.url` and `db.<database>.driver`. In other words, you can configure the database `url` and `driver` using both HikariCP property or these old ones. We recommend to use the old ones since they will keep compatibility with third party modules and plugins.
To switch back to BoneCP, you can set the `play.db.pool` property in `application.conf`:

```
play.db.pool = bonecp
```

The full range of configuration options available to the Play connection pools can be found in the Play JDBC [`reference.conf`](resources/confs/play-jdbc/reference.conf).

## Body Parsers

Expand Down
10 changes: 10 additions & 0 deletions documentation/manual/working/javaGuide/main/sql/JavaDatabase.md
Expand Up @@ -117,6 +117,16 @@ For example, if you use MySQL5, you need to add a [[dependency| SBTDependencies]
libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.18"
```

## Selecting and configuring the connection pool

Out of the box, Play provides two database connection pool implementations, [HikariCP](https://github.com/brettwooldridge/HikariCP) and [BoneCP](http://jolbox.com/). The default is HikariCP, but this can be changed by setting the `play.db.pool` property:

```
play.db.pool=bonecp
```

The full range of configuration options for connection pools can be found by inspecting the `play.db.prototype` property in Play's JDBC [`reference.conf`](resources/confs/play-jdbc/reference.conf).

## Testing

For information on testing with databases, including how to setup in-memory databases and, see [[Testing With Databases|JavaTestingWithDatabases]].
Expand Down
17 changes: 10 additions & 7 deletions documentation/manual/working/scalaGuide/main/sql/ScalaDatabase.md
Expand Up @@ -63,13 +63,6 @@ db.default.user=playdbuser
db.default.password="a strong password"
```

## How to see SQL Statement in the console?

```properties
db.default.logStatements=true
logger.com.jolbox=DEBUG // for EBean
```

## How to configure several data sources

```properties
Expand Down Expand Up @@ -171,6 +164,16 @@ DB.withTransaction { conn =>
}
```

## Selecting and configuring the connection pool

Out of the box, Play provides two database connection pool implementations, [HikariCP](https://github.com/brettwooldridge/HikariCP) and [BoneCP](http://jolbox.com/). The default is HikariCP, but this can be changed by setting the `play.db.pool` property:

```
play.db.pool=bonecp
```

The full range of configuration options for connection pools can be found by inspecting the `play.db.prototype` property in Play's JDBC [`reference.conf`](resources/confs/play-jdbc/reference.conf).

## Testing

For information on testing with databases, including how to setup in-memory databases and, see [[Testing With Databases|ScalaTestingWithDatabases]].
4 changes: 1 addition & 3 deletions documentation/project/Build.scala
Expand Up @@ -58,9 +58,7 @@ object ApplicationBuild extends Build {
"org.mockito" % "mockito-core" % "1.9.5" % "test"
),

PlayDocsKeys.fallbackToJar := false,

PlayDocsKeys.docsJarFile := Option((packageBin in (playDocs, Compile)).value),
PlayDocsKeys.docsJarFile := Some((packageBin in (playDocs, Compile)).value),

PlayDocsKeys.javaManualSourceDirectories := (baseDirectory.value / "manual" / "working" / "javaGuide" ** codeFilter).get,
PlayDocsKeys.scalaManualSourceDirectories := (baseDirectory.value / "manual" / "working" / "scalaGuide" ** codeFilter).get,
Expand Down
4 changes: 2 additions & 2 deletions framework/project/Dependencies.scala
Expand Up @@ -236,7 +236,7 @@ object Dependencies {
)

val playDocsDependencies = Seq(
"com.typesafe.play" %% "play-doc" % "1.2.1"
"com.typesafe.play" %% "play-doc" % "1.2.2"
) ++ playdocWebjarDependencies

val iterateesDependencies = Seq(
Expand Down Expand Up @@ -283,7 +283,7 @@ object Dependencies {
mockitoAll % Test

val playDocsSbtPluginDependencies = Seq(
"com.typesafe.play" %% "play-doc" % "1.2.0"
"com.typesafe.play" %% "play-doc" % "1.2.2"
)

}
39 changes: 36 additions & 3 deletions framework/project/Docs.scala
Expand Up @@ -21,6 +21,7 @@ object Docs {
val apiDocsUseCache = SettingKey[Boolean]("api-docs-use-cache", "Whether to cache the doc inputs (can hit cache limit with dbuild)")
val apiDocs = TaskKey[File]("api-docs", "Generate the API docs")
val extractWebjars = TaskKey[File]("extract-webjars", "Extract webjar contents")
val allReferenceConfs = TaskKey[Seq[(String, File)]]("all-reference-confs", "Gather all reference confs")

lazy val settings = Seq(
apiDocsInclude := false,
Expand All @@ -32,7 +33,8 @@ object Docs {
apiDocs <<= apiDocsTask,
ivyConfigurations += Webjars,
extractWebjars <<= extractWebjarContents,
mappings in (Compile, packageBin) <++= (baseDirectory, apiDocs, extractWebjars, version) map { (base, apiBase, webjars, playVersion) =>
allReferenceConfs in Global <<= (thisProjectRef, buildStructure) flatMap allReferenceConfsTask,
mappings in (Compile, packageBin) <++= (baseDirectory, apiDocs, extractWebjars, version, allReferenceConfs) map { (base, apiBase, webjars, playVersion, referenceConfs) =>
// Include documentation and API docs in main binary JAR
val docBase = base / "../../../documentation"
val raw = (docBase \ "manual" ** "*") +++ (docBase \ "style" ** "*")
Expand All @@ -44,7 +46,12 @@ object Docs {
// The play version is added so that resource paths are versioned
val webjarMappings = webjars.*** pair rebase(webjars, "play/docs/content/webjars/" + playVersion)

docMappings ++ apiDocMappings ++ webjarMappings
// Gather all the reference.conf files into the project
val referenceConfMappings = referenceConfs.map {
case (projectName, referenceConf) => referenceConf -> s"play/docs/content/confs/$projectName/reference.conf"
}

docMappings ++ apiDocMappings ++ webjarMappings ++ referenceConfMappings
}
)

Expand All @@ -65,7 +72,12 @@ object Docs {
val playVersion = version.value
val webjarMappings = webjars.*** pair rebase(webjars, "webjars/" + playVersion)

docMappings ++ webjarMappings
// Gather all the reference.conf files into the project
val referenceConfs = allReferenceConfs.value.map {
case (projectName, referenceConf) => referenceConf -> s"confs/$projectName/reference.conf"
}

docMappings ++ webjarMappings ++ referenceConfs
}
)

Expand Down Expand Up @@ -126,6 +138,27 @@ object Docs {
apiTarget
}

def allReferenceConfsTask(projectRef: ProjectRef, structure: BuildStructure): Task[Seq[(String, File)]] = {
val projects = allApiProjects(projectRef.build, structure)
val unmanagedResourcesTasks = projects map { ref =>
def taskFromProject[T](task: TaskKey[T]) = task in Compile in ref get structure.data

val projectId = moduleName in ref get structure.data

val referenceConfs = (unmanagedResources in Compile in ref get structure.data).map(_.map { resources =>
(for {
referenceConf <- resources.find(_.name == "reference.conf")
id <- projectId
} yield id -> referenceConf).toSeq
})

// Join them
val tasks = referenceConfs.toSeq
tasks.join.map(_.flatten)
}
unmanagedResourcesTasks.join.map(_.flatten)
}

def allSources(conf: Configuration, extension: String)(projectRef: ProjectRef, structure: BuildStructure): Task[Seq[File]] = {
val projects = allApiProjects(projectRef.build, structure)
val sourceTasks = projects map { ref =>
Expand Down
Expand Up @@ -6,6 +6,7 @@
import javax.sql.DataSource;

import play.Configuration;
import play.Environment;

/**
* Connection pool API for managing data sources.
Expand All @@ -17,10 +18,10 @@ public interface ConnectionPool {
*
* @param name the database name
* @param configuration the data source configuration
* @param classLoader the database class loader
* @param environment the database environment
* @return a data source backed by a connection pool
*/
public DataSource create(String name, Configuration configuration, ClassLoader classLoader);
public DataSource create(String name, Configuration configuration, Environment environment);

/**
* Close the given data source.
Expand Down
Expand Up @@ -26,8 +26,8 @@ public class DBModule extends Module {

@Override
public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
String dbKey = configuration.underlying().getString("play.modules.db.config");
String defaultDb = configuration.underlying().getString("play.modules.db.default");
String dbKey = configuration.underlying().getString("play.db.config");
String defaultDb = configuration.underlying().getString("play.db.default");

ImmutableList.Builder<Binding<?>> list = new ImmutableList.Builder<Binding<?>>();

Expand Down
Expand Up @@ -8,6 +8,9 @@
import javax.sql.DataSource;

import play.Configuration;
import play.Environment;
import play.api.PlayConfig;
import play.api.db.DatabaseConfig;

/**
* Default delegating implementation of the connection pool API.
Expand All @@ -22,8 +25,9 @@ public DefaultConnectionPool(play.api.db.ConnectionPool connectionPool) {
this.cp = connectionPool;
}

public DataSource create(String name, Configuration configuration, ClassLoader classLoader) {
return cp.create(name, configuration.getWrappedConfiguration(), classLoader);
public DataSource create(String name, Configuration configuration, Environment environment) {
PlayConfig config = new PlayConfig(configuration.getWrappedConfiguration().underlying());
return cp.create(name, DatabaseConfig.fromConfig(config, environment.underlying()), config.underlying());
}

public void close(DataSource dataSource) {
Expand Down
Expand Up @@ -26,14 +26,20 @@ public DefaultDatabase(play.api.db.Database database) {
* Create a default BoneCP-backed database.
*/
public DefaultDatabase(String name, Configuration configuration) {
this(new play.api.db.PooledDatabase(name, configuration.getWrappedConfiguration()));
this(new play.api.db.PooledDatabase(name, new play.api.Configuration(
configuration.underlying()
.withFallback(ConfigFactory.defaultReference().getConfig("play.db.prototype"))
)));
}

/**
* Create a default BoneCP-backed database.
*/
public DefaultDatabase(String name, Map<String, ? extends Object> config) {
this(new play.api.db.PooledDatabase(name, new play.api.Configuration(ConfigFactory.parseMap(config))));
this(new play.api.db.PooledDatabase(name, new play.api.Configuration(
ConfigFactory.parseMap(config)
.withFallback(ConfigFactory.defaultReference().getConfig("play.db.prototype"))
)));
}

@Override
Expand Down
Expand Up @@ -76,7 +76,7 @@ public void run() {
@Test
public void allowDefaultDatabaseNameToBeConfigured() {
Map<String, String> config = ImmutableMap.of(
"play.modules.db.default", "other",
"play.db.default", "other",
"db.other.driver", "org.h2.Driver",
"db.other.url", "jdbc:h2:mem:other"
);
Expand All @@ -94,7 +94,7 @@ public void run() {
@Test
public void allowDbConfigKeyToBeConfigured() {
Map<String, String> config = ImmutableMap.of(
"play.modules.db.config", "databases",
"play.db.config", "databases",
"databases.default.driver", "org.h2.Driver",
"databases.default.url", "jdbc:h2:mem:default"
);
Expand Down
36 changes: 36 additions & 0 deletions framework/src/play-java-jpa/src/test/resources/logback.xml
@@ -0,0 +1,36 @@
<!--
~ Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
-->
<!-- The default logback configuration that Play uses if no other configuration is provided -->
<configuration>

<conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${application.home}/logs/application.log</file>
<encoder>
<pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
</encoder>
</appender>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
</encoder>
</appender>

<logger name="play" level="INFO" />
<logger name="application" level="DEBUG" />

<!-- Off these ones as they are annoying, and anyway we manage configuration ourself -->
<logger name="com.avaje.ebean.config.PropertyMapLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.core.XmlConfigLoader" level="OFF" />
<logger name="com.avaje.ebeaninternal.server.lib.BackgroundThread" level="OFF" />
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />

<root level="WARN">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>

</configuration>

0 comments on commit 98e157f

Please sign in to comment.