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 }