Skip to content

97 implement null handling with jspecify#120

Merged
Martin-E-Karlsson merged 17 commits intomainfrom
97-implement-null-handling-with-jspecify
Mar 2, 2026
Merged

97 implement null handling with jspecify#120
Martin-E-Karlsson merged 17 commits intomainfrom
97-implement-null-handling-with-jspecify

Conversation

@Martin-E-Karlsson
Copy link

@Martin-E-Karlsson Martin-E-Karlsson commented Mar 2, 2026

  • Added jspecify to pom.xml
  • Added @NullMarked package-info.java files to all packages in both main and test
  • Searched files for null returns and evaluated null handling
  • Marked handled possible null returns wherever found

Summary by CodeRabbit

  • Chores

    • Added a nullability-checking library to the project.
  • Refactor

    • Applied nullability annotations broadly (including package-level and exposed APIs) to improve type-safety and static checks without changing runtime behavior.
  • Tests

    • Removed a test that previously asserted a NullPointerException in a specific case.

- @NullMarked to src/main/java/org/example
- Checked all files in example for handling null return values.
- Checked all files for handling null return values
- Checked all files for handling null return values
- Checked all files for handling null return values
- Checked all files for handling null return values
- Checked all files for handling null return values
- Checked all files for handling null return values
- Checked ConfigLoaderTest for null handling
- Checked all tests for null handling
- Marked body in HttpRequest parameters as nullable to satisfy tests in CompressionFilterTest.
- Marked ip parameters for normalizeIp, addBlockedIp and addAllowedIp to be Nullable since they work as intended but warnings need to be suppressed.
-Added @nullable to request parameter for doFilter in LocaleFilter since it is handled.
- Checked all tests for null handling
- Marked filename parameter in detectMimeType as Nullable since it's handled.
- Checked all tests for null handling
- Marked "in" parameter for setReader in HttpParser as nullable since it is handled.
- Checked ConfigurableFilterPipelineTest for null handling
- Marked the routePatterns parameter in the FilterRegistration record in FilterRegistration.java since it is safely null handled.
@Martin-E-Karlsson Martin-E-Karlsson linked an issue Mar 2, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d445813 and 48bf90b.

📒 Files selected for processing (1)
  • src/main/java/org/example/filter/CompressionFilter.java

📝 Walkthrough

Walkthrough

Adds JSpecify nullability: introduces the jspecify dependency, applies @Nullable annotations to several method signatures, record components, and fields, and adds @NullMarked package-info files across main and test packages; one null-check test was removed.

Changes

Cohort / File(s) Summary
Build Configuration
pom.xml
Added org.jspecify:jspecify:1.0.0 dependency.
Package-Level Nullability (main)
src/main/java/org/example/package-info.java, src/main/java/org/example/config/package-info.java, src/main/java/org/example/filter/package-info.java, src/main/java/org/example/http/package-info.java, src/main/java/org/example/httpparser/package-info.java, src/main/java/org/example/server/package-info.java
Added @NullMarked package-info files to mark packages as null-aware.
Package-Level Nullability (tests)
src/test/java/org/example/package-info.java, src/test/java/org/example/config/package-info.java, src/test/java/org/example/filter/package-info.java, src/test/java/org/example/http/package-info.java, src/test/java/org/example/httpparser/package-info.java, src/test/java/org/example/server/package-info.java
Added @NullMarked package-info files for test packages.
Nullability on Methods / Types
src/main/java/org/example/App.java, src/main/java/org/example/filter/CompressionFilter.java, src/main/java/org/example/filter/IpFilter.java, src/main/java/org/example/filter/LocaleFilter.java, src/main/java/org/example/filter/LocaleFilterWithCookie.java, src/main/java/org/example/http/MimeTypeDetector.java, src/main/java/org/example/httpparser/HttpRequest.java, src/main/java/org/example/server/FilterRegistration.java
Applied @Nullable to parameters, return types, a record component, and a field (HttpRequest.body); adjusted imports to include jspecify annotations.
Test Cleanup
src/test/java/org/example/httpparser/HttpParseRequestLineTest.java
Removed a test that asserted NPE on setReader(null) and its associated import.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • MartinStenhagen
  • eeebbaandersson

Poem

🐰 I hopped through code both near and far,
Marked each null like a twinkling star.
@Nullable here, @NullMarked there,
Safety stitched with rabbit care.
Hooray — tidy hops on every jar! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main objective: implementing null handling with JSpecify, which aligns with the comprehensive changes across the codebase adding JSpecify annotations and null-safety markers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 97-implement-null-handling-with-jspecify

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/java/org/example/httpparser/HttpParser.java (1)

18-22: ⚠️ Potential issue | 🔴 Critical

Null-pointer exception when in is null.

The parameter in is marked @Nullable, indicating null is a valid input, but line 20 passes in directly to InputStreamReader without a null check. If in is null and this.reader is also null, this will throw a NullPointerException.

Either remove @Nullable if null is not intended to be accepted, or add null handling:

Option 1: Remove `@Nullable` if null input is not valid
-    public void setReader(`@Nullable` InputStream in) {
+    public void setReader(InputStream in) {
         if (this.reader == null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }
Option 2: Add null handling if null input should be accepted
     public void setReader(`@Nullable` InputStream in) {
-        if (this.reader == null) {
+        if (this.reader == null && in != null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpParser.java` around lines 18 - 22,
The setReader method accepts a `@Nullable` InputStream but passes it directly to
new InputStreamReader, causing an NPE; update setReader(InputStream in) to
handle null: either remove the `@Nullable` annotation if null is invalid (and
optionally throw IllegalArgumentException when in is null) or add an explicit
null check inside setReader (e.g., if (in == null) { this.reader = null; return;
}) before creating new BufferedReader(new InputStreamReader(in,
StandardCharsets.UTF_8)); ensure you reference the setReader method, the reader
field, and the InputStreamReader creation when making the change.
src/main/java/org/example/filter/IpFilter.java (1)

96-107: ⚠️ Potential issue | 🟡 Minor

Reject blank IP values in list mutators.

Line 100 and Line 107 can insert empty strings (" """) into allow/block sets. Treat blank IPs as invalid input.

Proposed fix
     public void addBlockedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        blockedIps.add(normalizeIp(ip));
+        blockedIps.add(ip.trim());
     }
@@
     public void addAllowedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        allowedIps.add(normalizeIp(ip));
+        allowedIps.add(ip.trim());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/IpFilter.java` around lines 96 - 107,
addBlockedIp and addAllowedIp currently accept strings that are blank after
trimming and insert empty values into blockedIps/allowedIps; update both methods
(addBlockedIp and addAllowedIp) to treat blank input as invalid by trimming the
input (or using String.isBlank()) and throwing an IllegalArgumentException when
the trimmed value is empty before calling normalizeIp and adding to the set, so
only non-empty normalized IPs are inserted.
src/main/java/org/example/httpparser/HttpRequest.java (1)

27-44: ⚠️ Potential issue | 🟠 Major

Align body field/getter nullability with constructor contract.

The constructor at line 27 accepts @Nullable String body, but the field at line 20 and getter at line 43 are typed as non-nullable String. This mismatch allows null to be stored and returned while the type system claims otherwise. The call site in RequestTimeOutFilter.java:99 invokes getBody() without null-checking, assuming a non-null return.

Proposed fix
-        private final String body;
+        private final `@Nullable` String body;
@@
-        public String getBody() {
+        public `@Nullable` String getBody() {
             return body; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpRequest.java` around lines 27 - 44,
The HttpRequest constructor accepts a nullable body but the class field and
getter are declared non-nullable; update the class to reflect the nullable
contract: change the field (body) and its getter (getBody()) to return/hold
`@Nullable` String (or Optional<String>) consistent with the constructor, and
update callers such as RequestTimeOutFilter (e.g., where getBody() is used) to
null-check or handle Optional accordingly; ensure headers remain copied as
before and preserve immutability when adjusting types in class HttpRequest and
usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 88-90: Change the method signature of normalizeIp to declare a
nullable return type by annotating the return with `@Nullable` (i.e., add
`@Nullable` to the method return) so it matches the implementation and Javadoc;
update the import if needed (org.jetbrains.annotations.Nullable or the project's
`@Nullable`) and then scan callers of normalizeIp to handle or propagate the
possible null result where applicable (e.g., add null checks or Optional
wrapping) so the codebase respects the nullable contract.

---

Outside diff comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 96-107: addBlockedIp and addAllowedIp currently accept strings
that are blank after trimming and insert empty values into
blockedIps/allowedIps; update both methods (addBlockedIp and addAllowedIp) to
treat blank input as invalid by trimming the input (or using String.isBlank())
and throwing an IllegalArgumentException when the trimmed value is empty before
calling normalizeIp and adding to the set, so only non-empty normalized IPs are
inserted.

In `@src/main/java/org/example/httpparser/HttpParser.java`:
- Around line 18-22: The setReader method accepts a `@Nullable` InputStream but
passes it directly to new InputStreamReader, causing an NPE; update
setReader(InputStream in) to handle null: either remove the `@Nullable` annotation
if null is invalid (and optionally throw IllegalArgumentException when in is
null) or add an explicit null check inside setReader (e.g., if (in == null) {
this.reader = null; return; }) before creating new BufferedReader(new
InputStreamReader(in, StandardCharsets.UTF_8)); ensure you reference the
setReader method, the reader field, and the InputStreamReader creation when
making the change.

In `@src/main/java/org/example/httpparser/HttpRequest.java`:
- Around line 27-44: The HttpRequest constructor accepts a nullable body but the
class field and getter are declared non-nullable; update the class to reflect
the nullable contract: change the field (body) and its getter (getBody()) to
return/hold `@Nullable` String (or Optional<String>) consistent with the
constructor, and update callers such as RequestTimeOutFilter (e.g., where
getBody() is used) to null-check or handle Optional accordingly; ensure headers
remain copied as before and preserve immutability when adjusting types in class
HttpRequest and usages.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80ffccf and ef42e29.

📒 Files selected for processing (22)
  • pom.xml
  • src/main/java/org/example/App.java
  • src/main/java/org/example/config/package-info.java
  • src/main/java/org/example/filter/CompressionFilter.java
  • src/main/java/org/example/filter/IpFilter.java
  • src/main/java/org/example/filter/LocaleFilter.java
  • src/main/java/org/example/filter/LocaleFilterWithCookie.java
  • src/main/java/org/example/filter/package-info.java
  • src/main/java/org/example/http/MimeTypeDetector.java
  • src/main/java/org/example/http/package-info.java
  • src/main/java/org/example/httpparser/HttpParser.java
  • src/main/java/org/example/httpparser/HttpRequest.java
  • src/main/java/org/example/httpparser/package-info.java
  • src/main/java/org/example/package-info.java
  • src/main/java/org/example/server/FilterRegistration.java
  • src/main/java/org/example/server/package-info.java
  • src/test/java/org/example/config/package-info.java
  • src/test/java/org/example/filter/package-info.java
  • src/test/java/org/example/http/package-info.java
  • src/test/java/org/example/httpparser/package-info.java
  • src/test/java/org/example/package-info.java
  • src/test/java/org/example/server/package-info.java

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/httpparser/HttpParser.java`:
- Around line 18-20: The setter setReader currently silently ignores a null
InputStream and leaves the field reader unset, causing later NPEs in
parseHttp()/parseRequest(); update setReader(InputStream in) to fail fast by
validating the in parameter and throwing an appropriate runtime exception (e.g.,
IllegalArgumentException or NullPointerException) when in is null, instead of
silently returning, so callers get immediate feedback and reader is never left
unexpectedly null; keep the existing behavior of wrapping non-null in with new
BufferedReader(new InputStreamReader(..., StandardCharsets.UTF_8))) and document
the non-null requirement in the method contract for setReader and usages in
parseHttp and parseRequest.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef42e29 and f6a17d1.

📒 Files selected for processing (3)
  • src/main/java/org/example/filter/IpFilter.java
  • src/main/java/org/example/httpparser/HttpParser.java
  • src/main/java/org/example/httpparser/HttpRequest.java

@Martin-E-Karlsson
Copy link
Author

Jag kommer att kolla på setReader i HttpParser och det relaterade nullException testet efter jag sovit.

Copy link

@alicewersen-rgb alicewersen-rgb left a comment

Choose a reason for hiding this comment

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

Looks good! Nice use of @nullable and the null-checks in addBlockedIp / addAllowedIp to make the code safer. Just make sure that request in doFilter(@nullable HttpRequest request) is properly null-checked wherever it’s used, so we don’t risk a NullPointerException. Otherwise it's looking good!

@Martin-E-Karlsson Martin-E-Karlsson self-assigned this Mar 2, 2026
…solveLocale request parameter since resolveLocale does the actual null handling.

- Removed testParserThrowErrorWhenNull test since it is made redundant by jspecify null marking.
# Conflicts:
#	pom.xml
#	src/main/java/org/example/filter/CompressionFilter.java
#	src/main/java/org/example/filter/LocaleFilter.java
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/example/filter/CompressionFilter.java (1)

213-216: ⚠️ Potential issue | 🔴 Critical

Build blocker: Encoder symbol is unresolved in brotliCompress.

Line 215 calls Encoder.compress(data) but the encoder import is missing. Add the missing brotli4j encoder import.

🔧 Proposed fix
 import com.aayushatharva.brotli4j.Brotli4jLoader;
+import com.aayushatharva.brotli4j.encoder.Encoder;
 import org.example.http.HttpResponseBuilder;
 import org.example.httpparser.HttpRequest;
 import org.jspecify.annotations.Nullable;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/CompressionFilter.java` around lines 213 -
216, In CompressionFilter.brotliCompress add the missing brotli4j encoder import
and use the Encoder symbol from that library; specifically import
com.aayushatharva.brotli4j.encoder.Encoder at the top of the file so the call to
Encoder.compress(data) in brotliCompress resolves correctly within the
CompressionFilter class.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/main/java/org/example/filter/CompressionFilter.java`:
- Around line 213-216: In CompressionFilter.brotliCompress add the missing
brotli4j encoder import and use the Encoder symbol from that library;
specifically import com.aayushatharva.brotli4j.encoder.Encoder at the top of the
file so the call to Encoder.compress(data) in brotliCompress resolves correctly
within the CompressionFilter class.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0648782 and d445813.

📒 Files selected for processing (3)
  • pom.xml
  • src/main/java/org/example/filter/CompressionFilter.java
  • src/main/java/org/example/filter/LocaleFilter.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • pom.xml

@Xeutos
Copy link

Xeutos commented Mar 2, 2026

Du bör göra en pull request för bara pom.xml istället för med resterande kod.

- Tests and compilation run without issue on my local setup now.
@Xeutos
Copy link

Xeutos commented Mar 2, 2026

Actionable comments posted: 1

[!CAUTION]
Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/main/java/org/example/httpparser/HttpParser.java (1)

18-22: ⚠️ Potential issue | 🔴 Critical

Null-pointer exception when in is null.

The parameter in is marked @Nullable, indicating null is a valid input, but line 20 passes in directly to InputStreamReader without a null check. If in is null and this.reader is also null, this will throw a NullPointerException.

Either remove @Nullable if null is not intended to be accepted, or add null handling:

Option 1: Remove `@Nullable` if null input is not valid
-    public void setReader(`@Nullable` InputStream in) {
+    public void setReader(InputStream in) {
         if (this.reader == null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }
Option 2: Add null handling if null input should be accepted
     public void setReader(`@Nullable` InputStream in) {
-        if (this.reader == null) {
+        if (this.reader == null && in != null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpParser.java` around lines 18 - 22,
The setReader method accepts a `@Nullable` InputStream but passes it directly to
new InputStreamReader, causing an NPE; update setReader(InputStream in) to
handle null: either remove the `@Nullable` annotation if null is invalid (and
optionally throw IllegalArgumentException when in is null) or add an explicit
null check inside setReader (e.g., if (in == null) { this.reader = null; return;
}) before creating new BufferedReader(new InputStreamReader(in,
StandardCharsets.UTF_8)); ensure you reference the setReader method, the reader
field, and the InputStreamReader creation when making the change.
src/main/java/org/example/filter/IpFilter.java (1)

96-107: ⚠️ Potential issue | 🟡 Minor

Reject blank IP values in list mutators.

Line 100 and Line 107 can insert empty strings (" """) into allow/block sets. Treat blank IPs as invalid input.

Proposed fix
     public void addBlockedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        blockedIps.add(normalizeIp(ip));
+        blockedIps.add(ip.trim());
     }
@@
     public void addAllowedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        allowedIps.add(normalizeIp(ip));
+        allowedIps.add(ip.trim());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/IpFilter.java` around lines 96 - 107,
addBlockedIp and addAllowedIp currently accept strings that are blank after
trimming and insert empty values into blockedIps/allowedIps; update both methods
(addBlockedIp and addAllowedIp) to treat blank input as invalid by trimming the
input (or using String.isBlank()) and throwing an IllegalArgumentException when
the trimmed value is empty before calling normalizeIp and adding to the set, so
only non-empty normalized IPs are inserted.
src/main/java/org/example/httpparser/HttpRequest.java (1)

27-44: ⚠️ Potential issue | 🟠 Major

Align body field/getter nullability with constructor contract.

The constructor at line 27 accepts @Nullable String body, but the field at line 20 and getter at line 43 are typed as non-nullable String. This mismatch allows null to be stored and returned while the type system claims otherwise. The call site in RequestTimeOutFilter.java:99 invokes getBody() without null-checking, assuming a non-null return.

Proposed fix
-        private final String body;
+        private final `@Nullable` String body;
@@
-        public String getBody() {
+        public `@Nullable` String getBody() {
             return body; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpRequest.java` around lines 27 - 44,
The HttpRequest constructor accepts a nullable body but the class field and
getter are declared non-nullable; update the class to reflect the nullable
contract: change the field (body) and its getter (getBody()) to return/hold
`@Nullable` String (or Optional<String>) consistent with the constructor, and
update callers such as RequestTimeOutFilter (e.g., where getBody() is used) to
null-check or handle Optional accordingly; ensure headers remain copied as
before and preserve immutability when adjusting types in class HttpRequest and
usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 88-90: Change the method signature of normalizeIp to declare a
nullable return type by annotating the return with `@Nullable` (i.e., add
`@Nullable` to the method return) so it matches the implementation and Javadoc;
update the import if needed (org.jetbrains.annotations.Nullable or the project's
`@Nullable`) and then scan callers of normalizeIp to handle or propagate the
possible null result where applicable (e.g., add null checks or Optional
wrapping) so the codebase respects the nullable contract.

---

Outside diff comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 96-107: addBlockedIp and addAllowedIp currently accept strings
that are blank after trimming and insert empty values into
blockedIps/allowedIps; update both methods (addBlockedIp and addAllowedIp) to
treat blank input as invalid by trimming the input (or using String.isBlank())
and throwing an IllegalArgumentException when the trimmed value is empty before
calling normalizeIp and adding to the set, so only non-empty normalized IPs are
inserted.

In `@src/main/java/org/example/httpparser/HttpParser.java`:
- Around line 18-22: The setReader method accepts a `@Nullable` InputStream but
passes it directly to new InputStreamReader, causing an NPE; update
setReader(InputStream in) to handle null: either remove the `@Nullable` annotation
if null is invalid (and optionally throw IllegalArgumentException when in is
null) or add an explicit null check inside setReader (e.g., if (in == null) {
this.reader = null; return; }) before creating new BufferedReader(new
InputStreamReader(in, StandardCharsets.UTF_8)); ensure you reference the
setReader method, the reader field, and the InputStreamReader creation when
making the change.

In `@src/main/java/org/example/httpparser/HttpRequest.java`:
- Around line 27-44: The HttpRequest constructor accepts a nullable body but the
class field and getter are declared non-nullable; update the class to reflect
the nullable contract: change the field (body) and its getter (getBody()) to
return/hold `@Nullable` String (or Optional<String>) consistent with the
constructor, and update callers such as RequestTimeOutFilter (e.g., where
getBody() is used) to null-check or handle Optional accordingly; ensure headers
remain copied as before and preserve immutability when adjusting types in class
HttpRequest and usages.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80ffccf and ef42e29.

📒 Files selected for processing (22)
  • pom.xml
  • src/main/java/org/example/App.java
  • src/main/java/org/example/config/package-info.java
  • src/main/java/org/example/filter/CompressionFilter.java
  • src/main/java/org/example/filter/IpFilter.java
  • src/main/java/org/example/filter/LocaleFilter.java
  • src/main/java/org/example/filter/LocaleFilterWithCookie.java
  • src/main/java/org/example/filter/package-info.java
  • src/main/java/org/example/http/MimeTypeDetector.java
  • src/main/java/org/example/http/package-info.java
  • src/main/java/org/example/httpparser/HttpParser.java
  • src/main/java/org/example/httpparser/HttpRequest.java
  • src/main/java/org/example/httpparser/package-info.java
  • src/main/java/org/example/package-info.java
  • src/main/java/org/example/server/FilterRegistration.java
  • src/main/java/org/example/server/package-info.java
  • src/test/java/org/example/config/package-info.java
  • src/test/java/org/example/filter/package-info.java
  • src/test/java/org/example/http/package-info.java
  • src/test/java/org/example/httpparser/package-info.java
  • src/test/java/org/example/package-info.java
  • src/test/java/org/example/server/package-info.java

Har du kollat in dessa? Bra att kolla om det skall tillåta null till att börja med.

@Martin-E-Karlsson
Copy link
Author

Actionable comments posted: 1

[!CAUTION]
Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)> src/main/java/org/example/httpparser/HttpParser.java (1)> 18-22: ⚠️ Potential issue | 🔴 Critical

Null-pointer exception when in is null.
The parameter in is marked @Nullable, indicating null is a valid input, but line 20 passes in directly to InputStreamReader without a null check. If in is null and this.reader is also null, this will throw a NullPointerException.
Either remove @Nullable if null is not intended to be accepted, or add null handling:

Option 1: Remove @Nullable if null input is not valid

-    public void setReader(`@Nullable` InputStream in) {
+    public void setReader(InputStream in) {
         if (this.reader == null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }

Option 2: Add null handling if null input should be accepted

     public void setReader(`@Nullable` InputStream in) {
-        if (this.reader == null) {
+        if (this.reader == null && in != null) {
             this.reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
         }
     }

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpParser.java` around lines 18 - 22,
The setReader method accepts a `@Nullable` InputStream but passes it directly to
new InputStreamReader, causing an NPE; update setReader(InputStream in) to
handle null: either remove the `@Nullable` annotation if null is invalid (and
optionally throw IllegalArgumentException when in is null) or add an explicit
null check inside setReader (e.g., if (in == null) { this.reader = null; return;
}) before creating new BufferedReader(new InputStreamReader(in,
StandardCharsets.UTF_8)); ensure you reference the setReader method, the reader
field, and the InputStreamReader creation when making the change.

src/main/java/org/example/filter/IpFilter.java (1)> 96-107: ⚠️ Potential issue | 🟡 Minor

Reject blank IP values in list mutators.
Line 100 and Line 107 can insert empty strings (" """) into allow/block sets. Treat blank IPs as invalid input.

Proposed fix

     public void addBlockedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        blockedIps.add(normalizeIp(ip));
+        blockedIps.add(ip.trim());
     }
@@
     public void addAllowedIp(`@Nullable` String ip) {
-        if (ip == null) {
-            throw new IllegalArgumentException("IP address cannot be null");
+        if (ip == null || ip.isBlank()) {
+            throw new IllegalArgumentException("IP address cannot be null or blank");
         }
-        allowedIps.add(normalizeIp(ip));
+        allowedIps.add(ip.trim());
     }

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/IpFilter.java` around lines 96 - 107,
addBlockedIp and addAllowedIp currently accept strings that are blank after
trimming and insert empty values into blockedIps/allowedIps; update both methods
(addBlockedIp and addAllowedIp) to treat blank input as invalid by trimming the
input (or using String.isBlank()) and throwing an IllegalArgumentException when
the trimmed value is empty before calling normalizeIp and adding to the set, so
only non-empty normalized IPs are inserted.

src/main/java/org/example/httpparser/HttpRequest.java (1)> 27-44: ⚠️ Potential issue | 🟠 Major

Align body field/getter nullability with constructor contract.
The constructor at line 27 accepts @Nullable String body, but the field at line 20 and getter at line 43 are typed as non-nullable String. This mismatch allows null to be stored and returned while the type system claims otherwise. The call site in RequestTimeOutFilter.java:99 invokes getBody() without null-checking, assuming a non-null return.

Proposed fix

-        private final String body;
+        private final `@Nullable` String body;
@@
-        public String getBody() {
+        public `@Nullable` String getBody() {
             return body; }

🤖 Prompt for AI Agents

Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/httpparser/HttpRequest.java` around lines 27 - 44,
The HttpRequest constructor accepts a nullable body but the class field and
getter are declared non-nullable; update the class to reflect the nullable
contract: change the field (body) and its getter (getBody()) to return/hold
`@Nullable` String (or Optional<String>) consistent with the constructor, and
update callers such as RequestTimeOutFilter (e.g., where getBody() is used) to
null-check or handle Optional accordingly; ensure headers remain copied as
before and preserve immutability when adjusting types in class HttpRequest and
usages.

🤖 Prompt for all review comments with AI agents

Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 88-90: Change the method signature of normalizeIp to declare a
nullable return type by annotating the return with `@Nullable` (i.e., add
`@Nullable` to the method return) so it matches the implementation and Javadoc;
update the import if needed (org.jetbrains.annotations.Nullable or the project's
`@Nullable`) and then scan callers of normalizeIp to handle or propagate the
possible null result where applicable (e.g., add null checks or Optional
wrapping) so the codebase respects the nullable contract.

---

Outside diff comments:
In `@src/main/java/org/example/filter/IpFilter.java`:
- Around line 96-107: addBlockedIp and addAllowedIp currently accept strings
that are blank after trimming and insert empty values into
blockedIps/allowedIps; update both methods (addBlockedIp and addAllowedIp) to
treat blank input as invalid by trimming the input (or using String.isBlank())
and throwing an IllegalArgumentException when the trimmed value is empty before
calling normalizeIp and adding to the set, so only non-empty normalized IPs are
inserted.

In `@src/main/java/org/example/httpparser/HttpParser.java`:
- Around line 18-22: The setReader method accepts a `@Nullable` InputStream but
passes it directly to new InputStreamReader, causing an NPE; update
setReader(InputStream in) to handle null: either remove the `@Nullable` annotation
if null is invalid (and optionally throw IllegalArgumentException when in is
null) or add an explicit null check inside setReader (e.g., if (in == null) {
this.reader = null; return; }) before creating new BufferedReader(new
InputStreamReader(in, StandardCharsets.UTF_8)); ensure you reference the
setReader method, the reader field, and the InputStreamReader creation when
making the change.

In `@src/main/java/org/example/httpparser/HttpRequest.java`:
- Around line 27-44: The HttpRequest constructor accepts a nullable body but the
class field and getter are declared non-nullable; update the class to reflect
the nullable contract: change the field (body) and its getter (getBody()) to
return/hold `@Nullable` String (or Optional<String>) consistent with the
constructor, and update callers such as RequestTimeOutFilter (e.g., where
getBody() is used) to null-check or handle Optional accordingly; ensure headers
remain copied as before and preserve immutability when adjusting types in class
HttpRequest and usages.

ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro

📥 Commits
Reviewing files that changed from the base of the PR and between 80ffccf and ef42e29.

📒 Files selected for processing (22)

  • pom.xml
  • src/main/java/org/example/App.java
  • src/main/java/org/example/config/package-info.java
  • src/main/java/org/example/filter/CompressionFilter.java
  • src/main/java/org/example/filter/IpFilter.java
  • src/main/java/org/example/filter/LocaleFilter.java
  • src/main/java/org/example/filter/LocaleFilterWithCookie.java
  • src/main/java/org/example/filter/package-info.java
  • src/main/java/org/example/http/MimeTypeDetector.java
  • src/main/java/org/example/http/package-info.java
  • src/main/java/org/example/httpparser/HttpParser.java
  • src/main/java/org/example/httpparser/HttpRequest.java
  • src/main/java/org/example/httpparser/package-info.java
  • src/main/java/org/example/package-info.java
  • src/main/java/org/example/server/FilterRegistration.java
  • src/main/java/org/example/server/package-info.java
  • src/test/java/org/example/config/package-info.java
  • src/test/java/org/example/filter/package-info.java
  • src/test/java/org/example/http/package-info.java
  • src/test/java/org/example/httpparser/package-info.java
  • src/test/java/org/example/package-info.java
  • src/test/java/org/example/server/package-info.java

Har du kollat in dessa? Bra att kolla om det skall tillåta null till att börja med.

Null-pointer exception when in is null:
Jag tog bort @nullable och det tillhörande testet i HttpParserTest som gav utslag på null handling problem med setReader från början. Detta då @NullMarked redan kollar efter null handling och utdaterade testets funktion.

Reject blank IP values in list mutators:
Jag antog att problemet var out of scope för implementeringen av jspecify

Align body field/getter nullability with constructor contract:
Den verkar jag ha glömt, ska kolla på den nu

@Martin-E-Karlsson
Copy link
Author

Kom på nu att jag bara implementerade "Align body field/getter nullability with constructor contract" med fixen som gavs av CodeRabbit

@Xeutos
Copy link

Xeutos commented Mar 2, 2026

Null-pointer exception when in is null:
Jag tog bort @nullable och det tillhörande testet i HttpParserTest som gav utslag på null handling problem med setReader från början. Detta då @NullMarked redan kollar efter null handling och utdaterade testets funktion.

Reject blank IP values in list mutators:
Jag antog att problemet var out of scope för implementeringen av jspecify

Align body field/getter nullability with constructor contract:
Den verkar jag ha glömt, ska kolla på den nu

Bra, var den tredje outdated eller är det att den är märkt som non nullable fast kan returnera null?

@Xeutos Xeutos self-requested a review March 2, 2026 17:17
Copy link

@Xeutos Xeutos left a comment

Choose a reason for hiding this comment

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

Ser bra ut i helheten!

Värt att tänka på att det är bättre att göra en pull request för pom.xml separat det minskar mängden merge konflikter.

Copy link

@alicewersen-rgb alicewersen-rgb left a comment

Choose a reason for hiding this comment

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

Clean and easy to follow implementation. I like that null handling is explicit and that the IP values are normalized before being stored. The code is readable and consistent with the rest of the project!

@Martin-E-Karlsson Martin-E-Karlsson merged commit 2f851e9 into main Mar 2, 2026
3 checks passed
@Martin-E-Karlsson Martin-E-Karlsson deleted the 97-implement-null-handling-with-jspecify branch March 2, 2026 23:54
@coderabbitai coderabbitai bot mentioned this pull request Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement null handling with jspecify

3 participants