/*
 * Copyright (C) 2013 Brett Wooldridge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zaxxer.hikari;

import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.Closeable;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.zaxxer.hikari.pool.HikariPool.POOL_NORMAL;

The HikariCP pooled DataSource.
Author:Brett Wooldridge
/** * The HikariCP pooled DataSource. * * @author Brett Wooldridge */
public class HikariDataSource extends HikariConfig implements DataSource, Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class); private final AtomicBoolean isShutdown = new AtomicBoolean(); private final HikariPool fastPathPool; private volatile HikariPool pool;
Default constructor. Setters are used to configure the pool. Using this constructor vs. HikariDataSource(HikariConfig) will result in getConnection() performance that is slightly lower due to lazy initialization checks. The first call to getConnection() starts the pool. Once the pool is started, the configuration is "sealed" and no further configuration changes are possible -- except via HikariConfigMXBean methods.
/** * Default constructor. Setters are used to configure the pool. Using * this constructor vs. {@link #HikariDataSource(HikariConfig)} will * result in {@link #getConnection()} performance that is slightly lower * due to lazy initialization checks. * * The first call to {@link #getConnection()} starts the pool. Once the pool * is started, the configuration is "sealed" and no further configuration * changes are possible -- except via {@link HikariConfigMXBean} methods. */
public HikariDataSource() { super(); fastPathPool = null; }
Construct a HikariDataSource with the specified configuration. The HikariConfig is copied and the pool is started by invoking this constructor. The HikariConfig can be modified without affecting the HikariDataSource and used to initialize another HikariDataSource instance.
Params:
  • configuration – a HikariConfig instance
/** * Construct a HikariDataSource with the specified configuration. The * {@link HikariConfig} is copied and the pool is started by invoking this * constructor. * * The {@link HikariConfig} can be modified without affecting the HikariDataSource * and used to initialize another HikariDataSource instance. * * @param configuration a HikariConfig instance */
public HikariDataSource(HikariConfig configuration) { configuration.validate(); configuration.copyStateTo(this); LOGGER.info("{} - Starting...", configuration.getPoolName()); pool = fastPathPool = new HikariPool(this); LOGGER.info("{} - Start completed.", configuration.getPoolName()); this.seal(); } // *********************************************************************** // DataSource methods // ***********************************************************************
{@inheritDoc}
/** {@inheritDoc} */
@Override public Connection getConnection() throws SQLException { if (isClosed()) { throw new SQLException("HikariDataSource " + this + " has been closed."); } if (fastPathPool != null) { return fastPathPool.getConnection(); } // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java HikariPool result = pool; if (result == null) { synchronized (this) { result = pool; if (result == null) { validate(); LOGGER.info("{} - Starting...", getPoolName()); try { pool = result = new HikariPool(this); this.seal(); } catch (PoolInitializationException pie) { if (pie.getCause() instanceof SQLException) { throw (SQLException) pie.getCause(); } else { throw pie; } } LOGGER.info("{} - Start completed.", getPoolName()); } } } return result.getConnection(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public Connection getConnection(String username, String password) throws SQLException { throw new SQLFeatureNotSupportedException(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public PrintWriter getLogWriter() throws SQLException { HikariPool p = pool; return (p != null ? p.getUnwrappedDataSource().getLogWriter() : null); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setLogWriter(PrintWriter out) throws SQLException { HikariPool p = pool; if (p != null) { p.getUnwrappedDataSource().setLogWriter(out); } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setLoginTimeout(int seconds) throws SQLException { HikariPool p = pool; if (p != null) { p.getUnwrappedDataSource().setLoginTimeout(seconds); } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public int getLoginTimeout() throws SQLException { HikariPool p = pool; return (p != null ? p.getUnwrappedDataSource().getLoginTimeout() : 0); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override @SuppressWarnings("unchecked") public <T> T unwrap(Class<T> iface) throws SQLException { if (iface.isInstance(this)) { return (T) this; } HikariPool p = pool; if (p != null) { final DataSource unwrappedDataSource = p.getUnwrappedDataSource(); if (iface.isInstance(unwrappedDataSource)) { return (T) unwrappedDataSource; } if (unwrappedDataSource != null) { return unwrappedDataSource.unwrap(iface); } } throw new SQLException("Wrapped DataSource is not an instance of " + iface); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public boolean isWrapperFor(Class<?> iface) throws SQLException { if (iface.isInstance(this)) { return true; } HikariPool p = pool; if (p != null) { final DataSource unwrappedDataSource = p.getUnwrappedDataSource(); if (iface.isInstance(unwrappedDataSource)) { return true; } if (unwrappedDataSource != null) { return unwrappedDataSource.isWrapperFor(iface); } } return false; } // *********************************************************************** // HikariConfigMXBean methods // ***********************************************************************
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setMetricRegistry(Object metricRegistry) { boolean isAlreadySet = getMetricRegistry() != null; super.setMetricRegistry(metricRegistry); HikariPool p = pool; if (p != null) { if (isAlreadySet) { throw new IllegalStateException("MetricRegistry can only be set one time"); } else { p.setMetricRegistry(super.getMetricRegistry()); } } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setMetricsTrackerFactory(MetricsTrackerFactory metricsTrackerFactory) { boolean isAlreadySet = getMetricsTrackerFactory() != null; super.setMetricsTrackerFactory(metricsTrackerFactory); HikariPool p = pool; if (p != null) { if (isAlreadySet) { throw new IllegalStateException("MetricsTrackerFactory can only be set one time"); } else { p.setMetricsTrackerFactory(super.getMetricsTrackerFactory()); } } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setHealthCheckRegistry(Object healthCheckRegistry) { boolean isAlreadySet = getHealthCheckRegistry() != null; super.setHealthCheckRegistry(healthCheckRegistry); HikariPool p = pool; if (p != null) { if (isAlreadySet) { throw new IllegalStateException("HealthCheckRegistry can only be set one time"); } else { p.setHealthCheckRegistry(super.getHealthCheckRegistry()); } } } // *********************************************************************** // HikariCP-specific methods // ***********************************************************************
Returns true if the pool as been started and is not suspended or shutdown.
Returns:true if the pool as been started and is not suspended or shutdown.
/** * Returns {@code true} if the pool as been started and is not suspended or shutdown. * * @return {@code true} if the pool as been started and is not suspended or shutdown. */
public boolean isRunning() { return pool != null && pool.poolState == POOL_NORMAL; }
Get the HikariPoolMXBean for this HikariDataSource instance. If this method is called on a HikariDataSource that has been constructed without a HikariConfig instance, and before an initial call to #getConnection(), the return value will be null.
Returns:the HikariPoolMXBean instance, or null.
/** * Get the {@code HikariPoolMXBean} for this HikariDataSource instance. If this method is called on * a {@code HikariDataSource} that has been constructed without a {@code HikariConfig} instance, * and before an initial call to {@code #getConnection()}, the return value will be {@code null}. * * @return the {@code HikariPoolMXBean} instance, or {@code null}. */
public HikariPoolMXBean getHikariPoolMXBean() { return pool; }
Get the HikariConfigMXBean for this HikariDataSource instance.
Returns:the HikariConfigMXBean instance.
/** * Get the {@code HikariConfigMXBean} for this HikariDataSource instance. * * @return the {@code HikariConfigMXBean} instance. */
public HikariConfigMXBean getHikariConfigMXBean() { return this; }
Evict a connection from the pool. If the connection has already been closed (returned to the pool) this may result in a "soft" eviction; the connection will be evicted sometime in the future if it is currently in use. If the connection has not been closed, the eviction is immediate.
Params:
  • connection – the connection to evict from the pool
/** * Evict a connection from the pool. If the connection has already been closed (returned to the pool) * this may result in a "soft" eviction; the connection will be evicted sometime in the future if it is * currently in use. If the connection has not been closed, the eviction is immediate. * * @param connection the connection to evict from the pool */
public void evictConnection(Connection connection) { HikariPool p; if (!isClosed() && (p = pool) != null && connection.getClass().getName().startsWith("com.zaxxer.hikari")) { p.evictConnection(connection); } }
Shutdown the DataSource and its associated pool.
/** * Shutdown the DataSource and its associated pool. */
@Override public void close() { if (isShutdown.getAndSet(true)) { return; } HikariPool p = pool; if (p != null) { try { LOGGER.info("{} - Shutdown initiated...", getPoolName()); p.shutdown(); LOGGER.info("{} - Shutdown completed.", getPoolName()); } catch (InterruptedException e) { LOGGER.warn("{} - Interrupted during closing", getPoolName(), e); Thread.currentThread().interrupt(); } } }
Determine whether the HikariDataSource has been closed.
Returns:true if the HikariDataSource has been closed, false otherwise
/** * Determine whether the HikariDataSource has been closed. * * @return true if the HikariDataSource has been closed, false otherwise */
public boolean isClosed() { return isShutdown.get(); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public String toString() { return "HikariDataSource (" + pool + ")"; } }