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