From e8a52e290437c510927106d33efe9e6350bc66e8 Mon Sep 17 00:00:00 2001 From: Brett Meyer Date: Mon, 17 Mar 2014 16:33:44 -0400 Subject: [PATCH] HHH-8869 created hibernate-hikaricp connection provider Conflicts: libraries.gradle --- hibernate-hikaricp/hibernate-hikaricp.gradle | 17 +++ .../internal/HikariCPConnectionProvider.java | 131 ++++++++++++++++++ .../internal/HikariConfigurationUtil.java | 84 +++++++++++ .../StrategyRegistrationProviderImpl.java | 58 ++++++++ .../hikaricp/internal/package-info.java | 4 + ...stry.selector.StrategyRegistrationProvider | 1 + .../OSGI-INF/blueprint/blueprint.xml | 10 ++ .../HikariCPConnectionProviderTest.java | 90 ++++++++++++ .../src/test/resources/hibernate.properties | 37 +++++ libraries.gradle | 2 +- settings.gradle | 1 + 11 files changed, 434 insertions(+), 1 deletion(-) create mode 100644 hibernate-hikaricp/hibernate-hikaricp.gradle create mode 100644 hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java create mode 100644 hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariConfigurationUtil.java create mode 100644 hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/StrategyRegistrationProviderImpl.java create mode 100644 hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/package-info.java create mode 100644 hibernate-hikaricp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider create mode 100644 hibernate-hikaricp/src/main/resources/OSGI-INF/blueprint/blueprint.xml create mode 100644 hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariCPConnectionProviderTest.java create mode 100644 hibernate-hikaricp/src/test/resources/hibernate.properties diff --git a/hibernate-hikaricp/hibernate-hikaricp.gradle b/hibernate-hikaricp/hibernate-hikaricp.gradle new file mode 100644 index 0000000000..84090ce4a3 --- /dev/null +++ b/hibernate-hikaricp/hibernate-hikaricp.gradle @@ -0,0 +1,17 @@ +dependencies { + compile project( ':hibernate-core' ) + compile( libraries.hikaricp ) + testCompile project( ':hibernate-testing' ) +} + +def pomName() { + return 'Hibernate/HikariCP Integration' +} + +def pomDescription() { + return 'Integration for HikariCP into Hibernate O/RM' +} + +def osgiDescription() { + return pomDescription() +} diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java new file mode 100644 index 0000000000..e3dd642202 --- /dev/null +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariCPConnectionProvider.java @@ -0,0 +1,131 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.hibernate.hikaricp.internal; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.Stoppable; +import org.jboss.logging.Logger; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +/** + * HikariCP Connection provider for Hibernate. + * + * @author Brett Wooldridge + * @author Luca Burgazzoli + */ +public class HikariCPConnectionProvider implements ConnectionProvider, Configurable, Stoppable { + + private static final long serialVersionUID = -9131625057941275711L; + + private static final Logger LOGGER = Logger.getLogger( HikariCPConnectionProvider.class ); + + /** + * HikariCP configuration. + */ + private HikariConfig hcfg = null; + + /** + * HikariCP data source. + */ + private HikariDataSource hds = null; + + // ************************************************************************* + // Configurable + // ************************************************************************* + + @SuppressWarnings("rawtypes") + @Override + public void configure(Map props) throws HibernateException { + try { + LOGGER.debug( "Configuring HikariCP" ); + + hcfg = HikariConfigurationUtil.loadConfiguration( props ); + hds = new HikariDataSource( hcfg ); + + } + catch (Exception e) { + throw new HibernateException( e ); + } + + LOGGER.debug( "HikariCP Configured" ); + } + + // ************************************************************************* + // ConnectionProvider + // ************************************************************************* + + @Override + public Connection getConnection() throws SQLException { + Connection conn = null; + if ( hds != null ) { + conn = hds.getConnection(); + } + + 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 ) + || HikariCPConnectionProvider.class.isAssignableFrom( unwrapType ); + } + + @Override + @SuppressWarnings("unchecked") + public T unwrap(Class unwrapType) { + if ( isUnwrappableAs( unwrapType ) ) { + return (T) this; + } + else { + throw new UnknownUnwrapTypeException( unwrapType ); + } + } + + // ************************************************************************* + // Stoppable + // ************************************************************************* + + @Override + public void stop() { + hds.shutdown(); + } +} diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariConfigurationUtil.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariConfigurationUtil.java new file mode 100644 index 0000000000..d974289f34 --- /dev/null +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/HikariConfigurationUtil.java @@ -0,0 +1,84 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * JBoss, Home of Professional Open Source + * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @authors tag. All rights reserved. + * See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License, v. 2.1. + * This program is distributed in the hope that it will be useful, but WITHOUT A + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License, + * v.2.1 along with this distribution; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.hibernate.hikaricp.internal; + +import java.util.Map; +import java.util.Properties; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; + +import com.zaxxer.hikari.HikariConfig; + +/** + * Utility class to map Hibernate properties to HikariCP configuration properties. + * + * @author Brett Wooldridge + * @author Luca Burgazzoli + * @author Brett Meyer + */ +public class HikariConfigurationUtil { + + public static final String CONFIG_PREFIX = "hibernate.hikari."; + + /** + * Create/load a HikariConfig from Hibernate properties. + * + * @param props a map of Hibernate properties + * @return a HikariConfig + */ + @SuppressWarnings("rawtypes") + public static HikariConfig loadConfiguration(Map props) { + Properties hikariProps = new Properties(); + copyProperty( AvailableSettings.ISOLATION, props, "transactionIsolation", hikariProps ); + copyProperty( AvailableSettings.AUTOCOMMIT, props, "autoCommit", hikariProps ); + + if (props.containsKey( AvailableSettings.DIALECT ) + && props.get( AvailableSettings.DIALECT ).equals( H2Dialect.class.getName() )) { + // Terrible workaround for an issue with H2. The version of H2 we currently use does not include the + // standard DataSource#setUrl, but instead uses setURL. HikariCP's PropertyBeanSetter uses reflection and + // fails without this. + copyProperty( AvailableSettings.URL, props, "dataSource.URL", hikariProps ); + } + else { + copyProperty( AvailableSettings.URL, props, "dataSource.url", hikariProps ); + } + copyProperty( AvailableSettings.USER, props, "dataSource.user", hikariProps ); + copyProperty( AvailableSettings.PASS, props, "dataSource.password", hikariProps ); + + for ( Object keyo : props.keySet() ) { + String key = (String) keyo; + if ( key.startsWith( CONFIG_PREFIX ) ) { + hikariProps.setProperty( key.substring( CONFIG_PREFIX.length() ), (String) props.get( key ) ); + } + } + + return new HikariConfig( hikariProps ); + } + + @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 ) ); + } + } +} diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/StrategyRegistrationProviderImpl.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/StrategyRegistrationProviderImpl.java new file mode 100644 index 0000000000..b9fbb9be05 --- /dev/null +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/StrategyRegistrationProviderImpl.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.hikaricp.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 HikariCPConnectionProvider} to the + * {@link org.hibernate.boot.registry.selector.spi.StrategySelector} service. + * + * @author Brett Meyer + */ +public class StrategyRegistrationProviderImpl implements StrategyRegistrationProvider { + private static final List REGISTRATIONS = Collections.singletonList( + (StrategyRegistration) new SimpleStrategyRegistrationImpl( + ConnectionProvider.class, + HikariCPConnectionProvider.class, + "hikari", + "hikaricp", + HikariCPConnectionProvider.class.getSimpleName(), + // for consistency's sake + "org.hibernate.connection.HikariCPConnectionProvider" + ) + ); + + @Override + @SuppressWarnings("unchecked") + public Iterable getStrategyRegistrations() { + return REGISTRATIONS; + } +} diff --git a/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/package-info.java b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/package-info.java new file mode 100644 index 0000000000..ed2b12f239 --- /dev/null +++ b/hibernate-hikaricp/src/main/java/org/hibernate/hikaricp/internal/package-info.java @@ -0,0 +1,4 @@ +/** + * Implementation of ConnectionProvider using HikariCP. + */ +package org.hibernate.hikaricp.internal; diff --git a/hibernate-hikaricp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider b/hibernate-hikaricp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider new file mode 100644 index 0000000000..b4a1509a77 --- /dev/null +++ b/hibernate-hikaricp/src/main/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider @@ -0,0 +1 @@ +org.hibernate.hikaricp.internal.StrategyRegistrationProviderImpl diff --git a/hibernate-hikaricp/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/hibernate-hikaricp/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 0000000000..163905773e --- /dev/null +++ b/hibernate-hikaricp/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariCPConnectionProviderTest.java b/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariCPConnectionProviderTest.java new file mode 100644 index 0000000000..22a2e56d1b --- /dev/null +++ b/hibernate-hikaricp/src/test/java/org/hibernate/test/hikaricp/HikariCPConnectionProviderTest.java @@ -0,0 +1,90 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hikaricp; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.hikaricp.internal.HikariCPConnectionProvider; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +/** + * @author Brett Meyer + */ +public class HikariCPConnectionProviderTest extends BaseCoreFunctionalTestCase { + + @Test + public void testHikariCPConnectionProvider() throws Exception { + JdbcServices jdbcServices = serviceRegistry().getService( JdbcServices.class ); + ConnectionProvider provider = jdbcServices.getConnectionProvider(); + assertTrue( provider instanceof HikariCPConnectionProvider ); + + HikariCPConnectionProvider hikariCP = (HikariCPConnectionProvider) provider; + // For simplicity's sake, using the following in hibernate.properties: + // hibernate.hikari.minimumPoolSize 2 + // hibernate.hikari.maximumPoolSize 2 + final List conns = new ArrayList(); + for ( int i = 0; i < 2; i++ ) { + Connection conn = hikariCP.getConnection(); + assertNotNull( conn ); + assertFalse( conn.isClosed() ); + conns.add( conn ); + } + + try { + hikariCP.getConnection(); + fail( "SQLException expected -- no more connections should have been available in the pool." ); + } + catch (SQLException e) { + // expected + assertTrue( e.getMessage().contains( "Timeout" ) ); + } + + for ( Connection conn : conns ) { + hikariCP.closeConnection( conn ); + assertTrue( conn.isClosed() ); + } + + releaseSessionFactory(); + + try { + hikariCP.getConnection(); + fail( "Exception expected -- the pool should have been shutdown." ); + } + catch (Exception e) { + // expected + assertTrue( e.getMessage().contains( "shutdown" ) ); + } + } +} diff --git a/hibernate-hikaricp/src/test/resources/hibernate.properties b/hibernate-hikaricp/src/test/resources/hibernate.properties new file mode 100644 index 0000000000..d4471ee945 --- /dev/null +++ b/hibernate-hikaricp/src/test/resources/hibernate.properties @@ -0,0 +1,37 @@ +# +# Hibernate, Relational Persistence for Idiomatic Java +# +# Copyright (c) 2010, Red Hat Inc. or third-party contributors as +# indicated by the @author tags or express copyright attribution +# statements applied by the authors. All third-party contributions are +# distributed under license by Red Hat Inc. +# +# This copyrighted material is made available to anyone wishing to use, modify, +# copy, or redistribute it subject to the terms and conditions of the GNU +# Lesser General Public License, as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this distribution; if not, write to: +# Free Software Foundation, Inc. +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301 USA +# +hibernate.dialect org.hibernate.dialect.H2Dialect +hibernate.connection.driver_class org.h2.Driver +hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE +hibernate.connection.username sa + +hibernate.jdbc.batch_size 10 +hibernate.connection.provider_class HikariCPConnectionProvider + +hibernate.hikari.dataSourceClassName org.h2.jdbcx.JdbcDataSource +hibernate.hikari.poolName testPool +# Purposefully low and simplisitic. +hibernate.hikari.minimumPoolSize 2 +hibernate.hikari.maximumPoolSize 2 +hibernate.hikari.idleTimeout 3000 \ No newline at end of file diff --git a/libraries.gradle b/libraries.gradle index 931a88bbf5..64e6c34168 100644 --- a/libraries.gradle +++ b/libraries.gradle @@ -108,10 +108,10 @@ ext { jnp_client: "org.jboss.naming:jnp-client:${jnpVersion}", jnp_server: "org.jboss.naming:jnpserver:${jnpVersion}", - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ c3p0 c3p0: "com.mchange:c3p0:0.9.2.1", ehcache: "net.sf.ehcache:ehcache-core:2.4.3", proxool: "proxool:proxool:0.8.3" + hikaricp: "com.zaxxer:HikariCP:1.3.0" ] } diff --git a/settings.gradle b/settings.gradle index ec2b4bf276..0594193578 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ include 'hibernate-osgi' include 'hibernate-c3p0' include 'hibernate-proxool' +include 'hibernate-hikaricp' include 'hibernate-ehcache' include 'hibernate-infinispan'