View Javadoc

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 }