Reto Peter created CAMEL-23066:
----------------------------------
Summary: 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
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}
[email protected]
\{code}
After (UUID v7, globally unique):
\{code}
[email protected]
\{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)