001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.service.impl;
016    
017    import java.util.HashMap;
018    import java.util.Map;
019    
020    import org.apache.hivemind.service.ThreadCleanupListener;
021    import org.apache.hivemind.service.ThreadEventNotifier;
022    import org.apache.hivemind.service.ThreadLocalStorage;
023    
024    /**
025     * Implementation of {@link org.apache.hivemind.service.ThreadLocalStorage}.
026     * 
027     * @author Howard Lewis Ship, Harish Krishnaswamy
028     */
029    public class ThreadLocalStorageImpl implements ThreadLocalStorage, ThreadCleanupListener
030    {
031        private static final String INITIALIZED_KEY =
032            "$org.apache.hivemind.service.impl.ThreadLocalStorageImpl#initialized$";
033    
034        private CleanableThreadLocal _local = new CleanableThreadLocal();
035        private ThreadEventNotifier _notifier;
036    
037        private static class CleanableThreadLocal extends ThreadLocal
038        {
039            /**
040             * <p>
041             * Intializes the variable with a HashMap containing a single Boolean flag to denote the
042             * initialization of the variable. The Boolean flag will be used to determine when to
043             * register the listener with {@link ThreadEventNotifier}.
044             * <p>
045             * The registration cannot be done from here because it may get lost once the caller method (
046             * {@link ThreadLocal#get()}or {@link ThreadLocal#set(java.lang.Object)} completes, if
047             * this was the first ThreadLocal variable access for the Thread.
048             */
049            protected Object initialValue()
050            {
051                // NOTE: This is a workaround to circumvent the ThreadLocal behavior.
052                // It would be easier if the implementation of ThreadLocal.get() checked for
053                // the existence of the thread local map, after initialValue() is evaluated,
054                // and used it instead of creating a new map always after initialization (possibly
055                // overwriting any variables created from within ThreadLocal.initialValue()).
056    
057                Map map = new HashMap();
058                map.put(INITIALIZED_KEY, Boolean.TRUE);
059    
060                return map;
061            }
062        }
063    
064        /**
065         * Gets the thread local variable and registers the listener with {@link ThreadEventNotifier}
066         * if the thread local variable has been initialized. The registration cannot be done from
067         * within {@link CleanableThreadLocal#initialValue()} because the notifier's thread local
068         * variable will be overwritten and the listeners for the thread will be lost.
069         */
070        private Map getThreadLocalVariable()
071        {
072            Map map = (Map) _local.get();
073    
074            if (Boolean.TRUE.equals(map.get(INITIALIZED_KEY)) && _notifier != null)
075            {
076                _notifier.addThreadCleanupListener(this);
077    
078                map.remove(INITIALIZED_KEY);
079            }
080    
081            return map;
082        }
083    
084        public Object get(String key)
085        {
086            Map map = getThreadLocalVariable();
087    
088            return map.get(key);
089        }
090    
091        public void put(String key, Object value)
092        {
093            Map map = getThreadLocalVariable();
094    
095            map.put(key, value);
096        }
097    
098        public void clear()
099        {
100            Map map = (Map) _local.get();
101    
102            if (map != null)
103                map.clear();
104        }
105    
106        public void setNotifier(ThreadEventNotifier notifier)
107        {
108            _notifier = notifier;
109        }
110    
111        /**
112         * Invokes {@link #clear()}.
113         */
114        public void threadDidCleanup()
115        {
116            clear();
117        }
118    
119    }