Skip to content

Commit e050d3d

Browse files
authored
Merge pull request #651 from the-thing/650/truncated_filestore_messages
`FileStore` message length fix
2 parents 8cf71f9 + ddeb9bb commit e050d3d

File tree

10 files changed

+289
-48
lines changed

10 files changed

+289
-48
lines changed

quickfixj-base/src/main/java/org/quickfixj/CharsetSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ public static void setCharset(String charset) throws UnsupportedEncodingExceptio
6464
CharsetSupport.isStringEquivalent = isStringEquivalent(charsetInstance);
6565
}
6666

67+
public static void setDefaultCharset() throws UnsupportedEncodingException {
68+
setCharset(getDefaultCharset());
69+
}
70+
6771
public static String getCharset() {
6872
return charset;
6973
}

quickfixj-core/src/main/java/quickfix/CachedFileStore.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.BufferedOutputStream;
2929
import java.io.DataInputStream;
3030
import java.io.DataOutputStream;
31+
import java.io.EOFException;
3132
import java.io.File;
3233
import java.io.FileInputStream;
3334
import java.io.FileOutputStream;
@@ -86,8 +87,6 @@ public class CachedFileStore implements MessageStore {
8687

8788
private FileOutputStream headerFileOutputStream;
8889

89-
private final String charsetEncoding = CharsetSupport.getCharset();
90-
9190
CachedFileStore(String path, SessionID sessionID, boolean syncWrites) throws IOException {
9291
this.syncWrites = syncWrites;
9392

@@ -308,16 +307,15 @@ public boolean get(int sequence, String message) {
308307
throw new UnsupportedOperationException("not supported");
309308
}
310309

311-
private String read(long offset, long size) throws IOException {
312-
final byte[] data = new byte[(int) size];
313-
314-
messageFileReader.seek(offset);
315-
if (messageFileReader.read(data) != size) {
316-
throw new IOException("Truncated input while reading message: "
317-
+ new String(data, charsetEncoding));
310+
private String read(long offset, int size) throws IOException {
311+
try {
312+
final byte[] data = new byte[size];
313+
messageFileReader.seek(offset);
314+
messageFileReader.readFully(data);
315+
return new String(data, CharsetSupport.getCharset());
316+
} catch (EOFException eofe) { // can't read fully
317+
throw new IOException("Truncated input while reading message: offset=" + offset + ", expected size=" + size, eofe);
318318
}
319-
320-
return new String(data, charsetEncoding);
321319
}
322320

323321
private Collection<String> getMessage(long startSequence, long endSequence) throws IOException {
@@ -326,7 +324,7 @@ private Collection<String> getMessage(long startSequence, long endSequence) thro
326324
final List<long[]> offsetAndSizes = messageIndex.get(startSequence, endSequence);
327325
for (final long[] offsetAndSize : offsetAndSizes) {
328326
if (offsetAndSize != null) {
329-
final String message = read(offsetAndSize[0], offsetAndSize[1]);
327+
final String message = read(offsetAndSize[0], (int) offsetAndSize[1]);
330328
messages.add(message);
331329
}
332330
}
@@ -341,7 +339,8 @@ private Collection<String> getMessage(long startSequence, long endSequence) thro
341339
*/
342340
public boolean set(int sequence, String message) throws IOException {
343341
final long offset = messageFileWriter.getFilePointer();
344-
final int size = message.length();
342+
final byte[] messageBytes = message.getBytes(CharsetSupport.getCharset());
343+
final int size = messageBytes.length;
345344
messageIndex.put((long) sequence, new long[] { offset, size });
346345
headerDataOutputStream.writeInt(sequence);
347346
headerDataOutputStream.writeLong(offset);
@@ -350,7 +349,7 @@ public boolean set(int sequence, String message) throws IOException {
350349
if (syncWrites) {
351350
headerFileOutputStream.getFD().sync();
352351
}
353-
messageFileWriter.write(message.getBytes(CharsetSupport.getCharset()));
352+
messageFileWriter.write(messageBytes);
354353
return true;
355354
}
356355

quickfixj-core/src/main/java/quickfix/FileStore.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public class FileStore implements MessageStore, Closeable {
6363
private final String sessionFileName;
6464
private final boolean syncWrites;
6565
private final int maxCachedMsgs;
66-
private final String charsetEncoding = CharsetSupport.getCharset();
6766
private RandomAccessFile messageFileReader;
6867
private RandomAccessFile messageFileWriter;
6968
private DataOutputStream headerDataOutputStream;
@@ -350,7 +349,7 @@ private String getMessage(long offset, int size, int i) throws IOException {
350349
final byte[] data = new byte[size];
351350
messageFileReader.seek(offset);
352351
messageFileReader.readFully(data);
353-
return new String(data, charsetEncoding);
352+
return new String(data, CharsetSupport.getCharset());
354353
} catch (EOFException eofe) { // can't read fully
355354
throw new IOException("Truncated input while reading message: messageIndex=" + i
356355
+ ", offset=" + offset + ", expected size=" + size, eofe);
@@ -363,7 +362,8 @@ private String getMessage(long offset, int size, int i) throws IOException {
363362
@Override
364363
public boolean set(int sequence, String message) throws IOException {
365364
final long offset = messageFileWriter.getFilePointer();
366-
final int size = message.length();
365+
final byte[] messageBytes = message.getBytes(CharsetSupport.getCharset());
366+
final int size = messageBytes.length;
367367
if (messageIndex != null) {
368368
updateMessageIndex(sequence, offset, size);
369369
}
@@ -374,7 +374,7 @@ public boolean set(int sequence, String message) throws IOException {
374374
if (syncWrites) {
375375
headerFileOutputStream.getFD().sync();
376376
}
377-
messageFileWriter.write(message.getBytes(CharsetSupport.getCharset()));
377+
messageFileWriter.write(messageBytes);
378378
return true;
379379
}
380380

quickfixj-core/src/test/java/quickfix/AbstractMessageStoreTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
package quickfix;
2121

2222
import junit.framework.TestCase;
23+
import org.quickfixj.CharsetSupport;
2324

2425
import java.io.IOException;
2526
import java.util.ArrayList;
27+
import java.util.List;
2628

2729
public abstract class AbstractMessageStoreTest extends TestCase {
30+
2831
private SessionID sessionID;
2932
private MessageStore store;
3033

@@ -167,6 +170,44 @@ public void testRefreshMessageStore() throws Exception {
167170
}
168171
}
169172

173+
public void testSetAndGetMessageWithAsciiCharacters() throws IOException {
174+
MessageStore underTest = getStore();
175+
176+
if (underTest instanceof SleepycatStore) {
177+
return;
178+
}
179+
180+
underTest.set(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789");
181+
182+
List<String> messages = new ArrayList<>();
183+
underTest.get(1, 1, messages);
184+
185+
assertEquals(1, messages.size());
186+
assertEquals("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789", messages.get(0));
187+
}
188+
189+
public void testSetAndGetMessageWithUnicodeCharacters() throws IOException {
190+
MessageStore underTest = getStore();
191+
192+
if (underTest instanceof SleepycatStore) {
193+
return;
194+
}
195+
196+
CharsetSupport.setCharset("UTF-8");
197+
198+
try {
199+
underTest.set(1, "a \u00A9 \u2603 \uD834\uDF06");
200+
201+
List<String> messages = new ArrayList<>();
202+
underTest.get(1, 1, messages);
203+
204+
assertEquals(1, messages.size());
205+
assertEquals("a \u00A9 \u2603 \uD834\uDF06", messages.get(0));
206+
} finally {
207+
CharsetSupport.setDefaultCharset();
208+
}
209+
}
210+
170211
protected void closeMessageStore(MessageStore store) throws IOException {
171212
// does nothing, by default
172213
}

quickfixj-core/src/test/java/quickfix/FieldTest.java

Lines changed: 188 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919

2020
package quickfix;
2121

22-
import static org.junit.Assert.assertEquals;
23-
import static org.junit.Assert.assertTrue;
24-
22+
import java.io.UnsupportedEncodingException;
23+
import java.math.BigDecimal;
24+
import java.time.LocalDate;
2525
import java.time.LocalDateTime;
26+
import java.time.LocalTime;
2627
import java.util.Arrays;
28+
import java.util.Date;
2729

2830
import org.junit.Test;
2931
import org.quickfixj.CharsetSupport;
@@ -39,6 +41,10 @@
3941
import quickfix.fix50.MarketDataIncrementalRefresh;
4042
import quickfix.fix50.NewOrderSingle;
4143

44+
import static org.junit.Assert.assertEquals;
45+
import static org.junit.Assert.assertFalse;
46+
import static org.junit.Assert.assertTrue;
47+
4248
public class FieldTest {
4349

4450
@Test
@@ -52,6 +58,185 @@ public void testMessageSetGetString() {
5258
assertEquals(side1, side2);
5359
}
5460

61+
private void testFieldCalcuations(String value, int checksum, int length) {
62+
Field<String> field = new Field<>(12, value);
63+
field.setObject(value);
64+
assertEquals("12=" + value, field.toString());
65+
assertEquals(checksum, field.getChecksum());
66+
assertEquals(length, field.getLength());
67+
68+
value = value.substring(0, value.length() - 1) + (char)(value.charAt(value.length() - 1) + 1);
69+
checksum = (checksum + 1) & 0xFF;
70+
field.setObject(value);
71+
assertEquals("12=" + value, field.toString());
72+
assertEquals(checksum, field.getChecksum());
73+
assertEquals(length, field.getLength());
74+
75+
field.setTag(13);
76+
checksum = (checksum + 1) & 0xFF;
77+
assertEquals("13=" + value, field.toString());
78+
assertEquals(checksum, field.getChecksum());
79+
assertEquals(length, field.getLength());
80+
}
81+
82+
@Test
83+
public void testFieldCalculationsWithDefaultCharset() {
84+
testFieldCalcuations("VALUE", 30, 9);
85+
}
86+
87+
@Test
88+
public void testFieldCalculationsWithUTF8Charset() throws UnsupportedEncodingException {
89+
CharsetSupport.setCharset("UTF-8");
90+
try {
91+
testFieldCalcuations("\u6D4B\u9A8C\u6570\u636E", 50, 16);
92+
} finally {
93+
CharsetSupport.setDefaultCharset();
94+
}
95+
}
96+
97+
@Test
98+
public void testDateField() {
99+
DateField field = new DateField(11);
100+
Date date = new Date();
101+
field.setValue(date);
102+
assertEquals(11, field.getTag());
103+
assertEquals(date, field.getValue());
104+
field = new DateField(11, date);
105+
assertEquals(11, field.getTag());
106+
assertEquals(date, field.getValue());
107+
}
108+
109+
@Test
110+
public void testUtcDateOnlyField() {
111+
UtcDateOnlyField field = new UtcDateOnlyField(11);
112+
LocalDate date = LocalDate.now();
113+
field.setValue(date);
114+
assertEquals(11, field.getTag());
115+
assertEquals(date, field.getValue());
116+
field = new UtcDateOnlyField(11, date);
117+
assertEquals(11, field.getTag());
118+
assertEquals(date, field.getValue());
119+
}
120+
121+
@Test
122+
public void testUtcTimeOnlyField() {
123+
UtcTimeOnlyField field = new UtcTimeOnlyField(11);
124+
LocalTime date = LocalTime.now();
125+
field.setValue(date);
126+
assertEquals(11, field.getTag());
127+
assertEquals(date, field.getValue());
128+
field = new UtcTimeOnlyField(11, date);
129+
assertEquals(11, field.getTag());
130+
assertEquals(date, field.getValue());
131+
}
132+
133+
@Test
134+
public void testUtcTimeStampField() {
135+
UtcTimeStampField field = new UtcTimeStampField(11);
136+
LocalDateTime date = LocalDateTime.now();
137+
field.setValue(date);
138+
assertEquals(11, field.getTag());
139+
assertEquals(date, field.getValue());
140+
field = new UtcTimeStampField(11, date);
141+
assertEquals(11, field.getTag());
142+
assertEquals(date, field.getValue());
143+
}
144+
145+
@Test
146+
public void testBooleanField() {
147+
BooleanField field = new BooleanField(11);
148+
field.setValue(true);
149+
assertEquals(11, field.getTag());
150+
assertTrue(field.getValue());
151+
field.setValue(Boolean.FALSE);
152+
assertEquals(11, field.getTag());
153+
assertFalse(field.getValue());
154+
field = new BooleanField(22, true);
155+
assertEquals(22, field.getTag());
156+
assertTrue(field.getValue());
157+
field = new BooleanField(33, Boolean.TRUE);
158+
assertEquals(33, field.getTag());
159+
assertTrue(field.getValue());
160+
}
161+
162+
@Test
163+
public void testDoubleField() {
164+
DoubleField field = new DoubleField(11);
165+
field.setValue(12.3);
166+
assertEquals(11, field.getTag());
167+
assertEquals(12.3, field.getValue(), 0);
168+
field.setValue(new Double(23.4));
169+
assertEquals(11, field.getTag());
170+
assertEquals(23.4, field.getValue(), 0);
171+
field = new DoubleField(22, 34.5);
172+
assertEquals(22, field.getTag());
173+
assertEquals(34.5, field.getValue(), 0);
174+
field = new DoubleField(33, new Double(45.6));
175+
assertEquals(33, field.getTag());
176+
assertEquals(45.6, field.getValue(), 0);
177+
}
178+
179+
@Test(expected = NumberFormatException.class)
180+
public void testDoubleFieldException() {
181+
DoubleField field = new DoubleField(11, Double.NaN);
182+
}
183+
184+
@Test
185+
public void testDecimalField() {
186+
DecimalField field = new DecimalField(11);
187+
field.setValue(12.3);
188+
assertEquals(11, field.getTag());
189+
assertEquals(BigDecimal.valueOf(12.3), field.getValue());
190+
field.setValue(23.4);
191+
assertEquals(11, field.getTag());
192+
assertEquals(BigDecimal.valueOf(23.4), field.getValue());
193+
field = new DecimalField(22, 34.5);
194+
assertEquals(22, field.getTag());
195+
assertEquals(BigDecimal.valueOf(34.5), field.getValue());
196+
field = new DecimalField(33, 45.6);
197+
assertEquals(33, field.getTag());
198+
assertEquals(BigDecimal.valueOf(45.6), field.getValue());
199+
}
200+
201+
@Test(expected = NumberFormatException.class)
202+
public void testDecimalFieldException() {
203+
DecimalField field = new DecimalField(11, Double.POSITIVE_INFINITY);
204+
}
205+
206+
@Test
207+
public void testCharField() {
208+
CharField field = new CharField(11);
209+
field.setValue('x');
210+
assertEquals(11, field.getTag());
211+
assertEquals('x', field.getValue());
212+
field.setValue(Character.valueOf('X'));
213+
assertEquals(11, field.getTag());
214+
assertEquals('X', field.getValue());
215+
field = new CharField(22, 'a');
216+
assertEquals(22, field.getTag());
217+
assertEquals('a', field.getValue());
218+
field = new CharField(33, Character.valueOf('A'));
219+
assertEquals(33, field.getTag());
220+
assertEquals('A', field.getValue());
221+
}
222+
223+
@Test
224+
public void testIntField() {
225+
IntField field = new IntField(11);
226+
field.setValue(12);
227+
assertEquals(11, field.getTag());
228+
assertEquals(12, field.getValue());
229+
field.setValue(Integer.valueOf(23));
230+
assertEquals(11, field.getTag());
231+
assertEquals(23, field.getValue());
232+
field = new IntField(22, 23);
233+
assertEquals(22, field.getTag());
234+
assertEquals(23, field.getValue());
235+
field = new IntField(33, Integer.valueOf(44));
236+
assertEquals(33, field.getTag());
237+
assertEquals(44, field.getValue());
238+
}
239+
55240
@Test
56241
public void testBytesField() {
57242
byte[] data = "rawdata".getBytes();

0 commit comments

Comments
 (0)