From f3690c1be6a19e4be9e5aeb22f522f486eb396ad Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 21 May 2025 17:27:02 +0300 Subject: [PATCH] Fix tests for hibernate 7 (#13890) --- .../hibernate-6.0/javaagent/build.gradle.kts | 45 +- .../hibernate/v6_0/AbstractHibernateTest.java | 47 ++ .../hibernate/v6_0/ProcedureCallTest.java | 2 +- .../hibernate/v6_0/SessionTest.java | 2 +- .../instrumentation/hibernate/v6_0/Value.java | 47 ++ .../resources/hibernate.cfg.xml | 29 + .../procedure-call-hibernate.cfg.xml | 0 .../hibernate/v7_0/AbstractHibernateTest.java | 47 ++ .../hibernate/v7_0/ProcedureCallTest.java | 193 +++++ .../hibernate/v7_0/SessionTest.java | 756 ++++++++++++++++++ .../instrumentation/hibernate/v7_0/Value.java | 44 + .../hibernate/v7_0/ValueGeneratedId.java | 19 + .../hibernate/v7_0/ValueIdGenerator.java | 19 + .../resources/hibernate.cfg.xml | 29 + .../procedure-call-hibernate.cfg.xml | 24 + .../v6_0/SessionInstrumentation.java | 4 +- .../hibernate/v6_0/AbstractHibernateTest.java | 2 +- .../hibernate/v6_0/CriteriaTest.java | 2 +- .../hibernate/v6_0/EntityManagerTest.java | 4 +- .../instrumentation/hibernate/v6_0/Value.java | 7 +- .../spring-testing/build.gradle.kts | 2 +- .../test/java/spring/jpa/SpringJpaTest.java | 2 +- 22 files changed, 1314 insertions(+), 12 deletions(-) create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java rename instrumentation/hibernate/hibernate-6.0/javaagent/src/{test => hibernate6Test}/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java (99%) rename instrumentation/hibernate/hibernate-6.0/javaagent/src/{test => hibernate6Test}/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java (99%) create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/hibernate.cfg.xml rename instrumentation/hibernate/hibernate-6.0/javaagent/src/{test => hibernate6Test}/resources/procedure-call-hibernate.cfg.xml (100%) create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/AbstractHibernateTest.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ProcedureCallTest.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/SessionTest.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/Value.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueGeneratedId.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueIdGenerator.java create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/hibernate.cfg.xml create mode 100644 instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/procedure-call-hibernate.cfg.xml diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts b/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts index 6cab40bb5c..e55b3d8116 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/build.gradle.kts @@ -28,24 +28,67 @@ dependencies { testImplementation("com.sun.xml.bind:jaxb-impl:2.2.11") testImplementation("javax.activation:activation:1.1.1") testImplementation("org.hsqldb:hsqldb:2.0.0") - testImplementation("org.springframework.data:spring-data-jpa:3.0.0") + testLibrary("org.springframework.data:spring-data-jpa:3.0.0") } otelJava { minJavaVersionSupported.set(JavaVersion.VERSION_11) } +val latestDepTest = findProperty("testLatestDeps") as Boolean + +testing { + suites { + val hibernate6Test by registering(JvmTestSuite::class) { + dependencies { + implementation("com.h2database:h2:1.4.197") + implementation("org.hsqldb:hsqldb:2.0.0") + if (latestDepTest) { + implementation("org.hibernate:hibernate-core:6.+") + } else { + implementation("org.hibernate:hibernate-core:6.0.0.Final") + } + } + } + + val hibernate7Test by registering(JvmTestSuite::class) { + dependencies { + implementation("com.h2database:h2:1.4.197") + implementation("org.hsqldb:hsqldb:2.0.0") + if (latestDepTest) { + implementation("org.hibernate:hibernate-core:7.+") + } else { + implementation("org.hibernate:hibernate-core:7.0.0.Final") + } + } + } + } +} + tasks { withType().configureEach { // TODO run tests both with and without experimental span attributes jvmArgs("-Dotel.instrumentation.hibernate.experimental-span-attributes=true") } + named("compileHibernate7TestJava", JavaCompile::class).configure { + options.release.set(17) + } + val testJavaVersion = + gradle.startParameter.projectProperties.get("testJavaVersion")?.let(JavaVersion::toVersion) + ?: JavaVersion.current() + if (!testJavaVersion.isCompatibleWith(JavaVersion.VERSION_17)) { + named("hibernate7Test", Test::class).configure { + enabled = false + } + } + val testStableSemconv by registering(Test::class) { jvmArgs("-Dotel.semconv-stability.opt-in=database") } check { dependsOn(testStableSemconv) + dependsOn(testing.suites) } } diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java new file mode 100644 index 0000000000..9c2e6eda4c --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.ArrayList; +import java.util.List; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class AbstractHibernateTest { + protected static SessionFactory sessionFactory; + protected static List prepopulated; + + @RegisterExtension + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @BeforeAll + protected static void setup() { + sessionFactory = new Configuration().configure().buildSessionFactory(); + // Pre-populate the DB, so delete/update can be tested. + Session writer = sessionFactory.openSession(); + writer.beginTransaction(); + prepopulated = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + prepopulated.add(new Value("Hello :) " + i)); + writer.persist(prepopulated.get(i)); + } + writer.getTransaction().commit(); + writer.close(); + } + + @AfterAll + protected static void cleanup() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java similarity index 99% rename from instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java rename to instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java index 616a0bdd74..e1cd7b612f 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/ProcedureCallTest.java @@ -38,7 +38,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -public class ProcedureCallTest { +class ProcedureCallTest { protected static SessionFactory sessionFactory; protected static List prepopulated; diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java similarity index 99% rename from instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java rename to instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java index f28fcb457f..0aadd99ca8 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionTest.java @@ -45,7 +45,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @SuppressWarnings("deprecation") // testing instrumentation of deprecated class -public class SessionTest extends AbstractHibernateTest { +class SessionTest extends AbstractHibernateTest { @ParameterizedTest(name = "{index}: {0}") @MethodSource("provideHibernateActionParameters") void testHibernateAction(Parameter parameter) { diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java new file mode 100644 index 0000000000..2e33a22d0f --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v6_0; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.NamedQuery; + +@Entity +@Table +@NamedQuery(name = "TestNamedQuery", query = "from Value") +public class Value { + + private Long id; + private String name; + + public Value() {} + + public Value(String name) { + this.name = name; + } + + @Id + @GeneratedValue(generator = "increment") + @GenericGenerator(name = "increment", strategy = "increment") + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String title) { + name = title; + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/hibernate.cfg.xml b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/hibernate.cfg.xml new file mode 100644 index 0000000000..a57f93094a --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/hibernate.cfg.xml @@ -0,0 +1,29 @@ + + + + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + org.hibernate.dialect.H2Dialect + + 3 + org.hibernate.cache.internal.NoCacheProvider + true + + + create + + + + + + + diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/resources/procedure-call-hibernate.cfg.xml b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/procedure-call-hibernate.cfg.xml similarity index 100% rename from instrumentation/hibernate/hibernate-6.0/javaagent/src/test/resources/procedure-call-hibernate.cfg.xml rename to instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate6Test/resources/procedure-call-hibernate.cfg.xml diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/AbstractHibernateTest.java new file mode 100644 index 0000000000..fe7629447f --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/AbstractHibernateTest.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.ArrayList; +import java.util.List; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +abstract class AbstractHibernateTest { + protected static SessionFactory sessionFactory; + protected static List prepopulated; + + @RegisterExtension + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @BeforeAll + protected static void setup() { + sessionFactory = new Configuration().configure().buildSessionFactory(); + // Pre-populate the DB, so delete/update can be tested. + Session writer = sessionFactory.openSession(); + writer.beginTransaction(); + prepopulated = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + prepopulated.add(new Value("Hello :) " + i)); + writer.persist(prepopulated.get(i)); + } + writer.getTransaction().commit(); + writer.close(); + } + + @AfterAll + protected static void cleanup() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ProcedureCallTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ProcedureCallTest.java new file mode 100644 index 0000000000..42162edd47 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ProcedureCallTest.java @@ -0,0 +1,193 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import jakarta.persistence.ParameterMode; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.procedure.ProcedureCall; +import org.hibernate.procedure.ProcedureParameter; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ProcedureCallTest { + protected static SessionFactory sessionFactory; + protected static List prepopulated; + + @RegisterExtension + protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @BeforeAll + static void setup() throws SQLException { + sessionFactory = + new Configuration().configure("procedure-call-hibernate.cfg.xml").buildSessionFactory(); + // Pre-populate the DB, so delete/update can be tested. + Session writer = sessionFactory.openSession(); + writer.beginTransaction(); + prepopulated = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + prepopulated.add(new Value("Hello :) " + i)); + writer.persist(prepopulated.get(i)); + } + writer.getTransaction().commit(); + writer.close(); + + // Create a stored procedure. + Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", "1"); + Statement stmt = conn.createStatement(); + stmt.execute( + "CREATE PROCEDURE TEST_PROC() MODIFIES SQL DATA BEGIN ATOMIC INSERT INTO Value VALUES (420, 'fred'); END"); + stmt.close(); + conn.close(); + } + + @AfterAll + static void cleanup() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation + @Test + void testProcedureCall() { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + + ProcedureCall call = session.createStoredProcedureCall("TEST_PROC"); + call.getOutputs(); + + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("ProcedureCall.getOutputs TEST_PROC") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + satisfies( + AttributeKey.stringKey("hibernate.session_id"), + val -> val.isInstanceOf(String.class))), + span -> + span.hasName("CALL test.TEST_PROC") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), "hsqldb"), + equalTo(maybeStable(DB_NAME), "test"), + equalTo(DB_USER, emitStableDatabaseSemconv() ? null : "sa"), + equalTo( + DB_CONNECTION_STRING, + emitStableDatabaseSemconv() ? null : "hsqldb:mem:"), + equalTo(maybeStable(DB_STATEMENT), "{call TEST_PROC()}"), + equalTo(maybeStable(DB_OPERATION), "CALL")), + span -> + span.hasName("Transaction.commit") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + AttributeKey.stringKey("hibernate.session_id"), + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id")))))); + } + + @Test + void testFailingProcedureCall() { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + + ProcedureCall call = session.createStoredProcedureCall("TEST_PROC"); + ProcedureParameter parameterRegistration = + call.registerParameter("nonexistent", Long.class, ParameterMode.IN); + call.setParameter(parameterRegistration, 420L); + try { + call.getOutputs(); + } catch (RuntimeException e) { + // We expected this. + } + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("ProcedureCall.getOutputs TEST_PROC") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + AttributeKey.stringKey("exception.type"), + "org.hibernate.exception.AuthException"), + satisfies( + AttributeKey.stringKey("exception.message"), + val -> val.startsWith("could not prepare statement")), + satisfies( + AttributeKey.stringKey("exception.stacktrace"), + val -> val.isNotNull()))) + .hasAttributesSatisfyingExactly( + satisfies( + AttributeKey.stringKey("hibernate.session_id"), + val -> val.isInstanceOf(String.class))), + span -> + span.hasName("Transaction.commit") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + equalTo( + AttributeKey.stringKey("hibernate.session_id"), + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id")))))); + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/SessionTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/SessionTest.java new file mode 100644 index 0000000000..3359d3885a --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/SessionTest.java @@ -0,0 +1,756 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Named.named; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; +import jakarta.persistence.Timeout; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; +import org.hibernate.LockMode; +import org.hibernate.ReplicationMode; +import org.hibernate.Session; +import org.hibernate.StatelessSession; +import org.hibernate.UnknownEntityTypeException; +import org.hibernate.query.SelectionQuery; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +@SuppressWarnings("deprecation") // testing instrumentation of deprecated class +class SessionTest extends AbstractHibernateTest { + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("provideHibernateActionParameters") + void testHibernateAction(Parameter parameter) { + Session session = sessionFactory.openSession(); + Value value = session.merge(prepopulated.get(0)); + testing.waitForTraces(1); + testing.clearData(); + + testing.runWithSpan( + "parent", + () -> { + session.beginTransaction(); + try { + parameter.sessionMethodTest.accept(session, value); + } catch (RuntimeException e) { + // We expected this, we should see the error field set on the span. + } + session.getTransaction().commit(); + session.close(); + }); + assertTraces(parameter, "refresh".equals(parameter.methodName)); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("provideHibernateActionWithStatelessSessionParameters") + void testHibernateActionStatelessSession(Parameter parameter) { + testing.runWithSpan( + "parent", + () -> { + StatelessSession session = sessionFactory.openStatelessSession(); + session.beginTransaction(); + try { + parameter.statelessSessionMethodTest.accept(session, prepopulated.get(0)); + } catch (RuntimeException e) { + // We expected this, we should see the error field set on the span. + } + session.getTransaction().commit(); + session.close(); + }); + assertTraces(parameter, true); + } + + private static void assertTraces(Parameter parameter, boolean hasClientSpan) { + testing.waitAndAssertTraces( + trace -> { + List> assertions = new ArrayList<>(); + assertions.add(span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent()); + assertions.add( + span -> + assertSessionSpan( + span, + trace.getSpan(0), + "Session." + parameter.methodName + " " + parameter.resource)); + if (hasClientSpan) { + assertions.add(span -> assertClientSpan(span, trace.getSpan(1))); + } + assertions.add( + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Transaction.commit", + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id")))); + + trace.hasSpansSatisfyingExactly(assertions); + }); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("provideHibernateReplicateParameters") + void testHibernateReplicate(Parameter parameter) { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + try { + parameter.sessionMethodTest.accept(session, prepopulated.get(0)); + } catch (RuntimeException e) { + // We expected this, we should see the error field set on the span. + } + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + assertSessionSpan( + span, + trace.getSpan(0), + "Session." + parameter.methodName + " " + parameter.resource), + span -> assertClientSpan(span, trace.getSpan(1), "SELECT"), + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Transaction.commit", + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))), + span -> assertClientSpan(span, trace.getSpan(3)))); + } + + @Test + void testHibernateFailedReplicate() { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + try { + session.replicate( + Long.valueOf(123) /* Not a valid entity */, ReplicationMode.OVERWRITE); + } catch (RuntimeException e) { + // We expected this, we should see the error field set on the span. + } + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("Session.replicate java.lang.Long") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasException(new UnknownEntityTypeException("java.lang.Long")) + .hasAttributesSatisfyingExactly( + satisfies( + AttributeKey.stringKey("hibernate.session_id"), + val -> val.isInstanceOf(String.class))), + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Transaction.commit", + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))))); + } + + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("provideHibernateCommitActionParameters") + void testHibernateCommitAction(Parameter parameter) { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + try { + parameter.sessionMethodTest.accept(session, prepopulated.get(0)); + } catch (RuntimeException e) { + // We expected this, we should see the error field set on the span. + } + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + assertSessionSpan( + span, + trace.getSpan(0), + "Session." + parameter.methodName + " " + parameter.resource), + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Transaction.commit", + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))), + span -> assertClientSpan(span, trace.getSpan(2)))); + } + + @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("provideAttachesStateToQueryParameters") + void testAttachesStateToQuery(Parameter parameter) { + testing.runWithSpan( + "parent", + () -> { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + SelectionQuery query = parameter.queryBuildMethod.apply(session); + query.list(); + session.getTransaction().commit(); + session.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> assertSessionSpan(span, trace.getSpan(0), parameter.resource), + span -> + span.hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("h2")), + equalTo(maybeStable(DB_NAME), "db1"), + equalTo(DB_USER, emitStableDatabaseSemconv() ? null : "sa"), + equalTo( + DB_CONNECTION_STRING, + emitStableDatabaseSemconv() ? null : "h2:mem:"), + satisfies( + maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), + equalTo(maybeStable(DB_OPERATION), "SELECT"), + equalTo(maybeStable(DB_SQL_TABLE), "Value")), + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Transaction.commit", + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))))); + } + + @Test + void testHibernateOverlappingSessions() { + AtomicReference sessionId1 = new AtomicReference<>(); + AtomicReference sessionId2 = new AtomicReference<>(); + AtomicReference sessionId3 = new AtomicReference<>(); + + testing.runWithSpan( + "overlapping Sessions", + () -> { + Session session1 = sessionFactory.openSession(); + session1.beginTransaction(); + StatelessSession session2 = sessionFactory.openStatelessSession(); + Session session3 = sessionFactory.openSession(); + + Value value1 = new Value("Value 1"); + session1.merge(value1); + session2.insert(new Value("Value 2")); + session3.merge(new Value("Value 3")); + session1.remove(value1); + + session2.close(); + session1.getTransaction().commit(); + session1.close(); + session3.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("overlapping Sessions"), + span -> { + assertSessionSpan( + span, + trace.getSpan(0), + "Session.merge io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value"); + sessionId1.set( + trace + .getSpan(1) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))); + }, + span -> { + assertSessionSpan( + span, + trace.getSpan(0), + "Session.insert io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value"); + sessionId2.set( + trace + .getSpan(2) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))); + }, + span -> assertClientSpan(span, trace.getSpan(2), "INSERT"), + span -> { + assertSessionSpan( + span, + trace.getSpan(0), + "Session.merge io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value"); + sessionId3.set( + trace + .getSpan(4) + .getAttributes() + .get(AttributeKey.stringKey("hibernate.session_id"))); + }, + span -> + assertSpanWithSessionId( + span, + trace.getSpan(0), + "Session.remove io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + sessionId1.get()), + span -> + assertSpanWithSessionId( + span, trace.getSpan(0), "Transaction.commit", sessionId1.get()), + span -> assertClientSpan(span, trace.getSpan(6), "INSERT"))); + + assertNotEquals(sessionId1.get(), sessionId2.get()); + assertNotEquals(sessionId2.get(), sessionId3.get()); + assertNotEquals(sessionId1.get(), sessionId3.get()); + } + + private static Stream provideHibernateActionParameters() { + List> sessionMethodTests = + Arrays.asList( + (session, val) -> session.lock(val, LockMode.READ), + (session, val) -> session.lock(val, LockMode.READ, Timeout.seconds(1)), + (session, val) -> session.refresh(val), + (session, val) -> session.find(Value.class, val.getId())); + + return Stream.of( + Arguments.of( + named( + "lock", + new Parameter( + "lock", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(0), + null))), + Arguments.of( + named( + "lock with options", + new Parameter( + "lock", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(1), + null))), + Arguments.of( + named( + "refresh", + new Parameter( + "refresh", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(2), + null))), + Arguments.of( + named( + "find", + new Parameter( + "find", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(3), + null)))); + } + + private static Stream provideHibernateActionWithStatelessSessionParameters() { + List> statelessSessionMethodTests = + Arrays.asList( + (statelessSession, val) -> statelessSession.refresh(val), + (statelessSession, val) -> + statelessSession.refresh( + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", val), + (statelessSession, val) -> + statelessSession.get( + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", val.getId()), + (statelessSession, val) -> statelessSession.get(Value.class, val.getId()), + (statelessSession, val) -> statelessSession.insert(new Value("insert me")), + (statelessSession, val) -> + statelessSession.insert( + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + new Value("insert me")), + (statelessSession, val) -> statelessSession.insert(null, new Value("insert me")), + (statelessSession, val) -> { + val.setName("New name"); + statelessSession.update(val); + }, + (statelessSession, val) -> { + val.setName("New name"); + statelessSession.update( + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", val); + }, + (statelessSession, val) -> { + statelessSession.delete(val); + prepopulated.remove(val); + }, + (statelessSession, val) -> { + statelessSession.delete( + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", val); + prepopulated.remove(val); + }); + + return Stream.of( + Arguments.of( + named( + "refresh", + new Parameter( + "refresh", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(0)))), + Arguments.of( + named( + "refresh with entity name", + new Parameter( + "refresh", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(1)))), + Arguments.of( + named( + "get with entity name", + new Parameter( + "get", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(2)))), + Arguments.of( + named( + "get with entity class", + new Parameter( + "get", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(3)))), + Arguments.of( + named( + "insert", + new Parameter( + "insert", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(4)))), + Arguments.of( + named( + "insert with entity name", + new Parameter( + "insert", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(5)))), + Arguments.of( + named( + "insert with null entity name", + new Parameter( + "insert", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(6)))), + Arguments.of( + named( + "update (StatelessSession)", + new Parameter( + "update", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(7)))), + Arguments.of( + named( + "update with entity name (StatelessSession)", + new Parameter( + "update", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(8)))), + Arguments.of( + named( + "delete (Session)", + new Parameter( + "delete", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(9)))), + Arguments.of( + named( + "delete with entity name (Session)", + new Parameter( + "delete", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + null, + statelessSessionMethodTests.get(10))))); + } + + private static Stream provideHibernateReplicateParameters() { + List> sessionMethodTests = + Arrays.asList( + (session, val) -> { + Value replicated = new Value(val.getName() + " replicated"); + replicated.setId(val.getId()); + session.replicate(replicated, ReplicationMode.OVERWRITE); + }, + (session, val) -> { + Value replicated = new Value(val.getName() + " replicated"); + replicated.setId(val.getId()); + session.replicate("Value", replicated, ReplicationMode.OVERWRITE); + }); + + return Stream.of( + Arguments.of( + named( + "replicate", + new Parameter( + "replicate", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(0), + null))), + Arguments.of( + named( + "replicate by entityName", + new Parameter("replicate", "Value", null, sessionMethodTests.get(1), null)))); + } + + private static Stream provideHibernateCommitActionParameters() { + List> sessionMethodTests = + Arrays.asList( + (session, val) -> session.merge(new Value("merge me in")), + (session, val) -> session.merge("Value", new Value("merge me in")), + (session, val) -> session.persist(new Value("merge me in")), + (session, val) -> session.persist("Value", new Value("merge me in")), + (session, val) -> session.persist(null, new Value("merge me in")), + (session, val) -> { + session.remove(val); + prepopulated.remove(val); + }); + + return Stream.of( + Arguments.of( + named( + "merge", + new Parameter( + "merge", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(0), + null))), + Arguments.of( + named( + "merge with entity name", + new Parameter("merge", "Value", null, sessionMethodTests.get(1), null))), + Arguments.of( + named( + "persist", + new Parameter( + "persist", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(2), + null))), + Arguments.of( + named( + "persist with entity name", + new Parameter("persist", "Value", null, sessionMethodTests.get(3), null))), + Arguments.of( + named( + "persist with null entity name", + new Parameter( + "persist", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(4), + null))), + Arguments.of( + named( + "remove", + new Parameter( + "remove", + "io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + null, + sessionMethodTests.get(5), + null)))); + } + + private static Stream provideAttachesStateToQueryParameters() { + List>> queryBuildMethods = + Arrays.asList( + session -> session.createQuery("from Value"), + session -> session.getNamedQuery("TestNamedQuery"), + session -> session.createNativeQuery("SELECT * FROM Value"), + session -> session.createSelectionQuery("from Value")); + + return Stream.of( + Arguments.of( + named( + "createQuery", + new Parameter( + "createQuery", + "SELECT io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + queryBuildMethods.get(0), + null, + null))), + Arguments.of( + named( + "getNamedQuery", + new Parameter( + "getNamedQuery", + "SELECT io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + queryBuildMethods.get(1), + null, + null))), + Arguments.of( + named( + "createNativeQuery", + new Parameter( + "createNativeQuery", "SELECT Value", queryBuildMethods.get(2), null, null))), + Arguments.of( + named( + "createSelectionQuery", + new Parameter( + "createSelectionQuery", + "SELECT io.opentelemetry.javaagent.instrumentation.hibernate.v7_0.Value", + queryBuildMethods.get(3), + null, + null)))); + } + + private static class Parameter { + Parameter( + String methodName, + String resource, + Function> queryBuildMethod, + BiConsumer sessionMethodTest, + BiConsumer statelessSessionMethodTest) { + this.methodName = methodName; + this.resource = resource; + this.sessionMethodTest = sessionMethodTest; + this.queryBuildMethod = queryBuildMethod; + this.statelessSessionMethodTest = statelessSessionMethodTest; + } + + public final String methodName; + public final String resource; + public final Function> queryBuildMethod; + public final BiConsumer sessionMethodTest; + public final BiConsumer statelessSessionMethodTest; + } + + private static SpanDataAssert assertSessionSpan( + SpanDataAssert span, SpanData parent, String spanName) { + return span.hasName(spanName) + .hasKind(SpanKind.INTERNAL) + .hasParent(parent) + .hasAttributesSatisfyingExactly( + satisfies( + AttributeKey.stringKey("hibernate.session_id"), + val -> val.isInstanceOf(String.class))); + } + + private static SpanDataAssert assertSpanWithSessionId( + SpanDataAssert span, SpanData parent, String spanName, String sessionId) { + return span.hasName(spanName) + .hasKind(SpanKind.INTERNAL) + .hasParent(parent) + .hasAttributesSatisfyingExactly( + equalTo(AttributeKey.stringKey("hibernate.session_id"), sessionId)); + } + + @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation + private static SpanDataAssert assertClientSpan(SpanDataAssert span, SpanData parent) { + return span.hasKind(SpanKind.CLIENT) + .hasParent(parent) + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("h2")), + equalTo(maybeStable(DB_NAME), "db1"), + equalTo(DB_USER, emitStableDatabaseSemconv() ? null : "sa"), + equalTo(DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "h2:mem:"), + satisfies(maybeStable(DB_STATEMENT), val -> val.isInstanceOf(String.class)), + satisfies(maybeStable(DB_OPERATION), val -> val.isInstanceOf(String.class)), + equalTo(maybeStable(DB_SQL_TABLE), "Value")); + } + + @SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation + private static SpanDataAssert assertClientSpan( + SpanDataAssert span, SpanData parent, String verb) { + return span.hasName(verb.concat(" db1.Value")) + .hasKind(SpanKind.CLIENT) + .hasParent(parent) + .hasAttributesSatisfyingExactly( + equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("h2")), + equalTo(maybeStable(DB_NAME), "db1"), + equalTo(DB_USER, emitStableDatabaseSemconv() ? null : "sa"), + equalTo(DB_CONNECTION_STRING, emitStableDatabaseSemconv() ? null : "h2:mem:"), + satisfies( + maybeStable(DB_STATEMENT), + stringAssert -> stringAssert.startsWith(verb.toLowerCase(Locale.ROOT))), + equalTo(maybeStable(DB_OPERATION), verb), + equalTo(maybeStable(DB_SQL_TABLE), "Value")); + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/Value.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/Value.java new file mode 100644 index 0000000000..54cdf9dddf --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/Value.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.annotations.NamedQuery; + +@Entity +@Table +@NamedQuery(name = "TestNamedQuery", query = "from Value") +public class Value { + + private Long id; + private String name; + + public Value() {} + + public Value(String name) { + this.name = name; + } + + @Id + @ValueGeneratedId + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String title) { + name = title; + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueGeneratedId.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueGeneratedId.java new file mode 100644 index 0000000000..416d3fb070 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueGeneratedId.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.hibernate.annotations.IdGeneratorType; + +@IdGeneratorType(ValueIdGenerator.class) +@Target({FIELD, METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValueGeneratedId {} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueIdGenerator.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueIdGenerator.java new file mode 100644 index 0000000000..ff7e403079 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v7_0/ValueIdGenerator.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.hibernate.v7_0; + +import java.util.concurrent.atomic.AtomicLong; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.enhanced.SequenceStyleGenerator; + +public class ValueIdGenerator extends SequenceStyleGenerator { + private final AtomicLong counter = new AtomicLong(); + + @Override + public Object generate(SharedSessionContractImplementor session, Object owner) { + return counter.incrementAndGet(); + } +} diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/hibernate.cfg.xml b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/hibernate.cfg.xml new file mode 100644 index 0000000000..a90947d3a2 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/hibernate.cfg.xml @@ -0,0 +1,29 @@ + + + + + + + + + org.h2.Driver + jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE + sa + + org.hibernate.dialect.H2Dialect + + 3 + org.hibernate.cache.internal.NoCacheProvider + true + + + create + + + + + + + diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/procedure-call-hibernate.cfg.xml b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/procedure-call-hibernate.cfg.xml new file mode 100644 index 0000000000..2c445fac67 --- /dev/null +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/hibernate7Test/resources/procedure-call-hibernate.cfg.xml @@ -0,0 +1,24 @@ + + + + + + + + class,hbm + org.hibernate.dialect.HSQLDialect + true + org.hsqldb.jdbcDriver + sa + 1 + jdbc:hsqldb:mem:test + create + + + + + + + diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java index 6853916ca6..e6907d71bf 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/SessionInstrumentation.java @@ -64,7 +64,9 @@ public class SessionInstrumentation implements TypeInstrumentation { "fireLock", "refresh", "insert", - "delete")), + "delete", + "remove", + "upsert")), SessionInstrumentation.class.getName() + "$SessionMethodAdvice"); // Handle the non-generic 'get' separately. transformer.applyAdviceToMethod( diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java index d297fa6974..9c2e6eda4c 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/AbstractHibernateTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; -public abstract class AbstractHibernateTest { +abstract class AbstractHibernateTest { protected static SessionFactory sessionFactory; protected static List prepopulated; diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java index e458fddecb..f4a78cb78e 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/CriteriaTest.java @@ -34,7 +34,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -public class CriteriaTest extends AbstractHibernateTest { +class CriteriaTest extends AbstractHibernateTest { private static Stream provideParameters() { List>> interactions = Arrays.asList(Query::getResultList, Query::uniqueResult, Query::getSingleResultOrNull); diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java index b2aabba012..7cfaa7aecb 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/EntityManagerTest.java @@ -43,7 +43,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -public class EntityManagerTest extends AbstractHibernateTest { +class EntityManagerTest extends AbstractHibernateTest { static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("test-pu"); @@ -258,7 +258,7 @@ public class EntityManagerTest extends AbstractHibernateTest { named( "remove", new Parameter( - "delete", + "remove", "io.opentelemetry.javaagent.instrumentation.hibernate.v6_0.Value", true, true, diff --git a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java index 2e33a22d0f..477cddc0a7 100644 --- a/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java +++ b/instrumentation/hibernate/hibernate-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hibernate/v6_0/Value.java @@ -9,9 +9,12 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; -import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.NamedQuery; +@SuppressWarnings({ + "deprecation", + "removal" +}) // GenericGenerator is deprecated for removal in hibernate 7 @Entity @Table @NamedQuery(name = "TestNamedQuery", query = "from Value") @@ -28,7 +31,7 @@ public class Value { @Id @GeneratedValue(generator = "increment") - @GenericGenerator(name = "increment", strategy = "increment") + @org.hibernate.annotations.GenericGenerator(name = "increment", strategy = "increment") public Long getId() { return id; } diff --git a/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts b/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts index 91152bd509..4a00fb6e5a 100644 --- a/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts +++ b/instrumentation/hibernate/hibernate-6.0/spring-testing/build.gradle.kts @@ -15,7 +15,7 @@ dependencies { testInstrumentation(project(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent")) testImplementation("org.hsqldb:hsqldb:2.0.0") - testImplementation("org.springframework.data:spring-data-jpa:3.0.0") + testLibrary("org.springframework.data:spring-data-jpa:3.0.0") springAgent("org.springframework:spring-instrument:6.0.7") } diff --git a/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java b/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java index f8b900140b..702ce58149 100644 --- a/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java +++ b/instrumentation/hibernate/hibernate-6.0/spring-testing/src/test/java/spring/jpa/SpringJpaTest.java @@ -344,7 +344,7 @@ class SpringJpaTest { stringKey("hibernate.session_id"), val -> val.isInstanceOf(String.class))), span -> - span.hasName("Session.delete spring.jpa.Customer") + span.hasName("Session.remove spring.jpa.Customer") .hasKind(INTERNAL) .hasParent(trace.getSpan(0)) .hasAttributesSatisfyingExactly(