Skip to content
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
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/datasource.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ When compiling a Quarkus application to a native image, the MySQL support for JM

* The lack of JMX support is a natural consequence of running in native mode and is unlikely to be resolved.
* The integration with OCI is not supported.
* The OpenTelemetry integration included in the MySQL Connector/J driver is disabled in favor of the one provided by Agroal. If you really need to use the OpenTelemetry integration provided by the MySQL Connector/J driver, enable it explicitly by setting `quarkus.datasource.jdbc.additional-jdbc-properties.openTelemetry=PREFERRED` (https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-debugging-profiling.html#cj-conn-prop_openTelemetry[documentation]). Be aware that by enabling it, if your application is using the `quarkus-opentelemetry` extension, the application will likely fail to start with an `java.lang.IllegalStateException: GlobalOpenTelemetry.set has already been called.` error.

==== Oracle

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import io.quarkus.datasource.common.runtime.DataSourceUtil;
import io.quarkus.datasource.runtime.DataSourceBuildTimeConfig;

final class AggregatedDataSourceBuildTimeConfigBuildItem extends MultiBuildItem {
/**
* An aggregated build item holding the build time configuration for a given datasource,
* including both the build and the runtime datasource config.
*/
public final class AggregatedDataSourceBuildTimeConfigBuildItem extends MultiBuildItem {

private final String name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import io.quarkus.agroal.runtime.TransactionIntegration;
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
import io.quarkus.agroal.spi.JdbcPropertyBuildItem;
import io.quarkus.arc.BeanDestroyer;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.OpenTelemetrySdkBuildItem;
Expand Down Expand Up @@ -256,7 +257,8 @@ void generateDataSourceBeans(AgroalRecorder recorder,
Capabilities capabilities,
Optional<OpenTelemetrySdkBuildItem> openTelemetrySdkBuildItem,
BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer,
BuildProducer<JdbcDataSourceBuildItem> jdbcDataSource) {
BuildProducer<JdbcDataSourceBuildItem> jdbcDataSource,
List<JdbcPropertyBuildItem> jdbcPropertyBuildItems) {
if (aggregatedBuildTimeConfigBuildItems.isEmpty()) {
// No datasource has been configured so bail out
return;
Expand All @@ -266,6 +268,11 @@ void generateDataSourceBeans(AgroalRecorder recorder,

String dataSourceName = aggregatedBuildTimeConfigBuildItem.getName();

// Filter the JDBC properties for the current datasource
Map<String, String> jdbcProperties = jdbcPropertyBuildItems.stream()
.filter(p -> dataSourceName.equals(p.dataSourceName()))
.collect(Collectors.toMap(JdbcPropertyBuildItem::propertyName, JdbcPropertyBuildItem::propertyValue));

SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem
.configure(AgroalDataSource.class)
.addType(DATA_SOURCE)
Expand All @@ -277,7 +284,8 @@ void generateDataSourceBeans(AgroalRecorder recorder,
.addInjectionPoint(ClassType.create(DotName.createSimple(DataSources.class)))
.startup()
.checkActive(recorder.agroalDataSourceCheckActiveSupplier(dataSourceName))
.createWith(recorder.agroalDataSourceSupplier(dataSourceName, isOtelSdkEnabled(openTelemetrySdkBuildItem)))
.createWith(recorder.agroalDataSourceSupplier(dataSourceName, isOtelSdkEnabled(openTelemetrySdkBuildItem),
jdbcProperties))
.destroyer(BeanDestroyer.AutoCloseableDestroyer.class);

if (!DataSourceUtil.isDefault(dataSourceName)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.agroal.runtime;

import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -55,14 +56,15 @@ public ActiveResult get() {

public Function<SyntheticCreationalContext<AgroalDataSource>, AgroalDataSource> agroalDataSourceSupplier(
String dataSourceName,
Optional<RuntimeValue<Boolean>> otelEnabled) {
Optional<RuntimeValue<Boolean>> otelEnabled,
Map<String, String> jdbcProperties) {
return new Function<>() {
@SuppressWarnings("deprecation")
@Override
public AgroalDataSource apply(SyntheticCreationalContext<AgroalDataSource> context) {
DataSources dataSources = context.getInjectedReference(DataSources.class);
return dataSources.createDataSource(dataSourceName,
otelEnabled.isPresent() ? otelEnabled.get().getValue() : false);
otelEnabled.isPresent() ? otelEnabled.get().getValue() : false, jdbcProperties);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ public AgroalDataSource getDataSource(String dataSourceName) {
}

@SuppressWarnings("resource")
public AgroalDataSource createDataSource(String dataSourceName, boolean otelEnabled) {
public AgroalDataSource createDataSource(String dataSourceName, boolean otelEnabled,
Map<String, String> buildTimeJdbcProperties) {
if (!agroalDataSourceSupport.entries.containsKey(dataSourceName)) {
throw new IllegalArgumentException("No datasource named '" + dataSourceName + "' exists");
}
Expand Down Expand Up @@ -192,7 +193,7 @@ public AgroalDataSource createDataSource(String dataSourceName, boolean otelEnab
applyNewConfiguration(dataSourceName, dataSourceConfiguration, poolConfiguration, connectionFactoryConfiguration,
driver, jdbcUrl,
dataSourceJdbcBuildTimeConfig, dataSourceRuntimeConfig, dataSourceJdbcRuntimeConfig, transactionRuntimeConfig,
mpMetricsPresent);
mpMetricsPresent, buildTimeJdbcProperties);

if (agroalDataSourceSupport.disableSslSupport) {
agroalConnectionConfigurer.disableSslSupport(resolvedDbKind, dataSourceConfiguration,
Expand Down Expand Up @@ -243,7 +244,7 @@ private void applyNewConfiguration(String dataSourceName, AgroalDataSourceConfig
AgroalConnectionFactoryConfigurationSupplier connectionFactoryConfiguration, Class<?> driver, String jdbcUrl,
DataSourceJdbcBuildTimeConfig dataSourceJdbcBuildTimeConfig, DataSourceRuntimeConfig dataSourceRuntimeConfig,
DataSourceJdbcRuntimeConfig dataSourceJdbcRuntimeConfig, TransactionManagerConfiguration transactionRuntimeConfig,
boolean mpMetricsPresent) {
boolean mpMetricsPresent, Map<String, String> buildTimeJdbcProperties) {
connectionFactoryConfiguration.jdbcUrl(jdbcUrl);
connectionFactoryConfiguration.connectionProviderClass(driver);
connectionFactoryConfiguration.trackJdbcResources(dataSourceJdbcRuntimeConfig.detectStatementLeaks());
Expand Down Expand Up @@ -304,6 +305,11 @@ private void applyNewConfiguration(String dataSourceName, AgroalDataSourceConfig
.credential(new AgroalVaultCredentialsProviderPassword(name, credentialsProvider));
}

// Additional JDBC properties from build time config
for (Map.Entry<String, String> entry : buildTimeJdbcProperties.entrySet()) {
connectionFactoryConfiguration.jdbcProperty(entry.getKey(), entry.getValue());
}

// Extra JDBC properties
for (Map.Entry<String, String> entry : dataSourceJdbcRuntimeConfig.additionalJdbcProperties().entrySet()) {
connectionFactoryConfiguration.jdbcProperty(entry.getKey(), entry.getValue());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.agroal.spi;

import io.quarkus.builder.item.MultiBuildItem;

/**
* A build item to represent a JDBC property to be set as an additional JDBC property when the datasource is setup.
*/
public final class JdbcPropertyBuildItem extends MultiBuildItem {

private String dataSourceName;
private String propertyName;
private String propertyValue;

public JdbcPropertyBuildItem(String dataSourceName, String propertyName, String propertyValue) {
this.dataSourceName = dataSourceName;
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}

public String dataSourceName() {
return dataSourceName;
}

public String propertyName() {
return propertyName;
}

public String propertyValue() {
return propertyValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import com.mysql.cj.MysqlConnection;
import com.mysql.cj.WarningListener;
import com.mysql.cj.conf.PropertyDefinitions;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.PropertySet;
import com.mysql.cj.jdbc.JdbcConnection;
import com.mysql.cj.jdbc.JdbcPreparedStatement;
Expand All @@ -19,7 +21,9 @@
import com.mysql.cj.jdbc.result.ResultSetInternalMethods;
import com.mysql.cj.protocol.Resultset;

import io.quarkus.agroal.deployment.AggregatedDataSourceBuildTimeConfigBuildItem;
import io.quarkus.agroal.spi.JdbcDriverBuildItem;
import io.quarkus.agroal.spi.JdbcPropertyBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.datasource.common.runtime.DatabaseKind;
Expand Down Expand Up @@ -129,4 +133,28 @@ void registerServiceBinding(Capabilities capabilities,
}
dbKind.produce(new DefaultDataSourceDbKindBuildItem(DatabaseKind.MYSQL));
}

/**
* Disable built-in OpenTelemetry support for MySQL JDBC driver when both
* OpenTelemetry Tracer and Agroal capabilities are present.
*/
@BuildStep
void disableBuiltInOpenTelemetry(BuildProducer<JdbcPropertyBuildItem> properties,
List<AggregatedDataSourceBuildTimeConfigBuildItem> datasources,
Capabilities capabilities) {
// If OpenTelemetry Tracer or Agroal capabilities are missing, skip this step
if (capabilities.isMissing(Capability.OPENTELEMETRY_TRACER) ||
capabilities.isMissing(Capability.AGROAL)) {
return;
}
for (AggregatedDataSourceBuildTimeConfigBuildItem ds : datasources) {
if (ds.getDbKind().equals(DatabaseKind.MYSQL)) {
// NOTE: Checking if the "quarkus.datasource.jdbc.telemetry=true" property is set doesn't work because
// the driver checks for the OpenTelemetry classes in the classpath
properties.produce(new JdbcPropertyBuildItem(ds.getName(),
PropertyKey.openTelemetry.getKeyName(),
PropertyDefinitions.OpenTelemetry.DISABLED.name()));
}
}
}
}
Loading