cherry-picked changes from pull request #2166

pull/2241/head
Brett Wooldridge 5 months ago
parent d43c272f3f
commit aeabea98b7

@ -30,7 +30,7 @@
<commons.csv.version>1.5</commons.csv.version>
<h2.version>2.1.212</h2.version>
<junit.version>4.13.2</junit.version>
<testcontainers.version>1.17.6</testcontainers.version>
<testcontainers.version>1.20.1</testcontainers.version>
</properties>
<groupId>com.zaxxer</groupId>

@ -39,8 +39,7 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicReference;
import static com.zaxxer.hikari.util.UtilityElf.getNullIfEmpty;
import static com.zaxxer.hikari.util.UtilityElf.safeIsAssignableFrom;
import static com.zaxxer.hikari.util.UtilityElf.*;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -454,11 +453,11 @@ public class HikariConfig implements HikariConfigMXBean
/**
* Add a property (name/value pair) that will be used to configure the {@link DataSource}/{@link java.sql.Driver}.
*
* <p/>
* In the case of a {@link DataSource}, the property names will be translated to Java setters following the Java Bean
* naming convention. For example, the property {@code cachePrepStmts} will translate into {@code setCachePrepStmts()}
* with the {@code value} passed as a parameter.
*
* <p/>
* In the case of a {@link java.sql.Driver}, the property will be added to a {@link Properties} instance that will
* be passed to the driver during {@link java.sql.Driver#connect(String, Properties)} calls.
*
@ -1149,7 +1148,7 @@ public class HikariConfig implements HikariConfigMXBean
value = "internal";
}
else if (prop.contains("jdbcUrl") && value instanceof String) {
value = ((String)value).replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*", "$1<masked>");
value = maskPasswordInJdbcUrl((String) value);
}
else if (prop.contains("password")) {
value = "<masked>";

@ -36,6 +36,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLTransientConnectionException;
import java.sql.Statement;
import java.util.StringJoiner;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
@ -637,14 +638,13 @@ abstract class PoolBase
*/
private String stringFromResetBits(final int bits)
{
final var sb = new StringBuilder();
final var sb = new StringJoiner(", ");
for (int ndx = 0; ndx < RESET_STATES.length; ndx++) {
if ( (bits & (0b1 << ndx)) != 0) {
sb.append(RESET_STATES[ndx]).append(", ");
sb.add(RESET_STATES[ndx]);
}
}
sb.setLength(sb.length() - 2); // trim trailing comma
return sb.toString();
}

@ -28,6 +28,8 @@ import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.zaxxer.hikari.util.UtilityElf.maskPasswordInJdbcUrl;
public final class DriverDataSource implements DataSource
{
private static final Logger LOGGER = LoggerFactory.getLogger(DriverDataSource.class);
@ -98,8 +100,8 @@ public final class DriverDataSource implements DataSource
}
}
final var sanitizedUrl = jdbcUrl.replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*", "$1<masked>");
final var sanitizedUrl = maskPasswordInJdbcUrl(jdbcUrl);
try {
if (driver == null) {
driver = DriverManager.getDriver(jdbcUrl);

@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;
/**
* A class that reflectively sets bean properties on a target object.
@ -30,8 +29,6 @@ import java.util.regex.Pattern;
*/
public final class PropertyElf
{
private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is)[A-Z].+");
private PropertyElf() {
// cannot be constructed
}
@ -44,11 +41,12 @@ public final class PropertyElf
var methods = Arrays.asList(target.getClass().getMethods());
properties.forEach((key, value) -> {
if (target instanceof HikariConfig && key.toString().startsWith("dataSource.")) {
((HikariConfig) target).addDataSourceProperty(key.toString().substring("dataSource.".length()), value);
var keyName = key.toString();
if (target instanceof HikariConfig && keyName.startsWith("dataSource.")) {
((HikariConfig) target).addDataSourceProperty(keyName.substring("dataSource.".length()), value);
}
else {
setProperty(target, key.toString(), value, methods);
setProperty(target, keyName, value, methods);
}
});
}
@ -62,21 +60,17 @@ public final class PropertyElf
public static Set<String> getPropertyNames(final Class<?> targetClass)
{
var set = new HashSet<String>();
var matcher = GETTER_PATTERN.matcher("");
for (var method : targetClass.getMethods()) {
var name = method.getName();
if (method.getParameterTypes().length == 0 && matcher.reset(name).matches()) {
name = name.replaceFirst("(get|is)", "");
try {
if (targetClass.getMethod("set" + name, method.getReturnType()) != null) {
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
set.add(name);
}
}
catch (Exception e) {
// fall thru (continue)
var name = propertyNameFromGetterName(method.getName());
try {
if (method.getParameterTypes().length == 0 && name != null) {
targetClass.getMethod("set" + capitalizedPropertyName(name), method.getReturnType()); // throws if method setter does not exist
set.add(name);
}
}
catch (Exception e) {
// fall thru (continue)
}
}
return set;
@ -86,13 +80,13 @@ public final class PropertyElf
{
try {
// use the english locale to avoid the infamous turkish locale bug
var capitalized = "get" + propName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propName.substring(1);
var capitalized = "get" + capitalizedPropertyName(propName);
var method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
catch (Exception e) {
try {
var capitalized = "is" + propName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propName.substring(1);
var capitalized = "is" + capitalizedPropertyName(propName);
var method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
@ -109,6 +103,23 @@ public final class PropertyElf
return copy;
}
private static String propertyNameFromGetterName(final String methodName)
{
String name = null;
if (methodName.startsWith("get") && methodName.length() > 3) {
name = methodName.substring(3);
}
else if (methodName.startsWith("is") && methodName.length() > 2) {
name = methodName.substring(2);
}
if (name != null) {
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
return null;
}
private static void setProperty(final Object target, final String propName, final Object propValue, final List<Method> methods)
{
final var logger = LoggerFactory.getLogger(PropertyElf.class);
@ -163,4 +174,10 @@ public final class PropertyElf
throw new RuntimeException(e);
}
}
private static String capitalizedPropertyName(String propertyName)
{
// use the english locale to avoid the infamous turkish locale bug
return propertyName.substring(0, 1).toUpperCase(Locale.ENGLISH) + propertyName.substring(1);
}
}

@ -18,6 +18,7 @@ package com.zaxxer.hikari.util;
import java.util.Locale;
import java.util.concurrent.*;
import java.util.regex.Pattern;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -28,14 +29,21 @@ import static java.util.concurrent.TimeUnit.SECONDS;
*/
public final class UtilityElf
{
private static final Pattern PASSWORD_MASKING_PATTERN = Pattern.compile("([?&;][^&#;=]*[pP]assword=)[^&#;]*");
private UtilityElf()
{
// non-constructable
}
public static String maskPasswordInJdbcUrl(String jdbcUrl)
{
return PASSWORD_MASKING_PATTERN.matcher(jdbcUrl).replaceAll("$1<masked>");
}
/**
*
* @return null if string is null or empty
* @return null if string is null or empty, , trimmed string otherwise
*/
public static String getNullIfEmpty(final String text)
{
@ -91,12 +99,14 @@ public final class UtilityElf
try {
var loaded = UtilityElf.class.getClassLoader().loadClass(className);
if (args.length == 0) {
var totalArgs = args.length;
if (totalArgs == 0) {
return clazz.cast(loaded.getDeclaredConstructor().newInstance());
}
var argClasses = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
var argClasses = new Class<?>[totalArgs];
for (int i = 0; i < totalArgs; i++) {
argClasses[i] = args[i].getClass();
}
var constructor = loaded.getConstructor(argClasses);

@ -41,7 +41,7 @@ import org.testcontainers.utility.DockerImageName;
public class PostgresTest
{
private static final DockerImageName IMAGE_NAME = DockerImageName.parse("postgres:9.6.20");
private static final DockerImageName IMAGE_NAME = DockerImageName.parse("postgres:16");
private PostgreSQLContainer<?> postgres;

Loading…
Cancel
Save