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 // 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 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 getSubRegistries(Object provider) { 229 List l = new ArrayList(); 230 Iterator iter = categoryMap.keySet().iterator(); 231 while (iter.hasNext()) { 232 Class c = (Class)iter.next(); 233 if (c.isAssignableFrom(provider.getClass())) { 234 l.add((SubRegistry)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 = (SubRegistry)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 regs = getSubRegistries(provider); 305 while (regs.hasNext()) { 306 SubRegistry reg = (SubRegistry)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 = (SubRegistry)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 regs = getSubRegistries(provider); 398 while (regs.hasNext()) { 399 SubRegistry reg = (SubRegistry)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 regs = getSubRegistries(provider); 421 while (regs.hasNext()) { 422 SubRegistry reg = (SubRegistry)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 = (SubRegistry)categoryMap.get(category); 455 if (reg == null) { 456 throw new IllegalArgumentException("category unknown!"); 457 } 458 return reg.getServiceProviders(useOrdering); 459 } 460 461 /** 462 * A simple filter interface used by 463 * <code>ServiceRegistry.getServiceProviders</code> to select 464 * providers matching an arbitrary criterion. Classes that 465 * implement this interface should be defined in order to make use 466 * of the <code>getServiceProviders</code> method of 467 * <code>ServiceRegistry</code> that takes a <code>Filter</code>. 468 * 469 * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean) 470 */ 471 public interface Filter { 472 473 /** 474 * Returns <code>true</code> if the given 475 * <code>provider</code> object matches the criterion defined 476 * by this <code>Filter</code>. 477 * 478 * @param provider a service provider <code>Object</code>. 479 * 480 * @return true if the provider matches the criterion. 481 */ 482 boolean filter(Object provider); 483 } 484 485 /** 486 * Returns an <code>Iterator</code> containing service provider 487 * objects within a given category that satisfy a criterion 488 * imposed by the supplied <code>ServiceRegistry.Filter</code> 489 * object's <code>filter</code> method. 490 * 491 * <p> The <code>useOrdering</code> argument controls the 492 * ordering of the results using the same rules as 493 * <code>getServiceProviders(Class, boolean)</code>. 494 * 495 * @param category the category to be retrieved from. 496 * @param filter an instance of <code>ServiceRegistry.Filter</code> 497 * whose <code>filter</code> method will be invoked. 498 * @param useOrdering <code>true</code> if pairwise orderings 499 * should be taken account in ordering the returned objects. 500 * @param <T> the type of the category. 501 * 502 * @return an <code>Iterator</code> containing service provider 503 * objects from the given category, possibly in order. 504 * 505 * @exception IllegalArgumentException if there is no category 506 * corresponding to <code>category</code>. 507 */ 508 public <T> Iterator<T> getServiceProviders(Class<T> category, 509 Filter filter, 510 boolean useOrdering) { 511 SubRegistry reg = (SubRegistry)categoryMap.get(category); 512 if (reg == null) { 513 throw new IllegalArgumentException("category unknown!"); 514 } 515 Iterator iter = getServiceProviders(category, useOrdering); 516 return new FilterIterator(iter, filter); 517 } 518 519 /** 520 * Returns the currently registered service provider object that 521 * is of the given class type. At most one object of a given 522 * class is allowed to be registered at any given time. If no 523 * registered object has the desired class type, <code>null</code> 524 * is returned. 525 * 526 * @param providerClass the <code>Class</code> of the desired 527 * service provider object. 528 * @param <T> the type of the provider. 529 * 530 * @return a currently registered service provider object with the 531 * desired <code>Class</code>type, or <code>null</code> is none is 532 * present. 533 * 534 * @exception IllegalArgumentException if <code>providerClass</code> is 535 * <code>null</code>. 536 */ 537 public <T> T getServiceProviderByClass(Class<T> providerClass) { 538 if (providerClass == null) { 539 throw new IllegalArgumentException("providerClass == null!"); 540 } 541 Iterator iter = categoryMap.keySet().iterator(); 542 while (iter.hasNext()) { 543 Class c = (Class)iter.next(); 544 if (c.isAssignableFrom(providerClass)) { 545 SubRegistry reg = (SubRegistry)categoryMap.get(c); 546 T provider = reg.getServiceProviderByClass(providerClass); 547 if (provider != null) { 548 return provider; 549 } 550 } 551 } 552 return null; 553 } 554 555 /** 556 * Sets a pairwise ordering between two service provider objects 557 * within a given category. If one or both objects are not 558 * currently registered within the given category, or if the 559 * desired ordering is already set, nothing happens and 560 * <code>false</code> is returned. If the providers previously 561 * were ordered in the reverse direction, that ordering is 562 * removed. 563 * 564 * <p> The ordering will be used by the 565 * <code>getServiceProviders</code> methods when their 566 * <code>useOrdering</code> argument is <code>true</code>. 567 * 568 * @param category a <code>Class</code> object indicating the 569 * category under which the preference is to be established. 570 * @param firstProvider the preferred provider. 571 * @param secondProvider the provider to which 572 * <code>firstProvider</code> is preferred. 573 * @param <T> the type of the category. 574 * 575 * @return <code>true</code> if a previously unset ordering 576 * was established. 577 * 578 * @exception IllegalArgumentException if either provider is 579 * <code>null</code> or they are the same object. 580 * @exception IllegalArgumentException if there is no category 581 * corresponding to <code>category</code>. 582 */ 583 public <T> boolean setOrdering(Class<T> category, 584 T firstProvider, 585 T secondProvider) { 586 if (firstProvider == null || secondProvider == null) { 587 throw new IllegalArgumentException("provider is null!"); 588 } 589 if (firstProvider == secondProvider) { 590 throw new IllegalArgumentException("providers are the same!"); 591 } 592 SubRegistry reg = (SubRegistry)categoryMap.get(category); 593 if (reg == null) { 594 throw new IllegalArgumentException("category unknown!"); 595 } 596 if (reg.contains(firstProvider) && 597 reg.contains(secondProvider)) { 598 return reg.setOrdering(firstProvider, secondProvider); 599 } 600 return false; 601 } 602 603 /** 604 * Sets a pairwise ordering between two service provider objects 605 * within a given category. If one or both objects are not 606 * currently registered within the given category, or if no 607 * ordering is currently set between them, nothing happens 608 * and <code>false</code> is returned. 609 * 610 * <p> The ordering will be used by the 611 * <code>getServiceProviders</code> methods when their 612 * <code>useOrdering</code> argument is <code>true</code>. 613 * 614 * @param category a <code>Class</code> object indicating the 615 * category under which the preference is to be disestablished. 616 * @param firstProvider the formerly preferred provider. 617 * @param secondProvider the provider to which 618 * <code>firstProvider</code> was formerly preferred. 619 * @param <T> the type of the category. 620 * 621 * @return <code>true</code> if a previously set ordering was 622 * disestablished. 623 * 624 * @exception IllegalArgumentException if either provider is 625 * <code>null</code> or they are the same object. 626 * @exception IllegalArgumentException if there is no category 627 * corresponding to <code>category</code>. 628 */ 629 public <T> boolean unsetOrdering(Class<T> category, 630 T firstProvider, 631 T secondProvider) { 632 if (firstProvider == null || secondProvider == null) { 633 throw new IllegalArgumentException("provider is null!"); 634 } 635 if (firstProvider == secondProvider) { 636 throw new IllegalArgumentException("providers are the same!"); 637 } 638 SubRegistry reg = (SubRegistry)categoryMap.get(category); 639 if (reg == null) { 640 throw new IllegalArgumentException("category unknown!"); 641 } 642 if (reg.contains(firstProvider) && 643 reg.contains(secondProvider)) { 644 return reg.unsetOrdering(firstProvider, secondProvider); 645 } 646 return false; 647 } 648 649 /** 650 * Deregisters all service provider object currently registered 651 * under the given category. 652 * 653 * @param category the category to be emptied. 654 * 655 * @exception IllegalArgumentException if there is no category 656 * corresponding to <code>category</code>. 657 */ 658 public void deregisterAll(Class<?> category) { 659 SubRegistry reg = (SubRegistry)categoryMap.get(category); 660 if (reg == null) { 661 throw new IllegalArgumentException("category unknown!"); 662 } 663 reg.clear(); 664 } 665 666 /** 667 * Deregisters all currently registered service providers from all 668 * categories. 669 */ 670 public void deregisterAll() { 671 Iterator iter = categoryMap.values().iterator(); 672 while (iter.hasNext()) { 673 SubRegistry reg = (SubRegistry)iter.next(); 674 reg.clear(); 675 } 676 } 677 678 /** 679 * Finalizes this object prior to garbage collection. The 680 * <code>deregisterAll</code> method is called to deregister all 681 * currently registered service providers. This method should not 682 * be called from application code. 683 * 684 * @exception Throwable if an error occurs during superclass 685 * finalization. 686 */ 687 public void finalize() throws Throwable { 688 deregisterAll(); 689 super.finalize(); 690 } 691 } 692 693 694 /** 695 * A portion of a registry dealing with a single superclass or 696 * interface. 697 */ 698 class SubRegistry { 699 700 ServiceRegistry registry; 701 702 Class category; 703 704 // Provider Objects organized by partial oridering 705 PartiallyOrderedSet poset = new PartiallyOrderedSet(); 706 707 // Class -> Provider Object of that class 708 Map<Class<?>,Object> map = new HashMap(); 709 710 public SubRegistry(ServiceRegistry registry, Class category) { 711 this.registry = registry; 712 this.category = category; 713 } 714 715 public boolean registerServiceProvider(Object provider) { 716 Object oprovider = map.get(provider.getClass()); 717 boolean present = oprovider != null; 718 719 if (present) { 720 deregisterServiceProvider(oprovider); 721 } 722 map.put(provider.getClass(), provider); 723 poset.add(provider); 724 if (provider instanceof RegisterableService) { 725 RegisterableService rs = (RegisterableService)provider; 726 rs.onRegistration(registry, category); 727 } 728 729 return !present; 730 } 731 732 /** 733 * If the provider was not previously registered, do nothing. 734 * 735 * @return true if the provider was previously registered. 736 */ 737 public boolean deregisterServiceProvider(Object provider) { 738 Object oprovider = map.get(provider.getClass()); 739 740 if (provider == oprovider) { 741 map.remove(provider.getClass()); 742 poset.remove(provider); 743 if (provider instanceof RegisterableService) { 744 RegisterableService rs = (RegisterableService)provider; 745 rs.onDeregistration(registry, category); 746 } 747 748 return true; 749 } 750 return false; 751 } 752 753 public boolean contains(Object provider) { 754 Object oprovider = map.get(provider.getClass()); 755 return oprovider == provider; 756 } 757 758 public boolean setOrdering(Object firstProvider, 759 Object secondProvider) { 760 return poset.setOrdering(firstProvider, secondProvider); 761 } 762 763 public boolean unsetOrdering(Object firstProvider, 764 Object secondProvider) { 765 return poset.unsetOrdering(firstProvider, secondProvider); 766 } 767 768 public Iterator getServiceProviders(boolean useOrdering) { 769 if (useOrdering) { 770 return poset.iterator(); 771 } else { 772 return map.values().iterator(); 773 } 774 } 775 776 public <T> T getServiceProviderByClass(Class<T> providerClass) { 777 return (T)map.get(providerClass); 778 } 779 780 public void clear() { 781 Iterator iter = map.values().iterator(); 782 while (iter.hasNext()) { 783 Object provider = iter.next(); 784 iter.remove(); 785 786 if (provider instanceof RegisterableService) { 787 RegisterableService rs = (RegisterableService)provider; 788 rs.onDeregistration(registry, category); 789 } 790 } 791 poset.clear(); 792 } 793 794 public void finalize() { 795 clear(); 796 } 797 } 798 799 800 /** 801 * A class for wrapping <code>Iterators</code> with a filter function. 802 * This provides an iterator for a subset without duplication. 803 */ 804 class FilterIterator<T> implements Iterator<T> { 805 806 private Iterator<T> iter; 807 private ServiceRegistry.Filter filter; 808 809 private T next = null; 810 811 public FilterIterator(Iterator<T> iter, 812 ServiceRegistry.Filter filter) { 813 this.iter = iter; 814 this.filter = filter; 815 advance(); 816 } 817 818 private void advance() { 819 while (iter.hasNext()) { 820 T elt = iter.next(); 821 if (filter.filter(elt)) { 822 next = elt; 823 return; 824 } 825 } 826 827 next = null; 828 } 829 830 public boolean hasNext() { 831 return next != null; 832 } 833 834 public T next() { 835 if (next == null) { 836 throw new NoSuchElementException(); 837 } 838 T o = next; 839 advance(); 840 return o; 841 } 842 843 public void remove() { 844 throw new UnsupportedOperationException(); 845 } 846 }