Connection to localhost:5432 refused during application startup before unit test execution
bobbyrne01 opened this issue · comments
By default my application will acquire db details from application.properties
on app start-up, which is part of Hibernate's startup processing.
# Primary Spring DataSource configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=password
I'm wondering what host:port should be set in the application's properties in order for this library to handle connection requests like this? If it is even possible?
Configuration:
implementation("org.liquibase:liquibase-core:4.13.0")
implementation("org.liquibase:liquibase-gradle-plugin:2.0.4")
implementation("org.hibernate:hibernate-core:5.6.14.Final")
implementation("org.hibernate:hibernate-hikaricp")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.postgresql:postgresql")
testImplementation("io.zonky.test:embedded-database-spring-test:2.2.0")
Error:
2023-01-16 13:23:24.017 INFO 74329 --- [ Test worker] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-01-16 13:23:24.051 INFO 74329 --- [ Test worker] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.14.Final
2023-01-16 13:23:24.165 INFO 74329 --- [ Test worker] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-01-16 13:23:24.262 INFO 74329 --- [ Test worker] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-01-16 13:23:25.326 ERROR 74329 --- [ Test worker] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:319) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:223) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.Driver.makeConnection(Driver.java:402) ~[postgresql-42.3.8.jar:42.3.8]
at org.postgresql.Driver.connect(Driver.java:261) ~[postgresql-42.3.8.jar:42.3.8]
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138) ~[HikariCP-4.0.3.jar:na]
The Java test:
import static io.zonky.test.db.AutoConfigureEmbeddedDatabase.DatabaseType.POSTGRES;
import static org.junit.jupiter.api.Assertions.fail;
import java.sql.*;
import java.util.Date;
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;
@ActiveProfiles("test")
@SpringBootTest
@AutoConfigureEmbeddedDatabase(type = POSTGRES)
public class MyServiceTest {
@Autowired private MyService myService;
@Test
void test_Create() throws InterruptedException {
String myText = "Initial value: " + new Date();
MyDTO myDTO = new MyDTO();
myDTO.setText(myText);
myDto = myService.create(myDTO);
Assertions.assertEquals(1, myService.count());
Assertions.assertTrue(myDto.getId() > 0);
Assertions.assertEquals(myText, myDTO.getText());
}
}
Where the exception originates from:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.connections.spi.AbstractMultiTenantConnectionProvider;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.hikaricp.internal.HikariCPConnectionProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.stereotype.Component;
@Component
public class MultiTenantConnectionProviderImpl extends AbstractMultiTenantConnectionProvider {
private final ConcurrentHashMap<String, HikariCPConnectionProvider> connectionPools =
new ConcurrentHashMap<>();
@Autowired private org.springframework.core.env.Environment springEnv;
@Autowired private JpaProperties jpaProperties;
@Override
protected ConnectionProvider getAnyConnectionProvider() {
return selectConnectionProvider("");
}
@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
HikariCPConnectionProvider connectionProvider = connectionPools.get(tenantIdentifier);
String jdbcUrl;
String databaseUsername;
String databasePassword;
if (connectionProvider != null) {
return connectionProvider;
} else {
if (tenantIdentifier.equalsIgnoreCase("")) {
jdbcUrl = springEnv.getProperty("spring.datasource.url");
databaseUsername = springEnv.getProperty("spring.datasource.username");
databasePassword = springEnv.getProperty("spring.datasource.password");
} else {
jdbcUrl = springEnv.getProperty("jdbcurl");
databaseUsername = springEnv.getProperty("username");
databasePassword = springEnv.getProperty("database");
}
connectionProvider =
initializeConnectionProvider(
jdbcUrl, databaseUsername, databasePassword);
connectionPools.put(tenantIdentifier, connectionProvider);
}
return connectionProvider;
}
private HikariCPConnectionProvider initializeConnectionProvider(
String jdbcUrl, String databaseUsername, String databasePassword) {
final Map<String, String> settings = new HashMap<>();
settings.putAll(jpaProperties.getProperties());
// Set required properties
settings.put(Environment.URL, jdbcUrl);
settings.put(Environment.USER, databaseUsername);
settings.put(Environment.PASS, databasePassword);
settings.put(Environment.CONNECTION_PROVIDER, HikariCPConnectionProvider.class.getName());
final HikariCPConnectionProvider connectionProvider = new HikariCPConnectionProvider();
connectionProvider.configure(settings); // <-- Exception here, based on URL
return connectionProvider;
}
}
Hi @bobbyrne01!
The library doesn't rely on the mentioned properties, it works at the level of data source beans. So you don't have to worry about your application.properties
, it's fine as is. But what is a problem is the custom implementation of MultiTenantConnectionProvider
. Because the implementation operates with spring.datasource.*
variables instead of the data source beans. That's why a test data source cannot be correctly propagated to hibernate and maybe other components. So you have to get rid of the MultiTenantConnectionProviderImpl
(at least for tests, using spring profile), or replace it with a test implementation referring to a data source. Check out the example below.
@Component
public class TestConnectionProvider extends AbstractMultiTenantConnectionProvider {
@Autowired private DataSource dataSource;
@Override
protected ConnectionProvider getAnyConnectionProvider() {
return selectConnectionProvider("");
}
@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
DatasourceConnectionProviderImpl connectionProvider = new DatasourceConnectionProviderImpl();
connectionProvider.setDataSource(dataSource);
return connectionProvider;
}
}
Thanks @tomix26 I've used a test impl as you suggested and unit test is working now.
Appreciate the quick and helpful response!