From ed0d40750a1256aba750e53f7f71f3a1539cbf0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Sapp=C3=A9=20Griot?= Date: Tue, 14 May 2024 00:35:58 +0200 Subject: [PATCH] node-name needs to be truncated because the byte array is not base64 Add unit test, stress test commented and updated the JavaDoc --- extensions/narayana-jta/runtime/pom.xml | 11 ++++ .../jta/runtime/NarayanaJtaRecorder.java | 11 +++- .../TransactionManagerConfiguration.java | 2 +- .../jta/runtime/NarayanaJtaRecorderTest.java | 56 +++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 extensions/narayana-jta/runtime/src/test/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorderTest.java diff --git a/extensions/narayana-jta/runtime/pom.xml b/extensions/narayana-jta/runtime/pom.xml index 730b86416c8800..3949057ceaf5e4 100644 --- a/extensions/narayana-jta/runtime/pom.xml +++ b/extensions/narayana-jta/runtime/pom.xml @@ -83,6 +83,17 @@ org.jboss.narayana.jts narayana-jts-integration + + + io.quarkus + quarkus-junit5-internal + test + + + io.quarkus + quarkus-junit5 + test + diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java index 83d9f1b865cfa1..103c31285bc321 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorder.java @@ -5,6 +5,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.Map; @@ -59,7 +60,15 @@ private static void shortenNodeName(TransactionManagerConfiguration transactions HASH_ALGORITHM_FOR_SHORTENING); final byte[] nodeNameAsBytes = originalNodeName.getBytes(); MessageDigest messageDigest224 = MessageDigest.getInstance(HASH_ALGORITHM_FOR_SHORTENING); - transactions.nodeName = new String(messageDigest224.digest(nodeNameAsBytes), StandardCharsets.UTF_8); + byte[] hashedByteArray = messageDigest224.digest(nodeNameAsBytes); + + //Encode the byte array in Base64 + //encoding the array might result in a longer array + byte[] base64Result = Base64.getEncoder().encode(hashedByteArray); + //truncate the array + byte[] slice = Arrays.copyOfRange(base64Result, 0, 28); + + transactions.nodeName = new String(slice, StandardCharsets.UTF_8); log.warnf("New node name is \"%s\"", transactions.nodeName); } diff --git a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java index 6719ea8e1d128d..cad532cc6d9a4f 100644 --- a/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java +++ b/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/TransactionManagerConfiguration.java @@ -27,7 +27,7 @@ public final class TransactionManagerConfiguration { * Whether the node name should be shortened if necessary. * The node name must not exceed a length of 28 bytes. If this property is set to {@code true}, and the node name exceeds 28 * bytes, the node name is shortened by calculating the SHA-224 hash, - * which has a length of 28 bytes. + * which has a length of 28 bytes, encoded to Base64 format and then shorten to 28 bytes. * * @see #nodeName */ diff --git a/extensions/narayana-jta/runtime/src/test/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorderTest.java b/extensions/narayana-jta/runtime/src/test/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorderTest.java new file mode 100644 index 00000000000000..2458f14673522f --- /dev/null +++ b/extensions/narayana-jta/runtime/src/test/java/io/quarkus/narayana/jta/runtime/NarayanaJtaRecorderTest.java @@ -0,0 +1,56 @@ +package io.quarkus.narayana.jta.runtime; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.nio.charset.StandardCharsets; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +public class NarayanaJtaRecorderTest { + + @Test + void testByteLength() { + TransactionManagerConfiguration transactions = new TransactionManagerConfiguration(); + transactions.shortenNodeNameIfNecessary = true; + // create nodeNames larger than 28 bytes + for (int i = 1; i < 100; i++) { + String originalNodeName = UUID.randomUUID().toString(); + NarayanaJtaRecorder r = new NarayanaJtaRecorder(); + transactions.nodeName = originalNodeName; + r.setNodeName(transactions); + int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length; + assertFalse(numberOfBytes > 28, + "node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes); + } + for (int i = 1; i < 1000; i++) { + byte[] data = new byte[i]; + NarayanaJtaRecorder r = new NarayanaJtaRecorder(); + transactions.nodeName = new String(data); + r.setNodeName(transactions); + int numberOfBytes = transactions.nodeName.getBytes(StandardCharsets.UTF_8).length; + assertFalse(numberOfBytes > 28, + "node name bytes still exceeded 28 bytes limit, number of bytes is " + numberOfBytes); + } + } + + //commented because it is a stress test + // @Test + void testCollision() { + SortedSet set = new TreeSet<>(); + TransactionManagerConfiguration transactions = new TransactionManagerConfiguration(); + transactions.shortenNodeNameIfNecessary = true; + // create a nodeName larger than 28 bytes + for (int i = 1; i < 1000000; i++) { + String originalNodeName = UUID.randomUUID().toString(); + NarayanaJtaRecorder r = new NarayanaJtaRecorder(); + transactions.nodeName = originalNodeName; + r.setNodeName(transactions); + assertFalse(set.contains(transactions.nodeName), "Collision of IDs for " + transactions.nodeName); + set.add(transactions.nodeName); + } + } + +}