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. package org.apache.commons.beanutils;
  17. import java.util.Map;
  18. import java.util.WeakHashMap;
  19. /**
  20. * A value that is provided per (thread) context classloader.
  21. * Patterned after ThreadLocal.
  22. * There is a separate value used when Thread.getContextClassLoader() is null.
  23. * This mechanism provides isolation for web apps deployed in the same container.
  24. * <strong>Note:</strong> A WeakHashMap bug in several 1.3 JVMs results in a memory leak
  25. * for those JVMs.
  26. *
  27. * @see java.lang.Thread#getContextClassLoader
  28. * @author Eric Pabst
  29. */
  30. public class ContextClassLoaderLocal {
  31. private Map valueByClassLoader = new WeakHashMap();
  32. private boolean globalValueInitialized = false;
  33. private Object globalValue;
  34. public ContextClassLoaderLocal() {
  35. super();
  36. }
  37. /**
  38. * Returns the initial value for this ContextClassLoaderLocal
  39. * variable. This method will be called once per Context ClassLoader for
  40. * each ContextClassLoaderLocal, the first time it is accessed
  41. * with get or set. If the programmer desires ContextClassLoaderLocal variables
  42. * to be initialized to some value other than null, ContextClassLoaderLocal must
  43. * be subclassed, and this method overridden. Typically, an anonymous
  44. * inner class will be used. Typical implementations of initialValue
  45. * will call an appropriate constructor and return the newly constructed
  46. * object.
  47. *
  48. * @return a new Object to be used as an initial value for this ContextClassLoaderLocal
  49. */
  50. protected Object initialValue() {
  51. return null;
  52. }
  53. /**
  54. * Gets the instance which provides the functionality for {@link BeanUtils}.
  55. * This is a pseudo-singleton - an single instance is provided per (thread) context classloader.
  56. * This mechanism provides isolation for web apps deployed in the same container.
  57. * @return the object currently associated with the
  58. */
  59. public synchronized Object get() {
  60. // synchronizing the whole method is a bit slower
  61. // but guarentees no subtle threading problems, and there's no
  62. // need to synchronize valueByClassLoader
  63. // make sure that the map is given a change to purge itself
  64. valueByClassLoader.isEmpty();
  65. try {
  66. ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
  67. if (contextClassLoader != null) {
  68. Object value = valueByClassLoader.get(contextClassLoader);
  69. if ((value == null)
  70. && !valueByClassLoader.containsKey(contextClassLoader)) {
  71. value = initialValue();
  72. valueByClassLoader.put(contextClassLoader, value);
  73. }
  74. return value;
  75. }
  76. } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
  77. // if none or exception, return the globalValue
  78. if (!globalValueInitialized) {
  79. globalValue = initialValue();
  80. globalValueInitialized = true;
  81. }//else already set
  82. return globalValue;
  83. }
  84. /**
  85. * Sets the value - a value is provided per (thread) context classloader.
  86. * This mechanism provides isolation for web apps deployed in the same container.
  87. *
  88. * @param value the object to be associated with the entrant thread's context classloader
  89. */
  90. public synchronized void set(Object value) {
  91. // synchronizing the whole method is a bit slower
  92. // but guarentees no subtle threading problems
  93. // make sure that the map is given a change to purge itself
  94. valueByClassLoader.isEmpty();
  95. try {
  96. ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
  97. if (contextClassLoader != null) {
  98. valueByClassLoader.put(contextClassLoader, value);
  99. return;
  100. }
  101. } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
  102. // if in doubt, set the global value
  103. globalValue = value;
  104. globalValueInitialized = true;
  105. }
  106. /**
  107. * Unsets the value associated with the current thread's context classloader
  108. */
  109. public synchronized void unset() {
  110. try {
  111. ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
  112. unset(contextClassLoader);
  113. } catch (SecurityException e) { /* SWALLOW - should we log this? */ }
  114. }
  115. /**
  116. * Unsets the value associated with the given classloader
  117. */
  118. public synchronized void unset(ClassLoader classLoader) {
  119. valueByClassLoader.remove(classLoader);
  120. }
  121. }