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.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 | public ChunkIterator iterator() { |
127 | return new ChunkIterator(items); |
128 | } |
129 | |
130 | /** |
131 | * @return the number of items (excluding skips) |
132 | */ |
133 | public int size() { |
134 | return items.size(); |
135 | } |
136 | |
137 | /** |
138 | * Flag to indicate if the source data is exhausted. |
139 | * |
140 | * @return true if there is no more data to process |
141 | */ |
142 | public boolean isEnd() { |
143 | return end; |
144 | } |
145 | |
146 | /** |
147 | * Set the flag to say that this chunk represents an end of stream (there is |
148 | * no more data to process). |
149 | */ |
150 | public void setEnd() { |
151 | this.end = true; |
152 | } |
153 | |
154 | /** |
155 | * Query the chunk to see if anyone has registered an interest in keeping a |
156 | * reference to it. |
157 | * |
158 | * @return the busy flag |
159 | */ |
160 | public boolean isBusy() { |
161 | return busy; |
162 | } |
163 | |
164 | /** |
165 | * Register an interest in the chunk to prevent it from being cleaned up |
166 | * before the flag is reset to false. |
167 | * |
168 | * @param busy the flag to set |
169 | */ |
170 | public void setBusy(boolean busy) { |
171 | this.busy = busy; |
172 | } |
173 | |
174 | /** |
175 | * Clear only the skips list. |
176 | */ |
177 | public void clearSkips() { |
178 | skips.clear(); |
179 | } |
180 | |
181 | public Object getUserData() { |
182 | return userData; |
183 | } |
184 | |
185 | public void setUserData(Object userData) { |
186 | this.userData = userData; |
187 | } |
188 | |
189 | /* |
190 | * (non-Javadoc) |
191 | * |
192 | * @see java.lang.Object#toString() |
193 | */ |
194 | @Override |
195 | public String toString() { |
196 | return String.format("[items=%s, skips=%s]", items, skips); |
197 | } |
198 | |
199 | /** |
200 | * Special iterator for a chunk providing the {@link #remove(Exception)} |
201 | * method for dynamically removing an item and adding it to the skips. |
202 | * |
203 | * @author Dave Syer |
204 | * |
205 | */ |
206 | public class ChunkIterator implements Iterator<W> { |
207 | |
208 | final private Iterator<W> iterator; |
209 | |
210 | private W next; |
211 | |
212 | public ChunkIterator(List<W> items) { |
213 | iterator = items.iterator(); |
214 | } |
215 | |
216 | public boolean hasNext() { |
217 | return iterator.hasNext(); |
218 | } |
219 | |
220 | public W next() { |
221 | next = iterator.next(); |
222 | return next; |
223 | } |
224 | |
225 | public void remove(Throwable e) { |
226 | remove(); |
227 | skips.add(new SkipWrapper<W>(next, e)); |
228 | } |
229 | |
230 | public void remove() { |
231 | if (next == null) { |
232 | if (iterator.hasNext()) { |
233 | next = iterator.next(); |
234 | } |
235 | else { |
236 | return; |
237 | } |
238 | } |
239 | iterator.remove(); |
240 | } |
241 | |
242 | @Override |
243 | public String toString() { |
244 | return String.format("[items=%s, skips=%s]", items, skips); |
245 | } |
246 | |
247 | } |
248 | |
249 | } |