Hello Claus, First of thanks for the quick response. My colleague and I did some further analysis of the issue and we suspect that there are more facets to it. See below the analysis prepared with the assistance of AI.
------------------------- # Camel issue draft > File at https://github.com/apache/camel/issues (Apache Camel now uses GitHub > Issues). > Components: camel-core (PollEnricher / PollDynamicAware), camel-file, > camel-ftp. ## Title Dynamic `pollEnrich` `fileName` referencing an exchange property is silently ignored; result depends on `allowOptimisedComponents` (documented as an optimisation only) ## Affects version(s) - Reproduced on **4.14.7**. Likely all versions since **4.11.0** (when `PollDynamicAware` was introduced — CAMEL-21733). - Java 21. ## Summary When `pollEnrich` uses a dynamic endpoint URI whose `fileName` references an **exchange property**, the filter silently fails to match the intended file. The behaviour depends on `allowOptimisedComponents` (default `true`), which is documented purely as a performance optimisation — yet toggling it changes the **result**: - `allowOptimisedComponents(true)` (default): the wrong file is picked, or none is found. - `allowOptimisedComponents(false)`: works correctly. The same `fileName` referencing a **header** works with the optimisation on (for the directory-LIST path), but a header does **not** work on the FTP `useList=false` path. This asymmetry is undocumented and the failure is silent (no warning/exception). ## Steps to reproduce (camel-file, no server needed) Two files exist in `target/in`: `aaa.txt` and `wanted.txt`. ```java from("direct:start") .setProperty("myFile", constant("wanted.txt")) // dynamic pollEnrich; fileName references an EXCHANGE PROPERTY .pollEnrich() .simple("file:target/in?fileName=${exchangeProperty.myFile}&noop=true&idempotent=false") .cacheSize(1) .timeout(2000) .to("mock:result"); ``` Send one message to `direct:start` and inspect the enriched body / `CamelFileName`. ## Expected The enricher retrieves `wanted.txt` (the file named by the exchange property). ## Actual - With `allowOptimisedComponents(true)` (default): the enricher retrieves **`aaa.txt`** (the first file returned by the directory scan) — the `fileName` filter is silently ignored. - Change the expression to a **header** (`${header.myFile}` + `.setHeader("myFile", ...)`): now it correctly retrieves `wanted.txt` with the optimisation on. - Add `.allowOptimisedComponents(false)`: the original **exchange-property** version also correctly retrieves `wanted.txt`. ### FTP variant (camel-ftp) On the FTP consumer the failure mode also depends on `useList`: - `useList=true`: as above, the first listed file is taken (filter ignored). - `useList=false`: the poll finds no file at all and times out (`fileName` resolves empty), for **both** exchange-property and header expressions. ## Root cause (as read in 4.14.7 sources) 1. With the optimisation on, `GenericFilePollDynamicAware.resolveStaticUri()` re-injects the *original, unresolved* `fileName` expression into a shared static endpoint, so `fileName` is evaluated **lazily** by the consumer. 2. The consumer evaluates `fileName` against a **dummy** exchange: - `useList=true` → `GenericFileConsumer.isMatched()` → `GenericFileHelper.createDummy(endpoint, dynamic, file)`, which copies only **headers** and **variables** from the routing exchange — **not exchange properties**: ```java if (dynamic.getMessage().hasHeaders()) { MessageHelper.copyHeaders(dynamic.getMessage(), dummy.getMessage(), true); if (dynamic.hasVariables()) { dummy.getVariables().putAll(dynamic.getVariables()); } } ``` So `${exchangeProperty.X}` resolves to `null`. And because `isMatched` skips the `fileName` filter when it evaluates to `null` (`if (result != null) { if (!name.equals(result)) ... }`), **all** files match and the first is taken. - `useList=false` → `FtpConsumer.pollNamedFile()` evaluates the expression against `ExchangeHelper.getDummy(context)` — a **completely empty** exchange (no headers, variables, or properties) — so the name resolves empty and no file is polled. 3. With `allowOptimisedComponents(false)`, `PollEnricher` resolves the URI expression **eagerly against the routing exchange**, yielding a literal `fileName`, which works regardless of `useList` and regardless of whether the source was a header or a property. ## Why this is a bug (or at least should change) 1. `allowOptimisedComponents` is documented as an optimisation; enabling/disabling it should not change results. Here it silently changes correctness. 2. The optimised path **silently** produces a wrong result (wrong file, or no file) when the dynamic `fileName` references data it cannot resolve, instead of failing fast or falling back. 3. `createDummy` copies headers **and** variables but **not** exchange properties — an undocumented asymmetry. Separately, variables are only copied when `dynamic.getMessage().hasHeaders()` is true, which looks like a latent bug (a message with variables but no headers won't have its variables carried over). 4. `isMatched` treating an empty/`null` `fileName` as "match everything" turns a mis-resolved dynamic filename into "grab the first file", which is a surprising and dangerous default for a named-file poll. ## Suggested fix (any of) - Include exchange properties in `GenericFileHelper.createDummy` carry-over (and fix the variables-only-if-headers nesting), so property-based `fileName` expressions behave like header-based ones. - Or have `GenericFilePollDynamicAware` detect that the `fileName` expression references data it does not support on the lazy path (e.g. `${exchangeProperty...}`) and skip the optimisation for that endpoint (fall back to eager resolution) rather than silently no-op. - At minimum: document the limitation (only headers/variables are supported in an optimised dynamic `pollEnrich` `fileName`) and log a WARN when a dynamic `fileName` resolves to empty/null on the poll path. ## Workaround Set `.allowOptimisedComponents(false)` on the `pollEnrich`, which resolves the dynamic URI eagerly against the routing exchange into a literal `fileName`. Marcus Ionker Senior Product Evangelist, Messaging Fax: +49 89 1250400-1202 Mobile: +49 176 15528012 Email: [email protected] -------------------------------------------------------------------- retarus GmbH. Aschauer Straße 30, 81549 München, Germany. https://www.retarus.com On 2026/07/21 14:01:54 Marcus Ionker wrote: > Hello Apache Camel Developers, > > I suspect a bug GenericFilePollDynamicAware.resolveStaticUri. I did not find > an existing Jira ticket in ASF pertaining to this issue.I prepared the > following ticket, which contains a detailed description of the problem. I > hope I have not overlooked anything. My current workaround is to set > allowOptimisedComponents to false. Maybe the problem is related to the fact > that although the file endpoints (incl. ftp, fps and sftp) support > expressions in the URI, they are resolved with an empty dummy exchange, > rather than an actual exchange. > > Best Regads, > Marcus Ionker > > ---------------------------- > > Bug: > > GenericFilePollDynamicAware.resolveStaticUri incorrectly returns static URI > for dynamic fileNameexpressions, breaking PollEnricher optimization > > Description: > > There is an optimization regression in > org.apache.camel.component.file.GenericFilePollDynamicAware when used > alongside pollEnrich. > > When a pollEnrich endpoint contains a dynamic simple expression targeting a > parameter like fileName (e.g., in a File or FTP/SFTP endpoint), > GenericFilePollDynamicAware.resolveStaticUri fails to return null. Other > implementations of PollDynamicAwareSupport correctly yield null when a > component configuration is dynamic. Because it incorrectly evaluates to a > non-null static URI base, PollEnricher.java falls into an invalid > optimization block. > > Root Cause Analysis: > > GenericFilePollDynamicAware.resolveStaticUri is returning the resolved > dynamic URI instead of null > > if (fileName) { > Map<String, Object> params = entry.getProperties(); > Map<String, Object> originalParams = > URISupport.parseQuery(URISupport.extractQuery(entry.getOriginalUri())); > compute(originalParams, PROP_FILE_NAME, params); > return asEndpointUri(exchange, uri, params); > } else { > return uri; > } > > Inside org.apache.camel.processor.PollEnricher.process(), the framework > attempts to evaluate and optimize the dynamic endpoint. > > try { > recipient = expression.evaluate(exchange, Object.class); > if (dynamicAware != null) { > // if its the same scheme as the pre-resolved dynamic aware then we can > optimise to use it > String originalUri = uri; > String uri = resolveUri(exchange, recipient); > String scheme = resolveScheme(exchange, uri); > if (dynamicAware.getScheme().equals(scheme)) { > PollDynamicAware.DynamicAwareEntry entry = > dynamicAware.prepare(exchange, uri, originalUri); > if (entry != null) { > staticUri = dynamicAware.resolveStaticUri(exchange, entry); > if (staticUri != null) { > if (LOG.isDebugEnabled()) { > LOG.debug("Optimising poll via PollDynamicAware component: > {} to use static uri: {}", scheme, > URISupport.sanitizeUri(staticUri)); > } > } > } > } > } > }... > > > The Breakdown: > > - expression.evaluate(...) correctly resolves the dynamic endpoint. > > - However, because GenericFilePollDynamicAware.resolveStaticUri provides a > fallback staticUri string rather than null, PollEnricher assumes it can > safety optimize the consumer. > > - Instead of using the fully evaluated runtime endpoint string (recipient), > the route erroneously falls back to using the unparsed simple expression > itself as the literal target. > > - The FTP/File consumer subsequently tries to poll using the literal > expression text, resulting in a lookup failure because the string syntax does > not match an actual file name on the remote target file system. > > Current Workaround: > > Configuring the EIP via > .pollEnrich().simple("...").allowOptimisedComponents(false)bypasses > PollDynamicAware entirely, forcing Camel to correctly interpret the parsed > expression string on every exchange. > > Expected Fix: > > GenericFilePollDynamicAware.resolveStaticUri must return null if the URI is > dynamic to disable optimization, mirroring standard PollDynamicAwareSupport > behavior.
smime.p7s
Description: S/MIME cryptographic signature
