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 }