1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.batch.item.file;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25
26 import java.io.BufferedReader;
27 import java.io.File;
28 import java.io.FileReader;
29 import java.io.IOException;
30 import java.io.Writer;
31 import java.nio.charset.UnsupportedCharsetException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.List;
36
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.springframework.batch.item.ExecutionContext;
41 import org.springframework.batch.item.ItemStreamException;
42 import org.springframework.batch.item.UnexpectedInputException;
43 import org.springframework.batch.item.file.transform.LineAggregator;
44 import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
45 import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
46 import org.springframework.core.io.FileSystemResource;
47 import org.springframework.core.io.Resource;
48 import org.springframework.transaction.PlatformTransactionManager;
49 import org.springframework.transaction.TransactionStatus;
50 import org.springframework.transaction.support.TransactionCallback;
51 import org.springframework.transaction.support.TransactionTemplate;
52 import org.springframework.util.ClassUtils;
53
54
55
56
57
58
59
60
61
62 public class FlatFileItemWriterTests {
63
64
65 private FlatFileItemWriter<String> writer = new FlatFileItemWriter<String>();
66
67
68 private static final String TEST_STRING = "FlatFileOutputTemplateTest-OutputData";
69
70
71 private File outputFile;
72
73
74 private BufferedReader reader;
75
76 private ExecutionContext executionContext;
77
78
79
80
81 @Before
82 public void setUp() throws Exception {
83
84 outputFile = File.createTempFile("flatfile-test-output-", ".tmp");
85
86 writer.setResource(new FileSystemResource(outputFile));
87 writer.setLineSeparator("\n");
88 writer.setLineAggregator(new PassThroughLineAggregator<String>());
89 writer.afterPropertiesSet();
90 writer.setSaveState(true);
91 executionContext = new ExecutionContext();
92 }
93
94
95
96
97 @After
98 public void tearDown() throws Exception {
99 if (reader != null) {
100 reader.close();
101 }
102 writer.close();
103 outputFile.delete();
104 }
105
106
107
108
109
110 private String readLine() throws IOException {
111
112 if (reader == null) {
113 reader = new BufferedReader(new FileReader(outputFile));
114 }
115
116 return reader.readLine();
117 }
118
119 @Test
120 public void testWriteWithMultipleOpen() throws Exception {
121 writer.open(executionContext);
122 writer.write(Collections.singletonList("test1"));
123 writer.open(executionContext);
124 writer.write(Collections.singletonList("test2"));
125 assertEquals("test1", readLine());
126 assertEquals("test2", readLine());
127 }
128
129 @Test
130 public void testWriteWithDelete() throws Exception {
131 writer.open(executionContext);
132 writer.write(Collections.singletonList("test1"));
133 writer.close();
134 assertEquals("test1", readLine());
135 reader = null;
136 writer.setShouldDeleteIfExists(true);
137 writer.open(executionContext);
138 writer.write(Collections.singletonList("test2"));
139 assertEquals("test2", readLine());
140 }
141
142 @Test
143 public void testWriteWithAppend() throws Exception {
144 writer.setAppendAllowed(true);
145 writer.open(executionContext);
146 writer.write(Collections.singletonList("test1"));
147 writer.close();
148 assertEquals("test1", readLine());
149 reader = null;
150 writer.open(executionContext);
151 writer.write(Collections.singletonList("test2"));
152 assertEquals("test1", readLine());
153 assertEquals("test2", readLine());
154 }
155
156 @Test
157 public void testWriteWithAppendRestartOnSecondChunk() throws Exception {
158 writer.setAppendAllowed(true);
159 writer.open(executionContext);
160 writer.write(Collections.singletonList("test1"));
161 writer.close();
162 assertEquals("test1", readLine());
163 reader = null;
164 writer.open(executionContext);
165 writer.write(Collections.singletonList(TEST_STRING));
166 writer.update(executionContext);
167 writer.write(Collections.singletonList(TEST_STRING));
168 writer.close();
169 assertEquals("test1", readLine());
170 assertEquals(TEST_STRING, readLine());
171 assertEquals(TEST_STRING, readLine());
172 assertEquals(null, readLine());
173 writer.open(executionContext);
174 writer.write(Collections.singletonList(TEST_STRING));
175 writer.close();
176 reader = null;
177 assertEquals("test1", readLine());
178 assertEquals(TEST_STRING, readLine());
179 assertEquals(TEST_STRING, readLine());
180 assertEquals(null, readLine());
181 }
182
183 @Test
184 public void testOpenTwice() {
185
186 writer.open(executionContext);
187 writer.open(executionContext);
188 }
189
190
191
192
193
194
195 @Test
196 public void testWriteString() throws Exception {
197 writer.open(executionContext);
198 writer.write(Collections.singletonList(TEST_STRING));
199 writer.close();
200 String lineFromFile = readLine();
201
202 assertEquals(TEST_STRING, lineFromFile);
203 }
204
205 @Test
206 public void testForcedWriteString() throws Exception {
207 writer.setForceSync(true);
208 writer.open(executionContext);
209 writer.write(Collections.singletonList(TEST_STRING));
210 writer.close();
211 String lineFromFile = readLine();
212
213 assertEquals(TEST_STRING, lineFromFile);
214 }
215
216
217
218
219
220
221 @Test
222 public void testWriteWithConverter() throws Exception {
223 writer.setLineAggregator(new LineAggregator<String>() {
224 public String aggregate(String item) {
225 return "FOO:" + item;
226 }
227 });
228 String data = "string";
229 writer.open(executionContext);
230 writer.write(Collections.singletonList(data));
231 String lineFromFile = readLine();
232
233 assertEquals("FOO:" + data, lineFromFile);
234 }
235
236
237
238
239
240
241 @Test
242 public void testWriteWithConverterAndString() throws Exception {
243 writer.setLineAggregator(new LineAggregator<String>() {
244 public String aggregate(String item) {
245 return "FOO:" + item;
246 }
247 });
248 writer.open(executionContext);
249 writer.write(Collections.singletonList(TEST_STRING));
250 String lineFromFile = readLine();
251 assertEquals("FOO:" + TEST_STRING, lineFromFile);
252 }
253
254
255
256
257
258
259 @Test
260 public void testWriteRecord() throws Exception {
261 writer.open(executionContext);
262 writer.write(Collections.singletonList("1"));
263 String lineFromFile = readLine();
264 assertEquals("1", lineFromFile);
265 }
266
267 @Test
268 public void testWriteRecordWithrecordSeparator() throws Exception {
269 writer.setLineSeparator("|");
270 writer.open(executionContext);
271 writer.write(Arrays.asList(new String[] { "1", "2" }));
272 String lineFromFile = readLine();
273 assertEquals("1|2|", lineFromFile);
274 }
275
276 @Test
277 public void testRestart() throws Exception {
278
279 writer.setFooterCallback(new FlatFileFooterCallback() {
280
281 public void writeFooter(Writer writer) throws IOException {
282 writer.write("footer");
283 }
284
285 });
286
287 writer.open(executionContext);
288
289 writer.write(Arrays.asList(new String[] { "testLine1", "testLine2", "testLine3" }));
290
291 writer.write(Arrays.asList(new String[] { "testLine4", "testLine5" }));
292
293 writer.update(executionContext);
294
295 writer.close();
296
297
298 writer.open(executionContext);
299
300 writer.write(Arrays.asList(new String[] { "testLine6", "testLine7", "testLine8" }));
301
302 writer.update(executionContext);
303
304 writer.close();
305
306
307 for (int i = 1; i <= 8; i++) {
308 assertEquals("testLine" + i, readLine());
309 }
310
311 assertEquals("footer", readLine());
312
313
314 assertEquals(3, executionContext.getLong(ClassUtils.getShortName(FlatFileItemWriter.class) + ".written"));
315
316 }
317
318 @Test
319 public void testWriteStringTransactional() throws Exception {
320 writeStringTransactionCheck(null);
321 assertEquals(TEST_STRING, readLine());
322 }
323
324 @Test
325 public void testWriteStringNotTransactional() throws Exception {
326 writer.setTransactional(false);
327 writeStringTransactionCheck(TEST_STRING);
328 }
329
330 private void writeStringTransactionCheck(final String expectedInTransaction) {
331 PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
332
333 writer.open(executionContext);
334 new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
335 public Object doInTransaction(TransactionStatus status) {
336 try {
337 writer.write(Collections.singletonList(TEST_STRING));
338 assertEquals(expectedInTransaction, readLine());
339 }
340 catch (Exception e) {
341 throw new UnexpectedInputException("Could not write data", e);
342 }
343
344 return null;
345 }
346 });
347 writer.close();
348 }
349
350 @Test
351 public void testTransactionalRestart() throws Exception {
352
353 writer.setFooterCallback(new FlatFileFooterCallback() {
354
355 public void writeFooter(Writer writer) throws IOException {
356 writer.write("footer");
357 }
358
359 });
360
361 writer.open(executionContext);
362
363 PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
364
365 new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
366 public Object doInTransaction(TransactionStatus status) {
367 try {
368
369 writer.write(Arrays.asList(new String[] { "testLine1", "testLine2", "testLine3" }));
370
371 writer.write(Arrays.asList(new String[] { "testLine4", "testLine5" }));
372 }
373 catch (Exception e) {
374 throw new UnexpectedInputException("Could not write data", e);
375 }
376
377 writer.update(executionContext);
378 return null;
379 }
380 });
381
382 writer.close();
383
384
385 writer.open(executionContext);
386
387 new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
388 public Object doInTransaction(TransactionStatus status) {
389 try {
390
391 writer.write(Arrays.asList(new String[] { "testLine6", "testLine7", "testLine8" }));
392 }
393 catch (Exception e) {
394 throw new UnexpectedInputException("Could not write data", e);
395 }
396
397 writer.update(executionContext);
398 return null;
399 }
400 });
401
402 writer.close();
403
404
405 for (int i = 1; i <= 8; i++) {
406 assertEquals("testLine" + i, readLine());
407 }
408
409 assertEquals("footer", readLine());
410
411
412 assertEquals(3, executionContext.getLong(ClassUtils.getShortName(FlatFileItemWriter.class) + ".written"));
413
414 }
415
416 @Test
417 public void testOpenWithNonWritableFile() throws Exception {
418 writer = new FlatFileItemWriter<String>();
419 writer.setLineAggregator(new PassThroughLineAggregator<String>());
420 FileSystemResource file = new FileSystemResource("target/no-such-file.foo");
421 writer.setResource(file);
422 new File(file.getFile().getParent()).mkdirs();
423 file.getFile().createNewFile();
424 assertTrue("Test file must exist: " + file, file.exists());
425 assertTrue("Test file set to read-only: " + file, file.getFile().setReadOnly());
426 assertFalse("Should be readonly file: " + file, file.getFile().canWrite());
427 writer.afterPropertiesSet();
428 try {
429 writer.open(executionContext);
430 fail("Expected IllegalStateException");
431 }
432 catch (IllegalStateException e) {
433 String message = e.getMessage();
434 assertTrue("Message does not contain 'writable': " + message, message.indexOf("writable") >= 0);
435 }
436 }
437
438 @Test
439 public void testAfterPropertiesSetChecksMandatory() throws Exception {
440 writer = new FlatFileItemWriter<String>();
441 try {
442 writer.afterPropertiesSet();
443 fail("Expected IllegalArgumentException");
444 }
445 catch (IllegalArgumentException e) {
446
447 }
448 }
449
450 @Test
451 public void testDefaultStreamContext() throws Exception {
452 writer = new FlatFileItemWriter<String>();
453 writer.setResource(new FileSystemResource(outputFile));
454 writer.setLineAggregator(new PassThroughLineAggregator<String>());
455 writer.afterPropertiesSet();
456 writer.setSaveState(true);
457 writer.open(executionContext);
458 writer.update(executionContext);
459 assertNotNull(executionContext);
460 assertEquals(2, executionContext.entrySet().size());
461 assertEquals(0, executionContext.getLong(ClassUtils.getShortName(FlatFileItemWriter.class) + ".current.count"));
462 }
463
464 @Test
465 public void testWriteStringWithBogusEncoding() throws Exception {
466 writer.setTransactional(false);
467 writer.setEncoding("BOGUS");
468
469 try {
470 writer.open(executionContext);
471 fail("Expected ItemStreamException");
472 }
473 catch (ItemStreamException e) {
474 assertTrue(e.getCause() instanceof UnsupportedCharsetException);
475 }
476 writer.close();
477
478 writer.setEncoding("UTF-8");
479 writer.open(executionContext);
480 writer.write(Collections.singletonList(TEST_STRING));
481 }
482
483 @Test
484 public void testWriteStringWithEncodingAfterClose() throws Exception {
485 writer.open(executionContext);
486 writer.write(Collections.singletonList(TEST_STRING));
487 writer.close();
488 writer.setEncoding("UTF-8");
489 writer.open(executionContext);
490 writer.write(Collections.singletonList(TEST_STRING));
491 String lineFromFile = readLine();
492
493 assertEquals(TEST_STRING, lineFromFile);
494 }
495
496 @Test
497 public void testWriteFooter() throws Exception {
498 writer.setFooterCallback(new FlatFileFooterCallback() {
499
500 public void writeFooter(Writer writer) throws IOException {
501 writer.write("a\nb");
502 }
503
504 });
505 writer.open(executionContext);
506 writer.write(Collections.singletonList(TEST_STRING));
507 writer.close();
508 assertEquals(TEST_STRING, readLine());
509 assertEquals("a", readLine());
510 assertEquals("b", readLine());
511 }
512
513 @Test
514 public void testWriteHeader() throws Exception {
515 writer.setHeaderCallback(new FlatFileHeaderCallback() {
516
517 public void writeHeader(Writer writer) throws IOException {
518 writer.write("a\nb");
519 }
520
521 });
522 writer.open(executionContext);
523 writer.write(Collections.singletonList(TEST_STRING));
524 writer.close();
525 String lineFromFile = readLine();
526 assertEquals("a", lineFromFile);
527 lineFromFile = readLine();
528 assertEquals("b", lineFromFile);
529 lineFromFile = readLine();
530 assertEquals(TEST_STRING, lineFromFile);
531 }
532
533 @Test
534 public void testWriteWithAppendAfterHeaders() throws Exception {
535 writer.setHeaderCallback(new FlatFileHeaderCallback() {
536 public void writeHeader(Writer writer) throws IOException {
537 writer.write("a\nb");
538 }
539
540 });
541 writer.setAppendAllowed(true);
542 writer.open(executionContext);
543 writer.write(Collections.singletonList("test1"));
544 writer.close();
545 assertEquals("a", readLine());
546 assertEquals("b", readLine());
547 assertEquals("test1", readLine());
548 reader = null;
549 writer.open(executionContext);
550 writer.write(Collections.singletonList("test2"));
551 assertEquals("a", readLine());
552 assertEquals("b", readLine());
553 assertEquals("test1", readLine());
554 assertEquals("test2", readLine());
555 }
556
557 @Test
558 public void testWriteHeaderAndDeleteOnExit() throws Exception {
559 writer.setHeaderCallback(new FlatFileHeaderCallback() {
560
561 public void writeHeader(Writer writer) throws IOException {
562 writer.write("a\nb");
563 }
564
565 });
566 writer.setShouldDeleteIfEmpty(true);
567 writer.open(executionContext);
568 assertTrue(outputFile.exists());
569 writer.close();
570 assertFalse(outputFile.exists());
571 }
572
573 @Test
574 public void testDeleteOnExitReopen() throws Exception {
575 writer.setShouldDeleteIfEmpty(true);
576 writer.open(executionContext);
577 assertTrue(outputFile.exists());
578 writer.close();
579 assertFalse(outputFile.exists());
580 writer.open(executionContext);
581 writer.write(Collections.singletonList("test2"));
582 assertEquals("test2", readLine());
583 }
584
585 @Test
586 public void testWriteHeaderAfterRestartOnFirstChunk() throws Exception {
587 writer.setHeaderCallback(new FlatFileHeaderCallback() {
588
589 public void writeHeader(Writer writer) throws IOException {
590 writer.write("a\nb");
591 }
592
593 });
594 writer.open(executionContext);
595 writer.write(Collections.singletonList(TEST_STRING));
596 writer.close();
597 writer.open(executionContext);
598 writer.write(Collections.singletonList(TEST_STRING));
599 writer.close();
600 String lineFromFile = readLine();
601 assertEquals("a", lineFromFile);
602 lineFromFile = readLine();
603 assertEquals("b", lineFromFile);
604 lineFromFile = readLine();
605 assertEquals(TEST_STRING, lineFromFile);
606 lineFromFile = readLine();
607 assertEquals(null, lineFromFile);
608 }
609
610 @Test
611 public void testWriteHeaderAfterRestartOnSecondChunk() throws Exception {
612 writer.setHeaderCallback(new FlatFileHeaderCallback() {
613
614 public void writeHeader(Writer writer) throws IOException {
615 writer.write("a\nb");
616 }
617
618 });
619 writer.open(executionContext);
620 writer.write(Collections.singletonList(TEST_STRING));
621 writer.update(executionContext);
622 writer.write(Collections.singletonList(TEST_STRING));
623 writer.close();
624 String lineFromFile = readLine();
625 assertEquals("a", lineFromFile);
626 lineFromFile = readLine();
627 assertEquals("b", lineFromFile);
628 lineFromFile = readLine();
629 assertEquals(TEST_STRING, lineFromFile);
630 writer.open(executionContext);
631 writer.write(Collections.singletonList(TEST_STRING));
632 writer.close();
633 reader = null;
634 lineFromFile = readLine();
635 assertEquals("a", lineFromFile);
636 lineFromFile = readLine();
637 assertEquals("b", lineFromFile);
638 lineFromFile = readLine();
639 assertEquals(TEST_STRING, lineFromFile);
640 lineFromFile = readLine();
641 assertEquals(TEST_STRING, lineFromFile);
642 }
643
644 @Test
645
646
647
648 public void testLineAggregatorFailure() throws Exception {
649
650 writer.setLineAggregator(new LineAggregator<String>() {
651
652 public String aggregate(String item) {
653 if (item.equals("2")) {
654 throw new RuntimeException("aggregation failed on " + item);
655 }
656 return item;
657 }
658 });
659 List<String> items = new ArrayList<String>() {
660 {
661 add("1");
662 add("2");
663 add("3");
664 }
665 };
666
667 writer.open(executionContext);
668 try {
669 writer.write(items);
670 fail();
671 }
672 catch (RuntimeException expected) {
673 assertEquals("aggregation failed on 2", expected.getMessage());
674 }
675
676
677 assertNull(readLine());
678 }
679
680 @Test
681
682
683
684 public void testAppendToNotYetExistingFile() throws Exception {
685 Resource toBeCreated = new FileSystemResource("target/FlatFileItemWriterTests.out");
686
687 outputFile = toBeCreated.getFile();
688
689 assertFalse("output file does not exist yet", toBeCreated.exists());
690 writer.setResource(toBeCreated);
691 writer.setAppendAllowed(true);
692 writer.afterPropertiesSet();
693
694 writer.open(executionContext);
695 assertTrue("output file was created", toBeCreated.exists());
696
697 writer.write(Collections.singletonList("test1"));
698 writer.close();
699 assertEquals("test1", readLine());
700 }
701 }