1 | /* |
2 | * Copyright 2006-2013 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.core.step.item; |
18 | |
19 | import java.util.ArrayList; |
20 | import java.util.Collection; |
21 | import java.util.Collections; |
22 | import java.util.Iterator; |
23 | import java.util.List; |
24 | |
25 | /** |
26 | * Encapsulation of a list of items to be processed and possibly a list of |
27 | * failed items to be skipped. To mark an item as skipped clients should iterate |
28 | * over the chunk using the {@link #iterator()} method, and if there is a |
29 | * failure call {@link org.springframework.batch.core.step.item.Chunk.ChunkIterator#remove()} on the iterator. |
30 | * The skipped items are then available through the chunk. |
31 | * |
32 | * @author Dave Syer |
33 | * @since 2.0 |
34 | */ |
35 | public class Chunk<W> implements Iterable<W> { |
36 | |
37 | private List<W> items = new ArrayList<W>(); |
38 | |
39 | private List<SkipWrapper<W>> skips = new ArrayList<SkipWrapper<W>>(); |
40 | |
41 | private List<Exception> errors = new ArrayList<Exception>(); |
42 | |
43 | private Object userData; |
44 | |
45 | private boolean end; |
46 | |
47 | private boolean busy; |
48 | |
49 | public Chunk() { |
50 | this(null, null); |
51 | } |
52 | |
53 | public Chunk(Collection<? extends W> items) { |
54 | this(items, null); |
55 | } |
56 | |
57 | public Chunk(Collection<? extends W> items, List<SkipWrapper<W>> skips) { |
58 | super(); |
59 | if (items != null) { |
60 | this.items = new ArrayList<W>(items); |
61 | } |
62 | if (skips != null) { |
63 | this.skips = new ArrayList<SkipWrapper<W>>(skips); |
64 | } |
65 | } |
66 | |
67 | /** |
68 | * Add the item to the chunk. |
69 | * @param item |
70 | */ |
71 | public void add(W item) { |
72 | items.add(item); |
73 | } |
74 | |
75 | /** |
76 | * Clear the items down to signal that we are done. |
77 | */ |
78 | public void clear() { |
79 | items.clear(); |
80 | skips.clear(); |
81 | userData = null; |
82 | } |
83 | |
84 | /** |
85 | * @return a copy of the items to be processed as an unmodifiable list |
86 | */ |
87 | public List<W> getItems() { |
88 | return Collections.unmodifiableList(new ArrayList<W>(items)); |
89 | } |
90 | |
91 | /** |
92 | * @return a copy of the skips as an unmodifiable list |
93 | */ |
94 | public List<SkipWrapper<W>> getSkips() { |
95 | return Collections.unmodifiableList(skips); |
96 | } |
97 | |
98 | /** |
99 | * @return a copy of the anonymous errros as an unmodifiable list |
100 | */ |
101 | public List<Exception> getErrors() { |
102 | return Collections.unmodifiableList(errors); |
103 | } |
104 | |
105 | /** |
106 | * Register an anonymous skip. To skip an individual item, use |
107 | * {@link ChunkIterator#remove()}. |
108 | * |
109 | * @param e the exception that caused the skip |
110 | */ |
111 | public void skip(Exception e) { |
112 | errors.add(e); |
113 | } |
114 | |
115 | /** |
116 | * @return true if there are no items in the chunk |
117 | */ |
118 | public boolean isEmpty() { |
119 | return items.isEmpty(); |
120 | } |
121 | |
122 | /** |
123 | * Get an unmodifiable iterator for the underlying items. |
124 | * @see java.lang.Iterable#iterator() |
125 | */ |
126 | @Override |
127 | public ChunkIterator iterator() { |
128 | return new ChunkIterator(items); |
129 | } |
130 | |
131 | /** |
132 | * @return the number of items (excluding skips) |
133 | */ |
134 | public int size() { |
135 | return items.size(); |
136 | } |
137 | |
138 | /** |
139 | * Flag to indicate if the source data is exhausted. |
140 | * |
141 | * @return true if there is no more data to process |
142 | */ |
143 | public boolean isEnd() { |
144 | return end; |
145 | } |
146 | |
147 | /** |
148 | * Set the flag to say that this chunk represents an end of stream (there is |
149 | * no more data to process). |
150 | */ |
151 | public void setEnd() { |
152 | this.end = true; |
153 | } |
154 | |
155 | /** |
156 | * Query the chunk to see if anyone has registered an interest in keeping a |
157 | * reference to it. |
158 | * |
159 | * @return the busy flag |
160 | */ |
161 | public boolean isBusy() { |
162 | return busy; |
163 | } |
164 | |
165 | /** |
166 | * Register an interest in the chunk to prevent it from being cleaned up |
167 | * before the flag is reset to false. |
168 | * |
169 | * @param busy the flag to set |
170 | */ |
171 | public void setBusy(boolean busy) { |
172 | this.busy = busy; |
173 | } |
174 | |
175 | /** |
176 | * Clear only the skips list. |
177 | */ |
178 | public void clearSkips() { |
179 | skips.clear(); |
180 | } |
181 | |
182 | public Object getUserData() { |
183 | return userData; |
184 | } |
185 | |
186 | public void setUserData(Object userData) { |
187 | this.userData = userData; |
188 | } |
189 | |
190 | /* |
191 | * (non-Javadoc) |
192 | * |
193 | * @see java.lang.Object#toString() |
194 | */ |
195 | @Override |
196 | public String toString() { |
197 | return String.format("[items=%s, skips=%s]", items, skips); |
198 | } |
199 | |
200 | /** |
201 | * Special iterator for a chunk providing the {@link #remove(Throwable)} |
202 | * method for dynamically removing an item and adding it to the skips. |
203 | * |
204 | * @author Dave Syer |
205 | * |
206 | */ |
207 | public class ChunkIterator implements Iterator<W> { |
208 | |
209 | final private Iterator<W> iterator; |
210 | |
211 | private W next; |
212 | |
213 | public ChunkIterator(List<W> items) { |
214 | iterator = items.iterator(); |
215 | } |
216 | |
217 | @Override |
218 | public boolean hasNext() { |
219 | return iterator.hasNext(); |
220 | } |
221 | |
222 | @Override |
223 | public W next() { |
224 | next = iterator.next(); |
225 | return next; |
226 | } |
227 | |
228 | public void remove(Throwable e) { |
229 | remove(); |
230 | skips.add(new SkipWrapper<W>(next, e)); |
231 | } |
232 | |
233 | @Override |
234 | public void remove() { |
235 | if (next == null) { |
236 | if (iterator.hasNext()) { |
237 | next = iterator.next(); |
238 | } |
239 | else { |
240 | return; |
241 | } |
242 | } |
243 | iterator.remove(); |
244 | } |
245 | |
246 | @Override |
247 | public String toString() { |
248 | return String.format("[items=%s, skips=%s]", items, skips); |
249 | } |
250 | |
251 | } |
252 | |
253 | } |