Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6e0dcc8
Migrate JSR305 nullness annotations to JSpecify
Apisapple Mar 6, 2026
aafb253
Merge pull request #3 from Apisapple/feature/Replace-JSR305-to-JSpecify
Apisapple Mar 7, 2026
1c5c38f
Merge branch 'main' into main
siladu Mar 9, 2026
2cccbb1
Merge branch 'hyperledger:main' into main
Apisapple Mar 9, 2026
d446b70
Merge branch 'hyperledger:main' into main
Apisapple Mar 10, 2026
3ee264f
Merge branch 'besu-eth:main' into main
Apisapple Mar 11, 2026
968f49e
feat(util): add opt-in NullAway configuration for Error Prone
Apisapple Mar 11, 2026
a45214e
Merge branch 'besu-eth:main' into main
Apisapple Mar 11, 2026
8c884b3
Merge branch 'besu-eth:main' into feature/nullaway-util
Apisapple Mar 11, 2026
7ea098b
feat(util): enable NullAway with comprehensive nullability fixes
Apisapple Mar 11, 2026
3faa60c
Merge branch 'feature/nullaway-util' of https://github.com/Apisapple/…
Apisapple Mar 11, 2026
a89a961
style(util): format code for improved readability in StackTraceMatchF…
Apisapple Mar 11, 2026
556f767
fix(util): PlatformDetector.normalizeGLibcVersion returns UNKNOWN not…
Apisapple Mar 11, 2026
e139c95
Merge branch 'besu-eth:main' into main
Apisapple Mar 12, 2026
29ab5e1
Merge branch 'besu-eth:main' into feature/nullaway-util
Apisapple Mar 12, 2026
e9010a4
Merge branch 'besu-eth:main' into main
Apisapple Mar 12, 2026
663be1f
Merge branch 'besu-eth:main' into feature/nullaway-util
Apisapple Mar 12, 2026
fca589a
Merge branch 'besu-eth:main' into main
Apisapple Mar 13, 2026
03b6cd9
Merge branch 'besu-eth:main' into feature/nullaway-util
Apisapple Mar 13, 2026
04a98fd
Merge pull request #4 from Apisapple/feature/nullaway-util
Apisapple Mar 13, 2026
0ff7f02
fix(util): update getGlibc to return null instead of UNKNOWN
Apisapple Mar 13, 2026
ce10ea2
fix(util): remove NullAway flag from util compile command
Apisapple Mar 13, 2026
07538d3
docs(util): update getGlibc Javadoc to clarify return value can be null
Apisapple Mar 13, 2026
77be9e0
docs(util): update NullAway optional check job name for clarity
Apisapple Mar 13, 2026
3259c91
fix(util): update getGlibc to always return a value instead of null
Apisapple Mar 13, 2026
97efaa6
fix(util): remove NullAway optional check job from pre-review workflow
Apisapple Mar 13, 2026
9e69fc3
Merge pull request #5 from Apisapple/feature/fix-nullaway-util
Apisapple Mar 14, 2026
4fb6640
Fix YAML indentation in pre-review workflow
Apisapple Mar 14, 2026
f0043a3
Merge branch 'besu-eth:main' into main
Apisapple Mar 18, 2026
52fbc15
Merge branch 'besu-eth:main' into main
Apisapple Mar 19, 2026
216158f
Merge branch 'besu-eth:main' into main
Apisapple Mar 23, 2026
24b63ae
Merge branch 'besu-eth:main' into main
Apisapple Mar 26, 2026
c54fe44
Merge branch 'besu-eth:main' into main
Apisapple Apr 2, 2026
55fe3a2
Merge branch 'main' into main
Apisapple Apr 3, 2026
7fe9235
feat: Improve version metadata null-safety and bump NullAway to 0.13.1
Apisapple Apr 3, 2026
ba09e84
Merge branch 'besu-eth:main' into main
Apisapple Apr 7, 2026
7e951b8
Merge branch 'besu-eth:main' into main
Apisapple Apr 7, 2026
822ae1b
chroe: Add jspecify compileOnly dependency
Apisapple Apr 8, 2026
b7a9122
feat: Use shortVersion() and improve StackTraceMatchFilter
Apisapple Apr 8, 2026
ba1502e
docs: Fix Javadoc reference for UNKNOWN constant
Apisapple Apr 8, 2026
27c61f0
chroe: Remove old verification metadata entries
Apisapple Apr 8, 2026
bf86c2b
Merge pull request #7 from Apisapple/feature/nullaway-util
Apisapple Apr 8, 2026
fdccaad
Use UNKNOWN constant in version regex tests
Apisapple Apr 8, 2026
98d7b89
Use BesuVersionUtils.UNKNOWN constant
Apisapple Apr 8, 2026
327cd32
docs: Clarify rootCause javadoc null behavior
Apisapple Apr 8, 2026
0a49504
style: Wrap long regex in BesuVersionUtilsTest
Apisapple Apr 8, 2026
ee34253
Merge pull request #8 from Apisapple/feature/nullaway-util
Apisapple Apr 8, 2026
b8bf18b
chore: Use compileOnlyApi for jspecify dependency
Apisapple Apr 8, 2026
12e7836
feat: Quote UNKNOWN in version regex tests
Apisapple Apr 8, 2026
6b4d169
style: Apply Spotless formatting
Apisapple Apr 8, 2026
6d73567
Merge pull request #9 from Apisapple/feature/nullaway-util
Apisapple Apr 8, 2026
feda84d
Merge branch 'main' into main
Apisapple Apr 8, 2026
7c95c22
Merge branch 'besu-eth:main' into main
Apisapple Apr 9, 2026
6e0adbb
Merge branch 'besu-eth:main' into main
Apisapple Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@
public class VersionMetadata implements Comparable<VersionMetadata> {
private static final Logger LOG = LoggerFactory.getLogger(VersionMetadata.class);

/** Represents an unknown Besu version in the version metadata file */
public static final String BESU_VERSION_UNKNOWN = "UNKNOWN";

private static final String METADATA_FILENAME = "VERSION_METADATA.json";
private static final ObjectMapper MAPPER = new ObjectMapper();
private final String besuVersion;
Expand All @@ -46,9 +43,7 @@ public class VersionMetadata implements Comparable<VersionMetadata> {
* @return the version of Besu
*/
public static String getRuntimeVersionString() {
return BesuVersionUtils.shortVersion() == null
? BESU_VERSION_UNKNOWN
: BesuVersionUtils.shortVersion();
return BesuVersionUtils.shortVersion();
}

public static VersionMetadata getRuntimeVersion() {
Expand Down Expand Up @@ -97,7 +92,7 @@ private static VersionMetadata resolveVersionMetadata(final File metadataFile)
versionMetadata = MAPPER.readValue(metadataFile, VersionMetadata.class);
LOG.info("Existing version data detected. Besu version {}", versionMetadata.besuVersion);
} catch (FileNotFoundException fnfe) {
versionMetadata = new VersionMetadata(BESU_VERSION_UNKNOWN);
versionMetadata = new VersionMetadata(BesuVersionUtils.UNKNOWN);
} catch (JsonProcessingException jpe) {
throw new IllegalStateException(
String.format("Invalid metadata file %s", metadataFile.getAbsolutePath()), jpe);
Expand All @@ -118,7 +113,7 @@ public static void versionCompatibilityChecks(
final boolean enforceCompatibilityProtection, final Path dataDir) throws IOException {
final VersionMetadata metadataVersion = VersionMetadata.lookUpFrom(dataDir);
final VersionMetadata runtimeVersion = getRuntimeVersion();
if (metadataVersion.getBesuVersion().equals(VersionMetadata.BESU_VERSION_UNKNOWN)) {
if (metadataVersion.getBesuVersion().equals(BesuVersionUtils.UNKNOWN)) {
// The version isn't known, potentially because the file doesn't exist. Write the latest
// version to the metadata file.
LOG.info(
Expand Down
52 changes: 52 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,16 @@
<sha256 value="c54beff37f6d42d43e14722d54a522c9ad51ef97fc5b79277e62a3ebf882f3bb" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="30.1-jre">
<artifact name="guava-30.1-jre.pom">
<sha256 value="9646d4cd50094d4abe507e555d3f76d77e34a4c5566b22fb130ef55d4ebbe927" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="31.1-jre">
<artifact name="guava-31.1-jre.pom">
<sha256 value="9193d07bf4f660108d7358e58b27d21b44e34e80d6734e98e21916376f270de2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="32.1.2-jre">
<artifact name="guava-32.1.2-jre.module">
<sha256 value="e40cf085ced05ab18f9c94e7c7bc197e1cdb695bc939afc344ab24c1414d6c7e" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -1433,6 +1443,16 @@
<sha256 value="f8698ab46ca996ce889c1afc8ca4f25eb8ac6b034dc898d4583742360016cc04" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="30.1-jre">
<artifact name="guava-parent-30.1-jre.pom">
<sha256 value="e2afb747ebc4fe2328d6a90fa88c5d8a83bb1e32061bb9b10ff43e2c47ad6e73" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="31.1-jre">
<artifact name="guava-parent-31.1-jre.pom">
<sha256 value="4439626783b44ad25ef05ff07621dd4bb796cc4eb4f2966a4a461fea4130e0fc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava-parent" version="32.1.2-jre">
<artifact name="guava-parent-32.1.2-jre.pom">
<sha256 value="88e9cb007335ab5fdb314a6e3c987734ec2308c9a063ff747c7469189d0a92bf" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -1912,6 +1932,14 @@
<sha256 value="61b4d7c515a0894ffad925fd7052620c1425a86433fd35113b5fab0de890a57f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.uber.nullaway" name="nullaway" version="0.13.1">
<artifact name="nullaway-0.13.1.jar">
<sha256 value="be9ad201b92de86c3e493a45b0861b1b3dd955d8e7dcb11f8d80d18d7425d164" origin="Generated by Gradle"/>
</artifact>
<artifact name="nullaway-0.13.1.module">
<sha256 value="b0e0bdc585ea947ee50effa0fb01fbbcf2f998d57a6b89559f2446c8358a19ab" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="commons-beanutils" name="commons-beanutils" version="1.9.4">
<artifact name="commons-beanutils-1.9.4.jar">
<sha256 value="7d938c81789028045c08c065e94be75fc280527620d5bd62b519d5838532368a" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -5897,6 +5925,30 @@
<sha256 value="9313bf53b3efd8aaca266eea8b96e307976b65c0b16510cc6f02319fbaebed43" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.48.0">
<artifact name="checker-qual-3.48.0.jar">
<sha256 value="c2b4dcb51ab3ea99bac3d1034d9dca9a8208d85ae3256527a85718340a00eb32" origin="Generated by Gradle"/>
</artifact>
<artifact name="checker-qual-3.48.0.module">
<sha256 value="39ea1dabaec530bafbb078afc28e4e280618f93e111ebe98d72e09d601084866" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="checker-qual" version="3.53.0">
<artifact name="checker-qual-3.53.0.jar">
<sha256 value="7ca002815d92fad79e966b375c2ee7b2b4bf953024bc9a5d5e0c59df13ff5af8" origin="Generated by Gradle"/>
</artifact>
<artifact name="checker-qual-3.53.0.module">
<sha256 value="119d69d41349783cc1ebd392b24b61d6f380860147a80b2885b8f7fca3ab13bf" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.checkerframework" name="dataflow-nullaway" version="3.53.0">
<artifact name="dataflow-nullaway-3.53.0.jar">
<sha256 value="d0bc6e121f27a803e09404ae2f247f0e2b1f6022897f801cd792ebf375edec27" origin="Generated by Gradle"/>
</artifact>
<artifact name="dataflow-nullaway-3.53.0.pom">
<sha256 value="ad735e9e8c90d2603350ce6c0d3cb39e1cbd9dc13eec9c3ba3deab2161b744db" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.codehaus.groovy" name="groovy-bom" version="3.0.21">
<artifact name="groovy-bom-3.0.21.pom">
<sha256 value="92cc36affd20f568b5092c0b94ecf585ddeb0a281b6c8ba7256570bb185d6534" origin="Generated by Gradle"/>
Expand Down
14 changes: 14 additions & 0 deletions util/build.gradle
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add
compileOnly 'org.jspecify:jspecify'
in here similar to other modules' build.gradle e.g.

compileOnly 'org.jspecify:jspecify'
so not relying on transitive deps which might yield a different version perhaps.

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ compileJava {
'-Alog4j.graalvm.groupId=' + project.group,
'-Alog4j.graalvm.artifactId=' + project.name
]
options.errorprone {
check('NullAway', net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
option('NullAway:AnnotatedPackages', 'org.hyperledger.besu.util')
Comment thread
siladu marked this conversation as resolved.
}
}

tasks.named('compileTestJava', JavaCompile).configure {
options.errorprone {
check('NullAway', net.ltgt.gradle.errorprone.CheckSeverity.OFF)
}
}

dependencies {
Expand All @@ -43,6 +53,8 @@ dependencies {
annotationProcessor 'org.apache.logging.log4j:log4j-core'
annotationProcessor 'com.google.dagger:dagger-compiler'

errorprone 'com.uber.nullaway:nullaway:0.13.1'

implementation 'com.google.guava:guava'
implementation 'com.google.dagger:dagger'
implementation 'com.github.ben-manes.caffeine:caffeine'
Expand All @@ -55,6 +67,8 @@ dependencies {
implementation 'org.bouncycastle:bcpkix-jdk18on'
implementation 'org.xerial.snappy:snappy-java'

compileOnlyApi 'org.jspecify:jspecify'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: think I still prefer compileOnly since I expect every module to eventually add this anyway. Is there a reason I've missed why compileOnlyApi is better?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. I decided to keep compileOnlyApi for now, as it is currently used only in the util module. Adding compileOnly manually to every module could lead to omissions, so I felt this was the safer choice at this stage. Once all modules adopt it consistently, we can switch to a shared compileOnly configuration.

Copy link
Copy Markdown
Contributor Author

@Apisapple Apisapple Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve verified that updating the section you pointed out to compileOnly does not cause any issues during the build.


testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
*/
public final class BesuVersionUtils {
private static final Logger LOG = LoggerFactory.getLogger(BesuVersionUtils.class);

/** Sentinel value used when the version or commit metadata is not available. */
public static final String UNKNOWN = "UNKNOWN";

private static final String CLIENT = "besu";
private static final String VERSION;
private static final String OS = PlatformDetector.getOS();
Expand Down Expand Up @@ -66,16 +70,17 @@ public final class BesuVersionUtils {
Optional.ofNullable(implVersion).orElse("NONE/null"));
}
}
COMMIT = commit;
VERSION = implVersion;
COMMIT = commit != null ? commit : UNKNOWN;
VERSION = implVersion != null ? implVersion : UNKNOWN;
}

private BesuVersionUtils() {}

/**
* Generate version-only Besu version
*
* @return Besu version in format such as "v23.1.0" or "v23.1.1-dev-ac23d311"
* @return Besu version in format such as "v23.1.0" or "v23.1.1-dev-ac23d311", or {@value
* #UNKNOWN} if not available
*/
public static String shortVersion() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check usage of this, should be able to remove some null checks e.g. in VersionMetadata.
This is effectively push the VERSION sentinel down to this level, which makes me wonder if there was a good reason this was done at the VersionMetadata level instead...we'll need to manually test this I think to check versioning working as expected

return VERSION;
Expand Down Expand Up @@ -117,7 +122,7 @@ public static String nodeName(final Optional<String> maybeIdentity) {
/**
* Generate the commit hash for this besu version
*
* @return the commit hash for this besu version
* @return the commit hash for this besu version, or {@value #UNKNOWN} if not available
*/
public static String commit() {
return COMMIT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.util;

import com.google.common.base.Throwables;
import org.jspecify.annotations.Nullable;

/** The Exception utils. */
public class ExceptionUtils {
Expand All @@ -25,9 +26,9 @@ private ExceptionUtils() {}
* Returns the root cause of an exception
*
* @param throwable the throwable whose root cause we want to find
* @return The root cause
* @return The root cause, or {@code null} if the input is {@code null}
*/
public static Throwable rootCause(final Throwable throwable) {
public static @Nullable Throwable rootCause(final @Nullable Throwable throwable) {
return throwable != null ? Throwables.getRootCause(throwable) : null;
Comment thread
siladu marked this conversation as resolved.
}
Comment on lines 28 to 33
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc no longer matches the method contract: the parameter and return value are now nullable. Update the @param and @return docs to reflect that null is accepted and that the method may return null when passed null.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.jspecify.annotations.Nullable;

/**
* A memory-bound cache that uses Caffeine to limit the size of the cache based on the memory
Expand Down Expand Up @@ -61,7 +62,7 @@ public void put(final K key, final V value) {
* @param key the key whose associated value is to be returned
* @return the value associated with the key, or null if not present
*/
public V getIfPresent(final K key) {
public @Nullable V getIfPresent(final K key) {
return cache.getIfPresent(key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BiFunction;

Expand Down Expand Up @@ -51,10 +51,16 @@ public RollingFileWriter(
currentSize = 0;
fileNumber = 0;
final Path firstOutputFile = filenameGenerator.apply(fileNumber, compressed);
final File parentDir = firstOutputFile.getParent().toFile();
if (!parentDir.exists()) {
//noinspection ResultOfMethodCallIgnored
parentDir.mkdirs();
final Path parentPath = firstOutputFile.getParent();
if (parentPath != null) {
try {
Files.createDirectories(parentPath);
} catch (final IOException e) {
final FileNotFoundException fnfe =
new FileNotFoundException("Unable to create directory for rolling file: " + parentPath);
fnfe.initCause(e);
throw fnfe;
Comment on lines +58 to +62
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping a directory-creation IOException as a FileNotFoundException is a bit misleading (the directory may be unreadable, permission denied, invalid path, etc., not “file not found”). If the constructor/method signature allows, prefer throwing IOException directly (or UncheckedIOException if you can’t). If you must keep FileNotFoundException, consider using a message that clearly indicates this is a directory creation failure and ensure callers can still distinguish it (e.g., via a dedicated exception type or documented cause inspection).

Copilot uses AI. Check for mistakes.
}
Comment thread
siladu marked this conversation as resolved.
Comment thread
siladu marked this conversation as resolved.
}
out = new FileOutputStream(firstOutputFile.toFile());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.util.log4j.plugin;

import java.util.Arrays;
import java.util.Objects;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
Expand All @@ -25,6 +26,7 @@
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
import org.jspecify.annotations.Nullable;

/** Matches a text in the stack trace */
@Plugin(
Expand All @@ -34,11 +36,11 @@
printObject = true)
public class StackTraceMatchFilter extends AbstractFilter {
private final String stackContains;
private final String messageEquals;
private final @Nullable String messageEquals;

private StackTraceMatchFilter(
final String stackContains,
final String messageEquals,
final @Nullable String messageEquals,
final Result onMatch,
final Result onMismatch) {
super(onMatch, onMismatch);
Expand Down Expand Up @@ -71,9 +73,9 @@ public Result filter(final LogEvent event) {
return filter(event.getThrown());
}

private Result filter(final Throwable t) {
private Result filter(final @Nullable Throwable t) {
if (t != null) {
return (messageEquals == null || t.getMessage().equals(messageEquals))
return (messageEquals == null || Objects.equals(t.getMessage(), messageEquals))
&& Arrays.stream(t.getStackTrace())
.map(StackTraceElement::getClassName)
.anyMatch(cn -> cn.contains(stackContains))
Comment thread
siladu marked this conversation as resolved.
Expand Down Expand Up @@ -102,8 +104,8 @@ public static StackTraceMatchFilter.Builder newBuilder() {
public static class Builder extends AbstractFilterBuilder<StackTraceMatchFilter.Builder>
implements org.apache.logging.log4j.core.util.Builder<StackTraceMatchFilter> {

@PluginBuilderAttribute private String stackContains = null;
@PluginBuilderAttribute private String messageEquals = null;
@PluginBuilderAttribute private @Nullable String stackContains;
@PluginBuilderAttribute private @Nullable String messageEquals;

/** Default constructor */
public Builder() {
Expand All @@ -116,7 +118,7 @@ public Builder() {
* @param text the match string
* @return this builder
*/
public StackTraceMatchFilter.Builder setStackContains(final String text) {
public StackTraceMatchFilter.Builder setStackContains(final @Nullable String text) {
this.stackContains = text;
Comment on lines +121 to 122
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The builder setter setStackContains(@Nullable String) allows explicitly passing null, but build() then unconditionally fails via requireNonNull. This makes the API easier to misuse and shifts validation to a later point. Consider making the setter parameter non-null (and annotating it accordingly), or validate immediately in setStackContains (e.g., requireNonNull(text, ...)) so failures are earlier and closer to the source of the misuse.

Suggested change
public StackTraceMatchFilter.Builder setStackContains(final @Nullable String text) {
this.stackContains = text;
public StackTraceMatchFilter.Builder setStackContains(final String text) {
this.stackContains = Objects.requireNonNull(text, "stackContains must be provided");

Copilot uses AI. Check for mistakes.
return this;
}
Expand All @@ -127,15 +129,18 @@ public StackTraceMatchFilter.Builder setStackContains(final String text) {
* @param text the match string
* @return this builder
*/
public StackTraceMatchFilter.Builder setMessageEquals(final String text) {
public StackTraceMatchFilter.Builder setMessageEquals(final @Nullable String text) {
this.messageEquals = text;
return this;
}

@Override
public StackTraceMatchFilter build() {
final String nonNullStackContains =
Objects.requireNonNull(stackContains, "stackContains must be provided");
Comment thread
siladu marked this conversation as resolved.
Comment on lines +139 to +140
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The builder setter setStackContains(@Nullable String) allows explicitly passing null, but build() then unconditionally fails via requireNonNull. This makes the API easier to misuse and shifts validation to a later point. Consider making the setter parameter non-null (and annotating it accordingly), or validate immediately in setStackContains (e.g., requireNonNull(text, ...)) so failures are earlier and closer to the source of the misuse.

Copilot uses AI. Check for mistakes.

return new StackTraceMatchFilter(
this.stackContains, this.messageEquals, this.getOnMatch(), this.getOnMismatch());
nonNullStackContains, this.messageEquals, this.getOnMatch(), this.getOnMismatch());
}
}
}
Loading
Loading