1. /*
  2. * $Header$
  3. * $Revision$
  4. * $Date$
  5. *
  6. * ====================================================================
  7. *
  8. * The Apache Software License, Version 1.1
  9. *
  10. * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
  11. * reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * 1. Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * 2. Redistributions in binary form must reproduce the above copyright
  21. * notice, this list of conditions and the following disclaimer in
  22. * the documentation and/or other materials provided with the
  23. * distribution.
  24. *
  25. * 3. The end-user documentation included with the redistribution, if
  26. * any, must include the following acknowlegement:
  27. * "This product includes software developed by the
  28. * Apache Software Foundation (http://www.apache.org/)."
  29. * Alternately, this acknowlegement may appear in the software itself,
  30. * if and wherever such third-party acknowlegements normally appear.
  31. *
  32. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  33. * Foundation" must not be used to endorse or promote products derived
  34. * from this software without prior written permission. For written
  35. * permission, please contact apache@apache.org.
  36. *
  37. * 5. Products derived from this software may not be called "Apache"
  38. * nor may "Apache" appear in their names without prior written
  39. * permission of the Apache Group.
  40. *
  41. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  42. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  43. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  44. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  45. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  46. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  47. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  48. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  49. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  50. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  51. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  52. * SUCH DAMAGE.
  53. * ====================================================================
  54. *
  55. * This software consists of voluntary contributions made by many
  56. * individuals on behalf of the Apache Software Foundation. For more
  57. * information on the Apache Software Foundation, please see
  58. * <http://www.apache.org/>.
  59. *
  60. */
  61. package org.apache.commons.discovery.tools;
  62. import java.util.HashMap;
  63. import java.util.Properties;
  64. import org.apache.commons.discovery.DiscoveryException;
  65. import org.apache.commons.discovery.jdk.JDKHooks;
  66. import org.apache.commons.discovery.resource.ClassLoaders;
  67. /**
  68. * <p>Discover singleton service providers.
  69. * This
  70. * </p>
  71. *
  72. * <p>DiscoverSingleton instances are cached by the Discovery service,
  73. * keyed by a combination of
  74. * <ul>
  75. * <li>thread context class loader,</li>
  76. * <li>groupContext, and</li>
  77. * <li>SPI.</li>
  78. * </ul>
  79. * This DOES allow multiple instances of a given <i>singleton</i> class
  80. * to exist for different class loaders and different group contexts.
  81. * </p>
  82. *
  83. * <p>In the context of this package, a service interface is defined by a
  84. * Service Provider Interface (SPI). The SPI is expressed as a Java interface,
  85. * abstract class, or (base) class that defines an expected programming
  86. * interface.
  87. * </p>
  88. *
  89. * <p>DiscoverSingleton provides the <code>find</code> methods for locating and
  90. * instantiating a singleton instance of an implementation of a service (SPI).
  91. * Each form of <code>find</code> varies slightly, but they all perform the
  92. * same basic function.
  93. *
  94. * The simplest <code>find</code> methods are intended for direct use by
  95. * components looking for a service. If you are not sure which finder(s)
  96. * to use, you can narrow your search to one of these:
  97. * <ul>
  98. * <li>static Object find(Class spi);</li>
  99. * <li>static Object find(Class spi, Properties properties);</li>
  100. * <li>static Object find(Class spi, String defaultImpl);</li>
  101. * <li>static Object find(Class spi,
  102. * Properties properties, String defaultImpl);</li>
  103. * <li>static Object find(Class spi,
  104. * String propertiesFileName, String defaultImpl);</li>
  105. * <li>static Object find(String groupContext, Class spi,
  106. * Properties properties, String defaultImpl);</li>
  107. * <li>static Object find(String groupContext, Class spi,
  108. * String propertiesFileName, String defaultImpl);</li>
  109. * </ul>
  110. *
  111. * The <code>DiscoverSingleton.find</code> methods proceed as follows:
  112. * </p>
  113. * <ul>
  114. * <p><li>
  115. * Examine an internal cache to determine if the desired service was
  116. * previously identified and instantiated. If found in cache, return it.
  117. * </li></p>
  118. * <p><li>
  119. * Get the name of an implementation class. The name is the first
  120. * non-null value obtained from the following resources:
  121. * <ul>
  122. * <li>
  123. * The value of the (scoped) system property whose name is the same as
  124. * the SPI's fully qualified class name (as given by SPI.class.getName()).
  125. * The <code>ScopedProperties</code> class provides a way to bind
  126. * properties by classloader, in a secure hierarchy similar in concept
  127. * to the way classloader find class and resource files.
  128. * See <code>ScopedProperties</code> for more details.
  129. * <p>If the ScopedProperties are not set by users, then behaviour
  130. * is equivalent to <code>System.getProperty()</code>.
  131. * </p>
  132. * </li>
  133. * <p><li>
  134. * The value of a <code>Properties properties</code> property, if provided
  135. * as a parameter, whose name is the same as the SPI's fully qualifed class
  136. * name (as given by SPI.class.getName()).
  137. * </li></p>
  138. * <p><li>
  139. * The value obtained using the JDK1.3+ 'Service Provider' specification
  140. * (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
  141. * service named <code>SPI.class.getName()</code>. This is implemented
  142. * internally, so there is not a dependency on JDK 1.3+.
  143. * </li></p>
  144. * </ul>
  145. * </li></p>
  146. * <p><li>
  147. * If the name of the implementation class is non-null, load that class.
  148. * The class loaded is the first class loaded by the following sequence
  149. * of class loaders:
  150. * <ul>
  151. * <li>Thread Context Class Loader</li>
  152. * <li>DiscoverSingleton's Caller's Class Loader</li>
  153. * <li>SPI's Class Loader</li>
  154. * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
  155. * <li>System Class Loader</li>
  156. * </ul>
  157. * An exception is thrown if the class cannot be loaded.
  158. * </li></p>
  159. * <p><li>
  160. * If the name of the implementation class is null, AND the default
  161. * implementation class (<code>defaultImpl</code>) is null,
  162. * then an exception is thrown.
  163. * </li></p>
  164. * <p><li>
  165. * If the name of the implementation class is null, AND the default
  166. * implementation class (<code>defaultImpl</code>) is non-null,
  167. * then load the default implementation class. The class loaded is the
  168. * first class loaded by the following sequence of class loaders:
  169. * <ul>
  170. * <li>SPI's Class Loader</li>
  171. * <li>DiscoverSingleton's (this class or wrapper) Class Loader</li>
  172. * <li>System Class Loader</li>
  173. * </ul>
  174. * <p>
  175. * This limits the scope in which the default class loader can be found
  176. * to the SPI, DiscoverSingleton, and System class loaders. The assumption
  177. * here is that the default implementation is closely associated with the SPI
  178. * or system, and is not defined in the user's application space.
  179. * </p>
  180. * <p>
  181. * An exception is thrown if the class cannot be loaded.
  182. * </p>
  183. * </li></p>
  184. * <p><li>
  185. * Verify that the loaded class implements the SPI: an exception is thrown
  186. * if the loaded class does not implement the SPI.
  187. * </li></p>
  188. * <p><li>
  189. * Create an instance of the class.
  190. * </li></p>
  191. * </ul>
  192. *
  193. * <p>
  194. * Variances for various forms of the <code>find</code>
  195. * methods are discussed with each such method.
  196. * Variances include the following concepts:
  197. * <ul>
  198. * <li><b>rootFinderClass</b> - a wrapper encapsulating a finder method
  199. * (factory or other helper class). The root finder class is used to
  200. * determine the 'real' caller, and hence the caller's class loader -
  201. * thereby preserving knowledge that is relevant to finding the
  202. * correct/expected implementation class.
  203. * </li>
  204. * <li><b>propertiesFileName</b> - <code>Properties</code> may be specified
  205. * directly, or by property file name. A property file is loaded using the
  206. * same sequence of class loaders used to load the SPI implementation:
  207. * <ul>
  208. * <li>Thread Context Class Loader</li>
  209. * <li>DiscoverSingleton's Caller's Class Loader</li>
  210. * <li>SPI's Class Loader</li>
  211. * <li>DiscoverSingleton's (this class) Class Loader</li>
  212. * <li>System Class Loader</li>
  213. * </ul>
  214. * </li>
  215. * <li><b>groupContext</b> - differentiates service providers for different
  216. * logical groups of service users, that might otherwise be forced to share
  217. * a common service and, more importantly, a common configuration of that
  218. * service.
  219. * <p>The groupContext is used to qualify the name of the property file
  220. * name: <code>groupContext + '.' + propertiesFileName</code>. If that
  221. * file is not found, then the unqualified propertyFileName is used.
  222. * </p>
  223. * <p>In addition, groupContext is used to qualify the name of the system
  224. * property used to find the service implementation by prepending the value
  225. * of <code>groupContext</code> to the property name:
  226. * <code>groupContext> + '.' + SPI.class.getName()</code>.
  227. * Again, if a system property cannot be found by that name, then the
  228. * unqualified property name is used.
  229. * </p>
  230. * </li>
  231. * </ul>
  232. * </p>
  233. *
  234. * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is modelled
  235. * after the SAXParserFactory and DocumentBuilderFactory implementations
  236. * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
  237. * </p>
  238. *
  239. * @author Richard A. Sitze
  240. * @author Craig R. McClanahan
  241. * @author Costin Manolache
  242. * @version $Revision$ $Date$
  243. */
  244. public class DiscoverSingleton {
  245. /********************** (RELATIVELY) SIMPLE FINDERS **********************
  246. *
  247. * These finders are suitable for direct use in components looking for a
  248. * service. If you are not sure which finder(s) to use, you can narrow
  249. * your search to one of these.
  250. */
  251. /**
  252. * Find implementation of SPI.
  253. *
  254. * @param spiClass Service Provider Interface Class.
  255. *
  256. * @return Instance of a class implementing the SPI.
  257. *
  258. * @exception DiscoveryException Thrown if the name of a class implementing
  259. * the SPI cannot be found, if the class cannot be loaded and
  260. * instantiated, or if the resulting class does not implement
  261. * (or extend) the SPI.
  262. */
  263. public static Object find(Class spiClass)
  264. throws DiscoveryException
  265. {
  266. return find(null,
  267. new SPInterface(spiClass),
  268. DiscoverClass.nullProperties,
  269. DiscoverClass.nullDefaultImpl);
  270. }
  271. /**
  272. * Find implementation of SPI.
  273. *
  274. * @param spiClass Service Provider Interface Class.
  275. *
  276. * @param properties Used to determine name of SPI implementation,
  277. * and passed to implementation.init() method if
  278. * implementation implements Service interface.
  279. *
  280. * @return Instance of a class implementing the SPI.
  281. *
  282. * @exception DiscoveryException Thrown if the name of a class implementing
  283. * the SPI cannot be found, if the class cannot be loaded and
  284. * instantiated, or if the resulting class does not implement
  285. * (or extend) the SPI.
  286. */
  287. public static Object find(Class spiClass, Properties properties)
  288. throws DiscoveryException
  289. {
  290. return find(null,
  291. new SPInterface(spiClass),
  292. new PropertiesHolder(properties),
  293. DiscoverClass.nullDefaultImpl);
  294. }
  295. /**
  296. * Find implementation of SPI.
  297. *
  298. * @param spiClass Service Provider Interface Class.
  299. *
  300. * @param defaultImpl Default implementation.
  301. *
  302. * @return Instance of a class implementing the SPI.
  303. *
  304. * @exception DiscoveryException Thrown if the name of a class implementing
  305. * the SPI cannot be found, if the class cannot be loaded and
  306. * instantiated, or if the resulting class does not implement
  307. * (or extend) the SPI.
  308. */
  309. public static Object find(Class spiClass, String defaultImpl)
  310. throws DiscoveryException
  311. {
  312. return find(null,
  313. new SPInterface(spiClass),
  314. DiscoverClass.nullProperties,
  315. new DefaultClassHolder(defaultImpl));
  316. }
  317. /**
  318. * Find implementation of SPI.
  319. *
  320. * @param spiClass Service Provider Interface Class.
  321. *
  322. * @param properties Used to determine name of SPI implementation,
  323. * and passed to implementation.init() method if
  324. * implementation implements Service interface.
  325. *
  326. * @param defaultImpl Default implementation.
  327. *
  328. * @return Instance of a class implementing the SPI.
  329. *
  330. * @exception DiscoveryException Thrown if the name of a class implementing
  331. * the SPI cannot be found, if the class cannot be loaded and
  332. * instantiated, or if the resulting class does not implement
  333. * (or extend) the SPI.
  334. */
  335. public static Object find(Class spiClass,
  336. Properties properties,
  337. String defaultImpl)
  338. throws DiscoveryException
  339. {
  340. return find(null,
  341. new SPInterface(spiClass),
  342. new PropertiesHolder(properties),
  343. new DefaultClassHolder(defaultImpl));
  344. }
  345. /**
  346. * Find implementation of SPI.
  347. *
  348. * @param spiClass Service Provider Interface Class.
  349. *
  350. * @param properties Used to determine name of SPI implementation,
  351. * and passed to implementation.init() method if
  352. * implementation implements Service interface.
  353. *
  354. * @param defaultImpl Default implementation.
  355. *
  356. * @return Instance of a class implementing the SPI.
  357. *
  358. * @exception DiscoveryException Thrown if the name of a class implementing
  359. * the SPI cannot be found, if the class cannot be loaded and
  360. * instantiated, or if the resulting class does not implement
  361. * (or extend) the SPI.
  362. */
  363. public static Object find(Class spiClass,
  364. String propertiesFileName,
  365. String defaultImpl)
  366. throws DiscoveryException
  367. {
  368. return find(null,
  369. new SPInterface(spiClass),
  370. new PropertiesHolder(propertiesFileName),
  371. new DefaultClassHolder(defaultImpl));
  372. }
  373. /*************** FINDERS FOR USE IN FACTORY/HELPER METHODS ***************
  374. */
  375. /**
  376. * Find implementation of SPI.
  377. *
  378. * @param spiClass Service Provider Interface Class.
  379. *
  380. * @param properties Used to determine name of SPI implementation,
  381. * and passed to implementation.init() method if
  382. * implementation implements Service interface.
  383. *
  384. * @param defaultImpl Default implementation.
  385. *
  386. * @return Instance of a class implementing the SPI.
  387. *
  388. * @exception DiscoveryException Thrown if the name of a class implementing
  389. * the SPI cannot be found, if the class cannot be loaded and
  390. * instantiated, or if the resulting class does not implement
  391. * (or extend) the SPI.
  392. */
  393. public static Object find(ClassLoaders loaders,
  394. SPInterface spi,
  395. PropertiesHolder properties,
  396. DefaultClassHolder defaultImpl)
  397. throws DiscoveryException
  398. {
  399. ClassLoader contextLoader = JDKHooks.getJDKHooks().getThreadContextClassLoader();
  400. Object obj = get(contextLoader, spi.getSPName());
  401. if (obj == null) {
  402. try {
  403. obj = DiscoverClass.newInstance(loaders, spi, properties, defaultImpl);
  404. if (obj != null) {
  405. put(contextLoader, spi.getSPName(), obj);
  406. }
  407. } catch (DiscoveryException de) {
  408. throw de;
  409. } catch (Exception e) {
  410. throw new DiscoveryException("Unable to instantiate implementation class for " + spi.getSPName(), e);
  411. }
  412. }
  413. return obj;
  414. }
  415. /********************** CACHE-MANAGEMENT SUPPORT **********************/
  416. /**
  417. * Release all internal references to previously created service
  418. * instances associated with the current thread context class loader.
  419. * The <code>release()</code> method is called for service instances that
  420. * implement the <code>Service</code> interface.
  421. *
  422. * This is useful in environments like servlet containers,
  423. * which implement application reloading by throwing away a ClassLoader.
  424. * Dangling references to objects in that class loader would prevent
  425. * garbage collection.
  426. */
  427. public static synchronized void release() {
  428. EnvironmentCache.release();
  429. }
  430. /**
  431. * Release any internal references to a previously created service
  432. * instance associated with the current thread context class loader.
  433. * If the SPI instance implements <code>Service</code>, then call
  434. * <code>release()</code>.
  435. */
  436. public static synchronized void release(Class spiClass) {
  437. HashMap spis = (HashMap)EnvironmentCache.get(JDKHooks.getJDKHooks().getThreadContextClassLoader());
  438. if (spis != null) {
  439. spis.remove(spiClass.getName());
  440. }
  441. }
  442. /************************* SPI CACHE SUPPORT *************************
  443. *
  444. * Cache services by a 'key' unique to the requesting class/environment:
  445. *
  446. * When we 'release', it is expected that the caller of the 'release'
  447. * have the same thread context class loader... as that will be used
  448. * to identify all cached entries to be released.
  449. *
  450. * We will manage synchronization directly, so all caches are implemented
  451. * as HashMap (unsynchronized).
  452. *
  453. * - ClassLoader::groupContext::SPI::Instance Cache
  454. * Cache : HashMap
  455. * Key : Thread Context Class Loader (<code>ClassLoader</code>).
  456. * Value : groupContext::SPI Cache (<code>HashMap</code>).
  457. *
  458. * - groupContext::SPI::Instance Cache
  459. * Cache : HashMap
  460. * Key : groupContext (<code>String</code>).
  461. * Value : SPI Cache (<code>HashMap</code>).
  462. *
  463. * - SPI::Instance Cache
  464. * Cache : HashMap
  465. * Key : SPI Class Name (<code>String</code>).
  466. * Value : SPI Instance/Implementation (<code>Object</code>.
  467. */
  468. /**
  469. * Implements first two levels of the cache (loader & groupContext).
  470. * Allows null keys, important as default groupContext is null.
  471. */
  472. // FIXME: Why is this here? All the methods used are static.
  473. //private static final EnvironmentCache root_cache = new EnvironmentCache();
  474. /**
  475. * Get service keyed by spi & classLoader.
  476. */
  477. private static synchronized Object get(ClassLoader classLoader,
  478. String spiName)
  479. {
  480. HashMap spis = (HashMap)EnvironmentCache.get(classLoader);
  481. return (spis != null)
  482. ? spis.get(spiName)
  483. : null;
  484. }
  485. /**
  486. * Put service keyed by spi & classLoader.
  487. */
  488. private static synchronized void put(ClassLoader classLoader,
  489. String spiName,
  490. Object service)
  491. {
  492. if (service != null)
  493. {
  494. HashMap spis = (HashMap)EnvironmentCache.get(classLoader);
  495. if (spis == null) {
  496. spis = new HashMap(EnvironmentCache.smallHashSize);
  497. EnvironmentCache.put(classLoader, spis);
  498. }
  499. spis.put(spiName, service);
  500. }
  501. }
  502. }