Skip to content

Commit 882a132

Browse files
committed
#16 - ENH: Support specifying database owner username, password and InitDatabase
1 parent 50820fe commit 882a132

4 files changed

Lines changed: 142 additions & 39 deletions

File tree

pom.xml

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
<dependency>
2323
<groupId>io.ebean</groupId>
2424
<artifactId>ebean-datasource-api</artifactId>
25-
<version>4.2</version>
25+
<version>4.3</version>
2626
</dependency>
2727

2828
<dependency>
2929
<groupId>org.slf4j</groupId>
3030
<artifactId>slf4j-api</artifactId>
31-
<version>1.7.12</version>
31+
<version>1.7.25</version>
3232
<scope>provided</scope>
3333
</dependency>
3434

@@ -40,6 +40,20 @@
4040
<scope>test</scope>
4141
</dependency>
4242

43+
<dependency>
44+
<groupId>io.ebean.test</groupId>
45+
<artifactId>ebean-test-docker</artifactId>
46+
<version>2.2.2</version>
47+
<scope>test</scope>
48+
</dependency>
49+
50+
<dependency>
51+
<groupId>org.postgresql</groupId>
52+
<artifactId>postgresql</artifactId>
53+
<version>42.2.5</version>
54+
<scope>test</scope>
55+
</dependency>
56+
4357
<dependency>
4458
<groupId>ch.qos.logback</groupId>
4559
<artifactId>logback-classic</artifactId>
@@ -57,25 +71,4 @@
5771

5872
</dependencies>
5973

60-
<build>
61-
<plugins>
62-
63-
<plugin>
64-
<groupId>io.repaint.maven</groupId>
65-
<artifactId>tiles-maven-plugin</artifactId>
66-
<version>2.12</version>
67-
<extensions>true</extensions>
68-
<configuration>
69-
<tiles>
70-
<tile>org.avaje.tile:java-compile:1.1</tile>
71-
<tile>org.avaje.tile:dependency-tree:1.1</tile>
72-
<tile>org.avaje.tile:pygments-doclet:1.1</tile>
73-
</tiles>
74-
</configuration>
75-
</plugin>
76-
77-
</plugins>
78-
79-
</build>
80-
8174
</project>

src/main/java/io/ebean/datasource/pool/ConnectionPool.java

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.ebean.datasource.DataSourceInitialiseException;
77
import io.ebean.datasource.DataSourcePool;
88
import io.ebean.datasource.DataSourcePoolListener;
9+
import io.ebean.datasource.InitDatabase;
910
import io.ebean.datasource.PoolStatistics;
1011
import io.ebean.datasource.PoolStatus;
1112
import org.slf4j.Logger;
@@ -48,6 +49,8 @@ public class ConnectionPool implements DataSourcePool {
4849
*/
4950
private final String name;
5051

52+
private final DataSourceConfig config;
53+
5154
/**
5255
* Used to notify of changes to the DataSource status.
5356
*/
@@ -189,7 +192,7 @@ public class ConnectionPool implements DataSourcePool {
189192
private long leakTimeMinutes;
190193

191194
public ConnectionPool(String name, DataSourceConfig params) {
192-
195+
this.config = params;
193196
this.name = name;
194197
this.notify = params.getAlert();
195198
this.poolListener = params.getListener();
@@ -290,6 +293,10 @@ private void initialise() throws SQLException {
290293

291294
logger.info(sb.toString());
292295

296+
if (config.useInitDatabase()) {
297+
initialiseDatabase();
298+
}
299+
293300
try {
294301
queue.ensureMinimumConnections();
295302
} catch (SQLException e) {
@@ -300,6 +307,27 @@ private void initialise() throws SQLException {
300307
}
301308
}
302309

310+
/**
311+
* Initialise the database using the owner credentials if we can't connect using the normal credentials.
312+
* <p>
313+
* That is, if we think the username doesn't exist in the DB, initialise the DB using the owner credentials.
314+
* </p>
315+
*/
316+
private void initialiseDatabase() throws SQLException {
317+
try (Connection connection = createUnpooledConnection(connectionProps, false)) {
318+
// successfully obtained a connection so skip initDatabase
319+
connection.clearWarnings();
320+
} catch (SQLException e) {
321+
// expected when user does not exists, obtain a connection using owner credentials
322+
try (Connection connection = createUnpooledConnection(config.getOwnerUsername(), config.getOwnerPassword())) {
323+
// initialise the DB (typically create the user/role using the owner credentials etc)
324+
InitDatabase initDatabase = config.getInitDatabase();
325+
initDatabase.run(connection, config);
326+
connection.commit();
327+
}
328+
}
329+
}
330+
303331
/**
304332
* Returns false.
305333
*/
@@ -476,26 +504,33 @@ private void initConnection(Connection conn) throws SQLException {
476504
}
477505

478506
/**
479-
* Create a Connection that will not be part of the connection pool.
480-
* <p>
481-
* <p>
482-
* When this connection is closed it will not go back into the pool.
483-
* </p>
484-
* <p>
485-
* <p>
486-
* If withDefaults is true then the Connection will have the autoCommit and
487-
* transaction isolation set to the defaults for the pool.
488-
* </p>
507+
* Create an un-pooled connection with the given username and password.
508+
*/
509+
public Connection createUnpooledConnection(String username, String password) throws SQLException {
510+
511+
Properties properties = new Properties(connectionProps);
512+
properties.setProperty("user", username);
513+
properties.setProperty("password", password);
514+
return createUnpooledConnection(properties, true);
515+
}
516+
517+
/**
518+
* Create an un-pooled connection.
489519
*/
490520
public Connection createUnpooledConnection() throws SQLException {
521+
return createUnpooledConnection(connectionProps, true);
522+
}
491523

524+
private Connection createUnpooledConnection(Properties properties, boolean notifyIsDown) throws SQLException {
492525
try {
493-
Connection conn = DriverManager.getConnection(databaseUrl, connectionProps);
526+
Connection conn = DriverManager.getConnection(databaseUrl, properties);
494527
initConnection(conn);
495528
return conn;
496529

497530
} catch (SQLException ex) {
498-
notifyDataSourceIsDown(null);
531+
if (notifyIsDown) {
532+
notifyDataSourceIsDown(null);
533+
}
499534
throw ex;
500535
}
501536
}

src/test/java/io/ebean/datasource/FactoryTest.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,33 @@ public void createPool() throws Exception {
2121

2222
DataSourcePool pool = factory.createPool("test", config);
2323

24-
Connection connection = pool.getConnection();
24+
try (Connection connection = pool.getConnection()) {
25+
try (PreparedStatement stmt = connection.prepareStatement("create table junk (acol varchar(10))")) {
26+
stmt.execute();
27+
connection.commit();
28+
}
29+
}
30+
}
31+
32+
@Test
33+
public void dataSourceFactory_get_createPool() throws Exception {
34+
35+
DataSourceFactory factory = DataSourceFactory.get();
36+
37+
DataSourceConfig config = new DataSourceConfig();
38+
config.setDriver("org.h2.Driver");
39+
config.setUrl("jdbc:h2:mem:tests2");
40+
config.setUsername("sa");
41+
config.setPassword("");
42+
43+
DataSourcePool pool = factory.createPool("test", config);
2544

26-
PreparedStatement stmt = connection.prepareStatement("create table junk (acol varchar(10))");
27-
stmt.execute();
45+
try (Connection connection = pool.getConnection()) {
46+
try (PreparedStatement stmt = connection.prepareStatement("create table junk (acol varchar(10))")) {
47+
stmt.execute();
48+
connection.commit();
49+
}
50+
}
2851

2952
}
3053
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.ebean.datasource;
2+
3+
import io.ebean.docker.commands.PostgresConfig;
4+
import io.ebean.docker.commands.PostgresContainer;
5+
import org.testng.annotations.Test;
6+
7+
import java.sql.Connection;
8+
import java.sql.PreparedStatement;
9+
import java.sql.SQLException;
10+
11+
public class PostgresInitTest {
12+
13+
@Test
14+
public void test() throws SQLException {
15+
16+
PostgresConfig dockerConfig = new PostgresConfig("10");
17+
dockerConfig.setPort("9999");
18+
dockerConfig.setContainerName("pool_test");
19+
dockerConfig.setDbName("app");
20+
// create database with owner as "db_owner"
21+
dockerConfig.setUser("db_owner");
22+
23+
PostgresContainer container = new PostgresContainer(dockerConfig);
24+
try {
25+
container.startWithDropCreate();
26+
27+
DataSourceConfig ds = new DataSourceConfig();
28+
ds.setDriver("org.postgresql.Driver");
29+
ds.setUrl("jdbc:postgresql://127.0.0.1:9999/app");
30+
31+
// our application credentials (typically same as db and schema name with Postgres)
32+
ds.setUsername("app");
33+
ds.setPassword("app_pass");
34+
35+
// database owner credentials used to create the "app" role as needed
36+
ds.setOwnerUsername("db_owner");
37+
ds.setOwnerPassword("test");
38+
39+
DataSourcePool pool = DataSourceFactory.get().createPool("app", ds);
40+
41+
try (Connection connection = pool.getConnection()) {
42+
try (PreparedStatement statement = connection.prepareStatement("create table my_table (acol integer);")) {
43+
statement.execute();
44+
}
45+
connection.commit();
46+
}
47+
48+
} finally {
49+
container.stopRemove();
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)