diff --git a/.github/native-tests.json b/.github/native-tests.json index 52b608539dc106..a77ea33a73c1aa 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -117,7 +117,7 @@ { "category": "Misc4", "timeout": 75, - "test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, webjars-locator", + "test-modules": "picocli-native, gradle, micrometer-mp-metrics, micrometer-prometheus, logging-json, jaxp, jaxb, opentelemetry, opentelemetry-jdbc-instrumentation, webjars-locator", "os-name": "ubuntu-latest" }, { diff --git a/.github/workflows/ci-istio.yml b/.github/workflows/ci-istio.yml index 35a400699d7ca4..d6911db99f1ae2 100644 --- a/.github/workflows/ci-istio.yml +++ b/.github/workflows/ci-istio.yml @@ -62,7 +62,7 @@ jobs: minikube version: v1.16.0 kubernetes version: ${{ matrix.kubernetes }} github token: ${{ secrets.GITHUB_TOKEN }} - start args: '--addons=metrics-server --force' + start args: '--embed-certs --addons=metrics-server --force' - name: Quay login uses: docker/login-action@v2 with: diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index 80fecb6802fa5d..19cc80cff22d87 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -141,4 +141,5 @@ public interface Capability { String REDIS_CLIENT = QUARKUS_PREFIX + "redis"; String CACHE = QUARKUS_PREFIX + "cache"; + String JDBC_ORACLE = QUARKUS_PREFIX + "jdbc.oracle"; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java index 933451240e21dc..d23eeed870b0d0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java @@ -466,7 +466,6 @@ public enum MonitoringOption { HEAPDUMP, JVMSTAT, JFR, - ALL, - TRUE // only needed to support -Dquarkus.native.monitoring + ALL } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index d53fab22c7eca7..4d411fef2a0284 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -22,7 +22,6 @@ import java.util.stream.Stream; import org.apache.commons.lang3.SystemUtils; -import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.quarkus.bootstrap.util.IoUtils; @@ -830,17 +829,10 @@ public NativeImageInvokerInfo build() { if (nativeConfig.monitoring.isPresent()) { List monitoringOptions = nativeConfig.monitoring.get(); - if (monitoringOptions.stream().anyMatch(o -> o == NativeConfig.MonitoringOption.TRUE - || o == NativeConfig.MonitoringOption.ALL)) { - nativeImageArgs.add("--enable-monitoring"); + if (!monitoringOptions.isEmpty()) { + nativeImageArgs.add("--enable-monitoring=" + monitoringOptions.stream() + .map(o -> o.name().toLowerCase(Locale.ROOT)).collect(Collectors.joining(","))); } - nativeImageArgs - .add("--enable-monitoring=" + monitoringOptions.stream().map(o -> o.name().toLowerCase( - Locale.ROOT)).collect(Collectors.joining(","))); - } else if (ConfigProvider.getConfig().getConfigValue("quarkus.native.monitoring").getValue() != null) { - // this only happens when a user has configured 'quarkus.native.monitoring=' - // we want to support this use case as GraalVM allows the use of '--enable-monitoring' without an argument - nativeImageArgs.add("--enable-monitoring"); } if (nativeConfig.autoServiceLoaderRegistration) { nativeImageArgs.add("-H:+UseServiceLoaderFeature"); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java index 3e10e02d712432..c57a75a2c501f4 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java @@ -93,7 +93,6 @@ import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContextConfig; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; -import io.quarkus.bootstrap.resolver.maven.options.BootstrapMavenOptions; import io.quarkus.bootstrap.util.BootstrapUtils; import io.quarkus.bootstrap.workspace.ArtifactSources; import io.quarkus.bootstrap.workspace.SourceDir; @@ -1184,7 +1183,6 @@ private QuarkusDevModeLauncher newLauncher(Boolean debugPortOk) throws Exception if (argsString != null) { builder.applicationArgs(argsString); } - propagateUserProperties(builder); return builder.build(); } @@ -1207,26 +1205,6 @@ private void setJvmArgs(Builder builder) throws Exception { } - private void propagateUserProperties(MavenDevModeLauncher.Builder builder) { - Properties userProps = BootstrapMavenOptions.newInstance().getSystemProperties(); - if (userProps == null) { - return; - } - final StringBuilder buf = new StringBuilder(); - buf.append("-D"); - for (Object o : userProps.keySet()) { - String name = o.toString(); - final String value = userProps.getProperty(name); - buf.setLength(2); - buf.append(name); - if (value != null && !value.isEmpty()) { - buf.append('='); - buf.append(value); - } - builder.jvmArgs(buf.toString()); - } - } - private void applyCompilerFlag(Optional compilerPluginConfiguration, String flagName, Consumer builderCall) { compilerPluginConfiguration diff --git a/extensions/container-image/container-image-docker/deployment/src/main/java/io/quarkus/container/image/docker/deployment/DockerProcessor.java b/extensions/container-image/container-image-docker/deployment/src/main/java/io/quarkus/container/image/docker/deployment/DockerProcessor.java index a9bc9b2e303f2a..f6a5d79ab181d5 100644 --- a/extensions/container-image/container-image-docker/deployment/src/main/java/io/quarkus/container/image/docker/deployment/DockerProcessor.java +++ b/extensions/container-image/container-image-docker/deployment/src/main/java/io/quarkus/container/image/docker/deployment/DockerProcessor.java @@ -221,7 +221,7 @@ private String createContainerImage(ContainerImageConfig containerImageConfig, D } } - if (pushContainerImage) { + if (!useBuildx && pushContainerImage) { // If not using buildx, push the images loginToRegistryIfNeeded(containerImageConfig, containerImageInfo, dockerConfig); diff --git a/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceContainerConfig.java b/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceContainerConfig.java index 4c68beb42f42c1..abba376e6eaf07 100644 --- a/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceContainerConfig.java +++ b/extensions/datasource/deployment-spi/src/main/java/io/quarkus/datasource/deployment/spi/DevServicesDatasourceContainerConfig.java @@ -14,6 +14,7 @@ public class DevServicesDatasourceContainerConfig { private final Optional dbName; private final Optional username; private final Optional password; + private final Optional initScriptPath; public DevServicesDatasourceContainerConfig(Optional imageName, Map containerProperties, @@ -22,7 +23,8 @@ public DevServicesDatasourceContainerConfig(Optional imageName, Optional command, Optional dbName, Optional username, - Optional password) { + Optional password, + Optional initScriptPath) { this.imageName = imageName; this.containerProperties = containerProperties; this.additionalJdbcUrlProperties = additionalJdbcUrlProperties; @@ -31,6 +33,7 @@ public DevServicesDatasourceContainerConfig(Optional imageName, this.dbName = dbName; this.username = username; this.password = password; + this.initScriptPath = initScriptPath; } public Optional getImageName() { @@ -64,4 +67,8 @@ public Optional getUsername() { public Optional getPassword() { return password; } + + public Optional getInitScriptPath() { + return initScriptPath; + } } diff --git a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java index f10a51ffe2f2eb..65bc997799ab97 100644 --- a/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java +++ b/extensions/datasource/deployment/src/main/java/io/quarkus/datasource/deployment/devservices/DevServicesDatasourceProcessor.java @@ -275,7 +275,8 @@ private RunningDevService startDevDb(String dbName, dataSourceBuildTimeConfig.devservices.command, dataSourceBuildTimeConfig.devservices.dbName, dataSourceBuildTimeConfig.devservices.username, - dataSourceBuildTimeConfig.devservices.password); + dataSourceBuildTimeConfig.devservices.password, + dataSourceBuildTimeConfig.devservices.initScriptPath); DevServicesDatasourceProvider.RunningDevServicesDatasource datasource = devDbProvider .startDatabase( diff --git a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DevServicesBuildTimeConfig.java b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DevServicesBuildTimeConfig.java index f36be4a686c69c..9a2e33823e8245 100644 --- a/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DevServicesBuildTimeConfig.java +++ b/extensions/datasource/runtime/src/main/java/io/quarkus/datasource/runtime/DevServicesBuildTimeConfig.java @@ -76,4 +76,12 @@ public class DevServicesBuildTimeConfig { */ @ConfigItem public Optional password; + + /** + * Path to a SQL script that will be loaded from the classpath and applied to the Dev Service database + * + * If the provider is not container based (e.g. a H2 or Derby Database) then this has no effect. + */ + @ConfigItem + public Optional initScriptPath; } diff --git a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java index 1e03cab907ecab..de239be3b9d4c8 100644 --- a/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java +++ b/extensions/devservices/db2/src/main/java/io/quarkus/devservices/db2/deployment/DB2DevServicesProcessor.java @@ -53,6 +53,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); LOG.info("Dev Services for IBM Db2 started."); diff --git a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java index 3668d11dcfee46..c308925a2d6716 100644 --- a/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java +++ b/extensions/devservices/mariadb/src/main/java/io/quarkus/devservices/mariadb/deployment/MariaDBDevServicesProcessor.java @@ -61,6 +61,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); diff --git a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java index ea7983b8dcf1c6..5f133ae371ff62 100644 --- a/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java +++ b/extensions/devservices/mssql/src/main/java/io/quarkus/devservices/mssql/deployment/MSSQLDevServicesProcessor.java @@ -49,6 +49,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); diff --git a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java index b5fb64d81b4e5f..41d62223c327b9 100644 --- a/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java +++ b/extensions/devservices/mysql/src/main/java/io/quarkus/devservices/mysql/deployment/MySQLDevServicesProcessor.java @@ -60,6 +60,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); diff --git a/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java b/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java index 29c5400efb3437..fa4ff0f5766dc4 100644 --- a/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java +++ b/extensions/devservices/oracle/src/main/java/io/quarkus/devservices/oracle/deployment/OracleDevServicesProcessor.java @@ -63,6 +63,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); diff --git a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java index 28b0c10ab25ea8..a4c9c9bf1c2243 100644 --- a/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java +++ b/extensions/devservices/postgresql/src/main/java/io/quarkus/devservices/postgresql/deployment/PostgresqlDevServicesProcessor.java @@ -60,6 +60,7 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt containerConfig.getAdditionalJdbcUrlProperties().forEach(container::withUrlParam); containerConfig.getCommand().ifPresent(container::setCommand); + containerConfig.getInitScriptPath().ifPresent(container::withInitScript); container.start(); diff --git a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java index 8f26efb32a0204..b3b50efb6e6a57 100644 --- a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java +++ b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java @@ -16,12 +16,15 @@ import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.JPMSExportBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.jdbc.db2.runtime.DB2AgroalConnectionConfigurer; import io.quarkus.jdbc.db2.runtime.DB2ServiceBindingConverter; public class JDBCDB2Processor { + private static final String DB2_DRIVER_CLASS = "com.ibm.db2.jcc.DB2Driver"; + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(Feature.JDBC_DB2); @@ -30,7 +33,7 @@ FeatureBuildItem feature() { @BuildStep void registerDriver(BuildProducer jdbcDriver, SslNativeConfigBuildItem sslNativeConfigBuildItem) { - jdbcDriver.produce(new JdbcDriverBuildItem(DatabaseKind.DB2, "com.ibm.db2.jcc.DB2Driver", + jdbcDriver.produce(new JdbcDriverBuildItem(DatabaseKind.DB2, DB2_DRIVER_CLASS, "com.ibm.db2.jcc.DB2XADataSource")); } @@ -51,6 +54,15 @@ void configureAgroalConnection(BuildProducer additional } } + @BuildStep + void registerDriverForReflection(BuildProducer reflectiveClass) { + //Not strictly necessary when using Agroal, as it also registers + //any JDBC driver being configured explicitly through its configuration. + //We register it for the sake of people not using Agroal, + //for example when the driver is used with OpenTelemetry JDBC instrumentation. + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, DB2_DRIVER_CLASS)); + } + @BuildStep NativeImageConfigBuildItem build() { // The DB2 JDBC driver has been updated with conditional checks for the diff --git a/extensions/jdbc/jdbc-oracle/runtime/pom.xml b/extensions/jdbc/jdbc-oracle/runtime/pom.xml index 52e99e443461e7..0e692fb507e755 100644 --- a/extensions/jdbc/jdbc-oracle/runtime/pom.xml +++ b/extensions/jdbc/jdbc-oracle/runtime/pom.xml @@ -43,6 +43,11 @@ io.quarkus quarkus-extension-maven-plugin + + + io.quarkus.jdbc.oracle + + maven-compiler-plugin diff --git a/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java new file mode 100644 index 00000000000000..0f73387c82673a --- /dev/null +++ b/extensions/jdbc/jdbc-oracle/runtime/src/main/java/io/quarkus/jdbc/oracle/runtime/graal/ObjIdSubstitutions.java @@ -0,0 +1,44 @@ +package io.quarkus.jdbc.oracle.runtime.graal; + +import java.security.SecureRandom; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +/** + * Substitutions required when `jdbc-oracle` is combined with `jdbc-db2`. + */ +@TargetClass(className = "java.rmi.server.ObjID") +public final class ObjIdSubstitutions { + + @Alias + @InjectAccessors(SecureRandomAccessor.class) + private static SecureRandom secureRandom; + +} + +class SecureRandomAccessor { + private static volatile SecureRandom RANDOM; + + static SecureRandom get() { + SecureRandom result = RANDOM; + if (result == null) { + /* Lazy initialization on first access. */ + result = initializeOnce(); + } + return result; + } + + private static synchronized SecureRandom initializeOnce() { + SecureRandom result = RANDOM; + if (result != null) { + /* Double-checked locking is OK because INSTANCE is volatile. */ + return result; + } + + result = new SecureRandom(); + RANDOM = result; + return result; + } +} \ No newline at end of file diff --git a/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceWithInitScriptTestCase.java b/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceWithInitScriptTestCase.java new file mode 100644 index 00000000000000..7e1bab5f32c104 --- /dev/null +++ b/extensions/jdbc/jdbc-postgresql/deployment/src/test/java/io/quarkus/jdbc/postgresql/deployment/DevServicesPostgresqlDatasourceWithInitScriptTestCase.java @@ -0,0 +1,42 @@ +package io.quarkus.jdbc.postgresql.deployment; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.ResultSet; + +import javax.inject.Inject; +import javax.sql.DataSource; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class DevServicesPostgresqlDatasourceWithInitScriptTestCase { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(javaArchive -> javaArchive.addAsResource("init-db.sql")) + .overrideConfigKey("quarkus.datasource.db-kind", "postgresql") + .overrideConfigKey("quarkus.datasource.devservices.init-script-path", "init-db.sql"); + + @Inject + DataSource ds; + + @Test + @DisplayName("Test if init-script-path executed successfully") + public void testDatasource() throws Exception { + int result = 0; + try (Connection con = ds.getConnection(); + CallableStatement cs = con.prepareCall("SELECT my_func()"); + ResultSet rs = cs.executeQuery()) { + if (rs.next()) { + result = rs.getInt(1); + } + } + assertEquals(100, result, "The init script should have been executed"); + } +} diff --git a/extensions/jdbc/jdbc-postgresql/deployment/src/test/resources/init-db.sql b/extensions/jdbc/jdbc-postgresql/deployment/src/test/resources/init-db.sql new file mode 100644 index 00000000000000..3c2dd9de15b497 --- /dev/null +++ b/extensions/jdbc/jdbc-postgresql/deployment/src/test/resources/init-db.sql @@ -0,0 +1 @@ +CREATE OR REPLACE FUNCTION my_func() RETURNS integer LANGUAGE plpgsql as $func$ BEGIN return 100; END $func$; \ No newline at end of file diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java new file mode 100644 index 00000000000000..543fe2d36ebc1b --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDriverJdbcDataSourcesBuildItem.java @@ -0,0 +1,18 @@ +package io.quarkus.opentelemetry.deployment; + +import java.util.List; + +import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Contains list of all {@link io.quarkus.agroal.spi.JdbcDataSourceBuildItem} using OpenTelemetryDriver. + */ +public final class OpenTelemetryDriverJdbcDataSourcesBuildItem extends SimpleBuildItem { + + public final List jdbcDataSources; + + OpenTelemetryDriverJdbcDataSourcesBuildItem(List jdbcDataSources) { + this.jdbcDataSources = List.copyOf(jdbcDataSources); + } +} diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java index 9cda1e84141f34..ee63ae6a3a647f 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java @@ -1,10 +1,15 @@ package io.quarkus.opentelemetry.deployment; +import static io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder.OPEN_TELEMETRY_DRIVER; + +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.ConfigValue; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationValue; @@ -14,11 +19,17 @@ import io.opentelemetry.instrumentation.annotations.SpanAttribute; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; +import io.quarkus.agroal.spi.JdbcDriverBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem; import io.quarkus.arc.processor.AnnotationsTransformer; import io.quarkus.arc.processor.InterceptorBindingRegistrar; +import io.quarkus.datasource.common.runtime.DataSourceUtil; +import io.quarkus.datasource.common.runtime.DatabaseKind; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.BuildSteps; @@ -155,4 +166,57 @@ void createOpenTelemetry( void storeVertxOnContextStorage(OpenTelemetryRecorder recorder, CoreVertxBuildItem vertx) { recorder.storeVertxOnContextStorage(vertx.getVertx()); } + + @BuildStep + void collectAllJdbcDataSourcesUsingOTelDriver(BuildProducer resultProducer, + List jdbcDataSources) { + final List result = new ArrayList<>(); + for (JdbcDataSourceBuildItem dataSource : jdbcDataSources) { + // if the datasource is explicitly configured to use the OTel driver... + if (dataSourceUsesOTelJdbcDriver(dataSource.getName())) { + result.add(dataSource); + } + } + if (!result.isEmpty()) { + resultProducer.produce(new OpenTelemetryDriverJdbcDataSourcesBuildItem(result)); + } + } + + private static boolean dataSourceUsesOTelJdbcDriver(String dataSourceName) { + List driverPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSourceName, "jdbc.driver"); + for (String driverPropertyKey : driverPropertyKeys) { + ConfigValue explicitlyConfiguredDriverValue = ConfigProvider.getConfig().getConfigValue(driverPropertyKey); + if (explicitlyConfiguredDriverValue.getValue() != null) { + return explicitlyConfiguredDriverValue.getValue().equals(OPEN_TELEMETRY_DRIVER); + } + } + return false; + } + + /** + * 'OracleDriver' register itself as driver in static initialization block, however we don't want to + * force runtime initialization for compatibility reasons, for more information please check: + * io.quarkus.jdbc.oracle.deployment.OracleMetadataOverrides#runtimeInitializeDriver + */ + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void registerOracleDriver(Optional otJdbcDataSourcesBuildItem, + List driverBuildItems, Capabilities capabilities, OpenTelemetryRecorder recorder) { + // check if there are data sources using OT driver and jdbc-oracle extension is present + if (otJdbcDataSourcesBuildItem.isPresent() && capabilities.isPresent(Capability.JDBC_ORACLE)) { + for (JdbcDataSourceBuildItem jdbcDataSource : otJdbcDataSourcesBuildItem.get().jdbcDataSources) { + if (jdbcDataSource.getDbKind().equals(DatabaseKind.ORACLE)) { + // now we know there is Oracle JDBC datasource + // let's find Oracle driver + for (JdbcDriverBuildItem driverBuildItem : driverBuildItems) { + if (DatabaseKind.ORACLE.equals(driverBuildItem.getDbKind())) { + recorder.registerJdbcDriver(driverBuildItem.getDriverClass()); + break; + } + } + break; + } + } + } + } } diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java index 7ceab490aa88b4..1ec00718187e65 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/dev/DevServicesOpenTelemetryProcessor.java @@ -3,9 +3,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.ConfigValue; +import java.util.Optional; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -15,17 +13,18 @@ import io.quarkus.deployment.annotations.BuildSteps; import io.quarkus.deployment.builditem.DevServicesAdditionalConfigBuildItem; import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig; +import io.quarkus.opentelemetry.deployment.OpenTelemetryDriverJdbcDataSourcesBuildItem; import io.quarkus.opentelemetry.deployment.OpenTelemetryEnabled; @BuildSteps(onlyIfNot = IsNormal.class, onlyIf = { OpenTelemetryEnabled.class, GlobalDevServicesConfig.Enabled.class }) public class DevServicesOpenTelemetryProcessor { @BuildStep - void devServicesDatasources(List jdbcDataSources, + void devServicesDatasources(Optional otJdbcDataSourcesBuildItem, BuildProducer devServicesAdditionalConfig) { - for (JdbcDataSourceBuildItem dataSource : jdbcDataSources) { - // if the datasource is explicitly configured to use the OTel driver... - if (dataSourceUsesOTelJdbcDriver(dataSource.getName())) { + if (otJdbcDataSourcesBuildItem.isPresent()) { + // found datasources explicitly configured to use the OTel driver + for (JdbcDataSourceBuildItem dataSource : otJdbcDataSourcesBuildItem.get().jdbcDataSources) { List urlPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSource.getName(), "jdbc.url"); devServicesAdditionalConfig.produce(new DevServicesAdditionalConfigBuildItem(devServicesConfig -> { Map overrides = new HashMap<>(); @@ -42,16 +41,4 @@ void devServicesDatasources(List jdbcDataSources, } } } - - private boolean dataSourceUsesOTelJdbcDriver(String dataSourceName) { - List driverPropertyKeys = DataSourceUtil.dataSourcePropertyKeys(dataSourceName, "jdbc.driver"); - for (String driverPropertyKey : driverPropertyKeys) { - ConfigValue explicitlyConfiguredDriverValue = ConfigProvider.getConfig().getConfigValue(driverPropertyKey); - if (explicitlyConfiguredDriverValue.getValue() != null) { - return explicitlyConfiguredDriverValue.getValue() - .equals("io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"); - } - } - return false; - } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java index a6a4c760feb48d..8fb318b8cabce9 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryRecorder.java @@ -1,7 +1,11 @@ package io.quarkus.opentelemetry.runtime; +import java.lang.reflect.InvocationTargetException; +import java.sql.Driver; import java.util.function.Supplier; +import org.jboss.logging.Logger; + import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.ContextStorage; @@ -16,6 +20,9 @@ @Recorder public class OpenTelemetryRecorder { + public static final String OPEN_TELEMETRY_DRIVER = "io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver"; + private static final Logger LOG = Logger.getLogger(OpenTelemetryRecorder.class); + /* STATIC INIT */ public void resetGlobalOpenTelemetryForDevMode() { GlobalOpenTelemetry.resetForTest(); @@ -46,4 +53,31 @@ public void eagerlyCreateContextStorage() { public void storeVertxOnContextStorage(Supplier vertx) { QuarkusContextStorage.vertx = vertx.get(); } + + public void registerJdbcDriver(String driverClass) { + try { + var constructors = Class + .forName(driverClass, true, Thread.currentThread().getContextClassLoader()) + .getConstructors(); + if (constructors.length == 1) { + // create driver + Driver driver = ((Driver) constructors[0].newInstance()); + // register the driver with OpenTelemetryDriver + Class + .forName(OPEN_TELEMETRY_DRIVER, true, Thread.currentThread().getContextClassLoader()) + .getMethod("addDriverCandidate", Driver.class) + .invoke(null, driver); + } else { + // drivers should have default constructor + LOG.warn(String.format( + "Class '%s' has more than one constructor and won't be registered as driver. JDBC instrumentation might not work properly in native mode.", + driverClass)); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException + | ClassNotFoundException e) { + LOG.warn(String.format( + "Failed to register '%s' driver. JDBC instrumentation might not work properly in native mode.", + driverClass)); + } + } } diff --git a/extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java b/extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java index 898774bf6d92ed..f3844c2e911eed 100644 --- a/extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java +++ b/extensions/smallrye-reactive-messaging-kafka/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/SmallRyeReactiveMessagingKafkaProcessor.java @@ -47,6 +47,7 @@ import io.quarkus.smallrye.reactivemessaging.kafka.RedisStateStore; import io.smallrye.mutiny.tuples.Functions.TriConsumer; import io.smallrye.reactive.messaging.kafka.KafkaConnector; +import io.smallrye.reactive.messaging.kafka.commit.ProcessingState; import io.vertx.kafka.client.consumer.impl.KafkaReadStreamImpl; public class SmallRyeReactiveMessagingKafkaProcessor { @@ -71,6 +72,7 @@ public void build(BuildProducer reflectiveClass) { .constructors(true) .finalFieldsWritable(true) .build()); + reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, ProcessingState.class)); } @BuildStep @@ -108,8 +110,12 @@ private static List getChannelProperties(String keySuffix, Config config } @BuildStep - public void checkpointRedis(BuildProducer additionalBean, Capabilities capabilities) { + public void checkpointRedis(BuildProducer additionalBean, + BuildProducer reflectiveClass, + Capabilities capabilities) { if (hasStateStoreConfig(REDIS_STATE_STORE, ConfigProvider.getConfig())) { + Optional checkpointStateType = getConnectorProperty("checkpoint.state-type", ConfigProvider.getConfig()); + checkpointStateType.ifPresent(s -> reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, s))); if (capabilities.isPresent(Capability.REDIS_CLIENT)) { additionalBean.produce(new AdditionalBeanBuildItem(RedisStateStore.Factory.class)); additionalBean.produce(new AdditionalBeanBuildItem(DatabindProcessingStateCodec.Factory.class)); diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java index 27ae071ec82e7b..6cc48b1848fd38 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/EventBusCodecProcessor.java @@ -168,7 +168,12 @@ private static Type extractPayloadTypeFromParameter(MethodInfo method) { if (parameters.isEmpty()) { return null; } - Type param = method.parameterType(0); + /* + * VertxProcessor.collectEventConsumers makes sure that only methods with either just the message object, + * or headers as first argument then message object are allowed. + */ + int messageIndex = parameters.size() == 1 ? 0 : 1; + Type param = method.parameterType(messageIndex); if (param.kind() == Type.Kind.CLASS) { return param; } else if (param.kind() == Type.Kind.PARAMETERIZED_TYPE) { diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/CodecRegistrationTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/CodecRegistrationTest.java index 58bfeb0b163999..046e2468258f1a 100644 --- a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/CodecRegistrationTest.java +++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/CodecRegistrationTest.java @@ -8,6 +8,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.inject.Inject; @@ -18,6 +19,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; +import io.vertx.core.MultiMap; import io.vertx.mutiny.core.Vertx; import io.vertx.mutiny.core.eventbus.Message; @@ -83,6 +85,8 @@ public void testWithPrimitiveTypesAndCompletionStage() { @Test public void testCodecRegistrationBasedOnParameterType() { + assertThat(bean.getSink().size()).isEqualTo(0); + String address = "address-5"; vertx.eventBus().send(address, new CustomType1("foo")); vertx.eventBus().send(address, new CustomType1("bar")); @@ -104,6 +108,20 @@ public void testCodecRegistrationBasedOnParameterType() { set = bean.getSink().stream().map(x -> (CustomType1) x).map(CustomType1::getName) .collect(Collectors.toSet()); assertThat(set).contains("foo-x", "bar-x", "baz-x"); + bean.getSink().clear(); + } + + @Test + public void testCodecRegistrationBasedOnHeadersAndParameterType() { + assertThat(bean.getSink().size()).isEqualTo(0); + + vertx.eventBus().send("address-9", new CustomType5("foo-x")); + + await().timeout(5, TimeUnit.SECONDS).until(() -> bean.getSink().size() == 1); + Set set = bean.getSink().stream().map(x -> (CustomType5) x).map(CustomType5::getName) + .collect(Collectors.toSet()); + assertThat(set).contains("foo-x"); + bean.getSink().clear(); } @Test @@ -199,6 +217,11 @@ CompletionStage codecRegistrationBasedReturnTypeAndCS(String n) { return CompletableFuture.completedFuture(new CustomType4(n)); } + @ConsumeEvent("address-9") + void codecRegistrationBasedOnHeadersParam(MultiMap headers, CustomType5 ct) { + sink.add(ct); + } + public List getSink() { return sink; } @@ -259,4 +282,16 @@ public String getName() { return name; } } + + static class CustomType5 { + private final String name; + + CustomType5(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index 68b5a4bbec98b4..d5932df694854f 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -340,7 +340,8 @@ static Set getProducerMethodTypeClosure(MethodInfo producerMethod, BeanDep throw new IllegalArgumentException("Unsupported return type"); } } - return restrictBeanTypes(types, beanDeployment.getAnnotations(producerMethod), beanDeployment.getBeanArchiveIndex()); + return restrictBeanTypes(types, beanDeployment.getAnnotations(producerMethod), beanDeployment.getBeanArchiveIndex(), + producerMethod); } static Set getProducerFieldTypeClosure(FieldInfo producerField, BeanDeployment beanDeployment) { @@ -369,7 +370,8 @@ static Set getProducerFieldTypeClosure(FieldInfo producerField, BeanDeploy throw new IllegalArgumentException("Unsupported return type"); } } - return restrictBeanTypes(types, beanDeployment.getAnnotations(producerField), beanDeployment.getBeanArchiveIndex()); + return restrictBeanTypes(types, beanDeployment.getAnnotations(producerField), beanDeployment.getBeanArchiveIndex(), + producerField); } static Set getClassBeanTypeClosure(ClassInfo classInfo, BeanDeployment beanDeployment) { @@ -381,7 +383,8 @@ static Set getClassBeanTypeClosure(ClassInfo classInfo, BeanDeployment bea types = getTypeClosure(classInfo, null, buildResolvedMap(typeParameters, typeParameters, Collections.emptyMap(), beanDeployment.getBeanArchiveIndex()), beanDeployment, null); } - return restrictBeanTypes(types, beanDeployment.getAnnotations(classInfo), beanDeployment.getBeanArchiveIndex()); + return restrictBeanTypes(types, beanDeployment.getAnnotations(classInfo), beanDeployment.getBeanArchiveIndex(), + classInfo); } static Map resolveDecoratedTypeParams(ClassInfo decoratedTypeClass, DecoratorInfo decorator) { @@ -577,7 +580,8 @@ static Map> resolvedTypeVariables(ClassInfo classIn return resolvedTypeVariables; } - static Set restrictBeanTypes(Set types, Collection annotations, IndexView index) { + static Set restrictBeanTypes(Set types, Collection annotations, IndexView index, + AnnotationTarget target) { AnnotationInstance typed = null; for (AnnotationInstance a : annotations) { if (a.name().equals(DotNames.TYPED)) { @@ -603,8 +607,7 @@ static Set restrictBeanTypes(Set types, Collection restrictBeanTypes(Set types, Collection + + 4.0.0 + + + io.quarkus + quarkus-integration-tests-parent + 999-SNAPSHOT + + + quarkus-integration-test-opentelemetry-jdbc-instrumentation + Quarkus - Integration Tests - OpenTelemetry JDBC instrumentation + + + + io.quarkus + quarkus-opentelemetry + + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + + io.opentelemetry + opentelemetry-sdk-testing + + + + + io.opentelemetry.instrumentation + opentelemetry-jdbc + + + io.quarkus + quarkus-jdbc-oracle + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-jdbc-mariadb + + + io.quarkus + quarkus-jdbc-db2 + + + io.quarkus + quarkus-hibernate-orm-panache + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + + + org.testcontainers + testcontainers + test + + + org.testcontainers + oracle-xe + test + + + junit + junit + + + + + org.testcontainers + postgresql + test + + + junit + junit + + + + + org.testcontainers + mariadb + test + + + junit + junit + + + + + org.testcontainers + db2 + test + + + junit + junit + + + + + + + io.quarkus + quarkus-resteasy-reactive-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-hibernate-orm-panache-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-oracle-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-mariadb-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-postgresql-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-db2-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + + maven-surefire-plugin + + true + + + + maven-failsafe-plugin + + true + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + + native-image + + + native + + + + + native + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + test-jdbc-instrumentation + + + test-containers + + + + + + maven-surefire-plugin + + false + + ${oracle.image} + ${postgres.image} + ${mariadb.image} + ${db2.image} + + + + + maven-failsafe-plugin + + false + + ${oracle.image} + ${postgres.image} + ${mariadb.image} + ${db2.image} + + + + + + + + test-jdbc-instrumentation-docker-oracle + + + start-containers + + + + + + io.fabric8 + docker-maven-plugin + + + + ${oracle.image} + + + 1521:1521 + + + quarkus + + + Oracle Database: + default + red + + + + + + DATABASE IS READY TO USE! + + + + + + true + + + + docker-start + compile + + stop + start + + + + docker-stop + post-integration-test + + stop + + + + + + org.codehaus.mojo + exec-maven-plugin + + + docker-prune + generate-resources + + exec + + + ${docker-prune.location} + + + + + + + + + diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java new file mode 100644 index 00000000000000..144bcc4911d8aa --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java @@ -0,0 +1,46 @@ +package io.quarkus.it.opentelemetry; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.data.SpanData; + +@Path("") +public class ExporterResource { + @Inject + InMemorySpanExporter inMemorySpanExporter; + + @GET + @Path("/reset") + public Response reset() { + inMemorySpanExporter.reset(); + return Response.ok().build(); + } + + @GET + @Path("/export") + public List export() { + return inMemorySpanExporter.getFinishedSpanItems() + .stream() + .filter(sd -> !sd.getName().contains("export") && !sd.getName().contains("reset")) + .collect(Collectors.toList()); + } + + @ApplicationScoped + static class InMemorySpanExporterProducer { + @Produces + @Singleton + InMemorySpanExporter inMemorySpanExporter() { + return InMemorySpanExporter.create(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java new file mode 100644 index 00000000000000..f6f2d8cb17d612 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/PingPongResource.java @@ -0,0 +1,54 @@ +package io.quarkus.it.opentelemetry; + +import java.util.function.Supplier; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; + +import io.quarkus.it.opentelemetry.model.Hit; +import io.quarkus.it.opentelemetry.model.db2.Db2Hit; +import io.quarkus.it.opentelemetry.model.mariadb.MariaDbHit; +import io.quarkus.it.opentelemetry.model.oracle.OracleHit; +import io.quarkus.it.opentelemetry.model.pg.PgHit; + +@ApplicationScoped +@Path("/") +public class PingPongResource { + + @Transactional + @POST + @Produces(MediaType.APPLICATION_JSON) + @Path("/hit/{tenant}") + public Hit createHit(@QueryParam("id") Long id, @PathParam("tenant") String tenant) { + switch (tenant) { + case "postgresql": + persist(PgHit::new, id); + return PgHit.findById(id); + case "oracle": + persist(OracleHit::new, id); + return OracleHit.findById(id); + case "mariadb": + persist(MariaDbHit::new, id); + return MariaDbHit.findById(id); + case "db2": + persist(Db2Hit::new, id); + return Db2Hit.findById(id); + default: + throw new IllegalArgumentException(); + } + } + + private void persist(Supplier hitSupplier, Long id) { + Hit hit = hitSupplier.get(); + hit.setId(id); + hit.setMessage("Hit message."); + hit.persist(); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java new file mode 100644 index 00000000000000..518360e01e3705 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/Hit.java @@ -0,0 +1,15 @@ +package io.quarkus.it.opentelemetry.model; + +public interface Hit { + + Long getId(); + + String getMessage(); + + void setId(Long id); + + void setMessage(String message); + + void persist(); + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java new file mode 100644 index 00000000000000..c0b6637ded50b6 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/db2/Db2Hit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.db2; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class Db2Hit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java new file mode 100644 index 00000000000000..c3d77e52cc23b8 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/mariadb/MariaDbHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.mariadb; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class MariaDbHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java new file mode 100644 index 00000000000000..490c7fa5cf521c --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/oracle/OracleHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.oracle; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class OracleHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java new file mode 100644 index 00000000000000..a79d1b79c7c4a9 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/java/io/quarkus/it/opentelemetry/model/pg/PgHit.java @@ -0,0 +1,36 @@ +package io.quarkus.it.opentelemetry.model.pg; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; +import io.quarkus.it.opentelemetry.model.Hit; + +@Entity +public class PgHit extends PanacheEntityBase implements Hit { + + @Id + public Long id; + + public String message; + + @Override + public Long getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setMessage(String message) { + this.message = message; + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html new file mode 100644 index 00000000000000..d3e7968fdf060a --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/META-INF/resources/test.html @@ -0,0 +1 @@ +Test diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties new file mode 100644 index 00000000000000..0002291d802dc5 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/main/resources/application.properties @@ -0,0 +1,52 @@ +# Setting these for tests explicitly. Not required in normal application +quarkus.application.name=opentelemetry-jdbc-instrumentation-it +quarkus.application.version=999-SNAPSHOT + +# Disable Dev Services as we don't want to start 3 databases at once (CI can't handle it within hang timeout) +quarkus.datasource.devservices.enabled=false +quarkus.devservices.enabled=false + +# JDBC instrumentation setting +driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver +model-base-dir=io.quarkus.it.opentelemetry.model. + +# Oracle data source +quarkus.hibernate-orm.oracle.datasource=oracle +quarkus.hibernate-orm.oracle.packages=${model-base-dir}oracle +quarkus.hibernate-orm.oracle.database.generation=none +quarkus.datasource.oracle.db-kind=oracle +quarkus.datasource.oracle.jdbc.driver=${driver} +quarkus.datasource.oracle.jdbc.max-size=1 + +# MariaDB data source +quarkus.hibernate-orm.mariadb.datasource=mariadb +quarkus.hibernate-orm.mariadb.packages=${model-base-dir}mariadb +quarkus.hibernate-orm.mariadb.database.generation=none +quarkus.datasource.mariadb.db-kind=mariadb +quarkus.datasource.mariadb.jdbc.driver=${driver} +quarkus.datasource.mariadb.jdbc.max-size=1 + +# PostgreSQL data source +quarkus.hibernate-orm.postgresql.datasource=postgresql +quarkus.hibernate-orm.postgresql.packages=${model-base-dir}pg +quarkus.hibernate-orm.postgresql.database.generation=none +quarkus.datasource.postgresql.db-kind=postgresql +quarkus.datasource.postgresql.jdbc.driver=${driver} +quarkus.datasource.postgresql.jdbc.max-size=1 + +# Db2 data source +quarkus.hibernate-orm.db2.datasource=db2 +quarkus.hibernate-orm.db2.packages=${model-base-dir}db2 +quarkus.hibernate-orm.db2.database.generation=none +quarkus.datasource.db2.db-kind=db2 +quarkus.datasource.db2.jdbc.driver=${driver} +quarkus.datasource.db2.jdbc.max-size=1 + +# Oracle test profile properties +%oracle-profile.quarkus.datasource."oracle".jdbc.url=jdbc:otel:oracle:thin:@localhost:1521/XE +%oracle-profile.quarkus.datasource."oracle".password=quarkus +%oracle-profile.quarkus.datasource."oracle".username=SYSTEM +%oracle-profile.quarkus.hibernate-orm."oracle".database.generation=drop-and-create +%oracle-profile.quarkus.hibernate-orm.mariadb.active=false +%oracle-profile.quarkus.hibernate-orm.postgresql.active=false +%oracle-profile.quarkus.hibernate-orm.db2.active=false diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java new file mode 100644 index 00000000000000..e2d9f170bdb676 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2LifecycleManager.java @@ -0,0 +1,56 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.Db2Container; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class Db2LifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(Db2LifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String DB2_IMAGE = System.getProperty("db2.image"); + private StartedDb2Container db2Container; + + @Override + public Map start() { + db2Container = new StartedDb2Container(); + LOGGER.info(db2Container.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.db2.jdbc.url", + String.format("jdbc:otel:db2://%s:%s/%s", db2Container.getHost(), + db2Container.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.db2.password", QUARKUS); + properties.put("quarkus.datasource.db2.username", QUARKUS); + properties.put("quarkus.hibernate-orm.db2.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.postgresql.active", "false"); + properties.put("quarkus.hibernate-orm.mariadb.active", "false"); + + return properties; + } + + @Override + public void stop() { + db2Container.stop(); + } + + private static final class StartedDb2Container extends Db2Container { + + public StartedDb2Container() { + super(DockerImageName + .parse(DB2_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse("ibmcom/db2"))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(5000); + acceptLicense(); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 00000000000000..75c39e53bc4049 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,11 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@EnabledIfSystemProperty(named = "enable-db2", matches = "true") +@QuarkusIntegrationTest +public class Db2OpenTelemetryJdbcInstrumentationIT extends Db2OpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 00000000000000..32c0e203898143 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/Db2OpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,19 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@EnabledIfSystemProperty(named = "enable-db2", matches = "true") +@QuarkusTest +@QuarkusTestResource(value = Db2LifecycleManager.class, restrictToAnnotatedClass = true) +public class Db2OpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testDb2SqlQueryTraced() { + testQueryTraced("db2", "Db2Hit"); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java new file mode 100644 index 00000000000000..b6c671839953ab --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbLifecycleManager.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class MariaDbLifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(MariaDbLifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String MARIADB_IMAGE = System.getProperty("mariadb.image"); + private StartedMariaDBContainer mariaDbContainer; + + @Override + public Map start() { + mariaDbContainer = new StartedMariaDBContainer(); + LOGGER.info(mariaDbContainer.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.mariadb.jdbc.url", + String.format("jdbc:otel:mariadb://%s:%s/%s", mariaDbContainer.getHost(), + mariaDbContainer.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.mariadb.password", QUARKUS); + properties.put("quarkus.datasource.mariadb.username", QUARKUS); + properties.put("quarkus.hibernate-orm.mariadb.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.postgresql.active", "false"); + properties.put("quarkus.hibernate-orm.db2.active", "false"); + + return properties; + } + + @Override + public void stop() { + mariaDbContainer.stop(); + } + + private static final class StartedMariaDBContainer extends MariaDBContainer { + + public StartedMariaDBContainer() { + super(DockerImageName + .parse(MARIADB_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse(MariaDBContainer.NAME))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(3306); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 00000000000000..618f3c9060955f --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MariaDbOpenTelemetryJdbcInstrumentationIT extends MariaDbOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 00000000000000..5034ac7e7c978b --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/MariaDbOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,17 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(value = MariaDbLifecycleManager.class, restrictToAnnotatedClass = true) +public class MariaDbOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testMariaDbQueryTraced() { + testQueryTraced("mariadb", "MariaDbHit"); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 00000000000000..37e4fccdbbef98 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; + +import io.restassured.common.mapper.TypeRef; + +public abstract class OpenTelemetryJdbcInstrumentationTest { + + @BeforeEach + void reset() { + given().get("/reset").then().statusCode(HTTP_OK); + await().atMost(5, SECONDS).until(() -> getSpans().size() == 0); + } + + private List> getSpans() { + return get("/export").body().as(new TypeRef<>() { + }); + } + + protected void testQueryTraced(String dbKind, String expectedTable) { + given() + .queryParam("id", 1) + .when().post("/hit/" + dbKind) + .then() + .statusCode(200) + .body("message", Matchers.equalTo("Hit message.")); + + // Assert insert has been traced + boolean hitInserted = false; + for (Map spanData : getSpans()) { + if (spanData.get("attributes") instanceof Map) { + final Map attributes = (Map) spanData.get("attributes"); + var dbOperation = attributes.get("db.operation"); + var dbTable = attributes.get("db.sql.table"); + if ("INSERT".equals(dbOperation) && expectedTable.equals(dbTable)) { + hitInserted = true; + break; + } + } + } + assertTrue(hitInserted, "JDBC insert statement was not traced."); + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 00000000000000..e7ccd488acefe8 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class OracleOpenTelemetryJdbcInstrumentationIT extends OracleOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 00000000000000..e7359e9c47d653 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/OracleOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,26 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(OracleOpenTelemetryJdbcInstrumentationTest.OracleTestProfile.class) +public class OracleOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testOracleQueryTraced() { + testQueryTraced("oracle", "OracleHit"); + } + + public static class OracleTestProfile implements QuarkusTestProfile { + + @Override + public String getConfigProfile() { + return "oracle-profile"; + } + } + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java new file mode 100644 index 00000000000000..d02473db11df97 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgreSqlLifecycleManager.java @@ -0,0 +1,55 @@ +package io.quarkus.it.opentelemetry; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class PostgreSqlLifecycleManager implements QuarkusTestResourceLifecycleManager { + private static final Logger LOGGER = Logger.getLogger(PostgreSqlLifecycleManager.class); + private static final String QUARKUS = "quarkus"; + private static final String POSTGRES_IMAGE = System.getProperty("postgres.image"); + private StartedPostgresContainer postgresContainer; + + @Override + public Map start() { + postgresContainer = new StartedPostgresContainer(); + LOGGER.info(postgresContainer.getLogs()); + + Map properties = new HashMap<>(); + properties.put("quarkus.datasource.postgresql.jdbc.url", + String.format("jdbc:otel:postgresql://%s:%s/%s", postgresContainer.getHost(), + postgresContainer.getFirstMappedPort(), QUARKUS)); + properties.put("quarkus.datasource.postgresql.password", QUARKUS); + properties.put("quarkus.datasource.postgresql.username", QUARKUS); + properties.put("quarkus.hibernate-orm.postgresql.database.generation", "drop-and-create"); + properties.put("quarkus.hibernate-orm.oracle.active", "false"); + properties.put("quarkus.hibernate-orm.mariadb.active", "false"); + properties.put("quarkus.hibernate-orm.db2.active", "false"); + + return properties; + } + + @Override + public void stop() { + postgresContainer.stop(); + } + + private static final class StartedPostgresContainer extends PostgreSQLContainer { + + public StartedPostgresContainer() { + super(DockerImageName + .parse(POSTGRES_IMAGE) + .asCompatibleSubstituteFor(DockerImageName.parse(PostgreSQLContainer.IMAGE))); + withDatabaseName(QUARKUS); + withUsername(QUARKUS); + withPassword(QUARKUS); + addExposedPort(5432); + start(); + } + } +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java new file mode 100644 index 00000000000000..08e62277beea24 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class PostgresOpenTelemetryJdbcInstrumentationIT extends PostgresOpenTelemetryJdbcInstrumentationTest { + +} diff --git a/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java new file mode 100644 index 00000000000000..cde8956e413eb1 --- /dev/null +++ b/integration-tests/opentelemetry-jdbc-instrumentation/src/test/java/io/quarkus/it/opentelemetry/PostgresOpenTelemetryJdbcInstrumentationTest.java @@ -0,0 +1,17 @@ +package io.quarkus.it.opentelemetry; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(value = PostgreSqlLifecycleManager.class, restrictToAnnotatedClass = true) +public class PostgresOpenTelemetryJdbcInstrumentationTest extends OpenTelemetryJdbcInstrumentationTest { + + @Test + void testPostgreSqlQueryTraced() { + testQueryTraced("postgresql", "PgHit"); + } + +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index eb1dba664c1b64..6f555e02688095 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -311,6 +311,7 @@ micrometer-mp-metrics micrometer-prometheus opentelemetry + opentelemetry-jdbc-instrumentation opentelemetry-vertx opentelemetry-reactive opentelemetry-grpc diff --git a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusConsumer.java b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusConsumer.java index 419edb41ee92da..1cfb1af79207f9 100644 --- a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusConsumer.java +++ b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusConsumer.java @@ -3,6 +3,7 @@ import javax.enterprise.context.ApplicationScoped; import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.MultiMap; @ApplicationScoped public class EventBusConsumer { @@ -17,4 +18,10 @@ public String name(String name) { return "Hello " + name; } + @ConsumeEvent("person-headers") + public String personWithHeader(MultiMap headers, Person person) { + String s = "Hello " + person.getFirstName() + " " + person.getLastName() + ", " + headers; + return s; + } + } diff --git a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusSender.java b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusSender.java index d623a40c3bfb21..46b21d3df937da 100644 --- a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusSender.java +++ b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/EventBusSender.java @@ -3,8 +3,10 @@ import javax.inject.Inject; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import io.smallrye.mutiny.Uni; +import io.vertx.core.eventbus.DeliveryOptions; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; import io.vertx.mutiny.core.eventbus.Message; @@ -22,6 +24,17 @@ public Uni helloToPerson(JsonObject json) { .onItem().transform(Message::body); } + @POST + @Path("/person2") + @Produces("text/plain") + public Uni helloToPersonWithHeaders(JsonObject json) { + return bus. request( + "person-headers", + new Person(json.getString("firstName"), json.getString("lastName")), + new DeliveryOptions().addHeader("header", "headerValue")) + .onItem().transform(Message::body); + } + @POST @Path("/pet") public Uni helloToPet(JsonObject json) { diff --git a/integration-tests/vertx/src/test/java/io/quarkus/it/vertx/EventBusTest.java b/integration-tests/vertx/src/test/java/io/quarkus/it/vertx/EventBusTest.java index 31ada479f393a6..be8f78e784dd1b 100644 --- a/integration-tests/vertx/src/test/java/io/quarkus/it/vertx/EventBusTest.java +++ b/integration-tests/vertx/src/test/java/io/quarkus/it/vertx/EventBusTest.java @@ -20,6 +20,19 @@ public void testEventBusWithString() { .then().statusCode(200).body(equalTo("Hello Bob Morane")); } + @Test + public void testEventBusWithObjectAndHeader() { + String body = new JsonObject() + .put("firstName", "Bob") + .put("lastName", "Morane") + .toString(); + given().contentType(ContentType.JSON).body(body) + .post("/vertx-test/event-bus/person2") + .then().statusCode(200) + // For some reason Multimap.toString() has \n at the end. + .body(equalTo("Hello Bob Morane, header=headerValue\n")); + } + @Test public void testEventBusWithPet() { String body = new JsonObject().put("name", "Neo").put("kind", "rabbit").toString(); diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index fd9153d2010fc4..e64a8ace36a76c 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -816,8 +816,13 @@ public void interceptTestMethod(Invocation invocation, ReflectiveInvocatio runExtensionMethod(invocationContext, extensionContext, true); invocation.skip(); } catch (Throwable t) { - for (var i : serverExceptions) { - t.addSuppressed(i); + for (var serverException : serverExceptions) { + if (t == serverException) { + // do not add a suppressed exception to itself + continue; + } + + t.addSuppressed(serverException); } throw t; } finally {