View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.util;
20  
21  import java.lang.ref.WeakReference;
22  
23  import java.util.Collection;
24  import java.util.Iterator;
25  
26  import java.util.List;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentMap;
29  import java.util.concurrent.ConcurrentLinkedQueue;
30  
31  /**
32   * Utility functions related to ThreadLocals.
33   * This class provides utilities for managing the lifecycles of ThreadLocals.
34   * In particular, many application servers do not clean up the ThreadLocals
35   * added to the request thread before returning the request thread to
36   * its thread pool.  This may have several severe side-effects, including
37   * <ol>
38   *   <li>Leakage of objects referenced by the thread local</li>
39   *   <li>Possible security holes if the ThreadLocals contents are reused
40   *       on a request for a different user.</li>
41   *   <li>Leakage of the entire class loader in live patching scenarios</li>
42   *   <li>ClassCastExceptions in live patching scenarios</li>
43   * </ol>
44   * To avoid these problems, this class provides utilities for creating
45   * and registering ThreadLocals that will be removed from the thread
46   * automatically at the end of the current request.
47   * @see #newRequestThreadLocal
48   * @see #registerRequestThreadLocal
49   */
50  public final class ThreadLocalUtils
51  {
52    /**
53     * Integration interface implemented by object holding onto ThreadLocals
54     * with a specified lifetime
55     * @see ThreadLocalLifecycle
56     */
57    public static abstract class ThreadLocalManager
58    {
59      /**
60       * Called by the ThreadLocalLifeCycle when the ThreadLocals managed by
61       * this ThreadLocalManager need to be cleaned up by removing their
62       * instances from the current Thread.
63       */
64      public abstract void removeThreadLocals();
65    }
66    
67    /**
68     * Integration interface implemented by an object that cleans up the
69     * ThreadLocals in a ThreadLocalManager with the specified lifetime.
70     * The ThreadLocalLifecycle cleans up the ThreadLocals by calling
71     * <code>removeThreadLocals</code> on the ThreadLocalManager passed
72     * to it in <code>init</code> at the appropriate time.
73     * @see ThreadLocalManager
74     * @see #init
75     */
76    public static abstract class ThreadLocalLifecycle
77    {
78      /**
79       * Called after instantiating the ThreadLocalLifecycle so that the
80       * ThreadLocalLifecycle can clean up the ThreadLocalManager at the
81       * appropriate moment.
82       * @param manager Manager of ThreadLocals that will be cleaned up by
83       * this ThreadLocalLifecycle.
84       * @see ThreadLocalManager#removeThreadLocals
85       */
86      public abstract void init(ThreadLocalManager manager);
87    }
88    
89    // don't allow instantiation 
90    private ThreadLocalUtils()
91    {
92    }
93  
94    /**
95     * Creates and returns a new ThreadLocal that will be automatically removed from
96     * the each request thread when the request finishes.
97     * @return The ThreadLocal
98     * @see #registerRequestThreadLocal
99     */
100   public static <T> ThreadLocal<T> newRequestThreadLocal()
101   {
102     return registerRequestThreadLocal(new ThreadLocal<T>());
103   }
104 
105   /**
106    * Registers and returns the ThreadLocal to be automatically removed from
107    * the each request thread when the request finishes.
108    * @param threadLocal ThreadLocal to register for automatic removal
109    * @return The reigistered ThreadLocal
110    * @see #newRequestThreadLocal
111    */
112   public static <T> ThreadLocal<T> registerRequestThreadLocal(ThreadLocal<T> threadLocal)
113   {
114     return _getThreadLocalManager(_REQUEST_SERVICE).registerThreadLocal(threadLocal);
115   }
116   
117   /**
118    * Returns the ResettableThreadLocalManager for the specified threadLocalGroup name.  If no
119    * ResettableThreadLocalManager is currently registered for the threadLocalGroup, one will
120    * be instantiated, its related ThreadLocalLifecycle instantiated and the ThreadLocalManager
121    * registered with the ThreadLocalLifecycle
122    * @param threadLocalGroup Identifier for this group
123    * @return the ResettableThreadLocalManager for this threadLocalGroup name
124    */
125   private static ResettableThreadLocalManager _getThreadLocalManager(String threadLocalGroup)
126   {
127     // see if we already have a ResettableThreadLocalManager registered
128     ResettableThreadLocalManager threadLocalManager = _threadLocalManagers.get(threadLocalGroup);
129     
130     // if we don't, create one
131     if (threadLocalManager == null)
132     {
133       threadLocalManager = new ResettableThreadLocalManager();
134       
135       // handle simultaneous registration
136       ResettableThreadLocalManager oldThreadLocalManager =
137                             _threadLocalManagers.putIfAbsent(threadLocalGroup, threadLocalManager);
138       
139       if (oldThreadLocalManager != null)
140       {
141         // simultaneous registration, use the one that was registered first
142         threadLocalManager = oldThreadLocalManager;
143       }
144       else
145       {
146         // find the ThreadLocalLifecycle for this threadLocalGroup by looking in
147         // META-INF/Services, instantiate it and register ourselves with it so that we
148         // can be cleaned up
149         List<ThreadLocalLifecycle> lifecycles = ClassLoaderUtils.getServices(threadLocalGroup);
150         
151         if (!lifecycles.isEmpty())
152         {
153           lifecycles.get(0).init(threadLocalManager);
154         }
155       }
156     }
157     
158     return threadLocalManager;
159   }
160   
161   /**
162    * ThreadLocalManager implementation class
163    */
164   private static class ResettableThreadLocalManager extends ThreadLocalManager
165   {
166     public ResettableThreadLocalManager()
167     {
168       // create the list of resettable ThreadLocals for this group
169       _threadLocals = new ConcurrentLinkedQueue<WeakReference<ThreadLocal<?>>>();
170     }
171     
172     /**
173      * Registers the ThreadLocal for cleanup when this ThreadLocalManager is cleaned up
174      * @param threadLocal ThreadLocal to register for clean up
175      * @return The registered ThreadLocal
176      */
177     public <T> ThreadLocal<T> registerThreadLocal(ThreadLocal<T> threadLocal)
178     {
179       if (threadLocal == null)
180         throw new NullPointerException();
181       
182       // make sure we don't pin ThreadLocals, in case they were dynamically created, or
183       // statically created on a class that was then unloaded
184       _threadLocals.add(new WeakReference<ThreadLocal<?>>(threadLocal));
185       
186       return threadLocal;
187     }
188     
189     /**
190      * Called by the ThreadLocalLifecycle to clean up all of the ThreadLocals registered
191      * with this ThreadLocalManager
192      */
193     public void removeThreadLocals()
194     {
195       Iterator<WeakReference<ThreadLocal<?>>> iterator = _threadLocals.iterator();
196       
197       while (iterator.hasNext())
198       {
199         ThreadLocal<?> threadLocal = iterator.next().get();
200         
201         if (threadLocal != null)
202         {
203           // clean up the ThreadLocal
204           threadLocal.remove();
205         }
206         else
207         {
208           // ThreadLocal was gc'ed, so remove the entry
209           iterator.remove();
210         }
211       }
212     }
213     
214     private final Collection<WeakReference<ThreadLocal<?>>> _threadLocals;
215   }
216 
217   // Name used for the request-scoped ThreadLocalManager
218   private static final String _REQUEST_SERVICE = ThreadLocalUtils.class.getName() + 
219                                                                     ".ThreadLocalLifecycle.REQUEST";
220   
221   // threadLocalGroup -> ThreadLocalManager Map
222   private static final ConcurrentMap<String, ResettableThreadLocalManager>_threadLocalManagers
223                      = new ConcurrentHashMap<String, ResettableThreadLocalManager>();
224 }