From 824a99877289e3aece0909f246e6f2b4bd73eb1e Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Tue, 9 Apr 2024 10:24:04 -0700 Subject: [PATCH] Addresses a shortcoming of the Universal Connection Pool pool creation logic in certain test scenarios Signed-off-by: Laird Nelson --- .../ucp/cdi/UCPBackedDataSourceExtension.java | 26 ++++++++++++++++--- .../datasource/ucp/cdi/TestUcpApi.java | 24 +++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java index 03d40623d11..c3b4e3fa19e 100644 --- a/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java +++ b/integrations/cdi/datasource-ucp/src/main/java/io/helidon/integrations/datasource/ucp/cdi/UCPBackedDataSourceExtension.java @@ -40,6 +40,8 @@ import jakarta.enterprise.inject.spi.configurator.BeanConfigurator; import jakarta.enterprise.util.TypeLiteral; import jakarta.inject.Named; +import oracle.ucp.UniversalConnectionPoolException; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; import oracle.ucp.jdbc.PoolDataSourceImpl; @@ -141,7 +143,10 @@ protected final void addBean(BeanConfigurator beanConfigurator, .produceWith(instance -> { try { return createDataSource(instance, dataSourceName, xa, dataSourceProperties); - } catch (IntrospectionException | ReflectiveOperationException | SQLException exception) { + } catch (IntrospectionException + | ReflectiveOperationException + | SQLException + | UniversalConnectionPoolException exception) { throw new CreationException(exception.getMessage(), exception); } }) @@ -162,7 +167,7 @@ private static PoolDataSource createDataSource(Instance instance, Named dataSourceName, boolean xa, Properties properties) - throws IntrospectionException, ReflectiveOperationException, SQLException { + throws IntrospectionException, ReflectiveOperationException, SQLException, UniversalConnectionPoolException { // See // https://docs.oracle.com/en/database/oracle/oracle-database/19/jjucp/get-started.html#GUID-2CC8D6EC-483F-4942-88BA-C0A1A1B68226 // for the general pattern. @@ -269,7 +274,22 @@ private static PoolDataSource createDataSource(Instance instance, } } if (returnValue.getConnectionPoolName() == null) { - returnValue.setConnectionPoolName(dataSourceName.value()); + String proposedConnectionPoolName = dataSourceName.value(); + String[] existingConnectionPoolNames = + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager().getConnectionPoolNames(); + for (String existingConnectionPoolName : existingConnectionPoolNames) { + if (proposedConnectionPoolName.equals(existingConnectionPoolName)) { + // If the return value of an invocation of PoolDataSource#getConnectionPoolName() equals the name of + // an already existing UniversalConnectionPool instance, the first invocation of + // PoolDataSource#getConnection(), or any other operation that requires pool creation, will throw a + // SQLException (!). In this case only we let the auto-generated name (!) be used instead. + proposedConnectionPoolName = null; + break; + } + } + if (proposedConnectionPoolName != null) { + returnValue.setConnectionPoolName(proposedConnectionPoolName); + } } Instance sslContextInstance = instance.select(SSLContext.class, dataSourceName); if (!sslContextInstance.isUnsatisfied()) { diff --git a/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestUcpApi.java b/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestUcpApi.java index 81e8b809e6e..0e701580e3e 100644 --- a/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestUcpApi.java +++ b/integrations/cdi/datasource-ucp/src/test/java/io/helidon/integrations/datasource/ucp/cdi/TestUcpApi.java @@ -15,6 +15,7 @@ */ package io.helidon.integrations.datasource.ucp.cdi; +import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; @@ -77,6 +78,29 @@ void testCreateConnectionPoolFailsWithoutSufficientInformation() throws SQLExcep () -> ucpManager.createConnectionPool((UniversalConnectionPoolAdapter) getPoolDataSource())); } + @Test + void testTwoPoolDataSourceCreationsWithSameName() throws SQLException, UniversalConnectionPoolException { + PoolDataSource pds = getPoolDataSource(); + pds.setConnectionFactoryClassName("org.h2.jdbcx.JdbcDataSource"); + pds.setURL("jdbc:h2:mem:test"); + pds.setConnectionPoolName("pool0"); + try (Connection c = pds.getConnection()) { // creates the pool + } + UniversalConnectionPoolManager ucpManager = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager(); + String[] names = ucpManager.getConnectionPoolNames(); + assertThat("Connection pool names: " + java.util.Arrays.asList(names), names.length, is(1)); + assertThat(names[0], is("pool0")); + pds = getPoolDataSource(); // new one + pds.setConnectionFactoryClassName("org.h2.jdbcx.JdbcDataSource"); + pds.setURL("jdbc:h2:mem:test"); + pds.setConnectionPoolName("pool0"); + assertThrows(SQLException.class, pds::getConnection); // !! + pds.setConnectionPoolName(null); + try (Connection c = pds.getConnection()) { + } + assertThat(pds.getConnectionPoolName(), is(not(nullValue()))); // !! + } + @Test void testCreateConnectionPoolManuallyWithoutName() throws SQLException, UniversalConnectionPoolException { PoolDataSource pds = getPoolDataSource();