View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */ 
16   
17  package org.apache.commons.beanutils;
18  
19  import java.util.Map;
20  import java.util.WeakHashMap;
21  
22  /**
23   * A value that is provided per (thread) context classloader.
24   * Patterned after ThreadLocal. 
25   * There is a separate value used when Thread.getContextClassLoader() is null.  
26   * This mechanism provides isolation for web apps deployed in the same container. 
27   * <strong>Note:</strong> A WeakHashMap bug in several 1.3 JVMs results in a memory leak
28   * for those JVMs.
29   * 
30   * @see java.lang.Thread#getContextClassLoader  
31   * @author Eric Pabst
32   */
33  public class ContextClassLoaderLocal {
34      private Map valueByClassLoader = new WeakHashMap();
35      private boolean globalValueInitialized = false;
36      private Object globalValue;
37  
38      public ContextClassLoaderLocal() {
39          super();
40      }
41  
42      /**
43       * Returns the initial value for this ContextClassLoaderLocal
44       * variable. This method will be called once per Context ClassLoader for
45       * each ContextClassLoaderLocal, the first time it is accessed 
46       * with get or set.  If the programmer desires ContextClassLoaderLocal variables
47       * to be initialized to some value other than null, ContextClassLoaderLocal must
48       * be subclassed, and this method overridden.  Typically, an anonymous
49       * inner class will be used.  Typical implementations of initialValue
50       * will call an appropriate constructor and return the newly constructed
51       * object.
52       *
53       * @return a new Object to be used as an initial value for this ContextClassLoaderLocal
54       */
55      protected Object initialValue() {
56          return null;
57      }
58  
59      /** 
60       * Gets the instance which provides the functionality for {@link BeanUtils}.
61       * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
62       * This mechanism provides isolation for web apps deployed in the same container. 
63       * @return the object currently associated with the 
64       */
65      public synchronized Object get() {
66          // synchronizing the whole method is a bit slower 
67          // but guarentees no subtle threading problems, and there's no 
68          // need to synchronize valueByClassLoader
69          
70          // make sure that the map is given a change to purge itself
71          valueByClassLoader.isEmpty();
72          try {
73              
74              ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
75              if (contextClassLoader != null) {
76                  
77                  Object value = valueByClassLoader.get(contextClassLoader);
78                  if ((value == null) 
79                  && !valueByClassLoader.containsKey(contextClassLoader)) {
80                      value = initialValue();
81                      valueByClassLoader.put(contextClassLoader, value);
82                  }
83                  return value;
84                  
85              }
86              
87          } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
88          
89          // if none or exception, return the globalValue 
90          if (!globalValueInitialized) {
91              globalValue = initialValue();
92              globalValueInitialized = true;
93          }//else already set
94          return globalValue;
95      }
96  
97      /** 
98       * Sets the value - a value is provided per (thread) context classloader.
99       * This mechanism provides isolation for web apps deployed in the same container. 
100      * 
101      * @param value the object to be associated with the entrant thread's context classloader
102      */
103     public synchronized void set(Object value) {
104         // synchronizing the whole method is a bit slower 
105         // but guarentees no subtle threading problems
106         
107         // make sure that the map is given a change to purge itself
108         valueByClassLoader.isEmpty();
109         try {
110             
111             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
112             if (contextClassLoader != null) {
113                 valueByClassLoader.put(contextClassLoader, value);
114                 return;
115             }
116             
117         } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
118         
119         // if in doubt, set the global value
120         globalValue = value;
121         globalValueInitialized = true;
122     }
123     
124     /** 
125      * Unsets the value associated with the current thread's context classloader
126      */
127     public synchronized void unset() {    
128         try {
129         
130             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
131             unset(contextClassLoader);
132             
133         } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
134     }
135     
136     /** 
137      * Unsets the value associated with the given classloader
138      */
139     public synchronized void unset(ClassLoader classLoader) {    
140         valueByClassLoader.remove(classLoader);
141     }    
142 }