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