1. /*
  2. * @(#)ServiceRegistry.java 1.22 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.imageio.spi;
  8. import java.io.File;
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.Iterator;
  12. import java.util.List;
  13. import java.util.Map;
  14. import java.util.NoSuchElementException;
  15. import java.util.Set;
  16. import sun.misc.Service;
  17. /**
  18. * A registry for service provider instances.
  19. *
  20. * <p> A <i>service</i> is a well-known set of interfaces and (usually
  21. * abstract) classes. A <i>service provider</i> is a specific
  22. * implementation of a service. The classes in a provider typically
  23. * implement the interface or subclass the class defined by the
  24. * service itself.
  25. *
  26. * <p> Service providers are stored in one or more <i>categories</i>,
  27. * each of which is defined by a class of interface (described by a
  28. * <code>Class</code> object) that all of its members must implement.
  29. * The set of categories may be changed dynamically.
  30. *
  31. * <p> Only a single instance of a given leaf class (that is, the
  32. * actual class returned by <code>getClass()</code>, as opposed to any
  33. * inherited classes or interfaces) may be registered. That is,
  34. * suppose that the
  35. * <code>com.mycompany.mypkg.GreenServiceProvider</code> class
  36. * implements the <code>com.mycompany.mypkg.MyService</code>
  37. * interface. If a <code>GreenServiceProvider</code> instance is
  38. * registered, it will be stored in the category defined by the
  39. * <code>MyService</code> class. If a new instance of
  40. * <code>GreenServiceProvider</code> is registered, it will replace
  41. * the previous instance. In practice, service provider objects are
  42. * usually singletons so this behavior is appropriate.
  43. *
  44. * <p> To declare a service provider, a <code>services</code>
  45. * subdirectory is placed within the <code>META-INF</code> directory
  46. * that is present in every JAR file. This directory contains a file
  47. * for each service provider interface that has one or more
  48. * implementation classes present in the JAR file. For example, if
  49. * the JAR file contained a class named
  50. * <code>com.mycompany.mypkg.MyServiceImpl</code> which implements the
  51. * <code>javax.someapi.SomeService</code> interface, the JAR file
  52. * would contain a file named: <pre>
  53. * META-INF/services/javax.someapi.SomeService </pre>
  54. *
  55. * containing the line:
  56. *
  57. * <pre>
  58. * com.mycompany.mypkg.MyService
  59. * </pre>
  60. *
  61. * <p> The service provider classes should be to be lightweight and
  62. * quick to load. Implementations of these interfaces should avoid
  63. * complex dependencies on other classes and on native code. The usual
  64. * pattern for more complex services is to register a lightweight
  65. * proxy for the heavyweight service.
  66. *
  67. * <p> An application may customize the contents of a registry as it
  68. * sees fit, so long as it has the appropriate runtime permission.
  69. *
  70. * <p> For more details on declaring service providers, and the JAR
  71. * format in general, see the <a
  72. * href="http://java.sun.com/products/jdk/1.3/docs/guide/jar/jar.html">
  73. * JAR File Specification</a>.
  74. *
  75. * @see RegisterableService
  76. *
  77. * @version 0.5
  78. */
  79. public class ServiceRegistry {
  80. // Class -> Registry
  81. private Map categoryMap = new HashMap();
  82. /**
  83. * Constructs a <code>ServiceRegistry</code> instance with a
  84. * set of categories taken from the <code>categories</code>
  85. * argument.
  86. *
  87. * @param categories an <code>Iterator</code> containing
  88. * <code>Class</code> objects to be used to define categories.
  89. *
  90. * @exception IllegalArgumentException if
  91. * <code>categories</code> is <code>null</code>.
  92. */
  93. public ServiceRegistry(Iterator<Class<?>> categories) {
  94. if (categories == null) {
  95. throw new IllegalArgumentException("categories == null!");
  96. }
  97. while (categories.hasNext()) {
  98. Class category = (Class)categories.next();
  99. SubRegistry reg = new SubRegistry(this, category);
  100. categoryMap.put(category, reg);
  101. }
  102. }
  103. // The following two methods expose functionality from
  104. // sun.misc.Service. If that class is made public, they may be
  105. // removed.
  106. //
  107. // The sun.misc.ServiceConfigurationError class may also be
  108. // exposed, in which case the references to 'an
  109. // <code>Error</code>' below should be changed to 'a
  110. // <code>ServiceConfigurationError</code>'.
  111. /**
  112. * Searches for implementations of a particular service class
  113. * using the given class loader.
  114. *
  115. * <p> This method transforms the name of the given service class
  116. * into a provider-configuration filename as described in the
  117. * class comment and then uses the <code>getResources</code>
  118. * method of the given class loader to find all available files
  119. * with that name. These files are then read and parsed to
  120. * produce a list of provider-class names. The iterator that is
  121. * returned uses the given class loader to look up and then
  122. * instantiate each element of the list.
  123. *
  124. * <p> Because it is possible for extensions to be installed into
  125. * a running Java virtual machine, this method may return
  126. * different results each time it is invoked.
  127. *
  128. * @param providerClass a <code>Class</code>object indicating the
  129. * class or interface of the service providers being detected.
  130. *
  131. * @param loader the class loader to be used to load
  132. * provider-configuration files and instantiate provider classes,
  133. * or <code>null</code> if the system class loader (or, failing that
  134. * the bootstrap class loader) is to be used.
  135. *
  136. * @return An <code>Iterator</code> that yields provider objects
  137. * for the given service, in some arbitrary order. The iterator
  138. * will throw an <code>Error</code> if a provider-configuration
  139. * file violates the specified format or if a provider class
  140. * cannot be found and instantiated.
  141. *
  142. * @exception IllegalArgumentException if
  143. * <code>providerClass</code> is <code>null</code>.
  144. */
  145. public static <T> Iterator<T> lookupProviders(Class<T> providerClass,
  146. ClassLoader loader)
  147. {
  148. if (providerClass == null) {
  149. throw new IllegalArgumentException("providerClass == null!");
  150. }
  151. return Service.providers(providerClass, loader);
  152. }
  153. /**
  154. * Locates and incrementally instantiates the available providers
  155. * of a given service using the context class loader. This
  156. * convenience method is equivalent to:
  157. *
  158. * <pre>
  159. * ClassLoader cl = Thread.currentThread().getContextClassLoader();
  160. * return Service.providers(service, cl);
  161. * </pre>
  162. *
  163. * @param providerClass a <code>Class</code>object indicating the
  164. * class or interface of the service providers being detected.
  165. *
  166. * @return An <code>Iterator</code> that yields provider objects
  167. * for the given service, in some arbitrary order. The iterator
  168. * will throw an <code>Error</code> if a provider-configuration
  169. * file violates the specified format or if a provider class
  170. * cannot be found and instantiated.
  171. *
  172. * @exception IllegalArgumentException if
  173. * <code>providerClass</code> is <code>null</code>.
  174. */
  175. public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
  176. if (providerClass == null) {
  177. throw new IllegalArgumentException("providerClass == null!");
  178. }
  179. return Service.providers(providerClass);
  180. }
  181. /**
  182. * Returns an <code>Iterator</code> of <code>Class</code> objects
  183. * indicating the current set of categories. The iterator will be
  184. * empty if no categories exist.
  185. *
  186. * @return an <code>Iterator</code> containing
  187. * <code>Class</code>objects.
  188. */
  189. public Iterator<Class<?>> getCategories() {
  190. Set keySet = categoryMap.keySet();
  191. return keySet.iterator();
  192. }
  193. /**
  194. * Returns an Iterator containing the subregistries to which the
  195. * provider belongs.
  196. */
  197. private Iterator getSubRegistries(Object provider) {
  198. List l = new ArrayList();
  199. Iterator iter = categoryMap.keySet().iterator();
  200. while (iter.hasNext()) {
  201. Class c = (Class)iter.next();
  202. if (c.isAssignableFrom(provider.getClass())) {
  203. l.add((SubRegistry)categoryMap.get(c));
  204. }
  205. }
  206. return l.iterator();
  207. }
  208. /**
  209. * Adds a service provider object to the registry. The provider
  210. * is associated with the given category.
  211. *
  212. * <p> If <code>provider</code> implements the
  213. * <code>RegisterableService</code> interface, its
  214. * <code>onRegistration</code> method will be called. Its
  215. * <code>onDeregistration</code> method will be called each time
  216. * it is deregistered from a category, for example if a
  217. * category is removed or the registry is garbage collected.
  218. *
  219. * @param provider the service provide object to be registered.
  220. * @param category the category under which to register the
  221. * provider.
  222. *
  223. * @return true if no provider of the same class was previously
  224. * registered in the same category category.
  225. *
  226. * @exception IllegalArgumentException if <code>provider</code> is
  227. * <code>null</code>.
  228. * @exception IllegalArgumentException if there is no category
  229. * corresponding to <code>category</code>.
  230. * @exception ClassCastException if provider does not implement
  231. * the <code>Class</code> defined by <code>category</code>.
  232. */
  233. public <T> boolean registerServiceProvider(T provider,
  234. Class<T> category) {
  235. if (provider == null) {
  236. throw new IllegalArgumentException("provider == null!");
  237. }
  238. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  239. if (reg == null) {
  240. throw new IllegalArgumentException("category unknown!");
  241. }
  242. if (!category.isAssignableFrom(provider.getClass())) {
  243. throw new ClassCastException();
  244. }
  245. return reg.registerServiceProvider(provider);
  246. }
  247. /**
  248. * Adds a service provider object to the registry. The provider
  249. * is associated within each category present in the registry
  250. * whose <code>Class</code> it implements.
  251. *
  252. * <p> If <code>provider</code> implements the
  253. * <code>RegisterableService</code> interface, its
  254. * <code>onRegistration</code> method will be called once for each
  255. * category it is registered under. Its
  256. * <code>onDeregistration</code> method will be called each time
  257. * it is deregistered from a category or when the registry is
  258. * finalized.
  259. *
  260. * @param provider the service provider object to be registered.
  261. *
  262. * @exception IllegalArgumentException if
  263. * <code>provider</code> is <code>null</code>.
  264. */
  265. public void registerServiceProvider(Object provider) {
  266. if (provider == null) {
  267. throw new IllegalArgumentException("provider == null!");
  268. }
  269. Iterator regs = getSubRegistries(provider);
  270. while (regs.hasNext()) {
  271. SubRegistry reg = (SubRegistry)regs.next();
  272. reg.registerServiceProvider(provider);
  273. }
  274. }
  275. /**
  276. * Adds a set of service provider objects, taken from an
  277. * <code>Iterator</code> to the registry. Each provider is
  278. * associated within each category present in the registry whose
  279. * <code>Class</code> it implements.
  280. *
  281. * <p> For each entry of <code>providers</code> that implements
  282. * the <code>RegisterableService</code> interface, its
  283. * <code>onRegistration</code> method will be called once for each
  284. * category it is registered under. Its
  285. * <code>onDeregistration</code> method will be called each time
  286. * it is deregistered from a category or when the registry is
  287. * finalized.
  288. *
  289. * @param providers an Iterator containing service provider
  290. * objects to be registered.
  291. *
  292. * @exception IllegalArgumentException if <code>providers</code>
  293. * is <code>null</code> or contains a <code>null</code> entry.
  294. */
  295. public void registerServiceProviders(Iterator<?> providers) {
  296. if (providers == null) {
  297. throw new IllegalArgumentException("provider == null!");
  298. }
  299. while (providers.hasNext()) {
  300. registerServiceProvider(providers.next());
  301. }
  302. }
  303. /**
  304. * Removes a service provider object from the given category. If
  305. * the provider was not previously registered, nothing happens and
  306. * <code>false</code> is returned. Otherwise, <code>true</code>
  307. * is returned. If an object of the same class as
  308. * <code>provider</code> but not equal (using <code>==</code>) to
  309. * <code>provider</code> is registered, it will not be
  310. * deregistered.
  311. *
  312. * <p> If <code>provider</code> implements the
  313. * <code>RegisterableService</code> interface, its
  314. * <code>onDeregistration</code> method will be called.
  315. *
  316. * @param provider the service provider object to be deregistered.
  317. * @param category the category from which to deregister the
  318. * provider.
  319. *
  320. * @return <code>true</code> if the provider was previously
  321. * registered in the same category category,
  322. * <code>false</code> otherwise.
  323. *
  324. * @exception IllegalArgumentException if <code>provider</code> is
  325. * <code>null</code>.
  326. * @exception IllegalArgumentException if there is no category
  327. * corresponding to <code>category</code>.
  328. * @exception ClassCastException if provider does not implement
  329. * the class defined by <code>category</code>.
  330. */
  331. public <T> boolean deregisterServiceProvider(T provider,
  332. Class<T> category) {
  333. if (provider == null) {
  334. throw new IllegalArgumentException("provider == null!");
  335. }
  336. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  337. if (reg == null) {
  338. throw new IllegalArgumentException("category unknown!");
  339. }
  340. if (!category.isAssignableFrom(provider.getClass())) {
  341. throw new ClassCastException();
  342. }
  343. return reg.deregisterServiceProvider(provider);
  344. }
  345. /**
  346. * Removes a service provider object from all categories that
  347. * contain it.
  348. *
  349. * @param provider the service provider object to be deregistered.
  350. *
  351. * @exception IllegalArgumentException if <code>provider</code> is
  352. * <code>null</code>.
  353. */
  354. public void deregisterServiceProvider(Object provider) {
  355. if (provider == null) {
  356. throw new IllegalArgumentException("provider == null!");
  357. }
  358. Iterator regs = getSubRegistries(provider);
  359. while (regs.hasNext()) {
  360. SubRegistry reg = (SubRegistry)regs.next();
  361. reg.deregisterServiceProvider(provider);
  362. }
  363. }
  364. /**
  365. * Returns <code>true</code> if <code>provider</code> is currently
  366. * registered.
  367. *
  368. * @param provider the service provider object to be queried.
  369. *
  370. * @return <code>true</code> if the given provider has been
  371. * registered.
  372. *
  373. * @exception IllegalArgumentException if <code>provider</code> is
  374. * <code>null</code>.
  375. */
  376. public boolean contains(Object provider) {
  377. if (provider == null) {
  378. throw new IllegalArgumentException("provider == null!");
  379. }
  380. Iterator regs = getSubRegistries(provider);
  381. while (regs.hasNext()) {
  382. SubRegistry reg = (SubRegistry)regs.next();
  383. if (reg.contains(provider)) {
  384. return true;
  385. }
  386. }
  387. return false;
  388. }
  389. /**
  390. * Returns an <code>Iterator</code> containing all registered
  391. * service providers in the given category. If
  392. * <code>useOrdering</code> is <code>false</code>, the iterator
  393. * will return all of the server provider objects in an arbitrary
  394. * order. Otherwise, the ordering will respect any pairwise
  395. * orderings that have been set. If the graph of pairwise
  396. * orderings contains cycles, any providers that belong to a cycle
  397. * will not be returned.
  398. *
  399. * @param category the category to be retrieved from.
  400. * @param useOrdering <code>true</code> if pairwise orderings
  401. * should be taken account in ordering the returned objects.
  402. *
  403. * @return an <code>Iterator</code> containing service provider
  404. * objects from the given category, possibly in order.
  405. *
  406. * @exception IllegalArgumentException if there is no category
  407. * corresponding to <code>category</code>.
  408. */
  409. public <T> Iterator<T> getServiceProviders(Class<T> category,
  410. boolean useOrdering) {
  411. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  412. if (reg == null) {
  413. throw new IllegalArgumentException("category unknown!");
  414. }
  415. return reg.getServiceProviders(useOrdering);
  416. }
  417. /**
  418. * A simple filter interface used by
  419. * <code>ServiceRegistry.getServiceProviders</code> to select
  420. * providers matching an arbitrary criterion. Classes that
  421. * implement this interface should be defined in order to make use
  422. * of the <code>getServiceProviders</code> method of
  423. * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
  424. *
  425. * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
  426. */
  427. public interface Filter {
  428. /**
  429. * Returns <code>true</code> if the given
  430. * <code>provider</code> object matches the criterion defined
  431. * by this <code>Filter</code>.
  432. *
  433. * @param provider a service provider <code>Object</code>.
  434. *
  435. * @return true if the provider matches the criterion.
  436. */
  437. boolean filter(Object provider);
  438. }
  439. /**
  440. * Returns an <code>Iterator</code> containing service provider
  441. * objects within a given category that satisfy a criterion
  442. * imposed by the supplied <code>ServiceRegistry.Filter</code>
  443. * object's <code>filter</code> method.
  444. *
  445. * <p> The <code>useOrdering</code> argument controls the
  446. * ordering of the results using the same rules as
  447. * <code>getServiceProviders(Class, boolean)</code>.
  448. *
  449. * @param category the category to be retrieved from.
  450. * @param filter an instance of <code>ServiceRegistry.Filter</code>
  451. * whose <code>filter</code> method will be invoked.
  452. * @param useOrdering <code>true</code> if pairwise orderings
  453. * should be taken account in ordering the returned objects.
  454. *
  455. * @return an <code>Iterator</code> containing service provider
  456. * objects from the given category, possibly in order.
  457. *
  458. * @exception IllegalArgumentException if there is no category
  459. * corresponding to <code>category</code>.
  460. */
  461. public <T> Iterator<T> getServiceProviders(Class<T> category,
  462. Filter filter,
  463. boolean useOrdering) {
  464. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  465. if (reg == null) {
  466. throw new IllegalArgumentException("category unknown!");
  467. }
  468. Iterator iter = getServiceProviders(category, useOrdering);
  469. return new FilterIterator(iter, filter);
  470. }
  471. /**
  472. * Returns the currently registered service provider object that
  473. * is of the given class type. At most one object of a given
  474. * class is allowed to be registered at any given time. If no
  475. * registered object has the desired class type, <code>null</code>
  476. * is returned.
  477. *
  478. * @param providerClass the <code>Class</code> of the desired
  479. * service provider object.
  480. *
  481. * @return a currently registered service provider object with the
  482. * desired <code>Class</code>type, or <code>null</code> is none is
  483. * present.
  484. *
  485. * @exception IllegalArgumentException if <code>providerClass</code> is
  486. * <code>null</code>.
  487. */
  488. public <T> T getServiceProviderByClass(Class<T> providerClass) {
  489. if (providerClass == null) {
  490. throw new IllegalArgumentException("providerClass == null!");
  491. }
  492. Iterator iter = categoryMap.keySet().iterator();
  493. while (iter.hasNext()) {
  494. Class c = (Class)iter.next();
  495. if (c.isAssignableFrom(providerClass)) {
  496. SubRegistry reg = (SubRegistry)categoryMap.get(c);
  497. T provider = reg.getServiceProviderByClass(providerClass);
  498. if (provider != null) {
  499. return provider;
  500. }
  501. }
  502. }
  503. return null;
  504. }
  505. /**
  506. * Sets a pairwise ordering between two service provider objects
  507. * within a given category. If one or both objects are not
  508. * currently registered within the given category, or if the
  509. * desired ordering is already set, nothing happens and
  510. * <code>false</code> is returned. If the providers previously
  511. * were ordered in the reverse direction, that ordering is
  512. * removed.
  513. *
  514. * <p> The ordering will be used by the
  515. * <code>getServiceProviders</code> methods when their
  516. * <code>useOrdering</code> argument is <code>true</code>.
  517. *
  518. * @param category a <code>Class</code> object indicating the
  519. * category under which the preference is to be established.
  520. * @param firstProvider the preferred provider.
  521. * @param secondProvider the provider to which
  522. * <code>firstProvider</code> is preferred.
  523. *
  524. * @return <code>true</code> if a previously unset ordering
  525. * was established.
  526. *
  527. * @exception IllegalArgumentException if either provider is
  528. * <code>null</code> or they are the same object.
  529. * @exception IllegalArgumentException if there is no category
  530. * corresponding to <code>category</code>.
  531. */
  532. public <T> boolean setOrdering(Class<T> category,
  533. T firstProvider,
  534. T secondProvider) {
  535. if (firstProvider == null || secondProvider == null) {
  536. throw new IllegalArgumentException("provider is null!");
  537. }
  538. if (firstProvider == secondProvider) {
  539. throw new IllegalArgumentException("providers are the same!");
  540. }
  541. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  542. if (reg == null) {
  543. throw new IllegalArgumentException("category unknown!");
  544. }
  545. if (reg.contains(firstProvider) &&
  546. reg.contains(secondProvider)) {
  547. return reg.setOrdering(firstProvider, secondProvider);
  548. }
  549. return false;
  550. }
  551. /**
  552. * Sets a pairwise ordering between two service provider objects
  553. * within a given category. If one or both objects are not
  554. * currently registered within the given category, or if no
  555. * ordering is currently set between them, nothing happens
  556. * and <code>false</code> is returned.
  557. *
  558. * <p> The ordering will be used by the
  559. * <code>getServiceProviders</code> methods when their
  560. * <code>useOrdering</code> argument is <code>true</code>.
  561. *
  562. * @param category a <code>Class</code> object indicating the
  563. * category under which the preference is to be disestablished.
  564. * @param firstProvider the formerly preferred provider.
  565. * @param secondProvider the provider to which
  566. * <code>firstProvider</code> was formerly preferred.
  567. *
  568. * @return <code>true</code> if a previously set ordering was
  569. * disestablished.
  570. *
  571. * @exception IllegalArgumentException if either provider is
  572. * <code>null</code> or they are the same object.
  573. * @exception IllegalArgumentException if there is no category
  574. * corresponding to <code>category</code>.
  575. */
  576. public <T> boolean unsetOrdering(Class<T> category,
  577. T firstProvider,
  578. T secondProvider) {
  579. if (firstProvider == null || secondProvider == null) {
  580. throw new IllegalArgumentException("provider is null!");
  581. }
  582. if (firstProvider == secondProvider) {
  583. throw new IllegalArgumentException("providers are the same!");
  584. }
  585. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  586. if (reg == null) {
  587. throw new IllegalArgumentException("category unknown!");
  588. }
  589. if (reg.contains(firstProvider) &&
  590. reg.contains(secondProvider)) {
  591. return reg.unsetOrdering(firstProvider, secondProvider);
  592. }
  593. return false;
  594. }
  595. /**
  596. * Deregisters all service provider object currently registered
  597. * under the given category.
  598. *
  599. * @param category the category to be emptied.
  600. *
  601. * @exception IllegalArgumentException if there is no category
  602. * corresponding to <code>category</code>.
  603. */
  604. public void deregisterAll(Class<?> category) {
  605. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  606. if (reg == null) {
  607. throw new IllegalArgumentException("category unknown!");
  608. }
  609. reg.clear();
  610. }
  611. /**
  612. * Deregisters all currently registered service providers from all
  613. * categories.
  614. */
  615. public void deregisterAll() {
  616. Iterator iter = categoryMap.values().iterator();
  617. while (iter.hasNext()) {
  618. SubRegistry reg = (SubRegistry)iter.next();
  619. reg.clear();
  620. }
  621. }
  622. /**
  623. * Finalizes this object prior to garbage collection. The
  624. * <code>deregisterAll</code> method is called to deregister all
  625. * currently registered service providers. This method should not
  626. * be called from application code.
  627. *
  628. * @exception Throwable if an error occurs during superclass
  629. * finalization.
  630. */
  631. public void finalize() throws Throwable {
  632. deregisterAll();
  633. super.finalize();
  634. }
  635. }
  636. /**
  637. * A portion of a registry dealing with a single superclass or
  638. * interface.
  639. */
  640. class SubRegistry {
  641. ServiceRegistry registry;
  642. Class category;
  643. // Provider Objects organized by partial oridering
  644. PartiallyOrderedSet poset = new PartiallyOrderedSet();
  645. // Class -> Provider Object of that class
  646. Map<Class<?>,Object> map = new HashMap();
  647. public SubRegistry(ServiceRegistry registry, Class category) {
  648. this.registry = registry;
  649. this.category = category;
  650. }
  651. public boolean registerServiceProvider(Object provider) {
  652. Object oprovider = map.get(provider.getClass());
  653. boolean present = oprovider != null;
  654. if (present) {
  655. deregisterServiceProvider(oprovider);
  656. }
  657. map.put(provider.getClass(), provider);
  658. poset.add(provider);
  659. if (provider instanceof RegisterableService) {
  660. RegisterableService rs = (RegisterableService)provider;
  661. rs.onRegistration(registry, category);
  662. }
  663. return !present;
  664. }
  665. /**
  666. * If the provider was not previously registered, do nothing.
  667. *
  668. * @return true if the provider was previously registered.
  669. */
  670. public boolean deregisterServiceProvider(Object provider) {
  671. Object oprovider = map.get(provider.getClass());
  672. if (provider == oprovider) {
  673. map.remove(provider.getClass());
  674. poset.remove(provider);
  675. if (provider instanceof RegisterableService) {
  676. RegisterableService rs = (RegisterableService)provider;
  677. rs.onDeregistration(registry, category);
  678. }
  679. return true;
  680. }
  681. return false;
  682. }
  683. public boolean contains(Object provider) {
  684. Object oprovider = map.get(provider.getClass());
  685. return oprovider == provider;
  686. }
  687. public boolean setOrdering(Object firstProvider,
  688. Object secondProvider) {
  689. return poset.setOrdering(firstProvider, secondProvider);
  690. }
  691. public boolean unsetOrdering(Object firstProvider,
  692. Object secondProvider) {
  693. return poset.unsetOrdering(firstProvider, secondProvider);
  694. }
  695. public Iterator getServiceProviders(boolean useOrdering) {
  696. if (useOrdering) {
  697. return poset.iterator();
  698. } else {
  699. return map.values().iterator();
  700. }
  701. }
  702. public <T> T getServiceProviderByClass(Class<T> providerClass) {
  703. return (T)map.get(providerClass);
  704. }
  705. public void clear() {
  706. Iterator iter = map.values().iterator();
  707. while (iter.hasNext()) {
  708. Object provider = iter.next();
  709. iter.remove();
  710. if (provider instanceof RegisterableService) {
  711. RegisterableService rs = (RegisterableService)provider;
  712. rs.onDeregistration(registry, category);
  713. }
  714. }
  715. poset.clear();
  716. }
  717. public void finalize() {
  718. clear();
  719. }
  720. }
  721. /**
  722. * A class for wrapping <code>Iterators</code> with a filter function.
  723. * This provides an iterator for a subset without duplication.
  724. */
  725. class FilterIterator<T> implements Iterator<T> {
  726. private Iterator<T> iter;
  727. private ServiceRegistry.Filter filter;
  728. private T next = null;
  729. public FilterIterator(Iterator<T> iter,
  730. ServiceRegistry.Filter filter) {
  731. this.iter = iter;
  732. this.filter = filter;
  733. advance();
  734. }
  735. private void advance() {
  736. while (iter.hasNext()) {
  737. T elt = iter.next();
  738. if (filter.filter(elt)) {
  739. next = elt;
  740. return;
  741. }
  742. }
  743. next = null;
  744. }
  745. public boolean hasNext() {
  746. return next != null;
  747. }
  748. public T next() {
  749. if (next == null) {
  750. throw new NoSuchElementException();
  751. }
  752. T o = next;
  753. advance();
  754. return o;
  755. }
  756. public void remove() {
  757. throw new UnsupportedOperationException();
  758. }
  759. }