Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Commit dc5f51e

Browse files
authored
Obtaining url from database metadata (#52)
* Obtaining url from database metadata * Fixed code smell --------- Co-authored-by: Ivan Vakhrushev <[email protected]>
1 parent be39850 commit dc5f51e

File tree

5 files changed

+112
-15
lines changed

5 files changed

+112
-15
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ij_java_keep_simple_classes_in_one_line = false
3131
ij_java_keep_simple_lambdas_in_one_line = true
3232
ij_java_keep_simple_methods_in_one_line = true
3333
ij_java_names_count_to_use_import_on_demand = 101
34+
ij_java_class_count_to_use_import_on_demand = 101
3435
ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
3536
ij_java_prefer_longer_names = true
3637
ij_java_space_after_closing_angle_bracket_in_type_argument = false

src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfiguration.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import io.github.mfvanek.pg.checks.host.UnusedIndexesCheckOnHost;
2727
import io.github.mfvanek.pg.connection.PgConnection;
2828
import io.github.mfvanek.pg.connection.PgConnectionImpl;
29+
import io.github.mfvanek.pg.connection.PgHost;
2930
import io.github.mfvanek.pg.connection.PgHostImpl;
31+
import io.github.mfvanek.pg.connection.PgSqlException;
3032
import io.github.mfvanek.pg.settings.maintenance.ConfigurationMaintenanceOnHost;
3133
import io.github.mfvanek.pg.settings.maintenance.ConfigurationMaintenanceOnHostImpl;
3234
import io.github.mfvanek.pg.statistics.maintenance.StatisticsMaintenanceOnHost;
@@ -43,6 +45,9 @@
4345
import org.springframework.context.annotation.Bean;
4446
import org.springframework.context.annotation.Configuration;
4547

48+
import java.sql.Connection;
49+
import java.sql.SQLException;
50+
import java.util.Objects;
4651
import javax.sql.DataSource;
4752

4853
/**
@@ -64,9 +69,18 @@ public class DatabaseStructureHealthAutoConfiguration {
6469
@ConditionalOnBean(name = "dataSource")
6570
@ConditionalOnMissingBean
6671
public PgConnection pgConnection(@Qualifier("dataSource") final DataSource dataSource,
67-
@Value("${spring.datasource.url:jdbc:postgresql://localhost:5432}") final String databaseUrl) {
68-
// TODO Try to obtain URL from dataSource.getConnection().getMetaData().getURL()
69-
return PgConnectionImpl.of(dataSource, PgHostImpl.ofUrl(databaseUrl));
72+
@Value("${spring.datasource.url:#{null}}") final String databaseUrl) {
73+
final PgHost host;
74+
if (Objects.isNull(databaseUrl) || databaseUrl.isBlank()) {
75+
try (Connection connection = dataSource.getConnection()) {
76+
host = PgHostImpl.ofUrl(connection.getMetaData().getURL());
77+
} catch (SQLException ex) {
78+
throw new PgSqlException(ex);
79+
}
80+
} else {
81+
host = PgHostImpl.ofUrl(databaseUrl);
82+
}
83+
return PgConnectionImpl.of(dataSource, host);
7084
}
7185

7286
@Bean

src/test/java/io/github/mfvanek/pg/spring/AutoConfigurationTestBase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ abstract class AutoConfigurationTestBase {
4949
"configurationMaintenanceOnHost"
5050
);
5151
protected static final Class<?>[] EXPECTED_TYPES = {PgConnection.class, DatabaseCheckOnHost.class, StatisticsMaintenanceOnHost.class, ConfigurationMaintenanceOnHost.class};
52+
protected static final DataSource DATA_SOURCE_MOCK = Mockito.mock(DataSource.class);
5253

53-
protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
5454
protected final Predicate<String> beanNamesFilter = b -> !b.startsWith("org.springframework") && !b.startsWith("pg.index.health.test") &&
5555
!b.endsWith("AutoConfiguration") && !"dataSource".equals(b);
56+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
5657

5758
@Nonnull
5859
protected ApplicationContextRunner assertWithTestConfig() {
@@ -61,7 +62,7 @@ protected ApplicationContextRunner assertWithTestConfig() {
6162

6263
protected static <C extends ConfigurableApplicationContext> void initialize(@Nonnull final C applicationContext) {
6364
final GenericApplicationContext context = (GenericApplicationContext) applicationContext;
64-
context.registerBean("dataSource", DataSource.class, () -> Mockito.mock(DataSource.class));
65+
context.registerBean("dataSource", DataSource.class, () -> DATA_SOURCE_MOCK);
6566
}
6667

6768
@Nonnull

src/test/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfigurationFilteringTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void withoutPgConnectionClass() {
7070
ConfigurationMaintenanceOnHost.class})
7171
void withoutClass(final Class<?> type) {
7272
assertWithTestConfig()
73+
.withPropertyValues("spring.datasource.url=jdbc:postgresql://localhost:5432")
7374
.withInitializer(AutoConfigurationTestBase::initialize)
7475
.withClassLoader(new FilteredClassLoader(type))
7576
.run(context -> assertThat(context)

src/test/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfigurationTest.java

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,20 @@
99

1010
package io.github.mfvanek.pg.spring;
1111

12+
import io.github.mfvanek.pg.connection.PgConnection;
13+
import io.github.mfvanek.pg.connection.PgHostImpl;
1214
import org.junit.jupiter.api.Test;
15+
import org.mockito.Mockito;
16+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
17+
import org.springframework.context.ConfigurableApplicationContext;
18+
19+
import java.sql.Connection;
20+
import java.sql.DatabaseMetaData;
21+
import java.sql.SQLException;
22+
import javax.annotation.Nonnull;
1323

1424
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1526

1627
class DatabaseStructureHealthAutoConfigurationTest extends AutoConfigurationTestBase {
1728

@@ -27,16 +38,45 @@ void withoutDataSource() {
2738
@Test
2839
void withDataSource() {
2940
assertWithTestConfig()
41+
.withPropertyValues("spring.datasource.url=jdbc:postgresql://localhost:5432")
3042
.withInitializer(AutoConfigurationTestBase::initialize)
3143
.run(context -> {
32-
assertThat(context.getBeanDefinitionNames())
33-
.filteredOn(beanNamesFilter)
34-
.hasSameSizeAs(EXPECTED_BEANS)
35-
.containsAll(EXPECTED_BEANS);
44+
assertThatBeansPresent(context);
3645
assertThatBeansAreNotNullBean(context);
3746
});
3847
}
3948

49+
@Test
50+
void withDataSourceButWithoutConnectionString() throws SQLException {
51+
try (Connection connectionMock = Mockito.mock(Connection.class)) {
52+
setMocks(connectionMock);
53+
54+
assertWithTestConfig()
55+
.withInitializer(AutoConfigurationTestBase::initialize)
56+
.run(context -> {
57+
assertThatBeansPresent(context);
58+
assertThatBeansAreNotNullBean(context);
59+
assertThatPgConnectionIsValid(context);
60+
});
61+
}
62+
}
63+
64+
@Test
65+
void withDataSourceAndEmptyConnectionString() throws SQLException {
66+
try (Connection connectionMock = Mockito.mock(Connection.class)) {
67+
setMocks(connectionMock);
68+
69+
assertWithTestConfig()
70+
.withPropertyValues("spring.datasource.url=")
71+
.withInitializer(AutoConfigurationTestBase::initialize)
72+
.run(context -> {
73+
assertThatBeansPresent(context);
74+
assertThatBeansAreNotNullBean(context);
75+
assertThatPgConnectionIsValid(context);
76+
});
77+
}
78+
}
79+
4080
@Test
4181
void shouldNotCreateAutoConfigurationWithDisabledProperty() {
4282
assertWithTestConfig()
@@ -51,15 +91,55 @@ void shouldNotCreateAutoConfigurationWithDisabledProperty() {
5191
@Test
5292
void shouldCreateAutoConfigurationWhenPropertyExplicitlySet() {
5393
assertWithTestConfig()
54-
.withPropertyValues("pg.index.health.test.enabled=true")
94+
.withPropertyValues("pg.index.health.test.enabled=true",
95+
"spring.datasource.url=jdbc:postgresql://localhost:5432")
5596
.withInitializer(AutoConfigurationTestBase::initialize)
5697
.run(context -> {
57-
assertThat(context.getBeanDefinitionNames())
58-
.isNotEmpty()
59-
.filteredOn(beanNamesFilter)
60-
.hasSameSizeAs(EXPECTED_BEANS)
61-
.containsAll(EXPECTED_BEANS);
98+
assertThatBeansPresent(context);
6299
assertThatBeansAreNotNullBean(context);
63100
});
64101
}
102+
103+
@Test
104+
void withDataSourceAndExceptionWhileObtainingUrlFromMetadata() throws SQLException {
105+
try (Connection connectionMock = Mockito.mock(Connection.class)) {
106+
Mockito.when(DATA_SOURCE_MOCK.getConnection())
107+
.thenReturn(connectionMock);
108+
Mockito.when(connectionMock.getMetaData())
109+
.thenThrow(SQLException.class);
110+
111+
final ApplicationContextRunner contextRunner = assertWithTestConfig()
112+
.withInitializer(AutoConfigurationTestBase::initialize);
113+
assertThatThrownBy(() -> contextRunner.run(this::assertThatPgConnectionIsValid))
114+
.isInstanceOf(IllegalStateException.class)
115+
.hasMessage("Unstarted application context org.springframework.boot.test.context.assertj.AssertableApplicationContext[" +
116+
"startupFailure=org.springframework.beans.factory.BeanCreationException] failed to start")
117+
.hasStackTraceContaining("Factory method 'pgConnection' threw exception; nested exception is io.github.mfvanek.pg.connection.PgSqlException");
118+
}
119+
}
120+
121+
private void assertThatBeansPresent(@Nonnull final ConfigurableApplicationContext context) {
122+
assertThat(context.getBeanDefinitionNames())
123+
.isNotEmpty()
124+
.filteredOn(beanNamesFilter)
125+
.hasSameSizeAs(EXPECTED_BEANS)
126+
.containsAll(EXPECTED_BEANS);
127+
}
128+
129+
private void assertThatPgConnectionIsValid(@Nonnull final ConfigurableApplicationContext context) {
130+
assertThat(context.getBean("pgConnection", PgConnection.class))
131+
.isNotNull()
132+
.satisfies(c -> assertThat(c.getHost())
133+
.isEqualTo(PgHostImpl.ofUrl("jdbc:postgresql://192.168.1.1:6432")));
134+
}
135+
136+
private void setMocks(@Nonnull final Connection connectionMock) throws SQLException {
137+
Mockito.when(DATA_SOURCE_MOCK.getConnection())
138+
.thenReturn(connectionMock);
139+
final DatabaseMetaData metaDataMock = Mockito.mock(DatabaseMetaData.class);
140+
Mockito.when(connectionMock.getMetaData())
141+
.thenReturn(metaDataMock);
142+
Mockito.when(metaDataMock.getURL())
143+
.thenReturn("jdbc:postgresql://192.168.1.1:6432");
144+
}
65145
}

0 commit comments

Comments
 (0)