All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.zaxxer.hikari.HikariDataSource Maven / Gradle / Ivy

The newest version!
/*
 * 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 java.io.Closeable;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Wrapper;
import java.util.HashMap;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.util.DriverDataSource;

/**
 * The HikariCP pooled DataSource.
 *
 * @author Brett Wooldridge
 */
public class HikariDataSource extends HikariConfig implements DataSource, Closeable
{
   private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);

   // We use a concrete HashMap rather than Map to avoid an invokeinterface callsite
   private final HashMap multiPool;

   private volatile boolean isShutdown;

   private final HikariPool fastPathPool;
   private volatile HikariPool pool;

   /**
    * Default constructor.  Setters be 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.
    */
   public HikariDataSource()
   {
      super();
      fastPathPool = null;
      multiPool = new HashMap();
   }

   /**
    * Construct a HikariDataSource with the specified configuration.
    *
    * @param configuration a HikariConfig instance
    */
   public HikariDataSource(HikariConfig configuration)
   {
      configuration.validate();
      configuration.copyState(this);
      multiPool = new HashMap();

      LOGGER.info("HikariCP pool {} is starting.", configuration.getPoolName());
      pool = fastPathPool = new HikariPool(this);
      multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
   }

   /** {@inheritDoc} */
   @Override
   public Connection getConnection() throws SQLException
   {
      if (isShutdown) {
         throw new SQLException("Pool has been shutdown");
      }

      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("HikariCP pool {} is starting.", getPoolName());
               pool = result = new HikariPool(this);
               multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
            }
         }
      }

      return result.getConnection();
   }

   /** {@inheritDoc} */
   @Override
   @Deprecated
   public Connection getConnection(String username, String password) throws SQLException
   {
      if (isShutdown) {
         throw new SQLException("Pool has been shutdown");
      }

      final MultiPoolKey key = new MultiPoolKey(username, password);

      HikariPool hikariPool;
      synchronized (multiPool) {
         hikariPool = multiPool.get(key);
         if (hikariPool == null) {
            hikariPool = new HikariPool(this, username, password);
            multiPool.put(key, hikariPool);
         }
      }

      return hikariPool.getConnection();
   }

   /** {@inheritDoc} */
   @Override
   public PrintWriter getLogWriter() throws SQLException
   {
      return (pool.getDataSource() != null ? pool.getDataSource().getLogWriter() : null);
   }

   /** {@inheritDoc} */
   @Override
   public void setLogWriter(PrintWriter out) throws SQLException
   {
      if (pool.getDataSource() != null) {
         pool.getDataSource().setLogWriter(out);
      }
   }

   /** {@inheritDoc} */
   @Override
   public void setLoginTimeout(int seconds) throws SQLException
   {
      for (HikariPool hikariPool : multiPool.values()) {
         hikariPool.getDataSource().setLoginTimeout(seconds);
      }
   }

   /** {@inheritDoc} */
   @Override
   public int getLoginTimeout() throws SQLException
   {
      HikariPool hikariPool = multiPool.values().iterator().next();
      if (hikariPool != null) {
         return hikariPool.getDataSource().getLoginTimeout();
      }

      return 0;
   }

   /** {@inheritDoc} */
   public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
   {
      throw new SQLFeatureNotSupportedException();
   }

   /** {@inheritDoc} */
   @Override
   @SuppressWarnings("unchecked")
   public  T unwrap(Class iface) throws SQLException
   {
      if (iface.isInstance(this)) {
         return (T) this;
      }

      if (pool != null) {
         if (iface.isInstance(pool.getDataSource())) {
            return (T) pool.getDataSource();
         }

         if (pool.getDataSource() instanceof Wrapper) {
            return (T) pool.getDataSource().unwrap(iface);
         }
      }

      throw new SQLException("Wrapped DataSource is not an instance of " + iface);
   }

   /** {@inheritDoc} */
   @Override
   public boolean isWrapperFor(Class iface) throws SQLException
   {
      if (iface.isInstance(this)) {
         return true;
      }
      
      if (pool != null) {
         if (iface.isInstance(pool.getDataSource())) {
            return true;
         }

         if (pool.getDataSource() instanceof Wrapper) {
            return pool.getDataSource().isWrapperFor(iface);
         }
      }

      return false;
   }

   /** {@inheritDoc} */
   @Override
   public void setMetricRegistry(Object metricRegistry)
   {
      boolean isAlreadySet = getMetricRegistry() != null;
      super.setMetricRegistry(metricRegistry);

      if (fastPathPool != null || pool != null) {
         if (isAlreadySet) {
            throw new IllegalStateException("MetricRegistry can only be set one time");
         }
         else {
            pool.setMetricRegistry((MetricRegistry) super.getMetricRegistry());
         }
      }
   }

   /** {@inheritDoc} */
   @Override
   public void setHealthCheckRegistry(Object healthCheckRegistry)
   {
      boolean isAlreadySet = getHealthCheckRegistry() != null;
      super.setHealthCheckRegistry(healthCheckRegistry);

      if (fastPathPool != null || pool != null) {
         if (isAlreadySet) {
            throw new IllegalStateException("HealthCheckRegistry can only be set one time");
         }
         else {
            pool.setHealthCheckRegistry((HealthCheckRegistry) super.getHealthCheckRegistry());
         }
      }
   }

   /**
    * 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.
    *
    * @param connection the connection to evict from the pool
    */
   public void evictConnection(Connection connection)
   {
      if (!isShutdown && pool != null && connection instanceof IHikariConnectionProxy) {
         pool.evictConnection((IHikariConnectionProxy) connection);
      }
   }

   /**
    * Suspend allocation of connections from the pool.  All callers to getConnection()
    * will block indefinitely until resumePool() is called.
    */
   public void suspendPool()
   {
      if (!isShutdown && pool != null) {
         pool.suspendPool();
      }
   }

   /**
    * Resume allocation of connections from the pool.
    */
   public void resumePool()
   {
      if (!isShutdown && pool != null) {
         pool.resumePool();
      }
   }

   /**
    * close() and shutdown() are synonymous.
    */
   public void close()
   {
      shutdown();
   }

   /**
    * Shutdown the DataSource and its associated pool.
    */
   public void shutdown()
   {
      if (isShutdown) {
         return;
      }

      isShutdown = true;

      if (fastPathPool != null) {
         shutdownHelper(fastPathPool);
      }

      for (HikariPool hikariPool : multiPool.values()) {
         shutdownHelper(hikariPool);
      }
   }

   /** {@inheritDoc} */
   @Override
   public String toString()
   {
      return String.format("HikariDataSource (%s)", pool);
   }

   private void shutdownHelper(HikariPool hPool)
   {
      try {
         hPool.shutdown();
      }
      catch (InterruptedException e) {
         LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e);
      }

      if (hPool.getDataSource() instanceof DriverDataSource) {
         ((DriverDataSource) hPool.getDataSource()).shutdown();
      }
   }

   private static class MultiPoolKey
   {
      private String username;
      private String password;

      MultiPoolKey(String username, String password)
      {
         this.username = username;
         this.password = password;
      }

      @Override
      public int hashCode()
      {
         return (password == null ? 0 : password.hashCode());
      }

      @Override
      public boolean equals(Object obj)
      {
         MultiPoolKey otherKey = ((MultiPoolKey) obj);
         if (username != null && !username.equals(otherKey.username)) {
            return false;
         }
         else if (username == null && otherKey.username != null) {
            return false;
         }
         else if (password != null && !password.equals(otherKey.password)) {
            return false;
         }
         else if (password == null && otherKey.password != null) {
            return false;
         }

         return true;
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy