Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions quickfixj-base/src/main/java/quickfix/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,14 @@ private Object cloneTo(Message message) {
}

private static final class Context {

private static final int MAX_MESSAGE_BUFFER_SIZE = 4096;
private final BodyLength bodyLength = new BodyLength(100);
private final CheckSum checkSum = new CheckSum("000");
private final StringBuilder stringBuilder = new StringBuilder(1024);
}

private static final ThreadLocal<Context> stringContexts = new ThreadLocal<Context>() {
@Override
protected Context initialValue() {
return new Context();
}
};
private static final ThreadLocal<Context> STRING_CONTEXTS = ThreadLocal.withInitial(Context::new);

/**
* Do not call this method concurrently while modifying the contents of the message.
Expand All @@ -173,7 +170,7 @@ protected Context initialValue() {
*/
@Override
public String toString() {
Context context = stringContexts.get();
Context context = STRING_CONTEXTS.get();
if (CharsetSupport.isStringEquivalent()) { // length & checksum can easily be calculated after message is built
header.setField(context.bodyLength);
trailer.setField(context.checkSum);
Expand All @@ -192,6 +189,11 @@ public String toString() {
}
return stringBuilder.toString();
} finally {
if (stringBuilder.length() > Context.MAX_MESSAGE_BUFFER_SIZE) {
stringBuilder.setLength(Context.MAX_MESSAGE_BUFFER_SIZE);
stringBuilder.trimToSize();
}

stringBuilder.setLength(0);
}
}
Expand Down Expand Up @@ -1018,4 +1020,7 @@ void setGarbled(boolean isGarbled) {
this.isGarbled = isGarbled;
}

StringBuilder getStringBuilder() {
return STRING_CONTEXTS.get().stringBuilder;
}
}
43 changes: 43 additions & 0 deletions quickfixj-base/src/test/java/quickfix/MessageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

import org.junit.Rule;
import org.junit.Test;
Expand Down Expand Up @@ -746,4 +748,45 @@ public void testHeaderDataField() throws Exception {
DataDictionaryTest.getDictionary());
assertEquals("ABCD", m.getHeader().getString(SecureData.FIELD));
}

@Test
public void shouldTrimStringBuilder() {
// this test must run in a dedicated thread to avoid interference with other test cases (thread local)
CompletableFuture<?> future = CompletableFuture.runAsync(() -> {
Message message = new Message();

message.setString(131, "123456");
String str = message.toString();

assertEquals(23, str.length());
assertEquals(0, message.getStringBuilder().length());
assertEquals(1024, message.getStringBuilder().capacity());

message.setString(131, createLongString());
str = message.toString();

assertEquals(10020, str.length());
assertEquals(0, message.getStringBuilder().length());
assertEquals(4096, message.getStringBuilder().capacity());

message.setString(131, "123456");
str = message.toString();

assertEquals(23, str.length());
assertEquals(0, message.getStringBuilder().length());
assertEquals(4096, message.getStringBuilder().capacity());
}, Executors.newSingleThreadExecutor());

future.join();
}

private static String createLongString() {
StringBuilder builder = new StringBuilder(10_000);

for (int i = 0; i < 10_000; i++) {
builder.append('a');
}

return builder.toString();
}
}
Loading