diff --git a/gradle/databases.gradle b/gradle/databases.gradle index 8054134659..e0ccb006ff 100644 --- a/gradle/databases.gradle +++ b/gradle/databases.gradle @@ -20,6 +20,8 @@ ext { 'jdbc.user' : 'sa', 'jdbc.pass' : '', 'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE', + 'jdbc.datasource' : 'org.h2.Driver', +// 'jdbc.datasource' : 'org.h2.jdbcx.JdbcDataSource', 'connection.init_sql' : '', 'hibernate.dialect.native_param_markers' : 'true' ], @@ -29,6 +31,8 @@ ext { 'jdbc.user' : 'sa', 'jdbc.pass' : '', 'jdbc.url' : 'jdbc:hsqldb:mem:test', + 'jdbc.datasource' : 'org.hsqldb.jdbc.JDBCDriver', +// 'jdbc.datasource' : 'org.hsqldb.jdbc.JDBCDataSource', 'connection.init_sql' : '' ], derby : [ @@ -37,6 +41,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:derby:memory:;databaseName=hibernate_orm_test;create=true', + 'jdbc.datasource' : 'org.apache.derby.jdbc.EmbeddedDriver', +// 'jdbc.datasource' : 'org.apache.derby.jdbc.EmbeddedDataSource', 'connection.init_sql' : '' ], derby_old : [ @@ -45,6 +51,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:derby:memory:;databaseName=hibernate_orm_test;create=true', + 'jdbc.datasource' : 'org.apache.derby.jdbc.EmbeddedDriver', +// 'jdbc.datasource' : 'org.apache.derby.jdbc.EmbeddedDataSource', 'connection.init_sql' : '' ], pgsql : [ @@ -54,6 +62,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', + 'jdbc.datasource' : 'org.postgresql.Driver', +// 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], pgsql_ci : [ @@ -63,6 +73,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', + 'jdbc.datasource' : 'org.postgresql.Driver', +// 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], edb_ci : [ @@ -72,6 +84,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', + 'jdbc.datasource' : 'org.postgresql.Driver', +// 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], sybase_ci : [ @@ -81,6 +95,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching to avoid issues with changing schemas 'jdbc.url' : 'jdbc:jtds:sybase://' + dbHost + ':9000/hibernate_orm_test;maxStatements=0;cacheMetaData=false', + 'jdbc.datasource' : 'net.sourceforge.jtds.jdbc.Driver', +// 'jdbc.datasource' : 'net.sourceforge.jtds.jdbcx.JtdsDataSource', 'connection.init_sql' : 'set ansinull on' ], sybase_jconn_ci : [ @@ -90,6 +106,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // Disable prepared statement caching to avoid issues with changing schemas 'jdbc.url' : 'jdbc:sybase:Tds:' + dbHost + ':9000/hibernate_orm_test', + 'jdbc.datasource' : 'com.sybase.jdbc4.jdbc.SybDriver', +// 'jdbc.datasource' : 'com.sybase.jdbc4.jdbc.SybDataSource', 'connection.init_sql' : 'set ansinull on set quoted_identifier on' ], mysql : [ @@ -98,6 +116,8 @@ ext { 'jdbc.user' : 'hibernateormtest', 'jdbc.pass' : 'hibernateormtest', 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test', + 'jdbc.datasource' : 'com.mysql.jdbc.Driver', +// 'jdbc.datasource' : 'com.mysql.cj.jdbc.MysqlDataSource', 'connection.init_sql' : '' ], mysql_ci : [ @@ -106,6 +126,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:mysql://' + dbHost + '/hibernate_orm_test?allowPublicKeyRetrieval=true', + 'jdbc.datasource' : 'com.mysql.jdbc.Driver', +// 'jdbc.datasource' : 'com.mysql.cj.jdbc.MysqlDataSource', 'connection.init_sql' : '' ], mariadb : [ @@ -114,6 +136,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test', + 'jdbc.datasource' : 'org.mariadb.jdbc.Driver', +// 'jdbc.datasource' : 'org.mariadb.jdbc.MariaDbDataSource', 'connection.init_sql' : '' ], mariadb_ci : [ @@ -122,6 +146,8 @@ ext { 'jdbc.user' : 'root', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:mariadb://' + dbHost + '/hibernate_orm_test', + 'jdbc.datasource' : 'org.mariadb.jdbc.Driver', +// 'jdbc.datasource' : 'org.mariadb.jdbc.MariaDbDataSource', 'connection.init_sql' : '' ], tidb : [ @@ -130,6 +156,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:mysql://' + dbHost + ':4000/hibernate_orm_test', + 'jdbc.datasource' : 'com.mysql.jdbc.Driver', +// 'jdbc.datasource' : 'com.mysql.cj.jdbc.MysqlDataSource', 'connection.init_sql' : '' ], oracle : [ @@ -138,6 +166,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/xepdb1', + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_ci : [ @@ -146,6 +176,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/freepdb1', + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_xe_ci : [ @@ -154,6 +186,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/xepdb1', + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_legacy_ci : [ @@ -163,6 +197,8 @@ ext { 'jdbc.pass' : 'hibernate_orm_test', // For 11 version that doesn't have any XEPDB1 database service 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE', + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_cloud_autonomous_tls : [ @@ -175,6 +211,8 @@ ext { // To avoid hibernate-spatial tests failure, JVM must be enabled as stated in documentation: // https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/autonomous-oracle-java.html 'jdbc.url' : 'jdbc:oracle:thin:@(description=(retry_count=5)(retry_delay=1)(address=(protocol=tcps)(port=1521)(host=' + dbHost + '.oraclecloud.com))(connect_data=(service_name=' + dbService + '_tp.adb.oraclecloud.com))(security=(ssl_server_dn_match=no)))?oracle.jdbc.enableQueryResultCache=false&oracle.jdbc.thinForceDNSLoadBalancing=true&tcp.nodelay=yes', + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_cloud_db19c : [ @@ -187,6 +225,8 @@ ext { // To avoid hibernate-spatial tests failure, JVM must be enabled as stated in documentation: // https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/autonomous-oracle-java.html 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/' + dbService, + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_cloud_db21c : [ @@ -199,6 +239,8 @@ ext { // To avoid hibernate-spatial tests failure, JVM must be enabled as stated in documentation: // https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/autonomous-oracle-java.html 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/' + dbService, + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], oracle_cloud_db23c : [ @@ -211,6 +253,8 @@ ext { // To avoid hibernate-spatial tests failure, JVM must be enabled as stated in documentation: // https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/autonomous-oracle-java.html 'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521/' + dbService, + 'jdbc.datasource' : 'oracle.jdbc.OracleDriver', +// 'jdbc.datasource' : 'oracle.jdbc.datasource.impl.OracleDataSource', 'connection.init_sql' : '' ], mssql : [ @@ -219,6 +263,8 @@ ext { 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';instance=SQLEXPRESS;databaseName=hibernate_orm_test;trustServerCertificate=true', + 'jdbc.datasource' : 'com.microsoft.sqlserver.jdbc.SQLServerDriver', +// 'jdbc.datasource' : 'com.microsoft.sqlserver.jdbc.SQLServerDataSource', 'connection.init_sql' : '' ], mssql_ci : [ @@ -227,6 +273,8 @@ ext { 'jdbc.user' : 'sa', 'jdbc.pass' : 'Hibernate_orm_test', 'jdbc.url' : 'jdbc:sqlserver://' + dbHost + ';databaseName=hibernate_orm_test;sendTimeAsDatetime=false;trustServerCertificate=true', + 'jdbc.datasource' : 'com.microsoft.sqlserver.jdbc.SQLServerDriver', +// 'jdbc.datasource' : 'com.microsoft.sqlserver.jdbc.SQLServerDataSource', 'connection.init_sql' : '' ], informix : [ @@ -235,6 +283,8 @@ ext { 'jdbc.user' : 'informix', 'jdbc.pass' : 'in4mix', 'jdbc.url' : 'jdbc:informix-sqli://' + dbHost + ':9088/sysuser:INFORMIXSERVER=dev;user=informix;password=in4mix', + 'jdbc.datasource' : 'com.informix.jdbc.IfxDriver', +// 'jdbc.datasource' : 'com.informix.jdbcx.IfxDataSource', 'connection.init_sql' : '' ], db2 : [ @@ -243,6 +293,8 @@ ext { 'jdbc.user' : 'db2inst1', 'jdbc.pass' : 'db2inst1-pwd', 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', + 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2Driver', +// 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2SimpleDataSource', 'connection.init_sql' : '' ], db2_ci : [ @@ -251,6 +303,8 @@ ext { 'jdbc.user' : 'orm_test', 'jdbc.pass' : 'orm_test', 'jdbc.url' : 'jdbc:db2://' + dbHost + ':50000/orm_test', + 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2Driver', +// 'jdbc.datasource' : 'com.ibm.db2.jcc.DB2SimpleDataSource', 'connection.init_sql' : '' ], hana_cloud : [ @@ -260,6 +314,8 @@ ext { 'jdbc.pass' : 'H1bernate_test', // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html 'jdbc.url' : 'jdbc:sap://' + dbHost + ':443/?encrypt=true&validateCertificate=false&statementCacheSize=0', + 'jdbc.datasource' : 'com.sap.db.jdbc.Driver', +// 'jdbc.datasource' : 'com.sap.db.jdbcext.HanaDataSource', 'connection.init_sql' : '' ], hana_ci : [ @@ -269,6 +325,8 @@ ext { 'jdbc.pass' : 'H1bernate_test', // Disable prepared statement caching due to https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.04/en-US/78f2163887814223858e4369d18e2847.html 'jdbc.url' : 'jdbc:sap://' + dbHost + ':39017/?statementCacheSize=0', + 'jdbc.datasource' : 'com.sap.db.jdbc.Driver', +// 'jdbc.datasource' : 'com.sap.db.jdbcext.HanaDataSource', 'connection.init_sql' : '' ], cockroachdb : [ @@ -279,6 +337,8 @@ ext { 'jdbc.pass' : '', // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', + 'jdbc.datasource' : 'org.postgresql.Driver', +// 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', 'connection.init_sql' : '' ], firebird : [ @@ -290,6 +350,8 @@ ext { // Expects alias 'hibernate_orm_test' in aliases.conf (FB2.5 and earlier) or databases.conf (FB3.0 and later) // Created database must either use default character set NONE, or UTF8 with page size 16384 or higher (to prevent issues with indexes due to keysize) 'jdbc.url' : 'jdbc:firebirdsql://' + dbHost +'/hibernate_orm_test?charSet=utf-8;TRANSACTION_READ_COMMITTED=read_committed,rec_version,wait,lock_timeout=5', + 'jdbc.datasource' : 'org.firebirdsql.jdbc.FBDriver', +// 'jdbc.datasource' : 'org.firebirdsql.jdbc.FBDataSource', 'connection.init_sql' : '' ], altibase : [ @@ -298,7 +360,8 @@ ext { 'jdbc.user' : 'sys', 'jdbc.pass' : 'manager', 'connection.init_sql' : '', - 'jdbc.url' : 'jdbc:Altibase://' + dbHost + ':20300/mydb?force_clob_bind=true' + 'jdbc.url' : 'jdbc:Altibase://' + dbHost + ':20300/mydb?force_clob_bind=true', + 'jdbc.datasource' : 'Altibase.jdbc.driver.AltibaseDriver' ], ] } diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index c0366105fc..c6141d3907 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -95,6 +95,7 @@ dependencies { //Databases testRuntimeOnly dbLibs.h2 testRuntimeOnly dbLibs.derby + testRuntimeOnly dbLibs.derbyTools testRuntimeOnly dbLibs.hsqldb testRuntimeOnly dbLibs.postgresql testRuntimeOnly dbLibs.mssql diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 6af7b2bcb7..a7bdc502fe 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -152,6 +152,7 @@ task copyBundleResourcesXml (type: Copy) { 'jdbc.user' : dbBundle[db]['jdbc.user'].replace("&", "&"), 'jdbc.pass' : dbBundle[db]['jdbc.pass'].replace("&", "&"), 'jdbc.url' : dbBundle[db]['jdbc.url'].replace("&", "&"), + 'jdbc.datasource' : dbBundle[db]['jdbc.datasource'].replace("&", "&"), 'connection.init_sql' : dbBundle[db]['connection.init_sql'].replace("&", "&") ] ext.bundleTokens['buildDirName'] = project.relativePath( buildDir ) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java index f14c04cb96..d4bce495ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionProviderInitiator.java @@ -67,6 +67,11 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator configValues) { + for ( String key : configValues.keySet() ) { + if ( key.startsWith( "hibernate.oracleucp." ) ) { + return true; + } + } + return false; + } + + private ConnectionProvider instantiateUCPProvider(StrategySelector strategySelector) { + try { + return strategySelector.selectStrategyImplementor( ConnectionProvider.class, UCP_STRATEGY ).newInstance(); + } + catch ( Exception e ) { + LOG.ucpProviderClassNotFound(); + return null; + } + } + /** * Build the connection properties capable of being passed to * {@link java.sql.DriverManager#getConnection(String, Properties)} forms taking {@link Properties} argument. diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index 7a3083b29a..7157f40cac 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1866,4 +1866,8 @@ public interface CoreMessageLogger extends BasicLogger { id = 516) void enhancementDiscoveryFailed(String className, @Cause Throwable cause); + @LogMessage(level = WARN) + @Message(value = "UCP properties were encountered, but the UCP ConnectionProvider was not found on the classpath; these properties are going to be ignored.", + id = 517) + void ucpProviderClassNotFound(); } diff --git a/hibernate-ucp/hibernate-ucp.gradle b/hibernate-ucp/hibernate-ucp.gradle new file mode 100644 index 0000000000..1f99ed287d --- /dev/null +++ b/hibernate-ucp/hibernate-ucp.gradle @@ -0,0 +1,18 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +description = 'Integration for Oracle UCP into Hibernate O/RM' + +apply from: rootProject.file( 'gradle/published-java-module.gradle' ) + +dependencies { + implementation project( ':hibernate-core' ) + implementation libs.ucp + implementation libs.ojdbc11 + + testImplementation project( ':hibernate-testing' ) +} diff --git a/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/StrategyRegistrationProviderImpl.java b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/StrategyRegistrationProviderImpl.java new file mode 100644 index 0000000000..caa05df8d0 --- /dev/null +++ b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/StrategyRegistrationProviderImpl.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.oracleucp.internal; + +import java.util.Collections; +import java.util.List; + +import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl; +import org.hibernate.boot.registry.selector.StrategyRegistration; +import org.hibernate.boot.registry.selector.StrategyRegistrationProvider; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; + +/** + * Provides the {@link UCPCPConnectionProvider} to the + * {@link org.hibernate.boot.registry.selector.spi.StrategySelector} service. + * + */ +public class StrategyRegistrationProviderImpl implements StrategyRegistrationProvider { + private static final List REGISTRATIONS = Collections.singletonList( + (StrategyRegistration) new SimpleStrategyRegistrationImpl( + ConnectionProvider.class, + UCPConnectionProvider.class, + "ucp", + "oracleucp", + UCPConnectionProvider.class.getSimpleName(), + "org.oracle.ucp.connection.UCPConnectionProvider" + ) + ); + + @Override + @SuppressWarnings("unchecked") + public Iterable getStrategyRegistrations() { + return REGISTRATIONS; + } +} + diff --git a/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/UCPConnectionProvider.java b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/UCPConnectionProvider.java new file mode 100644 index 0000000000..a4fb795997 --- /dev/null +++ b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/UCPConnectionProvider.java @@ -0,0 +1,225 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.oracleucp.internal; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.Stoppable; + +import org.jboss.logging.Logger; + +import oracle.ucp.UniversalConnectionPoolAdapter; +import oracle.ucp.UniversalConnectionPoolException; +import oracle.ucp.admin.UniversalConnectionPoolManager; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import org.hibernate.cfg.AvailableSettings; + + +public class UCPConnectionProvider implements ConnectionProvider, Configurable, Stoppable { + + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger( "UCPConnectionProvider.class" ); + private PoolDataSource ucpDS = null; + private UniversalConnectionPoolManager poolManager = null; + private static final String CONFIG_PREFIX = "hibernate.oracleucp."; + private boolean autoCommit; + private Integer isolation; + + @SuppressWarnings("rawtypes") + @Override + public void configure(Map props) throws HibernateException { + try { + LOGGER.trace( "Configuring oracle UCP" ); + + isolation = ConnectionProviderInitiator.extractIsolation( props ); + autoCommit = ConfigurationHelper.getBoolean( AvailableSettings.AUTOCOMMIT, props ); + + UniversalConnectionPoolManager poolManager = UniversalConnectionPoolManagerImpl. + getUniversalConnectionPoolManager(); + ucpDS = PoolDataSourceFactory.getPoolDataSource(); + Properties ucpProps = getConfiguration(props); + configureDataSource(ucpDS, ucpProps); + } + catch (Exception e) { + LOGGER.debug( "oracle UCP Configuration failed" ); + throw new HibernateException( e ); + } + + LOGGER.trace( "oracle UCP Configured" ); + } + + private void configureDataSource(PoolDataSource ucpDS, Properties ucpProps) { + + List methods = Arrays.asList(PoolDataSource.class.getDeclaredMethods()); + + for(String propName : ucpProps.stringPropertyNames()) { + String value = ucpProps.getProperty(propName); + + final String methodName = "set" + propName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propName.substring(1); + Method writeMethod = methods.stream().filter(m -> m.getName().equals(methodName) && m.getParameterCount() == 1).findFirst().orElse(null); + if (writeMethod == null) { + throw new RuntimeException("Property " + propName + " does not exist on target " + PoolDataSource.class); + } + + try { + Class paramClass = writeMethod.getParameterTypes()[0]; + if (paramClass == int.class) { + writeMethod.invoke(ucpDS, Integer.parseInt(value.toString())); + } + else if (paramClass == long.class) { + writeMethod.invoke(ucpDS, Long.parseLong(value.toString())); + } + else if (paramClass == boolean.class || paramClass == Boolean.class) { + writeMethod.invoke(ucpDS, Boolean.parseBoolean(value.toString())); + } + else if (paramClass == String.class) { + writeMethod.invoke(ucpDS, value.toString()); + } + else { + if(propName.equals("connectionProperties") || + propName.equals("connectionFactoryProperties")) { + if (value != null) { + Properties connProps = new Properties(); + + // The Properties string is in the following format: + // {prop1=val1, prop2=val2, ..., propN=valN} + String[] propStrs = value.substring(1, value.length() - 1).split(", "); + for (String onePropStr : propStrs) { + // Separate the name and value strings for each property + String[] nvPair = onePropStr.split("="); + connProps.setProperty(nvPair[0], nvPair[1]); + } + + writeMethod.invoke(ucpDS, connProps); + } + } + else { + writeMethod.invoke(ucpDS, value); + } + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private Properties getConfiguration(Map props) { + Properties ucpProps = new Properties(); + + copyProperty( AvailableSettings.URL, props, "URL", ucpProps ); + copyProperty( AvailableSettings.USER, props, "user", ucpProps ); + copyProperty( AvailableSettings.PASS, props, "password", ucpProps ); + + for ( Object keyo : props.keySet() ) { + if ( !(keyo instanceof String) ) { + continue; + } + String key = (String) keyo; + if ( key.startsWith( CONFIG_PREFIX ) ) { + ucpProps.setProperty( key.substring( CONFIG_PREFIX.length() ), (String) props.get( key ) ); + } + } + + return ucpProps; + } + + @SuppressWarnings("rawtypes") + private static void copyProperty(String srcKey, Map src, String dstKey, Properties dst) { + if ( src.containsKey( srcKey ) ) { + dst.setProperty( dstKey, (String) src.get( srcKey ) ); + } + } + + // ************************************************************************* + // ConnectionProvider + // ************************************************************************* + + @Override + public Connection getConnection() throws SQLException { + Connection conn = null; + if ( ucpDS != null ) { + conn = ucpDS.getConnection(); + if ( isolation != null && isolation != conn.getTransactionIsolation()) { + conn.setTransactionIsolation( isolation ); + } + + if ( conn.getAutoCommit() != autoCommit ) { + conn.setAutoCommit( autoCommit ); + } + } + + return conn; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { + conn.close(); + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @Override + @SuppressWarnings("rawtypes") + public boolean isUnwrappableAs(Class unwrapType) { + return ConnectionProvider.class.equals( unwrapType ) + || UCPConnectionProvider.class.isAssignableFrom( unwrapType ) + || DataSource.class.isAssignableFrom( unwrapType ); + } + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class unwrapType) { + if ( ConnectionProvider.class.equals( unwrapType ) || + UCPConnectionProvider.class.isAssignableFrom( unwrapType ) ) { + return (T) this; + } + else if ( DataSource.class.isAssignableFrom( unwrapType ) ) { + return (T) ucpDS; + } + else { + throw new UnknownUnwrapTypeException( unwrapType ); + } + } + + @Override + public void stop() { + if(this.ucpDS!=null && ucpDS.getConnectionPoolName() != null) { + try { + UniversalConnectionPoolManager poolManager = UniversalConnectionPoolManagerImpl. + getUniversalConnectionPoolManager(); + poolManager.destroyConnectionPool(ucpDS.getConnectionPoolName()); + } + catch (UniversalConnectionPoolException e) { + LOGGER.debug("Unable to destroy UCP connection pool"); + } + } + } + +} + diff --git a/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/package-info.java b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/package-info.java new file mode 100644 index 0000000000..08c9e0957e --- /dev/null +++ b/hibernate-ucp/src/main/java/org/hibernate/ucp/internal/package-info.java @@ -0,0 +1,11 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +/** + * Implementation of ConnectionProvider using UCP. + */ +package org.hibernate.ucp.internal; diff --git a/hibernate-ucp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider b/hibernate-ucp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider new file mode 100644 index 0000000000..2a7dfc6d4e --- /dev/null +++ b/hibernate-ucp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider @@ -0,0 +1,14 @@ +# +# Hibernate, Relational Persistence for Idiomatic Java +# +# License: GNU Lesser General Public License (LGPL), version 2.1 or later. +# See the lgpl.txt file in the root directory or . +# +# +# Hibernate, Relational Persistence for Idiomatic Java +# +# License: GNU Lesser General Public License (LGPL), version 2.1 or later. +# See the lgpl.txt file in the root directory or . +# +org.hibernate.oracleucp.internal.StrategyRegistrationProviderImpl + diff --git a/hibernate-ucp/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/hibernate-ucp/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 0000000000..767a2b6559 --- /dev/null +++ b/hibernate-ucp/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPConnectionProviderTest.java b/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPConnectionProviderTest.java new file mode 100644 index 0000000000..0b3f2c91f0 --- /dev/null +++ b/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPConnectionProviderTest.java @@ -0,0 +1,75 @@ +package org.hibernate.test.ucp; + +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.oracleucp.internal.UCPConnectionProvider; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "The jTDS driver doesn't implement Connection#isValid so this fails") +public class UCPConnectionProviderTest extends BaseCoreFunctionalTestCase { + + @Test + public void testUCPConnectionProvider() throws Exception { + JdbcServices jdbcServices = serviceRegistry().getService( JdbcServices.class ); + ConnectionProviderJdbcConnectionAccess connectionAccess = assertTyping( + ConnectionProviderJdbcConnectionAccess.class, + jdbcServices.getBootstrapJdbcConnectionAccess() + ); + assertTyping( UCPConnectionProvider.class, connectionAccess.getConnectionProvider() ); + + UCPConnectionProvider ucpProvider = (UCPConnectionProvider) connectionAccess.getConnectionProvider(); + final List conns = new ArrayList(); + for ( int i = 0; i < 2; i++ ) { + Connection conn = ucpProvider.getConnection(); + assertNotNull( conn ); + assertFalse( conn.isClosed() ); + conns.add( conn ); + } + + try { + ucpProvider.getConnection(); + fail( "SQLException expected -- no more connections should have been available in the pool." ); + } + catch (SQLException e) { + // expected + assertTrue( e.getMessage().contains( "Failed to get a connection" ) ); + } + + for ( Connection conn : conns ) { + ucpProvider.closeConnection( conn ); + assertTrue( conn.isClosed() ); + } + + releaseSessionFactory(); + + try { + ucpProvider.getConnection(); + fail( "Exception expected -- the pool should have been shutdown." ); + } + catch (Exception e) { + // expected + assertTrue( e.getMessage().contains( "Failed to get a connection" ) ); + } + } +} diff --git a/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPTransactionIsolationConfigTest.java b/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPTransactionIsolationConfigTest.java new file mode 100644 index 0000000000..0b552febd7 --- /dev/null +++ b/hibernate-ucp/src/test/java/org/hibernate/test/ucp/UCPTransactionIsolationConfigTest.java @@ -0,0 +1,17 @@ +package org.hibernate.test.ucp; + +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.dialect.TiDBDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.oracleucp.internal.UCPConnectionProvider; +import org.hibernate.testing.common.connections.BaseTransactionIsolationConfigTest; +import org.hibernate.testing.orm.junit.SkipForDialect; + +@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "The jTDS driver doesn't implement Connection#getNetworkTimeout() so this fails") +@SkipForDialect(dialectClass = TiDBDialect.class, matchSubTypes = true, reason = "Doesn't support SERIALIZABLE isolation") +public class UCPTransactionIsolationConfigTest extends BaseTransactionIsolationConfigTest{ + @Override + protected ConnectionProvider getConnectionProviderUnderTest() { + return new UCPConnectionProvider(); + } +} diff --git a/hibernate-ucp/src/test/resources/hibernate.properties b/hibernate-ucp/src/test/resources/hibernate.properties new file mode 100644 index 0000000000..3d4093ce51 --- /dev/null +++ b/hibernate-ucp/src/test/resources/hibernate.properties @@ -0,0 +1,12 @@ +hibernate.dialect @db.dialect@ +hibernate.connection.driver_class @jdbc.driver@ +hibernate.connection.url @jdbc.url@ +hibernate.connection.username @jdbc.user@ +hibernate.connection.password @jdbc.pass@ +hibernate.connection.init_sql @connection.init_sql@ +hibernate.connection.provider_class=UCPConnectionProvider + +hibernate.oracleucp.connectionFactoryClassName=@jdbc.datasource@ +hibernate.oracleucp.minPoolSize=0 +hibernate.oracleucp.maxPoolSize=2 +hibernate.oracleucp.connectionPoolName=TestUCPPool diff --git a/settings.gradle b/settings.gradle index a2ea73e80b..0b8e99361f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -79,6 +79,7 @@ dependencyResolutionManagement { def hikaricpVersion = version "hikaricp", "3.2.0" def proxoolVersion = version "proxool", "0.8.3" def viburVersion = version "vibur", "25.0" + def ucpVersion = version "ucp", "23.3.0.23.09" def jcacheVersion = version "jcache", "1.0.0" def ehcache3Version = version "ehcache3", "3.10.8" @@ -117,6 +118,10 @@ dependencyResolutionManagement { library( "vibur", "org.vibur", "vibur-dbcp" ).versionRef( viburVersion ) + library( "ucp", "com.oracle.database.jdbc", "ucp11" ).versionRef( ucpVersion ) + + library( "ojdbc11", "com.oracle.database.jdbc", "ojdbc11" ).versionRef( ucpVersion ) + library( "geolatte", "org.geolatte", "geolatte-geom" ).versionRef( geolatteVersion ) library( "jcache", "javax.cache", "cache-api" ).versionRef( jcacheVersion ) @@ -236,6 +241,7 @@ dependencyResolutionManagement { library( "h2gis", "org.orbisgis", "h2gis" ).versionRef( h2gisVersion ) library( "hsqldb", "org.hsqldb", "hsqldb" ).versionRef( hsqldbVersion ) library( "derby", "org.apache.derby", "derby" ).versionRef( derbyVersion ) + library( "derbyTools", "org.apache.derby", "derbytools" ).versionRef( derbyVersion ) library( "postgresql", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) library( "cockroachdb", "org.postgresql", "postgresql" ).versionRef( pgsqlVersion ) library( "mysql", "com.mysql", "mysql-connector-j" ).versionRef( mysqlVersion ) @@ -311,6 +317,7 @@ include 'hibernate-proxool' include 'hibernate-hikaricp' include 'hibernate-vibur' include 'hibernate-agroal' +include 'hibernate-ucp' include 'hibernate-jcache'