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<Class<?>, SubRegistry> 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 = categories.next();
 121             SubRegistry reg = new SubRegistry(this, category);
 122             categoryMap.put(category, reg);
 123         }
 124     }
 125 
 126     // The following two methods expose functionality from
 127     // sun.misc.Service.  If that class is made public, they may be
 128     // removed.
 129     //
 130     // The sun.misc.ServiceConfigurationError class may also be
 131     // exposed, in which case the references to 'an
 132     // <code>Error</code>' below should be changed to 'a
 133     // <code>ServiceConfigurationError</code>'.
 134 
 135     /**
 136      * Searches for implementations of a particular service class
 137      * using the given class loader.
 138      *
 139      * <p> This method transforms the name of the given service class
 140      * into a provider-configuration filename as described in the
 141      * class comment and then uses the <code>getResources</code>
 142      * method of the given class loader to find all available files
 143      * with that name.  These files are then read and parsed to
 144      * produce a list of provider-class names.  The iterator that is
 145      * returned uses the given class loader to look up and then
 146      * instantiate each element of the list.
 147      *
 148      * <p> Because it is possible for extensions to be installed into
 149      * a running Java virtual machine, this method may return
 150      * different results each time it is invoked.
 151      *
 152      * @param providerClass a <code>Class</code>object indicating the
 153      * class or interface of the service providers being detected.
 154      *
 155      * @param loader the class loader to be used to load
 156      * provider-configuration files and instantiate provider classes,
 157      * or <code>null</code> if the system class loader (or, failing that
 158      * the bootstrap class loader) is to be used.
 159      *
 160      * @param <T> the type of the providerClass.
 161      *
 162      * @return An <code>Iterator</code> that yields provider objects
 163      * for the given service, in some arbitrary order.  The iterator
 164      * will throw an <code>Error</code> if a provider-configuration
 165      * file violates the specified format or if a provider class
 166      * cannot be found and instantiated.
 167      *
 168      * @exception IllegalArgumentException if
 169      * <code>providerClass</code> is <code>null</code>.
 170      */
 171     public static <T> Iterator<T> lookupProviders(Class<T> providerClass,
 172                                                   ClassLoader loader)
 173     {
 174         if (providerClass == null) {
 175             throw new IllegalArgumentException("providerClass == null!");
 176         }
 177         return ServiceLoader.load(providerClass, loader).iterator();
 178     }
 179 
 180     /**
 181      * Locates and incrementally instantiates the available providers
 182      * of a given service using the context class loader.  This
 183      * convenience method is equivalent to:
 184      *
 185      * <pre>
 186      *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
 187      *   return Service.providers(service, cl);
 188      * </pre>
 189      *
 190      * @param providerClass a <code>Class</code>object indicating the
 191      * class or interface of the service providers being detected.
 192      *
 193      * @param <T> the type of the providerClass.
 194      *
 195      * @return An <code>Iterator</code> that yields provider objects
 196      * for the given service, in some arbitrary order.  The iterator
 197      * will throw an <code>Error</code> if a provider-configuration
 198      * file violates the specified format or if a provider class
 199      * cannot be found and instantiated.
 200      *
 201      * @exception IllegalArgumentException if
 202      * <code>providerClass</code> is <code>null</code>.
 203      */
 204     public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
 205         if (providerClass == null) {
 206             throw new IllegalArgumentException("providerClass == null!");
 207         }
 208         return ServiceLoader.load(providerClass).iterator();
 209     }
 210 
 211     /**
 212      * Returns an <code>Iterator</code> of <code>Class</code> objects
 213      * indicating the current set of categories.  The iterator will be
 214      * empty if no categories exist.
 215      *
 216      * @return an <code>Iterator</code> containing
 217      * <code>Class</code>objects.
 218      */
 219     public Iterator<Class<?>> getCategories() {
 220         Set<Class<?>> keySet = categoryMap.keySet();
 221         return keySet.iterator();
 222     }
 223 
 224     /**
 225      * Returns an Iterator containing the subregistries to which the
 226      * provider belongs.
 227      */
 228     private Iterator<SubRegistry> getSubRegistries(Object provider) {
 229         List<SubRegistry> l = new ArrayList<>();
 230         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 231         while (iter.hasNext()) {
 232             Class<?> c = iter.next();
 233             if (c.isAssignableFrom(provider.getClass())) {
 234                 l.add(categoryMap.get(c));
 235             }
 236         }
 237         return l.iterator();
 238     }
 239 
 240     /**
 241      * Adds a service provider object to the registry.  The provider
 242      * is associated with the given category.
 243      *
 244      * <p> If <code>provider</code> implements the
 245      * <code>RegisterableService</code> interface, its
 246      * <code>onRegistration</code> method will be called.  Its
 247      * <code>onDeregistration</code> method will be called each time
 248      * it is deregistered from a category, for example if a
 249      * category is removed or the registry is garbage collected.
 250      *
 251      * @param provider the service provide object to be registered.
 252      * @param category the category under which to register the
 253      * provider.
 254      * @param <T> the type of the provider.
 255      *
 256      * @return true if no provider of the same class was previously
 257      * registered in the same category category.
 258      *
 259      * @exception IllegalArgumentException if <code>provider</code> is
 260      * <code>null</code>.
 261      * @exception IllegalArgumentException if there is no category
 262      * corresponding to <code>category</code>.
 263      * @exception ClassCastException if provider does not implement
 264      * the <code>Class</code> defined by <code>category</code>.
 265      */
 266     public <T> boolean registerServiceProvider(T provider,
 267                                                Class<T> category) {
 268         if (provider == null) {
 269             throw new IllegalArgumentException("provider == null!");
 270         }
 271         SubRegistry reg = categoryMap.get(category);
 272         if (reg == null) {
 273             throw new IllegalArgumentException("category unknown!");
 274         }
 275         if (!category.isAssignableFrom(provider.getClass())) {
 276             throw new ClassCastException();
 277         }
 278 
 279         return reg.registerServiceProvider(provider);
 280     }
 281 
 282     /**
 283      * Adds a service provider object to the registry.  The provider
 284      * is associated within each category present in the registry
 285      * whose <code>Class</code> it implements.
 286      *
 287      * <p> If <code>provider</code> implements the
 288      * <code>RegisterableService</code> interface, its
 289      * <code>onRegistration</code> method will be called once for each
 290      * category it is registered under.  Its
 291      * <code>onDeregistration</code> method will be called each time
 292      * it is deregistered from a category or when the registry is
 293      * finalized.
 294      *
 295      * @param provider the service provider object to be registered.
 296      *
 297      * @exception IllegalArgumentException if
 298      * <code>provider</code> is <code>null</code>.
 299      */
 300     public void registerServiceProvider(Object provider) {
 301         if (provider == null) {
 302             throw new IllegalArgumentException("provider == null!");
 303         }
 304         Iterator<SubRegistry> regs = getSubRegistries(provider);
 305         while (regs.hasNext()) {
 306             SubRegistry reg = regs.next();
 307             reg.registerServiceProvider(provider);
 308         }
 309     }
 310 
 311     /**
 312      * Adds a set of service provider objects, taken from an
 313      * <code>Iterator</code> to the registry.  Each provider is
 314      * associated within each category present in the registry whose
 315      * <code>Class</code> it implements.
 316      *
 317      * <p> For each entry of <code>providers</code> that implements
 318      * the <code>RegisterableService</code> interface, its
 319      * <code>onRegistration</code> method will be called once for each
 320      * category it is registered under.  Its
 321      * <code>onDeregistration</code> method will be called each time
 322      * it is deregistered from a category or when the registry is
 323      * finalized.
 324      *
 325      * @param providers an Iterator containing service provider
 326      * objects to be registered.
 327      *
 328      * @exception IllegalArgumentException if <code>providers</code>
 329      * is <code>null</code> or contains a <code>null</code> entry.
 330      */
 331     public void registerServiceProviders(Iterator<?> providers) {
 332         if (providers == null) {
 333             throw new IllegalArgumentException("provider == null!");
 334         }
 335         while (providers.hasNext()) {
 336             registerServiceProvider(providers.next());
 337         }
 338     }
 339 
 340     /**
 341      * Removes a service provider object from the given category.  If
 342      * the provider was not previously registered, nothing happens and
 343      * <code>false</code> is returned.  Otherwise, <code>true</code>
 344      * is returned.  If an object of the same class as
 345      * <code>provider</code> but not equal (using <code>==</code>) to
 346      * <code>provider</code> is registered, it will not be
 347      * deregistered.
 348      *
 349      * <p> If <code>provider</code> implements the
 350      * <code>RegisterableService</code> interface, its
 351      * <code>onDeregistration</code> method will be called.
 352      *
 353      * @param provider the service provider object to be deregistered.
 354      * @param category the category from which to deregister the
 355      * provider.
 356      * @param <T> the type of the provider.
 357      *
 358      * @return <code>true</code> if the provider was previously
 359      * registered in the same category category,
 360      * <code>false</code> otherwise.
 361      *
 362      * @exception IllegalArgumentException if <code>provider</code> is
 363      * <code>null</code>.
 364      * @exception IllegalArgumentException if there is no category
 365      * corresponding to <code>category</code>.
 366      * @exception ClassCastException if provider does not implement
 367      * the class defined by <code>category</code>.
 368      */
 369     public <T> boolean deregisterServiceProvider(T provider,
 370                                                  Class<T> category) {
 371         if (provider == null) {
 372             throw new IllegalArgumentException("provider == null!");
 373         }
 374         SubRegistry reg = categoryMap.get(category);
 375         if (reg == null) {
 376             throw new IllegalArgumentException("category unknown!");
 377         }
 378         if (!category.isAssignableFrom(provider.getClass())) {
 379             throw new ClassCastException();
 380         }
 381         return reg.deregisterServiceProvider(provider);
 382     }
 383 
 384     /**
 385      * Removes a service provider object from all categories that
 386      * contain it.
 387      *
 388      * @param provider the service provider object to be deregistered.
 389      *
 390      * @exception IllegalArgumentException if <code>provider</code> is
 391      * <code>null</code>.
 392      */
 393     public void deregisterServiceProvider(Object provider) {
 394         if (provider == null) {
 395             throw new IllegalArgumentException("provider == null!");
 396         }
 397         Iterator<SubRegistry> regs = getSubRegistries(provider);
 398         while (regs.hasNext()) {
 399             SubRegistry reg = regs.next();
 400             reg.deregisterServiceProvider(provider);
 401         }
 402     }
 403 
 404     /**
 405      * Returns <code>true</code> if <code>provider</code> is currently
 406      * registered.
 407      *
 408      * @param provider the service provider object to be queried.
 409      *
 410      * @return <code>true</code> if the given provider has been
 411      * registered.
 412      *
 413      * @exception IllegalArgumentException if <code>provider</code> is
 414      * <code>null</code>.
 415      */
 416     public boolean contains(Object provider) {
 417         if (provider == null) {
 418             throw new IllegalArgumentException("provider == null!");
 419         }
 420         Iterator<SubRegistry> regs = getSubRegistries(provider);
 421         while (regs.hasNext()) {
 422             SubRegistry reg = regs.next();
 423             if (reg.contains(provider)) {
 424                 return true;
 425             }
 426         }
 427 
 428         return false;
 429     }
 430 
 431     /**
 432      * Returns an <code>Iterator</code> containing all registered
 433      * service providers in the given category.  If
 434      * <code>useOrdering</code> is <code>false</code>, the iterator
 435      * will return all of the server provider objects in an arbitrary
 436      * order.  Otherwise, the ordering will respect any pairwise
 437      * orderings that have been set.  If the graph of pairwise
 438      * orderings contains cycles, any providers that belong to a cycle
 439      * will not be returned.
 440      *
 441      * @param category the category to be retrieved from.
 442      * @param useOrdering <code>true</code> if pairwise orderings
 443      * should be taken account in ordering the returned objects.
 444      * @param <T> the type of the category.
 445      *
 446      * @return an <code>Iterator</code> containing service provider
 447      * objects from the given category, possibly in order.
 448      *
 449      * @exception IllegalArgumentException if there is no category
 450      * corresponding to <code>category</code>.
 451      */
 452     public <T> Iterator<T> getServiceProviders(Class<T> category,
 453                                                boolean useOrdering) {
 454         SubRegistry reg = categoryMap.get(category);
 455         if (reg == null) {
 456             throw new IllegalArgumentException("category unknown!");
 457         }
 458         @SuppressWarnings("unchecked")
 459         Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);
 460         return it;
 461     }
 462 
 463     /**
 464      * A simple filter interface used by
 465      * <code>ServiceRegistry.getServiceProviders</code> to select
 466      * providers matching an arbitrary criterion.  Classes that
 467      * implement this interface should be defined in order to make use
 468      * of the <code>getServiceProviders</code> method of
 469      * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
 470      *
 471      * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
 472      */
 473     public interface Filter {
 474 
 475         /**
 476          * Returns <code>true</code> if the given
 477          * <code>provider</code> object matches the criterion defined
 478          * by this <code>Filter</code>.
 479          *
 480          * @param provider a service provider <code>Object</code>.
 481          *
 482          * @return true if the provider matches the criterion.
 483          */
 484         boolean filter(Object provider);
 485     }
 486 
 487     /**
 488      * Returns an <code>Iterator</code> containing service provider
 489      * objects within a given category that satisfy a criterion
 490      * imposed by the supplied <code>ServiceRegistry.Filter</code>
 491      * object's <code>filter</code> method.
 492      *
 493      * <p> The <code>useOrdering</code> argument controls the
 494      * ordering of the results using the same rules as
 495      * <code>getServiceProviders(Class, boolean)</code>.
 496      *
 497      * @param category the category to be retrieved from.
 498      * @param filter an instance of <code>ServiceRegistry.Filter</code>
 499      * whose <code>filter</code> method will be invoked.
 500      * @param useOrdering <code>true</code> if pairwise orderings
 501      * should be taken account in ordering the returned objects.
 502      * @param <T> the type of the category.
 503      *
 504      * @return an <code>Iterator</code> containing service provider
 505      * objects from the given category, possibly in order.
 506      *
 507      * @exception IllegalArgumentException if there is no category
 508      * corresponding to <code>category</code>.
 509      */
 510     public <T> Iterator<T> getServiceProviders(Class<T> category,
 511                                                Filter filter,
 512                                                boolean useOrdering) {
 513         SubRegistry reg = categoryMap.get(category);
 514         if (reg == null) {
 515             throw new IllegalArgumentException("category unknown!");
 516         }
 517         Iterator<T> iter = getServiceProviders(category, useOrdering);
 518         return new FilterIterator<>(iter, filter);
 519     }
 520 
 521     /**
 522      * Returns the currently registered service provider object that
 523      * is of the given class type.  At most one object of a given
 524      * class is allowed to be registered at any given time.  If no
 525      * registered object has the desired class type, <code>null</code>
 526      * is returned.
 527      *
 528      * @param providerClass the <code>Class</code> of the desired
 529      * service provider object.
 530      * @param <T> the type of the provider.
 531      *
 532      * @return a currently registered service provider object with the
 533      * desired <code>Class</code>type, or <code>null</code> is none is
 534      * present.
 535      *
 536      * @exception IllegalArgumentException if <code>providerClass</code> is
 537      * <code>null</code>.
 538      */
 539     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 540         if (providerClass == null) {
 541             throw new IllegalArgumentException("providerClass == null!");
 542         }
 543         Iterator<Class<?>> iter = categoryMap.keySet().iterator();
 544         while (iter.hasNext()) {
 545             Class<?> c = iter.next();
 546             if (c.isAssignableFrom(providerClass)) {
 547                 SubRegistry reg = categoryMap.get(c);
 548                 T provider = reg.getServiceProviderByClass(providerClass);
 549                 if (provider != null) {
 550                     return provider;
 551                 }
 552             }
 553         }
 554         return null;
 555     }
 556 
 557     /**
 558      * Sets a pairwise ordering between two service provider objects
 559      * within a given category.  If one or both objects are not
 560      * currently registered within the given category, or if the
 561      * desired ordering is already set, nothing happens and
 562      * <code>false</code> is returned.  If the providers previously
 563      * were ordered in the reverse direction, that ordering is
 564      * removed.
 565      *
 566      * <p> The ordering will be used by the
 567      * <code>getServiceProviders</code> methods when their
 568      * <code>useOrdering</code> argument is <code>true</code>.
 569      *
 570      * @param category a <code>Class</code> object indicating the
 571      * category under which the preference is to be established.
 572      * @param firstProvider the preferred provider.
 573      * @param secondProvider the provider to which
 574      * <code>firstProvider</code> is preferred.
 575      * @param <T> the type of the category.
 576      *
 577      * @return <code>true</code> if a previously unset ordering
 578      * was established.
 579      *
 580      * @exception IllegalArgumentException if either provider is
 581      * <code>null</code> or they are the same object.
 582      * @exception IllegalArgumentException if there is no category
 583      * corresponding to <code>category</code>.
 584      */
 585     public <T> boolean setOrdering(Class<T> category,
 586                                    T firstProvider,
 587                                    T secondProvider) {
 588         if (firstProvider == null || secondProvider == null) {
 589             throw new IllegalArgumentException("provider is null!");
 590         }
 591         if (firstProvider == secondProvider) {
 592             throw new IllegalArgumentException("providers are the same!");
 593         }
 594         SubRegistry reg = categoryMap.get(category);
 595         if (reg == null) {
 596             throw new IllegalArgumentException("category unknown!");
 597         }
 598         if (reg.contains(firstProvider) &&
 599             reg.contains(secondProvider)) {
 600             return reg.setOrdering(firstProvider, secondProvider);
 601         }
 602         return false;
 603     }
 604 
 605     /**
 606      * Sets a pairwise ordering between two service provider objects
 607      * within a given category.  If one or both objects are not
 608      * currently registered within the given category, or if no
 609      * ordering is currently set between them, nothing happens
 610      * and <code>false</code> is returned.
 611      *
 612      * <p> The ordering will be used by the
 613      * <code>getServiceProviders</code> methods when their
 614      * <code>useOrdering</code> argument is <code>true</code>.
 615      *
 616      * @param category a <code>Class</code> object indicating the
 617      * category under which the preference is to be disestablished.
 618      * @param firstProvider the formerly preferred provider.
 619      * @param secondProvider the provider to which
 620      * <code>firstProvider</code> was formerly preferred.
 621      * @param <T> the type of the category.
 622      *
 623      * @return <code>true</code> if a previously set ordering was
 624      * disestablished.
 625      *
 626      * @exception IllegalArgumentException if either provider is
 627      * <code>null</code> or they are the same object.
 628      * @exception IllegalArgumentException if there is no category
 629      * corresponding to <code>category</code>.
 630      */
 631     public <T> boolean unsetOrdering(Class<T> category,
 632                                      T firstProvider,
 633                                      T secondProvider) {
 634         if (firstProvider == null || secondProvider == null) {
 635             throw new IllegalArgumentException("provider is null!");
 636         }
 637         if (firstProvider == secondProvider) {
 638             throw new IllegalArgumentException("providers are the same!");
 639         }
 640         SubRegistry reg = categoryMap.get(category);
 641         if (reg == null) {
 642             throw new IllegalArgumentException("category unknown!");
 643         }
 644         if (reg.contains(firstProvider) &&
 645             reg.contains(secondProvider)) {
 646             return reg.unsetOrdering(firstProvider, secondProvider);
 647         }
 648         return false;
 649     }
 650 
 651     /**
 652      * Deregisters all service provider object currently registered
 653      * under the given category.
 654      *
 655      * @param category the category to be emptied.
 656      *
 657      * @exception IllegalArgumentException if there is no category
 658      * corresponding to <code>category</code>.
 659      */
 660     public void deregisterAll(Class<?> category) {
 661         SubRegistry reg = categoryMap.get(category);
 662         if (reg == null) {
 663             throw new IllegalArgumentException("category unknown!");
 664         }
 665         reg.clear();
 666     }
 667 
 668     /**
 669      * Deregisters all currently registered service providers from all
 670      * categories.
 671      */
 672     public void deregisterAll() {
 673         Iterator<SubRegistry> iter = categoryMap.values().iterator();
 674         while (iter.hasNext()) {
 675             SubRegistry reg = iter.next();
 676             reg.clear();
 677         }
 678     }
 679 
 680     /**
 681      * Finalizes this object prior to garbage collection.  The
 682      * <code>deregisterAll</code> method is called to deregister all
 683      * currently registered service providers.  This method should not
 684      * be called from application code.
 685      *
 686      * @exception Throwable if an error occurs during superclass
 687      * finalization.
 688      */
 689     public void finalize() throws Throwable {
 690         deregisterAll();
 691         super.finalize();
 692     }
 693 }
 694 
 695 
 696 /**
 697  * A portion of a registry dealing with a single superclass or
 698  * interface.
 699  */
 700 class SubRegistry {
 701 
 702     ServiceRegistry registry;
 703 
 704     Class<?> category;
 705 
 706     // Provider Objects organized by partial oridering
 707     PartiallyOrderedSet<Object> poset = new PartiallyOrderedSet<>();
 708 
 709     // Class -> Provider Object of that class
 710     // No way to express heterogeneous map, we want
 711     // Map<Class<T>, T>, where T is ?
 712     Map<Class<?>, Object> map = new HashMap<>();
 713 
 714     public SubRegistry(ServiceRegistry registry, Class<?> category) {
 715         this.registry = registry;
 716         this.category = category;
 717     }
 718 
 719     public boolean registerServiceProvider(Object provider) {
 720         Object oprovider = map.get(provider.getClass());
 721         boolean present =  oprovider != null;
 722 
 723         if (present) {
 724             deregisterServiceProvider(oprovider);
 725         }
 726         map.put(provider.getClass(), provider);
 727         poset.add(provider);
 728         if (provider instanceof RegisterableService) {
 729             RegisterableService rs = (RegisterableService)provider;
 730             rs.onRegistration(registry, category);
 731         }
 732 
 733         return !present;
 734     }
 735 
 736     /**
 737      * If the provider was not previously registered, do nothing.
 738      *
 739      * @return true if the provider was previously registered.
 740      */
 741     public boolean deregisterServiceProvider(Object provider) {
 742         Object oprovider = map.get(provider.getClass());
 743 
 744         if (provider == oprovider) {
 745             map.remove(provider.getClass());
 746             poset.remove(provider);
 747             if (provider instanceof RegisterableService) {
 748                 RegisterableService rs = (RegisterableService)provider;
 749                 rs.onDeregistration(registry, category);
 750             }
 751 
 752             return true;
 753         }
 754         return false;
 755     }
 756 
 757     public boolean contains(Object provider) {
 758         Object oprovider = map.get(provider.getClass());
 759         return oprovider == provider;
 760     }
 761 
 762     public boolean setOrdering(Object firstProvider,
 763                                Object secondProvider) {
 764         return poset.setOrdering(firstProvider, secondProvider);
 765     }
 766 
 767     public boolean unsetOrdering(Object firstProvider,
 768                                  Object secondProvider) {
 769         return poset.unsetOrdering(firstProvider, secondProvider);
 770     }
 771 
 772     public Iterator<Object> getServiceProviders(boolean useOrdering) {
 773         if (useOrdering) {
 774             return poset.iterator();
 775         } else {
 776             return map.values().iterator();
 777         }
 778     }
 779 
 780     @SuppressWarnings("unchecked")
 781     public <T> T getServiceProviderByClass(Class<T> providerClass) {
 782         return (T)map.get(providerClass);
 783     }
 784 
 785     public void clear() {
 786         Iterator<Object> iter = map.values().iterator();
 787         while (iter.hasNext()) {
 788             Object provider = iter.next();
 789             iter.remove();
 790 
 791             if (provider instanceof RegisterableService) {
 792                 RegisterableService rs = (RegisterableService)provider;
 793                 rs.onDeregistration(registry, category);
 794             }
 795         }
 796         poset.clear();
 797     }
 798 
 799     public void finalize() {
 800         clear();
 801     }
 802 }
 803 
 804 
 805 /**
 806  * A class for wrapping <code>Iterators</code> with a filter function.
 807  * This provides an iterator for a subset without duplication.
 808  */
 809 class FilterIterator<T> implements Iterator<T> {
 810 
 811     private Iterator<? extends T> iter;
 812     private ServiceRegistry.Filter filter;
 813 
 814     private T next = null;
 815 
 816     public FilterIterator(Iterator<? extends T> iter,
 817                           ServiceRegistry.Filter filter) {
 818         this.iter = iter;
 819         this.filter = filter;
 820         advance();
 821     }
 822 
 823     private void advance() {
 824         while (iter.hasNext()) {
 825             T elt = iter.next();
 826             if (filter.filter(elt)) {
 827                 next = elt;
 828                 return;
 829             }
 830         }
 831 
 832         next = null;
 833     }
 834 
 835     public boolean hasNext() {
 836         return next != null;
 837     }
 838 
 839     public T next() {
 840         if (next == null) {
 841             throw new NoSuchElementException();
 842         }
 843         T o = next;
 844         advance();
 845         return o;
 846     }
 847 
 848     public void remove() {
 849         throw new UnsupportedOperationException();
 850     }
 851 }