From 258098e31bec7a45df56c02e329158e3ba8a0991 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:02:07 +0200 Subject: [PATCH 01/10] chore: update scripts/update-sentry-native-ndk.sh to 0.17.4 (#6278) Co-authored-by: GitHub --- CHANGELOG.md | 6 ++++++ gradle/libs.versions.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f6624bfb3..496c5333ec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,12 @@ - Measure the hostname cache TTL on the monotonic ticker the options provide, so that a device time change no longer shortens or extends it ([#6100](https://github.com/getsentry/sentry-java/pull/6100)) - Measure Session Replay's 1h recording cap and its touch-move debounce on a monotonic clock, so a device time change no longer stops a healthy recording early, keeps a finished one alive, or suppresses gesture events ([#6090](https://github.com/getsentry/sentry-java/pull/6090)) +### Dependencies + +- Bump Native SDK from v0.17.3 to v0.17.4 ([#6278](https://github.com/getsentry/sentry-java/pull/6278)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0174) + - [diff](https://github.com/getsentry/sentry-native/compare/0.17.3...0.17.4) + ## 8.61.0 ### Fixes diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b31df47f63..43c3a7660c4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -179,7 +179,7 @@ quartz = { module = "org.quartz-scheduler:quartz", version = "2.3.0" } reactor-core = { module = "io.projectreactor:reactor-core", version = "3.5.3" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" } -sentry-native-ndk = { module = "io.sentry:sentry-native-ndk", version = "0.17.3" } +sentry-native-ndk = { module = "io.sentry:sentry-native-ndk", version = "0.17.4" } servlet-api = { module = "javax.servlet:javax.servlet-api", version = "3.1.0" } servlet-jakarta-api = { module = "jakarta.servlet:jakarta.servlet-api", version = "6.1.0" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } From ee24060bf022a10163193a2f414ac903cd6768f1 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Tue, 20 Oct 2026 14:37:33 +0200 Subject: [PATCH 02/10] feat(android): Add masked screenshot capture for Hybrid SDKs (#6281) * feat(android): Add masked screenshot capture for Hybrid SDKs Hybrid SDKs capture screenshots for their own errors through ScreenshotUtils.takeScreenshot, which ignores the screenshot masking options. Add ScreenshotUtils.takeMaskedScreenshot, backed by a shared captureMaskedScreenshot that ScreenshotEventProcessor now uses as well, so native and hybrid screenshots follow the same masking rules. Refs getsentry/sentry-react-native#6900 Co-Authored-By: Claude Opus 5.5 * changelog * ref(android): Narrow catches in ScreenshotMasking Catch only InterruptedException around the main-thread wait and RuntimeException around bitmap masking. Keep the broad catch for view hierarchy traversal, which runs custom View code, but let fatal errors through with ExceptionUtils.rethrowIfFatal. Co-Authored-By: Claude Opus 5.5 * changelog: Move entry to Internal * ref(android): Move replay availability check to ScreenshotMasking Add ScreenshotMasking.isAvailable() so the replay integration class name no longer needs to be public on SentryAndroid. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- CHANGELOG.md | 1 + .../core/ScreenshotEventProcessor.java | 133 +------------- .../android/core/SentryScreenshotOptions.java | 3 +- .../core/internal/util/ScreenshotMasking.java | 170 ++++++++++++++++++ .../core/internal/util/ScreenshotUtils.java | 57 ++++++ .../ScreenshotUtilsMaskedScreenshotTest.kt | 154 ++++++++++++++++ 6 files changed, 388 insertions(+), 130 deletions(-) create mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotMasking.java create mode 100644 sentry-android-core/src/test/java/io/sentry/android/core/internal/util/ScreenshotUtilsMaskedScreenshotTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 496c5333ec1..15817dd70d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Measure the hostname cache TTL on the monotonic ticker the options provide, so that a device time change no longer shortens or extends it ([#6100](https://github.com/getsentry/sentry-java/pull/6100)) - Measure Session Replay's 1h recording cap and its touch-move debounce on a monotonic clock, so a device time change no longer stops a healthy recording early, keeps a finished one alive, or suppresses gesture events ([#6090](https://github.com/getsentry/sentry-java/pull/6090)) +- Add `ScreenshotUtils.takeMaskedScreenshot` so Hybrid SDKs can capture screenshots masked according to `SentryAndroidOptions.getScreenshot()` ([#6281](https://github.com/getsentry/sentry-java/pull/6281)) ### Dependencies diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java index 6283b47f95d..8b51c512c87 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java @@ -1,12 +1,10 @@ package io.sentry.android.core; import static io.sentry.TypeCheckHint.ANDROID_ACTIVITY; -import static io.sentry.android.core.internal.util.ScreenshotUtils.captureScreenshot; import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; import android.app.Activity; import android.graphics.Bitmap; -import android.view.View; import io.sentry.Attachment; import io.sentry.Hint; import io.sentry.SentryEvent; @@ -14,18 +12,12 @@ import io.sentry.android.core.internal.util.AndroidCurrentDateProvider; import io.sentry.android.core.internal.util.Debouncer; import io.sentry.android.core.internal.util.ScreenshotUtils; -import io.sentry.android.replay.util.MaskRenderer; -import io.sentry.android.replay.util.ViewsKt; -import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode; import io.sentry.internal.eventprocessor.SentryEventProcessor; import io.sentry.protocol.SentryTransaction; import io.sentry.util.ExceptionUtils; import io.sentry.util.HintUtils; import io.sentry.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,7 +35,6 @@ public final class ScreenshotEventProcessor implements SentryEventProcessor { private final @NotNull Debouncer debouncer; private static final long DEBOUNCE_WAIT_TIME_MS = 2000; private static final int DEBOUNCE_MAX_EXECUTIONS = 3; - private static final long MASKING_TIMEOUT_MS = 2000; private final boolean isReplayAvailable; private final AtomicBoolean isReplayModuleAbsenceLogged = new AtomicBoolean(false); @@ -70,10 +61,6 @@ public ScreenshotEventProcessor( } } - private boolean isMaskingEnabled() { - return !options.getScreenshot().getMaskViewClasses().isEmpty() && isReplayAvailable; - } - @Override public @NotNull SentryTransaction process( @NotNull SentryTransaction transaction, @NotNull Hint hint) { @@ -131,32 +118,16 @@ private boolean isMaskingEnabled() { return event; } - Bitmap screenshot = - captureScreenshot( - activity, options.getThreadChecker(), options.getLogger(), buildInfoProvider); + final @Nullable Bitmap screenshot = + ScreenshotUtils.captureMaskedScreenshot( + activity, options, buildInfoProvider, isReplayAvailable); if (screenshot == null) { return event; } - // Apply masking if enabled and replay module is available - if (isMaskingEnabled()) { - final @Nullable ViewHierarchyNode rootNode = captureViewHierarchy(activity); - if (rootNode == null) { - screenshot.recycle(); - return event; - } - final @Nullable Bitmap masked = applyMasking(screenshot, rootNode); - if (masked == null) { - // applyMasking already recycles its bitmaps on failure - return event; - } - screenshot = masked; - } - - final Bitmap finalScreenshot = screenshot; hint.setScreenshot( Attachment.fromByteProvider( - () -> ScreenshotUtils.compressBitmapToPng(finalScreenshot, options.getLogger()), + () -> ScreenshotUtils.compressBitmapToPng(screenshot, options.getLogger()), "screenshot.png", "image/png", false)); @@ -164,102 +135,6 @@ private boolean isMaskingEnabled() { return event; } - /** - * Captures the view hierarchy on the main thread, since view traversal requires it. If already on - * the main thread, captures directly; otherwise posts to the main thread and waits. - */ - private @Nullable ViewHierarchyNode captureViewHierarchy(final @NotNull Activity activity) { - if (options.getThreadChecker().isMainThread()) { - return buildViewHierarchy(activity); - } - - final AtomicReference result = new AtomicReference<>(null); - final CountDownLatch latch = new CountDownLatch(1); - - try { - activity.runOnUiThread( - () -> { - try { - result.set(buildViewHierarchy(activity)); - } finally { - latch.countDown(); - } - }); - - if (!latch.await(MASKING_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - options - .getLogger() - .log( - SentryLevel.WARNING, "Timed out waiting for view hierarchy capture on main thread"); - return null; - } - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Failed to capture view hierarchy", e); - return null; - } - - return result.get(); - } - - private @Nullable ViewHierarchyNode buildViewHierarchy(final @NotNull Activity activity) { - try { - final @Nullable View rootView = - activity.getWindow() != null - && activity.getWindow().peekDecorView() != null - && activity.getWindow().peekDecorView().getRootView() != null - ? activity.getWindow().peekDecorView().getRootView() - : null; - if (rootView == null) { - return null; - } - - final ViewHierarchyNode rootNode = - ViewHierarchyNode.Companion.fromView(rootView, null, 0, options.getScreenshot()); - ViewsKt.traverse(rootView, rootNode, options.getScreenshot(), options.getLogger(), null); - return rootNode; - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Failed to build view hierarchy", e); - return null; - } - } - - private @Nullable Bitmap applyMasking( - final @NotNull Bitmap screenshot, final @NotNull ViewHierarchyNode rootNode) { - Bitmap mutableBitmap = screenshot; - boolean createdCopy = false; - try (final MaskRenderer maskRenderer = new MaskRenderer()) { - // Make bitmap mutable if needed - if (!screenshot.isMutable()) { - mutableBitmap = screenshot.copy(Bitmap.Config.RGB_565, true); - if (mutableBitmap == null) { - screenshot.recycle(); - return null; - } - createdCopy = true; - } - - maskRenderer.renderMasks(mutableBitmap, rootNode, null); - - // Recycle original if we created a copy - if (createdCopy && !screenshot.isRecycled()) { - screenshot.recycle(); - } - - return mutableBitmap; - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Failed to mask screenshot", e); - if (createdCopy) { - if (!mutableBitmap.isRecycled()) { - mutableBitmap.recycle(); - } - } - if (!screenshot.isRecycled()) { - screenshot.recycle(); - } - return null; - } - } - @Override public @Nullable Long getOrder() { return 10000L; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryScreenshotOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryScreenshotOptions.java index 8680ed907d1..6e1cceb760b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryScreenshotOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryScreenshotOptions.java @@ -10,7 +10,8 @@ * #setMaskAllText(boolean)} and/or {@link #setMaskAllImages(boolean)}. * *

Note: Screenshot masking requires the {@code sentry-android-replay} module to be present at - * runtime. If the replay module is not available, screenshots will be captured without masking. + * runtime. If masking is enabled but the replay module is not available, no screenshot is captured + * to avoid leaking sensitive data. */ public final class SentryScreenshotOptions extends SentryMaskingOptions { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotMasking.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotMasking.java new file mode 100644 index 00000000000..4d3e2d15955 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotMasking.java @@ -0,0 +1,170 @@ +package io.sentry.android.core.internal.util; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.view.View; +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import io.sentry.android.core.SentryScreenshotOptions; +import io.sentry.android.replay.util.MaskRenderer; +import io.sentry.android.replay.util.ViewsKt; +import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode; +import io.sentry.util.ExceptionUtils; +import io.sentry.util.LoadClass; +import io.sentry.util.thread.IThreadChecker; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Applies screenshot masking using the Session Replay view hierarchy approach. Requires the + * `sentry-android-replay` module at runtime, callers must check {@link #isAvailable} first. + */ +@ApiStatus.Internal +public final class ScreenshotMasking { + + private static final long MASKING_TIMEOUT_MS = 2000; + + // Checked instead of the masking classes, since it's the only replay class whose name is kept + // by the consumer proguard rules + private static final String REPLAY_INTEGRATION_CLASS_NAME = + "io.sentry.android.replay.ReplayIntegration"; + + private ScreenshotMasking() {} + + /** Whether the `sentry-android-replay` module, required for masking, is available at runtime. */ + public static boolean isAvailable(final @NotNull ILogger logger) { + return new LoadClass().isClassAvailable(REPLAY_INTEGRATION_CLASS_NAME, logger); + } + + /** + * Masks the given screenshot according to the screenshot options. + * + * @return the masked bitmap, or null if masking failed. The passed screenshot is recycled on + * failure, so a raw screenshot is never returned in place of a masked one. + */ + public static @Nullable Bitmap mask( + final @NotNull Activity activity, + final @NotNull Bitmap screenshot, + final @NotNull SentryScreenshotOptions screenshotOptions, + final @NotNull IThreadChecker threadChecker, + final @NotNull ILogger logger) { + final @Nullable ViewHierarchyNode rootNode = + captureViewHierarchy(activity, screenshotOptions, threadChecker, logger); + if (rootNode == null) { + screenshot.recycle(); + return null; + } + // applyMasking already recycles its bitmaps on failure + return applyMasking(screenshot, rootNode, logger); + } + + /** + * Captures the view hierarchy on the main thread, since view traversal requires it. If already on + * the main thread, captures directly; otherwise posts to the main thread and waits. + */ + private static @Nullable ViewHierarchyNode captureViewHierarchy( + final @NotNull Activity activity, + final @NotNull SentryScreenshotOptions screenshotOptions, + final @NotNull IThreadChecker threadChecker, + final @NotNull ILogger logger) { + if (threadChecker.isMainThread()) { + return buildViewHierarchy(activity, screenshotOptions, logger); + } + + final AtomicReference result = new AtomicReference<>(null); + final CountDownLatch latch = new CountDownLatch(1); + + try { + activity.runOnUiThread( + () -> { + try { + result.set(buildViewHierarchy(activity, screenshotOptions, logger)); + } finally { + latch.countDown(); + } + }); + + if (!latch.await(MASKING_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + logger.log( + SentryLevel.WARNING, "Timed out waiting for view hierarchy capture on main thread"); + return null; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.log(SentryLevel.ERROR, "Interrupted while capturing view hierarchy", e); + return null; + } + + return result.get(); + } + + private static @Nullable ViewHierarchyNode buildViewHierarchy( + final @NotNull Activity activity, + final @NotNull SentryScreenshotOptions screenshotOptions, + final @NotNull ILogger logger) { + try { + final @Nullable View rootView = + activity.getWindow() != null + && activity.getWindow().peekDecorView() != null + && activity.getWindow().peekDecorView().getRootView() != null + ? activity.getWindow().peekDecorView().getRootView() + : null; + if (rootView == null) { + return null; + } + + final ViewHierarchyNode rootNode = + ViewHierarchyNode.Companion.fromView(rootView, null, 0, screenshotOptions); + ViewsKt.traverse(rootView, rootNode, screenshotOptions, logger, null); + return rootNode; + } catch (Throwable e) { + // Traversal calls into arbitrary (custom) View code, which can throw anything + ExceptionUtils.rethrowIfFatal(e); + logger.log(SentryLevel.ERROR, "Failed to build view hierarchy", e); + return null; + } + } + + private static @Nullable Bitmap applyMasking( + final @NotNull Bitmap screenshot, + final @NotNull ViewHierarchyNode rootNode, + final @NotNull ILogger logger) { + Bitmap mutableBitmap = screenshot; + boolean createdCopy = false; + try (final MaskRenderer maskRenderer = new MaskRenderer()) { + // Make bitmap mutable if needed + if (!screenshot.isMutable()) { + mutableBitmap = screenshot.copy(Bitmap.Config.RGB_565, true); + if (mutableBitmap == null) { + screenshot.recycle(); + return null; + } + createdCopy = true; + } + + maskRenderer.renderMasks(mutableBitmap, rootNode, null); + + // Recycle original if we created a copy + if (createdCopy && !screenshot.isRecycled()) { + screenshot.recycle(); + } + + return mutableBitmap; + } catch (RuntimeException e) { + logger.log(SentryLevel.ERROR, "Failed to mask screenshot", e); + if (createdCopy) { + if (!mutableBitmap.isRecycled()) { + mutableBitmap.recycle(); + } + } + if (!screenshot.isRecycled()) { + screenshot.recycle(); + } + return null; + } + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java index 092caa040db..657ba54cbd2 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java @@ -14,6 +14,7 @@ import io.sentry.ILogger; import io.sentry.SentryLevel; import io.sentry.android.core.BuildInfoProvider; +import io.sentry.android.core.SentryAndroidOptions; import io.sentry.util.thread.IThreadChecker; import java.io.ByteArrayOutputStream; import java.util.concurrent.CountDownLatch; @@ -51,6 +52,62 @@ public class ScreenshotUtils { return compressBitmapToPng(screenshot, logger); } + // Used by Hybrid SDKs + /** + * Takes a screenshot and masks it according to {@link SentryAndroidOptions#getScreenshot()}, the + * same way {@link io.sentry.android.core.ScreenshotEventProcessor} does for native errors. + * + * @see #captureMaskedScreenshot(Activity, SentryAndroidOptions, BuildInfoProvider, boolean) + * @noinspection unused + */ + public static @Nullable byte[] takeMaskedScreenshot( + final @NotNull Activity activity, + final @NotNull SentryAndroidOptions options, + final @NotNull BuildInfoProvider buildInfoProvider) { + final boolean isReplayAvailable = ScreenshotMasking.isAvailable(options.getLogger()); + return compressBitmapToPng( + captureMaskedScreenshot(activity, options, buildInfoProvider, isReplayAvailable), + options.getLogger()); + } + + /** + * Captures a screenshot and masks it according to {@link SentryAndroidOptions#getScreenshot()}. + * Shared by {@link io.sentry.android.core.ScreenshotEventProcessor} and Hybrid SDKs, so native + * and hybrid screenshots follow the same masking rules. + * + *

If no masking is configured, the screenshot is returned unmasked. If masking is configured + * but the `sentry-android-replay` module is not available, or masking fails, null is returned to + * avoid leaking sensitive data. + */ + public static @Nullable Bitmap captureMaskedScreenshot( + final @NotNull Activity activity, + final @NotNull SentryAndroidOptions options, + final @NotNull BuildInfoProvider buildInfoProvider, + final boolean isReplayAvailable) { + final boolean isMaskingConfigured = !options.getScreenshot().getMaskViewClasses().isEmpty(); + if (isMaskingConfigured && !isReplayAvailable) { + options + .getLogger() + .log(SentryLevel.WARNING, "Screenshot masking requires sentry-android-replay module"); + return null; + } + + final @Nullable Bitmap screenshot = + captureScreenshot( + activity, options.getThreadChecker(), options.getLogger(), buildInfoProvider); + if (screenshot == null || !isMaskingConfigured) { + return screenshot; + } + + // mask() recycles the screenshot on failure, so a raw screenshot is never returned + return ScreenshotMasking.mask( + activity, + screenshot, + options.getScreenshot(), + options.getThreadChecker(), + options.getLogger()); + } + public static @Nullable Bitmap captureScreenshot( final @NotNull Activity activity, final @NotNull ILogger logger, diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/ScreenshotUtilsMaskedScreenshotTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/ScreenshotUtilsMaskedScreenshotTest.kt new file mode 100644 index 00000000000..9bc6f9f85b0 --- /dev/null +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/ScreenshotUtilsMaskedScreenshotTest.kt @@ -0,0 +1,154 @@ +package io.sentry.android.core.internal.util + +import android.app.Activity +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.widget.LinearLayout +import android.widget.LinearLayout.LayoutParams +import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.sentry.android.core.BuildInfoProvider +import io.sentry.android.core.SentryAndroidOptions +import io.sentry.util.thread.IThreadChecker +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.robolectric.Robolectric.buildActivity +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadows.ShadowPixelCopy + +@RunWith(AndroidJUnit4::class) +@Config(shadows = [ShadowPixelCopy::class], sdk = [30]) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class ScreenshotUtilsMaskedScreenshotTest { + + private class Fixture { + lateinit var activity: Activity + val threadChecker = mock() + val options = + SentryAndroidOptions().apply { + dsn = "https://key@sentry.io/proj" + threadChecker = this@Fixture.threadChecker + } + val buildInfoProvider = BuildInfoProvider(options.logger) + + init { + whenever(threadChecker.isMainThread).thenReturn(true) + } + } + + private lateinit var fixture: Fixture + + private fun takeMaskedScreenshot(isReplayAvailable: Boolean): ByteArray? = + ScreenshotUtils.compressBitmapToPng( + ScreenshotUtils.captureMaskedScreenshot( + fixture.activity, + fixture.options, + fixture.buildInfoProvider, + isReplayAvailable, + ), + fixture.options.logger, + ) + + @BeforeTest + fun `set up`() { + System.setProperty("robolectric.areWindowsMarkedVisible", "true") + System.setProperty("robolectric.pixelCopyRenderMode", "hardware") + + fixture = Fixture() + fixture.activity = buildActivity(TextActivity::class.java, null).setup().get() + } + + @Test + fun `when masking is not configured, returns an unmasked screenshot`() { + val bytes = takeMaskedScreenshot(isReplayAvailable = false) + + assertNotNull(bytes) + } + + @Test + fun `when masking is configured, returns a masked screenshot`() { + val unmasked = takeMaskedScreenshot(isReplayAvailable = true) + fixture.options.screenshot.setMaskAllText(true) + val masked = takeMaskedScreenshot(isReplayAvailable = true) + + assertNotNull(unmasked) + assertNotNull(masked) + assertFalse(unmasked.contentEquals(masked)) + } + + @Test + fun `when masking is configured but replay is not available, returns no screenshot`() { + fixture.options.screenshot.setMaskAllText(true) + + val bytes = takeMaskedScreenshot(isReplayAvailable = false) + + assertNull(bytes) + } + + @Test + fun `when masking is configured but fails, returns no screenshot`() { + fixture.options.screenshot.setMaskAllText(true) + fixture.activity.findViewById(android.R.id.text1).throwOnTraversal = true + + val bytes = takeMaskedScreenshot(isReplayAvailable = true) + + assertNull(bytes) + } + + @Test + fun `public overload detects replay availability`() { + fixture.options.screenshot.setMaskAllText(true) + + // sentry-android-replay is on the test classpath + val bytes = + ScreenshotUtils.takeMaskedScreenshot( + fixture.activity, + fixture.options, + fixture.buildInfoProvider, + ) + + assertNotNull(bytes) + } +} + +private class TextActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val linearLayout = + LinearLayout(this).apply { + setBackgroundColor(Color.WHITE) + orientation = LinearLayout.VERTICAL + layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + } + linearLayout.addView( + ThrowingTextView(this).apply { + id = android.R.id.text1 + text = "Sensitive text" + setTextColor(Color.BLACK) + textSize = 32f + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) + } + ) + setContentView(linearLayout) + } +} + +/** Fails view hierarchy traversal on demand, to exercise the masking failure path. */ +private class ThrowingTextView(context: Context) : TextView(context) { + var throwOnTraversal = false + + override fun getTotalPaddingLeft(): Int { + if (throwOnTraversal) { + throw IllegalStateException("Traversal failure") + } + return super.getTotalPaddingLeft() + } +} From e5c61b8485688d75251a6eec45c1f4b4d8bb62ef Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:42:42 +0200 Subject: [PATCH 03/10] ci: Move jobs from Bitrise back to GitHub-hosted runners (#6289) Co-Authored-By: Nelson Osacky Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> --- .github/actionlint.yaml | 4 ---- .github/workflows/build.yml | 4 +--- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/format-code.yml | 2 +- .github/workflows/generate-javadocs.yml | 2 +- .github/workflows/integration-tests-macrobenchmark.yml | 2 +- .github/workflows/integration-tests-size.yml | 2 +- .github/workflows/integration-tests-ui-critical.yml | 4 +--- .github/workflows/integration-tests-ui.yml | 2 +- 9 files changed, 8 insertions(+), 16 deletions(-) delete mode 100644 .github/actionlint.yaml diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml deleted file mode 100644 index 83df0b81ec3..00000000000 --- a/.github/actionlint.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Custom runner labels for actionlint (same approach as sentry-cocoa). -self-hosted-runner: - labels: - - bitrise_pool_name:tahoe diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6add05cd03..f4ae724df41 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,9 +13,7 @@ jobs: build: # Job name is a required status check in the main branch ruleset; keep it stable. name: Build Job ubuntu-latest - Java 17 - # Runs on Bitrise-hosted runners (same pattern as sentry-cocoa) instead of - # GitHub-hosted ubuntu-latest, which ran out of memory during `make preMerge`. - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d25122d59ad..e31db87a4e8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,7 +13,7 @@ concurrency: jobs: analyze: name: Analyze - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: macos-15 env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index ed0fa2539a0..d236a64b0ce 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -5,7 +5,7 @@ on: jobs: format-code: name: Format Code - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/generate-javadocs.yml b/.github/workflows/generate-javadocs.yml index 4bc252f7a30..7cb5c903ca3 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -6,7 +6,7 @@ on: jobs: build-and-deploy-javadocs: name: Build and deploy Javadocs - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest steps: - name: Checkout 🛎️ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.github/workflows/integration-tests-macrobenchmark.yml b/.github/workflows/integration-tests-macrobenchmark.yml index 7ba03815fc4..377db931fc6 100644 --- a/.github/workflows/integration-tests-macrobenchmark.yml +++ b/.github/workflows/integration-tests-macrobenchmark.yml @@ -12,7 +12,7 @@ concurrency: jobs: macrobenchmark: name: Macrobenchmark - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest # we copy the secret to the env variable in order to access it in the workflow env: diff --git a/.github/workflows/integration-tests-size.yml b/.github/workflows/integration-tests-size.yml index 530f5ea471d..ebdad2a4d9a 100644 --- a/.github/workflows/integration-tests-size.yml +++ b/.github/workflows/integration-tests-size.yml @@ -13,7 +13,7 @@ concurrency: jobs: build: name: Build and Analyze SDK Size - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index c793151d3d7..27ecbff7d1c 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -20,9 +20,7 @@ env: jobs: build: name: Build - # Only the APK build runs on Bitrise; the Maestro job below needs KVM for the - # x86_64 emulator, which the macOS Bitrise VMs don't provide. - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} diff --git a/.github/workflows/integration-tests-ui.yml b/.github/workflows/integration-tests-ui.yml index 2483394aa14..2ece76b6b53 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -12,7 +12,7 @@ concurrency: jobs: test: name: Ui tests - runs-on: ["bitrise_pool_name:tahoe"] + runs-on: ubuntu-latest # we copy the secret to the env variable in order to access it in the workflow env: From 3f3b8ef146bb106eaedd9eb238a43e4861c3d94a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:24:52 +0200 Subject: [PATCH 04/10] chore(deps): bump the github-actions group across 1 directory with 4 updates (#6280) Bumps the github-actions group with 4 updates in the / directory: [actions/setup-java](https://github.com/actions/setup-java), [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [getsentry/craft](https://github.com/getsentry/craft). Updates `actions/setup-java` from 6.0.1 to 6.1.0 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/de7274f081f381c8f8158605e0321c36c376e2e6...b843ebf9b913146b38da2eaa7aeddf76074ae1ad) Updates `github/codeql-action/init` from 4.38.3 to 4.39.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/24c54180a607b1449ed407dd24f251e4e9147c8d...6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee) Updates `github/codeql-action/analyze` from 4.38.3 to 4.39.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/24c54180a607b1449ed407dd24f251e4e9147c8d...6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee) Updates `getsentry/craft` from 2.34.1 to 2.35.0 - [Release notes](https://github.com/getsentry/craft/releases) - [Changelog](https://github.com/getsentry/craft/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/craft/compare/3f2abdc5703191d12ef2cc013f792f1bd220f44d...7a7857552e2cdc08b502de96e6076849c01fba32) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: getsentry/craft dependency-version: 2.35.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.39.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: github/codeql-action/init dependency-version: 4.39.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/agp-matrix.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/enforce-license-compliance.yml | 2 +- .github/workflows/format-code.yml | 2 +- .github/workflows/generate-javadocs.yml | 2 +- .github/workflows/integration-tests-macrobenchmark.yml | 2 +- .github/workflows/integration-tests-size.yml | 2 +- .github/workflows/integration-tests-ui-critical.yml | 2 +- .github/workflows/integration-tests-ui.yml | 2 +- .github/workflows/release-build.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/spring-boot-2-matrix.yml | 2 +- .github/workflows/spring-boot-3-matrix.yml | 2 +- .github/workflows/spring-boot-4-matrix.yml | 2 +- .github/workflows/system-tests-backend.yml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index f1c03f0884d..973cfcf6282 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -33,7 +33,7 @@ jobs: submodules: 'recursive' - name: Setup Java Version - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4ae724df41..f11c6475b79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: submodules: 'recursive' - name: Setup Java Version - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e31db87a4e8..662c20a0885 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: submodules: 'recursive' - name: Setup Java Version - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' @@ -36,7 +36,7 @@ jobs: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - name: Initialize CodeQL - uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # pin@v2 + uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # pin@v2 with: languages: 'java' @@ -45,4 +45,4 @@ jobs: ./gradlew buildForCodeQL --no-build-cache - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # pin@v2 + uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # pin@v2 diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index 8d37523888d..6b94bc1b62c 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -14,7 +14,7 @@ jobs: uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 - name: Set up Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index d236a64b0ce..7b356dbada4 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -13,7 +13,7 @@ jobs: submodules: 'recursive' - name: set up JDK 17 - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/generate-javadocs.yml b/.github/workflows/generate-javadocs.yml index 7cb5c903ca3..7a74d941f67 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -14,7 +14,7 @@ jobs: submodules: 'recursive' - name: set up JDK 17 - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/integration-tests-macrobenchmark.yml b/.github/workflows/integration-tests-macrobenchmark.yml index 377db931fc6..ad7153f7509 100644 --- a/.github/workflows/integration-tests-macrobenchmark.yml +++ b/.github/workflows/integration-tests-macrobenchmark.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: 'Set up Java: 17' - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/integration-tests-size.yml b/.github/workflows/integration-tests-size.yml index ebdad2a4d9a..e9d3d6e80c9 100644 --- a/.github/workflows/integration-tests-size.yml +++ b/.github/workflows/integration-tests-size.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Java Version - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: "temurin" java-version: "17" diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index 27ecbff7d1c..ab379c32f3f 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Java 17 - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/integration-tests-ui.yml b/.github/workflows/integration-tests-ui.yml index 2ece76b6b53..5871d37bebb 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -27,7 +27,7 @@ jobs: submodules: 'recursive' - name: 'Set up Java: 17' - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 5ab90f51c85..61510987035 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -20,7 +20,7 @@ jobs: submodules: 'recursive' - name: Setup Java Version - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9996b6dfa2f..ce434f67c40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: fetch-depth: 0 submodules: 'recursive' - name: Prepare release - uses: getsentry/craft@3f2abdc5703191d12ef2cc013f792f1bd220f44d # v2.34.1 + uses: getsentry/craft@7a7857552e2cdc08b502de96e6076849c01fba32 # v2.35.0 env: GITHUB_TOKEN: ${{ steps.token.outputs.token }} with: diff --git a/.github/workflows/spring-boot-2-matrix.yml b/.github/workflows/spring-boot-2-matrix.yml index c13f5e695a6..d4e0839209b 100644 --- a/.github/workflows/spring-boot-2-matrix.yml +++ b/.github/workflows/spring-boot-2-matrix.yml @@ -45,7 +45,7 @@ jobs: python3 -m pip install -r requirements.txt - name: Set up Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/spring-boot-3-matrix.yml b/.github/workflows/spring-boot-3-matrix.yml index fde7ec44a38..1e6623d7727 100644 --- a/.github/workflows/spring-boot-3-matrix.yml +++ b/.github/workflows/spring-boot-3-matrix.yml @@ -45,7 +45,7 @@ jobs: python3 -m pip install -r requirements.txt - name: Set up Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/spring-boot-4-matrix.yml b/.github/workflows/spring-boot-4-matrix.yml index 2d28cc01b93..3554926c3e0 100644 --- a/.github/workflows/spring-boot-4-matrix.yml +++ b/.github/workflows/spring-boot-4-matrix.yml @@ -45,7 +45,7 @@ jobs: python3 -m pip install -r requirements.txt - name: Set up Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index 7449e5a1827..1d32bba87bd 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -118,7 +118,7 @@ jobs: python3 -m pip install -r requirements.txt - name: Set up Java - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 with: distribution: 'temurin' java-version: '17' From d5802a7e014d3986a701d2f1e82437d88d610717 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:23:56 +0000 Subject: [PATCH 05/10] docs: Require issue discussion before PRs (#6288) * docs: Require issue discussion before PRs Co-Authored-By: Chris Aigner * docs: Add Sentry logo header to CONTRIBUTING.md Co-Authored-By: Chris Aigner * Apply suggestion from @christophaigner * Apply suggestion from @christophaigner --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Chris Aigner <25478494+christophaigner@users.noreply.github.com> --- CONTRIBUTING.md | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f4354c72a89..684ef3c7353 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,38 @@ -# Contributing to sentry-java +

+ + + +
+

-We love pull requests from everyone. -We suggest opening an issue to discuss bigger changes before investing on a big PR. +# Contributing + +Thanks for your interest in contributing to `sentry-java`! This guide explains how we work with external contributions and how to get your environment set up. If you have questions, reach out on [Discord](https://discord.com/invite/sentry). + +# Submitting Changes + +**TL;DR: Discuss your change in a GitHub issue before opening a pull request. We may close PRs that weren't discussed with a maintainer first.** + +Every line we merge is code we maintain long-term for hundreds of thousands of apps, so we want to agree on the problem and the approach before anyone writes code. This saves time for you and for us. + +## Before You Start + +1. **Find or create an issue.** Search the [issue tracker](https://github.com/getsentry/sentry-java/issues) first. If nothing fits, open a new issue describing the problem or feature. +2. **Discuss your approach with a maintainer.** Comment on the issue with what you plan to change and wait for a maintainer to respond before you start working. +3. **Check the assignee.** If the issue is already assigned to someone else, coordinate with them in the issue before starting work. + +## Making Your Contribution + +- Fork the repo and make your changes on a branch. +- Add tests. Non-trivial PRs won't be accepted without them. +- Don't bump version numbers. We do that when we release. +- Open a PR that links the issue (e.g. `Fixes #123`) +- Name your PR according to our [commit message format](https://develop.sentry.dev/commit-messages/#commit-message-format). +- Keep PRs focused: one change per PR, no unrelated refactors. + +## AI Use + +You are welcome to use whatever tools you prefer for making a contribution. However, any changes you propose have to be understood, reviewed and tested by you, a human, first, before you submit a pull request with them for the Sentry team to review. If we feel like that didn't happen, we will close the PR outright. For example, we won't review visibly AI-generated PRs from an agent instructed to look for and "fix" open issues in the repo. This aligns with our SDK principle: [every line has an owner](https://develop.sentry.dev/sdk/getting-started/principles/#every-line-has-an-owner). # Requirements @@ -68,8 +99,3 @@ issue without a closing keyword is not enough. Build and tests are automatically run against branches and pull requests via GH Actions. - - -# AI Use - -You are welcome to use whatever tools you prefer for making a contribution. However, any changes you propose have to be reviewed and tested by you, a human, first, before you submit a pull request with them for the Sentry team to review. If we feel like that did not happen, we will close the PR outright. For example, we will not review visibly AI-generated PRs from an agent instructed to look for and "fix" open issues in the repo. This aligns with our SDK principle: [every line has an owner](https://develop.sentry.dev/sdk/getting-started/principles/#every-line-has-an-owner). From ebf465f515f471725d89aea8c70961fe6431a574 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 21 Oct 2026 09:21:19 +0200 Subject: [PATCH 06/10] fix(client-reports): Count spans in batched span envelope items (#6265) Prior to this commit, ClientReportRecorder didn't have any explicit logic for dealing with span envelopes because sentry-java didn't implement them. That meant that any span envelopes created by hybrid SDKs and dropped by sentry-java would be shunted to the recorder's fallback branch and wrongly register a single dropped span per dropped envelope, rather than the actual number. This commit adds the missing span envelope logic, so that we now report the actual span count. Refs [sentry-docs#19854](https://github.com/getsentry/sentry-docs/pull/19854) --- CHANGELOG.md | 1 + .../clientreport/ClientReportRecorder.java | 12 +++- .../sentry/clientreport/ClientReportTest.kt | 59 +++++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15817dd70d3..acca00fc3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Report the number of dropped spans in client reports when a batched span envelope item is discarded, instead of a quantity of 1 (e.g. for Flutter span streaming) ([#6265](https://github.com/getsentry/sentry-java/pull/6265)) - Keep sending events from Android 7.1.0 and older devices after Sentry changes its TLS certificate authority ([#6227](https://github.com/getsentry/sentry-java/pull/6227)) - In February 2027, Sentry moves its TLS certificates from DigiCert to Let's Encrypt and Google Trust Services ([announcement](https://sentry.io/changelog/were-changing-our-tls-certificate-authority-in-february-2027)). - Android 7.1.0 (API 25) and older don't trust the Let's Encrypt root certificate (ISRG Root X1), so without this fix, the SDK on those devices can no longer send events to Sentry after the switch. diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java index ccd7c5dc402..ba6d60e2563 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java @@ -113,6 +113,12 @@ public void recordLostEnvelopeItem( recordLostEventInternal( reason.getReason(), DataCategory.TraceMetricByte.getCategory(), metricBytes); executeOnDiscard(reason, itemCategory, count); + } else if (itemCategory.equals(DataCategory.Span)) { + // Batched span items (span streaming) aren't created by this SDK yet, but hybrid SDKs + // like sentry-dart send them through it via InternalSentrySdk.captureEnvelope. + final long count = itemCountFromHeader(envelopeItem); + recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), count); + executeOnDiscard(reason, itemCategory, count); } else { recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L); executeOnDiscard(reason, itemCategory, 1L); @@ -156,9 +162,9 @@ private void recordLostEventInternal( storage.addCount(key, countToAdd); } - // The number of items batched into a log or metric envelope item is stored in its header, so we - // read it from there instead of deserializing the payload. Deserializing on the discard path is - // expensive and, under sustained rate limiting, caused a CPU busy-loop (JAVA-662). + // The number of items batched into a log, metric or span envelope item is stored in its header, + // so we read it from there instead of deserializing the payload. Deserializing on the discard + // path is expensive and, under sustained rate limiting, caused a CPU busy-loop (JAVA-662). private long itemCountFromHeader(final @NotNull SentryEnvelopeItem envelopeItem) { final @Nullable Integer itemCount = envelopeItem.getHeader().getItemCount(); return itemCount != null ? itemCount : 1L; diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index 165f3c6c030..e077b25fb3c 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -59,6 +59,7 @@ import org.mockito.kotlin.whenever private const val LOG_CONTENT_TYPE = "application/vnd.sentry.items.log+json" private const val METRIC_CONTENT_TYPE = "application/vnd.sentry.items.trace-metric+json" +private const val SPAN_CONTENT_TYPE = "application/vnd.sentry.items.span.v2+json" class ClientReportTest { lateinit var opts: SentryOptions @@ -503,6 +504,64 @@ class ClientReportTest { assertEquals(1, clientReport.quantityOf(DataCategory.TraceMetric)) } + @Test + fun `recording lost span item reads count from the header`() { + val onDiscardMock = mock() + givenClientReportRecorder { options -> options.onDiscard = onDiscardMock } + + // Batched span items reach this SDK from hybrid SDKs (e.g. Flutter span streaming). + val item = + mockEnvelopeItem( + SentryItemType.Span, + SPAN_CONTENT_TYPE, + itemCount = 500, + data = "irrelevant payload".toByteArray(), + ) + + clientReportRecorder.recordLostEnvelopeItem(DiscardReason.NETWORK_ERROR, item) + + verify(onDiscardMock, times(1)).execute(DiscardReason.NETWORK_ERROR, DataCategory.Span, 500) + + val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport() + assertEquals(500, clientReport.quantityOf(DataCategory.Span)) + } + + @Test + fun `recording rate limited span item reads count from the header`() { + givenClientReportRecorder() + + val item = + mockEnvelopeItem(SentryItemType.Span, SPAN_CONTENT_TYPE, itemCount = 7, data = ByteArray(0)) + + clientReportRecorder.recordLostEnvelopeItem(DiscardReason.RATELIMIT_BACKOFF, item) + + val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport() + val spanItem = + clientReport!!.discardedEvents!!.single { + it.category == DataCategory.Span.category && + it.reason == DiscardReason.RATELIMIT_BACKOFF.reason + } + assertEquals(7, spanItem.quantity) + } + + @Test + fun `recording lost span item without item count in header falls back to one`() { + givenClientReportRecorder() + + val item = + mockEnvelopeItem( + SentryItemType.Span, + SPAN_CONTENT_TYPE, + itemCount = null, + data = ByteArray(0), + ) + + clientReportRecorder.recordLostEnvelopeItem(DiscardReason.NETWORK_ERROR, item) + + val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport() + assertEquals(1, clientReport.quantityOf(DataCategory.Span)) + } + private fun mockEnvelopeItem( type: SentryItemType, contentType: String, From 37d61d8e6076e98fcc6a74f6d846aed5d6463a4e Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 09:21:38 +0200 Subject: [PATCH 07/10] build(deps): Bump certifi to 2026.10.19 (#6333) Removes trust in the compromised eMudhra/emSign CA roots (VULN-3216). Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Geoff Goldsmith --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ffdb120a7a7..62c248385cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -certifi==2025.7.14 +certifi==2026.10.19 charset-normalizer==3.4.2 idna==3.15 requests==2.33.0 From ece9581269ee35dac7c76397c2b9188014bbb75f Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 21 Oct 2026 10:07:21 +0200 Subject: [PATCH 08/10] test: Add an in-process MockSentryServer to sentry-test-support (#6266) First step of moving the sample system tests into the modules they cover. The server stands in for the Python mock server: it binds a free port, accepts envelopes through the SDK's real HTTP transport, and lets tests wait for a matching one. The release-minified UI test APK pulls in sentry-test-support, so R8 needs to be told the JDK-only com.sun.net.httpserver classes are absent. Co-authored-by: Claude Opus 5.5 (1M context) --- .../sentry-uitest-android/proguard-rules.pro | 1 + sentry-test-support/build.gradle.kts | 2 + .../kotlin/io/sentry/test/MockSentryServer.kt | 87 +++++++++++++++++++ .../io/sentry/test/MockSentryServerTest.kt | 34 ++++++++ 4 files changed, 124 insertions(+) create mode 100644 sentry-test-support/src/main/kotlin/io/sentry/test/MockSentryServer.kt create mode 100644 sentry-test-support/src/test/kotlin/io/sentry/test/MockSentryServerTest.kt diff --git a/sentry-android-integration-tests/sentry-uitest-android/proguard-rules.pro b/sentry-android-integration-tests/sentry-uitest-android/proguard-rules.pro index 5de2dac4bdb..174e6e38774 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/proguard-rules.pro +++ b/sentry-android-integration-tests/sentry-uitest-android/proguard-rules.pro @@ -24,6 +24,7 @@ -dontwarn com.google.errorprone.** -dontwarn com.sun.jna.** +-dontwarn com.sun.net.httpserver.** -dontwarn edu.umd.cs.findbugs.** -dontwarn java.lang.instrument.** -dontwarn java.lang.management.** diff --git a/sentry-test-support/build.gradle.kts b/sentry-test-support/build.gradle.kts index a0b508c9715..ca60a47c6bb 100644 --- a/sentry-test-support/build.gradle.kts +++ b/sentry-test-support/build.gradle.kts @@ -30,4 +30,6 @@ dependencies { implementation(kotlin(Config.kotlinStdLib)) implementation(libs.kotlin.test.junit) implementation(libs.mockito.kotlin) + + testImplementation(libs.google.truth) } diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/MockSentryServer.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/MockSentryServer.kt new file mode 100644 index 00000000000..439b61f2c2a --- /dev/null +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/MockSentryServer.kt @@ -0,0 +1,87 @@ +package io.sentry.test + +import com.sun.net.httpserver.HttpExchange +import com.sun.net.httpserver.HttpServer +import io.sentry.JsonSerializer +import io.sentry.SentryEnvelope +import io.sentry.SentryOptions +import io.sentry.time.Deadline +import io.sentry.time.JavaMonotonicTicker +import java.io.Closeable +import java.net.InetAddress +import java.net.InetSocketAddress +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.TimeUnit +import java.util.zip.GZIPInputStream + +/** + * A stand-in for Sentry's ingestion endpoint that integration tests point the SDK at through [dsn]. + * + * Envelopes travel through the real HTTP transport, so serialization, compression and the DSN are + * covered too. It binds a free port so that test tasks running in parallel don't clash, and it + * works for an app in a child process as well as in the test's own JVM. + */ +class MockSentryServer : Closeable { + private val serializer = JsonSerializer(SentryOptions.empty()) + private val received = CopyOnWriteArrayList() + private val server = + HttpServer.create(InetSocketAddress(InetAddress.getByName(HOST), 0), 0).apply { + createContext("/api/") { exchange -> receive(exchange) } + start() + } + + val dsn: String = "http://key@$HOST:${server.address.port}/0" + + val envelopes: List + get() = received.toList() + + /** Returns the first received envelope matching [predicate], waiting up to [timeoutMillis]. */ + fun awaitEnvelope( + timeoutMillis: Long = DEFAULT_TIMEOUT_MILLIS, + predicate: (SentryEnvelope) -> Boolean, + ): SentryEnvelope { + val deadline = + Deadline.after(JavaMonotonicTicker.getInstance(), timeoutMillis, TimeUnit.MILLISECONDS) + while (true) { + received.firstOrNull(predicate)?.let { + return it + } + if (deadline.hasPassed()) { + throw AssertionError( + "No matching envelope within ${timeoutMillis}ms, received ${received.size} envelopes" + ) + } + Thread.sleep(POLL_INTERVAL_MILLIS) + } + } + + fun reset() { + received.clear() + } + + override fun close() { + server.stop(0) + } + + private fun receive(exchange: HttpExchange) { + try { + val body = + if (exchange.requestHeaders.getFirst("Content-Encoding") == "gzip") { + GZIPInputStream(exchange.requestBody) + } else { + exchange.requestBody + } + serializer.deserializeEnvelope(body)?.let { received.add(it) } + exchange.sendResponseHeaders(200, -1) + } finally { + exchange.close() + } + } + + private companion object { + // An IP rather than "localhost", which can resolve to IPv4 for the server and IPv6 for the SDK. + const val HOST = "127.0.0.1" + const val DEFAULT_TIMEOUT_MILLIS = 10_000L + const val POLL_INTERVAL_MILLIS = 50L + } +} diff --git a/sentry-test-support/src/test/kotlin/io/sentry/test/MockSentryServerTest.kt b/sentry-test-support/src/test/kotlin/io/sentry/test/MockSentryServerTest.kt new file mode 100644 index 00000000000..6a1e3f03225 --- /dev/null +++ b/sentry-test-support/src/test/kotlin/io/sentry/test/MockSentryServerTest.kt @@ -0,0 +1,34 @@ +package io.sentry.test + +import com.google.common.truth.Truth.assertThat +import io.sentry.Sentry +import io.sentry.SentryItemType +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class MockSentryServerTest { + private val server = MockSentryServer() + + @AfterTest + fun teardown() { + Sentry.close() + server.close() + } + + @Test + fun `receives an event sent through the HTTP transport`() { + initForTest { it.dsn = server.dsn } + + val eventId = Sentry.captureMessage("hello") + Sentry.flush(5000) + + val envelope = server.awaitEnvelope { it.header.eventId == eventId } + assertThat(envelope.items.single().header.type).isEqualTo(SentryItemType.Event) + } + + @Test + fun `awaitEnvelope fails once the timeout passes without a match`() { + assertFailsWith { server.awaitEnvelope(timeoutMillis = 100) { true } } + } +} From 8d7b785cbc7863425cee09e6c5b0f28927c3319d Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 21 Oct 2026 10:45:11 +0200 Subject: [PATCH 09/10] ci: Use the latest Python 3.10 patch in CI (#6339) ubuntu-latest now resolves to Ubuntu 26.04, and actions/python-versions ships no 3.10.5 build for it, so "Set up Python" fails before any test runs in the Spring Boot matrix and system-test jobs. Pinning the minor version lets setup-python pick the newest 3.10.x available for the runner image while keeping requirements.txt and the system-test runner on the same Python minor. Co-authored-by: Claude Opus 5.5 --- .github/workflows/spring-boot-2-matrix.yml | 2 +- .github/workflows/spring-boot-3-matrix.yml | 2 +- .github/workflows/spring-boot-4-matrix.yml | 2 +- .github/workflows/system-tests-backend.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spring-boot-2-matrix.yml b/.github/workflows/spring-boot-2-matrix.yml index d4e0839209b..d798f0e4377 100644 --- a/.github/workflows/spring-boot-2-matrix.yml +++ b/.github/workflows/spring-boot-2-matrix.yml @@ -37,7 +37,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: '3.10.5' + python-version: '3.10' - name: Install Python dependencies run: | diff --git a/.github/workflows/spring-boot-3-matrix.yml b/.github/workflows/spring-boot-3-matrix.yml index 1e6623d7727..fdc9169cccc 100644 --- a/.github/workflows/spring-boot-3-matrix.yml +++ b/.github/workflows/spring-boot-3-matrix.yml @@ -37,7 +37,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: '3.10.5' + python-version: '3.10' - name: Install Python dependencies run: | diff --git a/.github/workflows/spring-boot-4-matrix.yml b/.github/workflows/spring-boot-4-matrix.yml index 3554926c3e0..08dfd5e389d 100644 --- a/.github/workflows/spring-boot-4-matrix.yml +++ b/.github/workflows/spring-boot-4-matrix.yml @@ -37,7 +37,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: '3.10.5' + python-version: '3.10' - name: Install Python dependencies run: | diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index 1d32bba87bd..aa25e9854c6 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -110,7 +110,7 @@ jobs: - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: '3.10.5' + python-version: '3.10' - name: Install Python dependencies run: | From 5e493bdef0dff1829b6914db10b348884e9b0097 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Wed, 21 Oct 2026 11:56:40 +0200 Subject: [PATCH 10/10] chore(build): Compile with a JDK 21 toolchain and upgrade Mockito and Error Prone (#6242) * build: Switch to JDK 21 and upgrade Mockito, Robolectric and Error Prone * build: Compile tests for JDK 21 and fix buildSrc JVM target * build: Compile with a JDK 21 toolchain from a build-logic convention plugin * docs(kafka): Explain the explicit double cast at the latency call site * build: Compile the samples and their tests with the toolchain JDK * Format code * build: Drop the now-unused JvmTarget imports from the samples * build: Clarify the jvm-targets plugin comments and rename its helper * build: Require JDK 21 to run Gradle again * build: Keep the Spring Boot 2 sample apps on their published bytecode level * ci: Drop the stale Java version from the build job name * build: Keep Robolectric at 4.15 and leave its test environment untouched * build: Move the Java 8 release pin into the jvm-targets convention plugin * build: Drop the Error Prone and NullAway code fixes, they move to their own PR * ci: Drop the stale Java version from the build job name * fix(core): Suppress the new Error Prone and javac 21 findings in the sentry module * fix(core): Resolve the remaining Error Prone and javac 21 findings in the sentry module * build: Move the JDK and bytecode level decisions into the io.sentry.compilation plugin * Address PR comments * test(android): Make the flush timeout verifies match the real log call * chore(core): Reword the JDK 21 suppression comments * build: Keep only the javac unit test targets inside afterEvaluate * build: Drop afterEvaluate, pin the Kotlin API level and rename the convention plugin * Fix JDK 21 findings in sentry-android-core (#6261) Fix JDK 21 findings in sentry-android-core * Fix JDK 21 findings in the Spring modules (#6260) * Fix JDK 21 findings in the JVM integrations (#6259) * fix multiplatform target compilations --------- Co-authored-by: Sentry Github Bot --- .github/workflows/agp-matrix.yml | 2 +- .github/workflows/build.yml | 4 +- .github/workflows/codeql-analysis.yml | 2 +- .../workflows/enforce-license-compliance.yml | 2 +- .github/workflows/format-code.yml | 2 +- .github/workflows/generate-javadocs.yml | 2 +- .../integration-tests-macrobenchmark.yml | 2 +- .github/workflows/integration-tests-size.yml | 2 +- .../integration-tests-ui-critical.yml | 2 +- .github/workflows/integration-tests-ui.yml | 2 +- .github/workflows/release-build.yml | 2 +- .github/workflows/spring-boot-2-matrix.yml | 2 +- .github/workflows/spring-boot-3-matrix.yml | 2 +- .github/workflows/spring-boot-4-matrix.yml | 2 +- .github/workflows/system-tests-backend.yml | 2 +- CONTRIBUTING.md | 4 +- build-logic/build.gradle.kts | 6 + build-logic/settings.gradle.kts | 2 + .../io/sentry/gradle/JdkTargetConvention.kt | 116 ++++++++++++++++++ build.gradle.kts | 27 +--- buildSrc/build.gradle.kts | 6 - gradle/libs.versions.toml | 16 ++- sentry-android-core/build.gradle.kts | 2 +- .../core/ApplicationNotResponding.java | 2 + .../core/SentryUserFeedbackButton.java | 3 + .../core/performance/AppStartMetrics.java | 3 + .../android/core/AnrV2IntegrationTest.kt | 2 +- .../ApplicationExitIntegrationTestBase.kt | 11 +- .../android/core/TombstoneIntegrationTest.kt | 2 +- sentry-android-fragment/build.gradle.kts | 2 +- .../build.gradle.kts | 2 - .../build.gradle.kts | 9 -- .../sentry-uitest-android/build.gradle.kts | 3 - sentry-android-navigation/build.gradle.kts | 2 +- sentry-android-navigation3/build.gradle.kts | 2 +- sentry-android-replay/build.gradle.kts | 2 +- sentry-android-sqlite/build.gradle.kts | 2 +- sentry-android-timber/build.gradle.kts | 2 +- .../android/timber/SentryTimberTreeTest.kt | 3 +- sentry-apache-http-client-5/build.gradle.kts | 4 +- sentry-apollo-3/build.gradle.kts | 4 +- sentry-apollo-4/build.gradle.kts | 4 +- sentry-apollo-5/build.gradle.kts | 4 +- sentry-apollo/build.gradle.kts | 6 +- sentry-async-profiler/build.gradle.kts | 4 +- ...AsyncProfilerToSentryProfileConverter.java | 2 +- sentry-compose/build.gradle.kts | 2 +- sentry-graphql-22/build.gradle.kts | 4 +- sentry-graphql-core/build.gradle.kts | 4 +- sentry-graphql/build.gradle.kts | 4 +- sentry-jcache/build.gradle.kts | 4 +- sentry-jdbc/build.gradle.kts | 4 +- sentry-jul/build.gradle.kts | 2 +- .../java/io/sentry/jul/SentryHandler.java | 3 + sentry-kafka/build.gradle.kts | 4 +- .../kafka/SentryKafkaConsumerTracing.java | 2 +- .../io/sentry/kafka/SentryKafkaProducer.java | 6 +- sentry-kotlin-extensions/build.gradle.kts | 2 +- sentry-ktor-client/build.gradle.kts | 4 +- sentry-launchdarkly-android/build.gradle.kts | 2 +- sentry-launchdarkly-server/build.gradle.kts | 4 +- sentry-log4j2/build.gradle.kts | 2 +- sentry-logback/build.gradle.kts | 2 +- .../io/sentry/logback/SentryAppender.java | 3 +- sentry-okhttp/build.gradle.kts | 4 +- sentry-openfeature/build.gradle.kts | 4 +- sentry-openfeign/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../OtelContextScopesStorage.java | 2 +- .../SentryOtelThreadLocalStorage.java | 2 + .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- sentry-quartz/build.gradle.kts | 4 +- sentry-reactor/build.gradle.kts | 8 +- .../io/sentry/reactor/SentryReactorUtils.java | 4 +- .../sentry-samples-android/build.gradle.kts | 8 -- .../build.gradle.kts | 14 --- .../build.gradle.kts | 14 --- .../sentry-samples-console/build.gradle.kts | 14 --- .../sentry-samples-jul/build.gradle.kts | 10 -- .../sentry-samples-log4j2/build.gradle.kts | 10 -- .../sentry-samples-logback/build.gradle.kts | 10 -- .../sentry-samples-spring-7/build.gradle.kts | 5 - .../build.gradle.kts | 10 -- .../build.gradle.kts | 10 -- .../build.gradle.kts | 10 -- .../build.gradle.kts | 10 -- .../build.gradle.kts | 5 - .../build.gradle.kts | 10 -- .../build.gradle.kts | 13 -- .../build.gradle.kts | 12 -- .../build.gradle.kts | 13 -- .../build.gradle.kts | 13 -- .../build.gradle.kts | 23 ++-- .../build.gradle.kts | 22 ++-- .../build.gradle.kts | 6 - .../build.gradle.kts | 16 ++- .../build.gradle.kts | 21 ++-- .../build.gradle.kts | 6 - .../sentry-samples-spring/build.gradle.kts | 17 +-- sentry-servlet-jakarta/build.gradle.kts | 2 +- sentry-servlet/build.gradle.kts | 2 +- sentry-spotlight/build.gradle.kts | 4 +- sentry-spring-7/build.gradle.kts | 12 +- .../HttpServletRequestSentryUserProvider.java | 5 +- .../spring7/SentryInitBeanPostProcessor.java | 3 +- .../cache/SentryCacheBeanPostProcessor.java | 4 +- .../spring7/checkin/SentryCheckInAdvice.java | 6 +- ...SentryCaptureExceptionParameterAdvice.java | 11 +- .../graphql/SentryBatchLoaderRegistry.java | 4 +- .../SentryGraphqlBeanPostProcessor.java | 4 +- .../SentryKafkaProducerBeanPostProcessor.java | 4 +- .../kafka/SentryKafkaRecordInterceptor.java | 4 +- .../spring7/tracing/SentrySpanAdvice.java | 6 +- ...entrySpanClientHttpRequestInterceptor.java | 2 +- .../tracing/SentryTransactionAdvice.java | 8 +- .../webflux/SentryWebExceptionHandler.java | 2 +- sentry-spring-boot-4-starter/build.gradle.kts | 8 +- sentry-spring-boot-4/build.gradle.kts | 8 +- .../spring/boot4/SentryLog4j2Initializer.java | 3 +- .../SentryOkHttpClientBeanPostProcessor.java | 3 +- sentry-spring-boot-jakarta/build.gradle.kts | 8 +- .../boot/jakarta/SentryLog4j2Initializer.java | 3 +- .../SentryOkHttpClientBeanPostProcessor.java | 3 +- .../build.gradle.kts | 8 +- sentry-spring-boot-starter/build.gradle.kts | 2 +- sentry-spring-boot/build.gradle.kts | 2 +- sentry-spring-jakarta/build.gradle.kts | 21 ++-- .../HttpServletRequestSentryUserProvider.java | 5 +- .../jakarta/SentryInitBeanPostProcessor.java | 3 +- .../cache/SentryCacheBeanPostProcessor.java | 4 +- ...SentryCaptureExceptionParameterAdvice.java | 4 +- .../graphql/SentryBatchLoaderRegistry.java | 4 +- .../SentryGraphqlBeanPostProcessor.java | 4 +- .../SentryKafkaProducerBeanPostProcessor.java | 4 +- .../kafka/SentryKafkaRecordInterceptor.java | 2 +- ...entrySpanClientHttpRequestInterceptor.java | 2 +- .../tracing/SentryTransactionAdvice.java | 2 +- sentry-spring/build.gradle.kts | 4 +- .../kafka/SentryKafkaRecordInterceptor.java | 2 +- .../tracing/SentryTransactionAdvice.java | 2 +- sentry-system-test-support/build.gradle.kts | 6 - sentry-test-support/build.gradle.kts | 6 - sentry/build.gradle.kts | 7 +- sentry/src/main/java/io/sentry/Baggage.java | 8 ++ .../src/main/java/io/sentry/Breadcrumb.java | 4 + .../java/io/sentry/CombinedContextsView.java | 2 +- sentry/src/main/java/io/sentry/DateUtils.java | 4 +- .../java/io/sentry/DefaultSpanFactory.java | 2 +- .../main/java/io/sentry/DiagnosticLogger.java | 2 + .../java/io/sentry/DirectoryProcessor.java | 2 +- .../main/java/io/sentry/ExternalOptions.java | 3 +- sentry/src/main/java/io/sentry/Hint.java | 2 +- .../main/java/io/sentry/MonitorContexts.java | 4 + .../main/java/io/sentry/ReplayRecording.java | 3 +- .../main/java/io/sentry/SamplingContext.java | 2 +- sentry/src/main/java/io/sentry/Scope.java | 5 +- .../java/io/sentry/SentryEnvelopeItem.java | 5 +- .../io/sentry/SentryExceptionFactory.java | 3 +- .../main/java/io/sentry/SentryOptions.java | 3 + .../java/io/sentry/SentryThreadFactory.java | 2 + .../src/main/java/io/sentry/SentryTracer.java | 2 +- .../src/main/java/io/sentry/SpanContext.java | 3 + .../io/sentry/SynchronizedCollection.java | 5 + .../src/main/java/io/sentry/TraceContext.java | 2 +- .../UncaughtExceptionHandlerIntegration.java | 2 + .../sentry/cache/PersistingScopeObserver.java | 3 + .../ExceptionMechanismException.java | 7 ++ .../featureflags/FeatureFlagBuffer.java | 5 +- .../modules/ManifestModulesLoader.java | 2 +- .../main/java/io/sentry/logger/LoggerApi.java | 7 +- .../java/io/sentry/metrics/MetricsApi.java | 7 +- .../java/io/sentry/protocol/Contexts.java | 7 ++ .../java/io/sentry/rrweb/RRWebEventType.java | 4 + .../rrweb/RRWebIncrementalSnapshotEvent.java | 4 + .../sentry/rrweb/RRWebInteractionEvent.java | 4 + .../main/java/io/sentry/time/Deadline.java | 2 + .../main/java/io/sentry/util/HttpUtils.java | 2 + .../main/java/io/sentry/util/InitUtil.java | 2 + .../sentry/util/JsonSerializationUtils.java | 2 +- .../src/main/java/io/sentry/util/Objects.java | 2 + .../main/java/io/sentry/util/Platform.java | 3 +- .../java/io/sentry/util/UUIDGenerator.java | 2 +- .../util/network/NetworkBodyParser.java | 7 +- .../sentry/vendor/gson/stream/JsonReader.java | 2 + .../test/java/io/sentry/OutboxSenderTest.kt | 5 +- .../io/sentry/PreviousSessionFinalizerTest.kt | 3 +- 188 files changed, 514 insertions(+), 540 deletions(-) create mode 100644 build-logic/src/main/kotlin/io/sentry/gradle/JdkTargetConvention.kt diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index 973cfcf6282..6b0ecf7d1fc 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -36,7 +36,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 f11c6475b79..3b185217b4c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ concurrency: jobs: build: # Job name is a required status check in the main branch ruleset; keep it stable. - name: Build Job ubuntu-latest - Java 17 + name: Build Job ubuntu-latest runs-on: ubuntu-latest env: @@ -29,7 +29,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 662c20a0885..00fe6dc8a7e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 6b94bc1b62c..0ed7cbb4efc 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -17,7 +17,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 7b356dbada4..0fe0c84ccf6 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -16,7 +16,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 7a74d941f67..262b0fb3f47 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -17,7 +17,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 ad7153f7509..b85ed387133 100644 --- a/.github/workflows/integration-tests-macrobenchmark.yml +++ b/.github/workflows/integration-tests-macrobenchmark.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 e9d3d6e80c9..fb61149ac58 100644 --- a/.github/workflows/integration-tests-size.yml +++ b/.github/workflows/integration-tests-size.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 ab379c32f3f..72d660c5053 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@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 5871d37bebb..2bd88afd5ad 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -30,7 +30,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 61510987035..9b04020e6c1 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -23,7 +23,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 d798f0e4377..8a723dd994d 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@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 fdc9169cccc..33e06c49b50 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@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 08dfd5e389d..a960e00082e 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@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 aa25e9854c6..2165a6c6a67 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -121,7 +121,7 @@ jobs: uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0 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 684ef3c7353..8c49840c0b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,9 @@ You are welcome to use whatever tools you prefer for making a contribution. Howe # Requirements -The project requires you to run JDK 17. +The project requires you to run JDK 21 or newer. Compilation and tests always happen on JDK 21 +through a [toolchain](https://docs.gradle.org/current/userguide/toolchains.html), so a newer JDK +runs the build but never changes its output. ## Android diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index fc90a792782..d02ab584885 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -5,10 +5,16 @@ plugins { dependencies { implementation(libs.animalsniffer.gradle.plugin) implementation(libs.spotlessLib) + compileOnly(libs.kotlin.gradle.plugin) + compileOnly(libs.android.gradle.plugin) } gradlePlugin { plugins { + register("sentryJdkTarget") { + id = "io.sentry.jdk-target" + implementationClass = "io.sentry.gradle.JdkTargetConvention" + } register("sentryAnimalSniffer") { id = "io.sentry.animalsniffer" implementationClass = "io.sentry.gradle.SentryAnimalSnifferPlugin" diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 58fadd9f178..76cf7346e5b 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -10,6 +10,8 @@ dependencyResolutionManagement { repositories { mavenCentral() gradlePluginPortal() + // AGP is only published to Google's repository. + google() } versionCatalogs { create("libs") { diff --git a/build-logic/src/main/kotlin/io/sentry/gradle/JdkTargetConvention.kt b/build-logic/src/main/kotlin/io/sentry/gradle/JdkTargetConvention.kt new file mode 100644 index 00000000000..c92c83bbac2 --- /dev/null +++ b/build-logic/src/main/kotlin/io/sentry/gradle/JdkTargetConvention.kt @@ -0,0 +1,116 @@ +package io.sentry.gradle + +import com.android.build.gradle.BaseExtension +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile + +/** The JDK every module compiles and tests with, whatever JDK runs Gradle. */ +private val BUILD_JDK = JavaLanguageVersion.of(21) + +/** The level the SDK publishes, so that Java 8 consumers can still use it. */ +private val PUBLISHED_JAVA_VERSION = JavaVersion.VERSION_1_8 + +/** + * Holds every toolchain, bytecode level decision and compilation option of a module. It does the + * following + * - sample apps target the build JDK (21) + * - unit test code will target the build JDK (21) (required for Mockito 5) + * - published JVM modules main code pins the Java 1.8 release, for Java and for Kotlin + * - published Android modules main code targets Java 1.8 + * - minSdk is set for Android libraries + */ +class JdkTargetConvention : Plugin { + override fun apply(project: Project) { + project.plugins.withType(JavaBasePlugin::class.java) { + project.extensions.getByType(JavaPluginExtension::class.java).toolchain { + languageVersion.set(BUILD_JDK) + } + } + + project.plugins.withId("com.vanniktech.maven.publish") { + // Only works for non-android modules + // See https://issuetracker.google.com/issues/278800528 + project.plugins.withId("java-library") { + project.tasks.named("compileJava", JavaCompile::class.java) { + options.release.set(PUBLISHED_JAVA_VERSION.majorVersion.toInt()) + } + } + // -Xjdk-release is the Kotlin counterpart of release: without it Kotlin resolves against the + // build JDK and happily compiles e.g. Files.readString() into Java 8 bytecode. The version + // follows jvmTarget, which the modules that publish a higher level set themselves. + project.plugins.withId("org.jetbrains.kotlin.jvm") { + project.tasks.named("compileKotlin", KotlinJvmCompile::class.java) { + compilerOptions.freeCompilerArgs.add( + compilerOptions.jvmTarget.map { "-Xjdk-release=${it.target}" } + ) + } + } + } + + project.plugins.withId("com.android.library") { + val android = project.extensions.getByType(BaseExtension::class.java) + android.compileOptions { + sourceCompatibility = PUBLISHED_JAVA_VERSION + targetCompatibility = PUBLISHED_JAVA_VERSION + } + android.defaultConfig { aarMetadata { minCompileSdk = project.minSdk() } } + } + + listOf("com.android.library", "com.android.application").forEach { pluginId -> + project.plugins.withId(pluginId) { project.targetBuildJdkForUnitTests() } + } + } + + private fun Project.minSdk(): Int = + extensions + .getByType(VersionCatalogsExtension::class.java) + .named("libs") + .findVersion("minSdk") + .orElseThrow { IllegalStateException("libs.versions.minSdk is missing") } + .requiredVersion + .toInt() + + /** + * Mockito 5 and mockito-kotlin 6 are built for Java 11, so test code - which is never published - + * cannot share the Java 8 target of the main code. AGP has no per variant compileOptions, so this + * goes through the Kotlin compilations: their compile task providers are configured after AGP + * wrote its own compileOptions, which an apply time configureEach would lose against. + */ + private fun Project.targetBuildJdkForUnitTests() { + plugins.withId("org.jetbrains.kotlin.android") { + extensions.getByType(KotlinAndroidProjectExtension::class.java).target.targetBuildJdk() + } + plugins.withId("org.jetbrains.kotlin.multiplatform") { + extensions + .getByType(KotlinMultiplatformExtension::class.java) + .targets + .withType(KotlinAndroidTarget::class.java) + .configureEach { targetBuildJdk() } + } + } + + private fun KotlinAndroidTarget.targetBuildJdk() { + compilations.configureEach { + if (name.endsWith("UnitTest")) { + compileTaskProvider.configure { + compilerOptions.jvmTarget.set(JvmTarget.fromTarget(BUILD_JDK.toString())) + } + compileJavaTaskProvider?.configure { + sourceCompatibility = BUILD_JDK.toString() + targetCompatibility = BUILD_JDK.toString() + } + } + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 52e17b1d309..898b429cb5d 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() @@ -111,6 +115,7 @@ allprojects { subprojects { apply { plugin("io.sentry.spotless") } + apply { plugin("io.sentry.jdk-target") } plugins.withId(Config.QualityPlugins.detektPlugin) { configure { @@ -162,28 +167,6 @@ subprojects { configure { withJavadocJar() - - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - } - - // AGP 9 defaults Android modules to Java 11. Pin the published library modules back - // to Java 8 so their bytecode stays consumable by Java 8 projects, mirroring the - // java-library pin above. - plugins.withId("com.android.library") { - configure { - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - - // AGP 9 defaults the AAR metadata minCompileSdk to the library's compileSdk, - // which would force every consumer onto that compile SDK. Pin it to our minSdk - // so consumers remain free to compile against any SDK we support, as before. - defaultConfig { - aarMetadata { minCompileSdk = libs.versions.minSdk.get().toInt() } - } } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 4459ee342fe..bc0172f0f07 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,9 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { `kotlin-dsl` } - -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 43c3a7660c4..e5c2f9e2171 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,6 +38,9 @@ retrofit = "2.9.0" room2 = "2.8.4" room3 = "3.0.0-rc01" sagp = "6.13.0" +# Compile-time only, for the AGP types the build-logic convention plugins use. The AGP that +# actually runs the build comes from the root buildscript (Config.AGP, overridable via VERSION_AGP). +agp = "9.4.0" sqlite = "2.6.2" sqliteRc = "2.7.0-rc01" # Required by Room3 3.0.0-rc* slf4j = "1.7.30" @@ -64,7 +67,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 +132,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" } @@ -152,9 +155,11 @@ log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "lo leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version = "2.14" } lottie-compose = { module = "com.airbnb.android:lottie-compose", version = "6.7.1" } logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } 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,9 +278,8 @@ 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 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..120f2e0beff 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; + // temp fix for JDK 21: 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..589a9fa2ce6 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,9 @@ /** * @deprecated `SentryUserFeedbackButton` will be removed in the next major version */ +// temp fix for JDK 21: 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..dacab11e4f4 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,9 @@ *

This class is also responsible for - determining the app start type (cold, warm) - determining * if the app was launched in foreground */ +// temp fix for JDK 21: 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/AnrV2IntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt index cd21f92fd28..e41d5778a7f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt @@ -121,7 +121,7 @@ class AnrV2IntegrationTest : ApplicationExitIntegrationTestBase() { } shadowActivityManager.addApplicationExitInfo(exitInfo) }, - flushLogPrefix = "Timed out waiting to flush ANR event to disk.", + flushLogPrefix = "Timed out waiting to flush %s event to disk.", ) override fun assertEnrichedEvent(event: SentryEvent) { 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..7db354ea64c 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 @@ -171,7 +172,7 @@ abstract class ApplicationExitIntegrationTestBase { verify(fixture.scopes).captureEvent(any(), anyOrNull()) verify(fixture.logger, never()) - .log(any(), argThat { startsWith(config.flushLogPrefix) }, any()) + .log(any(), argThat { startsWith(config.flushLogPrefix) }, anyVararg()) } @Test @@ -184,7 +185,7 @@ abstract class ApplicationExitIntegrationTestBase { verify(fixture.scopes).captureEvent(any(), anyOrNull()) verify(fixture.logger, never()) - .log(any(), argThat { startsWith(config.flushLogPrefix) }, any()) + .log(any(), argThat { startsWith(config.flushLogPrefix) }, anyVararg()) } @Test @@ -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/java/io/sentry/android/core/TombstoneIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/TombstoneIntegrationTest.kt index bc05f3b23fd..39a63148a17 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/TombstoneIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/TombstoneIntegrationTest.kt @@ -65,7 +65,7 @@ class TombstoneIntegrationTest : ApplicationExitIntegrationTestBase().configureEach { diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts index 9ca0c1f6d9d..e606ab52553 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/build.gradle.kts @@ -1,5 +1,4 @@ import io.gitlab.arturbosch.detekt.Detekt -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("com.android.application") @@ -33,7 +32,6 @@ android { proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } - kotlin { compilerOptions.jvmTarget = JvmTarget.JVM_11 } buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() } androidComponents.beforeVariants { diff --git a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts index 18a5b6cb7f5..cab7c28f934 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 - plugins { id("com.android.test") alias(libs.plugins.kotlin.android) @@ -28,13 +26,6 @@ android { } } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - kotlin { compilerOptions.jvmTarget = JVM_11 } - targetProjectPath = ":sentry-samples:sentry-samples-android" // Run the test in its own process so it measures the target app cold, not itself. experimentalProperties["android.experimental.self-instrumenting"] = true diff --git a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts index 85ff2b1a3c7..8f479470cac 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts @@ -1,6 +1,5 @@ import io.gitlab.arturbosch.detekt.Detekt import net.ltgt.gradle.errorprone.errorprone -import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("com.android.application") @@ -65,8 +64,6 @@ android { } } - kotlin { compilerOptions.jvmTarget = JvmTarget.JVM_11 } - lint { warningsAsErrors = true checkDependencies = true 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-apache-http-client-5/build.gradle.kts b/sentry-apache-http-client-5/build.gradle.kts index 7502fb6c4b8..06c5f839f95 100644 --- a/sentry-apache-http-client-5/build.gradle.kts +++ b/sentry-apache-http-client-5/build.gradle.kts @@ -1,6 +1,7 @@ import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-library` @@ -12,11 +13,12 @@ plugins { } kotlin { - compilerOptions.jvmTarget = JvmTarget.JVM_1_8 compilerOptions.languageVersion = KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = KotlinVersion.KOTLIN_1_9 } +tasks.named("compileKotlin") { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } + dependencies { api(projects.sentry) api(libs.apache.httpclient) diff --git a/sentry-apollo-3/build.gradle.kts b/sentry-apollo-3/build.gradle.kts index 6711b165dd9..ae8721b22b2 100644 --- a/sentry-apollo-3/build.gradle.kts +++ b/sentry-apollo-3/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -37,7 +37,7 @@ dependencies { testImplementation(libs.google.truth) 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 9fb024a01db..63c8b5431c6 100644 --- a/sentry-apollo-4/build.gradle.kts +++ b/sentry-apollo-4/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -38,7 +38,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..2d78dff8fa1 100644 --- a/sentry-apollo-5/build.gradle.kts +++ b/sentry-apollo-5/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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 b3cb3cff953..72a183bb7b3 100644 --- a/sentry-apollo/build.gradle.kts +++ b/sentry-apollo/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -38,7 +38,7 @@ dependencies { testImplementation(libs.google.truth) testImplementation(libs.kotlinx.coroutines) testImplementation(libs.mockito.kotlin) - testImplementation(libs.mockito.inline) + testImplementation(libs.mockito.core) testImplementation(libs.okhttp.mockwebserver) } @@ -46,6 +46,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..3bcdefd1d82 100644 --- a/sentry-async-profiler/build.gradle.kts +++ b/sentry-async-profiler/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } @@ -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 def6708f672..9da6bcc1e36 100644 --- a/sentry-graphql-22/build.gradle.kts +++ b/sentry-graphql-22/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -36,7 +36,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.google.truth) 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..df0496f070e 100644 --- a/sentry-graphql-core/build.gradle.kts +++ b/sentry-graphql-core/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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 f996e0310a1..25613e1fdd6 100644 --- a/sentry-graphql/build.gradle.kts +++ b/sentry-graphql/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -36,7 +36,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.google.truth) 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..b773c42c340 100644 --- a/sentry-jcache/build.gradle.kts +++ b/sentry-jcache/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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..9d0aa1596fe 100644 --- a/sentry-jdbc/build.gradle.kts +++ b/sentry-jdbc/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } @@ -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/build.gradle.kts b/sentry-jul/build.gradle.kts index 2eec61eb171..130b46ad5ed 100644 --- a/sentry-jul/build.gradle.kts +++ b/sentry-jul/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } 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..7d929ec7729 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,9 @@ import org.slf4j.MDC; /** Logging handler in charge of sending the java.util.logging records to a Sentry server. */ +// temp fix for JDK 21: 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..37240c8305e 100644 --- a/sentry-kafka/build.gradle.kts +++ b/sentry-kafka/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } @@ -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..312faabbb42 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") + // temp fix for JDK 21: 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-kotlin-extensions/build.gradle.kts b/sentry-kotlin-extensions/build.gradle.kts index 101761b2a82..ded17d445e7 100644 --- a/sentry-kotlin-extensions/build.gradle.kts +++ b/sentry-kotlin-extensions/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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-ktor-client/build.gradle.kts b/sentry-ktor-client/build.gradle.kts index cbee84179fb..28edd1689e0 100644 --- a/sentry-ktor-client/build.gradle.kts +++ b/sentry-ktor-client/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -37,7 +37,7 @@ dependencies { testImplementation(libs.kotlin.test.junit) testImplementation(libs.google.truth) 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..f2ed3d15742 100644 --- a/sentry-launchdarkly-server/build.gradle.kts +++ b/sentry-launchdarkly-server/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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-log4j2/build.gradle.kts b/sentry-log4j2/build.gradle.kts index 6e5250ece50..2d2444a2395 100644 --- a/sentry-log4j2/build.gradle.kts +++ b/sentry-log4j2/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } diff --git a/sentry-logback/build.gradle.kts b/sentry-logback/build.gradle.kts index de2c9dbb807..48f84725a78 100644 --- a/sentry-logback/build.gradle.kts +++ b/sentry-logback/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } 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..0b3e2388415 100644 --- a/sentry-okhttp/build.gradle.kts +++ b/sentry-okhttp/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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..6750e22e61c 100644 --- a/sentry-openfeature/build.gradle.kts +++ b/sentry-openfeature/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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-openfeign/build.gradle.kts b/sentry-openfeign/build.gradle.kts index 8b7adc97544..209e5c34e54 100644 --- a/sentry-openfeign/build.gradle.kts +++ b/sentry-openfeign/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index 087568d03ee..3390f2695c8 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.gradle.versions) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts index 3585aa40d4a..28a1b030e15 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.gradle.versions) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } 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..7ac6bee4a82 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<>(); + // temp fix for JDK 21: Same identity check as OpenTelemetry's ThreadLocalContextStorage. + @SuppressWarnings("ReferenceEquality") @Override public Scope attach(Context toAttach) { if (toAttach == null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts index a252628c1a2..1c1e7f984ba 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.gradle.versions) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } diff --git a/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts index ec240c681ae..afab9ef180f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-otlp/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.gradle.versions) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } diff --git a/sentry-quartz/build.gradle.kts b/sentry-quartz/build.gradle.kts index f4f0d9d07d2..b5387daf207 100644 --- a/sentry-quartz/build.gradle.kts +++ b/sentry-quartz/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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/build.gradle.kts b/sentry-reactor/build.gradle.kts index fe9023586ef..b98eafd028f 100644 --- a/sentry-reactor/build.gradle.kts +++ b/sentry-reactor/build.gradle.kts @@ -10,12 +10,10 @@ plugins { alias(libs.plugins.buildconfig) } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-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-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index 4fee4a1b92a..cf4b054e354 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -129,14 +129,6 @@ android { } } - // Java 11 b/c androidx.room3 requires it. - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 } - androidComponents.onVariants { variant -> variant.buildConfigFields?.put( "USE_SAGP", diff --git a/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts index a71f0bb9040..57d1dea0e8d 100644 --- a/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-console-opentelemetry-noagent/build.gradle.kts @@ -11,23 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.console.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-console-otlp/build.gradle.kts b/sentry-samples/sentry-samples-console-otlp/build.gradle.kts index 491583175ae..58a8578c7d1 100644 --- a/sentry-samples/sentry-samples-console-otlp/build.gradle.kts +++ b/sentry-samples/sentry-samples-console-otlp/build.gradle.kts @@ -11,23 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.console.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-console/build.gradle.kts b/sentry-samples/sentry-samples-console/build.gradle.kts index d714a0ed541..f0b0d862896 100644 --- a/sentry-samples/sentry-samples-console/build.gradle.kts +++ b/sentry-samples/sentry-samples-console/build.gradle.kts @@ -11,23 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.console.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-jul/build.gradle.kts b/sentry-samples/sentry-samples-jul/build.gradle.kts index 3edf8e427a7..6fa4bbb8c8b 100644 --- a/sentry-samples/sentry-samples-jul/build.gradle.kts +++ b/sentry-samples/sentry-samples-jul/build.gradle.kts @@ -11,19 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.jul.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-log4j2/build.gradle.kts b/sentry-samples/sentry-samples-log4j2/build.gradle.kts index 809167d040c..901604d2cff 100644 --- a/sentry-samples/sentry-samples-log4j2/build.gradle.kts +++ b/sentry-samples/sentry-samples-log4j2/build.gradle.kts @@ -11,19 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.log4j2.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-logback/build.gradle.kts b/sentry-samples/sentry-samples-logback/build.gradle.kts index 8bbc70a8fbb..6d6e7051030 100644 --- a/sentry-samples/sentry-samples-logback/build.gradle.kts +++ b/sentry-samples/sentry-samples-logback/build.gradle.kts @@ -11,19 +11,9 @@ plugins { application { mainClass.set("io.sentry.samples.logback.Main") } -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-7/build.gradle.kts b/sentry-samples/sentry-samples-spring-7/build.gradle.kts index 8770dd8d380..82a18d86c73 100644 --- a/sentry-samples/sentry-samples-spring-7/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-7/build.gradle.kts @@ -22,10 +22,6 @@ group = "io.sentry.sample.spring-7" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom(SpringBootPlugin.BOM_COORDINATES) @@ -65,7 +61,6 @@ tasks.withType().configureEach { // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts index 2ca03bfc284..06cbdbe9400 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-log4j2/build.gradle.kts @@ -13,22 +13,12 @@ group = "io.sentry.sample.spring-boot-4-log4j2" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { explicitApi() // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts index a2fefae6bac..bfd59790ce2 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry-noagent/build.gradle.kts @@ -13,22 +13,12 @@ group = "io.sentry.sample.spring-boot-4" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { explicitApi() // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts index 112465bb5fd..15bb3fca8a8 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-opentelemetry/build.gradle.kts @@ -14,22 +14,12 @@ group = "io.sentry.sample.spring-boot-4" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { explicitApi() // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts index d995d39de2a..c5bc2b96fb3 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-otlp/build.gradle.kts @@ -13,22 +13,12 @@ group = "io.sentry.sample.spring-boot-4-otlp" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { explicitApi() // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts index e5b44c1793a..c0c021e8320 100644 --- a/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4-webflux/build.gradle.kts @@ -13,10 +13,6 @@ group = "io.sentry.sample.spring-boot-4-webflux" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencies { implementation(Config.Libs.kotlinReflect) implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) @@ -53,7 +49,6 @@ tasks.withType().configureEach { // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-4/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts index 3ec1817ce66..00b85d45bd5 100644 --- a/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts @@ -13,22 +13,12 @@ group = "io.sentry.sample.spring-boot-4" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { kotlin { explicitApi() // skip metadata version check, as Spring 7 / Spring Boot 4 is // compiled against a newer version of Kotlin compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-Xskip-metadata-version-check") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts index 9ac03b5ebf8..3535e1232e2 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-log4j2/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -14,10 +13,6 @@ group = "io.sentry.sample.spring-boot-jakarta-log4j2" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}") @@ -27,17 +22,9 @@ dependencyManagement { // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { compilerOptions.jvmTarget = JvmTarget.JVM_17 } - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts index 6d1549842ce..da761af1f62 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry-noagent/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -14,10 +13,6 @@ group = "io.sentry.sample.spring-boot-jakarta" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}") @@ -28,16 +23,9 @@ dependencyManagement { // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - tasks.withType().configureEach { - compilerOptions.jvmTarget = JvmTarget.JVM_17 kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts index bfe39c0dfab..9b59911bd34 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta-opentelemetry/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.tasks.run.BootRun @@ -15,10 +14,6 @@ group = "io.sentry.sample.spring-boot-jakarta" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}") @@ -28,17 +23,9 @@ dependencyManagement { // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { compilerOptions.jvmTarget = JvmTarget.JVM_17 } - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts index 722ccc69d3e..cdc5753a9c5 100644 --- a/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -14,10 +13,6 @@ group = "io.sentry.sample.spring-boot-jakarta" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}") @@ -27,17 +22,9 @@ dependencyManagement { // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -tasks.withType().configureEach { compilerOptions.jvmTarget = JvmTarget.JVM_17 } - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts index 466c4fa7b58..5ffc83b7f65 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,16 +10,22 @@ plugins { id("io.sentry.systemtest") } +// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version +// the Spring Boot 2.x matrix covers (2.4.13, Spring 5.3.13) fails on class files as new as the +// toolchain's: "Unsupported class file major version 65". Only the app is scanned, so its test +// code keeps following the toolchain. +tasks.named("compileJava") { options.release.set(11) } + +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 +} + application { mainClass.set("io.sentry.samples.spring.boot.SentryDemoApplication") } group = "io.sentry.sample.spring-boot" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_11 - -java.targetCompatibility = JavaVersion.VERSION_11 - fun springBoot2SupportsOptionalIntegrations(): Boolean { val version = libs.versions.springboot2.get().removeSuffix(".RELEASE") val parts = version.split(".").map { it.toIntOrNull() ?: 0 } @@ -33,17 +38,9 @@ val includeGraphql = !project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations() val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations() -configure { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - -tasks.withType().configureEach { compilerOptions.jvmTarget = JvmTarget.JVM_11 } - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_11 } } diff --git a/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts index f27ee6b2346..3b7f56523df 100644 --- a/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,16 +10,22 @@ plugins { id("io.sentry.systemtest") } +// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version +// the Spring Boot 2.x matrix covers (2.4.13, Spring 5.3.13) fails on class files as new as the +// toolchain's: "Unsupported class file major version 65". Only the app is scanned, so its test +// code keeps following the toolchain. +tasks.named("compileJava") { options.release.set(11) } + +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 +} + application { mainClass.set("io.sentry.samples.spring.boot.SentryDemoApplication") } group = "io.sentry.sample.spring-boot" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_11 - -java.targetCompatibility = JavaVersion.VERSION_11 - fun springBoot2SupportsOptionalIntegrations(): Boolean { val version = libs.versions.springboot2.get().removeSuffix(".RELEASE") val parts = version.split(".").map { it.toIntOrNull() ?: 0 } @@ -33,16 +38,9 @@ val includeGraphql = !project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations() val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations() -configure { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - tasks.withType().configureEach { - compilerOptions.jvmTarget = JvmTarget.JVM_11 kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_11 } } diff --git a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts index 08f74e90baa..342aaa0d70c 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-webflux-jakarta/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -14,10 +13,6 @@ group = "io.sentry.sample.spring-boot-webflux-jakarta" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - dependencyManagement { imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}") @@ -59,7 +54,6 @@ dependencies { tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts index 1ef3a196e9c..fe1859c0df7 100644 --- a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,16 +10,22 @@ plugins { id("io.sentry.systemtest") } +// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version +// the Spring Boot 2.x matrix covers (2.4.13, Spring 5.3.13) fails on class files as new as the +// toolchain's: "Unsupported class file major version 65". Only the app is scanned, so its test +// code keeps following the toolchain. +tasks.named("compileJava") { options.release.set(11) } + +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 +} + application { mainClass.set("io.sentry.samples.spring.boot.SentryDemoApplication") } group = "io.sentry.sample.spring-boot" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_11 - -java.targetCompatibility = JavaVersion.VERSION_11 - fun springBoot2SupportsGraphql(): Boolean { val version = libs.versions.springboot2.get().removeSuffix(".RELEASE") val parts = version.split(".").map { it.toIntOrNull() ?: 0 } @@ -94,7 +99,6 @@ configure { tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_11 } } diff --git a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts index 86ed855e2d3..316e98ebdb6 100644 --- a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts @@ -1,5 +1,4 @@ import org.jetbrains.kotlin.config.KotlinCompilerVersion -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,16 +10,22 @@ plugins { id("io.sentry.systemtest") } +// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version +// the Spring Boot 2.x matrix covers (2.4.13, Spring 5.3.13) fails on class files as new as the +// toolchain's: "Unsupported class file major version 65". Only the app is scanned, so its test +// code keeps following the toolchain. +tasks.named("compileJava") { options.release.set(11) } + +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 +} + application { mainClass.set("io.sentry.samples.spring.boot.SentryDemoApplication") } group = "io.sentry.sample.spring-boot" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_11 - -java.targetCompatibility = JavaVersion.VERSION_11 - fun springBoot2SupportsOptionalIntegrations(): Boolean { val version = libs.versions.springboot2.get().removeSuffix(".RELEASE") val parts = version.split(".").map { it.toIntOrNull() ?: 0 } @@ -33,15 +38,9 @@ val includeGraphql = !project.hasProperty("excludeGraphql") && springBoot2SupportsOptionalIntegrations() val includeKafka = !project.hasProperty("excludeKafka") && springBoot2SupportsOptionalIntegrations() -configure { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 -} - tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_11 } } diff --git a/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts b/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts index 2d28ed2531f..022b9fbb0c6 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -20,10 +19,6 @@ group = "io.sentry.sample.spring-jakarta" version = "0.0.1-SNAPSHOT" -java.sourceCompatibility = JavaVersion.VERSION_17 - -java.targetCompatibility = JavaVersion.VERSION_17 - // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" @@ -64,7 +59,6 @@ dependencies { tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_17 } } diff --git a/sentry-samples/sentry-samples-spring/build.gradle.kts b/sentry-samples/sentry-samples-spring/build.gradle.kts index 7c6b6a915fc..8ae94a2e367 100644 --- a/sentry-samples/sentry-samples-spring/build.gradle.kts +++ b/sentry-samples/sentry-samples-spring/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,6 +10,16 @@ plugins { id("io.sentry.systemtest") } +// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version +// the Spring Boot 2.x matrix covers (2.4.13, Spring 5.3.13) fails on class files as new as the +// toolchain's: "Unsupported class file major version 65". Only the app is scanned, so its test +// code keeps following the toolchain. +tasks.named("compileJava") { options.release.set(8) } + +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 +} + application { mainClass.set("io.sentry.samples.spring.Main") } // Ensure WAR is up to date before run task @@ -20,11 +29,6 @@ group = "io.sentry.sample.spring" version = "0.0.1-SNAPSHOT" -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - // Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version extra["kotlin-coroutines.version"] = "1.9.0" @@ -65,7 +69,6 @@ dependencies { tasks.withType().configureEach { kotlin { compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } } diff --git a/sentry-servlet-jakarta/build.gradle.kts b/sentry-servlet-jakarta/build.gradle.kts index 5762a4ae52b..b9d6ffc2ec4 100644 --- a/sentry-servlet-jakarta/build.gradle.kts +++ b/sentry-servlet-jakarta/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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-servlet/build.gradle.kts b/sentry-servlet/build.gradle.kts index c2e3fe8e52e..08ac7d45284 100644 --- a/sentry-servlet/build.gradle.kts +++ b/sentry-servlet/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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-spotlight/build.gradle.kts b/sentry-spotlight/build.gradle.kts index a8d538c2e2e..072cb360e61 100644 --- a/sentry-spotlight/build.gradle.kts +++ b/sentry-spotlight/build.gradle.kts @@ -11,7 +11,7 @@ plugins { alias(libs.plugins.buildconfig) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } @@ -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..c848c9416a0 100644 --- a/sentry-spring-7/build.gradle.kts +++ b/sentry-spring-7/build.gradle.kts @@ -12,12 +12,10 @@ plugins { alias(libs.plugins.springboot4) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1) @@ -67,8 +65,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..dca7c7d61dd 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,9 @@ private boolean isIgnored() { if (enqueuedTimeStr != null) { try { final double enqueuedTimeSeconds = Double.parseDouble(enqueuedTimeStr); - final double nowSeconds = DateUtils.millisToSeconds(System.currentTimeMillis()); + // millisToSeconds takes a double; the cast is explicit for Error Prone's + // LongDoubleConversion check. + 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-starter/build.gradle.kts b/sentry-spring-boot-4-starter/build.gradle.kts index bffe53aab01..7ad300d4790 100644 --- a/sentry-spring-boot-4-starter/build.gradle.kts +++ b/sentry-spring-boot-4-starter/build.gradle.kts @@ -11,12 +11,10 @@ plugins { alias(libs.plugins.springboot4) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-spring-boot-4/build.gradle.kts b/sentry-spring-boot-4/build.gradle.kts index d1128ce8a03..ce3a42989e8 100644 --- a/sentry-spring-boot-4/build.gradle.kts +++ b/sentry-spring-boot-4/build.gradle.kts @@ -13,12 +13,10 @@ plugins { alias(libs.plugins.springboot4) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9) 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/build.gradle.kts b/sentry-spring-boot-jakarta/build.gradle.kts index 5f2bbe87b34..8c1a07aa9cf 100644 --- a/sentry-spring-boot-jakarta/build.gradle.kts +++ b/sentry-spring-boot-jakarta/build.gradle.kts @@ -12,12 +12,10 @@ plugins { alias(libs.plugins.springboot3) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-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-boot-starter-jakarta/build.gradle.kts b/sentry-spring-boot-starter-jakarta/build.gradle.kts index c6fe511073e..63594df7a2e 100644 --- a/sentry-spring-boot-starter-jakarta/build.gradle.kts +++ b/sentry-spring-boot-starter-jakarta/build.gradle.kts @@ -11,12 +11,10 @@ plugins { alias(libs.plugins.springboot3) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 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-spring-boot-starter/build.gradle.kts b/sentry-spring-boot-starter/build.gradle.kts index cd05baecc4d..641b335becb 100644 --- a/sentry-spring-boot-starter/build.gradle.kts +++ b/sentry-spring-boot-starter/build.gradle.kts @@ -10,7 +10,7 @@ plugins { alias(libs.plugins.gradle.versions) } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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-spring-boot/build.gradle.kts b/sentry-spring-boot/build.gradle.kts index 27cb2d7805c..c5537accef1 100644 --- a/sentry-spring-boot/build.gradle.kts +++ b/sentry-spring-boot/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 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-spring-jakarta/build.gradle.kts b/sentry-spring-jakarta/build.gradle.kts index f103bfcbe0a..138ad1e5f16 100644 --- a/sentry-spring-jakarta/build.gradle.kts +++ b/sentry-spring-jakarta/build.gradle.kts @@ -12,18 +12,17 @@ plugins { alias(libs.plugins.springboot3) apply false } -configure { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 +// Published code targets Java 17. Test code is not published and follows the toolchain. +tasks.named("compileJava") { options.release.set(17) } + +kotlin { + compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 + compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 + compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") } -tasks.withType().configureEach { - kotlin { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 - compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 - compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 - compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict") - } +tasks.named("compileKotlin") { + compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } dependencies { @@ -66,7 +65,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..71eb6d1f15e 100644 --- a/sentry-spring/build.gradle.kts +++ b/sentry-spring/build.gradle.kts @@ -12,7 +12,7 @@ plugins { id("io.sentry.animalsniffer") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = JvmTarget.JVM_1_8 compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 @@ -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..ecfdc6011f8 100644 --- a/sentry-system-test-support/build.gradle.kts +++ b/sentry-system-test-support/build.gradle.kts @@ -7,13 +7,7 @@ plugins { id("com.apollographql.apollo") version "4.1.1" } -configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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 ca60a47c6bb..2539beccb5f 100644 --- a/sentry-test-support/build.gradle.kts +++ b/sentry-test-support/build.gradle.kts @@ -6,13 +6,7 @@ plugins { alias(libs.plugins.gradle.versions) } -configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - tasks.withType().configureEach { - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 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..9cbcf5bf2c0 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -11,7 +11,7 @@ plugins { id("io.sentry.animalsniffer.android") } -tasks.withType().configureEach { +tasks.named("compileKotlin") { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 } @@ -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..a4ef2c9e859 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -257,6 +257,10 @@ public String getThirdPartyHeader() { return thirdPartyHeader; } + // temp fix for JDK 21: 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 +329,14 @@ public String getThirdPartyHeader() { return sb.toString(); } + // temp fix for JDK 21: The Charset overload requires Java 10. + @SuppressWarnings("JdkObsolete") private String encode(final @NotNull String value) throws UnsupportedEncodingException { return URLEncoder.encode(value, CHARSET).replaceAll("\\+", "%20"); } + // temp fix for JDK 21: 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..24ac5561d10 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; } + // temp fix for JDK 21: 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 */ + // temp fix for JDK 21: 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..1f067cd6f1b 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. */ + // temp fix for JDK 21: 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..73db50ea4ca 100644 --- a/sentry/src/main/java/io/sentry/MonitorContexts.java +++ b/sentry/src/main/java/io/sentry/MonitorContexts.java @@ -43,6 +43,10 @@ public void setTrace(final @NotNull SpanContext traceContext) { // region json + // temp fix for JDK 21: 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..0688ce38f0a 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") + // temp fix for JDK 21: 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 42aef2821ac..ec98cc5fcaf 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() @@ -476,7 +476,6 @@ public Queue getBreadcrumbs() { e); return null; } - return breadcrumb; } /** 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 3706a9f0a14..f4a17e3ebe5 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -58,6 +58,9 @@ import org.jetbrains.annotations.TestOnly; /** Sentry SDK options */ +// temp fix for JDK 21: 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..552ab1ed5c7 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 */ + // temp fix for JDK 21: 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..ebbeb56cd6e 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -16,6 +16,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +// temp fix for JDK 21: 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..6e8e6b76c6c 100644 --- a/sentry/src/main/java/io/sentry/SynchronizedCollection.java +++ b/sentry/src/main/java/io/sentry/SynchronizedCollection.java @@ -50,9 +50,14 @@ class SynchronizedCollection implements Collection, Serializable { private static final long serialVersionUID = 2412805092710877986L; /** The collection to decorate */ + // temp fix for JDK 21: 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 */ + // temp fix for JDK 21: 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..07e9b6357d7 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; } + // temp fix for JDK 21: 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..8ebf494dc99 100644 --- a/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java +++ b/sentry/src/main/java/io/sentry/cache/PersistingScopeObserver.java @@ -265,6 +265,9 @@ private void flush() { } } + // temp fix for JDK 21: 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..f3e8cbd4744 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; + // temp fix for JDK 21: Never serialized. + @SuppressWarnings("serial") private final @NotNull Mechanism exceptionMechanism; + private final @NotNull Throwable throwable; + + // temp fix for JDK 21: 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 139ac615394..77de6902614 100644 --- a/sentry/src/main/java/io/sentry/logger/LoggerApi.java +++ b/sentry/src/main/java/io/sentry/logger/LoggerApi.java @@ -151,7 +151,7 @@ private void captureLog( } } - private @NotNull HashMap createAttributes( + private @NotNull Map createAttributes( final @NotNull SentryLogParameters params, final @NotNull String message, final @Nullable Object... args) { @@ -246,8 +246,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) { @@ -264,7 +263,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 93564e4ddac..fa78511d224 100644 --- a/sentry/src/main/java/io/sentry/metrics/MetricsApi.java +++ b/sentry/src/main/java/io/sentry/metrics/MetricsApi.java @@ -155,7 +155,7 @@ private void captureMetrics( } } - private @NotNull HashMap createAttributes( + private @NotNull Map createAttributes( final @NotNull SentryMetricsParameters params) { final @NotNull HashMap attributes = new HashMap<>(); @@ -231,8 +231,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) { @@ -249,7 +248,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..0ca6d77ec8d 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -36,6 +36,9 @@ public class Contexts implements JsonSerializable { public Contexts() {} + // temp fix for JDK 21: 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 +255,10 @@ public void clear() { internalStorage.clear(); } + // temp fix for JDK 21: 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..9532998df28 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; + // temp fix for JDK 21: 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 { + // temp fix for JDK 21: 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..9c224bbbdd1 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; + // temp fix for JDK 21: 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 { + // temp fix for JDK 21: 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..21d5be90ab3 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; + // temp fix for JDK 21: 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 { + // temp fix for JDK 21: 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..68f65ba5f69 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. */ + // temp fix for JDK 21: 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..73da7808056 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; } + // temp fix for JDK 21: 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..6c1787474fa 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 { + // temp fix for JDK 21: 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..340d31c3857 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; } + // temp fix for JDK 21: 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..a7195c0462d 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,9 @@ private NetworkBodyParser() {} * @param logger Optional logger * @return A NetworkBody or null if bytes are null/empty */ + // temp fix for JDK 21: 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 +134,8 @@ private static NetworkBody parseJson( } /** Parses URL-encoded form data into a JsonObject NetworkBody. */ + // temp fix for JDK 21: 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 +211,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..e09b768bf24 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. */ + // temp fix for JDK 21: 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()) }