PoolingDriver.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.dbcp2;

  18. import java.sql.Connection;
  19. import java.sql.Driver;
  20. import java.sql.DriverManager;
  21. import java.sql.DriverPropertyInfo;
  22. import java.sql.SQLException;
  23. import java.sql.SQLFeatureNotSupportedException;
  24. import java.util.HashMap;
  25. import java.util.NoSuchElementException;
  26. import java.util.Properties;
  27. import java.util.logging.Logger;

  28. import org.apache.commons.pool2.ObjectPool;

  29. /**
  30.  * A {@link Driver} implementation that obtains {@link Connection}s from a registered {@link ObjectPool}.
  31.  *
  32.  * @since 2.0
  33.  */
  34. public class PoolingDriver implements Driver {

  35.     /**
  36.      * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
  37.      *
  38.      * @since 2.0
  39.      */
  40.     private final class PoolGuardConnectionWrapper extends DelegatingConnection<Connection> {

  41.         private final ObjectPool<? extends Connection> pool;

  42.         PoolGuardConnectionWrapper(final ObjectPool<? extends Connection> pool, final Connection delegate) {
  43.             super(delegate);
  44.             this.pool = pool;
  45.         }

  46.         /**
  47.          * @see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
  48.          */
  49.         @Override
  50.         public Connection getDelegate() {
  51.             if (isAccessToUnderlyingConnectionAllowed()) {
  52.                 return super.getDelegate();
  53.             }
  54.             return null;
  55.         }

  56.         /**
  57.          * @see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
  58.          */
  59.         @Override
  60.         public Connection getInnermostDelegate() {
  61.             if (isAccessToUnderlyingConnectionAllowed()) {
  62.                 return super.getInnermostDelegate();
  63.             }
  64.             return null;
  65.         }
  66.     }

  67.     private static final DriverPropertyInfo[] EMPTY_DRIVER_PROPERTY_INFO_ARRAY = {};

  68.     /* Register myself with the {@link DriverManager}. */
  69.     static {
  70.         try {
  71.             DriverManager.registerDriver(new PoolingDriver());
  72.         } catch (final Exception ignored) {
  73.             // Ignored
  74.         }
  75.     }

  76.     /** The map of registered pools. */
  77.     protected static final HashMap<String, ObjectPool<? extends Connection>> pools = new HashMap<>();

  78.     /**
  79.      * The Apache Commons connection string prefix {@value}.
  80.      */
  81.     public static final String URL_PREFIX = "jdbc:apache:commons:dbcp:";

  82.     /**
  83.      * The String length of {@link #URL_PREFIX}.
  84.      */
  85.     protected static final int URL_PREFIX_LEN = URL_PREFIX.length();

  86.     /**
  87.      * Major version number.
  88.      */
  89.     protected static final int MAJOR_VERSION = 1;

  90.     /**
  91.      * Minor version number.
  92.      */
  93.     protected static final int MINOR_VERSION = 0;

  94.     /** Controls access to the underlying connection */
  95.     private final boolean accessToUnderlyingConnectionAllowed;

  96.     /**
  97.      * Constructs a new driver with {@code accessToUnderlyingConnectionAllowed} enabled.
  98.      */
  99.     public PoolingDriver() {
  100.         this(true);
  101.     }

  102.     /**
  103.      * For unit testing purposes.
  104.      *
  105.      * @param accessToUnderlyingConnectionAllowed
  106.      *            Do {@link DelegatingConnection}s created by this driver permit access to the delegate?
  107.      */
  108.     protected PoolingDriver(final boolean accessToUnderlyingConnectionAllowed) {
  109.         this.accessToUnderlyingConnectionAllowed = accessToUnderlyingConnectionAllowed;
  110.     }

  111.     @Override
  112.     public boolean acceptsURL(final String url) throws SQLException {
  113.         return url != null && url.startsWith(URL_PREFIX);
  114.     }

  115.     /**
  116.      * Closes a named pool.
  117.      *
  118.      * @param name
  119.      *            The pool name.
  120.      * @throws SQLException
  121.      *             Thrown when a problem is caught closing the pool.
  122.      */
  123.     public synchronized void closePool(final String name) throws SQLException {
  124.         @SuppressWarnings("resource")
  125.         final ObjectPool<? extends Connection> pool = pools.get(name);
  126.         if (pool != null) {
  127.             pools.remove(name);
  128.             try {
  129.                 pool.close();
  130.             } catch (final Exception e) {
  131.                 throw new SQLException("Error closing pool " + name, e);
  132.             }
  133.         }
  134.     }

  135.     @Override
  136.     public Connection connect(final String url, final Properties info) throws SQLException {
  137.         if (acceptsURL(url)) {
  138.             final ObjectPool<? extends Connection> pool = getConnectionPool(url.substring(URL_PREFIX_LEN));
  139.             try {
  140.                 final Connection conn = pool.borrowObject();
  141.                 if (conn == null) {
  142.                     return null;
  143.                 }
  144.                 return new PoolGuardConnectionWrapper(pool, conn);
  145.             } catch (final NoSuchElementException e) {
  146.                 throw new SQLException("Cannot get a connection, pool error: " + e.getMessage(), e);
  147.             } catch (final SQLException | RuntimeException e) {
  148.                 throw e;
  149.             } catch (final Exception e) {
  150.                 throw new SQLException("Cannot get a connection, general error: " + e.getMessage(), e);
  151.             }
  152.         }
  153.         return null;
  154.     }

  155.     /**
  156.      * Gets the connection pool for the given name.
  157.      *
  158.      * @param name
  159.      *            The pool name
  160.      * @return The pool
  161.      * @throws SQLException
  162.      *             Thrown when the named pool is not registered.
  163.      */
  164.     public synchronized ObjectPool<? extends Connection> getConnectionPool(final String name) throws SQLException {
  165.         final ObjectPool<? extends Connection> pool = pools.get(name);
  166.         if (null == pool) {
  167.             throw new SQLException("Pool not registered: " + name);
  168.         }
  169.         return pool;
  170.     }

  171.     @Override
  172.     public int getMajorVersion() {
  173.         return MAJOR_VERSION;
  174.     }

  175.     @Override
  176.     public int getMinorVersion() {
  177.         return MINOR_VERSION;
  178.     }

  179.     @Override
  180.     public Logger getParentLogger() throws SQLFeatureNotSupportedException {
  181.         throw new SQLFeatureNotSupportedException();
  182.     }

  183.     /**
  184.      * Gets the pool names.
  185.      *
  186.      * @return the pool names.
  187.      */
  188.     public synchronized String[] getPoolNames() {
  189.         return pools.keySet().toArray(Utils.EMPTY_STRING_ARRAY);
  190.     }

  191.     @Override
  192.     public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) {
  193.         return EMPTY_DRIVER_PROPERTY_INFO_ARRAY;
  194.     }
  195.     /**
  196.      * Invalidates the given connection.
  197.      *
  198.      * @param conn
  199.      *            connection to invalidate
  200.      * @throws SQLException
  201.      *             if the connection is not a {@code PoolGuardConnectionWrapper} or an error occurs invalidating
  202.      *             the connection
  203.      */
  204.     public void invalidateConnection(final Connection conn) throws SQLException {
  205.         if (!(conn instanceof PoolGuardConnectionWrapper)) {
  206.             throw new SQLException("Invalid connection class");
  207.         }
  208.         final PoolGuardConnectionWrapper pgconn = (PoolGuardConnectionWrapper) conn;
  209.         @SuppressWarnings("unchecked")
  210.         final ObjectPool<Connection> pool = (ObjectPool<Connection>) pgconn.pool;
  211.         try {
  212.             pool.invalidateObject(pgconn.getDelegateInternal());
  213.         } catch (final Exception ignored) {
  214.             // Ignored.
  215.         }
  216.     }

  217.     /**
  218.      * Returns the value of the accessToUnderlyingConnectionAllowed property.
  219.      *
  220.      * @return true if access to the underlying is allowed, false otherwise.
  221.      */
  222.     protected boolean isAccessToUnderlyingConnectionAllowed() {
  223.         return accessToUnderlyingConnectionAllowed;
  224.     }

  225.     @Override
  226.     public boolean jdbcCompliant() {
  227.         return true;
  228.     }

  229.     /**
  230.      * Registers a named pool.
  231.      *
  232.      * @param name
  233.      *            The pool name.
  234.      * @param pool
  235.      *            The pool.
  236.      */
  237.     public synchronized void registerPool(final String name, final ObjectPool<? extends Connection> pool) {
  238.         pools.put(name, pool);
  239.     }
  240. }