INTEXT-118: Fox for long messages with newlines

JIRA: https://jira.spring.io/browse/INTEXT-118

It also tests long messages without newlines to verify that the fix works for both types of long messages.

INTEXT-118: Replace regular expression split with standard substring to split message into subparts with max length.
The regular expression used earlier failed splitting messages that contained newlines correctly. (See SmesMessageSpecificationTest)

Corrected spelling in constants

Polishing
This commit is contained in:
Flemming Jønsson
2014-11-15 19:14:15 +02:00
committed by Artem Bilan
parent ecd539d1dc
commit 85334c7e90
2 changed files with 239 additions and 4 deletions

View File

@@ -13,9 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.smpp.core;
import static org.springframework.integration.smpp.core.SmppConstants.*;
import static org.springframework.integration.smpp.core.SmppConstants.DATA_CODING;
import static org.springframework.integration.smpp.core.SmppConstants.DST_ADDR;
import static org.springframework.integration.smpp.core.SmppConstants.DST_NPI;
import static org.springframework.integration.smpp.core.SmppConstants.DST_TON;
import static org.springframework.integration.smpp.core.SmppConstants.ESM_CLASS;
import static org.springframework.integration.smpp.core.SmppConstants.MAXIMUM_CHARACTERS;
import static org.springframework.integration.smpp.core.SmppConstants.PRIORITY_FLAG;
import static org.springframework.integration.smpp.core.SmppConstants.PROTOCOL_ID;
import static org.springframework.integration.smpp.core.SmppConstants.REGISTERED_DELIVERY_MODE;
import static org.springframework.integration.smpp.core.SmppConstants.REPLACE_IF_PRESENT_FLAG;
import static org.springframework.integration.smpp.core.SmppConstants.SCHEDULED_DELIVERY_TIME;
import static org.springframework.integration.smpp.core.SmppConstants.SERVICE_TYPE;
import static org.springframework.integration.smpp.core.SmppConstants.SMS_MSG;
import static org.springframework.integration.smpp.core.SmppConstants.SM_DEFAULT_MSG_ID;
import static org.springframework.integration.smpp.core.SmppConstants.SRC_ADDR;
import static org.springframework.integration.smpp.core.SmppConstants.SRC_NPI;
import static org.springframework.integration.smpp.core.SmppConstants.SRC_TON;
import static org.springframework.integration.smpp.core.SmppConstants.VALIDITY_PERIOD;
import java.util.ArrayList;
import java.util.Collections;
@@ -56,6 +74,7 @@ import org.springframework.util.StringUtils;
*
* @author Josh Long
* @author Edge Dalmacio
* @author Flemming Jønsson
* @since 1.0
*/
public class SmesMessageSpecification {
@@ -387,8 +406,7 @@ public class SmesMessageSpecification {
* the {@link TypeOfNumber}
* @return this
*/
public
SmesMessageSpecification setSourceAddressTypeOfNumberIfRequired(TypeOfNumber sourceAddressTypeOfNumberIfRequired) {
public SmesMessageSpecification setSourceAddressTypeOfNumberIfRequired(TypeOfNumber sourceAddressTypeOfNumberIfRequired) {
if (this.sourceAddressTypeOfNumber == null) {
this.sourceAddressTypeOfNumber = sourceAddressTypeOfNumberIfRequired;
}
@@ -695,7 +713,7 @@ public class SmesMessageSpecification {
}
else {
if (s.length() > this.maxLengthSmsMessages) {
for (String split : s.split("(?<=\\G.{" + String.valueOf(this.maxLengthSmsMessages - 5) + "})")) {
for (String split : splitToLength(s, this.maxLengthSmsMessages - 5)) {
this.shortMessageParts.add(DataCodingSpecification.getMessageInBytes(split, dataCoding.toByte()));
}
}
@@ -706,6 +724,15 @@ public class SmesMessageSpecification {
return this;
}
private List<String> splitToLength(String text, int subStringSize) {
List<String> substrings = new ArrayList<String>((text.length() + subStringSize - 1) / subStringSize);
for (int idx = 0; idx < text.length(); idx += subStringSize) {
String substring = text.substring(idx, Math.min(text.length(), idx + subStringSize));
substrings.add(substring);
}
return substrings;
}
/**
* Setting short message using message_payload ({@link org.jsmpp.bean.OptionalParameter.Tag#MESSAGE_PAYLOAD})
* optional parameter

View File

@@ -0,0 +1,208 @@
/* Copyright 2014 the original author or authors.
*
* Licensed 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.springframework.integration.smpp.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.jsmpp.bean.DataCoding;
import org.jsmpp.bean.DataCodings;
import org.jsmpp.bean.SimpleDataCoding;
import org.junit.Test;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.test.util.ReflectionTestUtils;
/**
* @author Flemming Jønsson
* @since 1.0.1
*/
public class SmesMessageSpecificationTest {
private static final int PREFIX_BYTES = 5;
private static final String MULTI_LINE_PAYLOAD =
"1: This is a message longer than 140 chars and it contains newlines and foreign characters - æøå. " +
"This is the first line. \n" +
"2: This is the second line. \n" +
"3: This is the third line\n" +
"4: Fourth line\n" +
"5: Fifth line\n" +
"6: Sixth line\n" +
"7: Seventh line\n" +
"8: Eighth line\n" +
"9: Ninth line\n" +
"The end";
private static final String SINGLE_LINE_PAYLOAD = "1: This is a line that is longer than what can be sent in a " +
"single segment. It also has foreign characters æøå. Lorem ipsum dolor sit amet, atqui iudico epicuri mel" +
" eu. Cu quaestio voluptatum ullamcorper quo, nec ut numquam habemus, summo nostrum est ut. Nominati " +
"praesent repudiare at sit, ut tibique suscipit has, nec eu odio erat qualisque. Soleat ridens ut nec, eu" +
" denique mentitum his.";
/* ********** Single line tests ************* */
@Test
public void singleLineSplitsCorrectlyFor7Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 0);
verifySingleLineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void singleLineSplitsCorrectlyFor8Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 4);
verifySingleLineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void singleLineSplitsCorrectlyFor16Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 8);
verifySingleLineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void singleLineSplitsCorrectlyForSimpleDataCoding() throws Exception {
DataCoding dataCoding = new SimpleDataCoding();
verifySingleLineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void singleLineSplitsCorrectlyForDefault() throws Exception {
verifyMessageSplitCorrectlyForDataCoding(SINGLE_LINE_PAYLOAD, null, null);
}
@Test
public void singleLineSplitsCorrectlyForCustomMaxCharacters() throws Exception {
verifyMessageSplitCorrectlyForDataCoding(SINGLE_LINE_PAYLOAD, null, 99);
}
/* ********** Multi line tests ************* */
@Test
public void multiLineSplitsCorrectlyFor7Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 0);
verifyMultilineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void multiLineSplitsCorrectlyFor8Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 4);
verifyMultilineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void multiLineSplitsCorrectlyFor16Bit() throws Exception {
DataCoding dataCoding = DataCodings.newInstance((byte) 8);
verifyMultilineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void multiLineSplitsCorrectlyForSimpleDataCoding() throws Exception {
DataCoding dataCoding = new SimpleDataCoding();
verifyMultilineMessageSplitCorrectlyForDataCoding(dataCoding);
}
@Test
public void multiLineSplitsCorrectlyForDefault() throws Exception {
verifyMessageSplitCorrectlyForDataCoding(MULTI_LINE_PAYLOAD, null, null);
}
@Test
public void multiLineSplitsCorrectlyForCustomMaxCharacters() throws Exception {
verifyMessageSplitCorrectlyForDataCoding(MULTI_LINE_PAYLOAD, null, 99);
}
/* ***************** Helper methods ****************** */
private void verifySingleLineMessageSplitCorrectlyForDataCoding(DataCoding dataCoding)
throws UnsupportedEncodingException {
verifyMessageSplitCorrectlyForDataCoding(SINGLE_LINE_PAYLOAD, dataCoding, null);
}
private void verifyMultilineMessageSplitCorrectlyForDataCoding(DataCoding dataCoding)
throws UnsupportedEncodingException {
verifyMessageSplitCorrectlyForDataCoding(MULTI_LINE_PAYLOAD, dataCoding, null);
}
private void verifyMessageSplitCorrectlyForDataCoding(String payload, DataCoding dataCoding,
Integer maxMessageSize) throws UnsupportedEncodingException {
SmesMessageSpecification smesMessageSpecification = getSmesMessageSpecification(payload, dataCoding,
maxMessageSize);
if (dataCoding == null) {
dataCoding = (DataCoding) ReflectionTestUtils.getField(smesMessageSpecification, "dataCoding");
}
int maxCharacters;
if (maxMessageSize == null) {
maxCharacters = DataCodingSpecification.getMaxCharacters(dataCoding.toByte());
}
else {
maxCharacters = maxMessageSize;
}
int maxCharactersWithRoomForPrefix = maxCharacters - PREFIX_BYTES;
String charsetName = DataCodingSpecification.getCharsetName(dataCoding.toByte());
@SuppressWarnings("unchecked")
List<byte[]> messageParts = (List<byte[]>) TestUtils.getPropertyValue(smesMessageSpecification,
"shortMessageParts", List.class);
int expectedPartsCountAfterSplit = (payload.length() + maxCharactersWithRoomForPrefix - 1)
/ maxCharactersWithRoomForPrefix;
assertEquals("Message parts count after split does not match expected size", expectedPartsCountAfterSplit,
messageParts.size());
// Assert if individual parts are longer than allowed max characters
for (byte[] messagePart : messageParts) {
String string = new String(messagePart, charsetName);
assertTrue("MessagePart is longer than allowed max message size for data coding - Actual part length: "
+ string.length() + " vs. max size: " + maxCharacters
, string.length() <= maxCharacters);
}
}
private SmesMessageSpecification getSmesMessageSpecification(String payload, DataCoding dataCoding,
Integer maxMessageSize) {
MessageBuilder<String> messageBuilder = MessageBuilder.withPayload(payload);
messageBuilder.setHeaderIfAbsent(SmppConstants.SMS_MSG, payload);
if (dataCoding != null) {
messageBuilder.setHeaderIfAbsent(SmppConstants.DATA_CODING, dataCoding);
}
if (maxMessageSize != null) {
messageBuilder.setHeaderIfAbsent(SmppConstants.MAXIMUM_CHARACTERS, maxMessageSize);
}
Message<String> message = messageBuilder.build();
return SmesMessageSpecification.fromMessage(null, message);
}
}