1. /*
  2. * @(#)ServiceRegistry.java 1.18 03/01/23
  3. *
  4. * Copyright 2003 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 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 Iterator lookupProviders(Class providerClass,
  146. ClassLoader loader) {
  147. if (providerClass == null) {
  148. throw new IllegalArgumentException("providerClass == null!");
  149. }
  150. return Service.providers(providerClass, loader);
  151. }
  152. /**
  153. * Locates and incrementally instantiates the available providers
  154. * of a given service using the context class loader. This
  155. * convenience method is equivalent to:
  156. *
  157. * <pre>
  158. * ClassLoader cl = Thread.currentThread().getContextClassLoader();
  159. * return Service.providers(service, cl);
  160. * </pre>
  161. *
  162. * @param providerClass a <code>Class</code>object indicating the
  163. * class or interface of the service providers being detected.
  164. *
  165. * @return An <code>Iterator</code> that yields provider objects
  166. * for the given service, in some arbitrary order. The iterator
  167. * will throw an <code>Error</code> if a provider-configuration
  168. * file violates the specified format or if a provider class
  169. * cannot be found and instantiated.
  170. *
  171. * @exception IllegalArgumentException if
  172. * <code>providerClass</code> is <code>null</code>.
  173. */
  174. public static Iterator lookupProviders(Class providerClass) {
  175. if (providerClass == null) {
  176. throw new IllegalArgumentException("providerClass == null!");
  177. }
  178. return Service.providers(providerClass);
  179. }
  180. /**
  181. * Returns an <code>Iterator</code> of <code>Class</code> objects
  182. * indicating the current set of categories. The iterator will be
  183. * empty if no categories exist.
  184. *
  185. * @return an <code>Iterator</code> containing
  186. * <code>Class</code>objects.
  187. */
  188. public Iterator getCategories() {
  189. Set keySet = categoryMap.keySet();
  190. return keySet.iterator();
  191. }
  192. /**
  193. * Returns an Iterator containing the subregistries to which the
  194. * provider belongs.
  195. */
  196. private Iterator getSubRegistries(Object provider) {
  197. List l = new ArrayList();
  198. Iterator iter = categoryMap.keySet().iterator();
  199. while (iter.hasNext()) {
  200. Class c = (Class)iter.next();
  201. if (c.isAssignableFrom(provider.getClass())) {
  202. l.add((SubRegistry)categoryMap.get(c));
  203. }
  204. }
  205. return l.iterator();
  206. }
  207. /**
  208. * Adds a service provider object to the registry. The provider
  209. * is associated with the given category.
  210. *
  211. * <p> If <code>provider</code> implements the
  212. * <code>RegisterableService</code> interface, its
  213. * <code>onRegistration</code> method will be called. Its
  214. * <code>onDeregistration</code> method will be called each time
  215. * it is deregistered from a category, for example if a
  216. * category is removed or the registry is garbage collected.
  217. *
  218. * @param provider the service provide object to be registered.
  219. * @param category the category under which to register the
  220. * provider.
  221. *
  222. * @return true if no provider of the same class was previously
  223. * registered in the same category category.
  224. *
  225. * @exception IllegalArgumentException if <code>provider</code> is
  226. * <code>null</code>.
  227. * @exception IllegalArgumentException if there is no category
  228. * corresponding to <code>category</code>.
  229. * @exception ClassCastException if provider does not implement
  230. * the <code>Class</code> defined by <code>category</code>.
  231. */
  232. public boolean registerServiceProvider(Object provider,
  233. Class category) {
  234. if (provider == null) {
  235. throw new IllegalArgumentException("provider == null!");
  236. }
  237. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  238. if (reg == null) {
  239. throw new IllegalArgumentException("category unknown!");
  240. }
  241. if (!category.isAssignableFrom(provider.getClass())) {
  242. throw new ClassCastException();
  243. }
  244. return reg.registerServiceProvider(provider);
  245. }
  246. /**
  247. * Adds a service provider object to the registry. The provider
  248. * is associated within each category present in the registry
  249. * whose <code>Class</code> it implements.
  250. *
  251. * <p> If <code>provider</code> implements the
  252. * <code>RegisterableService</code> interface, its
  253. * <code>onRegistration</code> method will be called once for each
  254. * category it is registered under. Its
  255. * <code>onDeregistration</code> method will be called each time
  256. * it is deregistered from a category or when the registry is
  257. * finalized.
  258. *
  259. * @param provider the service provider object to be registered.
  260. *
  261. * @exception IllegalArgumentException if
  262. * <code>provider</code> is <code>null</code>.
  263. */
  264. public void registerServiceProvider(Object provider) {
  265. if (provider == null) {
  266. throw new IllegalArgumentException("provider == null!");
  267. }
  268. Iterator regs = getSubRegistries(provider);
  269. while (regs.hasNext()) {
  270. SubRegistry reg = (SubRegistry)regs.next();
  271. reg.registerServiceProvider(provider);
  272. }
  273. }
  274. /**
  275. * Adds a set of service provider objects, taken from an
  276. * <code>Iterator</code> to the registry. Each provider is
  277. * associated within each category present in the registry whose
  278. * <code>Class</code> it implements.
  279. *
  280. * <p> For each entry of <code>providers</code> that implements
  281. * the <code>RegisterableService</code> interface, its
  282. * <code>onRegistration</code> method will be called once for each
  283. * category it is registered under. Its
  284. * <code>onDeregistration</code> method will be called each time
  285. * it is deregistered from a category or when the registry is
  286. * finalized.
  287. *
  288. * @param providers an Iterator containing service provider
  289. * objects to be registered.
  290. *
  291. * @exception IllegalArgumentException if <code>providers</code>
  292. * is <code>null</code> or contains a <code>null</code> entry.
  293. */
  294. public void registerServiceProviders(Iterator providers) {
  295. if (providers == null) {
  296. throw new IllegalArgumentException("provider == null!");
  297. }
  298. while (providers.hasNext()) {
  299. registerServiceProvider(providers.next());
  300. }
  301. }
  302. /**
  303. * Removes a service provider object from the given category. If
  304. * the provider was not previously registered, nothing happens and
  305. * <code>false</code> is returned. Otherwise, <code>true</code>
  306. * is returned. If an object of the same class as
  307. * <code>provider</code> but not equal (using <code>==</code>) to
  308. * <code>provider</code> is registered, it will not be
  309. * deregistered.
  310. *
  311. * <p> If <code>provider</code> implements the
  312. * <code>RegisterableService</code> interface, its
  313. * <code>onDeregistration</code> method will be called.
  314. *
  315. * @param provider the service provider object to be deregistered.
  316. * @param category the category from which to deregister the
  317. * provider.
  318. *
  319. * @return <code>true</code> if the provider was previously
  320. * registered in the same category category,
  321. * <code>false</code> otherwise.
  322. *
  323. * @exception IllegalArgumentException if <code>provider</code> is
  324. * <code>null</code>.
  325. * @exception IllegalArgumentException if there is no category
  326. * corresponding to <code>category</code>.
  327. * @exception ClassCastException if provider does not implement
  328. * the class defined by <code>category</code>.
  329. */
  330. public boolean deregisterServiceProvider(Object provider,
  331. Class category) {
  332. if (provider == null) {
  333. throw new IllegalArgumentException("provider == null!");
  334. }
  335. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  336. if (reg == null) {
  337. throw new IllegalArgumentException("category unknown!");
  338. }
  339. if (!category.isAssignableFrom(provider.getClass())) {
  340. throw new ClassCastException();
  341. }
  342. return reg.deregisterServiceProvider(provider);
  343. }
  344. /**
  345. * Removes a service provider object from all categories that
  346. * contain it.
  347. *
  348. * @param provider the service provider object to be deregistered.
  349. *
  350. * @exception IllegalArgumentException if <code>provider</code> is
  351. * <code>null</code>.
  352. */
  353. public void deregisterServiceProvider(Object provider) {
  354. if (provider == null) {
  355. throw new IllegalArgumentException("provider == null!");
  356. }
  357. Iterator regs = getSubRegistries(provider);
  358. while (regs.hasNext()) {
  359. SubRegistry reg = (SubRegistry)regs.next();
  360. reg.deregisterServiceProvider(provider);
  361. }
  362. }
  363. /**
  364. * Returns <code>true</code> if <code>provider</code> is currently
  365. * registered.
  366. *
  367. * @param provider the service provider object to be queried.
  368. *
  369. * @return <code>true</code> if the given provider has been
  370. * registered.
  371. *
  372. * @exception IllegalArgumentException if <code>provider</code> is
  373. * <code>null</code>.
  374. */
  375. public boolean contains(Object provider) {
  376. if (provider == null) {
  377. throw new IllegalArgumentException("provider == null!");
  378. }
  379. Iterator regs = getSubRegistries(provider);
  380. while (regs.hasNext()) {
  381. SubRegistry reg = (SubRegistry)regs.next();
  382. if (reg.contains(provider)) {
  383. return true;
  384. }
  385. }
  386. return false;
  387. }
  388. /**
  389. * Returns an <code>Iterator</code> containing all registered
  390. * service providers in the given category. If
  391. * <code>useOrdering</code> is <code>false</code>, the iterator
  392. * will return all of the server provider objects in an arbitrary
  393. * order. Otherwise, the ordering will respect any pairwise
  394. * orderings that have been set. If the graph of pairwise
  395. * orderings contains cycles, any providers that belong to a cycle
  396. * will not be returned.
  397. *
  398. * @param category the category to be retrieved from.
  399. * @param useOrdering <code>true</code> if pairwise orderings
  400. * should be taken account in ordering the returned objects.
  401. *
  402. * @return an <code>Iterator</code> containing service provider
  403. * objects from the given category, possibly in order.
  404. *
  405. * @exception IllegalArgumentException if there is no category
  406. * corresponding to <code>category</code>.
  407. */
  408. public Iterator getServiceProviders(Class category,
  409. boolean useOrdering) {
  410. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  411. if (reg == null) {
  412. throw new IllegalArgumentException("category unknown!");
  413. }
  414. return reg.getServiceProviders(useOrdering);
  415. }
  416. /**
  417. * A simple filter interface used by
  418. * <code>ServiceRegistry.getServiceProviders</code> to select
  419. * providers matching an arbitrary criterion. Classes that
  420. * implement this interface should be defined in order to make use
  421. * of the <code>getServiceProviders</code> method of
  422. * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
  423. *
  424. * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
  425. */
  426. public interface Filter {
  427. /**
  428. * Returns <code>true</code> if the given
  429. * <code>provider</code> object matches the criterion defined
  430. * by this <code>Filter</code>.
  431. *
  432. * @param provider a service provider <code>Object</code>.
  433. *
  434. * @return true if the provider matches the criterion.
  435. */
  436. boolean filter(Object provider);
  437. }
  438. /**
  439. * Returns an <code>Iterator</code> containing service provider
  440. * objects within a given category that satisfy a criterion
  441. * imposed by the supplied <code>ServiceRegistry.Filter</code>
  442. * object's <code>filter</code> method.
  443. *
  444. * <p> The <code>useOrdering</code> argument controls the
  445. * ordering of the results using the same rules as
  446. * <code>getServiceProviders(Class, boolean)</code>.
  447. *
  448. * @param category the category to be retrieved from.
  449. * @param filter an instance of <code>ServiceRegistry.Filter</code>
  450. * whose <code>filter</code> method will be invoked.
  451. * @param useOrdering <code>true</code> if pairwise orderings
  452. * should be taken account in ordering the returned objects.
  453. *
  454. * @return an <code>Iterator</code> containing service provider
  455. * objects from the given category, possibly in order.
  456. *
  457. * @exception IllegalArgumentException if there is no category
  458. * corresponding to <code>category</code>.
  459. */
  460. public Iterator getServiceProviders(Class category,
  461. Filter filter,
  462. boolean useOrdering) {
  463. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  464. if (reg == null) {
  465. throw new IllegalArgumentException("category unknown!");
  466. }
  467. Iterator iter = getServiceProviders(category, useOrdering);
  468. return new FilterIterator(iter, filter);
  469. }
  470. /**
  471. * Returns the currently registered service provider object that
  472. * is of the given class type. At most one object of a given
  473. * class is allowed to be registered at any given time. If no
  474. * registered object has the desired class type, <code>null</code>
  475. * is returned.
  476. *
  477. * @param providerClass the <code>Class</code> of the desired
  478. * service provider object.
  479. *
  480. * @return a currently registered service provider object with the
  481. * desired <code>Class</code>type, or <code>null</code> is none is
  482. * present.
  483. *
  484. * @exception IllegalArgumentException if <code>providerClass</code> is
  485. * <code>null</code>.
  486. */
  487. public Object getServiceProviderByClass(Class providerClass) {
  488. if (providerClass == null) {
  489. throw new IllegalArgumentException("providerClass == null!");
  490. }
  491. Iterator iter = categoryMap.keySet().iterator();
  492. while (iter.hasNext()) {
  493. Class c = (Class)iter.next();
  494. if (c.isAssignableFrom(providerClass)) {
  495. SubRegistry reg = (SubRegistry)categoryMap.get(c);
  496. Object provider = reg.getServiceProviderByClass(providerClass);
  497. if (provider != null) {
  498. return provider;
  499. }
  500. }
  501. }
  502. return null;
  503. }
  504. /**
  505. * Sets a pairwise ordering between two service provider objects
  506. * within a given category. If one or both objects are not
  507. * currently registered within the given category, or if the
  508. * desired ordering is already set, nothing happens and
  509. * <code>false</code> is returned. If the providers previously
  510. * were ordered in the reverse direction, that ordering is
  511. * removed.
  512. *
  513. * <p> The ordering will be used by the
  514. * <code>getServiceProviders</code> methods when their
  515. * <code>useOrdering</code> argument is <code>true</code>.
  516. *
  517. * @param category a <code>Class</code> object indicating the
  518. * category under which the preference is to be established.
  519. * @param firstProvider the preferred provider.
  520. * @param secondProvider the provider to which
  521. * <code>firstProvider</code> is preferred.
  522. *
  523. * @return <code>true</code> if a previously unset ordering
  524. * was established.
  525. *
  526. * @exception IllegalArgumentException if either provider is
  527. * <code>null</code> or they are the same object.
  528. * @exception IllegalArgumentException if there is no category
  529. * corresponding to <code>category</code>.
  530. */
  531. public boolean setOrdering(Class category,
  532. Object firstProvider,
  533. Object secondProvider) {
  534. if (firstProvider == null || secondProvider == null) {
  535. throw new IllegalArgumentException("provider is null!");
  536. }
  537. if (firstProvider == secondProvider) {
  538. throw new IllegalArgumentException("providers are the same!");
  539. }
  540. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  541. if (reg == null) {
  542. throw new IllegalArgumentException("category unknown!");
  543. }
  544. if (reg.contains(firstProvider) &&
  545. reg.contains(secondProvider)) {
  546. return reg.setOrdering(firstProvider, secondProvider);
  547. }
  548. return false;
  549. }
  550. /**
  551. * Sets a pairwise ordering between two service provider objects
  552. * within a given category. If one or both objects are not
  553. * currently registered within the given category, or if no
  554. * ordering is currently set between them, nothing happens
  555. * and <code>false</code> is returned.
  556. *
  557. * <p> The ordering will be used by the
  558. * <code>getServiceProviders</code> methods when their
  559. * <code>useOrdering</code> argument is <code>true</code>.
  560. *
  561. * @param category a <code>Class</code> object indicating the
  562. * category under which the preference is to be disestablished.
  563. * @param firstProvider the formerly preferred provider.
  564. * @param secondProvider the provider to which
  565. * <code>firstProvider</code> was formerly preferred.
  566. *
  567. * @return <code>true</code> if a previously set ordering was
  568. * disestablished.
  569. *
  570. * @exception IllegalArgumentException if either provider is
  571. * <code>null</code> or they are the same object.
  572. * @exception IllegalArgumentException if there is no category
  573. * corresponding to <code>category</code>.
  574. */
  575. public boolean unsetOrdering(Class category,
  576. Object firstProvider,
  577. Object secondProvider) {
  578. if (firstProvider == null || secondProvider == null) {
  579. throw new IllegalArgumentException("provider is null!");
  580. }
  581. if (firstProvider == secondProvider) {
  582. throw new IllegalArgumentException("providers are the same!");
  583. }
  584. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  585. if (reg == null) {
  586. throw new IllegalArgumentException("category unknown!");
  587. }
  588. if (reg.contains(firstProvider) &&
  589. reg.contains(secondProvider)) {
  590. return reg.unsetOrdering(firstProvider, secondProvider);
  591. }
  592. return false;
  593. }
  594. /**
  595. * Deregisters all service provider object currently registered
  596. * under the given category.
  597. *
  598. * @param category the category to be emptied.
  599. *
  600. * @exception IllegalArgumentException if there is no category
  601. * corresponding to <code>category</code>.
  602. */
  603. public void deregisterAll(Class category) {
  604. SubRegistry reg = (SubRegistry)categoryMap.get(category);
  605. if (reg == null) {
  606. throw new IllegalArgumentException("category unknown!");
  607. }
  608. reg.clear();
  609. }
  610. /**
  611. * Deregisters all currently registered service providers from all
  612. * categories.
  613. */
  614. public void deregisterAll() {
  615. Iterator iter = categoryMap.values().iterator();
  616. while (iter.hasNext()) {
  617. SubRegistry reg = (SubRegistry)iter.next();
  618. reg.clear();
  619. }
  620. categoryMap.clear();
  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 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. boolean present = map.get(provider.getClass()) != null;
  653. map.put(provider.getClass(), provider);
  654. poset.add(provider);
  655. if (provider instanceof RegisterableService) {
  656. RegisterableService rs = (RegisterableService)provider;
  657. rs.onRegistration(registry, category);
  658. }
  659. return !present;
  660. }
  661. /**
  662. * If the provider was not previously registered, do nothing.
  663. *
  664. * @return true if the provider was previously registered.
  665. */
  666. public boolean deregisterServiceProvider(Object provider) {
  667. Object oprovider = map.get(provider.getClass());
  668. if (provider == oprovider) {
  669. map.remove(provider.getClass());
  670. poset.remove(provider);
  671. if (provider instanceof RegisterableService) {
  672. RegisterableService rs = (RegisterableService)provider;
  673. rs.onDeregistration(registry, category);
  674. }
  675. return true;
  676. }
  677. return false;
  678. }
  679. public boolean contains(Object provider) {
  680. Object oprovider = map.get(provider.getClass());
  681. return oprovider == provider;
  682. }
  683. public boolean setOrdering(Object firstProvider,
  684. Object secondProvider) {
  685. return poset.setOrdering(firstProvider, secondProvider);
  686. }
  687. public boolean unsetOrdering(Object firstProvider,
  688. Object secondProvider) {
  689. return poset.unsetOrdering(firstProvider, secondProvider);
  690. }
  691. public Iterator getServiceProviders(boolean useOrdering) {
  692. if (useOrdering) {
  693. return poset.iterator();
  694. } else {
  695. return map.values().iterator();
  696. }
  697. }
  698. public Object getServiceProviderByClass(Class providerClass) {
  699. return map.get(providerClass);
  700. }
  701. public void clear() {
  702. Iterator iter = map.values().iterator();
  703. while (iter.hasNext()) {
  704. Object provider = iter.next();
  705. iter.remove();
  706. if (provider instanceof RegisterableService) {
  707. RegisterableService rs = (RegisterableService)provider;
  708. rs.onDeregistration(registry, category);
  709. }
  710. }
  711. poset.clear();
  712. }
  713. public void finalize() {
  714. clear();
  715. }
  716. }
  717. /**
  718. * A class for wrapping <code>Iterators</code> with a filter function.
  719. * This provides an iterator for a subset without duplication.
  720. */
  721. class FilterIterator implements Iterator {
  722. private Iterator iter;
  723. private ServiceRegistry.Filter filter;
  724. private Object next = null;
  725. public FilterIterator(Iterator iter,
  726. ServiceRegistry.Filter filter) {
  727. this.iter = iter;
  728. this.filter = filter;
  729. advance();
  730. }
  731. private void advance() {
  732. while (iter.hasNext()) {
  733. Object elt = iter.next();
  734. if (filter.filter(elt)) {
  735. next = elt;
  736. return;
  737. }
  738. }
  739. next = null;
  740. }
  741. public boolean hasNext() {
  742. return next != null;
  743. }
  744. public Object next() {
  745. if (next == null) {
  746. throw new NoSuchElementException();
  747. }
  748. Object o = next;
  749. advance();
  750. return o;
  751. }
  752. public void remove() {
  753. throw new UnsupportedOperationException();
  754. }
  755. }