Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistently consider quoted AND non-quoted versions of quarkus.datasource configuration properties #26815

Merged
merged 2 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.deployment.builditem;

import java.util.Collection;
import java.util.List;

import io.quarkus.builder.item.MultiBuildItem;

/**
Expand All @@ -12,21 +15,40 @@
*/
public final class DevServicesAdditionalConfigBuildItem extends MultiBuildItem {

private final String triggeringKey;
private final Collection<String> triggeringKeys;
private final String key;
private final String value;
private final Runnable callbackWhenEnabled;

/**
* @deprecated Call
* {@link DevServicesAdditionalConfigBuildItem#DevServicesAdditionalConfigBuildItem(Collection, String, String, Runnable)}
* instead.
*/
@Deprecated
public DevServicesAdditionalConfigBuildItem(String triggeringKey,
String key, String value, Runnable callbackWhenEnabled) {
this.triggeringKey = triggeringKey;
this(List.of(triggeringKey), key, value, callbackWhenEnabled);
}

public DevServicesAdditionalConfigBuildItem(Collection<String> triggeringKeys,
String key, String value, Runnable callbackWhenEnabled) {
this.triggeringKeys = triggeringKeys;
this.key = key;
this.value = value;
this.callbackWhenEnabled = callbackWhenEnabled;
}

/**
* @deprecated Call {@link #getTriggeringKeys()} instead.
*/
@Deprecated
public String getTriggeringKey() {
return triggeringKey;
return getTriggeringKeys().iterator().next();
}

public Collection<String> getTriggeringKeys() {
return triggeringKeys;
}

public String getKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void run() {
// On contrary to dev services config, "additional" config build items are
// produced on each restart, so we don't want to remember them from one restart to the next.
for (DevServicesAdditionalConfigBuildItem item : devServicesAdditionalConfigBuildItems) {
if (newProperties.containsKey(item.getTriggeringKey())) {
if (item.getTriggeringKeys().stream().anyMatch(newProperties::containsKey)) {
var callback = item.getCallbackWhenEnabled();
if (callback != null) {
callback.run(); // This generally involves logging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Properties;
import java.util.Set;
Expand All @@ -25,6 +26,7 @@
import java.util.UUID;
import java.util.function.IntFunction;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
Expand Down Expand Up @@ -270,6 +272,48 @@ public static boolean isPropertyPresent(String propertyName) {
return ConfigProvider.getConfig().unwrap(SmallRyeConfig.class).isPropertyPresent(propertyName);
}

/**
* Checks if any of the given properties is present in the current Configuration.
* <p>
* Because the sources may not expose the property directly in {@link ConfigSource#getPropertyNames()}, we cannot
* reliably determine if the property is present in the properties list. The property needs to be retrieved to make
* sure it exists. Also, if the value is an expression, we want to ignore expansion, because this is not relevant
* for the check and the expansion value may not be available at this point.
* <p>
* It may be interesting to expose such API in SmallRyeConfig directly.
*
* @param propertyNames The configuration property names
* @return true if the property is present or false otherwise.
*/
public static boolean isAnyPropertyPresent(Collection<String> propertyNames) {
for (String propertyName : propertyNames) {
if (isPropertyPresent(propertyName)) {
return true;
}
}
return false;
}

/**
* Get the value of the first given property present in the current Configuration,
* or {@link Optional#empty()} if none of the properties is present.
*
* @param <T> The property type
* @param propertyNames The configuration property names
* @param propertyType The type that the resolved property value should be converted to
* @return true if the property is present or false otherwise.
*/
public static <T> Optional<T> getFirstOptionalValue(List<String> propertyNames, Class<T> propertyType) {
Config config = ConfigProvider.getConfig();
for (String propertyName : propertyNames) {
Optional<T> value = config.getOptionalValue(propertyName, propertyType);
if (value.isPresent()) {
return value;
}
}
return Optional.empty();
}

/**
* We override the EnvConfigSource, because we don't want the nothing back from getPropertiesNames at build time.
* The mapping is one way and there is no way to map them back.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.datasource.common.runtime;

import java.util.Collection;
import java.util.List;

public final class DataSourceUtil {

Expand All @@ -14,6 +15,17 @@ public static boolean hasDefault(Collection<String> dataSourceNames) {
return dataSourceNames.contains(DEFAULT_DATASOURCE_NAME);
}

public static List<String> dataSourcePropertyKeys(String datasourceName, String radical) {
if (datasourceName == null || DataSourceUtil.isDefault(datasourceName)) {
return List.of("quarkus.datasource." + radical);
} else {
// Two possible syntaxes: with or without quotes
return List.of(
"quarkus.datasource.\"" + datasourceName + "\"." + radical,
"quarkus.datasource." + datasourceName + "." + radical);
}
}

private DataSourceUtil() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.datasource.deployment.spi;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.runtime.configuration.ConfigUtils;

/**
Expand Down Expand Up @@ -56,33 +59,24 @@ public static DevServicesDatasourceConfigurationHandlerBuildItem jdbc(String dbK
public Map<String, String> apply(String dsName,
DevServicesDatasourceProvider.RunningDevServicesDatasource runningDevDb) {
String jdbcUrl = runningDevDb.getJdbcUrl();
if (dsName == null) {
return Collections.singletonMap("quarkus.datasource.jdbc.url", jdbcUrl);
} else {
// we use quoted and unquoted versions because depending on whether a user configured other JDBC properties
// one of the URLs may be ignored
// see https://github.com/quarkusio/quarkus/issues/21387
return Map.of(
datasourceURLPropName(dsName), jdbcUrl,
datasourceURLPropName("\"" + dsName + "\""), jdbcUrl);
}
// we use datasourceURLPropNames to generate quoted and unquoted versions of the property key,
// because depending on whether a user configured other JDBC properties
// one of the URLs may be ignored
// see https://github.com/quarkusio/quarkus/issues/21387
return datasourceURLPropNames(dsName).stream()
.collect(Collectors.toMap(Function.identity(), ignored -> jdbcUrl));
}

}, new Predicate<String>() {
@Override
public boolean test(String dsName) {
if (dsName == null) {
return ConfigUtils.isPropertyPresent("quarkus.datasource.jdbc.url");
} else {
return ConfigUtils.isPropertyPresent(datasourceURLPropName(dsName)) ||
ConfigUtils.isPropertyPresent(datasourceURLPropName("\"" + dsName + "\""));
}
return ConfigUtils.isAnyPropertyPresent(datasourceURLPropNames(dsName));
}
});
}

private static String datasourceURLPropName(String dsName) {
return String.format("quarkus.datasource.%s.jdbc.url", dsName);
private static List<String> datasourceURLPropNames(String dsName) {
return DataSourceUtil.dataSourcePropertyKeys(dsName, "jdbc.url");
}

public static DevServicesDatasourceConfigurationHandlerBuildItem reactive(String dbKind) {
Expand All @@ -92,41 +86,22 @@ public static DevServicesDatasourceConfigurationHandlerBuildItem reactive(String
public Map<String, String> apply(String dsName,
DevServicesDatasourceProvider.RunningDevServicesDatasource runningDevDb) {
String reactiveUrl = runningDevDb.getReactiveUrl();
if (dsName == null) {
return Collections.singletonMap("quarkus.datasource.reactive.url", reactiveUrl);
} else {
// we use quoted and unquoted versions because depending on whether a user configured other JDBC properties
// one of the URLs may be ignored
// see https://github.com/quarkusio/quarkus/issues/21387
return Map.of(
datasourceReactiveURLPropName(dsName, false), reactiveUrl,
datasourceReactiveURLPropName(dsName, true), reactiveUrl);
}
// we use datasourceURLPropNames to generate quoted and unquoted versions of the property key,
// because depending on whether a user configured other reactive properties
// one of the URLs may be ignored
// see https://github.com/quarkusio/quarkus/issues/21387
return datasourceReactiveURLPropNames(dsName).stream()
.collect(Collectors.toMap(Function.identity(), ignored -> reactiveUrl));
}
}, new Predicate<String>() {
@Override
public boolean test(String dsName) {
if (dsName == null) {
return ConfigUtils.isPropertyPresent("quarkus.datasource.reactive.url");
} else {
return ConfigUtils.isPropertyPresent(datasourceReactiveURLPropName(dsName, false)) ||
ConfigUtils.isPropertyPresent(datasourceReactiveURLPropName(dsName, true));
}
return ConfigUtils.isAnyPropertyPresent(datasourceReactiveURLPropNames(dsName));
}
});
}

private static String datasourceReactiveURLPropName(String dsName, boolean quotedName) {
StringBuilder key = new StringBuilder("quarkus.datasource");
key.append('.');
if (quotedName) {
key.append('"');
}
key.append(dsName);
if (quotedName) {
key.append('"');
}
key.append(".reactive.url");
return key.toString();
private static List<String> datasourceReactiveURLPropNames(String dsName) {
return DataSourceUtil.dataSourcePropertyKeys(dsName, "reactive.url");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.logging.Logger;

import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.deployment.spi.DefaultDataSourceDbKindBuildItem;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceConfigurationHandlerBuildItem;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceContainerConfig;
Expand All @@ -35,6 +36,7 @@
import io.quarkus.deployment.logging.LoggingSetupBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigUtils;

public class DevServicesDatasourceProcessor {

Expand Down Expand Up @@ -257,11 +259,6 @@ private RunningDevService startDevDb(String dbName,
consoleInstalledBuildItem,
loggingSetupBuildItem);
try {
String prefix = "quarkus.datasource.";
if (dbName != null) {
prefix = prefix + dbName + ".";
}

DevServicesDatasourceContainerConfig containerConfig = new DevServicesDatasourceContainerConfig(
dataSourceBuildTimeConfig.devservices.imageName,
dataSourceBuildTimeConfig.devservices.containerProperties,
Expand All @@ -273,26 +270,34 @@ private RunningDevService startDevDb(String dbName,
dataSourceBuildTimeConfig.devservices.password);

DevServicesDatasourceProvider.RunningDevServicesDatasource datasource = devDbProvider
.startDatabase(ConfigProvider.getConfig().getOptionalValue(prefix + "username", String.class),
ConfigProvider.getConfig().getOptionalValue(prefix + "password", String.class),
.startDatabase(
ConfigUtils.getFirstOptionalValue(DataSourceUtil.dataSourcePropertyKeys(dbName, "username"),
String.class),
ConfigUtils.getFirstOptionalValue(DataSourceUtil.dataSourcePropertyKeys(dbName, "password"),
String.class),
Optional.ofNullable(dbName), containerConfig,
launchMode, globalDevServicesConfig.timeout);

propertiesMap.put(prefix + "db-kind", dataSourceBuildTimeConfig.dbKind.orElse(null));
String devServicesPrefix = prefix + "devservices.";
for (String key : DataSourceUtil.dataSourcePropertyKeys(dbName, "db-kind")) {
propertiesMap.put(key, dataSourceBuildTimeConfig.dbKind.orElse(null));
}
String devServicesPrefix = "devservices.";
if (dataSourceBuildTimeConfig.devservices.command.isPresent()) {
propertiesMap.put(devServicesPrefix + "command", dataSourceBuildTimeConfig.devservices.command.get());
setDataSourceProperties(propertiesMap, dbName, devServicesPrefix + "command",
dataSourceBuildTimeConfig.devservices.command.get());
}
if (dataSourceBuildTimeConfig.devservices.imageName.isPresent()) {
propertiesMap.put(devServicesPrefix + "image-name", dataSourceBuildTimeConfig.devservices.imageName.get());
setDataSourceProperties(propertiesMap, dbName, devServicesPrefix + "image-name",
dataSourceBuildTimeConfig.devservices.imageName.get());
}
if (dataSourceBuildTimeConfig.devservices.port.isPresent()) {
propertiesMap.put(devServicesPrefix + "port",
setDataSourceProperties(propertiesMap, dbName, devServicesPrefix + "port",
Integer.toString(dataSourceBuildTimeConfig.devservices.port.getAsInt()));
}
if (!dataSourceBuildTimeConfig.devservices.properties.isEmpty()) {
for (var e : dataSourceBuildTimeConfig.devservices.properties.entrySet()) {
propertiesMap.put(devServicesPrefix + "properties." + e.getKey(), e.getValue());
setDataSourceProperties(propertiesMap, dbName, devServicesPrefix + "properties." + e.getKey(),
e.getValue());
}
}

Expand All @@ -301,21 +306,23 @@ private RunningDevService startDevDb(String dbName,
devDebProperties.putAll(devDbConfigurationHandlerBuildItem.getConfigProviderFunction()
.apply(dbName, datasource));
}
devDebProperties.put(prefix + "db-kind", defaultDbKind.get());
setDataSourceProperties(devDebProperties, dbName, "db-kind", defaultDbKind.get());
if (datasource.getUsername() != null) {
devDebProperties.put(prefix + "username", datasource.getUsername());
setDataSourceProperties(devDebProperties, dbName, "username", datasource.getUsername());
}
if (datasource.getPassword() != null) {
devDebProperties.put(prefix + "password", datasource.getPassword());
setDataSourceProperties(devDebProperties, dbName, "password", datasource.getPassword());
}
compressor.close();
log.info("Dev Services for " + prettyName
+ " (" + defaultDbKind.get() + ") started.");

String devservices = prefix + "devservices.";
List<String> devservicesPrefixes = DataSourceUtil.dataSourcePropertyKeys(dbName, "devservices.");
for (var name : ConfigProvider.getConfig().getPropertyNames()) {
if (name.startsWith(devservices)) {
devDebProperties.put(name, ConfigProvider.getConfig().getValue(name, String.class));
for (String prefix : devservicesPrefixes) {
if (name.startsWith(prefix)) {
devDebProperties.put(name, ConfigProvider.getConfig().getValue(name, String.class));
}
}
}
return new RunningDevService(defaultDbKind.get(), datasource.getId(), datasource.getCloseTask(), devDebProperties);
Expand All @@ -325,6 +332,13 @@ private RunningDevService startDevDb(String dbName,
}
}

private void setDataSourceProperties(Map<String, String> propertiesMap, String dbName, String propertyKeyRadical,
String value) {
for (String key : DataSourceUtil.dataSourcePropertyKeys(dbName, propertyKeyRadical)) {
propertiesMap.put(key, value);
}
}

private DevServicesDatasourceResultBuildItem.DbResult toDbResult(RunningDevService devService) {
if (devService == null) {
return null;
Expand Down
Loading