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