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 13f6624bfb..496c5333ec 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 7b31df47f6..43c3a7660c 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 496c5333ec..15817dd70d 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 6283b47f95..8b51c512c8 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 8680ed907d..6e1cceb760 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 0000000000..4d3e2d1595 --- /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 092caa040d..657ba54cbd 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 0000000000..9bc6f9f85b --- /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 83df0b81ec..0000000000 --- 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 c6add05cd0..f4ae724df4 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 d25122d59a..e31db87a4e 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 ed0fa2539a..d236a64b0c 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 4bc252f7a3..7cb5c903ca 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 7ba03815fc..377db931fc 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 530f5ea471..ebdad2a4d9 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 c793151d3d..27ecbff7d1 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 2483394aa1..2ece76b6b5 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 f1c03f0884..973cfcf628 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 f4ae724df4..f11c6475b7 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 e31db87a4e..662c20a088 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 8d37523888..6b94bc1b62 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 d236a64b0c..7b356dbada 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 7cb5c903ca..7a74d941f6 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 377db931fc..ad7153f750 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 ebdad2a4d9..e9d3d6e80c 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 27ecbff7d1..ab379c32f3 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 2ece76b6b5..5871d37beb 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 5ab90f51c8..6151098703 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 9996b6dfa2..ce434f67c4 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 c13f5e695a..d4e0839209 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 fde7ec44a3..1e6623d772 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 2d28cc01b9..3554926c3e 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 7449e5a182..1d32bba87b 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 f4354c72a8..684ef3c735 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 15817dd70d..acca00fc3c 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 ccd7c5dc40..ba6d60e256 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 165f3c6c03..e077b25fb3 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 ffdb120a7a..62c248385c 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 5de2dac4bd..174e6e3877 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 a0b508c971..ca60a47c6b 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 0000000000..439b61f2c2 --- /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 0000000000..6a1e3f0322 --- /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 d4e0839209..d798f0e437 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 1e6623d772..fdc9169ccc 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 3554926c3e..08dfd5e389 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 1d32bba87b..aa25e9854c 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 973cfcf628..6b0ecf7d1f 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 f11c6475b7..3b185217b4 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 662c20a088..00fe6dc8a7 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 6b94bc1b62..0ed7cbb4ef 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 7b356dbada..0fe0c84ccf 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 7a74d941f6..262b0fb3f4 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 ad7153f750..b85ed38713 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 e9d3d6e80c..fb61149ac5 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 ab379c32f3..72d660c505 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 5871d37beb..2bd88afd5a 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 6151098703..9b04020e6c 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 d798f0e437..8a723dd994 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 fdc9169ccc..33e06c49b5 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 08dfd5e389..a960e00082 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 aa25e9854c..2165a6c6a6 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 684ef3c735..8c49840c0b 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 fc90a79278..d02ab58488 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 58fadd9f17..76cf7346e5 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 0000000000..c92c83bbac --- /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 52e17b1d30..898b429cb5 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 4459ee342f..bc0172f0f0 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 43c3a7660c..e5c2f9e217 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 0d7f16af19..4facf6f4c2 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 7b21c2e392..120f2e0bef 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 f842f18674..589a9fa2ce 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 82376a75d8..dacab11e4f 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 cd21f92fd2..e41d5778a7 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 82cf1c25c9..7db354ea64 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 bc05f3b23f..39a63148a1 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 9ca0c1f6d9..e606ab5255 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 18a5b6cb7f..cab7c28f93 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 85ff2b1a3c..8f479470ca 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 19b9b3c753..ddc6d6f416 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 da7ad9f36f..b9d02bb49d 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 1318f7767c..1a92f9a0b3 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 fd15526fcb..38ea832082 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 f66c1a8a96..92f14b37c8 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 8ef43a9b69..3fd80e495f 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 7502fb6c4b..06c5f839f9 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 6711b165dd..ae8721b22b 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 9fb024a01d..63c8b5431c 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 e4cd7dd034..2d78dff8fa 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 b3cb3cff95..72a183bb7b 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 17454baa66..3bcdefd1d8 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 718fae422f..a0ce94c08e 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 394885df1b..19fce9f95b 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 def6708f67..9da6bcc1e3 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 34f71ab9cf..df0496f070 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 f996e0310a..25613e1fdd 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 b388f35881..b773c42c34 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 e2a7f57313..9d0aa1596f 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 2eec61eb17..130b46ad5e 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 4442052dd4..7d929ec772 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 0d543bad27..37240c8305 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 dbce760de9..a45505f101 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 bcc538e339..312faabbb4 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 101761b2a8..ded17d445e 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 cbee84179f..28edd1689e 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 ddc982e9d6..df081b2121 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 95aba9faaf..f2ed3d1574 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 6e5250ece5..2d2444a239 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 de2c9dbb80..48f84725a7 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 722845e2d7..66f61a182e 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 5abd1cf26b..0b3e238841 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 b079ead1fc..6750e22e61 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 8b7adc9754..209e5c34e5 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 087568d03e..3390f2695c 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 3585aa40d4..28a1b030e1 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 143ebb6c16..ad70d2346a 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 e44afc5a2d..7ac6bee4a8 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 a252628c1a..1c1e7f984b 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 ec240c681a..afab9ef180 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 f4f0d9d07d..b5387daf20 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 fe9023586e..b98eafd028 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 16fed92bd0..04d87a8b45 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 4fee4a1b92..cf4b054e35 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 a71f0bb904..57d1dea0e8 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 491583175a..58a8578c7d 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 d714a0ed54..f0b0d86289 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 3edf8e427a..6fa4bbb8c8 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 809167d040..901604d2cf 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 8bbc70a8fb..6d6e705103 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 8770dd8d38..82a18d86c7 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 2ca03bfc28..06cbdbe940 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 a2fefae6ba..bfd59790ce 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 112465bb5f..15bb3fca8a 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 d995d39de2..c5bc2b96fb 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 e5b44c1793..c0c021e832 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 3ec1817ce6..00b85d45bd 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 9ac03b5ebf..3535e1232e 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 6d1549842c..da761af1f6 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 bfe39c0dfa..9b59911bd3 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 722ccc69d3..cdc5753a9c 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 466c4fa7b5..5ffc83b7f6 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 f27ee6b234..3b7f56523d 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 08f74e90ba..342aaa0d70 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 1ef3a196e9..fe1859c0df 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 86ed855e2d..316e98ebdb 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 2d28ed2531..022b9fbb0c 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 7c6b6a915f..8ae94a2e36 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 5762a4ae52..b9d6ffc2ec 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 c2e3fe8e52..08ac7d4528 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 a8d538c2e2..072cb360e6 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 4e5ea54d29..c848c9416a 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 44ae584ec1..27720570a2 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 4b778d6864..b36df8be77 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 b6569a9953..8c755be2de 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 e58e0e9dd9..a14f0d4c96 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 cb5cba62a4..f4c630033a 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 591863d906..647da38381 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 2ea390fd2d..1a973c6203 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 eff0b4154b..c8b41d4d0f 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 b2b4d20b94..dca7c7d61d 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 6c9a3287ed..36eb08d524 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 46a31245ba..d0d0923a42 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 b16a6ab00e..913ee09b2d 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 19e6fa0fc4..421cb7a2a9 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 bffe53aab0..7ad300d479 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 d1128ce8a0..ce3a42989e 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 efdf9dc255..51d86eb6bb 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 5dd84ab413..88099b6eea 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 5f2bbe87b3..8c1a07aa9c 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 141f6a8ba0..c9d8684304 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 8409240d7f..4c0bbf6c92 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 c6fe511073..63594df7a2 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 cd05baecc4..641b335bec 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 27cb2d7805..c5537accef 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 f103bfcbe0..138ad1e5f1 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 b7f4646b4a..a64eb826f0 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 4d9e63a151..28157e0336 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 ec9964f7ab..ad4425131f 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 c6537f853c..5fe03f385d 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 4e7c3665aa..6a9f33e65f 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 5eb4374bb1..79bc5e11cc 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 8a06e4e338..dc3636a2b2 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 7253571269..4d5abda711 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 0628bc1d30..8a5d1db3e0 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 95618f76fd..833e59670e 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 03176d7b86..71eb6d1f15 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 d1ad308609..ed26e2ee79 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 180d5df00a..eab13d1a7d 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 7f08bf6d01..ecfdc6011f 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 ca60a47c6b..2539beccb5 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 6b98ff790c..9cbcf5bf2c 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 9f0753f867..a4ef2c9e85 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 4db0dcf46c..24ac5561d1 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 d9a8b14c62..0fff6b681b 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 fcba83fbe0..4be99b6322 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 f664360f49..71dd40843a 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 c96e061f2c..1f067cd6f1 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 17f65bb8c7..d9f0f32c40 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 272cf1c13a..810130416a 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 1e09dca554..9281d41fa9 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 a52ecc6b97..73db50ea4c 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 a83eddd380..0688ce38f0 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 17ce111d0c..7a9c69b073 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 42aef2821a..ec98cc5fca 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 2a7f06968d..d1858c0276 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 cd4ab1cc0e..3c07ec38b9 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 3706a9f0a1..f4a17e3ebe 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 8bd1448326..552ab1ed5c 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 d60187cb4d..781cf51417 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 19ab2c3ad8..ebbeb56cd6 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 8a58401020..6e8e6b76c6 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 1bb5508f85..957bda79f4 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 6ea6189579..07e9b6357d 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 8a097486db..8ebf494dc9 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 5df6d473f7..f3e8cbd474 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 5a02a911ac..8444887819 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 1f7ac93950..fc1c313ec4 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 139ac61539..77de690261 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 93564e4dda..fa78511d22 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 f6ba635383..0ca6d77ec8 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 fc9c8c7e69..9532998df2 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 aff3c55ac3..9c224bbbdd 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 c7bd613c1b..21d5be90ab 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 b6cc63e876..68f65ba5f6 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 59ed67eb9f..73da780805 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 471723ac18..6c1787474f 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 fc9bc75aa3..4e5f9be90e 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 df7aeab38b..340d31c385 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 ad2a4e7f3c..bad0f214db 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 1da7b4feee..0402190c85 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 42df5ca35b..a7195c0462 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 4991a554c1..e09b768bf2 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 eecacc95bd..703f0823c0 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 3a730e971b..4fb74690c3 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()) }