Skip to content

IO-892: Add maximum traversal depth to FileAlterationObserver - #870

Merged
garydgregory merged 2 commits into
apache:masterfrom
zx8410-ops:IO-892-file-alteration-max-depth
Aug 17, 2026
Merged

IO-892: Add maximum traversal depth to FileAlterationObserver#870
garydgregory merged 2 commits into
apache:masterfrom
zx8410-ops:IO-892-file-alteration-max-depth

Conversation

@zx8410-ops

Copy link
Copy Markdown
Contributor

Thanks for your contribution to Apache Commons!

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance.
  • I used AI to create part of this pull request. OpenAI Codex assisted with analysis, implementation, tests, local verification, and preparation of this description.
  • Run a successful build using the default Maven goal with mvn.
  • Write unit tests that match behavioral changes.
  • Write a detailed pull request description.
  • Each commit has a meaningful subject line and body.

Summary

This PR adds an optional maximum traversal depth to FileAlterationObserver through Builder.setMaxDepth(int).

The observed root has depth 0. Setting maxDepth to 0 prevents listing entries below the root. At greater depths, entries at maxDepth are monitored, but their children are not listed and do not generate events.

The default remains Integer.MAX_VALUE, so existing builder usage and deprecated constructors preserve full recursive monitoring. The depth limit applies during both initialization and every checkAndNotify() cycle.

Why this is not a FileFilter

A regular FileFilter cannot provide the same traversal behavior. FileAlterationObserver calls directory.listFiles(fileFilter), so a depth-aware filter would still enumerate every child in a boundary directory. It also cannot express "monitor this directory but do not descend into it" with one boolean result.

This implementation checks the depth boundary before invoking listFiles().

Tests

Tests cover default compatibility, invalid values, boundary directory events, ignored events below the boundary, repeated check cycles, verification that listFiles() is not called below the boundary, custom root entries, and serialization round trips.

Local Windows/NTFS results for 100,000 files below a depth-1 boundary:

Mode Initialize Steady check average
Unlimited 9069.894 ms 8772.802 ms
maxDepth=1 126.719 ms 16.517 ms

Verification completed successfully:

  • mvn -q "-Dtest=FileAlterationObserverTest,FileEntryTest,FileAlterationMonitorTest" test
  • mvn -q -DskipTests checkstyle:check javadoc:javadoc package japicmp:cmp
  • git diff --check origin/master...HEAD

JIRA: https://issues.apache.org/jira/browse/IO-892

Add an optional maximum traversal depth to the observer builder and apply
the boundary before listing child entries during initialization and polling.

Preserve unlimited recursive traversal by default and cover the new boundary
semantics, serialization compatibility, and directory-listing behavior with
tests.

Generated-by: OpenAI Codex

Copilot AI left a comment

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.

Pull request overview

Adds an optional maximum traversal depth to FileAlterationObserver to allow depth-bounded directory monitoring (primarily to reduce traversal cost and suppress events below a configured boundary) while preserving the default unlimited-recursion behavior.

Changes:

  • Add FileAlterationObserver.Builder#setMaxDepth(int) (default Integer.MAX_VALUE) and wire depth checks into initialization and checkAndNotify() traversal.
  • Add tests covering max-depth defaults/validation, boundary behavior (including ensuring listFiles() is not invoked past the boundary), and serialization round-trips.
  • Update release notes to document the new builder option (IO-892).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java Introduces maxDepth configuration and uses it to prevent descending below the configured directory depth.
src/test/java/org/apache/commons/io/monitor/FileAlterationObserverTest.java Adds unit tests validating depth-limited traversal behavior and serialization behavior for the new field.
src/changes/changes.xml Adds a release note entry for Builder.setMaxDepth(int) (IO-892).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/changes/changes.xml Outdated

@garydgregory garydgregory 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.

Hello @zx8410-ops
Thank you for your PR. Please comments.

Comment thread src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java Outdated
Comment thread src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java Outdated
Use a primitive int for maxDepth, clarify the traversal-boundary Javadoc, and add the release-note developer metadata.

Generated-by: OpenAI Codex
@zx8410-ops

Copy link
Copy Markdown
Contributor Author

@garydgregory Thank you for the review. I addressed all four inline comments in 5cde511:

  • changed maxDepth to a primitive int assigned on every construction path;
  • simplified getMaxDepth() to return the field directly;
  • clarified the traversal-boundary Javadoc;
  • added the dev attribute to the IO-892 change entry.

Local verification passed for the focused monitor tests, Checkstyle, Javadoc, package, and japicmp checks. Java CI, CodeQL, and Dependency Review are also all passing.

Could you please take another look?

@garydgregory

garydgregory commented Aug 16, 2026

Copy link
Copy Markdown
Member

Hello @zx8410-ops
I propose #871 as an alternative to this PR to reuse Java's Files.walkFileTree() and its maxDepth feature instead of managing depth ourselves.

@zx8410-ops

Copy link
Copy Markdown
Contributor Author

Thank you. I agree that reusing Files.walkFileTree() is the preferable implementation direction and that #871 can supersede this PR.

One detail I think we should verify before closing #870: FileAlterationObserver still builds its entry tree recursively (createFileEntry() -> listFileEntries() -> listFiles()), while #871 starts a new walk with the full maxDepth for every directory. Because maxDepth is relative to each walk's start path, this appears to reset the depth at each recursive call. For example, with maxDepth=1, the root walk can return the boundary directory, and creating that entry then starts another maxDepth=1 walk from the boundary, potentially exposing its children. Returning SKIP_SUBTREE for non-start directories also makes each individual walk effectively list one level.

The deep-event test in #871 currently uses maxDepth=0, so it may not catch the maxDepth=1 case. Could we keep a test that sets maxDepth=1 and verifies that events below the boundary are ignored, and either pass the remaining depth (maxDepth - relativeDepth) or perform one root-relative walk?

With that boundary case covered, I am happy for #871 to replace #870 and for this PR to be closed.

@garydgregory
garydgregory merged commit c0b87d3 into apache:master Aug 17, 2026
29 of 45 checks passed
garydgregory added a commit that referenced this pull request Aug 17, 2026
@garydgregory

Copy link
Copy Markdown
Member

I went with this PR as the other one didn't do the file walking quite right (too deep).

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.

3 participants