Skip to content

RAT-573: Make RAT safe for parallel Maven builds - #704

Open
gnodet wants to merge 6 commits into
apache:masterfrom
gnodet:quick-fix/thread-safe-default-log
Open

RAT-573: Make RAT safe for parallel Maven builds#704
gnodet wants to merge 6 commits into
apache:masterfrom
gnodet:quick-fix/thread-safe-default-log

Conversation

@gnodet

@gnodet gnodet commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Make the RAT Maven plugin safe for parallel builds (mvn -T4) by fixing three concurrency issues:

  1. DefaultLog (ThreadLocal) — Replace the shared static Log instance with a ThreadLocal<Log> so each Maven reactor thread gets its own logger. Without this, thread A's DefaultLog.setInstance(makeLog()) overwrites thread B's logger, causing log output to be mislabeled or lost.

  2. DeprecationReporter (ThreadLocal) — Same pattern: replace the shared static Consumer<Option> consumer with a ThreadLocal. The generated BaseRatMojo constructor calls setDeprecationReporter() per-module, creating the same overwrite race as DefaultLog.

  3. OptionCollection.parseCommands() (synchronized) — Add synchronized because this method uses shared mutable state that is not thread-safe:

    • Arg enum's OptionGroup.selected field is mutated by DefaultParser.parse()
    • Converters.FILE_CONVERTER.workingDirectory is overwritten during argument processing

    When two threads parse concurrently, thread A's --input-exclude setting gets overwritten by thread B's parse, causing exclusions to be silently skipped. The synchronized serializes only the config parsing phase — actual file scanning and license checking still runs in parallel.

Testing

  • New RAT-573 integration test: multi-module project (3 modules + parent) run with -T4, each module containing a src.apt file that must be excluded via **/src.apt pattern
  • Before the fix: ~10% failure rate under -T4 (exclusion silently skipped → RAT reports unlicensed files)
  • After the fix: 12/12 passes locally

Known remaining statics (out of scope)

As noted by @Claudenw, there are additional statics that would need attention for full thread-safety:

  • SPDXMatcherFactory.INSTANCE — singleton matcher factory
  • MatcherBuilderTracker — tracks custom matchers via static state
  • StandardCollection's AbstractFileProcessorBuilder — static builder instances

These are more deeply embedded and would require more invasive changes. This PR focuses on the most impactful fixes that resolve the reported parallel build failures.

Test plan

  • Full mvn clean install passes locally
  • RAT-573 integration test passes with -T4
  • RAT-268 integration test passes (restored to original single-threaded form)
  • CI matrix passes (all 12 platform/JDK combos)

🤖 Generated with Claude Code

… Maven builds

Replace the plain static fields in DefaultLog and DeprecationReporter
with ThreadLocal storage so that each thread (e.g. parallel Maven
reactor threads using `mvn -T`) gets its own logger and deprecation
reporter instance.

Previously, every Mojo constructor overwrote the JVM-wide
DefaultLog.instance singleton, causing log messages to be silently
routed to the wrong module's logger during parallel builds. The same
race condition affected DeprecationReporter.consumer.

The public API (getInstance/setInstance, getLogReporter/setLogReporter)
is preserved so all existing callers continue to work unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ottlinger

ottlinger commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

@gnodet Thanks for your contribution! There seems to be another known issue in RAT preventing multithreaded parsing of ignore files (filed as RAT-553).

@Claudenw do you see any sideeffects with your current restructurings or should we merge this PR with a changelog?

@ottlinger ottlinger changed the title fix: make DefaultLog and DeprecationReporter thread-safe for parallel Maven builds RAT-573: fix: make DefaultLog and DeprecationReporter thread-safe for parallel Maven builds Jul 26, 2026
@ottlinger
ottlinger requested review from Claudenw and ottlinger July 26, 2026 21:27

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

LGTM, I've added a changelog and ran the branch locally. @Claudenw feel free to merge if you are okay as well.

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

This is a great start on an issue that has been in the back of my mind. However, these changes will not make RAT thread safe for multi threaded Maven.

There is a class called the MatcherBuilderTracker that also contains statics. This class tracks any matchers that have been declared. Basically you can write your own matcher for some special condition and add it to the run. Those are tracked in as static variable in the MatcherBuilderTracker and are loaded from the configuration file declaration.

The SPDXMatcher factory has a static instance of the matcher factory. The structure is used to ensure that the SPDX matchers checks are clustered. Calling one matcher check causes all the matcher checks to run at once. This is because all the matchers use the same regular expression pattern and it is far more efficient to run the match check once across the file and extract the SPDX id and then verify that its accepted rather than run the expression find for each SPDX id that is accepted.

There may also be issues with reading files, but as I recall that was just an idea that we could speed up the processing by using multiple threads to check the files in parallel.

I would suggest that you change the definition of RAT-573 to make it read that it will make RAT safe for parallel builds, and address the other issues as well.

Finally, there is a massive change coming wherein we split the UIs off into their own projects. This will not be impacted by the changes I see here. However, it does move the conversion from RAT command line options to Maven options into a Maven specific section and Maven will need to track the mappings. I don't think there are any statics in the prototype code, but I will keep an eye out.

Thank you for this contribution. It is greatly appreciated.

Comment thread apache-rat-plugin/src/it/RAT-268/invoker.properties
gnodet and others added 2 commits July 27, 2026 10:19
…el builds

OptionCollection.parseCommands() uses shared mutable state: the Arg
enum's OptionGroup instances (whose 'selected' field is mutated by
DefaultParser.parse()) and Converters.FILE_CONVERTER (whose
workingDirectory field is set during argument processing).

When multiple Maven reactor threads call parseCommands() concurrently
(e.g. mvn -T4), they corrupt each other's parse state. This causes
options like --input-exclude to be silently skipped, resulting in
files being incorrectly reported as having unapproved licenses.

Making the method synchronized serializes only the configuration
parsing phase; the actual file scanning and license checking still
runs in parallel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gration test

Restore RAT-268 to its original single-threaded form as requested by
maintainer (integration tests named RAT-XXX are tied to specific Jira
tickets and should not be modified for unrelated purposes).

Add a new RAT-573 integration test that exercises the rat plugin under
parallel Maven builds (-T4) with a multi-module project containing
excluded files (src.apt). This directly tests the thread-safety fixes
in DefaultLog, DeprecationReporter, and OptionCollection.parseCommands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet

gnodet commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for the thorough review @Claudenw!

I've addressed both points:

  1. RAT-268 restored — reverted to original single-threaded form. Created a dedicated RAT-573 integration test that runs with -T4 to exercise the thread-safety fixes.

  2. Broader scope acknowledged — I've updated the PR description to document the remaining statics that would need attention for full thread-safety (SPDXMatcherFactory.INSTANCE, MatcherBuilderTracker, StandardCollection's builders). These are more deeply embedded and would require more invasive changes, so I've scoped this PR to the three most impactful fixes that resolve the reported parallel build failures:

    • DefaultLog → ThreadLocal
    • DeprecationReporter → ThreadLocal
    • OptionCollection.parseCommands() → synchronized

The synchronized on parseCommands() was the key fix — it prevents concurrent DefaultParser.parse() calls from corrupting each other's OptionGroup.selected state and Converters.FILE_CONVERTER.workingDirectory, which was the root cause of exclusions being silently skipped under -T4.

Happy to update the RAT-573 Jira description if you'd like it reframed as "make RAT safe for parallel builds" rather than just the DefaultLog issue.

@gnodet gnodet changed the title RAT-573: fix: make DefaultLog and DeprecationReporter thread-safe for parallel Maven builds RAT-573: Make RAT safe for parallel Maven builds Jul 27, 2026
@gnodet
gnodet marked this pull request as ready for review July 27, 2026 10:27
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