diff --git a/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/PgConnectionImpl.java b/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/PgConnectionImpl.java
index 5cb2cf45..39930238 100644
--- a/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/PgConnectionImpl.java
+++ b/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/PgConnectionImpl.java
@@ -10,10 +10,16 @@
package io.github.mfvanek.pg.connection;
+import io.github.mfvanek.pg.connection.exception.PgSqlException;
import io.github.mfvanek.pg.connection.host.PgHost;
+import io.github.mfvanek.pg.connection.host.PgHostImpl;
+import io.github.mfvanek.pg.connection.host.PgUrlParser;
+import java.sql.Connection;
+import java.sql.SQLException;
import java.util.Objects;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.sql.DataSource;
/**
@@ -28,7 +34,7 @@ public class PgConnectionImpl implements PgConnection {
private final PgHost host;
private PgConnectionImpl(@Nonnull final DataSource dataSource, @Nonnull final PgHost host) {
- this.dataSource = Objects.requireNonNull(dataSource, "dataSource cannot be null");
+ this.dataSource = validateDataSource(dataSource);
this.host = Objects.requireNonNull(host, "host cannot be null");
}
@@ -50,20 +56,6 @@ public PgHost getHost() {
return host;
}
- /**
- * Constructs a {@code PgConnection} object with given dataSource and host.
- *
- * @param dataSource a factory for connections to the physical database
- * @param host information about database host
- * @return {@code PgConnection}
- * @see DataSource
- * @see PgHost
- */
- @Nonnull
- public static PgConnection of(@Nonnull final DataSource dataSource, @Nonnull final PgHost host) {
- return new PgConnectionImpl(dataSource, host);
- }
-
/**
* {@inheritDoc}
*/
@@ -99,4 +91,54 @@ public String toString() {
"host=" + host +
'}';
}
+
+ /**
+ * Constructs a {@code PgConnection} object with given dataSource and host.
+ *
+ * @param dataSource a factory for connections to the physical database; should be non-null.
+ * @param host information about database host; should be non-null.
+ * @return {@code PgConnection}
+ * @see DataSource
+ * @see PgHost
+ */
+ @Nonnull
+ public static PgConnection of(@Nonnull final DataSource dataSource, @Nonnull final PgHost host) {
+ return new PgConnectionImpl(dataSource, host);
+ }
+
+ /**
+ * Constructs a {@code PgConnection} object with given dataSource and connection string.
+ *
+ * @param dataSource a factory for connections to the physical database; should be non-null.
+ * @param databaseUrl a connection string to the physical database; can be obtained from connection metadata.
+ * @return {@code PgConnection} object
+ * @see Connection#getMetaData()
+ * @see java.sql.DatabaseMetaData
+ * @since 0.14.2
+ */
+ @Nonnull
+ public static PgConnection ofUrl(@Nonnull final DataSource dataSource, @Nullable final String databaseUrl) {
+ final PgHost host;
+ if (needToGetUrlFromMetaData(databaseUrl)) {
+ try (Connection connection = validateDataSource(dataSource).getConnection()) {
+ host = PgHostImpl.ofUrl(connection.getMetaData().getURL());
+ } catch (SQLException ex) {
+ throw new PgSqlException(ex);
+ }
+ } else {
+ host = PgHostImpl.ofUrl(databaseUrl);
+ }
+ return new PgConnectionImpl(dataSource, host);
+ }
+
+ @Nonnull
+ private static DataSource validateDataSource(@Nonnull final DataSource dataSource) {
+ return Objects.requireNonNull(dataSource, "dataSource cannot be null");
+ }
+
+ private static boolean needToGetUrlFromMetaData(@Nullable final String databaseUrl) {
+ return databaseUrl == null ||
+ databaseUrl.isBlank() ||
+ databaseUrl.startsWith(PgUrlParser.TESTCONTAINERS_PG_URL_PREFIX);
+ }
}
diff --git a/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/host/PgUrlParser.java b/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/host/PgUrlParser.java
index 2244238d..42182667 100644
--- a/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/host/PgUrlParser.java
+++ b/pg-index-health-jdbc-connection/src/main/java/io/github/mfvanek/pg/connection/host/PgUrlParser.java
@@ -31,6 +31,19 @@ public final class PgUrlParser {
*/
public static final String URL_HEADER = "jdbc:postgresql://";
+ /**
+ * The URL prefix used in Testcontainers to initialize PostgreSQL containers.
+ *
+ * Testcontainers provides a special JDBC URL format that allows for on-the-fly creation and management
+ * of PostgreSQL database containers during tests. This prefix is part of the JDBC URL and signals
+ * Testcontainers to handle the lifecycle of the container automatically.
+ *
+ *
+ * @see Testcontainers JDBC Support
+ * @since 0.14.2
+ */
+ public static final String TESTCONTAINERS_PG_URL_PREFIX = "jdbc:tc:postgresql:";
+
private static final Map DEFAULT_URL_PARAMETERS = Map.ofEntries(
Map.entry("targetServerType", "primary"),
Map.entry("hostRecheckSeconds", "2"),
diff --git a/pg-index-health-jdbc-connection/src/test/java/io/github/mfvanek/pg/connection/PgConnectionImplTest.java b/pg-index-health-jdbc-connection/src/test/java/io/github/mfvanek/pg/connection/PgConnectionImplTest.java
index 65a5b5c2..6b59a037 100644
--- a/pg-index-health-jdbc-connection/src/test/java/io/github/mfvanek/pg/connection/PgConnectionImplTest.java
+++ b/pg-index-health-jdbc-connection/src/test/java/io/github/mfvanek/pg/connection/PgConnectionImplTest.java
@@ -10,6 +10,7 @@
package io.github.mfvanek.pg.connection;
+import io.github.mfvanek.pg.connection.exception.PgSqlException;
import io.github.mfvanek.pg.connection.host.PgHost;
import io.github.mfvanek.pg.connection.host.PgHostImpl;
import io.github.mfvanek.pg.connection.support.DatabaseAwareTestBase;
@@ -17,6 +18,8 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
+import java.sql.Connection;
+import java.sql.SQLException;
import java.util.Locale;
import javax.sql.DataSource;
@@ -46,10 +49,18 @@ void isPrimaryForAnyHost() {
@SuppressWarnings("ConstantConditions")
@Test
void withInvalidArguments() {
+ assertThatThrownBy(() -> PgConnectionImpl.of(null, null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessage("dataSource cannot be null");
+
final DataSource dataSource = getDataSource();
assertThatThrownBy(() -> PgConnectionImpl.of(dataSource, null))
.isInstanceOf(NullPointerException.class)
.hasMessage("host cannot be null");
+
+ assertThatThrownBy(() -> PgConnectionImpl.ofUrl(null, null))
+ .isInstanceOf(NullPointerException.class)
+ .hasMessage("dataSource cannot be null");
}
@SuppressWarnings("ConstantConditions")
@@ -57,7 +68,7 @@ void withInvalidArguments() {
void equalsAndHashCode() {
final PgHost host = PgHostImpl.ofUrl("jdbc:postgresql://first:6432");
final PgConnection first = PgConnectionImpl.of(getDataSource(), host);
- final PgConnection theSame = PgConnectionImpl.of(getDataSource(), host);
+ final PgConnection theSame = PgConnectionImpl.ofUrl(getDataSource(), "jdbc:postgresql://first:6432");
final PgConnection second = PgConnectionImpl.of(getDataSource(), PgHostImpl.ofUrl("jdbc:postgresql://second:5432"));
assertThat(first.equals(null)).isFalse();
@@ -107,4 +118,38 @@ void twoConnectionsDifferentSameHostWithDifferentPortsConsideredNotEqual() {
assertThat(firstPgConnection).isNotEqualTo(secondPgConnection);
}
+
+ @Test
+ void shouldGetUrlFromConnectionMetadata() {
+ final PgConnection first = PgConnectionImpl.ofUrl(getDataSource(), null);
+ assertThat(first.getHost())
+ .isNotNull()
+ .isEqualTo(getHost());
+
+ final PgConnection second = PgConnectionImpl.ofUrl(getDataSource(), " ");
+ assertThat(second.getHost())
+ .isNotNull()
+ .isEqualTo(getHost());
+
+ final PgConnection third = PgConnectionImpl.ofUrl(getDataSource(), "jdbc:tc:postgresql:17.2:///demo");
+ assertThat(third.getHost())
+ .isNotNull()
+ .isEqualTo(getHost());
+ }
+
+ @Test
+ void withExceptionWhileObtainingUrlFromMetadata() throws SQLException {
+ final DataSource dataSourceMock = Mockito.mock(DataSource.class);
+ try (Connection connectionMock = Mockito.mock(Connection.class)) {
+ Mockito.when(dataSourceMock.getConnection())
+ .thenReturn(connectionMock);
+ Mockito.when(connectionMock.getMetaData())
+ .thenThrow(new SQLException("Unable to obtain connection from metadata"));
+
+ assertThatThrownBy(() -> PgConnectionImpl.ofUrl(dataSourceMock, null))
+ .isInstanceOf(PgSqlException.class)
+ .hasMessage("Unable to obtain connection from metadata")
+ .hasCauseInstanceOf(SQLException.class);
+ }
+ }
}
diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/units/MemoryUnit.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/units/MemoryUnit.java
index b4ee450e..2b56e9a8 100644
--- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/units/MemoryUnit.java
+++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/units/MemoryUnit.java
@@ -20,8 +20,19 @@
*/
public enum MemoryUnit {
+ /**
+ * Represents a kilobyte, equivalent to 1,024 bytes.
+ */
KB(1024L, "kilobyte"),
+
+ /**
+ * Represents a megabyte, equivalent to 1,024 kilobytes or 1,048,576 bytes.
+ */
MB(1024L * 1024L, "megabyte"),
+
+ /**
+ * Represents a gigabyte, equivalent to 1,024 megabytes or 1,073,741,824 bytes.
+ */
GB(1024L * 1024L * 1024L, "gigabyte");
private final long dimension;
diff --git a/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfiguration.java b/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfiguration.java
index db099501..85bc166c 100644
--- a/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfiguration.java
+++ b/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthAutoConfiguration.java
@@ -12,9 +12,6 @@
import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.connection.PgConnectionImpl;
-import io.github.mfvanek.pg.connection.exception.PgSqlException;
-import io.github.mfvanek.pg.connection.host.PgHost;
-import io.github.mfvanek.pg.connection.host.PgHostImpl;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -27,10 +24,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Objects;
-import javax.annotation.Nullable;
import javax.sql.DataSource;
/**
@@ -58,22 +51,6 @@ public class DatabaseStructureHealthAutoConfiguration {
@ConditionalOnMissingBean
public PgConnection pgConnection(@Qualifier("dataSource") final DataSource dataSource,
@Value("${spring.datasource.url:#{null}}") final String databaseUrl) {
- final PgHost host;
- if (needToGetUrlFromMetaData(databaseUrl)) {
- try (Connection connection = dataSource.getConnection()) {
- host = PgHostImpl.ofUrl(connection.getMetaData().getURL());
- } catch (SQLException ex) {
- throw new PgSqlException(ex);
- }
- } else {
- host = PgHostImpl.ofUrl(databaseUrl);
- }
- return PgConnectionImpl.of(dataSource, host);
- }
-
- private static boolean needToGetUrlFromMetaData(@Nullable final String databaseUrl) {
- return Objects.isNull(databaseUrl) ||
- databaseUrl.isBlank() ||
- databaseUrl.startsWith(DatabaseStructureHealthCondition.TESTCONTAINERS_PG_URL_PREFIX);
+ return PgConnectionImpl.ofUrl(dataSource, databaseUrl);
}
}
diff --git a/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthCondition.java b/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthCondition.java
index 3e812ed9..09877d20 100644
--- a/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthCondition.java
+++ b/spring-boot-integration/pg-index-health-test-starter/src/main/java/io/github/mfvanek/pg/spring/DatabaseStructureHealthCondition.java
@@ -10,6 +10,7 @@
package io.github.mfvanek.pg.spring;
+import io.github.mfvanek.pg.connection.host.PgUrlParser;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
@@ -24,19 +25,6 @@
*/
public class DatabaseStructureHealthCondition extends SpringBootCondition {
- /**
- * The URL prefix used in Testcontainers to initialize PostgreSQL containers.
- *
- * Testcontainers provides a special JDBC URL format that allows for on-the-fly creation and management
- * of PostgreSQL database containers during tests. This prefix is part of the JDBC URL and signals
- * Testcontainers to handle the lifecycle of the container automatically.
- *
- *
- * @see Testcontainers JDBC Support
- */
- static final String TESTCONTAINERS_PG_URL_PREFIX = "jdbc:tc:postgresql:";
-
- private static final String ORIGINAL_PG_URL_PREFIX = "jdbc:postgresql://";
private static final String PROPERTY_NAME = "spring.datasource.url";
/**
@@ -47,7 +35,7 @@ public ConditionOutcome getMatchOutcome(final ConditionContext context, final An
final ConditionMessage.Builder message = ConditionMessage.forCondition("pg.index.health.test PostgreSQL condition");
final String jdbcUrl = getJdbcUrl(context);
if (jdbcUrl != null && !jdbcUrl.isBlank()) {
- if (jdbcUrl.startsWith(ORIGINAL_PG_URL_PREFIX) || jdbcUrl.startsWith(TESTCONTAINERS_PG_URL_PREFIX)) {
+ if (jdbcUrl.startsWith(PgUrlParser.URL_HEADER) || jdbcUrl.startsWith(PgUrlParser.TESTCONTAINERS_PG_URL_PREFIX)) {
return ConditionOutcome.match(message.foundExactly("found PostgreSQL connection " + jdbcUrl));
}
return ConditionOutcome.noMatch(message.notAvailable("not PostgreSQL connection"));