You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/08/10 19:20:57 UTC

[commons-dbcp] branch master updated: Add start, restart methods to BasicDataSource. JIRA: DBCP-559. (#50)

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-dbcp.git


The following commit(s) were added to refs/heads/master by this push:
     new 2ac2a27  Add start, restart methods to BasicDataSource. JIRA: DBCP-559. (#50)
2ac2a27 is described below

commit 2ac2a27e37c4e2beacca6a4df93e09ea364af966
Author: Phil Steitz <ps...@apache.org>
AuthorDate: Mon Aug 10 12:19:41 2020 -0700

    Add start, restart methods to BasicDataSource. JIRA: DBCP-559. (#50)
---
 .../org/apache/commons/dbcp2/BasicDataSource.java  | 51 +++++++++++++++-
 .../commons/dbcp2/BasicDataSourceMXBean.java       | 19 ++++++
 .../apache/commons/dbcp2/TestBasicDataSource.java  | 71 ++++++++++++++++++++++
 3 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java b/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
index cb96d46..d81af67 100644
--- a/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
+++ b/src/main/java/org/apache/commons/dbcp2/BasicDataSource.java
@@ -405,7 +405,7 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
      * </p>
      * <p>
      * Attempts to acquire connections using {@link #getConnection()} after this method has been invoked result in
-     * SQLExceptions.
+     * SQLExceptions.  To reopen a datasource that has been closed using this method, use {@link #start()}.
      * </p>
      * <p>
      * This method is idempotent - i.e., closing an already closed BasicDataSource has no effect and does not generate
@@ -436,6 +436,55 @@ public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBean
     }
 
     /**
+     * Starts the datasource.
+     * <p>
+     * It is not necessary to call this method before using a newly created BasicDataSource instance, but
+     * calling it in that context causes the datasource to be immediately initialized (instead of waiting for
+     * the first {@link #getConnection()} request). Its primary use is to restart and reinitialize a
+     * datasource that has been closed.
+     * <p>
+     * When this method is called after {@link #close()}, connections checked out by clients
+     * before the datasource was stopped do not count in {@link #getMaxTotal()} or {@link #getNumActive()}.
+     * For example, if there are 3 connections checked out by clients when {@link #close()} is invoked and they are
+     * not returned before {@link #start()} is invoked, after this method is called, {@link #getNumActive()} will
+     * return 0.  These connections will be physically closed when they are returned, but they will not count against
+     * the maximum allowed in the newly started datasource.
+     *
+     * @throws SQLException if an error occurs initializing the datasource
+     */
+    @Override
+    public synchronized void start() throws SQLException {
+        closed = false;
+        createDataSource();
+    }
+
+    /**
+     * Restarts the datasource.
+     * <p>
+     * This method calls {@link #close()} and {@link #start()} in sequence within synchronized scope so any
+     * connection requests that come in while the datsource is shutting down will be served by the new pool.
+     * <p>
+     * Idle connections that are stored in the connection pool when this method is invoked are closed, but
+     * connections that are checked out to clients when this method is invoked are not affected. When client
+     * applications subsequently invoke {@link Connection#close()} to return these connections to the pool, the
+     * underlying JDBC connections are closed. These connections do not count in {@link #getMaxTotal()} or
+     * {@link #getNumActive()} after invoking this method. For example, if there are 3 connections checked out by
+     * clients when {@link #restart()} is invoked, after this method is called, {@link #getNumActive()} will
+     * return 0 and up to {@link #getMaxTotal()} + 3 connections may be open until the connections sourced from
+     * the original pool are returned.
+     * <p>
+     * The new connection pool created by this method is initialized with currently set configuration properties.
+     *
+     * @throws SQLException if an error occurs initializing the datasource
+     */
+    @Override
+    public synchronized void restart() throws SQLException {
+        close();
+        start();
+    }
+
+
+    /**
      * Closes the connection pool, silently swallowing any exception that occurs.
      */
     private void closeConnectionPool() {
diff --git a/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java b/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
index 85f8a58..3d3033b 100644
--- a/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
+++ b/src/main/java/org/apache/commons/dbcp2/BasicDataSourceMXBean.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.dbcp2;
 
+import java.sql.SQLException;
+
 /**
  * Defines the methods that will be made available via JMX.
  *
@@ -325,4 +327,21 @@ public interface BasicDataSourceMXBean {
      * @since 2.1
      */
     String[] getDisconnectionSqlCodesAsArray();
+
+    /**
+     * See {@link BasicDataSource#start()}
+     *
+     * @throws SQLException if an error occurs initializing the datasource
+     *
+     * @since 2.8
+     */
+    default void start() throws SQLException {}
+
+    /**
+     * See {@link BasicDataSource#restart()}
+     * @throws SQLException if an error occurs initializing the datasource
+     *
+     * @since 2.8
+     */
+    default void restart() throws SQLException {}
 }
diff --git a/src/test/java/org/apache/commons/dbcp2/TestBasicDataSource.java b/src/test/java/org/apache/commons/dbcp2/TestBasicDataSource.java
index bc63c7a..4be757d 100644
--- a/src/test/java/org/apache/commons/dbcp2/TestBasicDataSource.java
+++ b/src/test/java/org/apache/commons/dbcp2/TestBasicDataSource.java
@@ -951,8 +951,79 @@ public class TestBasicDataSource extends TestConnectionPool {
                     + ds.getMinIdle() + ")");
         }
     }
+
+    @Test
+    public void testStart() throws Exception {
+        ds.setAccessToUnderlyingConnectionAllowed(true);
+        ds.setMaxTotal(2);
+        final DelegatingConnection<?> conn1 = (DelegatingConnection<?>) ds.getConnection();
+        final DelegatingConnection<?> conn2 = (DelegatingConnection<?>) ds.getConnection();
+        final Connection inner1 = conn1.getInnermostDelegate();
+        final Connection inner2 = conn2.getInnermostDelegate();
+        assertFalse(inner2.isClosed());
+        conn2.close();
+        assertFalse(inner2.isClosed());
+        // One active, one idle in the pool
+        ds.close();
+        // Idle connection should be physically closed, checked out unaffected
+        assertFalse(conn1.isClosed());
+        assertTrue(inner2.isClosed());
+        assertEquals(0, ds.getNumIdle());
+
+        // Reopen creates a new pool, so we can have three out
+        ds.start();
+        final Connection conn3 = ds.getConnection();
+        final Connection conn4 = ds.getConnection();
+        conn3.close();
+        conn4.close();
+
+        // Old pool's orphan should get physically closed on return
+        conn1.close();
+        assertTrue(inner1.isClosed());
+    }
+
+    @Test
+    public void testStartInitializes() throws Exception {
+        ds.setInitialSize(2);
+        // Note: if we ever move away from lazy init, next two will fail
+        assertEquals(0, ds.getNumIdle());
+        assertNull(ds.getRegisteredJmxName());
+
+        // Start forces init
+        ds.start();
+        assertEquals(2, ds.getNumIdle());
+        assertNotNull(ds.getRegisteredJmxName());
+    }
+
+    @Test
+    public void testRestart() throws Exception {
+        ds.setMaxTotal(2);
+        ds.setTimeBetweenEvictionRunsMillis(100);
+        ds.setNumTestsPerEvictionRun(2);
+        ds.setMinEvictableIdleTimeMillis(60000);
+        ds.setInitialSize(2);
+        ds.setDefaultCatalog("foo");
+        final Connection conn1 = ds.getConnection();
+        Thread.sleep(200);
+        // Now set some property that will not have effect until restart
+        ds.setDefaultCatalog("bar");
+        ds.setInitialSize(1);
+        // restart will load new properties
+        ds.restart();
+        assertEquals("bar", ds.getDefaultCatalog());
+        assertEquals(1, ds.getInitialSize());
+        ds.getLogWriter();  // side effect is to init
+        assertEquals(0, ds.getNumActive());
+        assertEquals(1, ds.getNumIdle());
+        conn1.close();
+        // verify old pool connection is not returned to pool
+        assertEquals(1, ds.getNumIdle());
+        ds.close();
+    }
 }
 
+
+
 /**
  * TesterDriver that keeps a static count of connection requests.
  */