diff --git a/CHANGELOG.md b/CHANGELOG.md index 878238076f3..a6f943c6d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ - Prevent a class of Session Replay deadlocks by confining lifecycle state changes to Android's main thread ([#5965](https://github.com/getsentry/sentry-java/pull/5965)) - Symbolicate tombstone native frames for libraries loaded directly from APKs ([#5992](https://github.com/getsentry/sentry-java/pull/5992)) +### Internal + +- Seal `SentryOptions` once `Sentry.init` has finished ([#5999](https://github.com/getsentry/sentry-java/pull/5999)) + - Configuring options after `Sentry.init` returns never took effect reliably, because the logger, serializer, executors, transport and profilers are already built from the values they read during init. Such writes are now dropped with an error log, and throw when `debug` is enabled. + - Configure the SDK from the `Sentry.init` callback instead. + ### Features - Add screenshot attachment button to the Android user feedback widget ([#5828](https://github.com/getsentry/sentry-java/pull/5828)) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 615db97a28d..a428b2444eb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -314,6 +314,9 @@ public boolean isAnrEnabled() { * @param anrEnabled true for enabled and false for disabled */ public void setAnrEnabled(boolean anrEnabled) { + if (rejectAfterSeal("setAnrEnabled")) { + return; + } this.anrEnabled = anrEnabled; } @@ -332,6 +335,9 @@ public long getAnrTimeoutIntervalMillis() { * @param anrTimeoutIntervalMillis the timeout internal in Millis */ public void setAnrTimeoutIntervalMillis(long anrTimeoutIntervalMillis) { + if (rejectAfterSeal("setAnrTimeoutIntervalMillis")) { + return; + } this.anrTimeoutIntervalMillis = anrTimeoutIntervalMillis; } @@ -351,6 +357,9 @@ public boolean isAnrReportInDebug() { * @param anrReportInDebug true for enabled and false for disabled */ public void setAnrReportInDebug(boolean anrReportInDebug) { + if (rejectAfterSeal("setAnrReportInDebug")) { + return; + } this.anrReportInDebug = anrReportInDebug; } @@ -373,6 +382,9 @@ public boolean isEnableNdkAppHangTracking() { */ @ApiStatus.Experimental public void setEnableNdkAppHangTracking(boolean enableNdkAppHangTracking) { + if (rejectAfterSeal("setEnableNdkAppHangTracking")) { + return; + } this.enableNdkAppHangTracking = enableNdkAppHangTracking; } @@ -395,6 +407,9 @@ public long getNdkAppHangTimeoutIntervalMillis() { */ @ApiStatus.Experimental public void setNdkAppHangTimeoutIntervalMillis(long ndkAppHangTimeoutIntervalMillis) { + if (rejectAfterSeal("setNdkAppHangTimeoutIntervalMillis")) { + return; + } this.ndkAppHangTimeoutIntervalMillis = ndkAppHangTimeoutIntervalMillis; } @@ -404,6 +419,9 @@ public void setNdkAppHangTimeoutIntervalMillis(long ndkAppHangTimeoutIntervalMil * @param enableTombstone true for enabled and false for disabled */ public void setTombstoneEnabled(boolean enableTombstone) { + if (rejectAfterSeal("setTombstoneEnabled")) { + return; + } this.enableTombstone = enableTombstone; } @@ -422,6 +440,9 @@ public boolean isEnableActivityLifecycleBreadcrumbs() { } public void setEnableActivityLifecycleBreadcrumbs(boolean enableActivityLifecycleBreadcrumbs) { + if (rejectAfterSeal("setEnableActivityLifecycleBreadcrumbs")) { + return; + } this.enableActivityLifecycleBreadcrumbs = enableActivityLifecycleBreadcrumbs; } @@ -430,6 +451,9 @@ public boolean isEnableAppLifecycleBreadcrumbs() { } public void setEnableAppLifecycleBreadcrumbs(boolean enableAppLifecycleBreadcrumbs) { + if (rejectAfterSeal("setEnableAppLifecycleBreadcrumbs")) { + return; + } this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs; } @@ -438,6 +462,9 @@ public boolean isEnableSystemEventBreadcrumbs() { } public void setEnableSystemEventBreadcrumbs(boolean enableSystemEventBreadcrumbs) { + if (rejectAfterSeal("setEnableSystemEventBreadcrumbs")) { + return; + } this.enableSystemEventBreadcrumbs = enableSystemEventBreadcrumbs; } @@ -446,6 +473,9 @@ public boolean isEnableAppComponentBreadcrumbs() { } public void setEnableAppComponentBreadcrumbs(boolean enableAppComponentBreadcrumbs) { + if (rejectAfterSeal("setEnableAppComponentBreadcrumbs")) { + return; + } this.enableAppComponentBreadcrumbs = enableAppComponentBreadcrumbs; } @@ -454,6 +484,9 @@ public boolean isEnableNetworkEventBreadcrumbs() { } public void setEnableNetworkEventBreadcrumbs(boolean enableNetworkEventBreadcrumbs) { + if (rejectAfterSeal("setEnableNetworkEventBreadcrumbs")) { + return; + } this.enableNetworkEventBreadcrumbs = enableNetworkEventBreadcrumbs; } @@ -486,6 +519,9 @@ public void enableAllAutoBreadcrumbs(boolean enable) { * @param debugImagesLoader the image loader */ public void setDebugImagesLoader(final @NotNull IDebugImagesLoader debugImagesLoader) { + if (rejectAfterSeal("setDebugImagesLoader")) { + return; + } this.debugImagesLoader = debugImagesLoader != null ? debugImagesLoader : NoOpDebugImagesLoader.getInstance(); } @@ -495,6 +531,9 @@ public boolean isEnableAutoActivityLifecycleTracing() { } public void setEnableAutoActivityLifecycleTracing(boolean enableAutoActivityLifecycleTracing) { + if (rejectAfterSeal("setEnableAutoActivityLifecycleTracing")) { + return; + } this.enableAutoActivityLifecycleTracing = enableAutoActivityLifecycleTracing; } @@ -504,6 +543,9 @@ public boolean isEnableActivityLifecycleTracingAutoFinish() { public void setEnableActivityLifecycleTracingAutoFinish( boolean enableActivityLifecycleTracingAutoFinish) { + if (rejectAfterSeal("setEnableActivityLifecycleTracingAutoFinish")) { + return; + } this.enableActivityLifecycleTracingAutoFinish = enableActivityLifecycleTracingAutoFinish; } @@ -512,6 +554,9 @@ public boolean isAttachScreenshot() { } public void setAttachScreenshot(boolean attachScreenshot) { + if (rejectAfterSeal("setAttachScreenshot")) { + return; + } this.attachScreenshot = attachScreenshot; } @@ -520,6 +565,9 @@ public boolean isAttachViewHierarchy() { } public void setAttachViewHierarchy(boolean attachViewHierarchy) { + if (rejectAfterSeal("setAttachViewHierarchy")) { + return; + } this.attachViewHierarchy = attachViewHierarchy; } @@ -528,6 +576,9 @@ public boolean isCollectAdditionalContext() { } public void setCollectAdditionalContext(boolean collectAdditionalContext) { + if (rejectAfterSeal("setCollectAdditionalContext")) { + return; + } this.collectAdditionalContext = collectAdditionalContext; } @@ -536,6 +587,9 @@ public boolean isCollectExternalStorageContext() { } public void setCollectExternalStorageContext(final boolean collectExternalStorageContext) { + if (rejectAfterSeal("setCollectExternalStorageContext")) { + return; + } this.collectExternalStorageContext = collectExternalStorageContext; } @@ -549,6 +603,9 @@ public boolean isEnableFramesTracking() { * @param enableFramesTracking true if frames tracking should be enabled, false otherwise. */ public void setEnableFramesTracking(boolean enableFramesTracking) { + if (rejectAfterSeal("setEnableFramesTracking")) { + return; + } this.enableFramesTracking = enableFramesTracking; } @@ -590,11 +647,17 @@ public long getStartupCrashDurationThresholdMillis() { */ @ApiStatus.Internal public void setNativeSdkName(final @Nullable String nativeSdkName) { + if (rejectAfterSeal("setNativeSdkName")) { + return; + } this.nativeSdkName = nativeSdkName; } @ApiStatus.Internal public void setNativeHandlerStrategy(final @NotNull NdkHandlerStrategy ndkHandlerStrategy) { + if (rejectAfterSeal("setNativeHandlerStrategy")) { + return; + } this.ndkHandlerStrategy = ndkHandlerStrategy; } @@ -618,6 +681,9 @@ public boolean isEnableRootCheck() { } public void setEnableRootCheck(final boolean enableRootCheck) { + if (rejectAfterSeal("setEnableRootCheck")) { + return; + } this.enableRootCheck = enableRootCheck; } @@ -633,6 +699,9 @@ public void setEnableRootCheck(final boolean enableRootCheck) { */ public void setBeforeScreenshotCaptureCallback( final @NotNull BeforeCaptureCallback beforeScreenshotCaptureCallback) { + if (rejectAfterSeal("setBeforeScreenshotCaptureCallback")) { + return; + } this.beforeScreenshotCaptureCallback = beforeScreenshotCaptureCallback; } @@ -648,6 +717,9 @@ public void setBeforeScreenshotCaptureCallback( */ public void setBeforeViewHierarchyCaptureCallback( final @NotNull BeforeCaptureCallback beforeViewHierarchyCaptureCallback) { + if (rejectAfterSeal("setBeforeViewHierarchyCaptureCallback")) { + return; + } this.beforeViewHierarchyCaptureCallback = beforeViewHierarchyCaptureCallback; } @@ -692,6 +764,9 @@ public boolean isReportHistoricalAnrs() { } public void setReportHistoricalAnrs(final boolean reportHistoricalAnrs) { + if (rejectAfterSeal("setReportHistoricalAnrs")) { + return; + } this.reportHistoricalAnrs = reportHistoricalAnrs; } @@ -700,6 +775,9 @@ public boolean isReportHistoricalTombstones() { } public void setReportHistoricalTombstones(final boolean reportHistoricalTombstones) { + if (rejectAfterSeal("setReportHistoricalTombstones")) { + return; + } this.reportHistoricalTombstones = reportHistoricalTombstones; } @@ -708,6 +786,9 @@ public boolean isAttachAnrThreadDump() { } public void setAttachAnrThreadDump(final boolean attachAnrThreadDump) { + if (rejectAfterSeal("setAttachAnrThreadDump")) { + return; + } this.attachAnrThreadDump = attachAnrThreadDump; } @@ -716,6 +797,9 @@ public boolean isAttachRawTombstone() { } public void setAttachRawTombstone(final boolean attachRawTombstone) { + if (rejectAfterSeal("setAttachRawTombstone")) { + return; + } this.attachRawTombstone = attachRawTombstone; } @@ -736,6 +820,9 @@ public boolean isEnablePerformanceV2() { * @param enablePerformanceV2 true if enabled or false otherwise */ public void setEnablePerformanceV2(final boolean enablePerformanceV2) { + if (rejectAfterSeal("setEnablePerformanceV2")) { + return; + } this.enablePerformanceV2 = enablePerformanceV2; } @@ -783,6 +870,9 @@ public boolean isEnableStandaloneAppStartTracing() { */ @ApiStatus.Experimental public void setEnableStandaloneAppStartTracing(final boolean enableStandaloneAppStartTracing) { + if (rejectAfterSeal("setEnableStandaloneAppStartTracing")) { + return; + } this.enableStandaloneAppStartTracing = enableStandaloneAppStartTracing; } @@ -794,6 +884,9 @@ public void setEnableStandaloneAppStartTracing(final boolean enableStandaloneApp @ApiStatus.Internal public void setFrameMetricsCollector( final @Nullable SentryFrameMetricsCollector frameMetricsCollector) { + if (rejectAfterSeal("setFrameMetricsCollector")) { + return; + } this.frameMetricsCollector = frameMetricsCollector; } @@ -802,6 +895,9 @@ public boolean isEnableAutoTraceIdGeneration() { } public void setEnableAutoTraceIdGeneration(final boolean enableAutoTraceIdGeneration) { + if (rejectAfterSeal("setEnableAutoTraceIdGeneration")) { + return; + } this.enableAutoTraceIdGeneration = enableAutoTraceIdGeneration; } @@ -811,6 +907,9 @@ public boolean isEnableSystemEventBreadcrumbsExtras() { public void setEnableSystemEventBreadcrumbsExtras( final boolean enableSystemEventBreadcrumbsExtras) { + if (rejectAfterSeal("setEnableSystemEventBreadcrumbsExtras")) { + return; + } this.enableSystemEventBreadcrumbsExtras = enableSystemEventBreadcrumbsExtras; } @@ -828,6 +927,9 @@ public void setEnableSystemEventBreadcrumbsExtras( } public void setAnrProfilingSampleRate(final @Nullable Double anrProfilingSampleRate) { + if (rejectAfterSeal("setAnrProfilingSampleRate")) { + return; + } if (!SampleRateUtils.isValidSampleRate(anrProfilingSampleRate)) { throw new IllegalArgumentException( "The value " @@ -861,6 +963,9 @@ public boolean isEnableAnrFingerprinting() { * @param enableAnrFingerprinting true to enable ANR fingerprinting */ public void setEnableAnrFingerprinting(final boolean enableAnrFingerprinting) { + if (rejectAfterSeal("setEnableAnrFingerprinting")) { + return; + } this.enableAnrFingerprinting = enableAnrFingerprinting; } diff --git a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt index ef1f12aeecf..f6af12f1d90 100644 --- a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt @@ -2,7 +2,6 @@ package io.sentry.spring.boot4 import com.acme.MainBootClass import io.opentelemetry.api.OpenTelemetry -import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.EventProcessor import io.sentry.FilterString @@ -799,8 +798,10 @@ class SentryAutoConfigurationTest { .withPropertyValues("sentry.dsn=http://key@localhost/proj") .withClassLoader(FilteredClassLoader(ApacheHttpClientTransportFactory::class.java)) .run { + // Spring installs no factory here; SentryClient resolves the async http one internally + // without writing it back to the options. assertThat(it.getBean(SentryOptions::class.java).transportFactory) - .isInstanceOf(AsyncHttpTransportFactory::class.java) + .isInstanceOf(NoOpTransportFactory::class.java) } } diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index 91677d16b4e..a7c755af5a8 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -2,7 +2,6 @@ package io.sentry.spring.boot.jakarta import com.acme.MainBootClass import io.opentelemetry.api.OpenTelemetry -import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.DataCategory import io.sentry.EventProcessor @@ -818,8 +817,10 @@ class SentryAutoConfigurationTest { .withPropertyValues("sentry.dsn=http://key@localhost/proj") .withClassLoader(FilteredClassLoader(ApacheHttpClientTransportFactory::class.java)) .run { + // Spring installs no factory here; SentryClient resolves the async http one internally + // without writing it back to the options. assertThat(it.getBean(SentryOptions::class.java).transportFactory) - .isInstanceOf(AsyncHttpTransportFactory::class.java) + .isInstanceOf(NoOpTransportFactory::class.java) } } diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index d9e598d0473..7bc436451e3 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -2,7 +2,6 @@ package io.sentry.spring.boot import com.acme.MainBootClass import io.opentelemetry.api.OpenTelemetry -import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.DataCategory import io.sentry.EventProcessor @@ -809,8 +808,10 @@ class SentryAutoConfigurationTest { .withPropertyValues("sentry.dsn=http://key@localhost/proj") .withClassLoader(FilteredClassLoader(ApacheHttpClientTransportFactory::class.java)) .run { + // Spring installs no factory here; SentryClient resolves the async http one internally + // without writing it back to the options. assertThat(it.getBean(SentryOptions::class.java).transportFactory) - .isInstanceOf(AsyncHttpTransportFactory::class.java) + .isInstanceOf(NoOpTransportFactory::class.java) } } diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/Init.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/Init.kt index 5061714d5ed..57a7957ec31 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/Init.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/Init.kt @@ -4,37 +4,47 @@ import io.sentry.Sentry import io.sentry.Sentry.OptionsConfiguration import io.sentry.SentryOptions -fun initForTest(optionsConfiguration: OptionsConfiguration) { - Sentry.init { - applyTestOptions(it) - optionsConfiguration.configure(it) +/** + * Sentry.init seals the options, but fixtures commonly keep configuring them afterwards. Undo the + * seal so those fixtures keep working; SentryOptionsSealTest covers the production behaviour. + */ +private fun initAndUnseal(globalHubMode: Boolean? = null, configure: (SentryOptions) -> Unit) { + var configured: SentryOptions? = null + val configuration = + OptionsConfiguration { + applyTestOptions(it) + configure(it) + configured = it + } + if (globalHubMode == null) { + Sentry.init(configuration) + } else { + Sentry.init(configuration, globalHubMode) } + configured?.unseal() +} + +fun initForTest(optionsConfiguration: OptionsConfiguration) { + initAndUnseal { optionsConfiguration.configure(it) } } fun initForTest(optionsConfiguration: OptionsConfiguration, globalHubMode: Boolean) { - Sentry.init( - { - applyTestOptions(it) - optionsConfiguration.configure(it) - }, - globalHubMode, - ) + initAndUnseal(globalHubMode) { optionsConfiguration.configure(it) } } fun initForTest(dsn: String) { - Sentry.init { - applyTestOptions(it) - it.dsn = dsn - } + initAndUnseal { it.dsn = dsn } } fun initForTest(options: SentryOptions) { applyTestOptions(options) Sentry.init(options) + options.unseal() } fun initForTest() { - Sentry.init() + // Mirrors the no-arg Sentry.init(), which enables external configuration. + initAndUnseal { it.isEnableExternalConfiguration = true } } fun applyTestOptions(options: SentryOptions) { diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index fa876b3312f..496077b549a 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3809,6 +3809,8 @@ public class io/sentry/SentryOptions { public fun isTraceSampling ()Z public fun isTracingEnabled ()Z public fun merge (Lio/sentry/ExternalOptions;)V + protected final fun rejectAfterSeal (Ljava/lang/String;)Z + public fun seal ()V public fun setAppStartExtender (Lio/sentry/IAppStartExtender;)V public fun setAttachServerName (Z)V public fun setAttachStacktrace (Z)V @@ -3939,6 +3941,7 @@ public class io/sentry/SentryOptions { public fun setTransportGate (Lio/sentry/transport/ITransportGate;)V public fun setVersionDetector (Lio/sentry/IVersionDetector;)V public fun setViewHierarchyExporters (Ljava/util/List;)V + public fun unseal ()V } public abstract interface class io/sentry/SentryOptions$BeforeBreadcrumbCallback { diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 266aa39e793..be744ac5877 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -306,6 +306,11 @@ private static void init(final @NotNull SentryOptions options, final boolean glo + options.getClass().getName()); } + // The SDK can be restarted with the same options instance, which init then has to re-wire + // (a closed executor service, for one). Reopen the configuration phase that a previous + // init sealed; the seal at the end of this method closes it again. + options.unseal(); + if (!preInitConfigurations(options)) { return; } @@ -407,6 +412,10 @@ private static void init(final @NotNull SentryOptions options, final boolean glo options .getLogger() .log(SentryLevel.DEBUG, "Using scopes storage %s", scopesStorage.getClass().getName()); + + // Everything is wired up and every integration has registered: from here on the options + // describe how the SDK was built, and writing to them can no longer take effect. + options.seal(); } else { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 18e247c510a..165dd40445f 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -57,7 +57,6 @@ public SentryClient(final @NotNull SentryOptions options) { ITransportFactory transportFactory = options.getTransportFactory(); if (transportFactory instanceof NoOpTransportFactory) { transportFactory = new AsyncHttpTransportFactory(); - options.setTransportFactory(transportFactory); } final RequestDetailsResolver requestDetailsResolver = new RequestDetailsResolver(options); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index d7a16d4ee23..ce511aeddff 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -688,9 +688,62 @@ public class SentryOptions { } public void setProfilerConverter(@NotNull IProfileConverter profilerConverter) { + if (rejectAfterSeal("setProfilerConverter")) { + return; + } this.profilerConverter = profilerConverter; } + /** + * Set by {@link #seal()} once Sentry.init has finished wiring the SDK up. Writes that arrive + * after that point cannot take effect consistently: collaborators have already been constructed + * from the values they read during init. + */ + private volatile boolean sealed = false; + + /** + * Marks the end of the configuration phase. Called by Sentry.init once every integration has been + * registered. + */ + @ApiStatus.Internal + public void seal() { + sealed = true; + } + + /** + * Reopens the configuration phase closed by {@link #seal()}. Called by Sentry.init, which has to + * re-wire an options instance that a previous init already sealed when the SDK is restarted. + */ + @ApiStatus.Internal + public void unseal() { + sealed = false; + } + + /** + * Guards a mutator against writes that arrive after {@link #seal()}. Throws when debug is enabled + * so the mistake is loud during development, and otherwise drops the write with an error log + * rather than risking a crash in a host application. + * + * @param mutator name of the calling mutator, for the diagnostic message + * @return true when the caller should skip the write + */ + @ApiStatus.Internal + protected final boolean rejectAfterSeal(final @NotNull String mutator) { + if (!sealed) { + return false; + } + final String message = + "Ignoring " + + mutator + + "(): SentryOptions are sealed once Sentry.init has finished. Configure the SDK from" + + " the Sentry.init callback instead."; + if (debug) { + throw new IllegalStateException(message); + } + logger.log(SentryLevel.ERROR, message); + return true; + } + /** Starts expensive parts of the options during Sentry.init */ @ApiStatus.Internal public void activate() { @@ -756,6 +809,9 @@ public static final class DistributionOptions { * @param eventProcessor the event processor */ public void addEventProcessor(@NotNull EventProcessor eventProcessor) { + if (rejectAfterSeal("addEventProcessor")) { + return; + } eventProcessors.add(eventProcessor); } @@ -774,6 +830,9 @@ public void addEventProcessor(@NotNull EventProcessor eventProcessor) { * @param integration the integration */ public void addIntegration(@NotNull Integration integration) { + if (rejectAfterSeal("addIntegration")) { + return; + } integrations.add(integration); } @@ -814,6 +873,9 @@ Dsn retrieveParsedDsn() throws IllegalArgumentException { * @param dsn the DSN */ public void setDsn(final @Nullable String dsn) { + if (rejectAfterSeal("setDsn")) { + return; + } this.dsn = dsn != null ? dsn.trim() : null; this.parsedDsn.resetValue(); @@ -835,6 +897,9 @@ public boolean isDebug() { * @param debug true if ON or false otherwise */ public void setDebug(final boolean debug) { + if (rejectAfterSeal("setDebug")) { + return; + } this.debug = debug; } @@ -853,6 +918,9 @@ public void setDebug(final boolean debug) { * @param logger the logger interface */ public void setLogger(final @Nullable ILogger logger) { + if (rejectAfterSeal("setLogger")) { + return; + } this.logger = (logger == null) ? NoOpLogger.getInstance() : new DiagnosticLogger(this, logger); } @@ -873,6 +941,9 @@ public void setLogger(final @Nullable ILogger logger) { */ @ApiStatus.Experimental public void setFatalLogger(final @Nullable ILogger logger) { + if (rejectAfterSeal("setFatalLogger")) { + return; + } this.fatalLogger = (logger == null) ? NoOpLogger.getInstance() : logger; } @@ -891,6 +962,9 @@ public void setFatalLogger(final @Nullable ILogger logger) { * @param diagnosticLevel the log level */ public void setDiagnosticLevel(@Nullable final SentryLevel diagnosticLevel) { + if (rejectAfterSeal("setDiagnosticLevel")) { + return; + } this.diagnosticLevel = (diagnosticLevel != null) ? diagnosticLevel : DEFAULT_DIAGNOSTIC_LEVEL; } @@ -909,6 +983,9 @@ public void setDiagnosticLevel(@Nullable final SentryLevel diagnosticLevel) { * @param serializer the serializer */ public void setSerializer(@Nullable ISerializer serializer) { + if (rejectAfterSeal("setSerializer")) { + return; + } this.serializer.setValue(serializer != null ? serializer : NoOpSerializer.getInstance()); } @@ -927,6 +1004,9 @@ public int getMaxDepth() { * @param maxDepth the max depth */ public void setMaxDepth(int maxDepth) { + if (rejectAfterSeal("setMaxDepth")) { + return; + } this.maxDepth = maxDepth; } @@ -935,6 +1015,9 @@ public void setMaxDepth(int maxDepth) { } public void setEnvelopeReader(final @Nullable IEnvelopeReader envelopeReader) { + if (rejectAfterSeal("setEnvelopeReader")) { + return; + } this.envelopeReader.setValue( envelopeReader != null ? envelopeReader : NoOpEnvelopeReader.getInstance()); } @@ -954,6 +1037,9 @@ public long getShutdownTimeoutMillis() { * @param shutdownTimeoutMillis the shutdown timeout in millis */ public void setShutdownTimeoutMillis(long shutdownTimeoutMillis) { + if (rejectAfterSeal("setShutdownTimeoutMillis")) { + return; + } this.shutdownTimeoutMillis = shutdownTimeoutMillis; } @@ -972,6 +1058,9 @@ public void setShutdownTimeoutMillis(long shutdownTimeoutMillis) { * @param sentryClientName the Sentry client name */ public void setSentryClientName(@Nullable String sentryClientName) { + if (rejectAfterSeal("setSentryClientName")) { + return; + } this.sentryClientName = sentryClientName; } @@ -990,6 +1079,9 @@ public void setSentryClientName(@Nullable String sentryClientName) { * @param beforeSend the beforeSend callback */ public void setBeforeSend(@Nullable BeforeSendCallback beforeSend) { + if (rejectAfterSeal("setBeforeSend")) { + return; + } this.beforeSend = beforeSend; } @@ -1009,6 +1101,9 @@ public void setBeforeSend(@Nullable BeforeSendCallback beforeSend) { */ public void setBeforeSendTransaction( @Nullable BeforeSendTransactionCallback beforeSendTransaction) { + if (rejectAfterSeal("setBeforeSendTransaction")) { + return; + } this.beforeSendTransaction = beforeSendTransaction; } @@ -1027,6 +1122,9 @@ public void setBeforeSendTransaction( * @param beforeSendFeedback the beforeSendFeedback callback */ public void setBeforeSendFeedback(@Nullable BeforeSendCallback beforeSendFeedback) { + if (rejectAfterSeal("setBeforeSendFeedback")) { + return; + } this.beforeSendFeedback = beforeSendFeedback; } @@ -1045,6 +1143,9 @@ public void setBeforeSendFeedback(@Nullable BeforeSendCallback beforeSendFeedbac * @param beforeSendReplay the beforeSend callback */ public void setBeforeSendReplay(@Nullable BeforeSendReplayCallback beforeSendReplay) { + if (rejectAfterSeal("setBeforeSendReplay")) { + return; + } this.beforeSendReplay = beforeSendReplay; } @@ -1063,6 +1164,9 @@ public void setBeforeSendReplay(@Nullable BeforeSendReplayCallback beforeSendRep * @param beforeBreadcrumb the beforeBreadcrumb callback */ public void setBeforeBreadcrumb(@Nullable BeforeBreadcrumbCallback beforeBreadcrumb) { + if (rejectAfterSeal("setBeforeBreadcrumb")) { + return; + } this.beforeBreadcrumb = beforeBreadcrumb; } @@ -1081,6 +1185,9 @@ public void setBeforeBreadcrumb(@Nullable BeforeBreadcrumbCallback beforeBreadcr * @param onDiscard the onDiscard callback */ public void setOnDiscard(@Nullable OnDiscardCallback onDiscard) { + if (rejectAfterSeal("setOnDiscard")) { + return; + } this.onDiscard = onDiscard; } @@ -1134,6 +1241,9 @@ String getCacheDirPathWithoutDsn() { * @param cacheDirPath the cache dir. path */ public void setCacheDirPath(final @Nullable String cacheDirPath) { + if (rejectAfterSeal("setCacheDirPath")) { + return; + } this.cacheDirPath = cacheDirPath; } @@ -1152,6 +1262,9 @@ public int getMaxBreadcrumbs() { * @param maxBreadcrumbs the max breadcrumbs */ public void setMaxBreadcrumbs(int maxBreadcrumbs) { + if (rejectAfterSeal("setMaxBreadcrumbs")) { + return; + } this.maxBreadcrumbs = maxBreadcrumbs; } @@ -1170,6 +1283,9 @@ public int getMaxFeatureFlags() { * @param maxFeatureFlags the max feature flags */ public void setMaxFeatureFlags(int maxFeatureFlags) { + if (rejectAfterSeal("setMaxFeatureFlags")) { + return; + } this.maxFeatureFlags = maxFeatureFlags; } @@ -1188,6 +1304,9 @@ public void setMaxFeatureFlags(int maxFeatureFlags) { * @param release the release */ public void setRelease(@Nullable String release) { + if (rejectAfterSeal("setRelease")) { + return; + } this.release = release; } @@ -1206,6 +1325,9 @@ public void setRelease(@Nullable String release) { * @param environment the environment */ public void setEnvironment(@Nullable String environment) { + if (rejectAfterSeal("setEnvironment")) { + return; + } this.environment = environment; } @@ -1224,6 +1346,9 @@ public void setEnvironment(@Nullable String environment) { * @param proxy the proxy */ public void setProxy(@Nullable Proxy proxy) { + if (rejectAfterSeal("setProxy")) { + return; + } this.proxy = proxy; } @@ -1242,6 +1367,9 @@ public void setProxy(@Nullable Proxy proxy) { * @param sampleRate the sample rate */ public void setSampleRate(@Nullable Double sampleRate) { + if (rejectAfterSeal("setSampleRate")) { + return; + } if (!SampleRateUtils.isValidSampleRate(sampleRate)) { throw new IllegalArgumentException( "The value " @@ -1266,6 +1394,9 @@ public void setSampleRate(@Nullable Double sampleRate) { * @param tracesSampleRate the sample rate */ public void setTracesSampleRate(final @Nullable Double tracesSampleRate) { + if (rejectAfterSeal("setTracesSampleRate")) { + return; + } if (!SampleRateUtils.isValidTracesSampleRate(tracesSampleRate)) { throw new IllegalArgumentException( "The value " @@ -1290,6 +1421,9 @@ public void setTracesSampleRate(final @Nullable Double tracesSampleRate) { * @param tracesSampler the callback */ public void setTracesSampler(final @Nullable TracesSamplerCallback tracesSampler) { + if (rejectAfterSeal("setTracesSampler")) { + return; + } this.tracesSampler = tracesSampler; } @@ -1320,6 +1454,9 @@ public void setTracesSampler(final @Nullable TracesSamplerCallback tracesSampler * @param exclude the inApp exclude module/package */ public void addInAppExclude(@NotNull String exclude) { + if (rejectAfterSeal("addInAppExclude")) { + return; + } inAppExcludes.add(exclude); } @@ -1338,6 +1475,9 @@ public void addInAppExclude(@NotNull String exclude) { * @param include the inApp include module/package */ public void addInAppInclude(@NotNull String include) { + if (rejectAfterSeal("addInAppInclude")) { + return; + } inAppIncludes.add(include); } @@ -1356,6 +1496,9 @@ public void addInAppInclude(@NotNull String include) { * @param transportFactory the transport factory */ public void setTransportFactory(@Nullable ITransportFactory transportFactory) { + if (rejectAfterSeal("setTransportFactory")) { + return; + } this.transportFactory = transportFactory != null ? transportFactory : NoOpTransportFactory.getInstance(); } @@ -1375,6 +1518,9 @@ public void setTransportFactory(@Nullable ITransportFactory transportFactory) { * @param dist the distribution */ public void setDist(@Nullable String dist) { + if (rejectAfterSeal("setDist")) { + return; + } this.dist = dist; } @@ -1393,6 +1539,9 @@ public void setDist(@Nullable String dist) { * @param transportGate the transport gate */ public void setTransportGate(@Nullable ITransportGate transportGate) { + if (rejectAfterSeal("setTransportGate")) { + return; + } this.transportGate = (transportGate != null) ? transportGate : NoOpTransportGate.getInstance(); } @@ -1411,6 +1560,9 @@ public boolean isAttachStacktrace() { * @param attachStacktrace true if enabled or false otherwise */ public void setAttachStacktrace(boolean attachStacktrace) { + if (rejectAfterSeal("setAttachStacktrace")) { + return; + } this.attachStacktrace = attachStacktrace; } @@ -1429,6 +1581,9 @@ public boolean isAttachThreads() { * @param attachThreads true if enabled or false otherwise */ public void setAttachThreads(boolean attachThreads) { + if (rejectAfterSeal("setAttachThreads")) { + return; + } this.attachThreads = attachThreads; } @@ -1447,6 +1602,9 @@ public boolean isEnableAutoSessionTracking() { * @param enableAutoSessionTracking true if enabled or false otherwise */ public void setEnableAutoSessionTracking(final boolean enableAutoSessionTracking) { + if (rejectAfterSeal("setEnableAutoSessionTracking")) { + return; + } this.enableAutoSessionTracking = enableAutoSessionTracking; } @@ -1465,6 +1623,9 @@ public void setEnableAutoSessionTracking(final boolean enableAutoSessionTracking * @param serverName the default server name or null if none should be used */ public void setServerName(@Nullable String serverName) { + if (rejectAfterSeal("setServerName")) { + return; + } this.serverName = serverName; } @@ -1483,6 +1644,9 @@ public boolean isAttachServerName() { * @param attachServerName true if enabled false if otherwise */ public void setAttachServerName(boolean attachServerName) { + if (rejectAfterSeal("setAttachServerName")) { + return; + } this.attachServerName = attachServerName; } @@ -1501,6 +1665,9 @@ public long getSessionTrackingIntervalMillis() { * @param sessionTrackingIntervalMillis the interval in millis */ public void setSessionTrackingIntervalMillis(long sessionTrackingIntervalMillis) { + if (rejectAfterSeal("setSessionTrackingIntervalMillis")) { + return; + } this.sessionTrackingIntervalMillis = sessionTrackingIntervalMillis; } @@ -1519,6 +1686,9 @@ public void setSessionTrackingIntervalMillis(long sessionTrackingIntervalMillis) * @param distinctId the distinct Id */ public void setDistinctId(final @Nullable String distinctId) { + if (rejectAfterSeal("setDistinctId")) { + return; + } this.distinctId = distinctId; } @@ -1537,6 +1707,9 @@ public long getFlushTimeoutMillis() { * @param flushTimeoutMillis the timeout in millis */ public void setFlushTimeoutMillis(long flushTimeoutMillis) { + if (rejectAfterSeal("setFlushTimeoutMillis")) { + return; + } this.flushTimeoutMillis = flushTimeoutMillis; } @@ -1555,6 +1728,9 @@ public boolean isEnableUncaughtExceptionHandler() { * @param enableUncaughtExceptionHandler true if enabled or false otherwise. */ public void setEnableUncaughtExceptionHandler(final boolean enableUncaughtExceptionHandler) { + if (rejectAfterSeal("setEnableUncaughtExceptionHandler")) { + return; + } this.enableUncaughtExceptionHandler = enableUncaughtExceptionHandler; } @@ -1573,6 +1749,9 @@ public boolean isPrintUncaughtStackTrace() { * @param printUncaughtStackTrace true if enabled or false otherwise. */ public void setPrintUncaughtStackTrace(final boolean printUncaughtStackTrace) { + if (rejectAfterSeal("setPrintUncaughtStackTrace")) { + return; + } this.printUncaughtStackTrace = printUncaughtStackTrace; } @@ -1595,6 +1774,9 @@ public ISentryExecutorService getExecutorService() { @ApiStatus.Internal @TestOnly public void setExecutorService(final @NotNull ISentryExecutorService executorService) { + if (rejectAfterSeal("setExecutorService")) { + return; + } if (executorService != null) { this.executorService = executorService; } @@ -1619,6 +1801,9 @@ public ISentryExecutorService getTimerExecutorService() { @ApiStatus.Internal @TestOnly public void setTimerExecutorService(final @NotNull ISentryExecutorService timerExecutorService) { + if (rejectAfterSeal("setTimerExecutorService")) { + return; + } if (timerExecutorService != null) { this.timerExecutorService = timerExecutorService; } @@ -1639,6 +1824,9 @@ public int getConnectionTimeoutMillis() { * @param connectionTimeoutMillis the connectionTimeoutMillis */ public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { + if (rejectAfterSeal("setConnectionTimeoutMillis")) { + return; + } this.connectionTimeoutMillis = connectionTimeoutMillis; } @@ -1657,6 +1845,9 @@ public int getReadTimeoutMillis() { * @param readTimeoutMillis the readTimeoutMillis */ public void setReadTimeoutMillis(int readTimeoutMillis) { + if (rejectAfterSeal("setReadTimeoutMillis")) { + return; + } this.readTimeoutMillis = readTimeoutMillis; } @@ -1675,6 +1866,9 @@ public void setReadTimeoutMillis(int readTimeoutMillis) { * @param envelopeDiskCache the EnvelopeCache object */ public void setEnvelopeDiskCache(final @Nullable IEnvelopeCache envelopeDiskCache) { + if (rejectAfterSeal("setEnvelopeDiskCache")) { + return; + } this.envelopeDiskCache = envelopeDiskCache != null ? envelopeDiskCache : NoOpEnvelopeCache.getInstance(); } @@ -1694,6 +1888,9 @@ public int getMaxQueueSize() { * @param maxQueueSize max queue size */ public void setMaxQueueSize(int maxQueueSize) { + if (rejectAfterSeal("setMaxQueueSize")) { + return; + } if (maxQueueSize > 0) { this.maxQueueSize = maxQueueSize; } @@ -1723,6 +1920,9 @@ public void setMaxQueueSize(int maxQueueSize) { * @param sslSocketFactory SSLSocketFactory object */ public void setSslSocketFactory(final @Nullable SSLSocketFactory sslSocketFactory) { + if (rejectAfterSeal("setSslSocketFactory")) { + return; + } this.sslSocketFactory = sslSocketFactory; } @@ -1733,6 +1933,9 @@ public void setSslSocketFactory(final @Nullable SSLSocketFactory sslSocketFactor */ @ApiStatus.Internal public void setSdkVersion(final @Nullable SdkVersion sdkVersion) { + if (rejectAfterSeal("setSdkVersion")) { + return; + } final @Nullable SdkVersion replaySdkVersion = getSessionReplay().getSdkVersion(); if (this.sdkVersion != null && replaySdkVersion != null @@ -1748,6 +1951,9 @@ public boolean isSendDefaultPii() { } public void setSendDefaultPii(boolean sendDefaultPii) { + if (rejectAfterSeal("setSendDefaultPii")) { + return; + } this.sendDefaultPii = sendDefaultPii; } @@ -1757,6 +1963,9 @@ public void setSendDefaultPii(boolean sendDefaultPii) { * @param observer the Observer */ public void addScopeObserver(final @NotNull IScopeObserver observer) { + if (rejectAfterSeal("addScopeObserver")) { + return; + } observers.add(observer); } @@ -1787,6 +1996,9 @@ public PersistingScopeObserver findPersistingScopeObserver() { * @param observer the Observer */ public void addOptionsObserver(final @NotNull IOptionsObserver observer) { + if (rejectAfterSeal("addOptionsObserver")) { + return; + } optionsObservers.add(observer); } @@ -1816,6 +2028,9 @@ public boolean isEnableExternalConfiguration() { * @param enableExternalConfiguration true if enabled or false otherwise */ public void setEnableExternalConfiguration(boolean enableExternalConfiguration) { + if (rejectAfterSeal("setEnableExternalConfiguration")) { + return; + } this.enableExternalConfiguration = enableExternalConfiguration; } @@ -1835,6 +2050,9 @@ public void setEnableExternalConfiguration(boolean enableExternalConfiguration) * @param value the value */ public void setTag(final @Nullable String key, final @Nullable String value) { + if (rejectAfterSeal("setTag")) { + return; + } if (key == null) { return; } @@ -1862,6 +2080,9 @@ public long getMaxAttachmentSize() { * @param maxAttachmentSize the max attachment size in bytes. */ public void setMaxAttachmentSize(long maxAttachmentSize) { + if (rejectAfterSeal("setMaxAttachmentSize")) { + return; + } this.maxAttachmentSize = maxAttachmentSize; } @@ -1880,6 +2101,9 @@ public boolean isEnableDeduplication() { * @param enableDeduplication true if enabled false otherwise */ public void setEnableDeduplication(final boolean enableDeduplication) { + if (rejectAfterSeal("setEnableDeduplication")) { + return; + } this.enableDeduplication = enableDeduplication; } @@ -1899,6 +2123,9 @@ public boolean isEnableEventSizeLimiting() { * @param enableEventSizeLimiting true to enable, false to disable */ public void setEnableEventSizeLimiting(final boolean enableEventSizeLimiting) { + if (rejectAfterSeal("setEnableEventSizeLimiting")) { + return; + } this.enableEventSizeLimiting = enableEventSizeLimiting; } @@ -1918,6 +2145,9 @@ public void setEnableEventSizeLimiting(final boolean enableEventSizeLimiting) { * @param onOversizedEvent the onOversizedEvent callback */ public void setOnOversizedEvent(@Nullable OnOversizedEventCallback onOversizedEvent) { + if (rejectAfterSeal("setOnOversizedEvent")) { + return; + } this.onOversizedEvent = onOversizedEvent; } @@ -1948,6 +2178,9 @@ public boolean isTracingEnabled() { * @param exceptionType - the exception type */ public void addIgnoredExceptionForType(final @NotNull Class exceptionType) { + if (rejectAfterSeal("addIgnoredExceptionForType")) { + return; + } this.ignoredExceptionsForType.add(exceptionType); } @@ -1982,6 +2215,9 @@ boolean containsIgnoredExceptionForType(final @NotNull Throwable throwable) { * @param ignoredErrors the list of strings/regex patterns */ public void setIgnoredErrors(final @Nullable List ignoredErrors) { + if (rejectAfterSeal("setIgnoredErrors")) { + return; + } if (ignoredErrors == null) { this.ignoredErrors = null; } else { @@ -2004,6 +2240,9 @@ public void setIgnoredErrors(final @Nullable List ignoredErrors) { * @param pattern the string/regex pattern */ public void addIgnoredError(final @NotNull String pattern) { + if (rejectAfterSeal("addIgnoredError")) { + return; + } if (ignoredErrors == null) { ignoredErrors = new ArrayList<>(); } @@ -2027,6 +2266,9 @@ public int getMaxSpans() { */ @ApiStatus.Experimental public void setMaxSpans(int maxSpans) { + if (rejectAfterSeal("setMaxSpans")) { + return; + } this.maxSpans = maxSpans; } @@ -2045,6 +2287,9 @@ public boolean isEnableShutdownHook() { * @param enableShutdownHook true if enabled or false otherwise. */ public void setEnableShutdownHook(boolean enableShutdownHook) { + if (rejectAfterSeal("setEnableShutdownHook")) { + return; + } this.enableShutdownHook = enableShutdownHook; } @@ -2063,6 +2308,9 @@ public int getMaxCacheItems() { * @param maxCacheItems the maxCacheItems */ public void setMaxCacheItems(int maxCacheItems) { + if (rejectAfterSeal("setMaxCacheItems")) { + return; + } this.maxCacheItems = maxCacheItems; } @@ -2071,6 +2319,9 @@ public void setMaxCacheItems(int maxCacheItems) { } public void setMaxRequestBodySize(final @NotNull RequestSize maxRequestBodySize) { + if (rejectAfterSeal("setMaxRequestBodySize")) { + return; + } this.maxRequestBodySize = maxRequestBodySize; } @@ -2098,6 +2349,9 @@ public boolean isTraceSampling() { */ @Deprecated public void setTraceSampling(boolean traceSampling) { + if (rejectAfterSeal("setTraceSampling")) { + return; + } this.traceSampling = traceSampling; } @@ -2116,6 +2370,9 @@ public long getMaxTraceFileSize() { * @param maxTraceFileSize the max trace file size in bytes. */ public void setMaxTraceFileSize(long maxTraceFileSize) { + if (rejectAfterSeal("setMaxTraceFileSize")) { + return; + } this.maxTraceFileSize = maxTraceFileSize; } @@ -2135,6 +2392,9 @@ public void setMaxTraceFileSize(long maxTraceFileSize) { * @param transactionProfiler - the listener for operations when a transaction is started or ended */ public void setTransactionProfiler(final @Nullable ITransactionProfiler transactionProfiler) { + if (rejectAfterSeal("setTransactionProfiler")) { + return; + } // We allow to set the profiler only if it was not set before, and we don't allow to unset it. if (this.transactionProfiler == NoOpTransactionProfiler.getInstance() && transactionProfiler != null) { @@ -2157,6 +2417,9 @@ public void setTransactionProfiler(final @Nullable ITransactionProfiler transact * @param continuousProfiler - the continuous profiler */ public void setContinuousProfiler(final @Nullable IContinuousProfiler continuousProfiler) { + if (rejectAfterSeal("setContinuousProfiler")) { + return; + } // We allow to set the profiler only if it was not set before, and we don't allow to unset it. if (this.continuousProfiler == NoOpContinuousProfiler.getInstance() && continuousProfiler != null) { @@ -2202,6 +2465,9 @@ public boolean isContinuousProfilingEnabled() { * @param profilesSampler the callback */ public void setProfilesSampler(final @Nullable ProfilesSamplerCallback profilesSampler) { + if (rejectAfterSeal("setProfilesSampler")) { + return; + } this.profilesSampler = profilesSampler; } @@ -2222,6 +2488,9 @@ public void setProfilesSampler(final @Nullable ProfilesSamplerCallback profilesS * @param profilesSampleRate the sample rate */ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) { + if (rejectAfterSeal("setProfilesSampleRate")) { + return; + } if (!SampleRateUtils.isValidProfilesSampleRate(profilesSampleRate)) { throw new IllegalArgumentException( "The value " @@ -2248,6 +2517,9 @@ public void setProfilesSampleRate(final @Nullable Double profilesSampleRate) { * set them to null. */ public void setProfileSessionSampleRate(final @Nullable Double profileSessionSampleRate) { + if (rejectAfterSeal("setProfileSessionSampleRate")) { + return; + } if (!SampleRateUtils.isValidContinuousProfilesSampleRate(profileSessionSampleRate)) { throw new IllegalArgumentException( "The value " @@ -2269,6 +2541,9 @@ public void setProfileSessionSampleRate(final @Nullable Double profileSessionSam /** Sets the profiling lifecycle. */ public void setProfileLifecycle(final @NotNull ProfileLifecycle profileLifecycle) { + if (rejectAfterSeal("setProfileLifecycle")) { + return; + } this.profileLifecycle = profileLifecycle; if (profileLifecycle == ProfileLifecycle.TRACE && !isTracingEnabled()) { logger.log( @@ -2289,6 +2564,9 @@ public boolean isStartProfilerOnAppStart() { * Set if profiling can automatically be started as early as possible during the app lifecycle. */ public void setStartProfilerOnAppStart(final boolean startProfilerOnAppStart) { + if (rejectAfterSeal("setStartProfilerOnAppStart")) { + return; + } this.startProfilerOnAppStart = startProfilerOnAppStart; } @@ -2319,6 +2597,9 @@ public boolean isEnableLegacyProfiling() { * @param enableLegacyProfiling false to disable legacy profiling. */ public void setEnableLegacyProfiling(final boolean enableLegacyProfiling) { + if (rejectAfterSeal("setEnableLegacyProfiling")) { + return; + } this.enableLegacyProfiling = enableLegacyProfiling; } @@ -2337,6 +2618,9 @@ public long getDeadlineTimeout() { * @param deadlineTimeout the timeout in milliseconds */ public void setDeadlineTimeout(long deadlineTimeout) { + if (rejectAfterSeal("setDeadlineTimeout")) { + return; + } this.deadlineTimeout = deadlineTimeout; } @@ -2362,6 +2646,9 @@ public void setDeadlineTimeout(long deadlineTimeout) { } public void setProfilingTracesDirPath(final @Nullable String profilingTracesDirPath) { + if (rejectAfterSeal("setProfilingTracesDirPath")) { + return; + } this.profilingTracesDirPath = profilingTracesDirPath; } @@ -2378,6 +2665,9 @@ public void setProfilingTracesDirPath(final @Nullable String profilingTracesDirP } public void setTracePropagationTargets(final @Nullable List tracePropagationTargets) { + if (rejectAfterSeal("setTracePropagationTargets")) { + return; + } if (tracePropagationTargets == null) { this.tracePropagationTargets = null; } else { @@ -2407,6 +2697,9 @@ public boolean isPropagateTraceparent() { * @param propagateTraceparent true if enabled false otherwise */ public void setPropagateTraceparent(final boolean propagateTraceparent) { + if (rejectAfterSeal("setPropagateTraceparent")) { + return; + } this.propagateTraceparent = propagateTraceparent; } @@ -2415,6 +2708,9 @@ public boolean isStrictTraceContinuation() { } public void setStrictTraceContinuation(final boolean strictTraceContinuation) { + if (rejectAfterSeal("setStrictTraceContinuation")) { + return; + } this.strictTraceContinuation = strictTraceContinuation; } @@ -2423,6 +2719,9 @@ public void setStrictTraceContinuation(final boolean strictTraceContinuation) { } public void setOrgId(final @Nullable String orgId) { + if (rejectAfterSeal("setOrgId")) { + return; + } this.orgId = orgId; } @@ -2461,6 +2760,9 @@ public void setOrgId(final @Nullable String orgId) { * @param proguardUuid - the Proguard UUID */ public void setProguardUuid(final @Nullable String proguardUuid) { + if (rejectAfterSeal("setProguardUuid")) { + return; + } this.proguardUuid = proguardUuid; } @@ -2472,6 +2774,9 @@ public void setProguardUuid(final @Nullable String proguardUuid) { * @param bundleId Bundle ID generated by sentry-cli or the sentry-android-gradle-plugin */ public void addBundleId(final @Nullable String bundleId) { + if (rejectAfterSeal("addBundleId")) { + return; + } if (bundleId != null) { final @NotNull String trimmedBundleId = bundleId.trim(); if (!trimmedBundleId.isEmpty()) { @@ -2504,6 +2809,9 @@ public void addBundleId(final @Nullable String bundleId) { * @param contextTag - the context tag */ public void addContextTag(final @NotNull String contextTag) { + if (rejectAfterSeal("addContextTag")) { + return; + } this.contextTags.add(contextTag); } @@ -2522,6 +2830,9 @@ public void addContextTag(final @NotNull String contextTag) { * @param idleTimeout the idle timeout in millis or null. */ public void setIdleTimeout(final @Nullable Long idleTimeout) { + if (rejectAfterSeal("setIdleTimeout")) { + return; + } this.idleTimeout = idleTimeout; } @@ -2540,6 +2851,9 @@ public boolean isSendClientReports() { * @param sendClientReports true enables client reports; false disables them */ public void setSendClientReports(boolean sendClientReports) { + if (rejectAfterSeal("setSendClientReports")) { + return; + } this.sendClientReports = sendClientReports; if (sendClientReports) { @@ -2554,6 +2868,9 @@ public boolean isEnableUserInteractionTracing() { } public void setEnableUserInteractionTracing(boolean enableUserInteractionTracing) { + if (rejectAfterSeal("setEnableUserInteractionTracing")) { + return; + } this.enableUserInteractionTracing = enableUserInteractionTracing; } @@ -2562,6 +2879,9 @@ public boolean isEnableUserInteractionBreadcrumbs() { } public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBreadcrumbs) { + if (rejectAfterSeal("setEnableUserInteractionBreadcrumbs")) { + return; + } this.enableUserInteractionBreadcrumbs = enableUserInteractionBreadcrumbs; } @@ -2579,6 +2899,9 @@ public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBre */ @Deprecated public void setInstrumenter(final @NotNull Instrumenter instrumenter) { + if (rejectAfterSeal("setInstrumenter")) { + return; + } this.instrumenter = instrumenter; } @@ -2613,6 +2936,9 @@ public void setInstrumenter(final @NotNull Instrumenter instrumenter) { @ApiStatus.Internal public void setModulesLoader(final @Nullable IModulesLoader modulesLoader) { + if (rejectAfterSeal("setModulesLoader")) { + return; + } this.modulesLoader = modulesLoader != null ? modulesLoader : NoOpModulesLoader.getInstance(); } @@ -2629,6 +2955,9 @@ public void setModulesLoader(final @Nullable IModulesLoader modulesLoader) { @ApiStatus.Internal public void setDebugMetaLoader(final @Nullable IDebugMetaLoader debugMetaLoader) { + if (rejectAfterSeal("setDebugMetaLoader")) { + return; + } this.debugMetaLoader = debugMetaLoader != null ? debugMetaLoader : NoOpDebugMetaLoader.getInstance(); } @@ -2650,6 +2979,9 @@ public List getGestureTargetLocators() { * @param locators a list of {@link GestureTargetLocator} */ public void setGestureTargetLocators(@NotNull final List locators) { + if (rejectAfterSeal("setGestureTargetLocators")) { + return; + } gestureTargetLocators.clear(); gestureTargetLocators.addAll(locators); } @@ -2671,6 +3003,9 @@ public final List getViewHierarchyExporters() { * @param exporters a list of {@link ViewHierarchyExporter} */ public void setViewHierarchyExporters(@NotNull final List exporters) { + if (rejectAfterSeal("setViewHierarchyExporters")) { + return; + } viewHierarchyExporters.clear(); viewHierarchyExporters.addAll(exporters); } @@ -2680,6 +3015,9 @@ public void setViewHierarchyExporters(@NotNull final List } public void setThreadChecker(final @NotNull IThreadChecker threadChecker) { + if (rejectAfterSeal("setThreadChecker")) { + return; + } this.threadChecker = threadChecker; } @@ -2701,6 +3039,9 @@ public void setThreadChecker(final @NotNull IThreadChecker threadChecker) { @ApiStatus.Internal public void setCompositePerformanceCollector( final @NotNull CompositePerformanceCollector compositePerformanceCollector) { + if (rejectAfterSeal("setCompositePerformanceCollector")) { + return; + } this.compositePerformanceCollector = compositePerformanceCollector; } @@ -2719,6 +3060,9 @@ public boolean isEnableTimeToFullDisplayTracing() { * @param enableTimeToFullDisplayTracing if the time-to-full-display spans should be tracked. */ public void setEnableTimeToFullDisplayTracing(final boolean enableTimeToFullDisplayTracing) { + if (rejectAfterSeal("setEnableTimeToFullDisplayTracing")) { + return; + } this.enableTimeToFullDisplayTracing = enableTimeToFullDisplayTracing; } @@ -2736,6 +3080,9 @@ public void setEnableTimeToFullDisplayTracing(final boolean enableTimeToFullDisp @TestOnly public void setFullyDisplayedReporter( final @NotNull FullyDisplayedReporter fullyDisplayedReporter) { + if (rejectAfterSeal("setFullyDisplayedReporter")) { + return; + } this.fullyDisplayedReporter = fullyDisplayedReporter; } @@ -2751,6 +3098,9 @@ public void setFullyDisplayedReporter( @ApiStatus.Internal public void setAppStartExtender(final @Nullable IAppStartExtender appStartExtender) { + if (rejectAfterSeal("setAppStartExtender")) { + return; + } this.appStartExtender = appStartExtender != null ? appStartExtender : NoOpAppStartExtender.getInstance(); } @@ -2770,6 +3120,9 @@ public boolean isTraceOptionsRequests() { * @param traceOptionsRequests true if OPTIONS requests should be traced */ public void setTraceOptionsRequests(boolean traceOptionsRequests) { + if (rejectAfterSeal("setTraceOptionsRequests")) { + return; + } this.traceOptionsRequests = traceOptionsRequests; } @@ -2788,6 +3141,9 @@ public boolean isEnableDatabaseTransactionTracing() { * @param enableDatabaseTransactionTracing true if database transaction spans should be traced */ public void setEnableDatabaseTransactionTracing(boolean enableDatabaseTransactionTracing) { + if (rejectAfterSeal("setEnableDatabaseTransactionTracing")) { + return; + } this.enableDatabaseTransactionTracing = enableDatabaseTransactionTracing; } @@ -2806,6 +3162,9 @@ public boolean isEnableCacheTracing() { * @param enableCacheTracing true if cache operations should be traced */ public void setEnableCacheTracing(boolean enableCacheTracing) { + if (rejectAfterSeal("setEnableCacheTracing")) { + return; + } this.enableCacheTracing = enableCacheTracing; } @@ -2826,6 +3185,9 @@ public boolean isEnableQueueTracing() { * @param enableQueueTracing true to enable queue tracing */ public void setEnableQueueTracing(boolean enableQueueTracing) { + if (rejectAfterSeal("setEnableQueueTracing")) { + return; + } this.enableQueueTracing = enableQueueTracing; } @@ -2844,6 +3206,9 @@ public boolean isEnabled() { * @param enabled true if Sentry should be enabled */ public void setEnabled(boolean enabled) { + if (rejectAfterSeal("setEnabled")) { + return; + } this.enabled = enabled; } @@ -2871,6 +3236,9 @@ public boolean isSendModules() { * @param enablePrettySerializationOutput true if output should be pretty printed */ public void setEnablePrettySerializationOutput(boolean enablePrettySerializationOutput) { + if (rejectAfterSeal("setEnablePrettySerializationOutput")) { + return; + } this.enablePrettySerializationOutput = enablePrettySerializationOutput; } @@ -2892,6 +3260,9 @@ public boolean isEnableAppStartProfiling() { * @param enableAppStartProfiling true if app launches should be profiled. */ public void setEnableAppStartProfiling(boolean enableAppStartProfiling) { + if (rejectAfterSeal("setEnableAppStartProfiling")) { + return; + } this.enableAppStartProfiling = enableAppStartProfiling; } @@ -2901,6 +3272,9 @@ public void setEnableAppStartProfiling(boolean enableAppStartProfiling) { * @param sendModules true if modules should be sent. */ public void setSendModules(boolean sendModules) { + if (rejectAfterSeal("setSendModules")) { + return; + } this.sendModules = sendModules; } @@ -2923,6 +3297,9 @@ public void setSendModules(boolean sendModules) { */ @ApiStatus.Experimental public void addIgnoredSpanOrigin(String ignoredSpanOrigin) { + if (rejectAfterSeal("addIgnoredSpanOrigin")) { + return; + } if (ignoredSpanOrigins == null) { ignoredSpanOrigins = new ArrayList<>(); } @@ -2937,6 +3314,9 @@ public void addIgnoredSpanOrigin(String ignoredSpanOrigin) { */ @ApiStatus.Experimental public void setIgnoredSpanOrigins(final @Nullable List ignoredSpanOrigins) { + if (rejectAfterSeal("setIgnoredSpanOrigins")) { + return; + } if (ignoredSpanOrigins == null) { this.ignoredSpanOrigins = null; } else { @@ -2969,6 +3349,9 @@ public void setIgnoredSpanOrigins(final @Nullable List ignoredSpanOrigin */ @ApiStatus.Experimental public void addIgnoredCheckIn(String ignoredCheckIn) { + if (rejectAfterSeal("addIgnoredCheckIn")) { + return; + } if (ignoredCheckIns == null) { ignoredCheckIns = new ArrayList<>(); } @@ -2982,6 +3365,9 @@ public void addIgnoredCheckIn(String ignoredCheckIn) { */ @ApiStatus.Experimental public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { + if (rejectAfterSeal("setIgnoredCheckIns")) { + return; + } if (ignoredCheckIns == null) { this.ignoredCheckIns = null; } else { @@ -3014,6 +3400,9 @@ public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { */ @ApiStatus.Experimental public void addIgnoredTransaction(String ignoredTransaction) { + if (rejectAfterSeal("addIgnoredTransaction")) { + return; + } if (ignoredTransactions == null) { ignoredTransactions = new ArrayList<>(); } @@ -3028,6 +3417,9 @@ public void addIgnoredTransaction(String ignoredTransaction) { */ @ApiStatus.Experimental public void setIgnoredTransactions(final @Nullable List ignoredTransactions) { + if (rejectAfterSeal("setIgnoredTransactions")) { + return; + } if (ignoredTransactions == null) { this.ignoredTransactions = null; } else { @@ -3056,6 +3448,9 @@ public void setIgnoredTransactions(final @Nullable List ignoredTransacti */ @ApiStatus.Internal public void setDateProvider(final @NotNull SentryDateProvider dateProvider) { + if (rejectAfterSeal("setDateProvider")) { + return; + } this.dateProvider.setValue(dateProvider); } @@ -3066,6 +3461,9 @@ public void setDateProvider(final @NotNull SentryDateProvider dateProvider) { */ @ApiStatus.Internal public void addPerformanceCollector(final @NotNull IPerformanceCollector collector) { + if (rejectAfterSeal("addPerformanceCollector")) { + return; + } performanceCollectors.add(collector); } @@ -3086,6 +3484,9 @@ public IConnectionStatusProvider getConnectionStatusProvider() { public void setConnectionStatusProvider( final @NotNull IConnectionStatusProvider connectionStatusProvider) { + if (rejectAfterSeal("setConnectionStatusProvider")) { + return; + } this.connectionStatusProvider = connectionStatusProvider; } @@ -3097,11 +3498,17 @@ public IBackpressureMonitor getBackpressureMonitor() { @ApiStatus.Internal public void setBackpressureMonitor(final @NotNull IBackpressureMonitor backpressureMonitor) { + if (rejectAfterSeal("setBackpressureMonitor")) { + return; + } this.backpressureMonitor = backpressureMonitor; } @ApiStatus.Experimental public void setEnableBackpressureHandling(final boolean enableBackpressureHandling) { + if (rejectAfterSeal("setEnableBackpressureHandling")) { + return; + } this.enableBackpressureHandling = enableBackpressureHandling; } @@ -3113,6 +3520,9 @@ public IVersionDetector getVersionDetector() { @ApiStatus.Internal public void setVersionDetector(final @NotNull IVersionDetector versionDetector) { + if (rejectAfterSeal("setVersionDetector")) { + return; + } this.versionDetector = versionDetector; } @@ -3129,6 +3539,9 @@ public int getProfilingTracesHz() { /** Sets the rate the profiler will sample rates at. 100 hz means 100 traces in 1 second. */ @ApiStatus.Internal public void setProfilingTracesHz(final int profilingTracesHz) { + if (rejectAfterSeal("setProfilingTracesHz")) { + return; + } this.profilingTracesHz = profilingTracesHz; } @@ -3144,6 +3557,9 @@ public long getSessionFlushTimeoutMillis() { @ApiStatus.Internal public void setSessionFlushTimeoutMillis(final long sessionFlushTimeoutMillis) { + if (rejectAfterSeal("setSessionFlushTimeoutMillis")) { + return; + } this.sessionFlushTimeoutMillis = sessionFlushTimeoutMillis; } @@ -3167,6 +3583,9 @@ public String getSpotlightConnectionUrl() { @ApiStatus.Experimental public void setSpotlightConnectionUrl(final @Nullable String spotlightConnectionUrl) { + if (rejectAfterSeal("setSpotlightConnectionUrl")) { + return; + } this.spotlightConnectionUrl = spotlightConnectionUrl; } @@ -3177,6 +3596,9 @@ public boolean isEnableSpotlight() { @ApiStatus.Experimental public void setEnableSpotlight(final boolean enableSpotlight) { + if (rejectAfterSeal("setEnableSpotlight")) { + return; + } this.enableSpotlight = enableSpotlight; } @@ -3185,6 +3607,9 @@ public boolean isEnableScopePersistence() { } public void setEnableScopePersistence(final boolean enableScopePersistence) { + if (rejectAfterSeal("setEnableScopePersistence")) { + return; + } this.enableScopePersistence = enableScopePersistence; } @@ -3194,6 +3619,9 @@ public void setEnableScopePersistence(final boolean enableScopePersistence) { @ApiStatus.Experimental public void setCron(@Nullable Cron cron) { + if (rejectAfterSeal("setCron")) { + return; + } this.cron = cron; } @@ -3207,6 +3635,9 @@ public ExperimentalOptions getExperimental() { } public void setReplayController(final @Nullable ReplayController replayController) { + if (rejectAfterSeal("setReplayController")) { + return; + } this.replayController = replayController != null ? replayController : NoOpReplayController.getInstance(); } @@ -3218,6 +3649,9 @@ public void setReplayController(final @Nullable ReplayController replayControlle @ApiStatus.Experimental public void setDistributionController(final @Nullable IDistributionApi distributionController) { + if (rejectAfterSeal("setDistributionController")) { + return; + } this.distributionController = distributionController != null ? distributionController : NoOpDistributionApi.getInstance(); } @@ -3229,10 +3663,16 @@ public boolean isEnableScreenTracking() { @ApiStatus.Experimental public void setEnableScreenTracking(final boolean enableScreenTracking) { + if (rejectAfterSeal("setEnableScreenTracking")) { + return; + } this.enableScreenTracking = enableScreenTracking; } public void setDefaultScopeType(final @NotNull ScopeType scopeType) { + if (rejectAfterSeal("setDefaultScopeType")) { + return; + } this.defaultScopeType = scopeType; } @@ -3242,6 +3682,9 @@ public void setDefaultScopeType(final @NotNull ScopeType scopeType) { @ApiStatus.Internal public void setInitPriority(final @NotNull InitPriority initPriority) { + if (rejectAfterSeal("setInitPriority")) { + return; + } this.initPriority = initPriority; } @@ -3260,6 +3703,9 @@ public void setInitPriority(final @NotNull InitPriority initPriority) { * @param forceInit true = replace previous init and options */ public void setForceInit(final boolean forceInit) { + if (rejectAfterSeal("setForceInit")) { + return; + } this.forceInit = forceInit; } @@ -3281,6 +3727,9 @@ public boolean isForceInit() { * @param globalHubMode true = automatic scope forking is disabled */ public void setGlobalHubMode(final @Nullable Boolean globalHubMode) { + if (rejectAfterSeal("setGlobalHubMode")) { + return; + } this.globalHubMode = globalHubMode; } @@ -3300,6 +3749,9 @@ public void setGlobalHubMode(final @Nullable Boolean globalHubMode) { * @param openTelemetryMode the mode */ public void setOpenTelemetryMode(final @NotNull SentryOpenTelemetryMode openTelemetryMode) { + if (rejectAfterSeal("setOpenTelemetryMode")) { + return; + } this.openTelemetryMode = openTelemetryMode; } @@ -3313,6 +3765,9 @@ public SentryReplayOptions getSessionReplay() { } public void setSessionReplay(final @NotNull SentryReplayOptions sessionReplayOptions) { + if (rejectAfterSeal("setSessionReplay")) { + return; + } this.sessionReplay = sessionReplayOptions; } @@ -3321,11 +3776,17 @@ public void setSessionReplay(final @NotNull SentryReplayOptions sessionReplayOpt } public void setFeedbackOptions(final @NotNull SentryFeedbackOptions feedbackOptions) { + if (rejectAfterSeal("setFeedbackOptions")) { + return; + } this.feedbackOptions = feedbackOptions; } @ApiStatus.Experimental public void setCaptureOpenTelemetryEvents(final boolean captureOpenTelemetryEvents) { + if (rejectAfterSeal("setCaptureOpenTelemetryEvents")) { + return; + } this.captureOpenTelemetryEvents = captureOpenTelemetryEvents; } @@ -3349,6 +3810,9 @@ public boolean isCaptureOpenTelemetryEvents() { * @param socketTagger the socket tagger */ public void setSocketTagger(final @Nullable ISocketTagger socketTagger) { + if (rejectAfterSeal("setSocketTagger")) { + return; + } this.socketTagger = socketTagger != null ? socketTagger : NoOpSocketTagger.getInstance(); } @@ -3798,6 +4262,9 @@ private void addPackageInfo() { @ApiStatus.Internal public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { + if (rejectAfterSeal("setSpanFactory")) { + return; + } this.spanFactory = spanFactory; } @@ -3819,6 +4286,9 @@ public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { */ @ApiStatus.Experimental public void setScopesStorageFactory(final @Nullable IScopesStorageFactory scopesStorageFactory) { + if (rejectAfterSeal("setScopesStorageFactory")) { + return; + } this.scopesStorageFactory = scopesStorageFactory; } @@ -3829,6 +4299,9 @@ public void setScopesStorageFactory(final @Nullable IScopesStorageFactory scopes @ApiStatus.Experimental public void setLogs(@NotNull SentryOptions.Logs logs) { + if (rejectAfterSeal("setLogs")) { + return; + } this.logs = logs; } @@ -3837,6 +4310,9 @@ public void setLogs(@NotNull SentryOptions.Logs logs) { } public void setMetrics(@NotNull SentryOptions.Metrics metrics) { + if (rejectAfterSeal("setMetrics")) { + return; + } this.metrics = metrics; } @@ -4134,6 +4610,9 @@ SentryMetricsEvent execute( @ApiStatus.Experimental public void setDistribution(final @NotNull DistributionOptions distribution) { + if (rejectAfterSeal("setDistribution")) { + return; + } this.distribution = distribution != null ? distribution : new DistributionOptions(); } diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index e4c4b447cf6..d8e193801c6 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -31,7 +31,9 @@ import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import io.sentry.protocol.ViewHierarchy import io.sentry.test.callMethod +import io.sentry.test.getProperty import io.sentry.test.injectForField +import io.sentry.transport.AsyncHttpTransport import io.sentry.transport.ITransport import io.sentry.transport.ITransportGate import io.sentry.util.HintUtils @@ -1139,10 +1141,10 @@ class SentryClientTest { } @Test - fun `when transport factory is NoOp, it should initialize it`() { + fun `when transport factory is NoOp, the client falls back to the async http transport`() { fixture.sentryOptions.setTransportFactory(NoOpTransportFactory.getInstance()) - fixture.getSut() - assertTrue(fixture.sentryOptions.transportFactory is AsyncHttpTransportFactory) + val sut = fixture.getSut() + assertTrue(sut.getProperty("transport") is AsyncHttpTransport) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryOptionsSealTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsSealTest.kt new file mode 100644 index 00000000000..2c245ea72e3 --- /dev/null +++ b/sentry/src/test/java/io/sentry/SentryOptionsSealTest.kt @@ -0,0 +1,89 @@ +package io.sentry + +import com.google.common.truth.Truth.assertThat +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class SentryOptionsSealTest { + @AfterTest + fun tearDown() { + Sentry.close() + } + + @Test + fun `setters apply before the options are sealed`() { + val options = SentryOptions() + options.environment = "staging" + assertThat(options.environment).isEqualTo("staging") + } + + @Test + fun `setters are ignored once the options are sealed`() { + val options = SentryOptions() + options.environment = "staging" + options.seal() + + options.environment = "production" + + assertThat(options.environment).isEqualTo("staging") + } + + @Test + fun `setters throw once the options are sealed and debug is enabled`() { + val options = SentryOptions() + options.isDebug = true + options.seal() + + assertFailsWith { options.environment = "production" } + } + + @Test + fun `unseal makes the options writable again`() { + val options = SentryOptions() + options.seal() + options.unseal() + + options.environment = "production" + + assertThat(options.environment).isEqualTo("production") + } + + // SpotlightIntegration claims and releases this slot from register()/close(), both of which run + // after the seal. See the exemption note on the setter. + @Test + fun `beforeEnvelopeCallback stays writable after the seal`() { + val options = SentryOptions() + val callback = SentryOptions.BeforeEnvelopeCallback { _, _ -> } + options.seal() + + options.setBeforeEnvelopeCallback(callback) + + assertThat(options.beforeEnvelopeCallback).isSameInstanceAs(callback) + } + + // The SDK can be restarted with the same options instance, and init has to be able to re-wire it. + @Test + fun `restarting the SDK with the same options instance re-opens them for wiring`() { + val options = SentryOptions() + options.dsn = "https://key@sentry.io/proj" + + Sentry.init(options) + Sentry.close() + Sentry.init(options) + + assertThat(options.executorService.isClosed).isFalse() + assertThat(Sentry.isEnabled()).isTrue() + } + + @Test + fun `Sentry init seals the options it was given`() { + val options = SentryOptions() + options.dsn = "https://key@sentry.io/proj" + Sentry.init(options) + + options.environment = "changed-after-init" + + assertThat(options.environment).isNotEqualTo("changed-after-init") + } +}