This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 5e5961308abe CAMEL-25318: camel-smpp - split an 8-bit message by its 
length in bytes, into at most 255 segments (#27394)
5e5961308abe is described below

commit 5e5961308abe8caad4a3db72cd0d71e0896238ad
Author: allthingssecurity <[email protected]>
AuthorDate: Tue Oct 6 14:52:44 2026 +0530

    CAMEL-25318: camel-smpp - split an 8-bit message by its length in bytes, 
into at most 255 segments (#27394)
    
    * CAMEL-25318: camel-smpp - split an 8-bit message by its length in bytes, 
into at most 255 segments
    
    An 8-bit short message holds 140 octets, and the splitter splits the bytes 
of
    the message, but SmppSmCommand created the 8-bit splitter with the length of
    the body read as a String. A body whose bytes decode to fewer characters 
(non
    ASCII text in the UTF-8 exchange charset, or binary data with valid UTF-8
    sequences) was sent as one short message of more than 140 octets.
    
    SmppSplitter.split applied the 255 segment limit before counting the last
    partial segment, so a message of 255 full segments and a few bytes more was
    split into 256 segments, and the total number in the UDH was written as 0.
    
    Create the 8-bit splitter with the length of the encoded message, and count
    the last partial segment before applying the limit.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    * CAMEL-25318: camel-smpp - at most 255 segments with the national language 
splitter too, and upgrade guide note
    
    SmppNLSTSplitter.split had the same clamp before the remainder: a message 
of exactly 255 full segments and a remainder
    gave 256 segments, with 0 as the total in the UDH. The segment count is now 
a shared helper of SmppSplitter used by
    both splitters.
    
    Review nits: createSplitter takes the already encoded body (no second 
conversion), and SmppSplitBodyTest sets the
    charset of the exchange to UTF-8.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    * CAMEL-25318: camel-smpp - Keep the createSplitter(Message) signature so 
overriding subclasses keep working
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../camel/component/smpp/SmppNLSTSplitter.java     |  14 +--
 .../apache/camel/component/smpp/SmppSmCommand.java |   4 +-
 .../apache/camel/component/smpp/SmppSplitter.java  |  26 +++--
 .../camel/component/smpp/SmppNLSTSplitterTest.java |  20 ++++
 .../camel/component/smpp/SmppSplitBodyTest.java    | 119 +++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  10 ++
 6 files changed, 170 insertions(+), 23 deletions(-)

diff --git 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppNLSTSplitter.java
 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppNLSTSplitter.java
index d903ec7f9e59..7311156e55a6 100644
--- 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppNLSTSplitter.java
+++ 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppNLSTSplitter.java
@@ -93,17 +93,9 @@ public class SmppNLSTSplitter extends SmppSplitter {
 
         int segmentLength = getSegmentLength();
 
-        // determine how many messages
-        int segmentNum = message.length / segmentLength;
-        int messageLength = message.length;
-        if (segmentNum > MAX_SEG_COUNT) {
-            // this is too long, can't fit, so chop
-            segmentNum = MAX_SEG_COUNT;
-            messageLength = segmentNum * segmentLength;
-        }
-        if ((messageLength % segmentLength) > 0) {
-            segmentNum++;
-        }
+        // determine how many messages (a message that is too long is chopped)
+        int segmentNum = getSegmentCount(message.length, segmentLength);
+        int messageLength = Math.min(message.length, segmentNum * 
segmentLength);
 
         byte[][] segments = new byte[segmentNum][];
 
diff --git 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSmCommand.java
 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSmCommand.java
index 360cd0f7e152..e60628f0dd3b 100644
--- 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSmCommand.java
+++ 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSmCommand.java
@@ -92,7 +92,9 @@ public abstract class SmppSmCommand extends 
AbstractSmppCommand {
         String body = message.getBody(String.class);
 
         if (SmppUtils.is8Bit(alphabet)) {
-            splitter = new Smpp8BitSplitter(body.length());
+            // an 8-bit message is split by its length in bytes (the signature 
of this method is kept, so subclasses
+            // that override it keep working, at the cost of encoding the body 
again)
+            splitter = new Smpp8BitSplitter(getShortMessage(message).length);
         } else if (alphabet == Alphabet.ALPHA_UCS2) {
             splitter = new SmppUcs2Splitter(body.length());
         } else {
diff --git 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSplitter.java
 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSplitter.java
index 57068ed99005..651df692199b 100644
--- 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSplitter.java
+++ 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppSplitter.java
@@ -138,17 +138,9 @@ public class SmppSplitter {
 
         int segmentLength = getSegmentLength();
 
-        // determine how many messages
-        int segmentNum = message.length / segmentLength;
-        int messageLength = message.length;
-        if (segmentNum > MAX_SEG_COUNT) {
-            // this is too long, can't fit, so chop
-            segmentNum = MAX_SEG_COUNT;
-            messageLength = segmentNum * segmentLength;
-        }
-        if ((messageLength % segmentLength) > 0) {
-            segmentNum++;
-        }
+        // determine how many messages (a message that is too long is chopped)
+        int segmentNum = getSegmentCount(message.length, segmentLength);
+        int messageLength = Math.min(message.length, segmentNum * 
segmentLength);
 
         byte[][] segments = new byte[segmentNum][];
 
@@ -184,6 +176,18 @@ public class SmppSplitter {
         return segments;
     }
 
+    /**
+     * The number of segments for a message of the given length: one more for 
a partial last segment, and at most
+     * {@link #MAX_SEG_COUNT}, as the UDH holds the total in one octet.
+     */
+    protected static int getSegmentCount(int messageLength, int segmentLength) 
{
+        int segmentNum = messageLength / segmentLength;
+        if (messageLength % segmentLength > 0) {
+            segmentNum++;
+        }
+        return Math.min(segmentNum, MAX_SEG_COUNT);
+    }
+
     protected boolean isSplitRequired() {
         return getCurrentLength() > getMessageLength();
     }
diff --git 
a/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppNLSTSplitterTest.java
 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppNLSTSplitterTest.java
index 702d0558d1b0..c4d8db2f64ce 100644
--- 
a/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppNLSTSplitterTest.java
+++ 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppNLSTSplitterTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.component.smpp;
 
+import java.util.Arrays;
+
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -87,4 +89,22 @@ public class SmppNLSTSplitterTest {
                 result[1].length - 
SmppNLSTSplitter.UDHIE_NLI_MULTI_MSG_HEADER_REAL_LENGTH);
         assertEquals(message, firstShortMessage + secondShortMessage);
     }
+
+    @Test
+    public void splitIntoAtMost255Segments() {
+        // one byte more than 255 full segments
+        byte[] message = new byte[255 * SmppNLSTSplitter.MAX_SEG_BYTE_SIZE + 
1];
+        Arrays.fill(message, (byte) '1');
+
+        SmppSplitter splitter = new SmppNLSTSplitter(message.length, (byte) 
0x01);
+        byte[][] result = splitter.split(message);
+
+        assertEquals(255, result.length, "a long message is truncated to 255 
segments");
+        for (int i = 0; i < result.length; i++) {
+            assertEquals((byte) 255, result[i][4], "total number of segments 
in the UDH of segment " + (i + 1));
+            assertEquals((byte) (i + 1), result[i][5], "number of segment " + 
(i + 1) + " in its UDH");
+            
assertEquals(SmppNLSTSplitter.UDHIE_NLI_MULTI_MSG_HEADER_REAL_LENGTH + 
SmppNLSTSplitter.MAX_SEG_BYTE_SIZE,
+                    result[i].length, "length of segment " + (i + 1));
+        }
+    }
 }
diff --git 
a/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppSplitBodyTest.java
 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppSplitBodyTest.java
new file mode 100644
index 000000000000..056d1d85571f
--- /dev/null
+++ 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppSplitBodyTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.smpp;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePattern;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.jsmpp.bean.Alphabet;
+import org.jsmpp.session.SMPPSession;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+/**
+ * An 8-bit short message holds 140 octets: the body must be split by its 
length in bytes, into at most 255 segments.
+ */
+class SmppSplitBodyTest {
+
+    private SmppSubmitSmCommand command;
+
+    @BeforeEach
+    void setUp() {
+        command = new SmppSubmitSmCommand(mock(SMPPSession.class), new 
SmppConfiguration());
+    }
+
+    @Test
+    void eightBitBinaryBodyOfMoreThan140BytesIsSplit() throws Exception {
+        // 150 bytes, which happen to be 50 valid UTF-8 sequences (50 
characters when read as a String)
+        byte[] body = new byte[150];
+        for (int i = 0; i < body.length; i += 3) {
+            body[i] = (byte) 0xE2;
+            body[i + 1] = (byte) 0x82;
+            body[i + 2] = (byte) 0xAC;
+        }
+
+        byte[][] segments = splitEightBit(body);
+
+        assertEquals(2, segments.length, "a body of 150 bytes needs two 
segments");
+        assertSegments(body, segments);
+    }
+
+    @Test
+    void eightBitTextBodyOfMoreThan140BytesIsSplit() throws Exception {
+        // 100 characters, 200 bytes in UTF-8 (the charset of the exchange)
+        String text = "é".repeat(100);
+
+        byte[][] segments = splitEightBit(text);
+
+        assertEquals(2, segments.length, "a body of 200 bytes needs two 
segments");
+        assertSegments(text.getBytes(StandardCharsets.UTF_8), segments);
+    }
+
+    @Test
+    void eightBitBodyIsNotSplitIntoMoreThan255Segments() throws Exception {
+        // one byte more than 255 full segments
+        byte[] body = new byte[255 * Smpp8BitSplitter.MAX_SEG_BYTE_SIZE + 1];
+        Arrays.fill(body, (byte) 'a');
+
+        byte[][] segments = splitEightBit(body);
+
+        assertEquals(255, segments.length, "a long message is truncated to 255 
segments");
+        for (int i = 0; i < segments.length; i++) {
+            assertEquals((byte) 255, segments[i][4], "total number of segments 
in the UDH of segment " + (i + 1));
+            assertEquals((byte) (i + 1), segments[i][5], "number of segment " 
+ (i + 1) + " in its UDH");
+        }
+    }
+
+    @Test
+    void eightBitBodyOf140BytesIsNotSplit() throws Exception {
+        byte[] body = new byte[140];
+        Arrays.fill(body, (byte) 'a');
+
+        byte[][] segments = splitEightBit(body);
+
+        assertEquals(1, segments.length);
+        assertArrayEquals(body, segments[0]);
+    }
+
+    private byte[][] splitEightBit(Object body) throws Exception {
+        Exchange exchange = new DefaultExchange(new DefaultCamelContext(), 
ExchangePattern.InOut);
+        // the charset that a String body is converted with
+        exchange.setProperty(Exchange.CHARSET_NAME, 
StandardCharsets.UTF_8.name());
+        exchange.getIn().setHeader(SmppConstants.ALPHABET, 
Alphabet.ALPHA_8_BIT.value());
+        exchange.getIn().setBody(body);
+        return command.splitBody(exchange.getIn());
+    }
+
+    private static void assertSegments(byte[] body, byte[][] segments) {
+        ByteArrayOutputStream data = new ByteArrayOutputStream();
+        for (byte[] segment : segments) {
+            assertTrue(segment.length <= 140, "a segment holds at most 140 
bytes, was " + segment.length);
+            data.write(segment, SmppSplitter.UDHIE_HEADER_REAL_LENGTH, 
segment.length - SmppSplitter.UDHIE_HEADER_REAL_LENGTH);
+        }
+        assertArrayEquals(body, data.toByteArray());
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index d7961fa10ba7..d14da98a6000 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -648,6 +648,16 @@ Alert notifications already worked this way. Two 
consequences:
 `SmppEndpoint#createOnAcceptDeliverSmExchange` and 
`SmppEndpoint#createOnAcceptDataSm` are deprecated. They are no
 longer used by the component; build such an exchange through the consumer 
instead.
 
+==== 8-bit messages are split by their length in bytes
+
+With an 8-bit alphabet (`alphabet=4`, or an 8-bit data coding), the producer 
now decides whether to split a message by
+the length in bytes of the body as it is sent, instead of the number of 
characters of the body read as a String. A
+body of at most 140 characters but more than 140 bytes (for example text with 
multi-byte characters, or binary data)
+is now sent as several segments; with `splittingPolicy=TRUNCATE` it is 
truncated to 140 bytes, and with
+`splittingPolicy=REJECT` it is rejected, where it was previously sent as one 
oversize short message. A message that
+needs more than 255 segments is cut to 255 segments: with exactly 255 full 
segments and a remainder, the total number
+of segments in the UDH was written as `0` (also with the national language 
splitter).
+
 === camel-console
 
 The `context` developer console no longer counts routes created by Kamelets in 
its `routesTotal` and `routesStarted`

Reply via email to