Skip to content

fix: Add AutoCloseable to clean up the context when an exception occurs#121

Merged
mvallim merged 2 commits intomvallim:feature/prepare-releasefrom
Rafaellinos:fix/clean-context-on-exception
Feb 15, 2026
Merged

fix: Add AutoCloseable to clean up the context when an exception occurs#121
mvallim merged 2 commits intomvallim:feature/prepare-releasefrom
Rafaellinos:fix/clean-context-on-exception

Conversation

@Rafaellinos
Copy link
Copy Markdown
Contributor

@Rafaellinos Rafaellinos commented Feb 15, 2026

Fix: Ensure proper cleanup of ValidationContext and ProcessorContext on exception

Fixes #117

Problem

When a predicate throws an exception during validation (e.g., NullPointerException, NumberFormatException), the ValidationContext and ProcessorContext ThreadLocal variables are not properly cleaned up. This leads to:

  • ThreadLocal memory leaks - stale context data remains attached to the thread
  • Incorrect validation behavior - subsequent validations on the same thread may use corrupted state

Root Cause

In RuleProcessorStrategy#process(), when ProcessorContext.get().create() is called and a predicate later throws an exception, the execution stack unwinds without ever reaching ProcessorContext.get().remove().

Similarly, in AbstractValidator#validate(), if ruleProcessor.process() throws an exception, ValidationContext.get().getValidationResult() is never called, meaning ValidationContext.remove() is never invoked.

Problem flow:

  1. RuleProcessorStrategy#process() calls ProcessorContext.get().create()
  2. Stream operations iterate over collection items, calling PredicateBuilder#test()
  3. If a predicate throws an exception, execution jumps out of the stream
  4. ProcessorContext.get().remove() / ValidationContext.remove() is never reached
  5. ThreadLocal retains stale context data

Solution

Leverage the AutoCloseable interface that both ValidationContext.Context and ProcessorContext.Context already implement, using try-with-resources to guarantee cleanup even when exceptions occur.

Changes

AbstractValidator.java:

@Override
public ValidationResult validate(final T instance) {
  try (ValidationContext.Context context = ValidationContext.get()) {
    ruleProcessor.process(instance, this);
    return context.getValidationResult();
  }
}

RuleProcessorStrategy.java:

default <E> boolean process(final Object obj, final Collection<E> values, final Rule<E> rule) {
  try (ProcessorContext.Context context = ProcessorContext.get()) {
    return values.stream().map(value -> {
      context.inc();
      return this.process(obj, value, rule);
    }).collect(Collectors.toList()).stream().allMatch(result -> result);
  }
}

default <E> boolean process(final Collection<E> values, final Rule<E> rule) {
  try (ProcessorContext.Context context = ProcessorContext.get()) {
    return values.stream().map(value -> {
      context.inc();
      return this.process(value, rule);
    }).collect(Collectors.toList()).stream().allMatch(result -> result);
  }
}

ValidationContext.java:

  • Removed ValidationContext.remove() call from getValidationResult() since cleanup is now handled by try-with-resources via the close() method

Testing

Added/improved test cases to verify that contexts are properly cleaned up after exceptions:

  • testSuccessWhenBrokenPredicate - validates that after a NullPointerException from a null description, subsequent validations work correctly
  • testSuccessWhenBrokenCollectionPredicate - validates that after a NumberFormatException from invalid numeric parsing, the ProcessorContext counter is reset and subsequent validations work correctly

Impact

  • No breaking changes - existing API remains unchanged
  • Improved reliability - contexts are always cleaned up, preventing memory leaks
  • Thread safety - subsequent validations on the same thread will have clean state

@mvallim mvallim changed the base branch from master to develop February 15, 2026 12:12
@mvallim mvallim changed the base branch from develop to feature/prepare-release February 15, 2026 12:13
…ption

# Conflicts:
#	src/test/java/br/com/fluentvalidator/ValidatorTest.java
#	src/test/java/br/com/fluentvalidator/predicate/MapPredicateTest.java
@mvallim mvallim merged commit 1b7224a into mvallim:feature/prepare-release Feb 15, 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.

[Bug]: Inconsistent state after exception

2 participants