You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/02/24 18:22:00 UTC
[jira] [Updated] (CAMEL-23066) camel-as2 - AS2Utils.createMessageId() produces duplicate Message-IDs after JVM restart (nanoTime-based)
[ https://issues.apache.org/jira/browse/CAMEL-23066?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Claus Ibsen updated CAMEL-23066:
--------------------------------
Summary: camel-as2 - AS2Utils.createMessageId() produces duplicate Message-IDs after JVM restart (nanoTime-based) (was: AS2Utils.createMessageId() produces duplicate Message-IDs after JVM restart (nanoTime-based))
> camel-as2 - AS2Utils.createMessageId() produces duplicate Message-IDs after JVM restart (nanoTime-based)
> --------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-23066
> URL: https://issues.apache.org/jira/browse/CAMEL-23066
> Project: Camel
> Issue Type: Bug
> Components: camel-as2
> Affects Versions: 4.18.0
> Reporter: Reto Peter
> Priority: Minor
> Attachments: AS2Utils.java
>
>
> {panel:title=Problem}
> \{{AS2Utils.createMessageId()}} generates Message-IDs using \{{System.nanoTime()}} combined with a random number. \{{nanoTime()}} is relative to an arbitrary JVM start time and resets on every JVM restart. This produces duplicate Message-IDs that violate the AS2
> specification (RFC 4130) requirement for globally unique message identifiers. Receiving AS2 servers that use the Message-ID as a primary key (e.g., OpenAS2) reject the message with a duplicate key violation.
> \{panel}
> h3. Root Cause
> The current implementation in \{{AS2Utils.java}}:
> \{code:java|title=Current code in AS2Utils.java (Camel 4.18.0)}
> private static SecureRandom generator = new SecureRandom();
> public static String createMessageId(String fqdn) {
> return "<" + Long.toString(System.nanoTime(), 36) + "."
> + Long.toString(generator.nextLong(), 36) + "@" + fqdn + ">";
> }
> \{code}
> The problem is \{{System.nanoTime()}}:
> - It returns a value relative to an arbitrary origin that resets on each JVM start
> - After a server restart, the first few messages will have the same nanoTime range as messages sent during a previous JVM lifetime
> - Combined with the random part, there is a significant collision probability across restarts because the nanoTime part provides the "sequential" component
> Example: Server sends messages, restarts, sends messages again. The nanoTime values from the second run overlap with values from the first run. If the random part happens to be similar, duplicate Message-IDs are produced.
> h3. Steps to Reproduce
> Configure a Camel AS2 endpoint and send several messages to a partner
> Restart the JVM (e.g., application server restart, deployment)
> Send messages again immediately after restart
> Observe: some Message-IDs may collide with IDs generated in the previous JVM lifetime
> Partners that store Message-IDs as unique keys (e.g., OpenAS2) reject the message with a primary key violation
> h3. Proposed Fix
> Replace the \{{nanoTime}}-based generation with UUID v7 (RFC 9562), which embeds a 48-bit millisecond wall-clock timestamp plus 74 bits of cryptographic randomness. This guarantees uniqueness across JVM restarts and provides lexicographic sortability by
> creation time.
> \{code:diff}
> - private static SecureRandom generator = new SecureRandom();
> - private static final SecureRandom RANDOM = new SecureRandom();
> - public static String createMessageId(String fqdn) {
> return "<" + Long.toString(System.nanoTime(), 36) + "."
> + Long.toString(generator.nextLong(), 36) + "@" + fqdn + ">";
> // UUID v7 (RFC 9562): 48-bit unix_ts_ms + 4-bit version + 12-bit random + 2-bit variant + 62-bit random
> long timestamp = System.currentTimeMillis();
> long msb = ((timestamp & 0xFFFF_FFFF_FFFFL) << 16) // 48-bit timestamp → bits 63-16
> | (0x7L << 12) // version 7 → bits 15-12
> | (RANDOM.nextLong() & 0xFFFL); // rand_a 12 bits → bits 11-0
> long lsb = (RANDOM.nextLong() & 0x3FFF_FFFF_FFFF_FFFFL) // rand_b 62 bits → bits 61-0
> | 0x8000_0000_0000_0000L; // variant 10 → bits 63-62
> UUID uuidV7 = new UUID(msb, lsb);
> return "<" + uuidV7 + "@" + fqdn + ">";
> - }
> \{code}
> h4. Additional import needed
> \{code:java}
> import java.util.UUID;
> \{code}
> h3. Why UUID v7
> ┌────────────────────────────┬──────────────────────────────┬──────────────────────┬────────────────────────┐
> │ │ nanoTime (current) │ UUID v4 (randomUUID) │ UUID v7 (proposed fix) │
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
> │ Unique across JVM restarts │ No │ Yes │ Yes │
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
> │ Sortable by creation time │ No │ No │ Yes │
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
> │ Cryptographically random │ Partially (random part only) │ Yes │ Yes (74 random bits) │
> ├────────────────────────────┼──────────────────────────────┼──────────────────────┼────────────────────────┤
> │ RFC standard │ No │ RFC 9562 │ RFC 9562 │
> └────────────────────────────┴──────────────────────────────┴──────────────────────┴────────────────────────┘
> Note: \{{UUID.randomUUID()}} (v4) would also solve the uniqueness problem. UUID v7 is preferred because it additionally provides time-based sortability, which is useful for log analysis and debugging. Both are acceptable solutions.
> h3. Example Output
> Before (nanoTime-based, restarts cause collisions):
> \{code}
> 1r8k2f.5g9h3j@mycompany.com
> \{code}
> After (UUID v7, globally unique):
> \{code}
> 019572a3-1e4b-7def-8a3c-4f2b1c8d9e0a@mycompany.com
> \{code}
> h3. Test Scenario
> Tested with OpenAS2 as the receiving partner. Before the fix, after a JVM restart OpenAS2 rejected messages with duplicate Message-ID errors. After the fix, all Message-IDs are unique across restarts.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)