Reformatting with new Eclipse formatter settings. Went with a variation of Sun-style, which leads to more compact method bodies, but still white-space to "highlight" class and method declarations.

pull/131/head
Brett Wooldridge 11 years ago
parent 0486c5904e
commit 775cba7db5

File diff suppressed because it is too large Load Diff

@ -23,123 +23,123 @@ package com.zaxxer.hikari;
*/ */
public interface HikariConfigMBean public interface HikariConfigMBean
{ {
/** /**
* Get the maximum number of milliseconds that a client will wait for a connection from the pool. If this * Get the maximum number of milliseconds that a client will wait for a connection from the pool. If this
* time is exceeded without a connection becoming available, a SQLException will be thrown from * time is exceeded without a connection becoming available, a SQLException will be thrown from
* {@link javax.sql.DataSource#getConnection()}. * {@link javax.sql.DataSource#getConnection()}.
* *
* @return the connection timeout in milliseconds * @return the connection timeout in milliseconds
*/ */
long getConnectionTimeout(); long getConnectionTimeout();
/** /**
* Set the maximum number of milliseconds that a client will wait for a connection from the pool. If this * Set the maximum number of milliseconds that a client will wait for a connection from the pool. If this
* time is exceeded without a connection becoming available, a SQLException will be thrown from * time is exceeded without a connection becoming available, a SQLException will be thrown from
* {@link javax.sql.DataSource#getConnection()}. * {@link javax.sql.DataSource#getConnection()}.
* *
* @param connectionTimeoutMs the connection timeout in milliseconds * @param connectionTimeoutMs the connection timeout in milliseconds
*/ */
void setConnectionTimeout(long connectionTimeoutMs); void setConnectionTimeout(long connectionTimeoutMs);
/** /**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30 * idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. * seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool. * A value of 0 means that idle connections are never removed from the pool.
* *
* @return the idle timeout in milliseconds * @return the idle timeout in milliseconds
*/ */
long getIdleTimeout(); long getIdleTimeout();
/** /**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit * This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30 * idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout. * seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool. * A value of 0 means that idle connections are never removed from the pool.
* *
* @param idleTimeoutMs the idle timeout in milliseconds * @param idleTimeoutMs the idle timeout in milliseconds
*/ */
void setIdleTimeout(long idleTimeoutMs); void setIdleTimeout(long idleTimeoutMs);
/** /**
* This property controls the amount of time that a connection can be out of the pool before a message is * This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled. * logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
* *
* @return the connection leak detection threshold in milliseconds * @return the connection leak detection threshold in milliseconds
*/ */
long getLeakDetectionThreshold(); long getLeakDetectionThreshold();
/** /**
* This property controls the amount of time that a connection can be out of the pool before a message is * This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled. * logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
* *
* @param leakDetectionThresholdMs the connection leak detection threshold in milliseconds * @param leakDetectionThresholdMs the connection leak detection threshold in milliseconds
*/ */
void setLeakDetectionThreshold(long leakDetectionThresholdMs); void setLeakDetectionThreshold(long leakDetectionThresholdMs);
/** /**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this * This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be * timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed. * retired, only when it is idle will it be removed.
* *
* @return the maximum connection lifetime in milliseconds * @return the maximum connection lifetime in milliseconds
*/ */
long getMaxLifetime(); long getMaxLifetime();
/** /**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this * This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be * timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed. * retired, only when it is idle will it be removed.
* *
* @param maxLifetimeMs the maximum connection lifetime in milliseconds * @param maxLifetimeMs the maximum connection lifetime in milliseconds
*/ */
void setMaxLifetime(long maxLifetimeMs); void setMaxLifetime(long maxLifetimeMs);
/** /**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use * The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database * connections. Basically this value will determine the maximum number of actual connections to the database
* backend. * backend.
* <p> * <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will * When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out. * block for up to connectionTimeout milliseconds before timing out.
* *
* @return the minimum number of connections in the pool * @return the minimum number of connections in the pool
*/ */
int getMinimumIdle(); int getMinimumIdle();
/** /**
* The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool, * The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool,
* including both idle and in-use connections. If the idle connections dip below this value, HikariCP will * including both idle and in-use connections. If the idle connections dip below this value, HikariCP will
* make a best effort to restore them quickly and efficiently. * make a best effort to restore them quickly and efficiently.
* *
* @param minIdle the minimum number of idle connections in the pool to maintain * @param minIdle the minimum number of idle connections in the pool to maintain
*/ */
void setMinimumIdle(int minIdle); void setMinimumIdle(int minIdle);
/** /**
* The property controls the maximum number of connections that HikariCP will keep in the pool, * The property controls the maximum number of connections that HikariCP will keep in the pool,
* including both idle and in-use connections. * including both idle and in-use connections.
* *
* @return the maximum number of connections in the pool * @return the maximum number of connections in the pool
*/ */
int getMaximumPoolSize(); int getMaximumPoolSize();
/** /**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use * The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database * connections. Basically this value will determine the maximum number of actual connections to the database
* backend. * backend.
* <p> * <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will * When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out. * block for up to connectionTimeout milliseconds before timing out.
* *
* @param maxPoolSize the maximum number of connections in the pool * @param maxPoolSize the maximum number of connections in the pool
*/ */
void setMaximumPoolSize(int maxPoolSize); void setMaximumPoolSize(int maxPoolSize);
/** /**
* The name of the connection pool. * The name of the connection pool.
* *
* @return the name of the connection pool * @return the name of the connection pool
*/ */
String getPoolName(); String getPoolName();
} }

@ -38,272 +38,251 @@ import com.zaxxer.hikari.util.DriverDataSource;
*/ */
public class HikariDataSource extends HikariConfig implements DataSource public class HikariDataSource extends HikariConfig implements DataSource
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class); private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);
// We use a concrete HashMap rather than Map to avoid an invokeinterface callsite // We use a concrete HashMap rather than Map to avoid an invokeinterface callsite
private final HashMap<MultiPoolKey, HikariPool> multiPool; private final HashMap<MultiPoolKey, HikariPool> multiPool;
private volatile boolean isShutdown; private volatile boolean isShutdown;
private int loginTimeout; private int loginTimeout;
/* Package scopped for testing */ /* Package scopped for testing */
private final HikariPool fastPathPool; private final HikariPool fastPathPool;
private volatile HikariPool pool; private volatile HikariPool pool;
/** /**
* Default constructor. Setters be used to configure the pool. Using * Default constructor. Setters be used to configure the pool. Using
* this constructor vs. {@link #HikariDataSource(HikariConfig)} will * this constructor vs. {@link #HikariDataSource(HikariConfig)} will
* result in {@link #getConnection()} performance that is slightly lower * result in {@link #getConnection()} performance that is slightly lower
* due to lazy initialization checks. * due to lazy initialization checks.
*/ */
public HikariDataSource() public HikariDataSource()
{ {
super(); super();
fastPathPool = null; fastPathPool = null;
multiPool = new HashMap<MultiPoolKey, HikariPool>(); multiPool = new HashMap<MultiPoolKey, HikariPool>();
} }
/** /**
* Construct a HikariDataSource with the specified configuration. * Construct a HikariDataSource with the specified configuration.
* *
* @param configuration a HikariConfig instance * @param configuration a HikariConfig instance
*/ */
public HikariDataSource(HikariConfig configuration) public HikariDataSource(HikariConfig configuration)
{ {
configuration.validate(); configuration.validate();
configuration.copyState(this); configuration.copyState(this);
multiPool = new HashMap<MultiPoolKey, HikariPool>(); multiPool = new HashMap<MultiPoolKey, HikariPool>();
LOGGER.info("HikariCP pool {} is starting.", configuration.getPoolName()); LOGGER.info("HikariCP pool {} is starting.", configuration.getPoolName());
pool = fastPathPool = new HikariPool(this); pool = fastPathPool = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool); multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public Connection getConnection() throws SQLException public Connection getConnection() throws SQLException
{ {
if (isShutdown) if (isShutdown) {
{ throw new SQLException("Pool has been shutdown");
throw new SQLException("Pool has been shutdown"); }
}
if (fastPathPool != null) {
if (fastPathPool != null) return fastPathPool.getConnection();
{ }
return fastPathPool.getConnection();
} // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
HikariPool result = pool;
// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java if (result == null) {
HikariPool result = pool; synchronized (this) {
if (result == null) result = pool;
{ if (result == null) {
synchronized (this) validate();
{ LOGGER.info("HikariCP pool {} is starting.", getPoolName());
result = pool; pool = result = new HikariPool(this);
if (result == null) multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
{
validate();
LOGGER.info("HikariCP pool {} is starting.", getPoolName());
pool = result = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
} }
} }
}
return result.getConnection();
} return result.getConnection();
}
/** {@inheritDoc} */
@Override /** {@inheritDoc} */
public Connection getConnection(String username, String password) throws SQLException @Override
{ public Connection getConnection(String username, String password) throws SQLException
if (isShutdown) {
{ if (isShutdown) {
throw new SQLException("Pool has been shutdown"); throw new SQLException("Pool has been shutdown");
} }
final MultiPoolKey key = new MultiPoolKey(username, password); final MultiPoolKey key = new MultiPoolKey(username, password);
HikariPool hikariPool; HikariPool hikariPool;
synchronized (multiPool) synchronized (multiPool) {
{ hikariPool = multiPool.get(key);
hikariPool = multiPool.get(key); if (hikariPool == null) {
if (hikariPool == null) hikariPool = new HikariPool(this, username, password);
{ multiPool.put(key, hikariPool);
hikariPool = new HikariPool(this, username, password); }
multiPool.put(key, hikariPool); }
}
} return hikariPool.getConnection();
}
return hikariPool.getConnection();
} /** {@inheritDoc} */
@Override
/** {@inheritDoc} */ public PrintWriter getLogWriter() throws SQLException
@Override {
public PrintWriter getLogWriter() throws SQLException return (pool.getDataSource() != null ? pool.getDataSource().getLogWriter() : null);
{ }
return (pool.getDataSource() != null ? pool.getDataSource().getLogWriter() : null);
} /** {@inheritDoc} */
@Override
/** {@inheritDoc} */ public void setLogWriter(PrintWriter out) throws SQLException
@Override {
public void setLogWriter(PrintWriter out) throws SQLException if (pool.getDataSource() != null) {
{ pool.getDataSource().setLogWriter(out);
if (pool.getDataSource() != null) }
{ }
pool.getDataSource().setLogWriter(out);
} /** {@inheritDoc} */
} @Override
public void setLoginTimeout(int seconds) throws SQLException
/** {@inheritDoc} */ {
@Override this.loginTimeout = seconds;
public void setLoginTimeout(int seconds) throws SQLException }
{
this.loginTimeout = seconds; /** {@inheritDoc} */
} @Override
public int getLoginTimeout() throws SQLException
/** {@inheritDoc} */ {
@Override return loginTimeout;
public int getLoginTimeout() throws SQLException }
{
return loginTimeout; /** {@inheritDoc} */
} public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
{
/** {@inheritDoc} */ throw new SQLFeatureNotSupportedException();
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException }
{
throw new SQLFeatureNotSupportedException(); /** {@inheritDoc} */
} @Override
@SuppressWarnings("unchecked")
/** {@inheritDoc} */ public <T> T unwrap(Class<T> iface) throws SQLException
@Override {
@SuppressWarnings("unchecked") if (pool != null && iface.isInstance(pool.getDataSource())) {
public <T> T unwrap(Class<T> iface) throws SQLException return (T) pool.getDataSource();
{ }
if (pool != null && iface.isInstance(pool.getDataSource()))
{ throw new SQLException("Wrapped connection is not an instance of " + iface);
return (T) pool.getDataSource(); }
}
/** {@inheritDoc} */
throw new SQLException("Wrapped connection is not an instance of " + iface); @Override
} public boolean isWrapperFor(Class<?> iface) throws SQLException
{
/** {@inheritDoc} */ return (pool != null && pool.getDataSource().getClass().isAssignableFrom(iface));
@Override }
public boolean isWrapperFor(Class<?> iface) throws SQLException
{ /**
return (pool != null && pool.getDataSource().getClass().isAssignableFrom(iface)); * Evict a connection from the pool. Use caution using this method, if you
} * evict the same connection more than one time, the internal pool accounting
* will become invalid and the pool may stop functioning.
/** *
* Evict a connection from the pool. Use caution using this method, if you * @param connection the connection to evict from the pool
* evict the same connection more than one time, the internal pool accounting */
* will become invalid and the pool may stop functioning. public void evictConnection(Connection connection)
* {
* @param connection the connection to evict from the pool if (!isShutdown && pool != null && connection instanceof IHikariConnectionProxy) {
*/ pool.closeConnection((IHikariConnectionProxy) connection);
public void evictConnection(Connection connection) }
{ }
if (!isShutdown && pool != null && connection instanceof IHikariConnectionProxy)
{ /**
pool.closeConnection((IHikariConnectionProxy) connection); * <code>close()</code> and <code>shutdown()</code> are synonymous.
} */
} public void close()
{
/** shutdown();
* <code>close()</code> and <code>shutdown()</code> are synonymous. }
*/
public void close() /**
{ * Shutdown the DataSource and its associated pool.
shutdown(); */
} public void shutdown()
{
/** if (isShutdown) {
* Shutdown the DataSource and its associated pool. return;
*/ }
public void shutdown()
{ isShutdown = true;
if (isShutdown)
{ if (fastPathPool != null) {
return; shutdownHelper(fastPathPool);
} }
isShutdown = true; for (HikariPool hikariPool : multiPool.values()) {
shutdownHelper(hikariPool);
if (fastPathPool != null) }
{ }
shutdownHelper(fastPathPool);
} /** {@inheritDoc} */
@Override
for (HikariPool hikariPool : multiPool.values()) public String toString()
{ {
shutdownHelper(hikariPool); return String.format("HikariDataSource (%s)", pool);
} }
}
private void shutdownHelper(HikariPool hPool)
/** {@inheritDoc} */ {
@Override try {
public String toString() hPool.shutdown();
{ }
return String.format("HikariDataSource (%s)", pool); catch (InterruptedException e) {
} LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e);
}
private void shutdownHelper(HikariPool hPool)
{ if (hPool.getDataSource() instanceof DriverDataSource) {
try ((DriverDataSource) hPool.getDataSource()).shutdown();
{ }
hPool.shutdown(); }
}
catch (InterruptedException e) private static class MultiPoolKey
{ {
LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e); private String username;
} private String password;
if (hPool.getDataSource() instanceof DriverDataSource) MultiPoolKey(String username, String password)
{ {
((DriverDataSource) hPool.getDataSource()).shutdown(); this.username = username;
} this.password = password;
} }
private static class MultiPoolKey @Override
{ public int hashCode()
private String username; {
private String password; return (password == null ? 0 : password.hashCode());
}
MultiPoolKey(String username, String password)
{ @Override
this.username = username; public boolean equals(Object obj)
this.password = password; {
} MultiPoolKey otherKey = ((MultiPoolKey) obj);
if (username != null && !username.equals(otherKey.username)) {
@Override return false;
public int hashCode() }
{ else if (username != otherKey.username) {
return (password == null ? 0 : password.hashCode()); return false;
} }
else if (password != null && !password.equals(otherKey.password)) {
@Override return false;
public boolean equals(Object obj) }
{ else if (password != otherKey.password) {
MultiPoolKey otherKey = ((MultiPoolKey) obj); return false;
if (username != null && !username.equals(otherKey.username)) }
{
return false; return true;
} }
else if (username != otherKey.username) }
{
return false;
}
else if (password != null && !password.equals(otherKey.password))
{
return false;
}
else if (password != otherKey.password)
{
return false;
}
return true;
}
}
} }

@ -37,85 +37,72 @@ import com.zaxxer.hikari.util.PropertyBeanSetter;
*/ */
public class HikariJNDIFactory implements ObjectFactory public class HikariJNDIFactory implements ObjectFactory
{ {
@Override @Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception
{ {
// We only know how to deal with <code>javax.naming.Reference</code> that specify a class name of "javax.sql.DataSource" // We only know how to deal with <code>javax.naming.Reference</code> that specify a class name of "javax.sql.DataSource"
if ((obj == null) || !(obj instanceof Reference)) if ((obj == null) || !(obj instanceof Reference)) {
{ return null;
return null; }
}
Reference ref = (Reference) obj; Reference ref = (Reference) obj;
if (!"javax.sql.DataSource".equals(ref.getClassName())) if (!"javax.sql.DataSource".equals(ref.getClassName())) {
{ throw new NamingException(ref.getClassName() + " is not a valid class name/type for this JNDI factory.");
throw new NamingException(ref.getClassName() + " is not a valid class name/type for this JNDI factory."); }
}
Properties properties = new Properties(); Properties properties = new Properties();
for (String propertyName : PropertyBeanSetter.getPropertyNames(HikariConfig.class)) for (String propertyName : PropertyBeanSetter.getPropertyNames(HikariConfig.class)) {
{ RefAddr ra = ref.get(propertyName);
RefAddr ra = ref.get(propertyName); if (ra != null) {
if (ra != null) String propertyValue = ra.getContent().toString();
{ properties.setProperty(propertyName, propertyValue);
String propertyValue = ra.getContent().toString(); }
properties.setProperty(propertyName, propertyValue); }
}
}
return createDataSource(properties, nameCtx); return createDataSource(properties, nameCtx);
} }
private DataSource createDataSource(Properties properties, Context context) private DataSource createDataSource(Properties properties, Context context)
{ {
if (properties.getProperty("dataSourceJNDI") != null) if (properties.getProperty("dataSourceJNDI") != null) {
{ return lookupJndiDataSource(properties, context);
return lookupJndiDataSource(properties, context); }
}
return new HikariDataSource(new HikariConfig(properties)); return new HikariDataSource(new HikariConfig(properties));
} }
private DataSource lookupJndiDataSource(Properties properties, Context context) private DataSource lookupJndiDataSource(Properties properties, Context context)
{ {
DataSource jndiDS = null; DataSource jndiDS = null;
String jndiName = properties.getProperty("dataSourceJNDI"); String jndiName = properties.getProperty("dataSourceJNDI");
try try {
{ if (context != null) {
if (context != null) jndiDS = (DataSource) context.lookup(jndiName);
{ }
jndiDS = (DataSource) context.lookup(jndiName); else {
} throw new RuntimeException("dataSourceJNDI property is configued, but local JNDI context is null.");
else }
{ }
throw new RuntimeException("dataSourceJNDI property is configued, but local JNDI context is null."); catch (NamingException e) {
} throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the local context.");
} }
catch (NamingException e)
{
throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the local context.");
}
if (jndiDS == null) if (jndiDS == null) {
{ try {
try context = (Context) (new InitialContext());
{ jndiDS = (DataSource) context.lookup(jndiName);
context = (Context) (new InitialContext()); }
jndiDS = (DataSource) context.lookup(jndiName); catch (NamingException e) {
} throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the InitialContext.");
catch (NamingException e) }
{ }
throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the InitialContext.");
}
}
if (jndiDS != null) if (jndiDS != null) {
{ HikariConfig config = new HikariConfig(properties);
HikariConfig config = new HikariConfig(properties); config.setDataSource(jndiDS);
config.setDataSource(jndiDS); return new HikariDataSource(config);
return new HikariDataSource(config); }
}
return null; return null;
} }
} }

@ -29,13 +29,13 @@ import java.sql.SQLException;
*/ */
public interface IConnectionCustomizer public interface IConnectionCustomizer
{ {
/** /**
* The Connection object that is passed into this method is the "raw" * The Connection object that is passed into this method is the "raw"
* Connection instance provided by the JDBC driver, not a wrapped * Connection instance provided by the JDBC driver, not a wrapped
* HikariCP connection. * HikariCP connection.
* *
* @param connection a native JDBC driver Connection instance to customize * @param connection a native JDBC driver Connection instance to customize
* @throws SQLException should be thrown if an error condition is encountered during customization * @throws SQLException should be thrown if an error condition is encountered during customization
*/ */
void customize(Connection connection) throws SQLException; void customize(Connection connection) throws SQLException;
} }

@ -30,44 +30,41 @@ import com.zaxxer.hikari.HikariConfig;
*/ */
public class HikariConfigurationUtil public class HikariConfigurationUtil
{ {
public static final String CONFIG_PREFIX = "hibernate.hikari."; public static final String CONFIG_PREFIX = "hibernate.hikari.";
public static final String CONFIG_PREFIX_DATASOURCE = "hibernate.hikari.dataSource."; public static final String CONFIG_PREFIX_DATASOURCE = "hibernate.hikari.dataSource.";
/** /**
* Create/load a HikariConfig from Hibernate properties. * Create/load a HikariConfig from Hibernate properties.
* *
* @param props a map of Hibernate properties * @param props a map of Hibernate properties
* @return a HikariConfig * @return a HikariConfig
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public static HikariConfig loadConfiguration(Map props) public static HikariConfig loadConfiguration(Map props)
{ {
Properties hikariProps = new Properties(); Properties hikariProps = new Properties();
copyProperty(AvailableSettings.ISOLATION, props, "transactionIsolation", hikariProps); copyProperty(AvailableSettings.ISOLATION, props, "transactionIsolation", hikariProps);
copyProperty(AvailableSettings.AUTOCOMMIT, props, "autoCommit", hikariProps); copyProperty(AvailableSettings.AUTOCOMMIT, props, "autoCommit", hikariProps);
copyProperty(AvailableSettings.DRIVER, props, "driverClassName", hikariProps); copyProperty(AvailableSettings.DRIVER, props, "driverClassName", hikariProps);
copyProperty(AvailableSettings.URL, props, "jdbcUrl", hikariProps); copyProperty(AvailableSettings.URL, props, "jdbcUrl", hikariProps);
copyProperty(AvailableSettings.USER, props, "username", hikariProps); copyProperty(AvailableSettings.USER, props, "username", hikariProps);
copyProperty(AvailableSettings.PASS, props, "password", hikariProps); copyProperty(AvailableSettings.PASS, props, "password", hikariProps);
for (Object keyo : props.keySet()) for (Object keyo : props.keySet()) {
{ String key = (String) keyo;
String key = (String) keyo; if (key.startsWith(CONFIG_PREFIX)) {
if (key.startsWith(CONFIG_PREFIX)) hikariProps.setProperty(key.substring(CONFIG_PREFIX.length()), (String) props.get(key));
{ }
hikariProps.setProperty(key.substring(CONFIG_PREFIX.length()), (String) props.get(key)); }
}
}
return new HikariConfig(hikariProps); return new HikariConfig(hikariProps);
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private static void copyProperty(String srcKey, Map src, String dstKey, Properties dst) private static void copyProperty(String srcKey, Map src, String dstKey, Properties dst)
{ {
if (src.containsKey(srcKey)) if (src.containsKey(srcKey)) {
{ dst.setProperty(dstKey, (String) src.get(srcKey));
dst.setProperty(dstKey, (String) src.get(srcKey)); }
} }
}
} }

@ -38,113 +38,108 @@ import com.zaxxer.hikari.HikariDataSource;
*/ */
public class HikariConnectionProvider implements ConnectionProvider, Configurable, Stoppable public class HikariConnectionProvider implements ConnectionProvider, Configurable, Stoppable
{ {
private static final long serialVersionUID = -9131625057941275711L; private static final long serialVersionUID = -9131625057941275711L;
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConnectionProvider.class); private static final Logger LOGGER = LoggerFactory.getLogger(HikariConnectionProvider.class);
/** /**
* HikariCP configuration. * HikariCP configuration.
*/ */
private HikariConfig hcfg; private HikariConfig hcfg;
/** /**
* HikariCP data source. * HikariCP data source.
*/ */
private HikariDataSource hds; private HikariDataSource hds;
// ************************************************************************* // *************************************************************************
// //
// ************************************************************************* // *************************************************************************
/** /**
* c-tor * c-tor
*/ */
public HikariConnectionProvider() public HikariConnectionProvider()
{ {
this.hcfg = null; this.hcfg = null;
this.hds = null; this.hds = null;
} }
// ************************************************************************* // *************************************************************************
// Configurable // Configurable
// ************************************************************************* // *************************************************************************
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void configure(Map props) throws HibernateException public void configure(Map props) throws HibernateException
{ {
try try {
{ LOGGER.debug("Configuring HikariCP");
LOGGER.debug("Configuring HikariCP");
this.hcfg = HikariConfigurationUtil.loadConfiguration(props);
this.hcfg = HikariConfigurationUtil.loadConfiguration(props); this.hds = new HikariDataSource(this.hcfg);
this.hds = new HikariDataSource(this.hcfg);
}
} catch (Exception e) {
catch (Exception e) throw new HibernateException(e);
{ }
throw new HibernateException(e);
} LOGGER.debug("HikariCP Configured");
}
LOGGER.debug("HikariCP Configured");
} // *************************************************************************
// ConnectionProvider
// ************************************************************************* // *************************************************************************
// ConnectionProvider
// ************************************************************************* @Override
public Connection getConnection() throws SQLException
@Override {
public Connection getConnection() throws SQLException Connection conn = null;
{ if (this.hds != null) {
Connection conn = null; conn = this.hds.getConnection();
if (this.hds != null) }
{
conn = this.hds.getConnection(); return conn;
} }
return conn; @Override
} public void closeConnection(Connection conn) throws SQLException
{
@Override conn.close();
public void closeConnection(Connection conn) throws SQLException }
{
conn.close(); @Override
} public boolean supportsAggressiveRelease()
{
@Override return false;
public boolean supportsAggressiveRelease() }
{
return false; @Override
} @SuppressWarnings("rawtypes")
public boolean isUnwrappableAs(Class unwrapType)
@Override {
@SuppressWarnings("rawtypes") return ConnectionProvider.class.equals(unwrapType) || HikariConnectionProvider.class.isAssignableFrom(unwrapType);
public boolean isUnwrappableAs(Class unwrapType) }
{
return ConnectionProvider.class.equals(unwrapType) || HikariConnectionProvider.class.isAssignableFrom(unwrapType); @Override
} @SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> unwrapType)
@Override {
@SuppressWarnings("unchecked") if (isUnwrappableAs(unwrapType)) {
public <T> T unwrap(Class<T> unwrapType) return (T) this;
{ }
if (isUnwrappableAs(unwrapType)) else {
{ throw new UnknownUnwrapTypeException(unwrapType);
return (T) this; }
} }
else
{ // *************************************************************************
throw new UnknownUnwrapTypeException(unwrapType); // Stoppable
} // *************************************************************************
}
@Override
// ************************************************************************* public void stop()
// Stoppable {
// ************************************************************************* this.hds.shutdown();
}
@Override
public void stop()
{
this.hds.shutdown();
}
} }

@ -23,44 +23,43 @@ import com.zaxxer.hikari.pool.HikariPool;
public final class CodaHaleMetricsTracker extends MetricsTracker public final class CodaHaleMetricsTracker extends MetricsTracker
{ {
private MetricRegistry registry; private MetricRegistry registry;
private Timer connectionObtainTimer; private Timer connectionObtainTimer;
private Histogram connectionUsage; private Histogram connectionUsage;
public CodaHaleMetricsTracker(String poolName) public CodaHaleMetricsTracker(String poolName)
{ {
registry = new MetricRegistry(); registry = new MetricRegistry();
connectionObtainTimer = registry.timer(MetricRegistry.name(HikariPool.class, "connection", "wait")); connectionObtainTimer = registry.timer(MetricRegistry.name(HikariPool.class, "connection", "wait"));
connectionUsage = registry.histogram(MetricRegistry.name(HikariPool.class, "connection", "usage")); connectionUsage = registry.histogram(MetricRegistry.name(HikariPool.class, "connection", "usage"));
} }
@Override @Override
public Context recordConnectionRequest(long requestTime) public Context recordConnectionRequest(long requestTime)
{ {
return new Context(connectionObtainTimer); return new Context(connectionObtainTimer);
} }
@Override @Override
public void recordConnectionUsage(long usageMilleseconds) public void recordConnectionUsage(long usageMilleseconds)
{ {
connectionUsage.update(usageMilleseconds); connectionUsage.update(usageMilleseconds);
} }
public static final class Context extends MetricsContext public static final class Context extends MetricsContext
{ {
Timer.Context innerContext; Timer.Context innerContext;
Context(Timer timer) Context(Timer timer)
{ {
innerContext = timer.time(); innerContext = timer.time();
} }
public void stop() public void stop()
{ {
if (innerContext != null) if (innerContext != null) {
{ innerContext.stop();
innerContext.stop(); }
} }
} }
}
} }

@ -22,36 +22,36 @@ package com.zaxxer.hikari.metrics;
*/ */
public interface IMetricsTracker public interface IMetricsTracker
{ {
/** /**
* This method is called when a connection request starts. The {@#MetricsContext.stop()} * This method is called when a connection request starts. The {@#MetricsContext.stop()}
* method will be called at the completion of the connection request, whether or not an * method will be called at the completion of the connection request, whether or not an
* exception occurred. * exception occurred.
* *
* @param startTime the timestamp of the start time as returned by System.currentTimeMillis() * @param startTime the timestamp of the start time as returned by System.currentTimeMillis()
* @return an instance of MetricsContext * @return an instance of MetricsContext
*/ */
public MetricsContext recordConnectionRequest(long startTime); public MetricsContext recordConnectionRequest(long startTime);
/** /**
* This method is called when a Connection is closed, with the total time in milliseconds * This method is called when a Connection is closed, with the total time in milliseconds
* that the Connection was out of the pool. * that the Connection was out of the pool.
* *
* @param usageMilleseconds the Connection usage time in milliseconds * @param usageMilleseconds the Connection usage time in milliseconds
*/ */
public void recordConnectionUsage(long usageMilleseconds); public void recordConnectionUsage(long usageMilleseconds);
/** /**
* A base instance of a MetricsContext. Classes extending this class should exhibit the * A base instance of a MetricsContext. Classes extending this class should exhibit the
* behavior of "starting" a timer upon contruction, and "stopping" the timer when the * behavior of "starting" a timer upon contruction, and "stopping" the timer when the
* {@#stop()} method is called. * {@#stop()} method is called.
* *
* @author Brett Wooldridge * @author Brett Wooldridge
*/ */
public static class MetricsContext public static class MetricsContext
{ {
public void stop() public void stop()
{ {
// do nothing // do nothing
} }
} }
} }

@ -24,13 +24,13 @@ import com.zaxxer.hikari.util.PoolUtilities;
*/ */
public final class MetricsFactory public final class MetricsFactory
{ {
private MetricsFactory() private MetricsFactory()
{ {
// private contructor // private contructor
} }
public static final IMetricsTracker createMetricsTracker(String metricsClassName, String poolName) public static final IMetricsTracker createMetricsTracker(String metricsClassName, String poolName)
{ {
return PoolUtilities.createInstance(metricsClassName, IMetricsTracker.class, poolName); return PoolUtilities.createInstance(metricsClassName, IMetricsTracker.class, poolName);
} }
} }

@ -16,7 +16,6 @@
package com.zaxxer.hikari.metrics; package com.zaxxer.hikari.metrics;
/** /**
* This class does absolutely nothing. * This class does absolutely nothing.
* *
@ -24,22 +23,22 @@ package com.zaxxer.hikari.metrics;
*/ */
public class MetricsTracker implements IMetricsTracker public class MetricsTracker implements IMetricsTracker
{ {
public static final MetricsContext NO_CONTEXT = new MetricsContext(); public static final MetricsContext NO_CONTEXT = new MetricsContext();
public MetricsTracker() public MetricsTracker()
{ {
} }
public MetricsTracker(String poolName) public MetricsTracker(String poolName)
{ {
} }
public MetricsContext recordConnectionRequest(long requestTime) public MetricsContext recordConnectionRequest(long requestTime)
{ {
return NO_CONTEXT; return NO_CONTEXT;
} }
public void recordConnectionUsage(long usageMilleseconds) public void recordConnectionUsage(long usageMilleseconds)
{ {
} }
} }

@ -33,70 +33,62 @@ import com.zaxxer.hikari.HikariConfig;
*/ */
public final class HikariMBeanElf public final class HikariMBeanElf
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(HikariMBeanElf.class); private static final Logger LOGGER = LoggerFactory.getLogger(HikariMBeanElf.class);
private HikariMBeanElf() private HikariMBeanElf()
{ {
// utility class // utility class
} }
/** /**
* Register MBeans for HikariConfig and HikariPool. * Register MBeans for HikariConfig and HikariPool.
* *
* @param configuration a HikariConfig instance * @param configuration a HikariConfig instance
* @param pool a HikariPool instance * @param pool a HikariPool instance
*/ */
public static void registerMBeans(HikariConfig configuration, HikariPool pool) public static void registerMBeans(HikariConfig configuration, HikariPool pool)
{ {
try try {
{ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")");
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")"); ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")");
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")"); if (!mBeanServer.isRegistered(poolConfigName)) {
if (!mBeanServer.isRegistered(poolConfigName)) mBeanServer.registerMBean(configuration, poolConfigName);
{ mBeanServer.registerMBean(pool, poolName);
mBeanServer.registerMBean(configuration, poolConfigName); }
mBeanServer.registerMBean(pool, poolName); else {
} LOGGER.error("You cannot use the same HikariConfig for separate pool instances.");
else }
{ }
LOGGER.error("You cannot use the same HikariConfig for separate pool instances."); catch (Exception e) {
} LOGGER.warn("Unable to register management beans.", e);
} }
catch (Exception e) }
{
LOGGER.warn("Unable to register management beans.", e); /**
} * Unregister MBeans for HikariConfig and HikariPool.
} *
* @param configuration a HikariConfig instance
/** * @param pool a HikariPool instance
* Unregister MBeans for HikariConfig and HikariPool. */
* public static void unregisterMBeans(HikariConfig configuration, HikariPool pool)
* @param configuration a HikariConfig instance {
* @param pool a HikariPool instance try {
*/ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
public static void unregisterMBeans(HikariConfig configuration, HikariPool pool)
{ ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")");
try ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")");
{ if (mBeanServer.isRegistered(poolConfigName)) {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); mBeanServer.unregisterMBean(poolConfigName);
mBeanServer.unregisterMBean(poolName);
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")"); }
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")"); else {
if (mBeanServer.isRegistered(poolConfigName)) LOGGER.error("No registered MBean for {}.", configuration.getPoolName());
{ }
mBeanServer.unregisterMBean(poolConfigName); }
mBeanServer.unregisterMBean(poolName); catch (Exception e) {
} LOGGER.warn("Unable to unregister management beans.", e);
else }
{ }
LOGGER.error("No registered MBean for {}.", configuration.getPoolName());
}
}
catch (Exception e)
{
LOGGER.warn("Unable to unregister management beans.", e);
}
}
} }

File diff suppressed because it is too large Load Diff

@ -23,13 +23,13 @@ package com.zaxxer.hikari.pool;
*/ */
public interface HikariPoolMBean public interface HikariPoolMBean
{ {
int getIdleConnections(); int getIdleConnections();
int getActiveConnections(); int getActiveConnections();
int getTotalConnections(); int getTotalConnections();
int getThreadsAwaitingConnection(); int getThreadsAwaitingConnection();
void closeIdleConnections(); void closeIdleConnections();
} }

@ -25,13 +25,13 @@ import java.sql.CallableStatement;
*/ */
public abstract class CallableStatementProxy extends PreparedStatementProxy implements CallableStatement public abstract class CallableStatementProxy extends PreparedStatementProxy implements CallableStatement
{ {
protected CallableStatementProxy(ConnectionProxy connection, CallableStatement statement) protected CallableStatementProxy(ConnectionProxy connection, CallableStatement statement)
{ {
super(connection, statement); super(connection, statement);
} }
// ********************************************************************** // **********************************************************************
// Overridden java.sql.CallableStatement Methods // Overridden java.sql.CallableStatement Methods
// ********************************************************************** // **********************************************************************
} }

File diff suppressed because it is too large Load Diff

@ -31,76 +31,76 @@ import com.zaxxer.hikari.util.ConcurrentBag.IBagManagable;
*/ */
public interface IHikariConnectionProxy extends Connection, IBagManagable public interface IHikariConnectionProxy extends Connection, IBagManagable
{ {
/** /**
* Catpure the stack and start leak detection. * Catpure the stack and start leak detection.
* *
* @param leakThreshold the number of milliseconds before a leak is reported * @param leakThreshold the number of milliseconds before a leak is reported
* @param houseKeepingTimer the timer to run the leak detection task with * @param houseKeepingTimer the timer to run the leak detection task with
*/ */
void captureStack(long leakThreshold, Timer houseKeepingTimer); void captureStack(long leakThreshold, Timer houseKeepingTimer);
/** /**
* Check if the provided SQLException contains a SQLSTATE that indicates * Check if the provided SQLException contains a SQLSTATE that indicates
* a disconnection from the server. * a disconnection from the server.
* *
* @param sqle the SQLException to check * @param sqle the SQLException to check
*/ */
void checkException(SQLException sqle); void checkException(SQLException sqle);
/** /**
* Get the expiration timestamp of the connection. * Get the expiration timestamp of the connection.
* *
* @return the expiration timestamp, or Long.MAX_VALUE if there is no maximum lifetime * @return the expiration timestamp, or Long.MAX_VALUE if there is no maximum lifetime
*/ */
long getExpirationTime(); long getExpirationTime();
/** /**
* Get the last access timestamp of the connection. * Get the last access timestamp of the connection.
* *
* @return the last access timestamp * @return the last access timestamp
*/ */
long getLastAccess(); long getLastAccess();
/** /**
* Get the timestamp of when the connection was removed from the pool for use. * Get the timestamp of when the connection was removed from the pool for use.
* *
* @return the timestamp the connection started to be used in the most recent request * @return the timestamp the connection started to be used in the most recent request
*/ */
long getLastOpenTime(); long getLastOpenTime();
/** /**
* Return the broken state of the connection. If checkException() detected * Return the broken state of the connection. If checkException() detected
* a broken connection, this method will return true, otherwise false. * a broken connection, this method will return true, otherwise false.
* *
* @return the broken state of the connection * @return the broken state of the connection
*/ */
boolean isBrokenConnection(); boolean isBrokenConnection();
/** /**
* Actually close the underlying delegate Connection. * Actually close the underlying delegate Connection.
* *
* @throws SQLException rethrown from the underlying delegate Connection * @throws SQLException rethrown from the underlying delegate Connection
*/ */
void realClose() throws SQLException; void realClose() throws SQLException;
/** /**
* Reset the delegate Connection back to pristine state. * Reset the delegate Connection back to pristine state.
* *
* @throws SQLException thrown if there is an error resetting any of the state * @throws SQLException thrown if there is an error resetting any of the state
*/ */
void resetConnectionState() throws SQLException; void resetConnectionState() throws SQLException;
/** /**
* Make the Connection available for use again by marking it as not closed. * Make the Connection available for use again by marking it as not closed.
* @param now the current time in milliseconds * @param now the current time in milliseconds
*/ */
void unclose(long now); void unclose(long now);
/** /**
* Called by Statement and its subclasses when they are closed to remove them * Called by Statement and its subclasses when they are closed to remove them
* from the tracking list. * from the tracking list.
* *
* @param statement the Statement to remove from tracking * @param statement the Statement to remove from tracking
*/ */
void untrackStatement(Statement statement); void untrackStatement(Statement statement);
} }

@ -45,191 +45,166 @@ import com.zaxxer.hikari.util.ClassLoaderUtils;
*/ */
public final class JavassistProxyFactory public final class JavassistProxyFactory
{ {
private ClassPool classPool; private ClassPool classPool;
static static {
{ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try {
try Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader());
{
Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader()); JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory();
proxyFactoryFactory.modifyProxyFactory();
JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory(); }
proxyFactoryFactory.modifyProxyFactory(); catch (Exception e) {
} LoggerFactory.getLogger(JavassistProxyFactory.class).error("Fatal exception during proxy generation", e);
catch (Exception e) throw new RuntimeException(e);
{ }
LoggerFactory.getLogger(JavassistProxyFactory.class).error("Fatal exception during proxy generation", e); finally {
throw new RuntimeException(e); Thread.currentThread().setContextClassLoader(contextClassLoader);
} }
finally }
{
Thread.currentThread().setContextClassLoader(contextClassLoader); /**
} * Simply invoking this method causes the initialization of this class. All work
} * by this class is performed in static initialization.
*/
/** public static void initialize()
* Simply invoking this method causes the initialization of this class. All work {
* by this class is performed in static initialization. // no-op
*/ }
public static void initialize()
{ private JavassistProxyFactory()
// no-op {
} classPool = new ClassPool();
classPool.importPackage("java.sql");
private JavassistProxyFactory() classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
{
classPool = new ClassPool(); try {
classPool.importPackage("java.sql"); // Connection is special, it has a checkClosed() call at the beginning
classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader())); String methodBody = "{ checkClosed(); try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }";
generateProxyClass(Connection.class, ConnectionProxy.class, methodBody);
try
{ // Cast is not needed for these
// Connection is special, it has a checkClosed() call at the beginning methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }";
String methodBody = "{ checkClosed(); try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }"; generateProxyClass(Statement.class, StatementProxy.class, methodBody);
generateProxyClass(Connection.class, ConnectionProxy.class, methodBody);
// For these we have to cast the delegate
// Cast is not needed for these methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { checkException(e); throw e;} }";
methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }"; generateProxyClass(PreparedStatement.class, PreparedStatementProxy.class, methodBody);
generateProxyClass(Statement.class, StatementProxy.class, methodBody); generateProxyClass(CallableStatement.class, CallableStatementProxy.class, methodBody);
}
// For these we have to cast the delegate catch (Exception e) {
methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { checkException(e); throw e;} }"; throw new RuntimeException(e);
generateProxyClass(PreparedStatement.class, PreparedStatementProxy.class, methodBody); }
generateProxyClass(CallableStatement.class, CallableStatementProxy.class, methodBody); }
}
catch (Exception e) private void modifyProxyFactory() throws Exception
{ {
throw new RuntimeException(e); String packageName = JavassistProxyFactory.class.getPackage().getName();
} CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.proxy.ProxyFactory");
} for (CtMethod method : proxyCt.getMethods()) {
String methodName = method.getName();
private void modifyProxyFactory() throws Exception if ("getProxyConnection".equals(methodName)) {
{ method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}");
String packageName = JavassistProxyFactory.class.getPackage().getName(); }
CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.proxy.ProxyFactory"); else if ("getProxyStatement".equals(methodName)) {
for (CtMethod method : proxyCt.getMethods()) method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}");
{ }
String methodName = method.getName(); else if ("getProxyPreparedStatement".equals(methodName)) {
if ("getProxyConnection".equals(methodName)) method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
{ }
method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}"); else if ("getProxyCallableStatement".equals(methodName)) {
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}");
}
}
proxyCt.toClass(classPool.getClassLoader(), null);
}
/**
* Generate Javassist Proxy Classes
*/
@SuppressWarnings("unchecked")
private <T> Class<T> generateProxyClass(Class<T> primaryInterface, Class<?> superClass, String methodBody) throws Exception
{
// Make a new class that extends one of the JavaProxy classes (ie. superClass); use the name to XxxJavassistProxy instead of XxxProxy
String superClassName = superClass.getName();
CtClass superClassCt = classPool.getCtClass(superClassName);
CtClass targetCt = classPool.makeClass(superClassName.replace("Proxy", "JavassistProxy"), superClassCt);
targetCt.setModifiers(Modifier.FINAL);
// Make a set of method signatures we inherit implementation for, so we don't generate delegates for these
Set<String> superSigs = new HashSet<String>();
for (CtMethod method : superClassCt.getMethods()) {
if ((method.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT) {
superSigs.add(method.getName() + method.getSignature());
}
}
methodBody = methodBody.replace("cast", primaryInterface.getName());
Set<String> methods = new HashSet<String>();
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(primaryInterface);
for (Class<?> intf : interfaces) {
CtClass intfCt = classPool.getCtClass(intf.getName());
targetCt.addInterface(intfCt);
for (CtMethod intfMethod : intfCt.getDeclaredMethods()) {
final String signature = intfMethod.getName() + intfMethod.getSignature();
// don't generate delegates for methods we override
if (superSigs.contains(signature)) {
continue;
} }
else if ("getProxyStatement".equals(methodName))
{ // Ignore already added methods that come from other interfaces
method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}"); if (methods.contains(signature)) {
} continue;
else if ("getProxyPreparedStatement".equals(methodName))
{
method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
} }
else if ("getProxyCallableStatement".equals(methodName))
{ // Track what methods we've added
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}"); methods.add(signature);
// Clone the method we want to inject into
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
// Generate a method that simply invokes the same method on the delegate
String modifiedBody;
if (isThrowsSqlException(intfMethod)) {
modifiedBody = methodBody.replace("method", method.getName());
} }
} else {
modifiedBody = "{ return ((cast) delegate).method($$); }".replace("method", method.getName()).replace("cast", primaryInterface.getName());
proxyCt.toClass(classPool.getClassLoader(), null);
}
/**
* Generate Javassist Proxy Classes
*/
@SuppressWarnings("unchecked")
private <T> Class<T> generateProxyClass(Class<T> primaryInterface, Class<?> superClass, String methodBody) throws Exception
{
// Make a new class that extends one of the JavaProxy classes (ie. superClass); use the name to XxxJavassistProxy instead of XxxProxy
String superClassName = superClass.getName();
CtClass superClassCt = classPool.getCtClass(superClassName);
CtClass targetCt = classPool.makeClass(superClassName.replace("Proxy", "JavassistProxy"), superClassCt);
targetCt.setModifiers(Modifier.FINAL);
// Make a set of method signatures we inherit implementation for, so we don't generate delegates for these
Set<String> superSigs = new HashSet<String>();
for (CtMethod method : superClassCt.getMethods())
{
if ((method.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT)
{
superSigs.add(method.getName() + method.getSignature());
} }
}
if (method.getReturnType() == CtClass.voidType) {
methodBody = methodBody.replace("cast", primaryInterface.getName()); modifiedBody = modifiedBody.replace("return", "");
Set<String> methods = new HashSet<String>();
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(primaryInterface);
for (Class<?> intf : interfaces)
{
CtClass intfCt = classPool.getCtClass(intf.getName());
targetCt.addInterface(intfCt);
for (CtMethod intfMethod : intfCt.getDeclaredMethods())
{
final String signature = intfMethod.getName() + intfMethod.getSignature();
// don't generate delegates for methods we override
if (superSigs.contains(signature))
{
continue;
}
// Ignore already added methods that come from other interfaces
if (methods.contains(signature))
{
continue;
}
// Track what methods we've added
methods.add(signature);
// Clone the method we want to inject into
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
// Generate a method that simply invokes the same method on the delegate
String modifiedBody;
if (isThrowsSqlException(intfMethod))
{
modifiedBody = methodBody.replace("method", method.getName());
}
else
{
modifiedBody = "{ return ((cast) delegate).method($$); }".replace("method", method.getName()).replace("cast", primaryInterface.getName());
}
if (method.getReturnType() == CtClass.voidType)
{
modifiedBody = modifiedBody.replace("return", "");
}
method.setBody(modifiedBody);
targetCt.addMethod(method);
} }
}
method.setBody(modifiedBody);
if (LoggerFactory.getLogger(getClass()).isDebugEnabled()) targetCt.addMethod(method);
{ }
targetCt.debugWriteFile(System.getProperty("java.io.tmpdir")); }
}
if (LoggerFactory.getLogger(getClass()).isDebugEnabled()) {
return targetCt.toClass(classPool.getClassLoader(), null); targetCt.debugWriteFile(System.getProperty("java.io.tmpdir"));
} }
private boolean isThrowsSqlException(CtMethod method) return targetCt.toClass(classPool.getClassLoader(), null);
{ }
try
{ private boolean isThrowsSqlException(CtMethod method)
for (CtClass clazz : method.getExceptionTypes()) {
{ try {
if (clazz.getSimpleName().equals("SQLException")) for (CtClass clazz : method.getExceptionTypes()) {
{ if (clazz.getSimpleName().equals("SQLException")) {
return true; return true;
}
} }
} }
catch (NotFoundException e) }
{ catch (NotFoundException e) {
// fall thru // fall thru
} }
return false; return false;
} }
} }

@ -25,36 +25,34 @@ import org.slf4j.LoggerFactory;
*/ */
class LeakTask extends TimerTask class LeakTask extends TimerTask
{ {
private final long leakTime; private final long leakTime;
private StackTraceElement[] stackTrace; private StackTraceElement[] stackTrace;
public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold) public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold)
{ {
this.stackTrace = stackTrace; this.stackTrace = stackTrace;
this.leakTime = System.currentTimeMillis() + leakDetectionThreshold; this.leakTime = System.currentTimeMillis() + leakDetectionThreshold;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void run() public void run()
{ {
if (System.currentTimeMillis() > leakTime) if (System.currentTimeMillis() > leakTime) {
{ Exception e = new Exception();
Exception e = new Exception(); e.setStackTrace(stackTrace);
e.setStackTrace(stackTrace); LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e);
LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); stackTrace = null;
stackTrace = null; }
} }
}
@Override @Override
public boolean cancel() public boolean cancel()
{ {
boolean cancelled = super.cancel(); boolean cancelled = super.cancel();
if (cancelled) if (cancelled) {
{ stackTrace = null;
stackTrace = null; }
} return cancelled;
return cancelled; }
}
} }

@ -27,27 +27,25 @@ import java.sql.SQLException;
*/ */
public abstract class PreparedStatementProxy extends StatementProxy implements PreparedStatement public abstract class PreparedStatementProxy extends StatementProxy implements PreparedStatement
{ {
protected PreparedStatementProxy(ConnectionProxy connection, PreparedStatement statement) protected PreparedStatementProxy(ConnectionProxy connection, PreparedStatement statement)
{ {
super(connection, statement); super(connection, statement);
} }
// ********************************************************************** // **********************************************************************
// Overridden java.sql.PreparedStatement Methods // Overridden java.sql.PreparedStatement Methods
// ********************************************************************** // **********************************************************************
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final ResultSet executeQuery() throws SQLException public final ResultSet executeQuery() throws SQLException
{ {
try try {
{ return ((PreparedStatement) delegate).executeQuery();
return ((PreparedStatement) delegate).executeQuery(); }
} catch (SQLException e) {
catch (SQLException e) connection.checkException(e);
{ throw e;
connection.checkException(e); }
throw e; }
}
}
} }

@ -31,44 +31,45 @@ import com.zaxxer.hikari.pool.HikariPool;
*/ */
public final class ProxyFactory public final class ProxyFactory
{ {
private ProxyFactory() private ProxyFactory()
{ {
// unconstructable // unconstructable
} }
/** /**
* Create a proxy for the specified {@link Connection} instance. * Create a proxy for the specified {@link Connection} instance.
* *
* @param pool the {@link HikariPool} that will own this proxy * @param pool the {@link HikariPool} that will own this proxy
* @param connection the {@link Connection} that will be wrapped by this proxy * @param connection the {@link Connection} that will be wrapped by this proxy
* @param maxLifeTime the lifetime of the connection * @param maxLifeTime the lifetime of the connection
* @param defaultIsolationLevel the default transaction isolation level of the underlying {@link Connection} * @param defaultIsolationLevel the default transaction isolation level of the underlying {@link Connection}
* @param defaultAutoCommit the default auto-commit state of the underlying {@link Connection} * @param defaultAutoCommit the default auto-commit state of the underlying {@link Connection}
* @param defaultIReadOnly the default readOnly state of the underlying {@link Connection} * @param defaultIReadOnly the default readOnly state of the underlying {@link Connection}
* @param defaultCatalog the default catalog of the underlying {@link Connection} * @param defaultCatalog the default catalog of the underlying {@link Connection}
* @return a proxy that wraps the specified {@link Connection} * @return a proxy that wraps the specified {@link Connection}
*/ */
public static IHikariConnectionProxy getProxyConnection(HikariPool pool, Connection connection, long maxLifeTime, int defaultIsolationLevel, boolean defaultAutoCommit, boolean defaultIReadOnly, String defaultCatalog) public static IHikariConnectionProxy getProxyConnection(HikariPool pool, Connection connection, long maxLifeTime, int defaultIsolationLevel,
{ boolean defaultAutoCommit, boolean defaultIReadOnly, String defaultCatalog)
// Body is injected by JavassistProxyFactory {
return null; // Body is injected by JavassistProxyFactory
} return null;
}
static Statement getProxyStatement(ConnectionProxy connection, Statement statement) static Statement getProxyStatement(ConnectionProxy connection, Statement statement)
{ {
// Body is injected by JavassistProxyFactory // Body is injected by JavassistProxyFactory
return null; return null;
} }
static CallableStatement getProxyCallableStatement(ConnectionProxy connection, CallableStatement statement) static CallableStatement getProxyCallableStatement(ConnectionProxy connection, CallableStatement statement)
{ {
// Body is injected by JavassistProxyFactory // Body is injected by JavassistProxyFactory
return null; return null;
} }
static PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement) static PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement)
{ {
// Body is injected by JavassistProxyFactory // Body is injected by JavassistProxyFactory
return null; return null;
} }
} }

@ -28,110 +28,100 @@ import java.sql.Statement;
*/ */
public abstract class StatementProxy implements Statement public abstract class StatementProxy implements Statement
{ {
protected final IHikariConnectionProxy connection; protected final IHikariConnectionProxy connection;
protected final Statement delegate; protected final Statement delegate;
private boolean isClosed; private boolean isClosed;
protected StatementProxy(IHikariConnectionProxy connection, Statement statement) protected StatementProxy(IHikariConnectionProxy connection, Statement statement)
{ {
this.connection = connection; this.connection = connection;
this.delegate = statement; this.delegate = statement;
} }
protected final void checkException(SQLException e) protected final void checkException(SQLException e)
{ {
connection.checkException(e); connection.checkException(e);
} }
// ********************************************************************** // **********************************************************************
// Overridden java.sql.Statement Methods // Overridden java.sql.Statement Methods
// ********************************************************************** // **********************************************************************
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final void close() throws SQLException public final void close() throws SQLException
{ {
if (isClosed) if (isClosed) {
{ return;
return; }
}
isClosed = true;
isClosed = true; connection.untrackStatement(this);
connection.untrackStatement(this);
try {
try delegate.close();
{ }
delegate.close(); catch (SQLException e) {
} connection.checkException(e);
catch (SQLException e) throw e;
{ }
connection.checkException(e); }
throw e;
} /** {@inheritDoc} */
} @Override
public final ResultSet executeQuery(String sql) throws SQLException
/** {@inheritDoc} */ {
@Override try {
public final ResultSet executeQuery(String sql) throws SQLException return delegate.executeQuery(sql);
{ }
try catch (SQLException e) {
{ connection.checkException(e);
return delegate.executeQuery(sql); throw e;
} }
catch (SQLException e) }
{
connection.checkException(e); /** {@inheritDoc} */
throw e; @Override
} public final ResultSet getResultSet() throws SQLException
} {
try {
/** {@inheritDoc} */ return delegate.getResultSet();
@Override }
public final ResultSet getResultSet() throws SQLException catch (SQLException e) {
{ connection.checkException(e);
try throw e;
{ }
return delegate.getResultSet(); }
}
catch (SQLException e) /** {@inheritDoc} */
{ @Override
connection.checkException(e); public final ResultSet getGeneratedKeys() throws SQLException
throw e; {
} try {
} return delegate.getGeneratedKeys();
}
/** {@inheritDoc} */ catch (SQLException e) {
@Override connection.checkException(e);
public final ResultSet getGeneratedKeys() throws SQLException throw e;
{ }
try }
{
return delegate.getGeneratedKeys(); /** {@inheritDoc} */
} @Override
catch (SQLException e) public final Connection getConnection() throws SQLException
{ {
connection.checkException(e); return connection;
throw e; }
}
} @Override
@SuppressWarnings("unchecked")
/** {@inheritDoc} */ public final <T> T unwrap(Class<T> iface) throws SQLException
@Override {
public final Connection getConnection() throws SQLException if (iface.isInstance(delegate)) {
{ return (T) delegate;
return connection; }
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
@Override }
@SuppressWarnings("unchecked")
public final <T> T unwrap(Class<T> iface) throws SQLException
{
if (iface.isInstance(delegate))
{
return (T) delegate;
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
}
} }

@ -26,76 +26,68 @@ import java.util.Set;
*/ */
public final class ClassLoaderUtils public final class ClassLoaderUtils
{ {
/** /**
* Get the class loader which can be used to generate proxies without leaking memory. * Get the class loader which can be used to generate proxies without leaking memory.
* @return the class loader which can be used to generate proxies without leaking memory. * @return the class loader which can be used to generate proxies without leaking memory.
*/ */
public static ClassLoader getClassLoader() public static ClassLoader getClassLoader()
{ {
ClassLoader cl = Thread.currentThread().getContextClassLoader(); ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) if (cl != null) {
{ return new CascadingClassLoader(cl);
return new CascadingClassLoader(cl); }
} return ClassLoaderUtils.class.getClassLoader();
return ClassLoaderUtils.class.getClassLoader(); }
}
public static Class<?> loadClass(String className) throws ClassNotFoundException public static Class<?> loadClass(String className) throws ClassNotFoundException
{ {
ClassLoader cl = Thread.currentThread().getContextClassLoader(); ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) if (cl != null) {
{ return new CascadingClassLoader(cl).loadClass(className);
return new CascadingClassLoader(cl).loadClass(className); }
}
return Class.forName(className); return Class.forName(className);
} }
public static Set<Class<?>> getAllInterfaces(Class<?> clazz) public static Set<Class<?>> getAllInterfaces(Class<?> clazz)
{ {
Set<Class<?>> interfaces = new HashSet<Class<?>>(); Set<Class<?>> interfaces = new HashSet<Class<?>>();
for (Class<?> intf : Arrays.asList(clazz.getInterfaces())) for (Class<?> intf : Arrays.asList(clazz.getInterfaces())) {
{ if (intf.getInterfaces().length > 0) {
if (intf.getInterfaces().length > 0) interfaces.addAll(getAllInterfaces(intf));
{ }
interfaces.addAll(getAllInterfaces(intf)); interfaces.add(intf);
} }
interfaces.add(intf); if (clazz.getSuperclass() != null) {
} interfaces.addAll(getAllInterfaces(clazz.getSuperclass()));
if (clazz.getSuperclass() != null) }
{
interfaces.addAll(getAllInterfaces(clazz.getSuperclass()));
}
if (clazz.isInterface()) if (clazz.isInterface()) {
{ interfaces.add(clazz);
interfaces.add(clazz); }
}
return interfaces; return interfaces;
} }
private static class CascadingClassLoader extends ClassLoader private static class CascadingClassLoader extends ClassLoader
{ {
private ClassLoader contextLoader; private ClassLoader contextLoader;
CascadingClassLoader(ClassLoader contextLoader) CascadingClassLoader(ClassLoader contextLoader)
{ {
this.contextLoader = contextLoader; this.contextLoader = contextLoader;
} }
@Override @Override
protected Class<?> findClass(String name) throws ClassNotFoundException protected Class<?> findClass(String name) throws ClassNotFoundException
{ {
try try {
{ return contextLoader.loadClass(name);
return contextLoader.loadClass(name); }
} catch (ClassNotFoundException cnfe) {
catch (ClassNotFoundException cnfe) return CascadingClassLoader.class.getClassLoader().loadClass(name);
{ }
return CascadingClassLoader.class.getClassLoader().loadClass(name); }
} }
}
}
} }

@ -44,323 +44,300 @@ import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
*/ */
public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagManagable> public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagManagable>
{ {
public static final int STATE_NOT_IN_USE = 0; public static final int STATE_NOT_IN_USE = 0;
public static final int STATE_IN_USE = 1; public static final int STATE_IN_USE = 1;
private static final int STATE_REMOVED = -1; private static final int STATE_REMOVED = -1;
private static final int STATE_RESERVED = -2; private static final int STATE_RESERVED = -2;
/** /**
* This interface must be implemented by classes wishing to be managed by * This interface must be implemented by classes wishing to be managed by
* ConcurrentBag. All implementations must be atomic with respect to state. * ConcurrentBag. All implementations must be atomic with respect to state.
* The suggested implementation is via AtomicInteger using the methods * The suggested implementation is via AtomicInteger using the methods
* <code>get()</code> and <code>compareAndSet()</code>. * <code>get()</code> and <code>compareAndSet()</code>.
*/ */
public interface IBagManagable public interface IBagManagable
{ {
int getState(); int getState();
boolean compareAndSetState(int expectedState, int newState); boolean compareAndSetState(int expectedState, int newState);
} }
/** /**
* This interface is implemented by a listener to the bag. The listener * This interface is implemented by a listener to the bag. The listener
* will be informed of when the bag has become empty. The usual course * will be informed of when the bag has become empty. The usual course
* of action by the listener in this case is to attempt to add an item * of action by the listener in this case is to attempt to add an item
* to the bag. * to the bag.
*/ */
public interface IBagStateListener public interface IBagStateListener
{ {
void addBagItem(); void addBagItem();
} }
private ThreadLocal<FastList<WeakReference<T>>> threadList; private ThreadLocal<FastList<WeakReference<T>>> threadList;
private CopyOnWriteArraySet<T> sharedList; private CopyOnWriteArraySet<T> sharedList;
private Synchronizer synchronizer; private Synchronizer synchronizer;
private IBagStateListener listener; private IBagStateListener listener;
/** /**
* Constructor. * Constructor.
*/ */
public ConcurrentBag() public ConcurrentBag()
{ {
this.sharedList = new CopyOnWriteArraySet<T>(); this.sharedList = new CopyOnWriteArraySet<T>();
this.synchronizer = new Synchronizer(); this.synchronizer = new Synchronizer();
this.threadList = new ThreadLocal<FastList<WeakReference<T>>>(); this.threadList = new ThreadLocal<FastList<WeakReference<T>>>();
} }
/** /**
* The method will borrow an IBagManagable from the bag, blocking for the * The method will borrow an IBagManagable from the bag, blocking for the
* specified timeout if none are available. * specified timeout if none are available.
* *
* @param timeout how long to wait before giving up, in units of unit * @param timeout how long to wait before giving up, in units of unit
* @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter * @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter
* @return a borrowed instance from the bag or null if a timeout occurs * @return a borrowed instance from the bag or null if a timeout occurs
* @throws InterruptedException if interrupted while waiting * @throws InterruptedException if interrupted while waiting
*/ */
public T borrow(long timeout, TimeUnit timeUnit) throws InterruptedException public T borrow(long timeout, TimeUnit timeUnit) throws InterruptedException
{ {
// Try the thread-local list first // Try the thread-local list first
FastList<WeakReference<T>> list = threadList.get(); FastList<WeakReference<T>> list = threadList.get();
if (list == null) if (list == null) {
{ list = new FastList<WeakReference<T>>(WeakReference.class);
list = new FastList<WeakReference<T>>(WeakReference.class); threadList.set(list);
threadList.set(list); }
} else {
else for (int i = list.size() - 1; i >= 0; i--) {
{ final WeakReference<T> reference = list.removeLast();
for (int i = list.size() - 1; i >= 0; i--) final T element = reference.get();
{ if (element != null && element.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE)) {
final WeakReference<T> reference = list.removeLast(); return element;
final T element = reference.get();
if (element != null && element.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE))
{
return element;
}
}
}
// Otherwise, scan the shared list ... for maximum of timeout
timeout = timeUnit.toNanos(timeout);
do {
final long startScan = System.nanoTime();
for (T reference : sharedList)
{
if (reference.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE))
{
return reference;
}
}
if (listener != null)
{
listener.addBagItem();
}
synchronizer.tryAcquireSharedNanos(startScan, timeout);
timeout -= (System.nanoTime() - startScan);
} while (timeout > 0);
return null;
}
/**
* This method will return a borrowed object to the bag. Objects
* that are borrowed from the bag but never "requited" will result
* in a memory leak.
*
* @param value the value to return to the bag
* @throws NullPointerException if value is null
* @throws IllegalStateException if the requited value was not borrowed from the bag
*/
public void requite(final T value)
{
if (value == null)
{
throw new NullPointerException("Cannot return a null value to the bag");
}
if (value.compareAndSetState(STATE_IN_USE, STATE_NOT_IN_USE))
{
FastList<WeakReference<T>> list = threadList.get();
if (list == null)
{
list = new FastList<WeakReference<T>>(WeakReference.class);
threadList.set(list);
}
list.add(new WeakReference<T>(value));
synchronizer.releaseShared(System.nanoTime());
}
else
{
throw new IllegalStateException("Value was returned to the bag that was not borrowed: ");
}
}
/**
* Add a new object to the bag for others to borrow.
*
* @param value an object to add to the bag
*/
public void add(final T value)
{
final long addTime = System.nanoTime();
sharedList.add(value);
synchronizer.releaseShared(addTime);
}
/**
* Remove a value from the bag. This method should only be called
* with objects obtained by {@link #borrow(long, TimeUnit)} or {@link #reserve(IBagManagable)}.
* @param value the value to remove
* @throws IllegalStateException if an attempt is made to remove an object
* from the bag that was not borrowed or reserved first
*/
public void remove(T value)
{
if (value.compareAndSetState(STATE_IN_USE, STATE_REMOVED) || value.compareAndSetState(STATE_RESERVED, STATE_REMOVED))
{
if (!sharedList.remove(value))
{
throw new IllegalStateException("Attempt to remove an object from the bag that does not exist");
}
}
else
{
throw new IllegalStateException("Attempt to remove an object from the bag that was not borrowed or reserved");
}
}
/**
* This method provides a "snaphot" in time of the IBagManagable
* items in the bag in the specified state. It does not "lock"
* or reserve items in any way. Call {@link #reserve(IBagManagable)}
* on items in list before performing any action on them.
*
* @param state one of STATE_NOT_IN_USE or STATE_IN_USE
* @return a possibly empty list of objects having the state specified
*/
public List<T> values(int state)
{
ArrayList<T> list = new ArrayList<T>(sharedList.size());
if (state == STATE_IN_USE || state == STATE_NOT_IN_USE)
{
for (T reference : sharedList)
{
if (reference.getState() == state)
{
list.add(reference);
}
}
}
return list;
}
/**
* The method is used to make an item in the bag "unavailable" for
* borrowing. It is primarily used when wanting to operate on items
* returned by the {@link #values(int)} method. Items that are
* reserved can be removed from the bag via {@link #remove(IBagManagable)}
* without the need to unreserve them. Items that are not removed
* from the bag can be make available for borrowing again by calling
* the {@link #unreserve(IBagManagable)} method.
*
* @param value the item to reserve
* @return true if the item was able to be reserved, false otherwise
*/
public boolean reserve(T value)
{
return value.compareAndSetState(STATE_NOT_IN_USE, STATE_RESERVED);
}
/**
* This method is used to make an item reserved via {@link #reserve(IBagManagable)}
* available again for borrowing.
*
* @param value the item to unreserve
*/
public void unreserve(T value)
{
final long checkInTime = System.nanoTime();
if (!value.compareAndSetState(STATE_RESERVED, STATE_NOT_IN_USE))
{
throw new IllegalStateException("Attempt to relinquish an object to the bag that was not reserved");
}
synchronizer.releaseShared(checkInTime);
}
/**
* Add a listener to the bag. There can only be one. If this method is
* called a second time, the original listener will be evicted.
*
* @param listener a listener to the bag
*/
public void addBagStateListener(IBagStateListener listener)
{
this.listener = listener;
}
/**
* Get the number of threads pending (waiting) for an item from the
* bag to become available.
*
* @return the number of threads waiting for items from the bag
*/
public int getPendingQueue()
{
return synchronizer.getQueueLength();
}
public int getCount(int state)
{
int count = 0;
for (T reference : sharedList)
{
if (reference.getState() == state)
{
count++;
}
}
return count;
}
/**
* Get the total number of items in the bag.
*
* @return the number of items in the bag
*/
public int size()
{
return sharedList.size();
}
/**
* Our private synchronizer that handles notify/wait type semantics.
*/
private static class Synchronizer extends AbstractQueuedLongSynchronizer
{
private static final long serialVersionUID = 104753538004341218L;
private static final boolean JAVA7;
static
{
boolean b = false;
try
{
b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class<?>[0]) != null;
} }
catch (Exception e) }
{ }
// Otherwise, scan the shared list ... for maximum of timeout
timeout = timeUnit.toNanos(timeout);
do {
final long startScan = System.nanoTime();
for (T reference : sharedList) {
if (reference.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE)) {
return reference;
} }
}
JAVA7 = b;
} if (listener != null) {
listener.addBagItem();
@Override }
protected long tryAcquireShared(long startScanTime)
{ synchronizer.tryAcquireSharedNanos(startScan, timeout);
return getState() >= startScanTime && !java67hasQueuedPredecessors() ? 1 : -1;
} timeout -= (System.nanoTime() - startScan);
}
/** {@inheritDoc} */ while (timeout > 0);
@Override
protected boolean tryReleaseShared(long updateTime) return null;
{ }
setState(updateTime);
/**
return true; * This method will return a borrowed object to the bag. Objects
} * that are borrowed from the bag but never "requited" will result
* in a memory leak.
private boolean java67hasQueuedPredecessors() *
{ * @param value the value to return to the bag
if (JAVA7) * @throws NullPointerException if value is null
{ * @throws IllegalStateException if the requited value was not borrowed from the bag
return hasQueuedPredecessors(); */
public void requite(final T value)
{
if (value == null) {
throw new NullPointerException("Cannot return a null value to the bag");
}
if (value.compareAndSetState(STATE_IN_USE, STATE_NOT_IN_USE)) {
FastList<WeakReference<T>> list = threadList.get();
if (list == null) {
list = new FastList<WeakReference<T>>(WeakReference.class);
threadList.set(list);
}
list.add(new WeakReference<T>(value));
synchronizer.releaseShared(System.nanoTime());
}
else {
throw new IllegalStateException("Value was returned to the bag that was not borrowed: ");
}
}
/**
* Add a new object to the bag for others to borrow.
*
* @param value an object to add to the bag
*/
public void add(final T value)
{
final long addTime = System.nanoTime();
sharedList.add(value);
synchronizer.releaseShared(addTime);
}
/**
* Remove a value from the bag. This method should only be called
* with objects obtained by {@link #borrow(long, TimeUnit)} or {@link #reserve(IBagManagable)}.
* @param value the value to remove
* @throws IllegalStateException if an attempt is made to remove an object
* from the bag that was not borrowed or reserved first
*/
public void remove(T value)
{
if (value.compareAndSetState(STATE_IN_USE, STATE_REMOVED) || value.compareAndSetState(STATE_RESERVED, STATE_REMOVED)) {
if (!sharedList.remove(value)) {
throw new IllegalStateException("Attempt to remove an object from the bag that does not exist");
}
}
else {
throw new IllegalStateException("Attempt to remove an object from the bag that was not borrowed or reserved");
}
}
/**
* This method provides a "snaphot" in time of the IBagManagable
* items in the bag in the specified state. It does not "lock"
* or reserve items in any way. Call {@link #reserve(IBagManagable)}
* on items in list before performing any action on them.
*
* @param state one of STATE_NOT_IN_USE or STATE_IN_USE
* @return a possibly empty list of objects having the state specified
*/
public List<T> values(int state)
{
ArrayList<T> list = new ArrayList<T>(sharedList.size());
if (state == STATE_IN_USE || state == STATE_NOT_IN_USE) {
for (T reference : sharedList) {
if (reference.getState() == state) {
list.add(reference);
} }
}
return false; }
} return list;
} }
/**
* The method is used to make an item in the bag "unavailable" for
* borrowing. It is primarily used when wanting to operate on items
* returned by the {@link #values(int)} method. Items that are
* reserved can be removed from the bag via {@link #remove(IBagManagable)}
* without the need to unreserve them. Items that are not removed
* from the bag can be make available for borrowing again by calling
* the {@link #unreserve(IBagManagable)} method.
*
* @param value the item to reserve
* @return true if the item was able to be reserved, false otherwise
*/
public boolean reserve(T value)
{
return value.compareAndSetState(STATE_NOT_IN_USE, STATE_RESERVED);
}
/**
* This method is used to make an item reserved via {@link #reserve(IBagManagable)}
* available again for borrowing.
*
* @param value the item to unreserve
*/
public void unreserve(T value)
{
final long checkInTime = System.nanoTime();
if (!value.compareAndSetState(STATE_RESERVED, STATE_NOT_IN_USE)) {
throw new IllegalStateException("Attempt to relinquish an object to the bag that was not reserved");
}
synchronizer.releaseShared(checkInTime);
}
/**
* Add a listener to the bag. There can only be one. If this method is
* called a second time, the original listener will be evicted.
*
* @param listener a listener to the bag
*/
public void addBagStateListener(IBagStateListener listener)
{
this.listener = listener;
}
/**
* Get the number of threads pending (waiting) for an item from the
* bag to become available.
*
* @return the number of threads waiting for items from the bag
*/
public int getPendingQueue()
{
return synchronizer.getQueueLength();
}
public int getCount(int state)
{
int count = 0;
for (T reference : sharedList) {
if (reference.getState() == state) {
count++;
}
}
return count;
}
/**
* Get the total number of items in the bag.
*
* @return the number of items in the bag
*/
public int size()
{
return sharedList.size();
}
/**
* Our private synchronizer that handles notify/wait type semantics.
*/
private static class Synchronizer extends AbstractQueuedLongSynchronizer
{
private static final long serialVersionUID = 104753538004341218L;
private static final boolean JAVA7;
static {
boolean b = false;
try {
b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class<?>[0]) != null;
}
catch (Exception e) {
}
JAVA7 = b;
}
@Override
protected long tryAcquireShared(long startScanTime)
{
return getState() >= startScanTime && !java67hasQueuedPredecessors() ? 1 : -1;
}
/** {@inheritDoc} */
@Override
protected boolean tryReleaseShared(long updateTime)
{
setState(updateTime);
return true;
}
private boolean java67hasQueuedPredecessors()
{
if (JAVA7) {
return hasQueuedPredecessors();
}
return false;
}
}
} }

@ -26,91 +26,86 @@ import javax.sql.DataSource;
public final class DriverDataSource implements DataSource public final class DriverDataSource implements DataSource
{ {
private final String jdbcUrl; private final String jdbcUrl;
private final Properties driverProperties; private final Properties driverProperties;
private PrintWriter logWriter; private PrintWriter logWriter;
public DriverDataSource(String jdbcUrl, Properties properties, String username, String password) public DriverDataSource(String jdbcUrl, Properties properties, String username, String password)
{ {
try try {
{ this.jdbcUrl = jdbcUrl;
this.jdbcUrl = jdbcUrl; this.driverProperties = new Properties(properties);
this.driverProperties = new Properties(properties); if (username != null) {
if (username != null) driverProperties.put("user", driverProperties.getProperty("user", username));
{ }
driverProperties.put("user", driverProperties.getProperty("user", username)); if (password != null) {
} driverProperties.put("password", driverProperties.getProperty("password", password));
if (password != null) }
{
driverProperties.put("password", driverProperties.getProperty("password", password)); if (DriverManager.getDriver(jdbcUrl) == null) {
} throw new IllegalArgumentException("DriverManager was unable to load driver for URL " + jdbcUrl);
}
if (DriverManager.getDriver(jdbcUrl) == null) }
{ catch (SQLException e) {
throw new IllegalArgumentException("DriverManager was unable to load driver for URL " + jdbcUrl); throw new RuntimeException("Unable to get driver for JDBC URL " + jdbcUrl);
} }
} }
catch (SQLException e)
{ @Override
throw new RuntimeException("Unable to get driver for JDBC URL "+ jdbcUrl); public Connection getConnection() throws SQLException
} {
} return DriverManager.getConnection(jdbcUrl, driverProperties);
}
@Override
public Connection getConnection() throws SQLException @Override
{ public Connection getConnection(String username, String password) throws SQLException
return DriverManager.getConnection(jdbcUrl, driverProperties); {
} return getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException @Override
{ public PrintWriter getLogWriter() throws SQLException
return getConnection(); {
} return logWriter;
}
@Override
public PrintWriter getLogWriter() throws SQLException @Override
{ public void setLogWriter(PrintWriter logWriter) throws SQLException
return logWriter; {
} this.logWriter = logWriter;
}
@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException @Override
{ public void setLoginTimeout(int seconds) throws SQLException
this.logWriter = logWriter; {
} DriverManager.setLoginTimeout(seconds);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException @Override
{ public int getLoginTimeout() throws SQLException
DriverManager.setLoginTimeout(seconds); {
} return DriverManager.getLoginTimeout();
}
@Override
public int getLoginTimeout() throws SQLException public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
{ {
return DriverManager.getLoginTimeout(); throw new SQLFeatureNotSupportedException();
} }
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException @Override
{ public <T> T unwrap(Class<T> iface) throws SQLException
throw new SQLFeatureNotSupportedException(); {
} throw new SQLFeatureNotSupportedException();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException @Override
{ public boolean isWrapperFor(Class<?> iface) throws SQLException
throw new SQLFeatureNotSupportedException(); {
} return false;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException public void shutdown()
{ {
return false; }
}
public void shutdown()
{
}
} }

@ -18,7 +18,6 @@ package com.zaxxer.hikari.util;
import java.lang.reflect.Array; import java.lang.reflect.Array;
/** /**
* Fast list without range checking. * Fast list without range checking.
* *
@ -26,124 +25,117 @@ import java.lang.reflect.Array;
*/ */
public final class FastList<T> public final class FastList<T>
{ {
private T[] elementData; private T[] elementData;
private int size; private int size;
/** /**
* Construct a FastList with a default size of 32. * Construct a FastList with a default size of 32.
* @param clazz the Class stored in the collection * @param clazz the Class stored in the collection
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public FastList(Class<?> clazz) public FastList(Class<?> clazz)
{ {
this.elementData = (T[]) Array.newInstance(clazz, 32); this.elementData = (T[]) Array.newInstance(clazz, 32);
} }
/** /**
* Construct a FastList with a specfied size. * Construct a FastList with a specfied size.
* @param clazz the Class stored in the collection * @param clazz the Class stored in the collection
* @param capacity the initial size of the FastList * @param capacity the initial size of the FastList
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public FastList(Class<?> clazz, int capacity) public FastList(Class<?> clazz, int capacity)
{ {
this.elementData = (T[]) Array.newInstance(clazz, capacity); this.elementData = (T[]) Array.newInstance(clazz, capacity);
} }
/** /**
* Add an element to the tail of the FastList. * Add an element to the tail of the FastList.
* *
* @param element the element to add * @param element the element to add
*/ */
public void add(T element) public void add(T element)
{ {
try try {
{ elementData[size++] = element;
elementData[size++] = element; }
} catch (ArrayIndexOutOfBoundsException e) {
catch (ArrayIndexOutOfBoundsException e) // overflow-conscious code
{ final int oldCapacity = elementData.length;
// overflow-conscious code final int newCapacity = oldCapacity << 1;
final int oldCapacity = elementData.length; @SuppressWarnings("unchecked")
final int newCapacity = oldCapacity << 1; final T[] newElementData = (T[]) Array.newInstance(element.getClass(), newCapacity);
@SuppressWarnings("unchecked") System.arraycopy(elementData, 0, newElementData, 0, oldCapacity);
final T[] newElementData = (T[]) Array.newInstance(element.getClass(), newCapacity); newElementData[size - 1] = element;
System.arraycopy(elementData, 0, newElementData, 0, oldCapacity); elementData = newElementData;
newElementData[size - 1] = element; }
elementData = newElementData; }
}
}
/** /**
* Get the element at the specified index. * Get the element at the specified index.
* *
* @param index the index of the element to get * @param index the index of the element to get
* @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid * @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid
*/ */
public T get(int index) public T get(int index)
{ {
return elementData[index]; return elementData[index];
} }
/** /**
* This remove method is most efficient when the element being removed * This remove method is most efficient when the element being removed
* is the last element. Equality is identity based, not equals() based. * is the last element. Equality is identity based, not equals() based.
* Only the first matching element is removed. * Only the first matching element is removed.
* *
* @return the last element of the list * @return the last element of the list
*/ */
public T removeLast() public T removeLast()
{ {
T t = elementData[--size]; T t = elementData[--size];
elementData[size] = null; elementData[size] = null;
return t; return t;
} }
/** /**
* This remove method is most efficient when the element being removed * This remove method is most efficient when the element being removed
* is the last element. Equality is identity based, not equals() based. * is the last element. Equality is identity based, not equals() based.
* Only the first matching element is removed. * Only the first matching element is removed.
* *
* @param element the element to remove * @param element the element to remove
*/ */
public void remove(T element) public void remove(T element)
{ {
for (int index = size - 1; index >= 0; index--) for (int index = size - 1; index >= 0; index--) {
{ if (element == elementData[index]) {
if (element == elementData[index]) final int numMoved = size - index - 1;
{ if (numMoved > 0) {
final int numMoved = size - index - 1; System.arraycopy(elementData, index + 1, elementData, index, numMoved);
if (numMoved > 0)
{
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null;
break;
} }
} elementData[--size] = null;
} break;
}
/** }
* Clear the FastList. }
*/
public void clear()
{
for (int i = 0; i < size; i++)
{
elementData[i] = null;
}
size = 0; /**
} * Clear the FastList.
*/
public void clear()
{
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
}
/** /**
* Get the current number of elements in the FastList. * Get the current number of elements in the FastList.
* *
* @return the number of current elements * @return the number of current elements
*/ */
public int size() public int size()
{ {
return size; return size;
} }
} }

@ -12,131 +12,116 @@ import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
public final class PoolUtilities public final class PoolUtilities
{ {
public static final boolean IS_JAVA7; public static final boolean IS_JAVA7;
static static {
{ boolean b = false;
boolean b = false; try {
try b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class<?>[0]) != null;
{ }
b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class<?>[0]) != null; catch (Exception e) {
} }
catch (Exception e)
{
}
IS_JAVA7 = b; IS_JAVA7 = b;
} }
public static void quietlyCloseConnection(Connection connection) public static void quietlyCloseConnection(Connection connection)
{ {
if (connection != null) if (connection != null) {
{ try {
try connection.close();
{ }
connection.close(); catch (SQLException e) {
} return;
catch (SQLException e) }
{ }
return; }
}
}
}
/** /**
* Get the elapsed time in millisecond between the specified start time and now. * Get the elapsed time in millisecond between the specified start time and now.
* *
* @param start the start time * @param start the start time
* @return the elapsed milliseconds * @return the elapsed milliseconds
*/ */
public static long elapsedTimeMs(long start) public static long elapsedTimeMs(long start)
{ {
return System.currentTimeMillis() - start; return System.currentTimeMillis() - start;
} }
/** /**
* Execute the user-specified init SQL. * Execute the user-specified init SQL.
* *
* @param connection the connection to initialize * @param connection the connection to initialize
* @param sql the SQL to execute * @param sql the SQL to execute
* @throws SQLException throws if the init SQL execution fails * @throws SQLException throws if the init SQL execution fails
*/ */
public static void executeSqlAutoCommit(Connection connection, String sql) throws SQLException public static void executeSqlAutoCommit(Connection connection, String sql) throws SQLException
{ {
if (sql != null) if (sql != null) {
{ connection.setAutoCommit(true);
connection.setAutoCommit(true); Statement statement = connection.createStatement();
Statement statement = connection.createStatement(); try {
try statement.execute(sql);
{ }
statement.execute(sql); finally {
} statement.close();
finally }
{ }
statement.close(); }
}
}
}
public static void quietlySleep(long millis) public static void quietlySleep(long millis)
{ {
try try {
{ Thread.sleep(millis);
Thread.sleep(millis); }
} catch (InterruptedException e) {
catch (InterruptedException e) throw new RuntimeException(e);
{ }
throw new RuntimeException(e); }
}
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T createInstance(String className, Class<T> clazz, Object...args) public static <T> T createInstance(String className, Class<T> clazz, Object... args)
{ {
if (className == null) if (className == null) {
{ return null;
return null; }
}
try try {
{ Class<?> loaded = PoolUtilities.class.getClassLoader().loadClass(className);
Class<?> loaded = PoolUtilities.class.getClassLoader().loadClass(className);
Class<?>[] argClasses = new Class<?>[args.length]; Class<?>[] argClasses = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) for (int i = 0; i < args.length; i++) {
{ argClasses[i] = args[i].getClass();
argClasses[i] = args[i].getClass(); }
}
if (args.length > 0) if (args.length > 0) {
{ Constructor<?> constructor = loaded.getConstructor(argClasses);
Constructor<?> constructor = loaded.getConstructor(argClasses); return (T) constructor.newInstance(args);
return (T) constructor.newInstance(args); }
}
return (T) loaded.newInstance(); return (T) loaded.newInstance();
} }
catch (Exception e) catch (Exception e) {
{ throw new RuntimeException(e);
throw new RuntimeException(e); }
} }
}
public static ThreadPoolExecutor createThreadPoolExecutor(final int queueSize, final String threadName) public static ThreadPoolExecutor createThreadPoolExecutor(final int queueSize, final String threadName)
{ {
ThreadFactory threadFactory = new ThreadFactory() { ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r) public Thread newThread(Runnable r)
{ {
Thread t = new Thread(r, threadName); Thread t = new Thread(r, threadName);
t.setDaemon(true); t.setDaemon(true);
return t; return t;
} }
}; };
int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize); LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 2, TimeUnit.SECONDS, queue, threadFactory, new ThreadPoolExecutor.DiscardPolicy()); ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 2, TimeUnit.SECONDS, queue, threadFactory,
executor.allowCoreThreadTimeOut(true); new ThreadPoolExecutor.DiscardPolicy());
return executor; executor.allowCoreThreadTimeOut(true);
} return executor;
}
} }

@ -38,134 +38,111 @@ import com.zaxxer.hikari.HikariConfig;
*/ */
public final class PropertyBeanSetter public final class PropertyBeanSetter
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertyBeanSetter.class); private static final Logger LOGGER = LoggerFactory.getLogger(PropertyBeanSetter.class);
public static void setTargetFromProperties(Object target, Properties properties) public static void setTargetFromProperties(Object target, Properties properties)
{ {
if (target == null || properties == null) if (target == null || properties == null) {
{ return;
return; }
}
for (Entry<Object, Object> propEntry : properties.entrySet()) for (Entry<Object, Object> propEntry : properties.entrySet()) {
{ String propName = propEntry.getKey().toString();
String propName = propEntry.getKey().toString(); Object propValue = propEntry.getValue();
Object propValue = propEntry.getValue();
if (target instanceof HikariConfig && propName.startsWith("dataSource.")) if (target instanceof HikariConfig && propName.startsWith("dataSource.")) {
{ HikariConfig config = (HikariConfig) target;
HikariConfig config = (HikariConfig) target; config.addDataSourceProperty(propName.substring("dataSource.".length()), propValue);
config.addDataSourceProperty(propName.substring("dataSource.".length()), propValue); }
} else {
else setProperty(target, propName, propValue);
{ }
setProperty(target, propName, propValue); }
} }
}
}
/** /**
* Get the bean-style property names for the specified object. * Get the bean-style property names for the specified object.
* *
* @param targetClass the target object * @param targetClass the target object
* @return a set of property names * @return a set of property names
*/ */
public static Set<String> getPropertyNames(Class<?> targetClass) public static Set<String> getPropertyNames(Class<?> targetClass)
{ {
HashSet<String> set = new HashSet<String>(); HashSet<String> set = new HashSet<String>();
try try {
{ BeanInfo info = Introspector.getBeanInfo(targetClass);
BeanInfo info = Introspector.getBeanInfo(targetClass); for (PropertyDescriptor descr : info.getPropertyDescriptors()) {
for (PropertyDescriptor descr : info.getPropertyDescriptors()) if (!"class".equals(descr.getName())) {
{ set.add(descr.getName());
if (!"class".equals(descr.getName()))
{
set.add(descr.getName());
}
} }
}
return set; return set;
} }
catch (IntrospectionException e) catch (IntrospectionException e) {
{ throw new RuntimeException(e);
throw new RuntimeException(e); }
} }
}
public static Object getProperty(String propName, Object target) public static Object getProperty(String propName, Object target)
{ {
try try {
{ String capitalized = "get" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
String capitalized = "get" + propName.substring(0, 1).toUpperCase() + propName.substring(1); Method method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
catch (Exception e) {
try {
String capitalized = "is" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
Method method = target.getClass().getMethod(capitalized); Method method = target.getClass().getMethod(capitalized);
return method.invoke(target); return method.invoke(target);
} }
catch (Exception e) catch (Exception e2) {
{ return null;
try }
{ }
String capitalized = "is" + propName.substring(0, 1).toUpperCase() + propName.substring(1); }
Method method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
catch (Exception e2)
{
return null;
}
}
}
private static void setProperty(Object target, String propName, Object propValue) private static void setProperty(Object target, String propName, Object propValue)
{ {
String capitalized = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); String capitalized = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
PropertyDescriptor propertyDescriptor; PropertyDescriptor propertyDescriptor;
try try {
{ propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e) {
capitalized = "set" + propName.toUpperCase();
try {
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized); propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
} }
catch (IntrospectionException e) catch (IntrospectionException e1) {
{ LOGGER.error("Property {} is does not exist on target class {}", propName, target.getClass());
capitalized = "set" + propName.toUpperCase();
try
{
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e1)
{
LOGGER.error("Property {} is does not exist on target class {}", propName, target.getClass());
throw new RuntimeException(e);
}
}
try
{
Method writeMethod = propertyDescriptor.getWriteMethod();
Class<?> paramClass = writeMethod.getParameterTypes()[0];
if (paramClass == int.class)
{
writeMethod.invoke(target, Integer.parseInt(propValue.toString()));
}
else if (paramClass == long.class)
{
writeMethod.invoke(target, Long.parseLong(propValue.toString()));
}
else if (paramClass == boolean.class)
{
writeMethod.invoke(target, Boolean.parseBoolean(propValue.toString()));
}
else if (paramClass == String.class)
{
writeMethod.invoke(target, propValue.toString());
}
else
{
writeMethod.invoke(target, propValue);
}
}
catch (Exception e)
{
LOGGER.error("Exception setting property {} on target class {}", propName, target.getClass(), e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
try {
Method writeMethod = propertyDescriptor.getWriteMethod();
Class<?> paramClass = writeMethod.getParameterTypes()[0];
if (paramClass == int.class) {
writeMethod.invoke(target, Integer.parseInt(propValue.toString()));
}
else if (paramClass == long.class) {
writeMethod.invoke(target, Long.parseLong(propValue.toString()));
}
else if (paramClass == boolean.class) {
writeMethod.invoke(target, Boolean.parseBoolean(propValue.toString()));
}
else if (paramClass == String.class) {
writeMethod.invoke(target, propValue.toString());
}
else {
writeMethod.invoke(target, propValue);
}
}
catch (Exception e) {
LOGGER.error("Exception setting property {} on target class {}", propName, target.getClass(), e);
throw new RuntimeException(e);
}
}
} }

Loading…
Cancel
Save