1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2001 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Axis" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation. For more
  52. * information on the Apache Software Foundation, please see
  53. * <http://www.apache.org/>.
  54. */
  55. package org.apache.commons.discovery.tools;
  56. import java.security.AccessController;
  57. import java.security.PrivilegedAction;
  58. import java.util.Enumeration;
  59. import java.util.HashMap;
  60. import java.util.Hashtable;
  61. import java.util.Map;
  62. import java.util.Properties;
  63. import org.apache.commons.discovery.jdk.JDKHooks;
  64. /**
  65. * <p>This class may disappear in the future, or be moved to another project..
  66. * </p>
  67. *
  68. * <p>Extend the concept of System properties to a hierarchical scheme
  69. * based around class loaders. System properties are global in nature,
  70. * so using them easily violates sound architectural and design principles
  71. * for maintaining separation between components and runtime environments.
  72. * Nevertheless, there is a need for properties broader in scope than
  73. * class or class instance scope.
  74. * </p>
  75. *
  76. * <p>This class is one solution.
  77. * </p>
  78. *
  79. * <p>Manage properties according to a secure
  80. * scheme similar to that used by classloaders:
  81. * <ul>
  82. * <li><code>ClassLoader</code>s are organized in a tree hierarchy.</li>
  83. * <li>each <code>ClassLoader</code> has a reference
  84. * to a parent <code>ClassLoader</code>.</li>
  85. * <li>the root of the tree is the bootstrap <code>ClassLoader</code>er.</li>
  86. * <li>the youngest decendent is the thread context class loader.</li>
  87. * <li>properties are bound to a <code>ClassLoader</code> instance
  88. * <ul>
  89. * <li><i>non-default</i> properties bound to a parent <code>ClassLoader</code>
  90. * instance take precedence over all properties of the same name bound
  91. * to any decendent.
  92. * Just to confuse the issue, this is the default case.</li>
  93. * <li><i>default</i> properties bound to a parent <code>ClassLoader</code>
  94. * instance may be overriden by (default or non-default) properties of
  95. * the same name bound to any decendent.
  96. * </li>
  97. * </ul>
  98. * </li>
  99. * <li>System properties take precedence over all other properties</li>
  100. * </ul>
  101. * </p>
  102. *
  103. * <p>This is not a perfect solution, as it is possible that
  104. * different <code>ClassLoader</code>s load different instances of
  105. * <code>ScopedProperties</code>. The 'higher' this class is loaded
  106. * within the <code>ClassLoader</code> hierarchy, the more usefull
  107. * it will be.
  108. * </p>
  109. *
  110. * @author Richard A. Sitze
  111. */
  112. public class ManagedProperties {
  113. /**
  114. * Cache of Properties, keyed by (thread-context) class loaders.
  115. * Use <code>HashMap</code> because it allows 'null' keys, which
  116. * allows us to account for the (null) bootstrap classloader.
  117. */
  118. private static final HashMap propertiesCache = new HashMap();
  119. /**
  120. * Get value for property bound to the current thread context class loader.
  121. *
  122. * @param property property name.
  123. * @return property value if found, otherwise default.
  124. */
  125. public static String getProperty(String propertyName) {
  126. return getProperty(getThreadContextClassLoader(), propertyName);
  127. }
  128. /**
  129. * Get value for property bound to the current thread context class loader.
  130. * If not found, then return default.
  131. *
  132. * @param property property name.
  133. * @param dephault default value.
  134. * @return property value if found, otherwise default.
  135. */
  136. public static String getProperty(String propertyName, String dephault) {
  137. return getProperty(getThreadContextClassLoader(), propertyName, dephault);
  138. }
  139. /**
  140. * Get value for property bound to the class loader.
  141. *
  142. * @param classLoader
  143. * @param property property name.
  144. * @return property value if found, otherwise default.
  145. */
  146. public static String getProperty(ClassLoader classLoader, String propertyName) {
  147. String value = System.getProperty(propertyName);
  148. if (value == null) {
  149. Value val = getValueProperty(classLoader, propertyName);
  150. if (val != null) {
  151. value = val.value;
  152. }
  153. }
  154. return value;
  155. }
  156. /**
  157. * Get value for property bound to the class loader.
  158. * If not found, then return default.
  159. *
  160. * @param classLoader
  161. * @param property property name.
  162. * @param dephault default value.
  163. * @return property value if found, otherwise default.
  164. */
  165. public static String getProperty(ClassLoader classLoader, String propertyName, String dephault) {
  166. String value = getProperty(classLoader, propertyName);
  167. return (value == null) ? dephault : value;
  168. }
  169. /**
  170. * Set value for property bound to the current thread context class loader.
  171. * @param property property name
  172. * @param value property value (non-default) If null, remove the property.
  173. */
  174. public static void setProperty(String propertyName, String value) {
  175. setProperty(propertyName, value, false);
  176. }
  177. /**
  178. * Set value for property bound to the current thread context class loader.
  179. * @param property property name
  180. * @param value property value. If null, remove the property.
  181. * @param isDefault determines if property is default or not.
  182. * A non-default property cannot be overriden.
  183. * A default property can be overriden by a property
  184. * (default or non-default) of the same name bound to
  185. * a decendent class loader.
  186. */
  187. public static void setProperty(String propertyName, String value, boolean isDefault) {
  188. if (propertyName != null) {
  189. synchronized (propertiesCache) {
  190. ClassLoader classLoader = getThreadContextClassLoader();
  191. HashMap properties = (HashMap)propertiesCache.get(classLoader);
  192. if (value == null) {
  193. properties.remove(propertyName);
  194. } else {
  195. if (properties == null) {
  196. properties = new HashMap();
  197. propertiesCache.put(classLoader, properties);
  198. }
  199. properties.put(propertyName, new Value(value, isDefault));
  200. }
  201. }
  202. }
  203. }
  204. /**
  205. * Set property values for <code>Properties</code> bound to the
  206. * current thread context class loader.
  207. *
  208. * @param newProperties name/value pairs to be bound
  209. */
  210. public static void setProperties(Map newProperties) {
  211. setProperties(newProperties, false);
  212. }
  213. /**
  214. * Set property values for <code>Properties</code> bound to the
  215. * current thread context class loader.
  216. *
  217. * @param newProperties name/value pairs to be bound
  218. * @param isDefault determines if properties are default or not.
  219. * A non-default property cannot be overriden.
  220. * A default property can be overriden by a property
  221. * (default or non-default) of the same name bound to
  222. * a decendent class loader.
  223. */
  224. public static void setProperties(Map newProperties, boolean isDefault) {
  225. java.util.Iterator it = newProperties.entrySet().iterator();
  226. /**
  227. * Each entry must be mapped to a Property.
  228. * 'setProperty' does this for us.
  229. */
  230. while (it.hasNext()) {
  231. Map.Entry entry = (Map.Entry)it.next();
  232. setProperty( String.valueOf(entry.getKey()),
  233. String.valueOf(entry.getValue()),
  234. isDefault);
  235. }
  236. }
  237. /**
  238. * Return list of all property names. This is an expensive
  239. * operation: ON EACH CALL it walks through all property lists
  240. * associated with the current context class loader upto
  241. * and including the bootstrap class loader.
  242. */
  243. public static Enumeration propertyNames() {
  244. Hashtable allProps = new Hashtable();
  245. ClassLoader classLoader = getThreadContextClassLoader();
  246. /**
  247. * Order doesn't matter, we are only going to use
  248. * the set of all keys...
  249. */
  250. while (true) {
  251. HashMap properties = null;
  252. synchronized (propertiesCache) {
  253. properties = (HashMap)propertiesCache.get(classLoader);
  254. }
  255. if (properties != null) {
  256. allProps.putAll(properties);
  257. }
  258. if (classLoader == null) break;
  259. classLoader = getParent(classLoader);
  260. }
  261. return allProps.keys();
  262. }
  263. /**
  264. * This is an expensive operation.
  265. * ON EACH CALL it walks through all property lists
  266. * associated with the current context class loader upto
  267. * and including the bootstrap class loader.
  268. *
  269. * @return Returns a <code>java.util.Properties</code> instance
  270. * that is equivalent to the current state of the scoped
  271. * properties, in that getProperty() will return the same value.
  272. * However, this is a copy, so setProperty on the
  273. * returned value will not effect the scoped properties.
  274. */
  275. public static Properties getProperties() {
  276. Properties p = new Properties();
  277. Enumeration names = propertyNames();
  278. while (names.hasMoreElements()) {
  279. String name = (String)names.nextElement();
  280. p.put(name, getProperty(name));
  281. }
  282. return p;
  283. }
  284. /***************** INTERNAL IMPLEMENTATION *****************/
  285. private static class Value {
  286. final String value;
  287. final boolean isDefault;
  288. Value(String value, boolean isDefault) {
  289. this.value = value;
  290. this.isDefault = isDefault;
  291. }
  292. }
  293. /**
  294. * Get value for properties bound to the class loader.
  295. * Explore up the tree first, as higher-level class
  296. * loaders take precedence over lower-level class loaders.
  297. */
  298. private static final Value getValueProperty(ClassLoader classLoader, String propertyName) {
  299. Value value = null;
  300. if (propertyName != null) {
  301. /**
  302. * If classLoader isn't bootstrap loader (==null),
  303. * then get up-tree value.
  304. */
  305. if (classLoader != null) {
  306. value = getValueProperty(getParent(classLoader), propertyName);
  307. }
  308. if (value == null || value.isDefault) {
  309. synchronized (propertiesCache) {
  310. HashMap properties = (HashMap)propertiesCache.get(classLoader);
  311. if (properties != null) {
  312. Value altValue = (Value)properties.get(propertyName);
  313. // set value only if override exists..
  314. // otherwise pass default (or null) on..
  315. if (altValue != null)
  316. value = altValue;
  317. }
  318. }
  319. }
  320. }
  321. return value;
  322. }
  323. private static final ClassLoader getThreadContextClassLoader() {
  324. return JDKHooks.getJDKHooks().getThreadContextClassLoader();
  325. }
  326. private static final ClassLoader getParent(final ClassLoader classLoader) {
  327. return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction() {
  328. public Object run() {
  329. return classLoader.getParent();
  330. }
  331. });
  332. }
  333. }