1 | /* |
2 | * Copyright 2006-2007 the original author or authors. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | package org.springframework.batch.item.support; |
18 | |
19 | import java.util.ArrayList; |
20 | import java.util.Collection; |
21 | |
22 | import org.apache.commons.logging.Log; |
23 | import org.apache.commons.logging.LogFactory; |
24 | import org.springframework.batch.item.ItemReader; |
25 | import org.springframework.batch.item.file.mapping.FieldSetMapper; |
26 | |
27 | /** |
28 | * An {@link ItemReader} that delivers a list as its item, storing up objects |
29 | * from the injected {@link ItemReader} until they are ready to be packed out |
30 | * as a collection. The {@link ItemReader} should mark the beginning and end of |
31 | * records with the constant values in {@link FieldSetMapper} ({@link AggregateItemReader#BEGIN_RECORD} |
32 | * and {@link AggregateItemReader#END_RECORD}).<br/> |
33 | * |
34 | * This class is thread safe (it can be used concurrently by multiple threads) |
35 | * as long as the {@link ItemReader} is also thread safe. |
36 | * |
37 | * @author Dave Syer |
38 | * |
39 | */ |
40 | public class AggregateItemReader extends DelegatingItemReader { |
41 | |
42 | private static final Log log = LogFactory |
43 | .getLog(AggregateItemReader.class); |
44 | |
45 | /** |
46 | * Marker for the end of a multi-object record. |
47 | */ |
48 | public static final Object END_RECORD = new Object(); |
49 | |
50 | /** |
51 | * Marker for the beginning of a multi-object record. |
52 | */ |
53 | public static final Object BEGIN_RECORD = new Object(); |
54 | |
55 | /** |
56 | * Get the next list of records. |
57 | * @throws Exception |
58 | * |
59 | * @see org.springframework.batch.item.ItemReader#read() |
60 | */ |
61 | public Object read() throws Exception { |
62 | ResultHolder holder = new ResultHolder(); |
63 | |
64 | while (process(super.read(), holder)) { |
65 | continue; |
66 | } |
67 | |
68 | if (!holder.exhausted) { |
69 | return holder.records; |
70 | } else { |
71 | return null; |
72 | } |
73 | } |
74 | |
75 | private boolean process(Object value, ResultHolder holder) { |
76 | // finish processing if we hit the end of file |
77 | if (value == null) { |
78 | log.debug("Exhausted ItemReader"); |
79 | holder.exhausted = true; |
80 | return false; |
81 | } |
82 | |
83 | // start a new collection |
84 | if (value == AggregateItemReader.BEGIN_RECORD) { |
85 | log.debug("Start of new record detected"); |
86 | return true; |
87 | } |
88 | |
89 | // mark we are finished with current collection |
90 | if (value == AggregateItemReader.END_RECORD) { |
91 | log.debug("End of record detected"); |
92 | return false; |
93 | } |
94 | |
95 | // add a simple record to the current collection |
96 | log.debug("Mapping: " + value); |
97 | holder.records.add(value); |
98 | return true; |
99 | } |
100 | |
101 | /** |
102 | * Private class for temporary state management while item is being |
103 | * collected. |
104 | * |
105 | * @author Dave Syer |
106 | * |
107 | */ |
108 | private static class ResultHolder { |
109 | Collection records = new ArrayList(); |
110 | boolean exhausted = false; |
111 | } |
112 | |
113 | } |