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) {
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)) {
|
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} 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()}, as opposed to any
69 * inherited classes or interfaces) may be registered. That is,
70 * suppose that the
71 * {@code com.mycompany.mypkg.GreenImageReaderProvider} class
72 * is a subclass of {@code javax.imageio.spi.ImageReaderSpi}.
73 * If a {@code GreenImageReaderProvider} instance is
74 * registered, it will be stored in the category defined by the
75 * {@code ImageReaderSpi} class. If a new instance of
76 * {@code GreenImageReaderProvider} 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}
81 * subdirectory is placed within the {@code META-INF} 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} which implements the
87 * {@code javax.imageio.spi.ImageReaderSpi} 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} instance with a
121 * set of categories taken from the {@code categories}
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} containing
126 * {@code Class} objects to be used to define categories.
127 *
128 * @exception IllegalArgumentException if
129 * {@code categories} is {@code null}, 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}
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} 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} 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} that yields provider objects
176 * for the given service, in some arbitrary order. The iterator
177 * will throw an {@code Error} 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} is {@code null}, 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} 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} that yields provider objects
215 * for the given service, in some arbitrary order. The iterator
216 * will throw an {@code Error} 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} is {@code null}, 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} of {@code Class} objects
234 * indicating the current set of categories. The iterator will be
235 * empty if no categories exist.
236 *
237 * @return an {@code Iterator} containing
238 * {@code Class} 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} implements the
266 * {@code RegisterableService} interface, its
267 * {@code onRegistration} method will be called. Its
268 * {@code onDeregistration} 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} is
281 * {@code null}.
282 * @exception IllegalArgumentException if there is no category
283 * corresponding to {@code category}.
284 * @exception ClassCastException if provider does not implement
285 * the {@code Class} defined by {@code category}.
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} it implements.
307 *
308 * <p> If {@code provider} implements the
309 * {@code RegisterableService} interface, its
310 * {@code onRegistration} method will be called once for each
311 * category it is registered under. Its
312 * {@code onDeregistration} 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} is {@code null}.
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} to the registry. Each provider is
335 * associated within each category present in the registry whose
336 * {@code Class} it implements.
337 *
338 * <p> For each entry of {@code providers} that implements
339 * the {@code RegisterableService} interface, its
340 * {@code onRegistration} method will be called once for each
341 * category it is registered under. Its
342 * {@code onDeregistration} 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}
350 * is {@code null} or contains a {@code null} 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} is returned. Otherwise, {@code true}
365 * is returned. If an object of the same class as
366 * {@code provider} but not equal (using {@code ==}) to
367 * {@code provider} is registered, it will not be
368 * deregistered.
369 *
370 * <p> If {@code provider} implements the
371 * {@code RegisterableService} interface, its
372 * {@code onDeregistration} 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} if the provider was previously
380 * registered in the same category category,
381 * {@code false} otherwise.
382 *
383 * @exception IllegalArgumentException if {@code provider} is
384 * {@code null}.
385 * @exception IllegalArgumentException if there is no category
386 * corresponding to {@code category}.
387 * @exception ClassCastException if provider does not implement
388 * the class defined by {@code category}.
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} is
412 * {@code null}.
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} if {@code provider} is currently
427 * registered.
428 *
429 * @param provider the service provider object to be queried.
430 *
431 * @return {@code true} if the given provider has been
432 * registered.
433 *
434 * @exception IllegalArgumentException if {@code provider} is
435 * {@code null}.
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} containing all registered
454 * service providers in the given category. If
455 * {@code useOrdering} is {@code false}, 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} 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} 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}.
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} 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} method of
490 * {@code ServiceRegistry} that takes a {@code Filter}.
491 *
492 * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
493 */
494 public interface Filter {
495
496 /**
497 * Returns {@code true} if the given
498 * {@code provider} object matches the criterion defined
499 * by this {@code Filter}.
500 *
501 * @param provider a service provider {@code Object}.
502 *
503 * @return true if the provider matches the criterion.
504 */
505 boolean filter(Object provider);
506 }
507
508 /**
509 * Returns an {@code Iterator} containing service provider
510 * objects within a given category that satisfy a criterion
511 * imposed by the supplied {@code ServiceRegistry.Filter}
512 * object's {@code filter} method.
513 *
514 * <p> The {@code useOrdering} argument controls the
515 * ordering of the results using the same rules as
516 * {@code getServiceProviders(Class, boolean)}.
517 *
518 * @param category the category to be retrieved from.
519 * @param filter an instance of {@code ServiceRegistry.Filter}
520 * whose {@code filter} method will be invoked.
521 * @param useOrdering {@code true} 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} 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}.
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}
547 * is returned.
548 *
549 * @param providerClass the {@code Class} 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} type, or {@code null} is none is
555 * present.
556 *
557 * @exception IllegalArgumentException if {@code providerClass} is
558 * {@code null}.
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} 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} methods when their
589 * {@code useOrdering} argument is {@code true}.
590 *
591 * @param category a {@code Class} 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} is preferred.
596 * @param <T> the type of the category.
597 *
598 * @return {@code true} if a previously unset ordering
599 * was established.
600 *
601 * @exception IllegalArgumentException if either provider is
602 * {@code null} or they are the same object.
603 * @exception IllegalArgumentException if there is no category
604 * corresponding to {@code category}.
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} is returned.
632 *
633 * <p> The ordering will be used by the
634 * {@code getServiceProviders} methods when their
635 * {@code useOrdering} argument is {@code true}.
636 *
637 * @param category a {@code Class} 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} was formerly preferred.
642 * @param <T> the type of the category.
643 *
644 * @return {@code true} if a previously set ordering was
645 * disestablished.
646 *
647 * @exception IllegalArgumentException if either provider is
648 * {@code null} or they are the same object.
649 * @exception IllegalArgumentException if there is no category
650 * corresponding to {@code category}.
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}.
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} 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) {
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} 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)) {
|