diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index 6b5f27fcd21..38ac14e0d01 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -36,7 +36,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ad77331434..955803c7cf3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' # Workaround for https://github.com/gradle/actions/issues/21 to use config cache - name: Cache buildSrc diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c94bcf3efd3..c8ddd2dfeb8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index 8d37523888d..3b4726150be 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -17,7 +17,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index d236a64b0ce..56972bdbd30 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -16,7 +16,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/generate-javadocs.yml b/.github/workflows/generate-javadocs.yml index 7cb5c903ca3..b023883240e 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -17,7 +17,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/integration-tests-macrobenchmark.yml b/.github/workflows/integration-tests-macrobenchmark.yml index 56dc9f04973..5389f2eb01a 100644 --- a/.github/workflows/integration-tests-macrobenchmark.yml +++ b/.github/workflows/integration-tests-macrobenchmark.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/integration-tests-size.yml b/.github/workflows/integration-tests-size.yml index ebdad2a4d9a..49fe70fabae 100644 --- a/.github/workflows/integration-tests-size.yml +++ b/.github/workflows/integration-tests-size.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: "temurin" - java-version: "17" + java-version: "21" # Workaround for https://github.com/gradle/actions/issues/21 to use config cache - name: Cache buildSrc diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index dce506124d0..821b4230593 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -33,7 +33,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/integration-tests-ui.yml b/.github/workflows/integration-tests-ui.yml index e1f11ac1f8c..cda4e801c8a 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -30,7 +30,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index c5038af6f9f..8f07d36d73a 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -23,7 +23,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/.github/workflows/spring-boot-2-matrix.yml b/.github/workflows/spring-boot-2-matrix.yml index 0ad89bd9caa..3617c8ae6ed 100644 --- a/.github/workflows/spring-boot-2-matrix.yml +++ b/.github/workflows/spring-boot-2-matrix.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' # Workaround for https://github.com/gradle/actions/issues/21 to use config cache - name: Cache buildSrc diff --git a/.github/workflows/spring-boot-3-matrix.yml b/.github/workflows/spring-boot-3-matrix.yml index 8b68a33e658..4f4c0ce7101 100644 --- a/.github/workflows/spring-boot-3-matrix.yml +++ b/.github/workflows/spring-boot-3-matrix.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' # Workaround for https://github.com/gradle/actions/issues/21 to use config cache - name: Cache buildSrc diff --git a/.github/workflows/spring-boot-4-matrix.yml b/.github/workflows/spring-boot-4-matrix.yml index c353f575cc9..d8f679190ec 100644 --- a/.github/workflows/spring-boot-4-matrix.yml +++ b/.github/workflows/spring-boot-4-matrix.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' # Workaround for https://github.com/gradle/actions/issues/21 to use config cache - name: Cache buildSrc diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index f77fd18f821..921b28450eb 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -121,7 +121,7 @@ jobs: uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Setup Gradle uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4354c72a89..7d4abc716ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ We suggest opening an issue to discuss bigger changes before investing on a big # Requirements -The project requires you to run JDK 17. +The project requires you to run JDK 21. ## Android diff --git a/build.gradle.kts b/build.gradle.kts index 52e17b1d309..1776997de2e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,10 @@ plugins { alias(libs.plugins.sentry) apply false } +check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) { + "This build requires JDK 21 or newer, but runs on JDK ${JavaVersion.current()}." +} + buildscript { repositories { google() @@ -112,6 +116,35 @@ allprojects { subprojects { apply { plugin("io.sentry.spotless") } + // Mockito 5 and mockito-kotlin 5+ are built for Java 11, so test code (never published) can't + // share the Java 8 target of the published code; it compiles for the build JDK instead. + // Registered once each plugin is applied, so this afterEvaluate runs after the plugins and the + // module have pinned the JVM target of all compile tasks. + listOf("java", "com.android.library", "com.android.application").forEach { pluginId -> + plugins.withId(pluginId) { + afterEvaluate { + val isTestTask = { task: Task -> task.name.contains("Test") && !task.name.contains("AndroidTest") } + tasks.withType().matching(isTestTask).configureEach { + sourceCompatibility = JavaVersion.VERSION_21.toString() + targetCompatibility = JavaVersion.VERSION_21.toString() + } + tasks.withType().matching(isTestTask).configureEach { + compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) + } + } + } + } + listOf("com.android.library", "com.android.application").forEach { pluginId -> + plugins.withId(pluginId) { + // Robolectric's SDK 37 sandbox (the default, as targetSdk is 37) reflectively writes + // FileDescriptor fields in ApplicationSharedMemory; without this, setup fails with + // "Failed to interact with raw FileDescriptor internals". + tasks.withType().configureEach { + jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED") + } + } + } + plugins.withId(Config.QualityPlugins.detektPlugin) { configure { buildUponDefaultConfig = true @@ -166,6 +199,15 @@ subprojects { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } + + // Compile against the class library of the target Java version instead of the build + // JDK's, so newer JDK APIs (e.g. List.getFirst()) can't leak into published bytecode. + val javaExtension = the() + tasks.named("compileJava") { + options.release.set( + provider { javaExtension.targetCompatibility.majorVersion.toInt() } + ) + } } // AGP 9 defaults Android modules to Java 11. Pin the published library modules back diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 451e5827ed9..876c922b222 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { `kotlin-dsl` } @@ -7,7 +5,3 @@ plugins { repositories { mavenCentral() } - -tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d79bda786c..aa598b8c35b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,7 +64,7 @@ buildconfig = { id = "com.github.gmazzo.buildconfig", version = "5.6.5" } dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } dokka-javadoc = { id = "org.jetbrains.dokka-javadoc", version = "2.0.0" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.13.0" } -errorprone = { id = "net.ltgt.errorprone", version = "3.0.1" } +errorprone = { id = "net.ltgt.errorprone", version = "5.1.1" } gradle-versions = { id = "com.github.ben-manes.versions", version = "0.42.0" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } detekt = { id = "io.gitlab.arturbosch.detekt", version = "1.23.8" } @@ -129,7 +129,7 @@ caffeine-jcache = { module = "com.github.ben-manes.caffeine:jcache", version = " coil-compose = { module = "io.coil-kt:coil-compose", version = "2.6.0" } commons-compress = {module = "org.apache.commons:commons-compress", version = "1.28.0"} context-propagation = { module = "io.micrometer:context-propagation", version = "1.1.0" } -errorprone-core = { module = "com.google.errorprone:error_prone_core", version = "2.11.0" } +errorprone-core = { module = "com.google.errorprone:error_prone_core", version = "2.50.0" } feign-core = { module = "io.github.openfeign:feign-core", version.ref = "feign" } feign-gson = { module = "io.github.openfeign:feign-gson", version.ref = "feign" } graphql-java17 = { module = "com.graphql-java:graphql-java", version = "17.3" } @@ -154,7 +154,7 @@ lottie-compose = { module = "com.airbnb.android:lottie-compose", version = "6.7. logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } nopen-annotations = { module = "com.jakewharton.nopen:nopen-annotations", version.ref = "nopen" } nopen-checker = { module = "com.jakewharton.nopen:nopen-checker", version.ref = "nopen" } -nullaway = { module = "com.uber.nullaway:nullaway", version = "0.9.5" } +nullaway = { module = "com.uber.nullaway:nullaway", version = "0.14.2" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp" } openfeature = { module = "dev.openfeature:sdk", version.ref = "openfeature" } @@ -273,15 +273,14 @@ hsqldb = { module = "org.hsqldb:hsqldb", version = "2.6.1" } javafaker = { module = "com.github.javafaker:javafaker", version = "1.0.2" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } leakcanary-instrumentation = { module = "com.squareup.leakcanary:leakcanary-android-instrumentation", version = "2.14" } -mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "4.1.0" } -mockito-kotlin-spring7 = { module = "org.mockito.kotlin:mockito-kotlin", version = "6.0.0" } -mockito-inline = { module = "org.mockito:mockito-inline", version = "4.8.0" } +mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version = "6.4.0" } +mockito-core = { module = "org.mockito:mockito-core", version = "5.24.0" } msgpack = { module = "org.msgpack:msgpack-core", version = "0.9.8" } okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } # apollo-runtime 5.x pulls in okhttp 5.x; mockwebserver 4.x is incompatible with that classpath okhttp-mockwebserver5 = { module = "com.squareup.okhttp3:mockwebserver", version = "5.3.0" } okio = { module = "com.squareup.okio:okio", version = "1.13.0" } -roboelectric = { module = "org.robolectric:robolectric", version = "4.15" } +roboelectric = { module = "org.robolectric:robolectric", version = "4.17" } [bundles] androidx-room2 = ["androidx-room-runtime", "androidx-room-ktx"] diff --git a/sentry-android-core/build.gradle.kts b/sentry-android-core/build.gradle.kts index 0d7f16af199..4facf6f4c23 100644 --- a/sentry-android-core/build.gradle.kts +++ b/sentry-android-core/build.gradle.kts @@ -118,7 +118,7 @@ dependencies { testImplementation(libs.awaitility.kotlin) testImplementation(libs.google.truth) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(projects.sentryTestSupport) testImplementation(projects.sentrySpotlight) testImplementation(projects.sentryAndroidFragment) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationNotResponding.java b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationNotResponding.java index 7b21c2e392c..9d76cdf298b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationNotResponding.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationNotResponding.java @@ -17,6 +17,8 @@ final class ApplicationNotResponding extends RuntimeException { private static final long serialVersionUID = 252541144579117016L; + // Never serialized. + @SuppressWarnings("serial") private final @Nullable Thread thread; ApplicationNotResponding(final @Nullable String message) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java index f842f18674b..0ae5583253e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryUserFeedbackButton.java @@ -13,6 +13,8 @@ /** * @deprecated `SentryUserFeedbackButton` will be removed in the next major version */ +// Constructors call overridable methods; making them final would break the public API. +@SuppressWarnings("this-escape") @Deprecated public class SentryUserFeedbackButton extends Button { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java index 82376a75d8b..090ca2d2669 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java @@ -50,6 +50,8 @@ *

This class is also responsible for - determining the app start type (cold, warm) - determining * if the app was launched in foreground */ +// Constructors call overridable methods; making them final would break the public API. +@SuppressWarnings("this-escape") @ApiStatus.Internal public class AppStartMetrics extends ActivityLifecycleCallbacksAdapter { public interface HeadlessAppStartListener { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ApplicationExitIntegrationTestBase.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ApplicationExitIntegrationTestBase.kt index 82cf1c25c9c..3fd6140f41c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ApplicationExitIntegrationTestBase.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ApplicationExitIntegrationTestBase.kt @@ -27,6 +27,7 @@ import org.junit.Rule import org.junit.rules.TemporaryFolder import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.anyVararg import org.mockito.kotlin.argThat import org.mockito.kotlin.atMost import org.mockito.kotlin.check @@ -316,7 +317,7 @@ abstract class ApplicationExitIntegrationTestBase { .log( any(), argThat { startsWith("Timed out waiting to flush previous session to its own file.") }, - any(), + anyVararg(), ) } @@ -337,7 +338,7 @@ abstract class ApplicationExitIntegrationTestBase { .log( any(), argThat { startsWith("Timed out waiting to flush previous session to its own file.") }, - any(), + anyVararg(), ) verify(fixture.scopes).captureEvent(any(), any()) } @@ -354,7 +355,7 @@ abstract class ApplicationExitIntegrationTestBase { .log( any(), argThat { startsWith("Timed out waiting to flush previous session to its own file.") }, - any(), + anyVararg(), ) assertTrue((fixture.options.envelopeDiskCache as EnvelopeCache).waitPreviousSessionFlush()) } diff --git a/sentry-android-core/src/test/resources/robolectric.properties b/sentry-android-core/src/test/resources/robolectric.properties index c216eeab9dc..c0f2ddf599d 100644 --- a/sentry-android-core/src/test/resources/robolectric.properties +++ b/sentry-android-core/src/test/resources/robolectric.properties @@ -1,2 +1,2 @@ -sdk=21 +sdk=23 application=io.sentry.android.core.ApplicationStub diff --git a/sentry-android-fragment/build.gradle.kts b/sentry-android-fragment/build.gradle.kts index e613f1fd5bb..3c7a0404970 100644 --- a/sentry-android-fragment/build.gradle.kts +++ b/sentry-android-fragment/build.gradle.kts @@ -70,7 +70,7 @@ dependencies { testImplementation(libs.androidx.fragment.ktx) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-android-navigation/build.gradle.kts b/sentry-android-navigation/build.gradle.kts index 19b9b3c753f..ddc6d6f4161 100644 --- a/sentry-android-navigation/build.gradle.kts +++ b/sentry-android-navigation/build.gradle.kts @@ -71,7 +71,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.roboelectric) testImplementation(libs.androidx.core) diff --git a/sentry-android-navigation3/build.gradle.kts b/sentry-android-navigation3/build.gradle.kts index da7ad9f36f4..b9d02bb49d4 100644 --- a/sentry-android-navigation3/build.gradle.kts +++ b/sentry-android-navigation3/build.gradle.kts @@ -67,7 +67,7 @@ dependencies { testImplementation(libs.androidx.test.core) testImplementation(libs.androidx.test.ext.junit) testImplementation(libs.google.truth) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.mockito.kotlin) testImplementation(libs.roboelectric) } diff --git a/sentry-android-replay/build.gradle.kts b/sentry-android-replay/build.gradle.kts index 1318f7767c9..1a92f9a0b36 100644 --- a/sentry-android-replay/build.gradle.kts +++ b/sentry-android-replay/build.gradle.kts @@ -85,7 +85,7 @@ dependencies { testImplementation(libs.awaitility.kotlin) testImplementation(libs.google.truth) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.androidx.compose.ui) testImplementation(libs.androidx.compose.foundation) testImplementation(libs.androidx.compose.foundation.layout) diff --git a/sentry-android-sqlite/build.gradle.kts b/sentry-android-sqlite/build.gradle.kts index fd15526fcbe..38ea832082f 100644 --- a/sentry-android-sqlite/build.gradle.kts +++ b/sentry-android-sqlite/build.gradle.kts @@ -79,7 +79,7 @@ dependencies { testImplementation(libs.androidx.sqlite) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-android-timber/build.gradle.kts b/sentry-android-timber/build.gradle.kts index f66c1a8a96b..92f14b37c8c 100644 --- a/sentry-android-timber/build.gradle.kts +++ b/sentry-android-timber/build.gradle.kts @@ -80,7 +80,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.androidx.test.ext.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index 8ef43a9b691..3fd80e495fd 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -14,6 +14,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import org.mockito.kotlin.any +import org.mockito.kotlin.anyVararg import org.mockito.kotlin.check import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -326,7 +327,7 @@ class SentryTimberTreeTest { fun `Tree adds a log if min level is higher`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.logs).log(any(), any(), any(), any()) + verify(fixture.logs).log(any(), any(), any(), anyVararg()) } @Test diff --git a/sentry-apollo-3/build.gradle.kts b/sentry-apollo-3/build.gradle.kts index 357d5224495..9f07593e5f6 100644 --- a/sentry-apollo-3/build.gradle.kts +++ b/sentry-apollo-3/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.kotlinx.coroutines) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp.mockwebserver) } diff --git a/sentry-apollo-4/build.gradle.kts b/sentry-apollo-4/build.gradle.kts index 078d26b8ff8..7534347c4e1 100644 --- a/sentry-apollo-4/build.gradle.kts +++ b/sentry-apollo-4/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { testImplementation(libs.kotlinx.coroutines) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp.mockwebserver) testImplementation("org.jetbrains.kotlin:kotlin-reflect:2.0.0") } diff --git a/sentry-apollo-5/build.gradle.kts b/sentry-apollo-5/build.gradle.kts index e4cd7dd0345..2f93f36e739 100644 --- a/sentry-apollo-5/build.gradle.kts +++ b/sentry-apollo-5/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { testImplementation(libs.kotlinx.coroutines) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp.mockwebserver5) } diff --git a/sentry-apollo/build.gradle.kts b/sentry-apollo/build.gradle.kts index 570214e60b8..1187f2c3545 100644 --- a/sentry-apollo/build.gradle.kts +++ b/sentry-apollo/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.kotlinx.coroutines) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp.mockwebserver) } @@ -45,6 +45,8 @@ tasks.withType().configureEach { options.errorprone { check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) option("NullAway:AnnotatedPackages", "io.sentry") + // Generated by the Apollo plugin and marked "DO NOT MODIFY". + excludedPaths.set(".*/LaunchDetailsQuery.java") } } diff --git a/sentry-async-profiler/build.gradle.kts b/sentry-async-profiler/build.gradle.kts index 17454baa662..976452f94aa 100644 --- a/sentry-async-profiler/build.gradle.kts +++ b/sentry-async-profiler/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { testImplementation(projects.sentryTestSupport) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter.java b/sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter.java index 718fae422f7..a0ce94c08ec 100644 --- a/sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter.java +++ b/sentry-async-profiler/src/main/java/io/sentry/asyncprofiler/convert/JfrAsyncProfilerToSentryProfileConverter.java @@ -83,7 +83,7 @@ private class ProfileEventVisitor implements EventCollector.Visitor { private final @NotNull JfrReader jfr; private final @NotNull Arguments args; - public ProfileEventVisitor( + ProfileEventVisitor( @NotNull SentryProfile sentryProfile, @NotNull SentryStackTraceFactory stackTraceFactory, @NotNull JfrReader jfr, diff --git a/sentry-compose/build.gradle.kts b/sentry-compose/build.gradle.kts index 394885df1b8..19fce9f95b5 100644 --- a/sentry-compose/build.gradle.kts +++ b/sentry-compose/build.gradle.kts @@ -69,7 +69,7 @@ kotlin { implementation(libs.androidx.test.runner) implementation(libs.google.truth) implementation(libs.kotlin.test.junit) - implementation(libs.mockito.inline) + implementation(libs.mockito.core) implementation(libs.mockito.kotlin) implementation(libs.roboelectric) } diff --git a/sentry-graphql-22/build.gradle.kts b/sentry-graphql-22/build.gradle.kts index 32db28fae8f..ba36db3e40b 100644 --- a/sentry-graphql-22/build.gradle.kts +++ b/sentry-graphql-22/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { testImplementation(libs.graphql.java22) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.springboot.starter.graphql) diff --git a/sentry-graphql-core/build.gradle.kts b/sentry-graphql-core/build.gradle.kts index 34f71ab9cfb..5044127fb42 100644 --- a/sentry-graphql-core/build.gradle.kts +++ b/sentry-graphql-core/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { testImplementation(libs.graphql.java17) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.springboot.starter.graphql) diff --git a/sentry-graphql/build.gradle.kts b/sentry-graphql/build.gradle.kts index d92dc52c6d7..484c2ed8499 100644 --- a/sentry-graphql/build.gradle.kts +++ b/sentry-graphql/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { testImplementation(libs.graphql.java17) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.springboot.starter.graphql) diff --git a/sentry-jcache/build.gradle.kts b/sentry-jcache/build.gradle.kts index b388f35881f..3c8829eaf04 100644 --- a/sentry-jcache/build.gradle.kts +++ b/sentry-jcache/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-jdbc/build.gradle.kts b/sentry-jdbc/build.gradle.kts index e2a7f573138..425f707083d 100644 --- a/sentry-jdbc/build.gradle.kts +++ b/sentry-jdbc/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { testImplementation(libs.hsqldb) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java index 4442052dd41..c0eb576ce77 100644 --- a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java +++ b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java @@ -40,6 +40,8 @@ import org.slf4j.MDC; /** Logging handler in charge of sending the java.util.logging records to a Sentry server. */ +// Constructors call overridable methods; making them final would break the public API. +@SuppressWarnings("this-escape") @Open public class SentryHandler extends Handler { public static final String MECHANISM_TYPE = "JulSentryHandler"; diff --git a/sentry-kafka/build.gradle.kts b/sentry-kafka/build.gradle.kts index 0d543bad270..94ce4d528c1 100644 --- a/sentry-kafka/build.gradle.kts +++ b/sentry-kafka/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.kafka.clients) } diff --git a/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaConsumerTracing.java b/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaConsumerTracing.java index dbce760de99..a45505f1019 100644 --- a/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaConsumerTracing.java +++ b/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaConsumerTracing.java @@ -247,7 +247,7 @@ private void finishTransaction( try { final double enqueuedTimeSeconds = Double.parseDouble(enqueuedTimeStr); - final double nowSeconds = DateUtils.millisToSeconds(System.currentTimeMillis()); + final double nowSeconds = DateUtils.millisToSeconds((double) System.currentTimeMillis()); final long latencyMs = (long) ((nowSeconds - enqueuedTimeSeconds) * 1000); return latencyMs >= 0 ? latencyMs : null; } catch (NumberFormatException ignored) { diff --git a/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaProducer.java b/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaProducer.java index bcc538e339c..573038d5ee9 100644 --- a/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaProducer.java +++ b/sentry-kafka/src/main/java/io/sentry/kafka/SentryKafkaProducer.java @@ -114,7 +114,8 @@ static final class SentryProducerHandler implements InvocationHandler { } @Override - @SuppressWarnings("unchecked") + // Proxy equals() is identity-based. + @SuppressWarnings({"unchecked", "ReferenceEquality"}) public @Nullable Object invoke( final @NotNull Object proxy, final @NotNull Method method, final @Nullable Object[] args) throws Throwable { @@ -236,7 +237,8 @@ private void maybeInjectHeaders(final @NotNull Headers headers, final @Nullable headers.remove(SENTRY_ENQUEUED_TIME_HEADER); headers.add( SENTRY_ENQUEUED_TIME_HEADER, - DateUtils.doubleToBigDecimal(DateUtils.millisToSeconds(System.currentTimeMillis())) + DateUtils.doubleToBigDecimal( + DateUtils.millisToSeconds((double) System.currentTimeMillis())) .toString() .getBytes(StandardCharsets.UTF_8)); } catch (Throwable t) { diff --git a/sentry-ktor-client/build.gradle.kts b/sentry-ktor-client/build.gradle.kts index a1eb9150d6f..2d17fc7ce53 100644 --- a/sentry-ktor-client/build.gradle.kts +++ b/sentry-ktor-client/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { testImplementation(projects.sentryTestSupport) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.ktor.client.core) testImplementation(libs.ktor.client.java) testImplementation(libs.okhttp.mockwebserver) diff --git a/sentry-launchdarkly-android/build.gradle.kts b/sentry-launchdarkly-android/build.gradle.kts index ddc982e9d6a..df081b21213 100644 --- a/sentry-launchdarkly-android/build.gradle.kts +++ b/sentry-launchdarkly-android/build.gradle.kts @@ -70,6 +70,6 @@ dependencies { testImplementation(libs.androidx.test.ext.junit) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.launchdarkly.android) } diff --git a/sentry-launchdarkly-server/build.gradle.kts b/sentry-launchdarkly-server/build.gradle.kts index 95aba9faaf5..1e9c074226f 100644 --- a/sentry-launchdarkly-server/build.gradle.kts +++ b/sentry-launchdarkly-server/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.launchdarkly.server) } diff --git a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java index 722845e2d70..66f61a182e5 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -197,7 +197,8 @@ protected void captureLog(@NotNull ILoggingEvent loggingEvent) { final @NotNull SentryLogParameters params = SentryLogParameters.create(attributes); params.setOrigin("auto.log.logback"); - Sentry.logger().log(sentryLevel, params, formattedMessage, arguments); + Sentry.logger() + .log(sentryLevel, params, formattedMessage, arguments != null ? arguments : new Object[0]); } @SuppressWarnings("deprecation") diff --git a/sentry-okhttp/build.gradle.kts b/sentry-okhttp/build.gradle.kts index 5abd1cf26b9..0d2b2e2d2ec 100644 --- a/sentry-okhttp/build.gradle.kts +++ b/sentry-okhttp/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { testImplementation(projects.sentryTestSupport) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp) testImplementation(libs.okhttp.mockwebserver) testImplementation(libs.google.truth) diff --git a/sentry-openfeature/build.gradle.kts b/sentry-openfeature/build.gradle.kts index b079ead1fc5..56561810334 100644 --- a/sentry-openfeature/build.gradle.kts +++ b/sentry-openfeature/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.openfeature) } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index 143ebb6c16d..ad70d2346a8 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -17,7 +17,7 @@ public final class OtelContextScopesStorage implements IScopesStorage { @Override public void init() { - /** + /* * We're currently overriding the storage mechanism to allow for cleanup of non closed OTel * scopes. These happen when using e.g. Sentry static API due to getCurrentScopes() invoking * Context.makeCurrent and then ignoring the returned lifecycle token (OTel Scope). After fixing diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java index e44afc5a2dc..feec48908fd 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java @@ -29,6 +29,8 @@ public final class SentryOtelThreadLocalStorage implements ContextStorage { private static final ThreadLocal THREAD_LOCAL_STORAGE = new ThreadLocal<>(); + // Same identity check as OpenTelemetry's ThreadLocalContextStorage. + @SuppressWarnings("ReferenceEquality") @Override public Scope attach(Context toAttach) { if (toAttach == null) { diff --git a/sentry-quartz/build.gradle.kts b/sentry-quartz/build.gradle.kts index f4f0d9d07d2..bb04435f1d9 100644 --- a/sentry-quartz/build.gradle.kts +++ b/sentry-quartz/build.gradle.kts @@ -33,7 +33,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) } tasks.withType().configureEach { diff --git a/sentry-reactor/src/main/java/io/sentry/reactor/SentryReactorUtils.java b/sentry-reactor/src/main/java/io/sentry/reactor/SentryReactorUtils.java index 16fed92bd01..04d87a8b45b 100644 --- a/sentry-reactor/src/main/java/io/sentry/reactor/SentryReactorUtils.java +++ b/sentry-reactor/src/main/java/io/sentry/reactor/SentryReactorUtils.java @@ -48,7 +48,7 @@ public static Mono withSentryForkedRoots(final @NotNull Mono mono) { */ public static Mono withSentryScopes( final @NotNull Mono mono, final @NotNull IScopes scopes) { - /** + /* * WARNING: Cannot set the scopes as current. It would be used by others to clone again causing * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. */ @@ -96,7 +96,7 @@ public static Flux withSentryForkedRoots(final @NotNull Flux flux) { */ public static Flux withSentryScopes( final @NotNull Flux flux, final @NotNull IScopes scopes) { - /** + /* * WARNING: Cannot set the scopes as current. It would be used by others to fork again causing * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. */ diff --git a/sentry-spotlight/build.gradle.kts b/sentry-spotlight/build.gradle.kts index a8d538c2e2e..9833a432473 100644 --- a/sentry-spotlight/build.gradle.kts +++ b/sentry-spotlight/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(projects.sentryTestSupport) } diff --git a/sentry-spring-7/build.gradle.kts b/sentry-spring-7/build.gradle.kts index 4e5ea54d294..3f1d6d18a02 100644 --- a/sentry-spring-7/build.gradle.kts +++ b/sentry-spring-7/build.gradle.kts @@ -67,8 +67,8 @@ dependencies { testImplementation(libs.context.propagation) testImplementation(libs.graphql.java24) testImplementation(libs.kotlin.test.junit) - testImplementation(libs.mockito.kotlin.spring7) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.kotlin) + testImplementation(libs.mockito.core) testImplementation(libs.springboot4.starter.aspectj) testImplementation(libs.springboot4.starter.graphql) testImplementation(libs.spring.kafka4) diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/HttpServletRequestSentryUserProvider.java b/sentry-spring-7/src/main/java/io/sentry/spring7/HttpServletRequestSentryUserProvider.java index 44ae584ec17..27720570a2e 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/HttpServletRequestSentryUserProvider.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/HttpServletRequestSentryUserProvider.java @@ -25,9 +25,8 @@ public HttpServletRequestSentryUserProvider(final @NotNull SentryOptions options public @Nullable User provideUser() { if (options.getDataCollectionResolver().isUserInfo()) { final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - if (requestAttributes instanceof ServletRequestAttributes) { - final ServletRequestAttributes servletRequestAttributes = - (ServletRequestAttributes) requestAttributes; + if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) { + final HttpServletRequest request = servletRequestAttributes.getRequest(); final User user = new User(); diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/SentryInitBeanPostProcessor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/SentryInitBeanPostProcessor.java index 4b778d68649..b36df8be77b 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/SentryInitBeanPostProcessor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/SentryInitBeanPostProcessor.java @@ -42,8 +42,7 @@ public SentryInitBeanPostProcessor() { @SuppressWarnings({"unchecked", "deprecation"}) public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, @NotNull final String beanName) throws BeansException { - if (bean instanceof SentryOptions) { - final SentryOptions options = (SentryOptions) bean; + if (bean instanceof SentryOptions options) { if (applicationContext != null) { applicationContext diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheBeanPostProcessor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheBeanPostProcessor.java index b6569a9953b..8c755be2dea 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheBeanPostProcessor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheBeanPostProcessor.java @@ -16,8 +16,8 @@ public final class SentryCacheBeanPostProcessor implements BeanPostProcessor, Pr @Override public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (bean instanceof CacheManager && !(bean instanceof SentryCacheManagerWrapper)) { - return new SentryCacheManagerWrapper((CacheManager) bean, ScopesAdapter.getInstance()); + if (bean instanceof CacheManager cacheManager && !(bean instanceof SentryCacheManagerWrapper)) { + return new SentryCacheManagerWrapper(cacheManager, ScopesAdapter.getInstance()); } return bean; } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/checkin/SentryCheckInAdvice.java b/sentry-spring-7/src/main/java/io/sentry/spring7/checkin/SentryCheckInAdvice.java index e58e0e9dd97..a14f0d4c96a 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/checkin/SentryCheckInAdvice.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/checkin/SentryCheckInAdvice.java @@ -44,9 +44,11 @@ public SentryCheckInAdvice(final @NotNull IScopes scopes) { } @Override - public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + final @Nullable Object target = invocation.getThis(); final Method mostSpecificMethod = - AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()); + AopUtils.getMostSpecificMethod( + invocation.getMethod(), target != null ? target.getClass() : null); @Nullable SentryCheckIn checkInAnnotation = diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring-7/src/main/java/io/sentry/spring7/exception/SentryCaptureExceptionParameterAdvice.java index cb5cba62a4d..f4c630033a8 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/exception/SentryCaptureExceptionParameterAdvice.java @@ -11,6 +11,7 @@ import org.aopalliance.intercept.MethodInvocation; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.springframework.aop.support.AopUtils; import org.springframework.core.annotation.AnnotationUtils; @@ -33,17 +34,19 @@ public SentryCaptureExceptionParameterAdvice(final @NotNull IScopes scopes) { } @Override - public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + final @Nullable Object target = invocation.getThis(); final Method mostSpecificMethod = - AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()); + AopUtils.getMostSpecificMethod( + invocation.getMethod(), target != null ? target.getClass() : null); SentryCaptureExceptionParameter sentryCaptureExceptionParameter = AnnotationUtils.findAnnotation(mostSpecificMethod, SentryCaptureExceptionParameter.class); if (sentryCaptureExceptionParameter != null) { Object[] args = invocation.getArguments(); for (Object arg : args) { - if (arg instanceof Exception) { - captureException((Exception) arg); + if (arg instanceof Exception exception) { + captureException(exception); break; } } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryBatchLoaderRegistry.java b/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryBatchLoaderRegistry.java index 591863d906b..647da383818 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryBatchLoaderRegistry.java @@ -108,8 +108,8 @@ public void registerMappedBatchLoader( private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); - if (context instanceof GraphQLContext) { - GraphQLContext graphqlContext = (GraphQLContext) context; + if (context instanceof GraphQLContext graphqlContext) { + return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryGraphqlBeanPostProcessor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryGraphqlBeanPostProcessor.java index 2ea390fd2dd..1a973c62031 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryGraphqlBeanPostProcessor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/graphql/SentryGraphqlBeanPostProcessor.java @@ -12,8 +12,8 @@ public final class SentryGraphqlBeanPostProcessor implements BeanPostProcessor, @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof BatchLoaderRegistry) { - return new SentryBatchLoaderRegistry((BatchLoaderRegistry) bean); + if (bean instanceof BatchLoaderRegistry batchLoaderRegistry) { + return new SentryBatchLoaderRegistry(batchLoaderRegistry); } return bean; } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaProducerBeanPostProcessor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaProducerBeanPostProcessor.java index eff0b4154bb..c8b41d4d0f7 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaProducerBeanPostProcessor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaProducerBeanPostProcessor.java @@ -36,8 +36,8 @@ public final class SentryKafkaProducerBeanPostProcessor @SuppressWarnings({"unchecked", "rawtypes"}) public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (bean instanceof ProducerFactory) { - final @NotNull ProducerFactory factory = (ProducerFactory) bean; + if (bean instanceof ProducerFactory factory) { + final @NotNull SentryProducerPostProcessor pp = new SentryProducerPostProcessor<>(); factory.addPostProcessor(pp); if (!factory.getPostProcessors().contains(pp)) { diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaRecordInterceptor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaRecordInterceptor.java index b2b4d20b948..d08351ac5f5 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaRecordInterceptor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/kafka/SentryKafkaRecordInterceptor.java @@ -196,7 +196,7 @@ private boolean isIgnored() { if (enqueuedTimeStr != null) { try { final double enqueuedTimeSeconds = Double.parseDouble(enqueuedTimeStr); - final double nowSeconds = DateUtils.millisToSeconds(System.currentTimeMillis()); + final double nowSeconds = DateUtils.millisToSeconds((double) System.currentTimeMillis()); final long latencyMs = (long) ((nowSeconds - enqueuedTimeSeconds) * 1000); if (latencyMs >= 0) { transaction.setData(SpanDataConvention.MESSAGING_MESSAGE_RECEIVE_LATENCY, latencyMs); diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanAdvice.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanAdvice.java index 6c9a3287edc..36eb08d524c 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanAdvice.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanAdvice.java @@ -35,15 +35,17 @@ public SentrySpanAdvice(final @NotNull IScopes scopes) { @SuppressWarnings("deprecation") @Override - public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span return invocation.proceed(); } else { + final @Nullable Object target = invocation.getThis(); final Method mostSpecificMethod = - AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()); + AopUtils.getMostSpecificMethod( + invocation.getMethod(), target != null ? target.getClass() : null); final Class targetClass = invocation.getMethod().getDeclaringClass(); SentrySpan sentrySpan = AnnotationUtils.findAnnotation(mostSpecificMethod, SentrySpan.class); if (sentrySpan == null) { diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java index 46a31245ba1..d0d0923a42b 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -39,7 +39,7 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { } public SentrySpanClientHttpRequestInterceptor( - final @NotNull IScopes scopes, final @NotNull boolean isRestTemplate) { + final @NotNull IScopes scopes, final boolean isRestTemplate) { this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.traceOrigin = isRestTemplate ? TRACE_ORIGIN_REST_TEMPLATE : TRACE_ORIGIN_REST_CLIENT; } diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentryTransactionAdvice.java b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentryTransactionAdvice.java index b16a6ab00ef..913ee09b2d3 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/tracing/SentryTransactionAdvice.java @@ -41,9 +41,11 @@ public SentryTransactionAdvice(final @NotNull IScopes scopes) { @SuppressWarnings("deprecation") @Override - public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { + final @Nullable Object target = invocation.getThis(); final Method mostSpecificMethod = - AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()); + AopUtils.getMostSpecificMethod( + invocation.getMethod(), target != null ? target.getClass() : null); @Nullable SentryTransaction sentryTransaction = @@ -116,7 +118,7 @@ private static class TransactionNameAndSource { private final @NotNull String name; private final @NotNull TransactionNameSource source; - public TransactionNameAndSource( + TransactionNameAndSource( final @NotNull String name, final @NotNull TransactionNameSource source) { this.name = name; this.source = source; diff --git a/sentry-spring-7/src/main/java/io/sentry/spring7/webflux/SentryWebExceptionHandler.java b/sentry-spring-7/src/main/java/io/sentry/spring7/webflux/SentryWebExceptionHandler.java index 19e6fa0fc4e..421cb7a2a90 100644 --- a/sentry-spring-7/src/main/java/io/sentry/spring7/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring-7/src/main/java/io/sentry/spring7/webflux/SentryWebExceptionHandler.java @@ -38,7 +38,7 @@ public SentryWebExceptionHandler(final @NotNull IScopes scopes) { public @NotNull Mono handle( final @NotNull ServerWebExchange serverWebExchange, final @NotNull Throwable ex) { final @Nullable IScopes requestScopes = - serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_SCOPES_KEY, null); + serverWebExchange.getAttribute(SentryWebFilter.SENTRY_SCOPES_KEY); final @NotNull IScopes scopesToUse = requestScopes != null ? requestScopes : scopes; return SentryReactorUtils.withSentryScopes( diff --git a/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryLog4j2Initializer.java b/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryLog4j2Initializer.java index efdf9dc255a..51d86eb6bb8 100644 --- a/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryLog4j2Initializer.java +++ b/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryLog4j2Initializer.java @@ -45,13 +45,12 @@ public boolean supportsEventType(final @NotNull ResolvableType eventType) { @Override public void onApplicationEvent(final @NotNull ApplicationEvent event) { final Object context = LogManager.getContext(false); - if (!(context instanceof LoggerContext)) { + if (!(context instanceof LoggerContext loggerContext)) { logger.info( "Sentry Log4j2 appender was not configured because Log4j2 Core is not the active logging backend. Log4j2 API calls may be routed through SLF4J."); return; } - final LoggerContext loggerContext = (LoggerContext) context; final Configuration configuration = loggerContext.getConfiguration(); boolean changed = false; diff --git a/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryOkHttpClientBeanPostProcessor.java b/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryOkHttpClientBeanPostProcessor.java index 5dd84ab413c..88099b6eea3 100644 --- a/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryOkHttpClientBeanPostProcessor.java +++ b/sentry-spring-boot-4/src/main/java/io/sentry/spring/boot4/SentryOkHttpClientBeanPostProcessor.java @@ -19,11 +19,10 @@ final class SentryOkHttpClientBeanPostProcessor implements BeanPostProcessor, Pr @Override public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (!(bean instanceof OkHttpClient)) { + if (!(bean instanceof OkHttpClient client)) { return bean; } - final @NotNull OkHttpClient client = (OkHttpClient) bean; if (client.getClass() != OkHttpClient.class) { ScopesAdapter.getInstance() .getOptions() diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryLog4j2Initializer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryLog4j2Initializer.java index 141f6a8ba00..c9d8684304c 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryLog4j2Initializer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryLog4j2Initializer.java @@ -45,13 +45,12 @@ public boolean supportsEventType(final @NotNull ResolvableType eventType) { @Override public void onApplicationEvent(final @NotNull ApplicationEvent event) { final Object context = LogManager.getContext(false); - if (!(context instanceof LoggerContext)) { + if (!(context instanceof LoggerContext loggerContext)) { logger.info( "Sentry Log4j2 appender was not configured because Log4j2 Core is not the active logging backend. Log4j2 API calls may be routed through SLF4J."); return; } - final LoggerContext loggerContext = (LoggerContext) context; final Configuration configuration = loggerContext.getConfiguration(); boolean changed = false; diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryOkHttpClientBeanPostProcessor.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryOkHttpClientBeanPostProcessor.java index 8409240d7fe..4c0bbf6c920 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryOkHttpClientBeanPostProcessor.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryOkHttpClientBeanPostProcessor.java @@ -19,11 +19,10 @@ final class SentryOkHttpClientBeanPostProcessor implements BeanPostProcessor, Pr @Override public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (!(bean instanceof OkHttpClient)) { + if (!(bean instanceof OkHttpClient client)) { return bean; } - final @NotNull OkHttpClient client = (OkHttpClient) bean; if (client.getClass() != OkHttpClient.class) { ScopesAdapter.getInstance() .getOptions() diff --git a/sentry-spring-jakarta/build.gradle.kts b/sentry-spring-jakarta/build.gradle.kts index f103bfcbe0a..d518fce95bb 100644 --- a/sentry-spring-jakarta/build.gradle.kts +++ b/sentry-spring-jakarta/build.gradle.kts @@ -66,7 +66,7 @@ dependencies { testImplementation(libs.graphql.java24) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.springboot3.starter.aop) testImplementation(libs.springboot3.starter.graphql) testImplementation(libs.springboot3.starter.security) diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider.java index b7f4646b4a8..a64eb826f04 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider.java @@ -25,9 +25,8 @@ public HttpServletRequestSentryUserProvider(final @NotNull SentryOptions options public @Nullable User provideUser() { if (options.getDataCollectionResolver().isUserInfo()) { final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - if (requestAttributes instanceof ServletRequestAttributes) { - final ServletRequestAttributes servletRequestAttributes = - (ServletRequestAttributes) requestAttributes; + if (requestAttributes instanceof ServletRequestAttributes servletRequestAttributes) { + final HttpServletRequest request = servletRequestAttributes.getRequest(); final User user = new User(); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java index 4d9e63a151b..28157e0336b 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java @@ -42,8 +42,7 @@ public SentryInitBeanPostProcessor() { @SuppressWarnings({"unchecked", "deprecation"}) public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, @NotNull final String beanName) throws BeansException { - if (bean instanceof SentryOptions) { - final SentryOptions options = (SentryOptions) bean; + if (bean instanceof SentryOptions options) { if (applicationContext != null) { applicationContext diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheBeanPostProcessor.java index ec9964f7abc..ad4425131ff 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/cache/SentryCacheBeanPostProcessor.java @@ -16,8 +16,8 @@ public final class SentryCacheBeanPostProcessor implements BeanPostProcessor, Pr @Override public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (bean instanceof CacheManager && !(bean instanceof SentryCacheManagerWrapper)) { - return new SentryCacheManagerWrapper((CacheManager) bean, ScopesAdapter.getInstance()); + if (bean instanceof CacheManager cacheManager && !(bean instanceof SentryCacheManagerWrapper)) { + return new SentryCacheManagerWrapper(cacheManager, ScopesAdapter.getInstance()); } return bean; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java index c6537f853c2..5fe03f385df 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java @@ -42,8 +42,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl if (sentryCaptureExceptionParameter != null) { Object[] args = invocation.getArguments(); for (Object arg : args) { - if (arg instanceof Exception) { - captureException((Exception) arg); + if (arg instanceof Exception exception) { + captureException(exception); break; } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java index 4e7c3665aae..6a9f33e65f1 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java @@ -108,8 +108,8 @@ public void registerMappedBatchLoader( private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); - if (context instanceof GraphQLContext) { - GraphQLContext graphqlContext = (GraphQLContext) context; + if (context instanceof GraphQLContext graphqlContext) { + return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor.java index 5eb4374bb1f..79bc5e11ccd 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor.java @@ -12,8 +12,8 @@ public final class SentryGraphqlBeanPostProcessor implements BeanPostProcessor, @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof BatchLoaderRegistry) { - return new SentryBatchLoaderRegistry((BatchLoaderRegistry) bean); + if (bean instanceof BatchLoaderRegistry batchLoaderRegistry) { + return new SentryBatchLoaderRegistry(batchLoaderRegistry); } return bean; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaProducerBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaProducerBeanPostProcessor.java index 8a06e4e338e..dc3636a2b24 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaProducerBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaProducerBeanPostProcessor.java @@ -36,8 +36,8 @@ public final class SentryKafkaProducerBeanPostProcessor @SuppressWarnings({"unchecked", "rawtypes"}) public @NotNull Object postProcessAfterInitialization( final @NotNull Object bean, final @NotNull String beanName) throws BeansException { - if (bean instanceof ProducerFactory) { - final @NotNull ProducerFactory factory = (ProducerFactory) bean; + if (bean instanceof ProducerFactory factory) { + final @NotNull SentryProducerPostProcessor pp = new SentryProducerPostProcessor<>(); factory.addPostProcessor(pp); if (!factory.getPostProcessors().contains(pp)) { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaRecordInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaRecordInterceptor.java index 72535712695..4d5abda711f 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaRecordInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/kafka/SentryKafkaRecordInterceptor.java @@ -196,7 +196,7 @@ private boolean isIgnored() { if (enqueuedTimeStr != null) { try { final double enqueuedTimeSeconds = Double.parseDouble(enqueuedTimeStr); - final double nowSeconds = DateUtils.millisToSeconds(System.currentTimeMillis()); + final double nowSeconds = DateUtils.millisToSeconds((double) System.currentTimeMillis()); final long latencyMs = (long) ((nowSeconds - enqueuedTimeSeconds) * 1000); if (latencyMs >= 0) { transaction.setData(SpanDataConvention.MESSAGING_MESSAGE_RECEIVE_LATENCY, latencyMs); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 0628bc1d30e..8a5d1db3e05 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -39,7 +39,7 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { } public SentrySpanClientHttpRequestInterceptor( - final @NotNull IScopes scopes, final @NotNull boolean isRestTemplate) { + final @NotNull IScopes scopes, final boolean isRestTemplate) { this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.traceOrigin = isRestTemplate ? TRACE_ORIGIN_REST_TEMPLATE : TRACE_ORIGIN_REST_CLIENT; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index 95618f76fd7..833e59670e2 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -116,7 +116,7 @@ private static class TransactionNameAndSource { private final @NotNull String name; private final @NotNull TransactionNameSource source; - public TransactionNameAndSource( + TransactionNameAndSource( final @NotNull String name, final @NotNull TransactionNameSource source) { this.name = name; this.source = source; diff --git a/sentry-spring/build.gradle.kts b/sentry-spring/build.gradle.kts index 03176d7b867..c6d66456d8c 100644 --- a/sentry-spring/build.gradle.kts +++ b/sentry-spring/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { testImplementation(libs.graphql.java17) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.springboot.starter.aop) testImplementation(libs.springboot.starter.graphql) testImplementation(libs.springboot.starter.security) diff --git a/sentry-spring/src/main/java/io/sentry/spring/kafka/SentryKafkaRecordInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/kafka/SentryKafkaRecordInterceptor.java index d1ad3086098..ed26e2ee795 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/kafka/SentryKafkaRecordInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/kafka/SentryKafkaRecordInterceptor.java @@ -202,7 +202,7 @@ private boolean isIgnored() { if (enqueuedTimeStr != null) { try { final double enqueuedTimeSeconds = Double.parseDouble(enqueuedTimeStr); - final double nowSeconds = DateUtils.millisToSeconds(System.currentTimeMillis()); + final double nowSeconds = DateUtils.millisToSeconds((double) System.currentTimeMillis()); final long latencyMs = (long) ((nowSeconds - enqueuedTimeSeconds) * 1000); if (latencyMs >= 0) { transaction.setData(SpanDataConvention.MESSAGING_MESSAGE_RECEIVE_LATENCY, latencyMs); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index 180d5df00a7..eab13d1a7d6 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -115,7 +115,7 @@ private static class TransactionNameAndSource { private final @NotNull String name; private final @NotNull TransactionNameSource source; - public TransactionNameAndSource( + TransactionNameAndSource( final @NotNull String name, final @NotNull TransactionNameSource source) { this.name = name; this.source = source; diff --git a/sentry-system-test-support/build.gradle.kts b/sentry-system-test-support/build.gradle.kts index 7f08bf6d01b..32bfa45ee93 100644 --- a/sentry-system-test-support/build.gradle.kts +++ b/sentry-system-test-support/build.gradle.kts @@ -7,13 +7,14 @@ plugins { id("com.apollographql.apollo") version "4.1.1" } +// Not published. Mockito 5 and mockito-kotlin 5+ are built for Java 11. configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 } diff --git a/sentry-test-support/build.gradle.kts b/sentry-test-support/build.gradle.kts index a0b508c9715..4c3776df8ea 100644 --- a/sentry-test-support/build.gradle.kts +++ b/sentry-test-support/build.gradle.kts @@ -6,13 +6,14 @@ plugins { alias(libs.plugins.gradle.versions) } +// Not published. Mockito 5 and mockito-kotlin 5+ are built for Java 11. configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 } diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index 6b98ff790c6..731a6608a05 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { testImplementation(libs.javafaker) testImplementation(libs.kotlin.test.junit) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.msgpack) testImplementation(libs.okio) testImplementation(projects.sentryTestSupport) @@ -51,6 +51,9 @@ tasks { "--add-opens", "java.base/java.lang=ALL-UNNAMED", ) + // SentryEnvelopeItemTest."fromAttachment with file SecurityManager denies read access" installs + // a SecurityManager to simulate unreadable attachments; JDK 18+ refuses that without opt-in. + systemProperty("java.security.manager", "allow") environment["SENTRY_TEST_PROPERTY"] = "\"some-value\"" environment["SENTRY_TEST_MAP_KEY1"] = "\"value1\"" environment["SENTRY_TEST_MAP_KEY2"] = "value2" diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index 9f0753f8671..3c3b0aea28c 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -257,6 +257,9 @@ public String getThirdPartyHeader() { return thirdPartyHeader; } + // ConcurrentHashMap.keySet() compiled against JDK 8+ returns KeySetView, which crashes on Android + // < 24. + @SuppressWarnings("JdkObsolete") public @NotNull String toHeaderString(@Nullable String thirdPartyBaggageHeaderString) { final StringBuilder sb = new StringBuilder(); String separator = ""; @@ -325,10 +328,14 @@ public String getThirdPartyHeader() { return sb.toString(); } + // The Charset overload requires Java 10. + @SuppressWarnings("JdkObsolete") private String encode(final @NotNull String value) throws UnsupportedEncodingException { return URLEncoder.encode(value, CHARSET).replaceAll("\\+", "%20"); } + // The Charset overload requires Java 10. + @SuppressWarnings("JdkObsolete") private static String decode(final @NotNull String value) throws UnsupportedEncodingException { return URLDecoder.decode(value, CHARSET); } diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index 4db0dcf46c5..a00fb496602 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -630,6 +630,8 @@ public void setType(@Nullable String type) { this.type = type; } + // EMPTY_DATA is a sentinel, so identity comparison is intended. + @SuppressWarnings("ReferenceEquality") private @NotNull Map getOrCreateData() { Map currentData = data; if (currentData == EMPTY_DATA) { @@ -691,6 +693,8 @@ public void setData(@Nullable String key, @Nullable Object value) { * * @param key the key */ + // EMPTY_DATA is a sentinel, so identity comparison is intended. + @SuppressWarnings("ReferenceEquality") public void removeData(@Nullable String key) { if (key == null) { return; diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java index d9a8b14c628..0fff6b681bd 100644 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -243,7 +243,7 @@ public void setSpring(@NotNull Spring spring) { @ApiStatus.Internal @Override - /** Not intended to be set on a scopes Context directly */ + /* Not intended to be set on a scopes Context directly */ public void setFeatureFlags(@NotNull FeatureFlags spring) { getDefaultContexts().setFeatureFlags(spring); } diff --git a/sentry/src/main/java/io/sentry/DateUtils.java b/sentry/src/main/java/io/sentry/DateUtils.java index fcba83fbe05..4be99b6322f 100644 --- a/sentry/src/main/java/io/sentry/DateUtils.java +++ b/sentry/src/main/java/io/sentry/DateUtils.java @@ -151,10 +151,10 @@ public static double nanosToSeconds(final long nanos) { */ @SuppressWarnings("JavaUtilDate") public static double dateToSeconds(final @NotNull Date date) { - return millisToSeconds(date.getTime()); + return millisToSeconds((double) date.getTime()); } - public static long secondsToNanos(final @NotNull long seconds) { + public static long secondsToNanos(final long seconds) { return seconds * (1000L * 1000L * 1000L); } diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java index f664360f49b..71dd40843a4 100644 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -21,7 +21,7 @@ public final class DefaultSpanFactory implements ISpanFactory { final @NotNull SpanOptions spanOptions, final @NotNull SpanContext spanContext, @Nullable ISpan parentSpan) { - /** + /* * Be careful here when executing something like parentSpan.startChild() as that might cause a * loop and a stack overflow. This can happen, e.g. when OpenTelemetry is creating spans that * use OtelSpanWrapper which calls this createSpan method that then in turn calls startChild diff --git a/sentry/src/main/java/io/sentry/DiagnosticLogger.java b/sentry/src/main/java/io/sentry/DiagnosticLogger.java index c96e061f2c1..81b8e30d2ba 100644 --- a/sentry/src/main/java/io/sentry/DiagnosticLogger.java +++ b/sentry/src/main/java/io/sentry/DiagnosticLogger.java @@ -29,6 +29,8 @@ public DiagnosticLogger(final @NotNull SentryOptions options, final @Nullable IL * @param level The SentryLevel to test against. * @return True if a log message would be recorded for the level. Otherwise false. */ + // SentryLevel is declared in ascending severity order. + @SuppressWarnings("EnumOrdinal") @Override public boolean isEnabled(final @Nullable SentryLevel level) { final SentryLevel diagLevel = options.getDiagnosticLevel(); diff --git a/sentry/src/main/java/io/sentry/DirectoryProcessor.java b/sentry/src/main/java/io/sentry/DirectoryProcessor.java index 17f65bb8c76..d9f0f32c40c 100644 --- a/sentry/src/main/java/io/sentry/DirectoryProcessor.java +++ b/sentry/src/main/java/io/sentry/DirectoryProcessor.java @@ -120,7 +120,7 @@ private static final class SendCachedEnvelopeHint private final @NotNull String filePath; private final @NotNull Queue processedEnvelopes; - public SendCachedEnvelopeHint( + SendCachedEnvelopeHint( final long flushTimeoutMillis, final @NotNull ILogger logger, final @NotNull String filePath, diff --git a/sentry/src/main/java/io/sentry/ExternalOptions.java b/sentry/src/main/java/io/sentry/ExternalOptions.java index 272cf1c13a9..810130416aa 100644 --- a/sentry/src/main/java/io/sentry/ExternalOptions.java +++ b/sentry/src/main/java/io/sentry/ExternalOptions.java @@ -242,7 +242,8 @@ public final class ExternalOptions { String profileLifecycleString = propertiesProvider.getProperty("profile-lifecycle"); if (profileLifecycleString != null && !profileLifecycleString.isEmpty()) { - options.setProfileLifecycle(ProfileLifecycle.valueOf(profileLifecycleString.toUpperCase())); + options.setProfileLifecycle( + ProfileLifecycle.valueOf(profileLifecycleString.toUpperCase(Locale.ROOT))); } return options; diff --git a/sentry/src/main/java/io/sentry/Hint.java b/sentry/src/main/java/io/sentry/Hint.java index 1e09dca5541..9281d41fa91 100644 --- a/sentry/src/main/java/io/sentry/Hint.java +++ b/sentry/src/main/java/io/sentry/Hint.java @@ -60,7 +60,7 @@ public void set(@NotNull String name, @Nullable Object hint) { } @SuppressWarnings("unchecked") - public @Nullable T getAs(@NotNull String name, @NotNull Class clazz) { + public @Nullable T getAs(@NotNull String name, @NotNull Class clazz) { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { Object hintValue = internalStorage.get(name); diff --git a/sentry/src/main/java/io/sentry/MonitorContexts.java b/sentry/src/main/java/io/sentry/MonitorContexts.java index a52ecc6b97f..55178893767 100644 --- a/sentry/src/main/java/io/sentry/MonitorContexts.java +++ b/sentry/src/main/java/io/sentry/MonitorContexts.java @@ -43,6 +43,9 @@ public void setTrace(final @NotNull SpanContext traceContext) { // region json + // ConcurrentHashMap.keySet() compiled against JDK 8+ returns KeySetView, which crashes on Android + // < 24. + @SuppressWarnings("JdkObsolete") @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { diff --git a/sentry/src/main/java/io/sentry/ReplayRecording.java b/sentry/src/main/java/io/sentry/ReplayRecording.java index a83eddd380f..8c038d64c33 100644 --- a/sentry/src/main/java/io/sentry/ReplayRecording.java +++ b/sentry/src/main/java/io/sentry/ReplayRecording.java @@ -102,7 +102,8 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { - @SuppressWarnings("unchecked") + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings({"unchecked", "EnumOrdinal"}) @Override public @NotNull ReplayRecording deserialize( @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { diff --git a/sentry/src/main/java/io/sentry/SamplingContext.java b/sentry/src/main/java/io/sentry/SamplingContext.java index 17ce111d0c2..7a9c69b0739 100644 --- a/sentry/src/main/java/io/sentry/SamplingContext.java +++ b/sentry/src/main/java/io/sentry/SamplingContext.java @@ -20,7 +20,7 @@ public final class SamplingContext { @Deprecated @SuppressWarnings("InlineMeSuggester") - /** + /* * @deprecated creating a SamplingContext is something only the SDK should do */ public SamplingContext( diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 54e8b893555..d02e1b75728 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -463,10 +463,10 @@ public Queue getBreadcrumbs() { */ private @Nullable Breadcrumb executeBeforeBreadcrumb( final @NotNull SentryOptions.BeforeBreadcrumbCallback callback, - @NotNull Breadcrumb breadcrumb, + final @NotNull Breadcrumb breadcrumb, final @NotNull Hint hint) { try (final @NotNull ISentryLifecycleToken ignored = SentryCallbackReentrancyGuard.enter()) { - breadcrumb = callback.execute(breadcrumb, hint); + return callback.execute(breadcrumb, hint); } catch (Throwable e) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 012587eaa59..f8adb54e2ca 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -1353,7 +1353,7 @@ public void captureLog(@Nullable SentryLogEvent logEvent, @Nullable IScope scope options .getClientReportRecorder() .recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.LogItem); - final @NotNull long logEventNumberOfBytes = + final long logEventNumberOfBytes = JsonSerializationUtils.byteSizeOf( options.getSerializer(), options.getLogger(), tmpLogEvent); options diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index 2a7f06968dc..d1858c02767 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -691,11 +691,12 @@ private static class CachedItem { private @Nullable byte[] bytes; private final @Nullable Callable dataFactory; - public CachedItem(final @Nullable Callable dataFactory) { + CachedItem(final @Nullable Callable dataFactory) { this.dataFactory = dataFactory; } - public @NotNull byte[] getBytes() throws Exception { + @NotNull + byte[] getBytes() throws Exception { if (bytes == null && dataFactory != null) { bytes = dataFactory.call(); } diff --git a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java index cd4ab1cc0e9..3c07ec38b91 100644 --- a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java +++ b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java @@ -11,6 +11,7 @@ import java.util.Deque; import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -141,7 +142,7 @@ Deque extractExceptionQueue(final @NotNull Throwable throwable) Deque extractExceptionQueueInternal( final @NotNull Throwable throwable, final @NotNull AtomicInteger exceptionId, - final @NotNull HashSet circularityDetector, + final @NotNull Set circularityDetector, final @NotNull Deque exceptions, @Nullable String mechanismTypeOverride) { Mechanism exceptionMechanism; diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 2b2f9d370a0..c43b9f7a105 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -58,6 +58,8 @@ import org.jetbrains.annotations.TestOnly; /** Sentry SDK options */ +// Constructors call overridable methods; making them final would break the public API. +@SuppressWarnings("this-escape") @Open public class SentryOptions implements RateLimiterConfig { diff --git a/sentry/src/main/java/io/sentry/SentryThreadFactory.java b/sentry/src/main/java/io/sentry/SentryThreadFactory.java index 8bd14483269..cb0608f825e 100644 --- a/sentry/src/main/java/io/sentry/SentryThreadFactory.java +++ b/sentry/src/main/java/io/sentry/SentryThreadFactory.java @@ -78,6 +78,8 @@ List getCurrentThreads( * crashed. This is the case for e.g. watchdog threads which are not the one erroring. * @return a list of SentryThread or null if none */ + // Identifies the current thread by identity. + @SuppressWarnings("ReferenceEquality") @TestOnly @Nullable List getCurrentThreads( diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index d60187cb4d5..781cf51417e 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -154,7 +154,7 @@ private void onDeadlineTimeoutReached() { } @Override - public @NotNull void forceFinish( + public void forceFinish( final @NotNull SpanStatus status, final boolean dropIfNoChildren, final @Nullable Hint hint) { if (isFinished()) { return; diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index 19ab2c3ad87..4beb6673c54 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -16,6 +16,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +// Constructors call overridable methods; making them final would break the public API. +@SuppressWarnings("this-escape") @Open public class SpanContext implements JsonUnknown, JsonSerializable { public static final String TYPE = "trace"; diff --git a/sentry/src/main/java/io/sentry/SynchronizedCollection.java b/sentry/src/main/java/io/sentry/SynchronizedCollection.java index 8a58401020b..e88d25a235e 100644 --- a/sentry/src/main/java/io/sentry/SynchronizedCollection.java +++ b/sentry/src/main/java/io/sentry/SynchronizedCollection.java @@ -50,9 +50,13 @@ class SynchronizedCollection implements Collection, Serializable { private static final long serialVersionUID = 2412805092710877986L; /** The collection to decorate */ + // Serializable only if the decorated collection is, as in Apache Commons Collections. + @SuppressWarnings("serial") private final Collection collection; /** The object to lock on, needed for List/SortedSet views */ + // Not Serializable either; the SDK never serializes these collections. + @SuppressWarnings("serial") final AutoClosableReentrantLock lock; /** diff --git a/sentry/src/main/java/io/sentry/TraceContext.java b/sentry/src/main/java/io/sentry/TraceContext.java index 1bb5508f85b..957bda79f49 100644 --- a/sentry/src/main/java/io/sentry/TraceContext.java +++ b/sentry/src/main/java/io/sentry/TraceContext.java @@ -30,7 +30,7 @@ public final class TraceContext implements JsonUnknown, JsonSerializable { } @SuppressWarnings("InlineMeSuggester") - /** + /* * @deprecated please use the constructor than also takes sampleRand */ @Deprecated diff --git a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java index 6ea61895799..a6015051d1c 100644 --- a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java +++ b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java @@ -47,6 +47,8 @@ public UncaughtExceptionHandlerIntegration() { this.threadAdapter = threadAdapter; } + // Both integrations must share the same global scope instance. + @SuppressWarnings("ReferenceEquality") @Override public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { if (registered) { diff --git a/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java b/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java index 8a097486db1..8dca5dfb7e3 100644 --- a/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java +++ b/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java @@ -265,6 +265,8 @@ private void flush() { } } + // DELETE_MARKER and CLEAR_MARKER are sentinels, so identity comparison is intended. + @SuppressWarnings("ReferenceEquality") private void writePending() { // ConcurrentHashMap's iterator is weakly consistent, so removing while iterating is safe. Keys // added after iteration starts may be missed, but flush() re-checks and queues another write. diff --git a/sentry/src/main/java/io/sentry/exception/ExceptionMechanismException.java b/sentry/src/main/java/io/sentry/exception/ExceptionMechanismException.java index 5df6d473f78..009e28c22ad 100644 --- a/sentry/src/main/java/io/sentry/exception/ExceptionMechanismException.java +++ b/sentry/src/main/java/io/sentry/exception/ExceptionMechanismException.java @@ -14,9 +14,16 @@ public final class ExceptionMechanismException extends RuntimeException { private static final long serialVersionUID = 142345454265713915L; + // Never serialized. + @SuppressWarnings("serial") private final @NotNull Mechanism exceptionMechanism; + private final @NotNull Throwable throwable; + + // Never serialized. + @SuppressWarnings("serial") private final @Nullable Thread thread; + private final boolean snapshot; /** diff --git a/sentry/src/main/java/io/sentry/featureflags/FeatureFlagBuffer.java b/sentry/src/main/java/io/sentry/featureflags/FeatureFlagBuffer.java index 5a02a911ace..84448878196 100644 --- a/sentry/src/main/java/io/sentry/featureflags/FeatureFlagBuffer.java +++ b/sentry/src/main/java/io/sentry/featureflags/FeatureFlagBuffer.java @@ -233,13 +233,14 @@ private static class FeatureFlagEntry { private final boolean result; private final long nanos; - public FeatureFlagEntry(final @NotNull String flag, final boolean result, final long nanos) { + FeatureFlagEntry(final @NotNull String flag, final boolean result, final long nanos) { this.flag = flag; this.result = result; this.nanos = nanos; } - public @NotNull FeatureFlag toFeatureFlag() { + @NotNull + FeatureFlag toFeatureFlag() { return new FeatureFlag(flag, result); } } diff --git a/sentry/src/main/java/io/sentry/internal/modules/ManifestModulesLoader.java b/sentry/src/main/java/io/sentry/internal/modules/ManifestModulesLoader.java index 1f7ac93950e..fc1c313ec43 100644 --- a/sentry/src/main/java/io/sentry/internal/modules/ManifestModulesLoader.java +++ b/sentry/src/main/java/io/sentry/internal/modules/ManifestModulesLoader.java @@ -93,7 +93,7 @@ private static final class Module { private final @NotNull String name; private final @NotNull String version; - public Module(final @NotNull String name, final @NotNull String version) { + Module(final @NotNull String name, final @NotNull String version) { this.name = name; this.version = version; } diff --git a/sentry/src/main/java/io/sentry/logger/LoggerApi.java b/sentry/src/main/java/io/sentry/logger/LoggerApi.java index 3741ddc6209..69f5a5a44bc 100644 --- a/sentry/src/main/java/io/sentry/logger/LoggerApi.java +++ b/sentry/src/main/java/io/sentry/logger/LoggerApi.java @@ -152,7 +152,7 @@ private void captureLog( } } - private @NotNull HashMap createAttributes( + private @NotNull Map createAttributes( final @NotNull SentryLogParameters params, final @NotNull String message, final @Nullable Object... args) { @@ -247,8 +247,7 @@ private void captureLog( return attributes; } - private void setServerName( - final @NotNull HashMap attributes) { + private void setServerName(final @NotNull Map attributes) { final @NotNull SentryOptions options = scopes.getOptions(); final @Nullable String optionsServerName = options.getServerName(); if (optionsServerName != null) { @@ -265,7 +264,7 @@ private void setServerName( } } - private void setUser(final @NotNull HashMap attributes) { + private void setUser(final @NotNull Map attributes) { final @Nullable User user = scopes.getCombinedScopeView().getUser(); if (user == null) { // In case no user is set, we should fallback to the distinct id, known as installation id, diff --git a/sentry/src/main/java/io/sentry/metrics/MetricsApi.java b/sentry/src/main/java/io/sentry/metrics/MetricsApi.java index d70b4ab5c97..70f5006be23 100644 --- a/sentry/src/main/java/io/sentry/metrics/MetricsApi.java +++ b/sentry/src/main/java/io/sentry/metrics/MetricsApi.java @@ -156,7 +156,7 @@ private void captureMetrics( } } - private @NotNull HashMap createAttributes( + private @NotNull Map createAttributes( final @NotNull SentryMetricsParameters params) { final @NotNull HashMap attributes = new HashMap<>(); @@ -232,8 +232,7 @@ private void captureMetrics( return attributes; } - private void setServerName( - final @NotNull HashMap attributes) { + private void setServerName(final @NotNull Map attributes) { final @NotNull SentryOptions options = scopes.getOptions(); final @Nullable String optionsServerName = options.getServerName(); if (optionsServerName != null) { @@ -250,7 +249,7 @@ private void setServerName( } } - private void setUser(final @NotNull HashMap attributes) { + private void setUser(final @NotNull Map attributes) { final @Nullable User user = scopes.getCombinedScopeView().getUser(); if (user == null) { // In case no user is set, we should fallback to the distinct id, known as installation id, diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index f6ba635383a..d5076714076 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -36,6 +36,8 @@ public class Contexts implements JsonSerializable { public Contexts() {} + // Constructors call overridable methods; making them final would break the public API. + @SuppressWarnings("this-escape") public Contexts(final @NotNull Contexts contexts) { for (final Map.Entry entry : contexts.entrySet()) { if (entry != null) { @@ -252,6 +254,9 @@ public void clear() { internalStorage.clear(); } + // ConcurrentHashMap.keySet() compiled against JDK 8+ returns KeySetView, which crashes on Android + // < 24. + @SuppressWarnings("JdkObsolete") public @NotNull Enumeration keys() { return internalStorage.keys(); } diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java b/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java index fc9c8c7e690..792a46ed5bb 100644 --- a/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java @@ -17,6 +17,8 @@ public enum RRWebEventType implements JsonSerializable { Custom, Plugin; + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { @@ -24,6 +26,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger } public static final class Deserializer implements JsonDeserializer { + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public @NotNull RRWebEventType deserialize( final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebIncrementalSnapshotEvent.java b/sentry/src/main/java/io/sentry/rrweb/RRWebIncrementalSnapshotEvent.java index aff3c55ac37..df926d49be1 100644 --- a/sentry/src/main/java/io/sentry/rrweb/RRWebIncrementalSnapshotEvent.java +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebIncrementalSnapshotEvent.java @@ -30,6 +30,8 @@ public enum IncrementalSource implements JsonSerializable { AdoptedStyleSheet, CustomElement; + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { @@ -37,6 +39,8 @@ public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) } public static final class Deserializer implements JsonDeserializer { + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public @NotNull IncrementalSource deserialize( final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebInteractionEvent.java b/sentry/src/main/java/io/sentry/rrweb/RRWebInteractionEvent.java index c7bd613c1b6..6f224591659 100644 --- a/sentry/src/main/java/io/sentry/rrweb/RRWebInteractionEvent.java +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebInteractionEvent.java @@ -30,6 +30,8 @@ public enum InteractionType implements JsonSerializable { TouchEnd, TouchCancel; + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { @@ -37,6 +39,8 @@ public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) } public static final class Deserializer implements JsonDeserializer { + // The rrweb protocol encodes these enums by ordinal. + @SuppressWarnings("EnumOrdinal") @Override public @NotNull InteractionType deserialize( final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { diff --git a/sentry/src/main/java/io/sentry/time/Deadline.java b/sentry/src/main/java/io/sentry/time/Deadline.java index b6cc63e8768..e2aeef9f72c 100644 --- a/sentry/src/main/java/io/sentry/time/Deadline.java +++ b/sentry/src/main/java/io/sentry/time/Deadline.java @@ -79,6 +79,8 @@ public long remaining(final @NotNull TimeUnit unit) { * @throws IllegalArgumentException if the two were created from different tickers, whose origins * are unrelated and whose ticks are therefore not comparable. */ + // Deadlines are only comparable when they share the same ticker instance. + @SuppressWarnings("ReferenceEquality") public boolean isAfter(final @NotNull Deadline other) { if (ticker != other.ticker) { throw new IllegalArgumentException( diff --git a/sentry/src/main/java/io/sentry/util/HttpUtils.java b/sentry/src/main/java/io/sentry/util/HttpUtils.java index 59ed67eb9f8..e81c9820e15 100644 --- a/sentry/src/main/java/io/sentry/util/HttpUtils.java +++ b/sentry/src/main/java/io/sentry/util/HttpUtils.java @@ -125,6 +125,8 @@ public static boolean containsSensitiveHeader(final @NotNull String header) { return filteredHeaders; } + // The Charset overload requires Java 10. + @SuppressWarnings("JdkObsolete") private static @NotNull String decodeQueryParamName(final @NotNull String name) { try { return URLDecoder.decode(name, "UTF-8"); diff --git a/sentry/src/main/java/io/sentry/util/InitUtil.java b/sentry/src/main/java/io/sentry/util/InitUtil.java index 471723ac188..f41099b26ad 100644 --- a/sentry/src/main/java/io/sentry/util/InitUtil.java +++ b/sentry/src/main/java/io/sentry/util/InitUtil.java @@ -16,6 +16,8 @@ @ApiStatus.Internal public final class InitUtil { + // InitPriority is declared in ascending priority order. + @SuppressWarnings("EnumOrdinal") public static boolean shouldInit( final @Nullable SentryOptions previousOptions, final @NotNull SentryOptions newOptions, diff --git a/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java b/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java index fc9bc75aa3f..4e5f9be90e5 100644 --- a/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java +++ b/sentry/src/main/java/io/sentry/util/JsonSerializationUtils.java @@ -129,7 +129,7 @@ public void close() { // Nothing to close } - public long getByteCount() { + long getByteCount() { return byteCount; } diff --git a/sentry/src/main/java/io/sentry/util/Objects.java b/sentry/src/main/java/io/sentry/util/Objects.java index df7aeab38b8..20f21c570b7 100644 --- a/sentry/src/main/java/io/sentry/util/Objects.java +++ b/sentry/src/main/java/io/sentry/util/Objects.java @@ -14,6 +14,8 @@ public static T requireNonNull(final @Nullable T obj, final @NotNull String return obj; } + // Identity short-circuit, same as java.util.Objects.equals (Java 7+). + @SuppressWarnings("ReferenceEquality") public static boolean equals(@Nullable Object a, @Nullable Object b) { return (a == b) || (a != null && a.equals(b)); } diff --git a/sentry/src/main/java/io/sentry/util/Platform.java b/sentry/src/main/java/io/sentry/util/Platform.java index ad2a4e7f3c3..bad0f214db3 100644 --- a/sentry/src/main/java/io/sentry/util/Platform.java +++ b/sentry/src/main/java/io/sentry/util/Platform.java @@ -1,7 +1,6 @@ package io.sentry.util; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @ApiStatus.Internal @@ -27,7 +26,7 @@ public final class Platform { try { final @Nullable String javaStringVersion = System.getProperty("java.specification.version"); if (javaStringVersion != null) { - final @NotNull double javaVersion = Double.parseDouble(javaStringVersion); + final double javaVersion = Double.parseDouble(javaStringVersion); isJavaNinePlus = javaVersion >= 9.0; } else { isJavaNinePlus = false; diff --git a/sentry/src/main/java/io/sentry/util/UUIDGenerator.java b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java index 1da7b4feee1..0402190c851 100644 --- a/sentry/src/main/java/io/sentry/util/UUIDGenerator.java +++ b/sentry/src/main/java/io/sentry/util/UUIDGenerator.java @@ -56,7 +56,7 @@ public static UUID randomUUID() { // clear variant randomBytes[8] &= 0x3f; // set to IETF variant - randomBytes[8] |= 0x80; + randomBytes[8] = (byte) (randomBytes[8] | 0x80); long msb = 0; long lsb = 0; for (int i = 0; i < 8; i++) { diff --git a/sentry/src/main/java/io/sentry/util/network/NetworkBodyParser.java b/sentry/src/main/java/io/sentry/util/network/NetworkBodyParser.java index 42df5ca35b9..2bd50125656 100644 --- a/sentry/src/main/java/io/sentry/util/network/NetworkBodyParser.java +++ b/sentry/src/main/java/io/sentry/util/network/NetworkBodyParser.java @@ -34,6 +34,8 @@ private NetworkBodyParser() {} * @param logger Optional logger * @return A NetworkBody or null if bytes are null/empty */ + // The charset is a name from the Content-Type header; unsupported names are handled below. + @SuppressWarnings("JdkObsolete") public static @Nullable NetworkBody fromBytes( @Nullable final byte[] bytes, @Nullable final String contentType, @@ -131,6 +133,8 @@ private static NetworkBody parseJson( } /** Parses URL-encoded form data into a JsonObject NetworkBody. */ + // The Charset overload requires Java 10. + @SuppressWarnings("JdkObsolete") @NotNull private static NetworkBody parseFormUrlEncoded( @NotNull final String content, final boolean isPartial, @Nullable final ILogger logger) { @@ -206,7 +210,7 @@ private static class Result { private SaferJsonParser() {} @NotNull - public static SaferJsonParser.Result parse(final @NotNull JsonReader reader) { + static SaferJsonParser.Result parse(final @NotNull JsonReader reader) { final SaferJsonParser parser = new SaferJsonParser(); parser.result.data = parser.parse(reader, 0); return parser.result; diff --git a/sentry/src/main/java/io/sentry/vendor/gson/stream/JsonReader.java b/sentry/src/main/java/io/sentry/vendor/gson/stream/JsonReader.java index 4991a554c1e..52e44a37eb9 100644 --- a/sentry/src/main/java/io/sentry/vendor/gson/stream/JsonReader.java +++ b/sentry/src/main/java/io/sentry/vendor/gson/stream/JsonReader.java @@ -1502,6 +1502,8 @@ public String getPath() { * @throws NumberFormatException if any unicode escape sequences are * malformed. */ + // Vendored from Gson, kept identical to upstream. + @SuppressWarnings("lossy-conversions") private char readEscapeCharacter() throws IOException { if (pos == limit && !fillBuffer(1)) { throw syntaxError("Unterminated escape sequence"); diff --git a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt index eecacc95bd2..703f0823c00 100644 --- a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt +++ b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt @@ -17,6 +17,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertTrue import org.mockito.kotlin.any +import org.mockito.kotlin.anyVararg import org.mockito.kotlin.argWhere import org.mockito.kotlin.check import org.mockito.kotlin.eq @@ -309,7 +310,7 @@ class OutboxSenderTest { sut.processEnvelopeFile(path, hints) // Additionally make sure we have no errors logged - verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) + verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), anyVararg()) verify(fixture.scopes, never()).captureEvent(any()) assertFalse(File(path).exists()) } @@ -327,7 +328,7 @@ class OutboxSenderTest { sut.processEnvelopeFile(path, hints) // Additionally make sure we have no errors logged - verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) + verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), anyVararg()) verify(fixture.scopes, never()).captureEvent(any()) assertFalse(File(path).exists()) } diff --git a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt index 3a730e971bf..4fb74690c37 100644 --- a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt +++ b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt @@ -13,6 +13,7 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.mockito.kotlin.any +import org.mockito.kotlin.anyVararg import org.mockito.kotlin.argThat import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -280,7 +281,7 @@ class PreviousSessionFinalizerTest { "Timed out waiting to flush previous session to its own file in session finalizer." ) }, - any(), + anyVararg(), ) verify(fixture.scopes, never()).captureEnvelope(any()) }