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