Skip to content

Commit

Permalink
cherry-picked changes from pull request #2166
Browse files Browse the repository at this point in the history
  • Loading branch information
brettwooldridge committed Sep 23, 2024
1 parent d43c272 commit aeabea9
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 36 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/com/zaxxer/hikari/HikariConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@
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;

Expand Down Expand Up @@ -454,11 +453,11 @@ public void setDataSourceClassName(String className)

/**
* 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.
*
Expand Down Expand Up @@ -1149,7 +1148,7 @@ else if (prop.matches("scheduledExecutorService|threadFactory") && value == null
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>";
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/zaxxer/hikari/pool/PoolBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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;
Expand Down Expand Up @@ -637,14 +638,13 @@ private void setLoginTimeout(final DataSource dataSource)
*/
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();
}

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/zaxxer/hikari/util/DriverDataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
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);
Expand Down Expand Up @@ -98,8 +100,8 @@ public DriverDataSource(String jdbcUrl, String driverClassName, Properties prope
}
}

final var sanitizedUrl = jdbcUrl.replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*", "$1<masked>");
final var sanitizedUrl = maskPasswordInJdbcUrl(jdbcUrl);

try {
if (driver == null) {
driver = DriverManager.getDriver(jdbcUrl);
Expand Down
57 changes: 37 additions & 20 deletions src/main/java/com/zaxxer/hikari/util/PropertyElf.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;

/**
* A class that reflectively sets bean properties on a target object.
Expand All @@ -30,8 +29,6 @@
*/
public final class PropertyElf
{
private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is)[A-Z].+");

private PropertyElf() {
// cannot be constructed
}
Expand All @@ -44,11 +41,12 @@ public static void setTargetFromProperties(final Object target, final Properties

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);
}
});
}
Expand All @@ -62,21 +60,17 @@ public static void setTargetFromProperties(final Object target, final Properties
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;
Expand All @@ -86,13 +80,13 @@ public static Object getProperty(final String propName, final Object target)
{
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);
}
Expand All @@ -109,6 +103,23 @@ public static Properties copyProperties(final Properties props)
return copy;
}

private static String propertyNameFromGetterName(final String methodName)

This comment has been minimized.

Copy link
@OrangeDog

OrangeDog Sep 24, 2024

The "correct" way to do this sort of thing is via java.beans.Introspector.

{
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);
Expand Down Expand Up @@ -163,4 +174,10 @@ else if (paramClass == String.class) {
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 changes: 14 additions & 4 deletions src/main/java/com/zaxxer/hikari/util/UtilityElf.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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;
Expand All @@ -28,14 +29,21 @@
*/
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)
{
Expand Down Expand Up @@ -91,12 +99,14 @@ public static <T> T createInstance(final String className, final Class<T> clazz,

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);
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/zaxxer/hikari/pool/PostgresTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

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;

Expand Down

0 comments on commit aeabea9

Please sign in to comment.