Skip to content

[backport camel-spring-boot-4.18.x] CAMEL-24593: platform-http-starter - delete multipart uploads when the exchange is done - #1965

Merged
Croway merged 1 commit into
apache:camel-spring-boot-4.18.xfrom
Croway:backport/1934-to-camel-spring-boot-4.18.x
Sep 9, 2026
Merged

Croway merged 1 commit into
apache:camel-spring-boot-4.18.xfrom
Croway:backport/1934-to-camel-spring-boot-4.18.x

Conversation

@Croway

@Croway Croway commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Update: Rebased onto the now-merged CAMEL-24577 (#1958) to resolve a merge conflict introduced
once #1958 landed on camel-spring-boot-4.18.x after this PR was opened. Both fixes now coexist in
SpringBootPlatformHttpBinding.java (CAMEL-24577's path-variable extraction and CAMEL-24593's
upload-cleanup registration live in separate, non-overlapping parts of the class), and the
"Path variables" doc section (CAMEL-24577) is restored alongside the "File uploads" section this PR
adds in platform-http.adoc — see the updated "Depends on" section below.

Cherry-pick of #1934 onto camel-spring-boot-4.18.x.

Original PR: #1934 — platform-http-starter - delete multipart uploads when the exchange is done
JIRA: CAMEL-24593

What it fixes

SpringBootPlatformHttpBinding.populateAttachments() copies every accepted multipart upload out of
the servlet container into the servlet temp directory, and uses that copy as the attachment
DataSource and, for a single upload, as the Path message body and the CamelFilePath header.
Because MultipartFile.transferTo() moves the container's part file, the container's own
end-of-request cleanup can no longer find it, and nothing in the starter deleted the copy either —
every accepted upload stayed on disk for the life of the process.

The copy was introduced in CAMEL-21461 so the body can be a Path and the attachment stays readable
after the servlet request has completed, which is a good reason to own the file — but owning it means
being responsible for removing it too. The other HTTP bindings don't have this problem:
camel-http-common reads the part through the container-managed file (which the container deletes),
and camel-platform-http-vertx already defaults deleteUploadedFilesOnEnd to true.

The binding now tracks the temp files it created for a request and registers a Synchronization
via ExchangeExtension.addOnCompletion that deletes them once the exchange is done being routed —
after the HTTP response has already been written, so nothing currently reading the files is
affected. A new opt-out property, camel.component.platform-http.server.delete-uploaded-files-on-end
(default true), mirrors the Vert.x option, for routes that hand the file to something that reads it
after the exchange completes.

This is a resource-leak fix only: no default-behavior change to existing routes, and existing public
constructors are unchanged.

Depends on CAMEL-24496

This cherry-picks onto camel-spring-boot-4.18.x on top of CAMEL-24496 (#1952), merged into this
branch just before this PR was opened. It did not apply fully cleanly — four conflicts, all
because this branch's SpringBootPlatformHttpAutoConfiguration/SpringBootPlatformHttpEngine carry
pre-existing 4.18.x-only code (a more elaborate executor-selection strategy for virtual threads /
Spring Security executors, and env.getProperty("server.port", ...) instead of a ServerProperties
bean) that main no longer has. I resolved these by keeping this branch's existing structure and
wiring the new SpringBootPlatformHttpServerProperties/deleteUploadedFilesOnEnd plumbing through it,
rather than pulling in main's unrelated refactors:

  • SpringBootPlatformHttpAutoConfiguration.java — kept this branch's @Configuration/executor-selection
    logic, added SpringBootPlatformHttpServerProperties to @EnableConfigurationProperties and threaded
    it into springBootPlatformHttpEngine(...).
  • SpringBootPlatformHttpEngine.java — kept this branch's mutable executor field / constructor
    chaining (no executor == null branch in createConsumer, which is a pre-existing main-only
    refactor unrelated to this fix), added the deleteUploadedFilesOnEnd field, the new 3-arg
    constructor, and wired setDeleteUploadedFilesOnEnd onto the created consumer.
  • components-starter/camel-platform-http-starter/src/main/docs/platform-http.adoc — this PR was
    originally opened before CAMEL-24577 ([backport camel-spring-boot-4.18.x] CAMEL-24577: platform-http path variables follow the path Spring matched #1958, "platform-http path variables follow the path Spring
    matched") was merged into camel-spring-boot-4.18.x, so the file conflicted twice: first against
    CAMEL-24496 (resolved by taking only the new "File uploads" section), and again after rebasing onto
    the now-merged CAMEL-24577, whose "Path variables" section landed in the same spot. Both sections
    now coexist: "Path variables" (CAMEL-24577) followed by "File uploads" (this PR).
  • docs/spring-boot/modules/ROOT/pages/starters/platform-http.adoc — modify/delete conflict: this
    aggregated docs page no longer exists on this branch (removed as part of an earlier, unrelated docs
    restructuring), so I kept it deleted rather than resurrecting it.
  • SpringBootPlatformHttpBinding.java and SpringBootPlatformHttpConsumer.java auto-merged cleanly
    against CAMEL-24496 and, after the later rebase, against CAMEL-24577 as well — they coexist correctly
    with CAMEL-24496's filename-whitelist checks (which run earlier in populateAttachments(), before
    this fix's cleanup registration) and with CAMEL-24577's path-variable extraction (getMatchedPath()),
    which lives in an entirely separate part of the class.

I also updated the two new test classes' CamelSpringBootTest import from
org.apache.camel.test.spring.junit6 (main) to org.apache.camel.test.spring.junit5 (this branch is
Spring Boot 3 / JUnit 5), matching every neighboring test in this module.

Verification on this branch

  • Cherry-pick required manual conflict resolution as described above (not a clean apply); resolution
    reviewed against both sides of the diff. Re-verified after rebasing onto the merged CAMEL-24577.
  • Built with ./mvnw -DskipTests -Dfastinstall install -pl components-starter/camel-platform-http-starter -am
    (parent/camel-version temporarily repointed to the released 4.18.4 for local resolution, reverted
    afterwards) — succeeded.
  • Ran ../../mvnw verify in components-starter/camel-platform-http-starter: 124 tests run, 1
    failure. The failure is SpringBootPlatformHttpRequestTimeoutTest.testGetAsync, a pre-existing,
    unrelated flake on this branch (retried 3 times by surefire, failed each time) — not caused by this
    change. Both this fix's tests (SpringBootPlatformHttpUploadCleanupTest,
    SpringBootPlatformHttpUploadCleanupDisabledTest) and CAMEL-24577's path-variable tests
    (SpringBootPlatformHttpPathVariableTest, SpringBootPlatformHttpBindingPathVariableTest) passed.
  • Confirmed git diff origin/camel-spring-boot-4.18.x --stat only shows the 10 files touched by the
    cherry-picked commit itself (temporary pom.xml/catalog build artifacts reverted).

Claude Code on behalf of Federico Mariani

@Croway
Croway requested review from davsclaus and oscerd September 9, 2026 12:41
…e exchange is done

SpringBootPlatformHttpBinding.populateAttachments() copies every accepted
multipart upload into the servlet temp directory and uses that copy as the
attachment DataSource and, for a single upload, as the Path message body and
the CamelFilePath header. MultipartFile.transferTo() moves the container's part
file, so the container's own end-of-request cleanup no longer finds it, and
nothing in the starter deleted the copy either: every upload the application
accepted stayed on disk for the life of the process.

The copy was introduced in CAMEL-21461 so the body can be a Path and the
attachment stays readable after the servlet request completed, which is a good
reason to own the file - but owning it means removing it. The other HTTP
bindings do not leak: camel-http-common reads the part through the container
managed file, which the container deletes, and camel-platform-http-vertx has
deleteUploadedFilesOnEnd defaulting to true.

The binding now collects the temp files it created for a request and registers
a Synchronization through ExchangeExtension.addOnCompletion that deletes them
when the exchange is done being routed. The DataSource and the Path body point
at the files until then, so they cannot be deleted any earlier; the consumer
writes the HTTP response before doneUoW, so the response is already out.

The opt-out mirrors the Vert.x option. Endpoint options are defined in upstream
camel-platform-http and cannot be extended from here, so the option is a new
starter owned configuration class, SpringBootPlatformHttpServerProperties,
next to the existing camel.component.platform-http.server.undertow.accesslog
properties:

    camel.component.platform-http.server.delete-uploaded-files-on-end=false

It is wired from the auto configuration through the engine and the consumer
onto the binding, and defaults to true. Existing public constructors are
unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit cf374a9)
@Croway
Croway force-pushed the backport/1934-to-camel-spring-boot-4.18.x branch from cbd32a3 to 13a5812 Compare September 9, 2026 15:06
@Croway
Croway merged commit 3f2d55e into apache:camel-spring-boot-4.18.x Sep 9, 2026
2 checks passed
@Croway
Croway deleted the backport/1934-to-camel-spring-boot-4.18.x branch September 9, 2026 15:10
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