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