Skip to content

applied order filter annotations to existing filters - #107

Merged
kristinaxm merged 4 commits into
mainfrom
feature/add-annotations-to-filters
Feb 22, 2026
Merged

applied order filter annotations to existing filters#107
kristinaxm merged 4 commits into
mainfrom
feature/add-annotations-to-filters

Conversation

@kristinaxm

@kristinaxm kristinaxm commented Feb 20, 2026

Copy link
Copy Markdown

Closes #106

0 SecurityHeadersFilter (wrapper)
1 LoggingFilter (wrapper)
2 IpFilter (can block)
3 RateLimitingFilter (can block)
4 RedirectFilter (can short-circuit)

Summary by CodeRabbit

  • New Features

    • IP-based request filtering as an early processing stage
    • Optional configurable rate limiting to control request traffic
  • Improvements

    • Reordered request processing pipeline for more predictable filtering and redirects
    • Explicit routing for static content and a dedicated not-found handler for clearer error responses

@kristinaxm kristinaxm self-assigned this Feb 20, 2026
@coderabbitai

coderabbitai Bot commented Feb 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Applied @Global(order) annotations to four filters and updated App.java to register and order global filters (Logging, Ip, RateLimiting conditional, Redirect) and to explicitly configure the SimpleRouter plugins and pipeline router.

Changes

Cohort / File(s) Summary
Filter Ordering Annotations
src/main/java/org/juv25d/filter/LoggingFilter.java, src/main/java/org/juv25d/filter/IpFilter.java, src/main/java/org/juv25d/filter/RateLimitingFilter.java, src/main/java/org/juv25d/filter/RedirectFilter.java
Added import org.juv25d.filter.annotation.Global; and applied @Global(order = 1..4) to each filter class (Logging=1, Ip=2, RateLimiting=3, Redirect=4). No changes to method bodies.
Pipeline & Router Configuration
src/main/java/org/juv25d/App.java
Replaced placeholder/inline filter setup with explicit ordered global filter registration (Logging, Ip, conditional RateLimiting, Redirect), instantiated SimpleRouter, registered StaticFilesPlugin for / and /* and NotFoundPlugin for /notfound, and set the router on the pipeline. Minor whitespace edits.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • fmazmz
  • addee1
  • kristina0x7

Poem

🐇 I hopped through code in morning light,
I ordered filters, set them right.
Logs first, then IPs to bind,
Rate guard next (if config’s kind),
Redirect closes out the line — hooray, pipeline! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes to App.java include router initialization and filter pipeline configuration, which are beyond the scope of applying annotations to existing filters. Revert App.java changes to App.java; they should be addressed separately or in issue #106's follow-up PR about filter configuration and automation.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: applying order filter annotations to existing filter classes.
Linked Issues check ✅ Passed All coding requirements from issue #106 are met: order annotations added to LoggingFilter (1), IpFilter (2), RateLimitingFilter (3), and RedirectFilter (4).

✏️ 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 feature/add-annotations-to-filters

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/main/java/org/juv25d/App.java (1)

25-26: Reformat the conditional RateLimitingFilter registration.

The current inline style is hard to read and inconsistent with the surrounding code.

♻️ Proposed refactor
-        if (config.isRateLimitingEnabled()) {pipeline.addGlobalFilter(new RateLimitingFilter(
-            config.getRequestsPerMinute(), config.getBurstCapacity()), 1);}
+        if (config.isRateLimitingEnabled()) {
+            pipeline.addGlobalFilter(
+                new RateLimitingFilter(config.getRequestsPerMinute(), config.getBurstCapacity()), 1);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/App.java` around lines 25 - 26, The inline
registration of RateLimitingFilter is hard to read; refactor the conditional
that calls pipeline.addGlobalFilter(new
RateLimitingFilter(config.getRequestsPerMinute(), config.getBurstCapacity()), 1)
into a clear multi-line if block: call config.isRateLimitingEnabled() in the if
condition, instantiate RateLimitingFilter on its own line using
config.getRequestsPerMinute() and config.getBurstCapacity(), then pass that
instance to pipeline.addGlobalFilter(...) on a separate line (preserving the
priority arg 1); this improves readability and matches surrounding code style.
src/main/java/org/juv25d/filter/LoggingFilter.java (1)

14-14: Replace System.out.println with a structured logger.

LoggingFilter is the dedicated request-logging component yet it writes to stdout instead of using a Logger instance. This bypasses log-level control, log formatting, and centralised output routing.

♻️ Proposed refactor
+import java.util.logging.Logger;
+
 `@Global`(order = 3)
 public class LoggingFilter implements Filter {
+    private static final Logger logger = Logger.getLogger(LoggingFilter.class.getName());
 
     `@Override`
     public void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) throws IOException {
-        System.out.println(req.method() + " " + req.path());
+        logger.info(req.method() + " " + req.path());
         chain.doFilter(req, res);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/filter/LoggingFilter.java` at line 14, Replace the
System.out.println call in LoggingFilter with a structured logger: add a private
static final Logger (e.g., LoggerFactory.getLogger(LoggingFilter.class)) to the
LoggingFilter class and replace the println(req.method() + " " + req.path())
with a logger call (e.g., logger.info or logger.debug) that uses parameterized
logging like logger.info("{} {}", req.method(), req.path()); also add the
necessary import for the logger implementation (SLF4J/your project's logging
facade) and remove the stdout call so request logging goes through the
centralized logger.
🤖 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/juv25d/App.java`:
- Line 23: The IpFilter is currently constructed with empty sets at
pipeline.addGlobalFilter(...), making IpFilter.doFilter() always allow requests;
either make this explicit or implement real filtering: if this is a placeholder,
add a clear comment next to pipeline.addGlobalFilter(new IpFilter(Set.of(),
Set.of()), 0) stating it’s intentionally permissive and will be replaced,
otherwise change the call to pass actual values (e.g., load whitelist/blacklist
from configuration or environment and pass those Sets into new IpFilter) so
IpFilter.doFilter() enforces the intended IP rules. Ensure references to the
IpFilter constructor and the doFilter method remain consistent when you wire in
config-loading logic.
- Around line 42-44: Remove the redundant router.registerPlugin("/", ...) call
and stop using the all-matching "/*" for StaticFilesPlugin because that wildcard
prevents SimpleRouter's notFoundPlugin from ever running; instead register
StaticFilesPlugin on a specific static prefix (e.g., "/static/*") or other
explicit paths and register NotFoundPlugin as the catch-all ("/*") so
SimpleRouter's fallback behavior is preserved—update the calls to
router.registerPlugin and leave NotFoundPlugin as the final wildcard handler.

In `@src/main/java/org/juv25d/filter/SecurityHeadersFilter.java`:
- Line 13: SecurityHeadersFilter is currently annotated as `@Global`(order = 4) so
it runs last and can be skipped by upstream short-circuiting filters, causing
security headers to be missing on error/redirect responses; change the `@Global`
annotation on the SecurityHeadersFilter class from order = 4 to order = 0 so it
runs outermost (wrapping all downstream filters) and guarantees the try/finally
always applies its headers to every response.

---

Duplicate comments:
In `@src/main/java/org/juv25d/App.java`:
- Line 38: SecurityHeadersFilter is currently added at position 4 so it never
runs for responses short-circuited earlier (IpFilter, RateLimitingFilter,
RedirectFilter); move it back to the outermost position so its try/finally can
always add headers. Update the pipeline wiring to register SecurityHeadersFilter
with order 0 (or before the other global filters) in App.java (the
pipeline.addGlobalFilter call) so SecurityHeadersFilter.wrap/try-finally logic
executes around all downstream filters and covers error/redirect responses.
Ensure no other filter is registered with order 0 to avoid ordering conflicts.

---

Nitpick comments:
In `@src/main/java/org/juv25d/App.java`:
- Around line 25-26: The inline registration of RateLimitingFilter is hard to
read; refactor the conditional that calls pipeline.addGlobalFilter(new
RateLimitingFilter(config.getRequestsPerMinute(), config.getBurstCapacity()), 1)
into a clear multi-line if block: call config.isRateLimitingEnabled() in the if
condition, instantiate RateLimitingFilter on its own line using
config.getRequestsPerMinute() and config.getBurstCapacity(), then pass that
instance to pipeline.addGlobalFilter(...) on a separate line (preserving the
priority arg 1); this improves readability and matches surrounding code style.

In `@src/main/java/org/juv25d/filter/LoggingFilter.java`:
- Line 14: Replace the System.out.println call in LoggingFilter with a
structured logger: add a private static final Logger (e.g.,
LoggerFactory.getLogger(LoggingFilter.class)) to the LoggingFilter class and
replace the println(req.method() + " " + req.path()) with a logger call (e.g.,
logger.info or logger.debug) that uses parameterized logging like
logger.info("{} {}", req.method(), req.path()); also add the necessary import
for the logger implementation (SLF4J/your project's logging facade) and remove
the stdout call so request logging goes through the centralized logger.

Comment thread src/main/java/org/juv25d/App.java Outdated
Comment thread src/main/java/org/juv25d/App.java
Comment thread src/main/java/org/juv25d/filter/SecurityHeadersFilter.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/main/java/org/juv25d/filter/RedirectFilter.java (1)

31-32: Nit: stray blank line between Javadoc and annotation.

The blank line at line 31 between the closing */ and @Global(order = 3) is inconsistent with how other filter classes (RateLimitingFilter, etc.) position their class-level annotation immediately after the Javadoc.

✏️ Proposed fix
  * </pre>
  */
-
 `@Global`(order = 3)
 public class RedirectFilter implements Filter {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/filter/RedirectFilter.java` around lines 31 - 32,
Remove the stray blank line between the class Javadoc and the class-level
annotation in RedirectFilter so the annotation appears immediately after the
closing */; specifically, move the `@Global`(order = 3) annotation up to directly
follow the Javadoc for the RedirectFilter class to match other filters like
RateLimitingFilter.
src/main/java/org/juv25d/App.java (1)

27-28: Expand the inline if-block for readability.

The opening brace, body, and closing brace are all collapsed onto two lines, which makes the conditional registration hard to scan at a glance.

✏️ Proposed fix
-        if (config.isRateLimitingEnabled()) {pipeline.addGlobalFilter(new RateLimitingFilter(
-            config.getRequestsPerMinute(), config.getBurstCapacity()), 2);}
+        if (config.isRateLimitingEnabled()) {
+            pipeline.addGlobalFilter(new RateLimitingFilter(
+                config.getRequestsPerMinute(), config.getBurstCapacity()), 2);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/App.java` around lines 27 - 28, The inline
if-statement registering the RateLimitingFilter is compressed onto two lines and
hurts readability; expand the conditional into a normal multi-line block: use if
(config.isRateLimitingEnabled()) { on its own line, then call
pipeline.addGlobalFilter(new RateLimitingFilter(config.getRequestsPerMinute(),
config.getBurstCapacity()), 2); on the next line, then close the block with } on
its own line. Locate the conditional that references
config.isRateLimitingEnabled(), RateLimitingFilter, and pipeline.addGlobalFilter
and reformat it to a standard three-line if-block for clarity.
🤖 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/juv25d/App.java`:
- Around line 36-38: Logging is registered last (pipeline.addGlobalFilter(new
LoggingFilter(), 4)) so LoggingFilter misses requests that are short-circuited
by IpFilter, RateLimitingFilter, and RedirectFilter; move LoggingFilter to
highest precedence so it wraps the chain (or change its implementation to log in
a try/finally around chain.doFilter), and update the `@Global` order annotations
on LoggingFilter, IpFilter, RateLimitingFilter, and RedirectFilter to reflect
the new ordering (e.g., LoggingFilter order=1, IpFilter order=2,
RateLimitingFilter order=3, RedirectFilter order=4) so the logger runs before
downstream filters and always observes blocked/redirected requests.

---

Duplicate comments:
In `@src/main/java/org/juv25d/App.java`:
- Line 25: IpFilter is being instantiated with empty Set arguments which makes
IpFilter.doFilter a no-op; update the pipeline.addGlobalFilter call to pass real
whitelist/blacklist sets (e.g., values loaded from configuration or environment)
or change the construction to use the intended source (e.g.,
IpFilter.of(config.getWhitelist(), config.getBlacklist()) or similar) so the
filter enforces rules; locate the IpFilter constructor usage in the
addGlobalFilter call and replace the empty Set.of() arguments with the proper
collections or a factory method that supplies configured IP sets.
- Around line 42-44: Remove the redundant router.registerPlugin("/", ...) call
and stop using a broad "/*" pattern that swallows the NotFoundPlugin; instead
register StaticFilesPlugin on a specific path (e.g. "/static/*") using
router.registerPlugin("...static.../*", new StaticFilesPlugin()) and ensure
router.registerPlugin("/notfound", new NotFoundPlugin()) remains or is
registered last so NotFoundPlugin can act as the fallback; adjust the pattern
and registration order around router.registerPlugin and the
StaticFilesPlugin/NotFoundPlugin to restore proper fallback behavior.

---

Nitpick comments:
In `@src/main/java/org/juv25d/App.java`:
- Around line 27-28: The inline if-statement registering the RateLimitingFilter
is compressed onto two lines and hurts readability; expand the conditional into
a normal multi-line block: use if (config.isRateLimitingEnabled()) { on its own
line, then call pipeline.addGlobalFilter(new
RateLimitingFilter(config.getRequestsPerMinute(), config.getBurstCapacity()),
2); on the next line, then close the block with } on its own line. Locate the
conditional that references config.isRateLimitingEnabled(), RateLimitingFilter,
and pipeline.addGlobalFilter and reformat it to a standard three-line if-block
for clarity.

In `@src/main/java/org/juv25d/filter/RedirectFilter.java`:
- Around line 31-32: Remove the stray blank line between the class Javadoc and
the class-level annotation in RedirectFilter so the annotation appears
immediately after the closing */; specifically, move the `@Global`(order = 3)
annotation up to directly follow the Javadoc for the RedirectFilter class to
match other filters like RateLimitingFilter.

Comment thread src/main/java/org/juv25d/App.java Outdated

@fmazmz fmazmz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, this will make things easier and more professional.

Is it not a perfect time for us to remove the order from the Plugin method parameters, so that we dont have to apply the order twice?

Right now each filter class has the annotation with the order inside. But then in App.java we are writing the order again during instantiation.

There should be a way for us to read the order directly from the class annotation inside the respective methods in Plugin.
That way App.java doesnt have to care about specifying the order as it is handled automatically inside Plugin.

Just a thought.

@kristinaxm

Copy link
Copy Markdown
Author

Yes exactly, that was my thinking as well! :)

For this PR I just wanted to add the orderering and make sure the filters are in correct order.

Then in the next PR, as I mentioned in the issue, we can decide how we want to handle it properly, whether we read them inside pipeline, add some type of automation or refactor so the responsibility is handled in one place..?

Since we havent decided this yet, I would take this small step for now. (: @fmazmz

@kristinaxm
kristinaxm requested a review from fmazmz February 21, 2026 17:51

@addee1 addee1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice update 👍
This makes the filter order much clearer and easier to follow. Adding @global(order = X) directly on the filters feels cleaner. The order also makes sense with blocking filters before redirect. Overall, this makes the pipeline easier to understand.

Good job :)

@kristinaxm

Copy link
Copy Markdown
Author

Thanks for the reviews, @fmazmz and @addee1 ! 💯

@kristinaxm
kristinaxm merged commit 251a3a8 into main Feb 22, 2026
1 of 2 checks passed
@kristinaxm
kristinaxm deleted the feature/add-annotations-to-filters branch February 22, 2026 20:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/main/java/org/juv25d/App.java (2)

32-36: Externalise redirect rules to configuration instead of hardcoding them.

Paths like /old-page, /temp, and /docs/* are effectively runtime policy. Hardcoding them in App.java means every redirect change requires a rebuild and redeploy. ConfigLoader is already wired in; the redirect rules should be loaded from it (or from a dedicated config file) and passed into RedirectFilter.

♻️ Sketch of config-driven approach
-        List<RedirectRule> redirectRules = List.of(
-            new RedirectRule("/old-page", "/new-page", 301),
-            new RedirectRule("/temp", "https://example.com/temporary", 302),
-            new RedirectRule("/docs/*", "/documentation/", 301)
-        );
+        // Load redirect rules from configuration (e.g., redirect.properties or ConfigLoader)
+        List<RedirectRule> redirectRules = config.getRedirectRules();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/App.java` around lines 32 - 36, The redirect rules
are hardcoded in App.java; change this to load them from the existing
configuration system by using ConfigLoader (or a dedicated config source) to
deserialize a list of RedirectRule entries and pass that list into
RedirectFilter at startup; replace the List.of(...) instance (and any use of new
RedirectRule(...) in App) with code that reads redirect definitions (pattern,
target, status) from the config, validates them, constructs RedirectRule
objects, and injects the resulting collection into RedirectFilter so rule
changes are driven by config rather than code.

29-30: Expand the single-line if block for readability.

The compact style is inconsistent with every other addGlobalFilter call in this block.

♻️ Proposed formatting fix
-        if (config.isRateLimitingEnabled()) {pipeline.addGlobalFilter(new RateLimitingFilter(
-            config.getRequestsPerMinute(), config.getBurstCapacity()), 3);}
+        if (config.isRateLimitingEnabled()) {
+            pipeline.addGlobalFilter(new RateLimitingFilter(
+                config.getRequestsPerMinute(), config.getBurstCapacity()), 3);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/juv25d/App.java` around lines 29 - 30, The single-line if
in App.java that adds the RateLimitingFilter should be expanded to a multi-line
block for readability and consistency: replace the compact if
(config.isRateLimitingEnabled()) { pipeline.addGlobalFilter(new
RateLimitingFilter(config.getRequestsPerMinute(), config.getBurstCapacity()),
3); } with a standard block that checks config.isRateLimitingEnabled(),
constructs the RateLimitingFilter using config.getRequestsPerMinute() and
config.getBurstCapacity(), and calls pipeline.addGlobalFilter(...) on its own
indented line(s), matching the formatting used for the other addGlobalFilter
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/java/org/juv25d/App.java`:
- Around line 32-36: The redirect rules are hardcoded in App.java; change this
to load them from the existing configuration system by using ConfigLoader (or a
dedicated config source) to deserialize a list of RedirectRule entries and pass
that list into RedirectFilter at startup; replace the List.of(...) instance (and
any use of new RedirectRule(...) in App) with code that reads redirect
definitions (pattern, target, status) from the config, validates them,
constructs RedirectRule objects, and injects the resulting collection into
RedirectFilter so rule changes are driven by config rather than code.
- Around line 29-30: The single-line if in App.java that adds the
RateLimitingFilter should be expanded to a multi-line block for readability and
consistency: replace the compact if (config.isRateLimitingEnabled()) {
pipeline.addGlobalFilter(new RateLimitingFilter(config.getRequestsPerMinute(),
config.getBurstCapacity()), 3); } with a standard block that checks
config.isRateLimitingEnabled(), constructs the RateLimitingFilter using
config.getRequestsPerMinute() and config.getBurstCapacity(), and calls
pipeline.addGlobalFilter(...) on its own indented line(s), matching the
formatting used for the other addGlobalFilter calls.

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.

Apply order nums to existing filters

3 participants