[
https://issues.apache.org/jira/browse/CAMEL-25100?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18121438#comment-18121438
]
Claus Ibsen commented on CAMEL-25100:
-------------------------------------
Merged in https://github.com/apache/camel/pull/27002 (commit b0fed420c974),
thanks!
_Claude Code on behalf of davsclaus_
> camel-core - OnCompletion EIP (default after-consumer mode) cannot read a
> body spooled to disk by stream caching, as the spool file is deleted before
> the onCompletion runs
> ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25100
> URL: https://issues.apache.org/jira/browse/CAMEL-25100
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Minor
> Fix For: 4.23.0
>
>
> With stream caching spooled to disk ({{spoolEnabled=true}}, a body over the
> spool threshold), the body is a {{FileInputStreamCache}}. Its temporary file
> is deleted by an on completion that
> {{FileInputStreamCache.TempFileManager.addExchange}} registers on the
> exchange ({{FileInputStreamCache.java:255}}). That on completion is a
> {{SynchronizationAdapter}} with the default order 0
> ({{SynchronizationAdapter.java:49}}).
> An {{onCompletion()}} in the default mode ({{modeAfterConsumer}}) also runs
> as an on completion of the unit of work
> ({{OnCompletionSynchronizationAfterConsumer}}), with the order
> {{Ordered.LOWEST}} ({{OnCompletionProcessor.java:326}}). {{UnitOfWorkHelper}}
> sorts the on completions by their order ({{UnitOfWorkHelper.java:112}}), so
> the spool file is always deleted before the onCompletion starts. The
> onCompletion then works on a copy of the exchange ({{prepareExchange}},
> {{OnCompletionProcessor.java:280}}) whose body is the
> {{FileInputStreamCache}} of the deleted file.
> Results with a small spool threshold and a streamed body of 16 KB (and, in
> the original investigation, 100 KB with a 1 KB threshold, 3 runs each):
> * {{onCompletion().convertBodyTo(byte[].class)}}: fails with a
> {{NoSuchFileException}} for the spool file, every time.
> * {{onCompletion().onFailureOnly()}} on a route that fails: the same.
> * {{onCompletion().parallelProcessing()}}: the onCompletion route stops at
> the first step that reads the body, and nothing is logged at WARN or above.
> * {{onCompletion().modeBeforeConsumer()}}: works, as it runs before the unit
> of work is done.
> * Without an onCompletion, and for bodies below the threshold (kept in
> memory), there is no problem.
> Typical onCompletion uses (archive or audit the message, send a notification
> with the payload) therefore fail as soon as the payload is big enough to be
> spooled.
> h3. Proposed fix
> * The stream cache clean-up on completion returns {{Ordered.LOWEST}} from
> {{getOrder()}}, so it runs after the other on completions of the exchange,
> which may still read the body.
> * The after-consumer onCompletion uses {{Ordered.LOWEST - 1}}, so it still
> runs before the on completions that want to be last (such as the FTP and SMB
> consumers' disconnect), and {{prepareExchange}} gives its copy its own
> reference to the stream cache: it removes {{CamelStreamCacheUnitOfWork}} from
> the copy and replaces the body with {{sc.copy(copy)}}, as the Wire Tap EIP
> does ({{WireTapProcessor.java:282}}). The copy keeps the file until the
> onCompletion route is done, which also covers {{parallelProcessing}}.
> * With {{parallelProcessing}}, a task that never runs must release the copy's
> reference: when the thread pool rejects the task, discards it because it is
> shut down, or drops it when the processor shuts the pool down with
> {{shutdownNow}}. Otherwise the file would be kept until the spool directory
> is removed when the context stops. Since CAMEL-25012 the processor also
> counts its parallel tasks as pending for the graceful shutdown, so a task
> that never runs must also stop being counted. That already happened for a
> rejected task and for the tasks dropped by {{shutdownNow}}, but not for a
> task that a thread pool that is shut down discards without an exception (such
> as with the {{CallerRuns}} policy): it stayed counted as pending. Both are
> now done in one place: each parallel task is either run or discarded, never
> both, and a discarded task is no longer counted as pending and releases its
> copy.
> The first point changes the order of every stream cache clean-up, not only
> for routes with an onCompletion: a spooled file is now deleted after the
> other on completions of the exchange instead of among the order 0 ones. The
> file is only deleted later than before, never earlier, so nothing that worked
> before can now see a deleted file. On completions that share the order
> {{Ordered.LOWEST}} keep their reverse registration order among themselves.
> This needs an upgrade guide note.
> With this change the onCompletion variants above read the full body, and no
> spool file is left behind afterwards, also when a parallel task is rejected,
> discarded, or dropped at shutdown.
> Affected: long-standing, all 4.x versions (the order of the two on
> completions has not changed).
> Duplicate check (2026-09-28): JIRA "onCompletion" with "stream cache",
> "stream caching", "spool" or "FileInputStreamCache": CAMEL-2776 (2010,
> deletion on close), CAMEL-2636 and unrelated issues. CAMEL-25012 (fixed) is
> about the graceful shutdown of the parallel onCompletion, not this, but this
> change builds on it. GitHub pull requests: nothing on this.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)