r2243 - in trunk/topia-persistence/src: main/java/org/nuiton/topia/framework test/java/org/nuiton/topia test/java/org/nuiton/topia/framework test/resources
Author: tchemit Date: 2011-04-14 13:15:26 +0200 (Thu, 14 Apr 2011) New Revision: 2243 Url: http://nuiton.org/repositories/revision/topia/2243 Log: Introduce a ConnectionProvider to fix Anomalie #561: PostgreSQL socket closed error Added: trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java trunk/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties Modified: trunk/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java Added: trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java =================================================================== --- trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java (rev 0) +++ trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java 2011-04-14 11:15:26 UTC (rev 2243) @@ -0,0 +1,245 @@ +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.connection.ConnectionProvider; +import org.hibernate.connection.ConnectionProviderFactory; +import org.hibernate.util.PropertiesHelper; +import org.hibernate.util.ReflectHelper; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** + * Customized connection provider. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProvider implements ConnectionProvider { + + private String url; + + private Properties connectionProps; + + private Integer isolation; + + private final List<Connection> pool = new ArrayList<Connection>(); + + private int poolSize; + + private int checkedOut; + + private boolean autocommit; + + /** Logger. */ + private static final Log log = + LogFactory.getLog(TopiaConnectionProvider.class); + + @Override + public void configure(Properties props) throws HibernateException { + String driverClass = props.getProperty(Environment.DRIVER); + + poolSize = PropertiesHelper.getInt(Environment.POOL_SIZE, props, 20); //default pool size 20 + if (log.isInfoEnabled()) { + + log.info("Using Hibernate built-in connection pool (not for production use!)"); + log.info("Hibernate connection pool size: " + poolSize); + } + + autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props); + if (log.isInfoEnabled()) + log.info("autocommit mode: " + autocommit); + + isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props); + if (isolation != null) { + if (log.isInfoEnabled()) { + log.info("JDBC isolation level: " + + Environment.isolationLevelToString(isolation)); + } + } + + if (driverClass == null) { + + if (log.isWarnEnabled()) { + log.warn("no JDBC Driver class was specified by property " + + Environment.DRIVER); + } + } else { + try { + // trying via forName() first to be as close to DriverManager's semantics + Class.forName(driverClass); + } catch (ClassNotFoundException cnfe) { + try { + ReflectHelper.classForName(driverClass); + } catch (ClassNotFoundException e) { + String msg = "JDBC Driver class not found: " + driverClass; + log.error(msg, e); + throw new HibernateException(msg, e); + } + } + } + + url = props.getProperty(Environment.URL); + if (url == null) { + String msg = "JDBC URL was not specified by property " + + Environment.URL; + if (log.isErrorEnabled()) { + log.error(msg); + } + throw new HibernateException(msg); + } + + connectionProps = + ConnectionProviderFactory.getConnectionProperties(props); + + if (log.isInfoEnabled()) { + log.info("using driver: " + driverClass + " at URL: " + url); + } + // if debug level is enabled, then log the password, otherwise mask it + if (log.isDebugEnabled()) { + log.info("connection properties: " + connectionProps); + } else if (log.isInfoEnabled()) { + log.info("connection properties: " + + PropertiesHelper.maskOut(connectionProps, "password")); + } + } + + @Override + public Connection getConnection() throws SQLException { + if (log.isTraceEnabled()) { + log.trace("total checked-out connections: " + checkedOut); + } + + // always clean the pool before any use of it + + synchronized (pool) { + + // before getting any connection, make sure there is not a closed + // connection in pool, if so then remove them from the pool + + Iterator<?> itr = pool.iterator(); + while (itr.hasNext()) { + Connection connection = (Connection) itr.next(); + if (connection.isClosed()) { + + // this connection is closed!, remove it + itr.remove(); + + if (log.isDebugEnabled()) { + log.debug("Remove already closed connection from pool " + + connection); + } + } + } + if (!pool.isEmpty()) { + int last = pool.size() - 1; + if (log.isTraceEnabled()) { + log.trace("using pooled JDBC connection, pool size: " + + last); + checkedOut++; + } + Connection pooled = pool.remove(last); + if (isolation != null) { + pooled.setTransactionIsolation(isolation); + } + if (pooled.getAutoCommit() != autocommit) + pooled.setAutoCommit(autocommit); + return pooled; + } + } + + log.debug("opening new JDBC connection"); + Connection conn = DriverManager.getConnection(url, connectionProps); + if (isolation != null) { + conn.setTransactionIsolation(isolation); + } + if (conn.getAutoCommit() != autocommit) { + conn.setAutoCommit(autocommit); + } + + if (log.isDebugEnabled()) { + log.debug("created connection to: " + url + ", Isolation Level: " + + conn.getTransactionIsolation()); + } + if (log.isTraceEnabled()) { + checkedOut++; + } + + return conn; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { + + if (conn.isClosed()) { + + // if connection already closed (I don't know how, make sure it is + // not any longer used by pool) + if (log.isDebugEnabled()) { + log.debug("Connection [" + conn + + "] alreay closed!, will not use it any longer "); + } + return; + } + if (log.isDebugEnabled()) { + checkedOut--; + } + + synchronized (pool) { + int currentSize = pool.size(); + if (currentSize < poolSize) { + if (log.isTraceEnabled()) { + log.trace("returning connection to pool, pool size: " + + (currentSize + 1)); + } + + pool.add(conn); + return; + } + } + + if (log.isDebugEnabled()) { + log.debug("closing JDBC connection"); + } + + conn.close(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } + + public void close() { + + if (log.isInfoEnabled()) { + log.info("cleaning up connection pool: " + url); + } + + for (Connection connection : pool) { + try { + connection.close(); + } catch (SQLException sqle) { + if (log.isWarnEnabled()) { + log.warn("problem closing pooled connection", sqle); + } + } + } + pool.clear(); + + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } +} Property changes on: trunk/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Modified: trunk/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java =================================================================== --- trunk/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java 2011-04-12 06:46:26 UTC (rev 2242) +++ trunk/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java 2011-04-14 11:15:26 UTC (rev 2243) @@ -26,6 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Environment; import org.junit.Ignore; import org.nuiton.util.FileUtil; @@ -111,30 +112,40 @@ String dbname) throws IOException { - InputStream stream = TestHelper.class.getResourceAsStream(dbPropertiesPath); + Properties configuration = loadHibernateConfiguration(dbPropertiesPath); - Properties configuration = new Properties(); - - configuration.load(stream); - configuration.setProperty( - "topia.persistence.classes", - TopiaTestDAOHelper.getImplementationClassesAsString()); - // make sure we always use a different directory - String dbPath = new File(testDirectory, - dbname + '_' + System.nanoTime()).getAbsolutePath(); + String dbPath = getDbName(testDirectory, dbname); if (log.isInfoEnabled()) { log.info("dbPath = " + dbPath); } configuration.setProperty( - "hibernate.connection.url", + Environment.URL, +// "hibernate.connection.url", "jdbc:h2:file:" + dbPath); return configuration; } + public static Properties loadHibernateConfiguration(String dbPropertiesPath) throws IOException { + InputStream stream = TestHelper.class.getResourceAsStream(dbPropertiesPath); + + Properties configuration = new Properties(); + + configuration.load(stream); + configuration.setProperty( + "topia.persistence.classes", + TopiaTestDAOHelper.getImplementationClassesAsString()); + return configuration; + } + + public static String getDbName(File testDirectory, String dbname) { + return new File(testDirectory, + dbname + '_' + System.nanoTime()).getAbsolutePath(); + } + public static Properties initTopiaContextConfiguration(File testDirectory, String dbname) throws IOException { Added: trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java =================================================================== --- trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java (rev 0) +++ trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java 2011-04-14 11:15:26 UTC (rev 2243) @@ -0,0 +1,185 @@ +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.connection.ConnectionProvider; +import org.hibernate.connection.ConnectionProviderFactory; +import org.hibernate.util.PropertiesHelper; +import org.hibernate.util.ReflectHelper; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Properties; + +/** + * Customized connection provider. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProviderHardCoded implements ConnectionProvider { + + private String url; + + private Properties connectionProps; + + private Integer isolation; + + private final ArrayList pool = new ArrayList(); + + private int poolSize; + + private int checkedOut = 0; + + private boolean autocommit; + + /** Logger. */ + private static final Log log = + LogFactory.getLog(TopiaConnectionProviderHardCoded.class); + + @Override + public void configure(Properties props) throws HibernateException { + String driverClass = props.getProperty(Environment.DRIVER); + + poolSize = PropertiesHelper.getInt(Environment.POOL_SIZE, props, 20); //default pool size 20 + log.info("Using Hibernate built-in connection pool (not for production use!)"); + log.info("Hibernate connection pool size: " + poolSize); + + autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props); + log.info("autocommit mode: " + autocommit); + + isolation = PropertiesHelper.getInteger(Environment.ISOLATION, props); + if (isolation != null) + log.info("JDBC isolation level: " + Environment.isolationLevelToString(isolation)); + + if (driverClass == null) { + log.warn("no JDBC Driver class was specified by property " + Environment.DRIVER); + } else { + try { + // trying via forName() first to be as close to DriverManager's semantics + Class.forName(driverClass); + } catch (ClassNotFoundException cnfe) { + try { + ReflectHelper.classForName(driverClass); + } catch (ClassNotFoundException e) { + String msg = "JDBC Driver class not found: " + driverClass; + log.error(msg, e); + throw new HibernateException(msg, e); + } + } + } + + // use a dummy directory to make sure only the connection provider knows + // the real directory where db is and then make sure hibernate always + // use the connection provider... + String directory = + props.getProperty(TopiaConnectionProviderTest.TEST_URL); + + url = directory; +// url = props.getProperty(Environment.URL); + +// if (url == null) { +// String msg = "JDBC URL was not specified by property " + Environment.URL; +// log.error(msg); +// throw new HibernateException(msg); +// } + + connectionProps = ConnectionProviderFactory.getConnectionProperties(props); + + log.info("using driver: " + driverClass + " at URL: " + url); + // if debug level is enabled, then log the password, otherwise mask it + if (log.isDebugEnabled()) { + log.info("connection properties: " + connectionProps); + } else if (log.isInfoEnabled()) { + log.info("connection properties: " + PropertiesHelper.maskOut(connectionProps, "password")); + } + } + + @Override + public Connection getConnection() throws SQLException { + if (log.isTraceEnabled()) + log.trace("total checked-out connections: " + checkedOut); + + synchronized (pool) { + if (!pool.isEmpty()) { + int last = pool.size() - 1; + if (log.isTraceEnabled()) { + log.trace("using pooled JDBC connection, pool size: " + last); + } + checkedOut++; + Connection pooled = (Connection) pool.remove(last); + if (isolation != null) + pooled.setTransactionIsolation(isolation.intValue()); + if (pooled.getAutoCommit() != autocommit) + pooled.setAutoCommit(autocommit); + return pooled; + } + } + + log.debug("opening new JDBC connection"); + Connection conn = DriverManager.getConnection(url, connectionProps); + if (isolation != null) conn.setTransactionIsolation(isolation); + if (conn.getAutoCommit() != autocommit) conn.setAutoCommit(autocommit); + + if (log.isDebugEnabled()) { + log.debug("created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation()); + } +// if ( log.isTraceEnabled() ) + checkedOut++; + + return conn; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { +// if ( log.isDebugEnabled() ) + checkedOut--; + + synchronized (pool) { + int currentSize = pool.size(); + if (currentSize < poolSize) { + if (log.isTraceEnabled()) { + log.trace("returning connection to pool, pool size: " + (currentSize + 1)); + } + pool.add(conn); + return; + } + } + + log.debug("closing JDBC connection"); + + conn.close(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } + + public void close() { + + log.info("cleaning up connection pool: " + url); + + Iterator iter = pool.iterator(); + while (iter.hasNext()) { + try { + ((Connection) iter.next()).close(); + } catch (SQLException sqle) { + log.warn("problem closing pooled connection", sqle); + } + } + pool.clear(); + + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } +} Property changes on: trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Added: trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java =================================================================== --- trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java (rev 0) +++ trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java 2011-04-14 11:15:26 UTC (rev 2243) @@ -0,0 +1,107 @@ +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Environment; +import org.junit.After; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TestHelper; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; +import org.nuiton.topiatest.Personne; + +import java.io.File; +import java.util.Locale; +import java.util.Properties; + +import static org.junit.Assert.assertNotNull; + +/** + * To test the {@link TopiaConnectionProvider} and make sure all connections + * are done from here... + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProviderTest { + + + private static final Log log = + LogFactory.getLog(TopiaConnectionProviderTest.class); + + protected static File testBasedir; + + public static final String TEST_URL = "testURL"; + + protected TopiaContext root; + + @BeforeClass + public static void setUpClass() throws Exception { + + testBasedir = TestHelper.getTestBasedir(TopiaConnectionProviderTest.class); + } + + @After + public void tearDown() throws TopiaException { + if (root != null && !root.isClosed()) { + root.closeContext(); + } + } + + @Test + public void testWithHardcoded() throws Exception { + + Properties dbProperties = TestHelper.loadHibernateConfiguration( + "/TopiaConnectionProviderHardcoded.properties"); + + File directory = new File(TestHelper.getDbName(testBasedir, "testWithHardcoded")); + + String dbPath = new File(directory, "real" + File.separator + "db").getAbsolutePath(); + Assert.assertFalse(new File(dbPath).getParentFile().exists()); + + String dbPathFake = new File(directory, "fake" + File.separator + "db").getAbsolutePath(); + + Assert.assertFalse(new File(dbPathFake).getParentFile().exists()); + + // give the path where connection provider will create db + dbProperties.setProperty(TEST_URL, "jdbc:h2:file:" + dbPath); + + // give a fake db path (we will make sure it is never create after hibernate usage). + dbProperties.setProperty(Environment.URL, "jdbc:h2:file:" + dbPathFake); + + root = TopiaContextFactory.getContext(dbProperties); + + Locale.setDefault(Locale.FRANCE); + + doStuffOnDb(); + + // the db file must have been created + Assert.assertTrue(new File(dbPath).getParentFile().exists()); + + // make sure the fake db path was never used + Assert.assertFalse(new File(dbPathFake).getParentFile().exists()); + } + + private void doStuffOnDb() throws TopiaException { + TopiaContext transaction = root.beginTransaction(); + + try { + PersonDAO dao = TopiaTestDAOHelper.getPersonDAO(transaction); + + Person personne = dao.create(Personne.PROPERTY_NAME, "Jack Bauer"); + transaction.commitTransaction(); + String idPersonne = personne.getTopiaId(); + assertNotNull(idPersonne); + + transaction.commitTransaction(); + } finally { + transaction.closeContext(); + } + } +} Property changes on: trunk/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Copied: trunk/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties (from rev 2242, trunk/topia-persistence/src/test/resources/TopiaContextImpl.properties) =================================================================== --- trunk/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties (rev 0) +++ trunk/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties 2011-04-14 11:15:26 UTC (rev 2243) @@ -0,0 +1,35 @@ +### +# #%L +# ToPIA :: Persistence +# +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2010 CodeLutin +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Proprietes par defaut pour une base de donnees de type H2 +hibernate.hbm2ddl.auto=update +hibernate.show_sql=false + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.connection.username=sa +hibernate.connection.password= +hibernate.connection.driver_class=org.h2.Driver +hibernate.connection.provider_class=org.nuiton.topia.framework.TopiaConnectionProviderHardCoded +# tchemit 2010-11-28 : comment this line, each test must define his own db path +#hibernate.connection.url=jdbc:h2:file:target/surefire-workdir/h2data \ No newline at end of file Property changes on: trunk/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL
participants (1)
-
tchemit@users.nuiton.org