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 }