View Javadoc

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 }