001package com.identityworksllc.iiq.common;
002
003import java.util.concurrent.ConcurrentHashMap;
004import java.util.concurrent.ConcurrentMap;
005
006/**
007 * A singleton global object with an internal InheritableThreadLocal, allowing
008 * sharing of cached objects within a single thread. Objects will be garbage
009 * collected when the thread itself is cleaned up, so this should either not
010 * be used or routinely clear()'d in long-running threads.
011 *
012 * Since the ThreadLocal is inheritable, child threads will automatically inherit
013 * any stored values.
014 *
015 * This class exposes the ConcurrentMap interface and can be used as such.
016 */
017@SuppressWarnings("unused")
018public final class ThreadStorage implements DelegatedConcurrentMap<String, Object>, TypeFriendlyMap<String, Object> {
019    /**
020     * The lock object used for singleton construction
021     */
022    private static final Object _LOCK = new Object();
023
024    /**
025     * The singleton object
026     */
027    private static ThreadStorage _SINGLETON;
028
029    /**
030     * Retrieves the singleton thread storage object, constructing a new one if needed
031     * @return The singleton thread storage object
032     */
033    public static ThreadStorage get() {
034        if (_SINGLETON == null) {
035            synchronized (_LOCK) {
036                if (_SINGLETON == null) {
037                    _SINGLETON = new ThreadStorage();
038                }
039            }
040        }
041        return _SINGLETON;
042    }
043
044    /**
045     * The InheritableThreadLocal object that stores the local Map data. It will
046     * be initialized with an empty ConcurrentHashMap on first use in a given thread.
047     */
048    private final ThreadLocal<ConcurrentMap<String, Object>> threadLocal;
049
050    /**
051     * Private constructor to enforce singleton
052     */
053    private ThreadStorage() {
054        threadLocal = InheritableThreadLocal.withInitial(ConcurrentHashMap::new);
055    }
056
057    /**
058     * Retrieves the delegate, in this case the ThreadLocal map
059     * @return The delegated map object
060     */
061    @Override
062    public ConcurrentMap<String, Object> getDelegate() {
063        return threadLocal.get();
064    }
065}