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; |
18 | |
19 | import java.io.Serializable; |
20 | import java.util.Map; |
21 | import java.util.Set; |
22 | import java.util.Map.Entry; |
23 | import java.util.concurrent.ConcurrentHashMap; |
24 | |
25 | import org.springframework.util.Assert; |
26 | |
27 | /** |
28 | * Object representing a context for an {@link ItemStream}. It is a thin wrapper |
29 | * for a map that allows optionally for type safety on reads. It also allows for |
30 | * dirty checking by setting a 'dirty' flag whenever any put is called. |
31 | * |
32 | * Note that putting <code>null</code> value is equivalent to removing the entry |
33 | * for the given key. |
34 | * |
35 | * @author Lucas Ward |
36 | * @author Douglas Kaminsky |
37 | */ |
38 | public class ExecutionContext implements Serializable { |
39 | |
40 | private volatile boolean dirty = false; |
41 | |
42 | private final Map<String, Object> map; |
43 | |
44 | /** |
45 | * Default constructor. Initializes a new execution context with an empty |
46 | * internal map. |
47 | */ |
48 | public ExecutionContext() { |
49 | map = new ConcurrentHashMap<String, Object>(); |
50 | } |
51 | |
52 | /** |
53 | * Initializes a new execution context with the contents of another map. |
54 | * |
55 | * @param map Initial contents of context. |
56 | */ |
57 | public ExecutionContext(Map<String, Object> map) { |
58 | this.map = new ConcurrentHashMap<String, Object>(map); |
59 | } |
60 | |
61 | /** |
62 | * @param executionContext |
63 | */ |
64 | public ExecutionContext(ExecutionContext executionContext) { |
65 | this(); |
66 | if (executionContext == null) { |
67 | return; |
68 | } |
69 | for (Entry<String, Object> entry : executionContext.entrySet()) { |
70 | this.map.put(entry.getKey(), entry.getValue()); |
71 | } |
72 | } |
73 | |
74 | /** |
75 | * Adds a String value to the context. |
76 | * |
77 | * @param key Key to add to context |
78 | * @param value Value to associate with key |
79 | */ |
80 | |
81 | public void putString(String key, String value) { |
82 | |
83 | put(key, value); |
84 | } |
85 | |
86 | /** |
87 | * Adds a Long value to the context. |
88 | * |
89 | * @param key Key to add to context |
90 | * @param value Value to associate with key |
91 | */ |
92 | public void putLong(String key, long value) { |
93 | |
94 | put(key, Long.valueOf(value)); |
95 | } |
96 | |
97 | /** |
98 | * Adds an Integer value to the context. |
99 | * |
100 | * @param key Key to add to context |
101 | * @param value Value to associate with key |
102 | */ |
103 | public void putInt(String key, int value) { |
104 | put(key, Integer.valueOf(value)); |
105 | } |
106 | |
107 | /** |
108 | * Add a Double value to the context. |
109 | * |
110 | * @param key Key to add to context |
111 | * @param value Value to associate with key |
112 | */ |
113 | public void putDouble(String key, double value) { |
114 | |
115 | put(key, Double.valueOf(value)); |
116 | } |
117 | |
118 | /** |
119 | * Add an Object value to the context (must be Serializable). Putting |
120 | * <code>null</code> value for a given key removes the key. |
121 | * |
122 | * @param key Key to add to context |
123 | * @param value Value to associate with key |
124 | */ |
125 | public void put(String key, Object value) { |
126 | if (value != null) { |
127 | Assert.isInstanceOf(Serializable.class, value, "Value: [ " + value + "must be serializable."); |
128 | Object result = map.put(key, value); |
129 | dirty = result==null || result!=null && !result.equals(value); |
130 | } |
131 | else { |
132 | Object result = map.remove(key); |
133 | dirty = result!=null; |
134 | } |
135 | } |
136 | |
137 | /** |
138 | * Indicates if context has been changed with a "put" operation since the |
139 | * dirty flag was last cleared. Note that the last time the flag was cleared |
140 | * might correspond to creation of the context. |
141 | * |
142 | * @return True if "put" operation has occurred since flag was last cleared |
143 | */ |
144 | public boolean isDirty() { |
145 | return dirty; |
146 | } |
147 | |
148 | /** |
149 | * Typesafe Getter for the String represented by the provided key. |
150 | * |
151 | * @param key The key to get a value for |
152 | * @return The <code>String</code> value |
153 | */ |
154 | public String getString(String key) { |
155 | |
156 | return (String) readAndValidate(key, String.class); |
157 | } |
158 | |
159 | /** |
160 | * Typesafe Getter for the String represented by the provided key with |
161 | * default value to return if key is not represented. |
162 | * |
163 | * @param key The key to get a value for |
164 | * @param defaultString Default to return if key is not represented |
165 | * @return The <code>String</code> value if key is repreesnted, specified |
166 | * default otherwise |
167 | */ |
168 | public String getString(String key, String defaultString) { |
169 | if (!map.containsKey(key)) { |
170 | return defaultString; |
171 | } |
172 | |
173 | return (String) readAndValidate(key, String.class); |
174 | } |
175 | |
176 | /** |
177 | * Typesafe Getter for the Long represented by the provided key. |
178 | * |
179 | * @param key The key to get a value for |
180 | * @return The <code>Long</code> value |
181 | */ |
182 | public long getLong(String key) { |
183 | |
184 | return ((Long) readAndValidate(key, Long.class)).longValue(); |
185 | } |
186 | |
187 | /** |
188 | * Typesafe Getter for the Long represented by the provided key with default |
189 | * value to return if key is not represented. |
190 | * |
191 | * @param key The key to get a value for |
192 | * @param defaultLong Default to return if key is not represented |
193 | * @return The <code>long</code> value if key is represented, specified |
194 | * default otherwise |
195 | */ |
196 | public long getLong(String key, long defaultLong) { |
197 | if (!map.containsKey(key)) { |
198 | return defaultLong; |
199 | } |
200 | |
201 | return ((Long) readAndValidate(key, Long.class)).longValue(); |
202 | } |
203 | |
204 | /** |
205 | * Typesafe Getter for the Integer represented by the provided key. |
206 | * |
207 | * @param key The key to get a value for |
208 | * @return The <code>Integer</code> value |
209 | */ |
210 | public int getInt(String key) { |
211 | |
212 | return ((Integer) readAndValidate(key, Integer.class)).intValue(); |
213 | } |
214 | |
215 | /** |
216 | * Typesafe Getter for the Integer represented by the provided key with |
217 | * default value to return if key is not represented. |
218 | * |
219 | * @param key The key to get a value for |
220 | * @param defaultInt Default to return if key is not represented |
221 | * @return The <code>int</code> value if key is represented, specified |
222 | * default otherwise |
223 | */ |
224 | public int getInt(String key, int defaultInt) { |
225 | if (!map.containsKey(key)) { |
226 | return defaultInt; |
227 | } |
228 | |
229 | return ((Integer) readAndValidate(key, Integer.class)).intValue(); |
230 | } |
231 | |
232 | /** |
233 | * Typesafe Getter for the Double represented by the provided key. |
234 | * |
235 | * @param key The key to get a value for |
236 | * @return The <code>Double</code> value |
237 | */ |
238 | public double getDouble(String key) { |
239 | return ((Double) readAndValidate(key, Double.class)).doubleValue(); |
240 | } |
241 | |
242 | /** |
243 | * Typesafe Getter for the Double represented by the provided key with |
244 | * default value to return if key is not represented. |
245 | * |
246 | * @param key The key to get a value for |
247 | * @param defaultDouble Default to return if key is not represented |
248 | * @return The <code>double</code> value if key is represented, specified |
249 | * default otherwise |
250 | */ |
251 | public double getDouble(String key, double defaultDouble) { |
252 | if (!map.containsKey(key)) { |
253 | return defaultDouble; |
254 | } |
255 | |
256 | return ((Double) readAndValidate(key, Double.class)).doubleValue(); |
257 | } |
258 | |
259 | /** |
260 | * Getter for the value represented by the provided key. |
261 | * |
262 | * @param key The key to get a value for |
263 | * @return The value represented by the given key |
264 | */ |
265 | public Object get(String key) { |
266 | return map.get(key); |
267 | } |
268 | |
269 | /** |
270 | * Utility method that attempts to take a value represented by a given key |
271 | * and validate it as a member of the specified type. |
272 | * |
273 | * @param key The key to validate a value for |
274 | * @param type Class against which value should be validated |
275 | * @return Value typed to the specified <code>Class</code> |
276 | */ |
277 | private Object readAndValidate(String key, Class<?> type) { |
278 | |
279 | Object value = map.get(key); |
280 | |
281 | if (!type.isInstance(value)) { |
282 | throw new ClassCastException("Value for key=[" + key + "] is not of type: [" + type + "], it is [" |
283 | + (value == null ? null : "(" + value.getClass() + ")" + value) + "]"); |
284 | } |
285 | |
286 | return value; |
287 | } |
288 | |
289 | /** |
290 | * Indicates whether or not the context is empty. |
291 | * |
292 | * @return True if the context has no entries, false otherwise. |
293 | * @see java.util.Map#isEmpty() |
294 | */ |
295 | public boolean isEmpty() { |
296 | return map.isEmpty(); |
297 | } |
298 | |
299 | /** |
300 | * Clears the dirty flag. |
301 | */ |
302 | public void clearDirtyFlag() { |
303 | dirty = false; |
304 | } |
305 | |
306 | /** |
307 | * Returns the entry set containing the contents of this context. |
308 | * |
309 | * @return A set representing the contents of the context |
310 | * @see java.util.Map#entrySet() |
311 | */ |
312 | public Set<Entry<String, Object>> entrySet() { |
313 | return map.entrySet(); |
314 | } |
315 | |
316 | /** |
317 | * Indicates whether or not a key is represented in this context. |
318 | * |
319 | * @param key Key to check existence for |
320 | * @return True if key is represented in context, false otherwise |
321 | * @see java.util.Map#containsKey(Object) |
322 | */ |
323 | public boolean containsKey(String key) { |
324 | return map.containsKey(key); |
325 | } |
326 | |
327 | /** |
328 | * Removes the mapping for a key from this context if it is present. |
329 | * |
330 | * @see java.util.Map#remove(Object) |
331 | */ |
332 | public Object remove(String key) { |
333 | return map.remove(key); |
334 | } |
335 | |
336 | /** |
337 | * Indicates whether or not a value is represented in this context. |
338 | * |
339 | * @param value Value to check existence for |
340 | * @return True if value is represented in context, false otherwise |
341 | * @see java.util.Map#containsValue(Object) |
342 | */ |
343 | public boolean containsValue(Object value) { |
344 | return map.containsValue(value); |
345 | } |
346 | |
347 | /* |
348 | * (non-Javadoc) |
349 | * |
350 | * @see java.lang.Object#equals(java.lang.Object) |
351 | */ |
352 | public boolean equals(Object obj) { |
353 | if (obj instanceof ExecutionContext == false) { |
354 | return false; |
355 | } |
356 | if (this == obj) { |
357 | return true; |
358 | } |
359 | ExecutionContext rhs = (ExecutionContext) obj; |
360 | return this.entrySet().equals(rhs.entrySet()); |
361 | } |
362 | |
363 | /* |
364 | * (non-Javadoc) |
365 | * |
366 | * @see java.lang.Object#hashCode() |
367 | */ |
368 | public int hashCode() { |
369 | return map.hashCode(); |
370 | } |
371 | |
372 | /* |
373 | * (non-Javadoc) |
374 | * |
375 | * @see java.lang.Object#toString() |
376 | */ |
377 | public String toString() { |
378 | return map.toString(); |
379 | } |
380 | |
381 | /** |
382 | * Returns number of entries in the context |
383 | * |
384 | * @return Number of entries in the context |
385 | * @see java.util.Map#size() |
386 | */ |
387 | public int size() { |
388 | return map.size(); |
389 | } |
390 | |
391 | } |