View Javadoc

1   package org.springframework.batch.item.file;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertNotNull;
5   import static org.junit.Assert.assertNull;
6   import static org.junit.Assert.assertTrue;
7   import static org.junit.Assert.fail;
8   
9   import java.io.IOException;
10  import java.io.InputStream;
11  
12  import org.junit.Before;
13  import org.junit.Test;
14  import org.springframework.batch.item.ExecutionContext;
15  import org.springframework.batch.item.ItemStreamException;
16  import org.springframework.batch.item.file.mapping.PassThroughLineMapper;
17  import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
18  import org.springframework.core.io.AbstractResource;
19  import org.springframework.core.io.ByteArrayResource;
20  import org.springframework.core.io.FileSystemResource;
21  import org.springframework.core.io.Resource;
22  import org.springframework.util.ClassUtils;
23  import org.springframework.util.StringUtils;
24  
25  /**
26   * Tests for {@link FlatFileItemReader}.
27   */
28  public class FlatFileItemReaderTests {
29  
30  	// common value used for writing to a file
31  	private String TEST_STRING = "FlatFileInputTemplate-TestData";
32  
33  	private FlatFileItemReader<String> reader = new FlatFileItemReader<String>();
34  
35  	private ExecutionContext executionContext = new ExecutionContext();
36  
37  	@Before
38  	public void setUp() {
39  
40  		reader.setResource(getInputResource("testLine1\ntestLine2\ntestLine3\ntestLine4\ntestLine5\ntestLine6"));
41  		reader.setLineMapper(new PassThroughLineMapper());
42  	}
43  
44  	@Test
45  	public void testRestartWithCustomRecordSeparatorPolicy() throws Exception {
46  
47  		reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() {
48  			// 1 record = 2 lines
49  			boolean pair = true;
50  
51  			public boolean isEndOfRecord(String line) {
52  				pair = !pair;
53  				return pair;
54  			}
55  
56  			public String postProcess(String record) {
57  				return record;
58  			}
59  
60  			public String preProcess(String record) {
61  				return record;
62  			}
63  		});
64  
65  		reader.open(executionContext);
66  
67  		assertEquals("testLine1testLine2", reader.read());
68  		assertEquals("testLine3testLine4", reader.read());
69  
70  		reader.update(executionContext);
71  
72  		reader.close();
73  
74  		reader.open(executionContext);
75  
76  		assertEquals("testLine5testLine6", reader.read());
77  	}
78  
79  	@Test
80  	public void testCustomRecordSeparatorPolicyEndOfFile() throws Exception {
81  
82  		reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() {
83  			// 1 record = 2 lines
84  			boolean pair = true;
85  
86  			public boolean isEndOfRecord(String line) {
87  				pair = !pair;
88  				return pair;
89  			}
90  
91  			public String postProcess(String record) {
92  				return record;
93  			}
94  
95  			public String preProcess(String record) {
96  				return record;
97  			}
98  		});
99  
100 		reader.setResource(getInputResource("testLine1\ntestLine2\ntestLine3\n"));
101 		reader.open(executionContext);
102 
103 		assertEquals("testLine1testLine2", reader.read());
104 
105 		try {
106 			reader.read();
107 			fail("Expected Exception");
108 		}
109 		catch (FlatFileParseException e) {
110 			// File ends in the middle of a record
111 			assertEquals(3, e.getLineNumber());
112 			assertEquals("testLine3", e.getInput());
113 		}
114 
115 	}
116 
117 	@Test
118 	public void testCustomRecordSeparatorBlankLine() throws Exception {
119 
120 		reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() {
121 
122 			public boolean isEndOfRecord(String line) {
123 				return StringUtils.hasText(line);
124 			}
125 
126 			public String postProcess(String record) {
127 				return StringUtils.hasText(record) ? record : null;
128 			}
129 
130 			public String preProcess(String record) {
131 				return record;
132 			}
133 		});
134 
135 		reader.setResource(getInputResource("testLine1\ntestLine2\ntestLine3\n\n"));
136 		reader.open(executionContext);
137 
138 		assertEquals("testLine1", reader.read());
139 		assertEquals("testLine2", reader.read());
140 		assertEquals("testLine3", reader.read());
141 		assertEquals(null, reader.read());
142 
143 	}
144 
145 	@Test
146 	public void testCustomRecordSeparatorMultilineBlankLineAfterEnd() throws Exception {
147 
148 		reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() {
149 
150 			// 1 record = 2 lines
151 			boolean pair = true;
152 
153 			public boolean isEndOfRecord(String line) {
154 				if (StringUtils.hasText(line)) {
155 					pair = !pair;
156 				}
157 				return pair;
158 			}
159 
160 			public String postProcess(String record) {
161 				return StringUtils.hasText(record) ? record : null;
162 			}
163 
164 			public String preProcess(String record) {
165 				return record;
166 			}
167 		});
168 
169 		reader.setResource(getInputResource("testLine1\ntestLine2\n\n"));
170 		reader.open(executionContext);
171 
172 		assertEquals("testLine1testLine2", reader.read());
173 		assertEquals(null, reader.read());
174 
175 	}
176 
177 	@Test
178 	public void testRestartWithSkippedLines() throws Exception {
179 
180 		reader.setLinesToSkip(2);
181 		reader.open(executionContext);
182 
183 		// read some records
184 		reader.read();
185 		reader.read();
186 		// get restart data
187 		reader.update(executionContext);
188 		// read next two records
189 		reader.read();
190 		reader.read();
191 
192 		assertEquals(2, executionContext.getInt(ClassUtils.getShortName(FlatFileItemReader.class) + ".read.count"));
193 		// close input
194 		reader.close();
195 
196 		reader.setResource(getInputResource("header\nignoreme\ntestLine1\ntestLine2\ntestLine3\ntestLine4\ntestLine5\ntestLine6"));
197 
198 		// init for restart
199 		reader.open(executionContext);
200 
201 		// read remaining records
202 		assertEquals("testLine3", reader.read());
203 		assertEquals("testLine4", reader.read());
204 
205 		reader.update(executionContext);
206 		assertEquals(4, executionContext.getInt(ClassUtils.getShortName(FlatFileItemReader.class) + ".read.count"));
207 	}
208 
209 	@Test
210 	public void testCurrentItemCount() throws Exception {
211 
212 		reader.setCurrentItemCount(2);
213 		reader.open(executionContext);
214 
215 		// read some records
216 		reader.read();
217 		reader.read();
218 		// get restart data
219 		reader.update(executionContext);
220 
221 		assertEquals(4, executionContext.getInt(ClassUtils.getShortName(FlatFileItemReader.class) + ".read.count"));
222 		// close input
223 		reader.close();
224 
225 	}
226 
227 	@Test
228 	public void testMaxItemCount() throws Exception {
229 
230 		reader.setMaxItemCount(2);
231 		reader.open(executionContext);
232 
233 		// read some records
234 		reader.read();
235 		reader.read();
236 		// get restart data
237 		reader.update(executionContext);
238 		assertNull(reader.read());
239 
240 		assertEquals(2, executionContext.getInt(ClassUtils.getShortName(FlatFileItemReader.class) + ".read.count"));
241 		// close input
242 		reader.close();
243 
244 	}
245 
246 	@Test
247 	public void testMaxItemCountFromContext() throws Exception {
248 
249 		reader.setMaxItemCount(2);
250 		executionContext.putInt(reader.getClass().getSimpleName() + ".read.count.max", Integer.MAX_VALUE);
251 		reader.open(executionContext);
252 		// read some records
253 		reader.read();
254 		reader.read();
255 		assertNotNull(reader.read());
256 		// close input
257 		reader.close();
258 
259 	}
260 
261 	@Test
262 	public void testCurrentItemCountFromContext() throws Exception {
263 
264 		reader.setCurrentItemCount(2);
265 		executionContext.putInt(reader.getClass().getSimpleName() + ".read.count", 3);
266 		reader.open(executionContext);
267 		// read some records
268 		assertEquals("testLine4", reader.read());
269 		// close input
270 		reader.close();
271 
272 	}
273 
274 	@Test
275 	public void testMaxAndCurrentItemCount() throws Exception {
276 
277 		reader.setMaxItemCount(2);
278 		reader.setCurrentItemCount(2);
279 		reader.open(executionContext);
280 		// read some records
281 		assertNull(reader.read());
282 		// close input
283 		reader.close();
284 
285 	}
286 
287 	@Test
288 	public void testNonExistentResource() throws Exception {
289 
290 		Resource resource = new NonExistentResource();
291 
292 		reader.setResource(resource);
293 
294 		// afterPropertiesSet should only throw an exception if the Resource is
295 		// null
296 		reader.afterPropertiesSet();
297 
298 		reader.setStrict(false);
299 		reader.open(executionContext);
300 		assertNull(reader.read());
301 		reader.close();
302 	}
303 
304 	@Test
305 	public void testOpenBadIOInput() throws Exception {
306 
307 		reader.setResource(new AbstractResource() {
308 			public String getDescription() {
309 				return null;
310 			}
311 
312 			public InputStream getInputStream() throws IOException {
313 				throw new IOException();
314 			}
315 
316 			public boolean exists() {
317 				return true;
318 			}
319 		});
320 
321 		try {
322 			reader.open(executionContext);
323 			fail();
324 		}
325 		catch (ItemStreamException ex) {
326 			// expected
327 		}
328 
329 		// read() should then return a null
330 		assertNull(reader.read());
331 		reader.close();
332 
333 	}
334 
335 	@Test
336 	public void testDirectoryResource() throws Exception {
337 
338 		FileSystemResource resource = new FileSystemResource("target/data");
339 		resource.getFile().mkdirs();
340 		assertTrue(resource.getFile().isDirectory());
341 		reader.setResource(resource);
342 		reader.afterPropertiesSet();
343 
344 		reader.setStrict(false);
345 		reader.open(executionContext);
346 		assertNull(reader.read());
347 
348 	}
349 
350 	@Test
351 	public void testRuntimeFileCreation() throws Exception {
352 
353 		Resource resource = new NonExistentResource();
354 
355 		reader.setResource(resource);
356 
357 		// afterPropertiesSet should only throw an exception if the Resource is
358 		// null
359 		reader.afterPropertiesSet();
360 
361 		// replace the resource to simulate runtime resource creation
362 		reader.setResource(getInputResource(TEST_STRING));
363 		reader.open(executionContext);
364 		assertEquals(TEST_STRING, reader.read());
365 	}
366 
367 	/**
368 	 * In strict mode, resource must exist at the time reader is opened.
369 	 */
370 	@Test(expected = ItemStreamException.class)
371 	public void testStrictness() throws Exception {
372 
373 		Resource resource = new NonExistentResource();
374 
375 		reader.setResource(resource);
376 		reader.setStrict(true);
377 
378 		reader.afterPropertiesSet();
379 
380 		reader.open(executionContext);
381 	}
382 
383 	/**
384 	 * Exceptions from {@link LineMapper} are wrapped as {@link FlatFileParseException} containing contextual info about
385 	 * the problematic line and its line number.
386 	 */
387 	@Test
388 	public void testMappingExceptionWrapping() throws Exception {
389 		LineMapper<String> exceptionLineMapper = new LineMapper<String>() {
390 			public String mapLine(String line, int lineNumber) throws Exception {
391 				if (lineNumber == 2) {
392 					throw new Exception("Couldn't map line 2");
393 				}
394 				return line;
395 			}
396 		};
397 		reader.setLineMapper(exceptionLineMapper);
398 		reader.afterPropertiesSet();
399 
400 		reader.open(executionContext);
401 		assertNotNull(reader.read());
402 
403 		try {
404 			reader.read();
405 			fail();
406 		}
407 		catch (FlatFileParseException expected) {
408 			assertEquals(2, expected.getLineNumber());
409 			assertEquals("testLine2", expected.getInput());
410 			assertEquals("Couldn't map line 2", expected.getCause().getMessage());
411 			assertEquals("Parsing error at line: 2 in resource=[resource loaded from byte array], input=[testLine2]", expected.getMessage());
412 		}
413 	}
414 
415 	private Resource getInputResource(String input) {
416 		return new ByteArrayResource(input.getBytes());
417 	}
418 
419 	private static class NonExistentResource extends AbstractResource {
420 
421 		public NonExistentResource() {
422 		}
423 
424 		public boolean exists() {
425 			return false;
426 		}
427 
428 		public String getDescription() {
429 			return "NonExistentResource";
430 		}
431 
432 		public InputStream getInputStream() throws IOException {
433 			return null;
434 		}
435 	}
436 }