Skip to content

fix(plugins): harden JAR checksum and guard registry reads#82

Merged
fronzec merged 1 commit into
mainfrom
fix/plugin-engine-hygiene-pr-a
Jul 2, 2026
Merged

fix(plugins): harden JAR checksum and guard registry reads#82
fronzec merged 1 commit into
mainfrom
fix/plugin-engine-hygiene-pr-a

Conversation

@fronzec

@fronzec fronzec commented Jun 28, 2026

Copy link
Copy Markdown
Owner

PR-A — Plugin engine hygiene (P3 + P5)

First slice of the plugin-engine backbone backlog (deep-dive audit, executable view "La vida de un JAR"). Two small, verified, self-contained fixes.

P3 — checksum reflects the persisted artifact

JarUploadService computed the SHA-256 from the multipart stream before writing to disk. Now it computes it from the stored file (ChecksumUtil.computeSha256(storedPath)), so the persisted digest matches the exact bytes that load-time integrity verification re-checks. Defensive hardening — removes a latent stream-vs-disk divergence rather than fixing a live bug.

P5 — guard concurrent registry reads

getPlugins, getPlugin, and getPluginBySource read pluginsByJobName without holding the monitor, racing with registerDynamicPlugin / unregisterDynamicPlugin (which already synchronize). They now take the pluginsByJobName monitor — matching the existing getRegisteredJobNames pattern. getPlugins returns an immutable snapshot; the DB lookup in getPluginBySource stays outside the lock.

Tests

  • New JarUploadServiceTest (the service had zero coverage) locks the P3 invariant: persisted checksum == digest of the bytes on disk, using a real in-memory JAR.
  • Full affected suite green: 18 tests (ChecksumUtil 7, PluginRegistryService 10, JarUploadService 1).

Out of scope (deferred)

  • P4 (classloaderRefs) — the audit flagged it as a "dead map to remove", but it is write-only scaffolding for classloader cleanup that P2/PR-C implements. Removing it now would churn registerDynamicPlugin's signature + its call sites and PR-C would re-add it properly typed. Folded into PR-C.

Follow-ups

  • PR-B = P1 (loadJob atomicity)
  • PR-C = P2 + P4 (unload + classloader close, done right)

Summary by CodeRabbit

  • Bug Fixes

    • Improved plugin loading to use safer, consistent access when reading registered plugins.
    • Fixed JAR upload handling so duplicate checks happen earlier and checksum values are based on the file actually saved.
  • Tests

    • Added coverage for JAR uploads to verify files are stored correctly and checksums match the saved content.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a400550f-9103-4479-8c8d-c448bd02fb9c

📥 Commits

Reviewing files that changed from the base of the PR and between 786dafb and 526196d.

📒 Files selected for processing (3)
  • fr-batch-service/src/main/java/com/fronzec/frbatchservice/batchjobs/plugins/PluginRegistryService.java
  • fr-batch-service/src/main/java/com/fronzec/frbatchservice/batchjobs/plugins/service/JarUploadService.java
  • fr-batch-service/src/test/java/com/fronzec/frbatchservice/batchjobs/plugins/service/JarUploadServiceTest.java

📝 Walkthrough

Walkthrough

PluginRegistryService read methods (getPlugins, getPlugin, getPluginBySource) are updated to synchronize on pluginsByJobName and return an immutable snapshot. In JarUploadService.uploadJar, the duplicate check is moved before file storage, and the SHA-256 checksum is computed from the stored path instead of the multipart stream. A new test class validates the checksum-source change.

Changes

PluginRegistryService synchronization

Layer / File(s) Summary
Synchronized reads and immutable snapshot
fr-batch-service/src/.../PluginRegistryService.java
Removes unused Collections import; getPlugins() returns List.copyOf() under synchronized(pluginsByJobName); getPlugin() and the classpath branch of getPluginBySource() also read the map under the same lock.

JAR upload checksum ordering

Layer / File(s) Summary
Upload step reordering and checksum source
fr-batch-service/src/.../service/JarUploadService.java
checkDuplicate(jobName) is called before storeFile(...); checksum is computed from storedPath (disk) instead of the multipart stream.
Test: disk-derived checksum
fr-batch-service/src/test/.../service/JarUploadServiceTest.java
New test class stubs repository/signature mocks, statically mocks ChecksumUtil for the Path overload only, invokes uploadJar, and asserts stored bytes, entity checksum, response checksum, and that ChecksumUtil.computeSha256(MultipartFile) is never called.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • fronzec/spring-batch-projects#29: Introduces the plugin registry dynamic lookup APIs (getPlugins, getPlugin) that this PR refines with synchronized access and snapshotting.

Poem

🐇 A rabbit checks the map with care,
Locks the door before peeking there.
The checksum now reads from the disk it stored,
No stale stream bytes shall be ignored.
Hop hop, the tests confirm it's right! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 two main changes: checksum hardening and synchronized plugin registry reads.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/plugin-engine-hygiene-pr-a

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.

fronzec commented Jun 28, 2026

Copy link
Copy Markdown
Owner Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

P3: compute the upload checksum from the persisted file instead of the
multipart stream, so the stored digest matches the bytes that load-time
integrity verification re-checks.

P5: synchronize getPlugins/getPlugin/getPluginBySource on the
pluginsByJobName monitor (matching getRegisteredJobNames) so reads no
longer race with concurrent register/unregister mutations; getPlugins
now returns an immutable snapshot.

Add JarUploadServiceTest covering the checksum invariant (first test
for the service).
@fronzec
fronzec force-pushed the fix/plugin-engine-hygiene-pr-a branch from ee0e267 to 526196d Compare June 28, 2026 05:53
@fronzec
fronzec marked this pull request as ready for review June 28, 2026 05:55
@fronzec
fronzec merged commit dcad8e8 into main Jul 2, 2026
8 checks passed
@fronzec
fronzec deleted the fix/plugin-engine-hygiene-pr-a branch July 2, 2026 06:53
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.

1 participant