All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xyz.downgoon.mydk.concurrent.ThreadContext Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
package xyz.downgoon.mydk.concurrent;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import xyz.downgoon.mydk.util.CollectionUtils;

/**
 * A ThreadContext provides a means of binding and unbinding objects to the
 * current thread based on key/value pairs.
 * 

*

* An internal {@link java.util.HashMap} is used to maintain the key/value pairs * for each thread. *

*

*

* If the desired behavior is to ensure that bound data is not shared across * threads in a pooled or reusable threaded environment, the application (or * more likely a framework) must bind and remove any necessary values at the * beginning and end of stack execution, respectively (i.e. individually * explicitly or all via the clear method). *

* * @see #removeAll() or {@link #clear()} * @since 0.1 */ public abstract class ThreadContext { private static final ThreadLocal> resources = new InheritableThreadLocalMap>(); /** * Default no-argument constructor. */ protected ThreadContext() { } /** * Returns the ThreadLocal Map. This Map is used internally to bind objects * to the current thread by storing each object under a unique key. * * @return the map of bound resources */ public static Map getResources() { if (resources.get() == null) { return Collections.emptyMap(); } else { return new HashMap(resources.get()); } } /** * Allows a caller to explicitly set the entire resource map. This operation * overwrites everything that existed previously in the ThreadContext - if * you need to retain what was on the thread prior to calling this method, * call the {@link #getResources()} method, which will give you the existing * state. * * @param newResources * the resources to replace the existing {@link #getResources() * resources}. * @since 1.0 */ public static void setResources(Map newResources) { if (CollectionUtils.isEmpty(newResources)) { return; } ensureResourcesInitialized(); resources.get().clear(); resources.get().putAll(newResources); } /** * Returns the value bound in the {@code ThreadContext} under the specified * {@code key}, or {@code null} if there is no value for that {@code key}. * * @param key * the map key to use to lookup the value * @return the value bound in the {@code ThreadContext} under the specified * {@code key}, or {@code null} if there is no value for that * {@code key}. * @since 1.0 */ private static Object getValue(Object key) { Map perThreadResources = resources.get(); return perThreadResources != null ? perThreadResources.get(key) : null; } private static void ensureResourcesInitialized() { if (resources.get() == null) { resources.set(new HashMap()); } } /** * Returns the object for the specified key that is bound to * the current thread. * * @param key * the key that identifies the value to return * @return the object keyed by key or null if no * value exists for the specified key */ public static Object get(Object key) { return getValue(key); } /** * Binds value for the given key to the current * thread. *

*

* A null value has the same effect as if remove * was called for the given key, i.e.: *

* *

	 * if (value == null) {
	 * 	remove(key);
	 * }
	 * 
* * @param key * The key with which to identify the value. * @param value * The value to bind to the thread. * @throws IllegalArgumentException * if the key argument is null. */ public static void put(Object key, Object value) { if (key == null) { throw new IllegalArgumentException("key cannot be null"); } if (value == null) { remove(key); return; } ensureResourcesInitialized(); resources.get().put(key, value); } /** * Unbinds the value for the given key from the current thread. * * @param key * The key identifying the value bound to the current thread. * @return the object unbound or null if there was nothing bound * under the specified key name. */ public static Object remove(Object key) { Map perThreadResources = resources.get(); Object value = perThreadResources != null ? perThreadResources.remove(key) : null; return value; } /** * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal * ThreadLocal} from the thread. *

* This method is meant to be the final 'clean up' operation that is called * at the end of thread execution to prevent thread corruption in pooled * thread environments. * * @since 1.0 */ public static void removeAll() { resources.remove(); } /** * {@link ThreadLocal#remove Remove}s the underlying {@link ThreadLocal * ThreadLocal} from the thread. *

* This method is meant to be the final 'clean up' operation that is called * at the end of thread execution to prevent thread corruption in pooled * thread environments. * * @since 1.0 */ public static void clear() { removeAll(); } private static final class InheritableThreadLocalMap> extends InheritableThreadLocal> { /** * This implementation was added to address a * user-reported issue. * * @param parentValue * the parent value, a HashMap as defined in the * {@link #initialValue()} method. * @return the HashMap to be used by any parent-spawned child threads (a * clone of the parent HashMap). */ @SuppressWarnings({ "unchecked" }) protected Map childValue(Map parentValue) { if (parentValue != null) { return (Map) ((HashMap) parentValue).clone(); } else { return null; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy