shashank created CAMEL-25723:
--------------------------------

             Summary: camel-zipfile - ZipAggregationStrategy silently replaces 
a zip entry when a later message has the same entry name, e.g. two orders.csv 
from different folders of a recursive file consumer, so the earlier file is lost
                 Key: CAMEL-25723
                 URL: https://issues.apache.org/jira/browse/CAMEL-25723
             Project: Camel
          Issue Type: Bug
          Components: camel-zipfile
            Reporter: shashank
            Assignee: shashank


Since CAMEL-13399 (Camel 3.0) {{ZipAggregationStrategy}} adds each message to 
the zip file through a {{ZipFileSystem}}: {{Files.copy(file, dest, 
REPLACE_EXISTING)}} for a file body (line 319) and {{Files.write(dest, buffer, 
CREATE, TRUNCATE_EXISTING)}} for other bodies (line 334, main 826a664c5f16). 
When the zip already has an entry with that name, the entry is replaced and the 
content of the earlier message is gone, without an error or a log.

With the default strategy ({{new ZipAggregationStrategy()}}, 
{{preserveFolderStructure=false}}) the entry name of a file is its name only. A 
recursive file consumer that polls {{in/a/orders.csv}} ("from A") and 
{{in/b/orders.csv}} ("from B") and aggregates them with the documented 
{{completionFromBatchConsumer().eagerCheckCompletion()}} produces one zip with 
one entry {{orders.csv}} = "from B". {{useFilenameHeader=true}} with a repeated 
{{CamelFileName}} behaves the same. Folders per day or per sender with the same 
file names are common.

Trigger: default options of {{ZipAggregationStrategy}}; two messages of one 
group with the same entry name (file bodies with the same file name, or the 
same {{CamelFileName}} with {{useFilenameHeader=true}}).

Why CAMEL-13399 used these options: the issue was about speed (the strategy 
rebuilt the whole zip for every message). The commit "Optimized 
ZipAggregationStrategy to use ZipFileSystem" wrote with {{REPLACE_EXISTING}}, 
and the follow-up "Truncate zip entry, if already exists" added 
{{TRUNCATE_EXISTING}} so that a shorter content does not leave the tail of the 
old one in the entry. Neither commit nor the issue discusses two messages with 
the same name; before 3.0 the {{ZipOutputStream}} rebuild failed with 
"duplicate entry" in that case.

{{TarAggregationStrategy}} on main does not lose the content but does not 
separate the names either: it rebuilds the tar for each message, copies the 
existing entries ({{copyExistingEntries}}, TarAggregationStrategy.java 285-292) 
and appends the new one under the same name ({{addNewEntry}} 277-283, 
{{createNewEntry}} 321-327). The tar then holds two entries {{orders.csv}}; a 
usual extraction writes both to the same path and keeps only the last.

h3. Reproduction

New test {{ZipAggregationStrategySameFileNameTest}} (7 tests). Without the fix 
6 fail, for example:
* a recursive file route over only {{a/orders.csv}} and {{b/orders.csv}} with 
{{completionFromBatchConsumer().eagerCheckCompletion()}}: {{expected: <[orders 
(1).csv=from B, orders.csv=from A]> but was: <[orders.csv=from B]>}};
* the same route with a third file {{c/other.csv}}: {{but was: 
<[orders.csv=from B, other.csv=from C]>}};
* the strategy called directly with {{a/orders.csv}} then {{b/orders.csv}}, and 
with {{useFilenameHeader=true}} and two byte bodies named {{report.txt}}: the 
zip holds only the second content;
* five files, four named {{orders.csv}} and one really named {{orders 
(1).csv}}: main keeps 2 of the 5 contents;
* names without an extension ({{README}}, {{.profile}}).
The seventh test checks that {{preserveFolderStructure=true}} still gives 
{{a/orders.csv}} and {{b/orders.csv}}; it passes on main as well and is there 
to show that the fix does not rename entries whose names differ.

h3. Proposed fix

When the zip file already has an entry with the name, write the new entry under 
the first free name with " (n)" before the extension: {{orders.csv}}, {{orders 
(1).csv}}, {{orders (2).csv}}. A leading dot is part of the name ({{.profile 
(1)}}), a name without an extension gets the number at the end ({{README 
(1)}}), and with {{preserveFolderStructure}} only the file name part changes. 
The free name is checked against the entries of the zip file itself (one lookup 
in the open {{ZipFileSystem}} when the name is free, one more per earlier entry 
of the same name otherwise), so a file really named {{orders (1).csv}} is not 
overwritten either. A name with two dots gets the number before the last one 
({{data.tar (1).gz}}). The write options of CAMEL-13399 stay as they are.

A rename was chosen over failing the second message: with 
{{eagerCheckCompletion}} the batch completion is checked before the 
aggregation, so when the duplicate is the last file of the poll it completes 
the batch and then fails; the group with the first file is never completed and 
the duplicate fails again at every poll. A rename needs no rollback and keeps 
every message in the zip of its batch.

Behaviour change: entry names can now differ from the file names (or the 
{{CamelFileName}} header) when names repeat within a group. A note goes into 
the 4.24 upgrade guide and the zipfile data format page (and its catalog copy).

With the fix the camel-zipfile tests pass: 56 run, 0 failures. 
{{ZipSplitterRouteTest.testSplitterLargeFileWithSpoolEnabled}} (zip splitter, 
stream cache spool directory not yet empty when checked) is flaky in this 
environment with and without the fix: it does not use the aggregation strategy, 
failed on main's code in 2 of 2 runs here (passed on a surefire rerun), and 
failed in 3 runs and passed in the last one with the fix.

Affected: main (tested); camel-4.23.x, camel-4.22.x, camel-4.18.x and 
camel-4.14.x have the same {{REPLACE_EXISTING}} / {{TRUNCATE_EXISTING}} lines 
at 319 / 334 (read, not run).

Duplicate check (2026-10-17): JIRA text "ZipAggregationStrategy" (12 issues, 
none about entries with the same name), "zip aggregation same name"; GitHub 
pull requests "ZipAggregationStrategy", "zipfile aggregation": none.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to