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.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 }