Skip to content

[Java] Remove CT_CONSTRUCTOR_THROW from the global SpotBugs filter - #40008

Open
mxtymoshyk wants to merge 1 commit into
apache:masterfrom
mxtymoshyk:spotbugs-ct-constructor-throw
Open

[Java] Remove CT_CONSTRUCTOR_THROW from the global SpotBugs filter#40008
mxtymoshyk wants to merge 1 commit into
apache:masterfrom
mxtymoshyk:spotbugs-ct-constructor-throw

Conversation

@mxtymoshyk

Copy link
Copy Markdown
Contributor

Part of #35312.

The global SpotBugs filter suppressed CT_CONSTRUCTOR_THROW in every module, so a constructor that throws after publishing this was never reported anywhere. This deletes those three filter lines and fixes everything the detector then found.

I used two fixes. Where the class is not public, I marked it final. SpotBugs' ConstructorThrow detector returns early on a final class, so the warning goes away because the problem is gone: a finalizer attack needs an attacker-supplied subclass, and a final class cannot have one. Every other class gets a @SuppressFBWarnings whose justification says why it has to stay extensible.

A class was made final only when it is neither abstract nor public. Code outside the repo can subclass a package-private class only by declaring itself inside a Beam package, which is unsupported and breaks under shaded jars, so sealing it cannot break a downstream user.

Public classes annotated @Internal get the suppression as well. @Internal withdraws the compatibility guarantee, but the class is still reachable, so a forked runner may already extend it and sealing it would break that fork at compile time. Six classes fall in this category: BoundedTrieData, ExplicitShardedFile, PCollectionViews.SingletonViewFn, PCollectionViews.SingletonViewFn2, FnApiDoFnRunner, and SplittableSplitAndSizeRestrictionsDoFnRunner.

I also checked that nothing in the repo extends any class I sealed. That check only proves the tree still compiles. It is not by itself a reason to seal a class, and it says nothing about out-of-tree code.

Verified locally with ./gradlew spotbugsMain spotlessJavaCheck --continue: 140 modules pass spotbugsMain, 190 pass spotlessJavaCheck, and no report contains a CT_CONSTRUCTOR_THROW finding.


Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:

  • Mention the appropriate issue in your description (for example: addresses #123), if applicable. This will automatically add a link to the pull request in the issue. If you would like the issue to automatically close on merging the pull request, comment fixes #<ISSUE NUMBER> instead.
  • Update CHANGES.md with noteworthy changes.
  • If this contribution is large, please file an Apache Individual Contributor License Agreement.

See the Contributor Guide for more tips on how to make review process smoother.

To check the build health, please visit https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md

GitHub Actions Tests Status (on master branch)

Build python source distribution and wheels
Python tests
Java tests
Go tests

See CI.md for more information about GitHub Actions CI or the workflows README to see a list of phrases to trigger workflows.

The global filter suppressed CT_CONSTRUCTOR_THROW repo-wide, so a constructor
that throws after publishing `this` was never flagged in any module. This
removes the blanket suppression and resolves every resulting finding.

Two remediations were used:

  - Marked the class `final` where it is not public, so no code outside the
    repo can subclass it in a supported way. SpotBugs' detector short-circuits
    on final classes, so the finding genuinely goes away: a finalizer attack
    needs an attacker-supplied subclass, which a final class cannot have.

  - Added a class-level @SuppressFBWarnings with a justification everywhere
    `final` would be a source-incompatible change: every public class, every
    abstract class, and anything already subclassed inside Beam.

A public class annotated @internal still gets the suppression rather than
`final`. @internal disclaims the compatibility guarantee, but the class is
reachable, so a forked runner may already extend it and sealing it would break
that fork at compile time.

The per-class suppressions are not equivalent to the global filter: a new
throwing constructor added anywhere now fails spotbugsMain instead of being
silently ignored.
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Checks are failing. Will not request review until checks are succeeding. If you'd like to override that behavior, comment assign set of reviewers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant