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
commit dc2c59ac652e1923b2e7f163ebacc3c1315e5314 Author: Gary D. Gregory <garydgreg...@gmail.com> AuthorDate: Mon May 26 11:36:35 2025 -0400 Add org.apache.commons.dbcp2.datasources.PooledConnectionManager.setPassword(char[]) --- src/changes/changes.xml | 1 + .../dbcp2/datasources/CPDSConnectionFactory.java | 1 + .../dbcp2/datasources/InstanceKeyDataSource.java | 2 +- .../datasources/KeyedCPDSConnectionFactory.java | 8 +++ .../dbcp2/datasources/PooledConnectionManager.java | 29 ++++----- .../datasources/PooledConnectionManagerTest.java | 70 ++++++++++++++++++++++ .../datasources/TestCPDSConnectionFactory.java | 10 +++- 7 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4234a424..85a9cdf5 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -109,6 +109,7 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs [ERROR] Medium: Shared primitive variable "poolStatements" in one thread may not yield the value of the most recent write from another thread [org.apache.commons.dbcp2.PoolableConnectionFactory] AT_STALE_THREAD_WRITE_OF_PRIMITIVE.</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs [ERROR] Medium: Shared primitive variable "rollbackOnReturn" in one thread may not yield the value of the most recent write from another thread [org.apache.commons.dbcp2.PoolableConnectionFactory] AT_STALE_THREAD_WRITE_OF_PRIMITIVE.</action> <!-- ADD --> + <action type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.dbcp2.datasources.PooledConnectionManager.setPassword(char[]).</action> <!-- UPDATE --> <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 78 to 81.</action> <action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-pool2 from 2.12.0 to 2.12.1 #474.</action> diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java index eae72bc4..276e1546 100644 --- a/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java +++ b/src/main/java/org/apache/commons/dbcp2/datasources/CPDSConnectionFactory.java @@ -228,6 +228,7 @@ final class CPDSConnectionFactory extends AbstractConnectionFactory * @param userPassword * new password */ + @Override public synchronized void setPassword(final char[] userPassword) { this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword); } diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java index d3bf08eb..73c6b814 100644 --- a/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java +++ b/src/main/java/org/apache/commons/dbcp2/datasources/InstanceKeyDataSource.java @@ -271,7 +271,7 @@ public abstract class InstanceKeyDataSource implements DataSource, Referenceable // Destroy and remove from pool manager.invalidate(info.getPooledConnection()); // Reset the password on the factory if using CPDSConnectionFactory - manager.setPassword(upkey.getPassword()); + manager.setPassword(upkey.getPasswordCharArray()); info = null; for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return try { diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java b/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java index 1f2e6373..9464aca2 100644 --- a/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java +++ b/src/main/java/org/apache/commons/dbcp2/datasources/KeyedCPDSConnectionFactory.java @@ -212,6 +212,14 @@ final class KeyedCPDSConnectionFactory extends AbstractConnectionFactory validateLifetime(pooledObject); } + /** + * Does nothing. This factory does not cache user credentials. + */ + @Override + public void setPassword(final char[] password) { + // Does nothing. This factory does not cache user credentials. + } + /** * Does nothing. This factory does not cache user credentials. */ diff --git a/src/main/java/org/apache/commons/dbcp2/datasources/PooledConnectionManager.java b/src/main/java/org/apache/commons/dbcp2/datasources/PooledConnectionManager.java index 2dfde18f..b7f288cf 100644 --- a/src/main/java/org/apache/commons/dbcp2/datasources/PooledConnectionManager.java +++ b/src/main/java/org/apache/commons/dbcp2/datasources/PooledConnectionManager.java @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.commons.dbcp2.datasources; import java.sql.SQLException; @@ -31,9 +32,9 @@ interface PooledConnectionManager { * Closes the connection pool associated with the given user. * * @param userName - * user name + * user name. * @throws SQLException - * if an error occurs closing idle connections in the pool + * if an error occurs closing idle connections in the pool. */ void closePool(String userName) throws SQLException; @@ -41,27 +42,27 @@ interface PooledConnectionManager { * Closes the PooledConnection and remove it from the connection pool to which it belongs, adjusting pool counters. * * @param pc - * PooledConnection to be invalidated + * PooledConnection to be invalidated. * @throws SQLException - * if an SQL error occurs closing the connection + * if an SQL error occurs closing the connection. */ void invalidate(PooledConnection pc) throws SQLException; -// /** -// * Sets the database password used when creating connections. -// * -// * @param password password used when authenticating to the database -// * @since 2.10.0 -// */ -// default void setPassword(char[] password) { -// setPassword(String.copyValueOf(password)); -// } + /** + * Sets the database password used when creating connections. + * + * @param password password used when authenticating to the database. + * @since 2.14.0 + */ + default void setPassword(final char[] password) { + setPassword(String.copyValueOf(password)); + } /** * Sets the database password used when creating connections. * * @param password - * password used when authenticating to the database + * password used when authenticating to the database. */ void setPassword(String password); diff --git a/src/test/java/org/apache/commons/dbcp2/datasources/PooledConnectionManagerTest.java b/src/test/java/org/apache/commons/dbcp2/datasources/PooledConnectionManagerTest.java new file mode 100644 index 00000000..7353db99 --- /dev/null +++ b/src/test/java/org/apache/commons/dbcp2/datasources/PooledConnectionManagerTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * https://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 org.apache.commons.dbcp2.datasources; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.sql.SQLException; + +import javax.sql.PooledConnection; + +import org.junit.jupiter.api.Test; + +/* + * Tests {@link PooledConnectionManager}. + */ +public class PooledConnectionManagerTest { + + static class Fixture implements PooledConnectionManager { + + private char[] password; + + @Override + public void closePool(final String userName) throws SQLException { + // empty + } + + char[] getPassword() { + return password; + } + + @Override + public void invalidate(final PooledConnection pc) throws SQLException { + // empty + } + + @Override + public void setPassword(final String password) { + this.password = password.toCharArray(); + } + } + + @Test + public void testSetPasswordCharArray() { + final Fixture fixture = new Fixture(); + fixture.setPassword("p".toCharArray()); + assertArrayEquals("p".toCharArray(), fixture.getPassword()); + } + + @Test + public void testSetPasswordString() { + final Fixture fixture = new Fixture(); + fixture.setPassword("p"); + assertArrayEquals("p".toCharArray(), fixture.getPassword()); + } +} diff --git a/src/test/java/org/apache/commons/dbcp2/datasources/TestCPDSConnectionFactory.java b/src/test/java/org/apache/commons/dbcp2/datasources/TestCPDSConnectionFactory.java index 41642886..7183a48e 100644 --- a/src/test/java/org/apache/commons/dbcp2/datasources/TestCPDSConnectionFactory.java +++ b/src/test/java/org/apache/commons/dbcp2/datasources/TestCPDSConnectionFactory.java @@ -130,7 +130,7 @@ public class TestCPDSConnectionFactory { } @Test - public void testSetPasswordThenModCharArray() { + public void testSetPasswordCharArray() { final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, Duration.ofMillis(-1), false, "userName", "password".toCharArray()); final char[] pwd = { 'a' }; factory.setPassword(pwd); @@ -139,6 +139,14 @@ public class TestCPDSConnectionFactory { assertEquals("a", String.valueOf(factory.getPasswordCharArray())); } + @Test + public void testSetPasswordString() { + final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, null, Duration.ofMillis(-1), false, "userName", "password".toCharArray()); + final String pwd = "a"; + factory.setPassword(pwd); + assertEquals("a", String.valueOf(factory.getPasswordCharArray())); + } + /** * JIRA DBCP-216 *