001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import java.util.HashMap;
018    import java.util.Map;
019    import java.util.concurrent.atomic.AtomicInteger;
020    
021    /**
022     * @author Shuyang Zhou
023     */
024    public class CentralizedThreadLocal<T> extends ThreadLocal<T> {
025    
026            public static void clearLongLivedThreadLocals() {
027                    _longLivedThreadLocals.remove();
028            }
029    
030            public static void clearShortLivedThreadLocals() {
031                    _shortLivedThreadLocals.remove();
032            }
033    
034            public static Map<CentralizedThreadLocal<?>, Object>
035                    getLongLivedThreadLocals() {
036    
037                    return _toMap(_longLivedThreadLocals.get());
038            }
039    
040            public static Map<CentralizedThreadLocal<?>, Object>
041                    getShortLivedThreadLocals() {
042    
043                    return _toMap(_shortLivedThreadLocals.get());
044            }
045    
046            public static void setThreadLocals(
047                    Map<CentralizedThreadLocal<?>, Object> longLivedThreadLocals,
048                    Map<CentralizedThreadLocal<?>, Object> shortLivedThreadLocals) {
049    
050                    ThreadLocalMap threadLocalMap = _longLivedThreadLocals.get();
051    
052                    for (Map.Entry<CentralizedThreadLocal<?>, Object> entry :
053                                    longLivedThreadLocals.entrySet()) {
054    
055                            threadLocalMap.putEntry(entry.getKey(), entry.getValue());
056                    }
057    
058                    threadLocalMap = _shortLivedThreadLocals.get();
059    
060                    for (Map.Entry<CentralizedThreadLocal<?>, Object> entry :
061                                    shortLivedThreadLocals.entrySet()) {
062    
063                            threadLocalMap.putEntry(entry.getKey(), entry.getValue());
064                    }
065            }
066    
067            public CentralizedThreadLocal(boolean shortLived) {
068                    _shortLived = shortLived;
069    
070                    if (shortLived) {
071                            _hashCode = _shortLivedNextHasCode.getAndAdd(_HASH_INCREMENT);
072                    }
073                    else {
074                            _hashCode = _longLivedNextHasCode.getAndAdd(_HASH_INCREMENT);
075                    }
076            }
077    
078            @Override
079            public T get() {
080                    ThreadLocalMap threadLocalMap = _getThreadLocalMap();
081    
082                    Entry entry = threadLocalMap.getEntry(this);
083    
084                    if (entry == null) {
085                            T value = initialValue();
086    
087                            threadLocalMap.putEntry(this, value);
088    
089                            return value;
090                    }
091                    else {
092                            return (T)entry._value;
093                    }
094            }
095    
096            @Override
097            public int hashCode() {
098                    return _hashCode;
099            }
100    
101            @Override
102            public void remove() {
103                    ThreadLocalMap threadLocalMap = _getThreadLocalMap();
104    
105                    threadLocalMap.removeEntry(this);
106            }
107    
108            @Override
109            public void set(T value) {
110                    ThreadLocalMap threadLocalMap = _getThreadLocalMap();
111    
112                    threadLocalMap.putEntry(this, value);
113            }
114    
115            private static Map<CentralizedThreadLocal<?>, Object> _toMap(
116                    ThreadLocalMap threadLocalMap) {
117    
118                    Map<CentralizedThreadLocal<?>, Object> map =
119                            new HashMap<CentralizedThreadLocal<?>, Object>(
120                                    threadLocalMap._table.length);
121    
122                    for (Entry entry : threadLocalMap._table) {
123                            map.put(entry._key, entry._value);
124                    }
125    
126                    return map;
127            }
128    
129            private ThreadLocalMap _getThreadLocalMap() {
130                    if (_shortLived) {
131                            return _shortLivedThreadLocals.get();
132                    }
133                    else {
134                            return _longLivedThreadLocals.get();
135                    }
136            }
137    
138            private static final int _HASH_INCREMENT = 0x61c88647;
139    
140            private static final AtomicInteger _longLivedNextHasCode =
141                    new AtomicInteger();
142            private static final ThreadLocal<ThreadLocalMap> _longLivedThreadLocals =
143                    new ThreadLocalMapThreadLocal();
144            private static final AtomicInteger _shortLivedNextHasCode =
145                    new AtomicInteger();
146            private static final ThreadLocal<ThreadLocalMap> _shortLivedThreadLocals =
147                    new ThreadLocalMapThreadLocal();
148    
149            private final int _hashCode;
150            private final boolean _shortLived;
151    
152            private static class Entry {
153    
154                    public Entry(CentralizedThreadLocal<?> key, Object value, Entry next) {
155                            _key = key;
156                            _value = value;
157                            _next = next;
158                    }
159    
160                    private CentralizedThreadLocal<?> _key;
161                    private Entry _next;
162                    private Object _value;
163    
164            }
165    
166            private static class ThreadLocalMap {
167    
168                    public void expand(int newCapacity) {
169                            if (_table.length == _MAXIMUM_CAPACITY) {
170                                    _threshold = Integer.MAX_VALUE;
171    
172                                    return;
173                            }
174    
175                            Entry[] newTable = new Entry[newCapacity];
176    
177                            for (int i = 0; i < _table.length; i++) {
178                                    Entry entry = _table[i];
179    
180                                    if (entry == null) {
181                                            continue;
182                                    }
183    
184                                    _table[i] = null;
185    
186                                    do {
187                                            Entry nextEntry = entry._next;
188    
189                                            int index = entry._key._hashCode & (newCapacity - 1);
190    
191                                            entry._next = newTable[index];
192    
193                                            newTable[index] = entry;
194    
195                                            entry = nextEntry;
196                                    }
197                                    while (entry != null);
198                            }
199    
200                            _table = newTable;
201    
202                            _threshold = newCapacity * 2 / 3;
203                    }
204    
205                    public Entry getEntry(CentralizedThreadLocal<?> key) {
206                            int index = key._hashCode & (_table.length - 1);
207    
208                            Entry entry = _table[index];
209    
210                            if (entry == null) {
211                                    return null;
212                            }
213                            else if (entry._key == key) {
214                                    return entry;
215                            }
216                            else {
217                                    while ((entry = entry._next) != null) {
218                                            if (entry._key == key) {
219                                                    return entry;
220                                            }
221                                    }
222    
223                                    return null;
224                            }
225                    }
226    
227                    public void putEntry(CentralizedThreadLocal<?> key, Object value) {
228                            int index = key._hashCode & (_table.length - 1);
229    
230                            for (Entry entry = _table[index]; entry != null;
231                                    entry = entry._next) {
232    
233                                    if (entry._key == key) {
234                                            entry._value = value;
235    
236                                            return;
237                                    }
238                            }
239    
240                            _table[index] = new Entry(key, value, _table[index]);
241    
242                            if (_size++ >= _threshold) {
243                                    expand(2 * _table.length);
244                            }
245                    }
246    
247                    public void removeEntry(CentralizedThreadLocal<?> key) {
248                            int index = key._hashCode & (_table.length - 1);
249    
250                            Entry previousEntry = null;
251    
252                            Entry entry = _table[index];
253    
254                            while (entry != null) {
255                                    Entry nextEntry = entry._next;
256    
257                                    if (entry._key == key) {
258                                            _size--;
259    
260                                            if (previousEntry == null) {
261                                                    _table[index] = nextEntry;
262                                            }
263                                            else {
264                                                    previousEntry._next = nextEntry;
265                                            }
266    
267                                            return;
268                                    }
269    
270                                    previousEntry = entry;
271                                    entry = nextEntry;
272                            }
273                    };
274    
275                    private static final int _INITIAL_CAPACITY = 16;
276    
277                    private static final int _MAXIMUM_CAPACITY = 1 << 30;
278    
279                    private int _size;
280                    private Entry[] _table = new Entry[_INITIAL_CAPACITY];
281                    private int _threshold = _INITIAL_CAPACITY * 2 / 3;
282    
283            }
284    
285            private static class ThreadLocalMapThreadLocal
286                    extends ThreadLocal<ThreadLocalMap> {
287    
288                    @Override
289                    protected ThreadLocalMap initialValue() {
290                            return new ThreadLocalMap();
291                    }
292    
293            }
294    
295    }