82 * <p> The service provider classes should be to be lightweight and
83 * quick to load. Implementations of these interfaces should avoid
84 * complex dependencies on other classes and on native code. The usual
85 * pattern for more complex services is to register a lightweight
86 * proxy for the heavyweight service.
87 *
88 * <p> An application may customize the contents of a registry as it
89 * sees fit, so long as it has the appropriate runtime permission.
90 *
91 * <p> For more details on declaring service providers, and the JAR
92 * format in general, see the <a
93 * href="../../../../technotes/guides/jar/jar.html">
94 * JAR File Specification</a>.
95 *
96 * @see RegisterableService
97 *
98 */
99 public class ServiceRegistry {
100
101 // Class -> Registry
102 private Map categoryMap = new HashMap();
103
104 /**
105 * Constructs a <code>ServiceRegistry</code> instance with a
106 * set of categories taken from the <code>categories</code>
107 * argument.
108 *
109 * @param categories an <code>Iterator</code> containing
110 * <code>Class</code> objects to be used to define categories.
111 *
112 * @exception IllegalArgumentException if
113 * <code>categories</code> is <code>null</code>.
114 */
115 public ServiceRegistry(Iterator<Class<?>> categories) {
116 if (categories == null) {
117 throw new IllegalArgumentException("categories == null!");
118 }
119 while (categories.hasNext()) {
120 Class category = (Class)categories.next();
121 SubRegistry reg = new SubRegistry(this, category);
122 categoryMap.put(category, reg);
123 }
124 }
125
126 // The following two methods expose functionality from
127 // sun.misc.Service. If that class is made public, they may be
128 // removed.
129 //
130 // The sun.misc.ServiceConfigurationError class may also be
131 // exposed, in which case the references to 'an
132 // <code>Error</code>' below should be changed to 'a
133 // <code>ServiceConfigurationError</code>'.
134
135 /**
136 * Searches for implementations of a particular service class
137 * using the given class loader.
138 *
139 * <p> This method transforms the name of the given service class
140 * into a provider-configuration filename as described in the
200 *
201 * @exception IllegalArgumentException if
202 * <code>providerClass</code> is <code>null</code>.
203 */
204 public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
205 if (providerClass == null) {
206 throw new IllegalArgumentException("providerClass == null!");
207 }
208 return ServiceLoader.load(providerClass).iterator();
209 }
210
211 /**
212 * Returns an <code>Iterator</code> of <code>Class</code> objects
213 * indicating the current set of categories. The iterator will be
214 * empty if no categories exist.
215 *
216 * @return an <code>Iterator</code> containing
217 * <code>Class</code>objects.
218 */
219 public Iterator<Class<?>> getCategories() {
220 Set keySet = categoryMap.keySet();
221 return keySet.iterator();
222 }
223
224 /**
225 * Returns an Iterator containing the subregistries to which the
226 * provider belongs.
227 */
228 private Iterator getSubRegistries(Object provider) {
229 List l = new ArrayList();
230 Iterator iter = categoryMap.keySet().iterator();
231 while (iter.hasNext()) {
232 Class c = (Class)iter.next();
233 if (c.isAssignableFrom(provider.getClass())) {
234 l.add((SubRegistry)categoryMap.get(c));
235 }
236 }
237 return l.iterator();
238 }
239
240 /**
241 * Adds a service provider object to the registry. The provider
242 * is associated with the given category.
243 *
244 * <p> If <code>provider</code> implements the
245 * <code>RegisterableService</code> interface, its
246 * <code>onRegistration</code> method will be called. Its
247 * <code>onDeregistration</code> method will be called each time
248 * it is deregistered from a category, for example if a
249 * category is removed or the registry is garbage collected.
250 *
251 * @param provider the service provide object to be registered.
252 * @param category the category under which to register the
253 * provider.
254 * @param <T> the type of the provider.
255 *
256 * @return true if no provider of the same class was previously
257 * registered in the same category category.
258 *
259 * @exception IllegalArgumentException if <code>provider</code> is
260 * <code>null</code>.
261 * @exception IllegalArgumentException if there is no category
262 * corresponding to <code>category</code>.
263 * @exception ClassCastException if provider does not implement
264 * the <code>Class</code> defined by <code>category</code>.
265 */
266 public <T> boolean registerServiceProvider(T provider,
267 Class<T> category) {
268 if (provider == null) {
269 throw new IllegalArgumentException("provider == null!");
270 }
271 SubRegistry reg = (SubRegistry)categoryMap.get(category);
272 if (reg == null) {
273 throw new IllegalArgumentException("category unknown!");
274 }
275 if (!category.isAssignableFrom(provider.getClass())) {
276 throw new ClassCastException();
277 }
278
279 return reg.registerServiceProvider(provider);
280 }
281
282 /**
283 * Adds a service provider object to the registry. The provider
284 * is associated within each category present in the registry
285 * whose <code>Class</code> it implements.
286 *
287 * <p> If <code>provider</code> implements the
288 * <code>RegisterableService</code> interface, its
289 * <code>onRegistration</code> method will be called once for each
290 * category it is registered under. Its
291 * <code>onDeregistration</code> method will be called each time
292 * it is deregistered from a category or when the registry is
293 * finalized.
294 *
295 * @param provider the service provider object to be registered.
296 *
297 * @exception IllegalArgumentException if
298 * <code>provider</code> is <code>null</code>.
299 */
300 public void registerServiceProvider(Object provider) {
301 if (provider == null) {
302 throw new IllegalArgumentException("provider == null!");
303 }
304 Iterator regs = getSubRegistries(provider);
305 while (regs.hasNext()) {
306 SubRegistry reg = (SubRegistry)regs.next();
307 reg.registerServiceProvider(provider);
308 }
309 }
310
311 /**
312 * Adds a set of service provider objects, taken from an
313 * <code>Iterator</code> to the registry. Each provider is
314 * associated within each category present in the registry whose
315 * <code>Class</code> it implements.
316 *
317 * <p> For each entry of <code>providers</code> that implements
318 * the <code>RegisterableService</code> interface, its
319 * <code>onRegistration</code> method will be called once for each
320 * category it is registered under. Its
321 * <code>onDeregistration</code> method will be called each time
322 * it is deregistered from a category or when the registry is
323 * finalized.
324 *
325 * @param providers an Iterator containing service provider
326 * objects to be registered.
354 * @param category the category from which to deregister the
355 * provider.
356 * @param <T> the type of the provider.
357 *
358 * @return <code>true</code> if the provider was previously
359 * registered in the same category category,
360 * <code>false</code> otherwise.
361 *
362 * @exception IllegalArgumentException if <code>provider</code> is
363 * <code>null</code>.
364 * @exception IllegalArgumentException if there is no category
365 * corresponding to <code>category</code>.
366 * @exception ClassCastException if provider does not implement
367 * the class defined by <code>category</code>.
368 */
369 public <T> boolean deregisterServiceProvider(T provider,
370 Class<T> category) {
371 if (provider == null) {
372 throw new IllegalArgumentException("provider == null!");
373 }
374 SubRegistry reg = (SubRegistry)categoryMap.get(category);
375 if (reg == null) {
376 throw new IllegalArgumentException("category unknown!");
377 }
378 if (!category.isAssignableFrom(provider.getClass())) {
379 throw new ClassCastException();
380 }
381 return reg.deregisterServiceProvider(provider);
382 }
383
384 /**
385 * Removes a service provider object from all categories that
386 * contain it.
387 *
388 * @param provider the service provider object to be deregistered.
389 *
390 * @exception IllegalArgumentException if <code>provider</code> is
391 * <code>null</code>.
392 */
393 public void deregisterServiceProvider(Object provider) {
394 if (provider == null) {
395 throw new IllegalArgumentException("provider == null!");
396 }
397 Iterator regs = getSubRegistries(provider);
398 while (regs.hasNext()) {
399 SubRegistry reg = (SubRegistry)regs.next();
400 reg.deregisterServiceProvider(provider);
401 }
402 }
403
404 /**
405 * Returns <code>true</code> if <code>provider</code> is currently
406 * registered.
407 *
408 * @param provider the service provider object to be queried.
409 *
410 * @return <code>true</code> if the given provider has been
411 * registered.
412 *
413 * @exception IllegalArgumentException if <code>provider</code> is
414 * <code>null</code>.
415 */
416 public boolean contains(Object provider) {
417 if (provider == null) {
418 throw new IllegalArgumentException("provider == null!");
419 }
420 Iterator regs = getSubRegistries(provider);
421 while (regs.hasNext()) {
422 SubRegistry reg = (SubRegistry)regs.next();
423 if (reg.contains(provider)) {
424 return true;
425 }
426 }
427
428 return false;
429 }
430
431 /**
432 * Returns an <code>Iterator</code> containing all registered
433 * service providers in the given category. If
434 * <code>useOrdering</code> is <code>false</code>, the iterator
435 * will return all of the server provider objects in an arbitrary
436 * order. Otherwise, the ordering will respect any pairwise
437 * orderings that have been set. If the graph of pairwise
438 * orderings contains cycles, any providers that belong to a cycle
439 * will not be returned.
440 *
441 * @param category the category to be retrieved from.
442 * @param useOrdering <code>true</code> if pairwise orderings
443 * should be taken account in ordering the returned objects.
444 * @param <T> the type of the category.
445 *
446 * @return an <code>Iterator</code> containing service provider
447 * objects from the given category, possibly in order.
448 *
449 * @exception IllegalArgumentException if there is no category
450 * corresponding to <code>category</code>.
451 */
452 public <T> Iterator<T> getServiceProviders(Class<T> category,
453 boolean useOrdering) {
454 SubRegistry reg = (SubRegistry)categoryMap.get(category);
455 if (reg == null) {
456 throw new IllegalArgumentException("category unknown!");
457 }
458 return reg.getServiceProviders(useOrdering);
459 }
460
461 /**
462 * A simple filter interface used by
463 * <code>ServiceRegistry.getServiceProviders</code> to select
464 * providers matching an arbitrary criterion. Classes that
465 * implement this interface should be defined in order to make use
466 * of the <code>getServiceProviders</code> method of
467 * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
468 *
469 * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
470 */
471 public interface Filter {
472
473 /**
474 * Returns <code>true</code> if the given
475 * <code>provider</code> object matches the criterion defined
476 * by this <code>Filter</code>.
477 *
478 * @param provider a service provider <code>Object</code>.
491 * <p> The <code>useOrdering</code> argument controls the
492 * ordering of the results using the same rules as
493 * <code>getServiceProviders(Class, boolean)</code>.
494 *
495 * @param category the category to be retrieved from.
496 * @param filter an instance of <code>ServiceRegistry.Filter</code>
497 * whose <code>filter</code> method will be invoked.
498 * @param useOrdering <code>true</code> if pairwise orderings
499 * should be taken account in ordering the returned objects.
500 * @param <T> the type of the category.
501 *
502 * @return an <code>Iterator</code> containing service provider
503 * objects from the given category, possibly in order.
504 *
505 * @exception IllegalArgumentException if there is no category
506 * corresponding to <code>category</code>.
507 */
508 public <T> Iterator<T> getServiceProviders(Class<T> category,
509 Filter filter,
510 boolean useOrdering) {
511 SubRegistry reg = (SubRegistry)categoryMap.get(category);
512 if (reg == null) {
513 throw new IllegalArgumentException("category unknown!");
514 }
515 Iterator iter = getServiceProviders(category, useOrdering);
516 return new FilterIterator(iter, filter);
517 }
518
519 /**
520 * Returns the currently registered service provider object that
521 * is of the given class type. At most one object of a given
522 * class is allowed to be registered at any given time. If no
523 * registered object has the desired class type, <code>null</code>
524 * is returned.
525 *
526 * @param providerClass the <code>Class</code> of the desired
527 * service provider object.
528 * @param <T> the type of the provider.
529 *
530 * @return a currently registered service provider object with the
531 * desired <code>Class</code>type, or <code>null</code> is none is
532 * present.
533 *
534 * @exception IllegalArgumentException if <code>providerClass</code> is
535 * <code>null</code>.
536 */
537 public <T> T getServiceProviderByClass(Class<T> providerClass) {
538 if (providerClass == null) {
539 throw new IllegalArgumentException("providerClass == null!");
540 }
541 Iterator iter = categoryMap.keySet().iterator();
542 while (iter.hasNext()) {
543 Class c = (Class)iter.next();
544 if (c.isAssignableFrom(providerClass)) {
545 SubRegistry reg = (SubRegistry)categoryMap.get(c);
546 T provider = reg.getServiceProviderByClass(providerClass);
547 if (provider != null) {
548 return provider;
549 }
550 }
551 }
552 return null;
553 }
554
555 /**
556 * Sets a pairwise ordering between two service provider objects
557 * within a given category. If one or both objects are not
558 * currently registered within the given category, or if the
559 * desired ordering is already set, nothing happens and
560 * <code>false</code> is returned. If the providers previously
561 * were ordered in the reverse direction, that ordering is
562 * removed.
563 *
564 * <p> The ordering will be used by the
565 * <code>getServiceProviders</code> methods when their
572 * <code>firstProvider</code> is preferred.
573 * @param <T> the type of the category.
574 *
575 * @return <code>true</code> if a previously unset ordering
576 * was established.
577 *
578 * @exception IllegalArgumentException if either provider is
579 * <code>null</code> or they are the same object.
580 * @exception IllegalArgumentException if there is no category
581 * corresponding to <code>category</code>.
582 */
583 public <T> boolean setOrdering(Class<T> category,
584 T firstProvider,
585 T secondProvider) {
586 if (firstProvider == null || secondProvider == null) {
587 throw new IllegalArgumentException("provider is null!");
588 }
589 if (firstProvider == secondProvider) {
590 throw new IllegalArgumentException("providers are the same!");
591 }
592 SubRegistry reg = (SubRegistry)categoryMap.get(category);
593 if (reg == null) {
594 throw new IllegalArgumentException("category unknown!");
595 }
596 if (reg.contains(firstProvider) &&
597 reg.contains(secondProvider)) {
598 return reg.setOrdering(firstProvider, secondProvider);
599 }
600 return false;
601 }
602
603 /**
604 * Sets a pairwise ordering between two service provider objects
605 * within a given category. If one or both objects are not
606 * currently registered within the given category, or if no
607 * ordering is currently set between them, nothing happens
608 * and <code>false</code> is returned.
609 *
610 * <p> The ordering will be used by the
611 * <code>getServiceProviders</code> methods when their
612 * <code>useOrdering</code> argument is <code>true</code>.
618 * <code>firstProvider</code> was formerly preferred.
619 * @param <T> the type of the category.
620 *
621 * @return <code>true</code> if a previously set ordering was
622 * disestablished.
623 *
624 * @exception IllegalArgumentException if either provider is
625 * <code>null</code> or they are the same object.
626 * @exception IllegalArgumentException if there is no category
627 * corresponding to <code>category</code>.
628 */
629 public <T> boolean unsetOrdering(Class<T> category,
630 T firstProvider,
631 T secondProvider) {
632 if (firstProvider == null || secondProvider == null) {
633 throw new IllegalArgumentException("provider is null!");
634 }
635 if (firstProvider == secondProvider) {
636 throw new IllegalArgumentException("providers are the same!");
637 }
638 SubRegistry reg = (SubRegistry)categoryMap.get(category);
639 if (reg == null) {
640 throw new IllegalArgumentException("category unknown!");
641 }
642 if (reg.contains(firstProvider) &&
643 reg.contains(secondProvider)) {
644 return reg.unsetOrdering(firstProvider, secondProvider);
645 }
646 return false;
647 }
648
649 /**
650 * Deregisters all service provider object currently registered
651 * under the given category.
652 *
653 * @param category the category to be emptied.
654 *
655 * @exception IllegalArgumentException if there is no category
656 * corresponding to <code>category</code>.
657 */
658 public void deregisterAll(Class<?> category) {
659 SubRegistry reg = (SubRegistry)categoryMap.get(category);
660 if (reg == null) {
661 throw new IllegalArgumentException("category unknown!");
662 }
663 reg.clear();
664 }
665
666 /**
667 * Deregisters all currently registered service providers from all
668 * categories.
669 */
670 public void deregisterAll() {
671 Iterator iter = categoryMap.values().iterator();
672 while (iter.hasNext()) {
673 SubRegistry reg = (SubRegistry)iter.next();
674 reg.clear();
675 }
676 }
677
678 /**
679 * Finalizes this object prior to garbage collection. The
680 * <code>deregisterAll</code> method is called to deregister all
681 * currently registered service providers. This method should not
682 * be called from application code.
683 *
684 * @exception Throwable if an error occurs during superclass
685 * finalization.
686 */
687 public void finalize() throws Throwable {
688 deregisterAll();
689 super.finalize();
690 }
691 }
692
693
694 /**
695 * A portion of a registry dealing with a single superclass or
696 * interface.
697 */
698 class SubRegistry {
699
700 ServiceRegistry registry;
701
702 Class category;
703
704 // Provider Objects organized by partial oridering
705 PartiallyOrderedSet poset = new PartiallyOrderedSet();
706
707 // Class -> Provider Object of that class
708 Map<Class<?>,Object> map = new HashMap();
709
710 public SubRegistry(ServiceRegistry registry, Class category) {
711 this.registry = registry;
712 this.category = category;
713 }
714
715 public boolean registerServiceProvider(Object provider) {
716 Object oprovider = map.get(provider.getClass());
717 boolean present = oprovider != null;
718
719 if (present) {
720 deregisterServiceProvider(oprovider);
721 }
722 map.put(provider.getClass(), provider);
723 poset.add(provider);
724 if (provider instanceof RegisterableService) {
725 RegisterableService rs = (RegisterableService)provider;
726 rs.onRegistration(registry, category);
727 }
728
729 return !present;
730 }
748 return true;
749 }
750 return false;
751 }
752
753 public boolean contains(Object provider) {
754 Object oprovider = map.get(provider.getClass());
755 return oprovider == provider;
756 }
757
758 public boolean setOrdering(Object firstProvider,
759 Object secondProvider) {
760 return poset.setOrdering(firstProvider, secondProvider);
761 }
762
763 public boolean unsetOrdering(Object firstProvider,
764 Object secondProvider) {
765 return poset.unsetOrdering(firstProvider, secondProvider);
766 }
767
768 public Iterator getServiceProviders(boolean useOrdering) {
769 if (useOrdering) {
770 return poset.iterator();
771 } else {
772 return map.values().iterator();
773 }
774 }
775
776 public <T> T getServiceProviderByClass(Class<T> providerClass) {
777 return (T)map.get(providerClass);
778 }
779
780 public void clear() {
781 Iterator iter = map.values().iterator();
782 while (iter.hasNext()) {
783 Object provider = iter.next();
784 iter.remove();
785
786 if (provider instanceof RegisterableService) {
787 RegisterableService rs = (RegisterableService)provider;
788 rs.onDeregistration(registry, category);
789 }
790 }
791 poset.clear();
792 }
793
794 public void finalize() {
795 clear();
796 }
797 }
798
799
800 /**
801 * A class for wrapping <code>Iterators</code> with a filter function.
802 * This provides an iterator for a subset without duplication.
803 */
804 class FilterIterator<T> implements Iterator<T> {
805
806 private Iterator<T> iter;
807 private ServiceRegistry.Filter filter;
808
809 private T next = null;
810
811 public FilterIterator(Iterator<T> iter,
812 ServiceRegistry.Filter filter) {
813 this.iter = iter;
814 this.filter = filter;
815 advance();
816 }
817
818 private void advance() {
819 while (iter.hasNext()) {
820 T elt = iter.next();
821 if (filter.filter(elt)) {
822 next = elt;
823 return;
824 }
825 }
826
827 next = null;
828 }
829
830 public boolean hasNext() {
831 return next != null;
|
82 * <p> The service provider classes should be to be lightweight and
83 * quick to load. Implementations of these interfaces should avoid
84 * complex dependencies on other classes and on native code. The usual
85 * pattern for more complex services is to register a lightweight
86 * proxy for the heavyweight service.
87 *
88 * <p> An application may customize the contents of a registry as it
89 * sees fit, so long as it has the appropriate runtime permission.
90 *
91 * <p> For more details on declaring service providers, and the JAR
92 * format in general, see the <a
93 * href="../../../../technotes/guides/jar/jar.html">
94 * JAR File Specification</a>.
95 *
96 * @see RegisterableService
97 *
98 */
99 public class ServiceRegistry {
100
101 // Class -> Registry
102 private Map<Class<?>, SubRegistry> categoryMap = new HashMap<>();
103
104 /**
105 * Constructs a <code>ServiceRegistry</code> instance with a
106 * set of categories taken from the <code>categories</code>
107 * argument.
108 *
109 * @param categories an <code>Iterator</code> containing
110 * <code>Class</code> objects to be used to define categories.
111 *
112 * @exception IllegalArgumentException if
113 * <code>categories</code> is <code>null</code>.
114 */
115 public ServiceRegistry(Iterator<Class<?>> categories) {
116 if (categories == null) {
117 throw new IllegalArgumentException("categories == null!");
118 }
119 while (categories.hasNext()) {
120 Class<?> category = categories.next();
121 SubRegistry reg = new SubRegistry(this, category);
122 categoryMap.put(category, reg);
123 }
124 }
125
126 // The following two methods expose functionality from
127 // sun.misc.Service. If that class is made public, they may be
128 // removed.
129 //
130 // The sun.misc.ServiceConfigurationError class may also be
131 // exposed, in which case the references to 'an
132 // <code>Error</code>' below should be changed to 'a
133 // <code>ServiceConfigurationError</code>'.
134
135 /**
136 * Searches for implementations of a particular service class
137 * using the given class loader.
138 *
139 * <p> This method transforms the name of the given service class
140 * into a provider-configuration filename as described in the
200 *
201 * @exception IllegalArgumentException if
202 * <code>providerClass</code> is <code>null</code>.
203 */
204 public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
205 if (providerClass == null) {
206 throw new IllegalArgumentException("providerClass == null!");
207 }
208 return ServiceLoader.load(providerClass).iterator();
209 }
210
211 /**
212 * Returns an <code>Iterator</code> of <code>Class</code> objects
213 * indicating the current set of categories. The iterator will be
214 * empty if no categories exist.
215 *
216 * @return an <code>Iterator</code> containing
217 * <code>Class</code>objects.
218 */
219 public Iterator<Class<?>> getCategories() {
220 Set<Class<?>> keySet = categoryMap.keySet();
221 return keySet.iterator();
222 }
223
224 /**
225 * Returns an Iterator containing the subregistries to which the
226 * provider belongs.
227 */
228 private Iterator<SubRegistry> getSubRegistries(Object provider) {
229 List<SubRegistry> l = new ArrayList<>();
230 Iterator<Class<?>> iter = categoryMap.keySet().iterator();
231 while (iter.hasNext()) {
232 Class<?> c = iter.next();
233 if (c.isAssignableFrom(provider.getClass())) {
234 l.add(categoryMap.get(c));
235 }
236 }
237 return l.iterator();
238 }
239
240 /**
241 * Adds a service provider object to the registry. The provider
242 * is associated with the given category.
243 *
244 * <p> If <code>provider</code> implements the
245 * <code>RegisterableService</code> interface, its
246 * <code>onRegistration</code> method will be called. Its
247 * <code>onDeregistration</code> method will be called each time
248 * it is deregistered from a category, for example if a
249 * category is removed or the registry is garbage collected.
250 *
251 * @param provider the service provide object to be registered.
252 * @param category the category under which to register the
253 * provider.
254 * @param <T> the type of the provider.
255 *
256 * @return true if no provider of the same class was previously
257 * registered in the same category category.
258 *
259 * @exception IllegalArgumentException if <code>provider</code> is
260 * <code>null</code>.
261 * @exception IllegalArgumentException if there is no category
262 * corresponding to <code>category</code>.
263 * @exception ClassCastException if provider does not implement
264 * the <code>Class</code> defined by <code>category</code>.
265 */
266 public <T> boolean registerServiceProvider(T provider,
267 Class<T> category) {
268 if (provider == null) {
269 throw new IllegalArgumentException("provider == null!");
270 }
271 SubRegistry reg = categoryMap.get(category);
272 if (reg == null) {
273 throw new IllegalArgumentException("category unknown!");
274 }
275 if (!category.isAssignableFrom(provider.getClass())) {
276 throw new ClassCastException();
277 }
278
279 return reg.registerServiceProvider(provider);
280 }
281
282 /**
283 * Adds a service provider object to the registry. The provider
284 * is associated within each category present in the registry
285 * whose <code>Class</code> it implements.
286 *
287 * <p> If <code>provider</code> implements the
288 * <code>RegisterableService</code> interface, its
289 * <code>onRegistration</code> method will be called once for each
290 * category it is registered under. Its
291 * <code>onDeregistration</code> method will be called each time
292 * it is deregistered from a category or when the registry is
293 * finalized.
294 *
295 * @param provider the service provider object to be registered.
296 *
297 * @exception IllegalArgumentException if
298 * <code>provider</code> is <code>null</code>.
299 */
300 public void registerServiceProvider(Object provider) {
301 if (provider == null) {
302 throw new IllegalArgumentException("provider == null!");
303 }
304 Iterator<SubRegistry> regs = getSubRegistries(provider);
305 while (regs.hasNext()) {
306 SubRegistry reg = regs.next();
307 reg.registerServiceProvider(provider);
308 }
309 }
310
311 /**
312 * Adds a set of service provider objects, taken from an
313 * <code>Iterator</code> to the registry. Each provider is
314 * associated within each category present in the registry whose
315 * <code>Class</code> it implements.
316 *
317 * <p> For each entry of <code>providers</code> that implements
318 * the <code>RegisterableService</code> interface, its
319 * <code>onRegistration</code> method will be called once for each
320 * category it is registered under. Its
321 * <code>onDeregistration</code> method will be called each time
322 * it is deregistered from a category or when the registry is
323 * finalized.
324 *
325 * @param providers an Iterator containing service provider
326 * objects to be registered.
354 * @param category the category from which to deregister the
355 * provider.
356 * @param <T> the type of the provider.
357 *
358 * @return <code>true</code> if the provider was previously
359 * registered in the same category category,
360 * <code>false</code> otherwise.
361 *
362 * @exception IllegalArgumentException if <code>provider</code> is
363 * <code>null</code>.
364 * @exception IllegalArgumentException if there is no category
365 * corresponding to <code>category</code>.
366 * @exception ClassCastException if provider does not implement
367 * the class defined by <code>category</code>.
368 */
369 public <T> boolean deregisterServiceProvider(T provider,
370 Class<T> category) {
371 if (provider == null) {
372 throw new IllegalArgumentException("provider == null!");
373 }
374 SubRegistry reg = categoryMap.get(category);
375 if (reg == null) {
376 throw new IllegalArgumentException("category unknown!");
377 }
378 if (!category.isAssignableFrom(provider.getClass())) {
379 throw new ClassCastException();
380 }
381 return reg.deregisterServiceProvider(provider);
382 }
383
384 /**
385 * Removes a service provider object from all categories that
386 * contain it.
387 *
388 * @param provider the service provider object to be deregistered.
389 *
390 * @exception IllegalArgumentException if <code>provider</code> is
391 * <code>null</code>.
392 */
393 public void deregisterServiceProvider(Object provider) {
394 if (provider == null) {
395 throw new IllegalArgumentException("provider == null!");
396 }
397 Iterator<SubRegistry> regs = getSubRegistries(provider);
398 while (regs.hasNext()) {
399 SubRegistry reg = regs.next();
400 reg.deregisterServiceProvider(provider);
401 }
402 }
403
404 /**
405 * Returns <code>true</code> if <code>provider</code> is currently
406 * registered.
407 *
408 * @param provider the service provider object to be queried.
409 *
410 * @return <code>true</code> if the given provider has been
411 * registered.
412 *
413 * @exception IllegalArgumentException if <code>provider</code> is
414 * <code>null</code>.
415 */
416 public boolean contains(Object provider) {
417 if (provider == null) {
418 throw new IllegalArgumentException("provider == null!");
419 }
420 Iterator<SubRegistry> regs = getSubRegistries(provider);
421 while (regs.hasNext()) {
422 SubRegistry reg = regs.next();
423 if (reg.contains(provider)) {
424 return true;
425 }
426 }
427
428 return false;
429 }
430
431 /**
432 * Returns an <code>Iterator</code> containing all registered
433 * service providers in the given category. If
434 * <code>useOrdering</code> is <code>false</code>, the iterator
435 * will return all of the server provider objects in an arbitrary
436 * order. Otherwise, the ordering will respect any pairwise
437 * orderings that have been set. If the graph of pairwise
438 * orderings contains cycles, any providers that belong to a cycle
439 * will not be returned.
440 *
441 * @param category the category to be retrieved from.
442 * @param useOrdering <code>true</code> if pairwise orderings
443 * should be taken account in ordering the returned objects.
444 * @param <T> the type of the category.
445 *
446 * @return an <code>Iterator</code> containing service provider
447 * objects from the given category, possibly in order.
448 *
449 * @exception IllegalArgumentException if there is no category
450 * corresponding to <code>category</code>.
451 */
452 public <T> Iterator<T> getServiceProviders(Class<T> category,
453 boolean useOrdering) {
454 SubRegistry reg = categoryMap.get(category);
455 if (reg == null) {
456 throw new IllegalArgumentException("category unknown!");
457 }
458 @SuppressWarnings("unchecked")
459 Iterator<T> it = (Iterator<T>)reg.getServiceProviders(useOrdering);
460 return it;
461 }
462
463 /**
464 * A simple filter interface used by
465 * <code>ServiceRegistry.getServiceProviders</code> to select
466 * providers matching an arbitrary criterion. Classes that
467 * implement this interface should be defined in order to make use
468 * of the <code>getServiceProviders</code> method of
469 * <code>ServiceRegistry</code> that takes a <code>Filter</code>.
470 *
471 * @see ServiceRegistry#getServiceProviders(Class, ServiceRegistry.Filter, boolean)
472 */
473 public interface Filter {
474
475 /**
476 * Returns <code>true</code> if the given
477 * <code>provider</code> object matches the criterion defined
478 * by this <code>Filter</code>.
479 *
480 * @param provider a service provider <code>Object</code>.
493 * <p> The <code>useOrdering</code> argument controls the
494 * ordering of the results using the same rules as
495 * <code>getServiceProviders(Class, boolean)</code>.
496 *
497 * @param category the category to be retrieved from.
498 * @param filter an instance of <code>ServiceRegistry.Filter</code>
499 * whose <code>filter</code> method will be invoked.
500 * @param useOrdering <code>true</code> if pairwise orderings
501 * should be taken account in ordering the returned objects.
502 * @param <T> the type of the category.
503 *
504 * @return an <code>Iterator</code> containing service provider
505 * objects from the given category, possibly in order.
506 *
507 * @exception IllegalArgumentException if there is no category
508 * corresponding to <code>category</code>.
509 */
510 public <T> Iterator<T> getServiceProviders(Class<T> category,
511 Filter filter,
512 boolean useOrdering) {
513 SubRegistry reg = categoryMap.get(category);
514 if (reg == null) {
515 throw new IllegalArgumentException("category unknown!");
516 }
517 Iterator<T> iter = getServiceProviders(category, useOrdering);
518 return new FilterIterator<>(iter, filter);
519 }
520
521 /**
522 * Returns the currently registered service provider object that
523 * is of the given class type. At most one object of a given
524 * class is allowed to be registered at any given time. If no
525 * registered object has the desired class type, <code>null</code>
526 * is returned.
527 *
528 * @param providerClass the <code>Class</code> of the desired
529 * service provider object.
530 * @param <T> the type of the provider.
531 *
532 * @return a currently registered service provider object with the
533 * desired <code>Class</code>type, or <code>null</code> is none is
534 * present.
535 *
536 * @exception IllegalArgumentException if <code>providerClass</code> is
537 * <code>null</code>.
538 */
539 public <T> T getServiceProviderByClass(Class<T> providerClass) {
540 if (providerClass == null) {
541 throw new IllegalArgumentException("providerClass == null!");
542 }
543 Iterator<Class<?>> iter = categoryMap.keySet().iterator();
544 while (iter.hasNext()) {
545 Class<?> c = iter.next();
546 if (c.isAssignableFrom(providerClass)) {
547 SubRegistry reg = categoryMap.get(c);
548 T provider = reg.getServiceProviderByClass(providerClass);
549 if (provider != null) {
550 return provider;
551 }
552 }
553 }
554 return null;
555 }
556
557 /**
558 * Sets a pairwise ordering between two service provider objects
559 * within a given category. If one or both objects are not
560 * currently registered within the given category, or if the
561 * desired ordering is already set, nothing happens and
562 * <code>false</code> is returned. If the providers previously
563 * were ordered in the reverse direction, that ordering is
564 * removed.
565 *
566 * <p> The ordering will be used by the
567 * <code>getServiceProviders</code> methods when their
574 * <code>firstProvider</code> is preferred.
575 * @param <T> the type of the category.
576 *
577 * @return <code>true</code> if a previously unset ordering
578 * was established.
579 *
580 * @exception IllegalArgumentException if either provider is
581 * <code>null</code> or they are the same object.
582 * @exception IllegalArgumentException if there is no category
583 * corresponding to <code>category</code>.
584 */
585 public <T> boolean setOrdering(Class<T> category,
586 T firstProvider,
587 T secondProvider) {
588 if (firstProvider == null || secondProvider == null) {
589 throw new IllegalArgumentException("provider is null!");
590 }
591 if (firstProvider == secondProvider) {
592 throw new IllegalArgumentException("providers are the same!");
593 }
594 SubRegistry reg = categoryMap.get(category);
595 if (reg == null) {
596 throw new IllegalArgumentException("category unknown!");
597 }
598 if (reg.contains(firstProvider) &&
599 reg.contains(secondProvider)) {
600 return reg.setOrdering(firstProvider, secondProvider);
601 }
602 return false;
603 }
604
605 /**
606 * Sets a pairwise ordering between two service provider objects
607 * within a given category. If one or both objects are not
608 * currently registered within the given category, or if no
609 * ordering is currently set between them, nothing happens
610 * and <code>false</code> is returned.
611 *
612 * <p> The ordering will be used by the
613 * <code>getServiceProviders</code> methods when their
614 * <code>useOrdering</code> argument is <code>true</code>.
620 * <code>firstProvider</code> was formerly preferred.
621 * @param <T> the type of the category.
622 *
623 * @return <code>true</code> if a previously set ordering was
624 * disestablished.
625 *
626 * @exception IllegalArgumentException if either provider is
627 * <code>null</code> or they are the same object.
628 * @exception IllegalArgumentException if there is no category
629 * corresponding to <code>category</code>.
630 */
631 public <T> boolean unsetOrdering(Class<T> category,
632 T firstProvider,
633 T secondProvider) {
634 if (firstProvider == null || secondProvider == null) {
635 throw new IllegalArgumentException("provider is null!");
636 }
637 if (firstProvider == secondProvider) {
638 throw new IllegalArgumentException("providers are the same!");
639 }
640 SubRegistry reg = categoryMap.get(category);
641 if (reg == null) {
642 throw new IllegalArgumentException("category unknown!");
643 }
644 if (reg.contains(firstProvider) &&
645 reg.contains(secondProvider)) {
646 return reg.unsetOrdering(firstProvider, secondProvider);
647 }
648 return false;
649 }
650
651 /**
652 * Deregisters all service provider object currently registered
653 * under the given category.
654 *
655 * @param category the category to be emptied.
656 *
657 * @exception IllegalArgumentException if there is no category
658 * corresponding to <code>category</code>.
659 */
660 public void deregisterAll(Class<?> category) {
661 SubRegistry reg = categoryMap.get(category);
662 if (reg == null) {
663 throw new IllegalArgumentException("category unknown!");
664 }
665 reg.clear();
666 }
667
668 /**
669 * Deregisters all currently registered service providers from all
670 * categories.
671 */
672 public void deregisterAll() {
673 Iterator<SubRegistry> iter = categoryMap.values().iterator();
674 while (iter.hasNext()) {
675 SubRegistry reg = iter.next();
676 reg.clear();
677 }
678 }
679
680 /**
681 * Finalizes this object prior to garbage collection. The
682 * <code>deregisterAll</code> method is called to deregister all
683 * currently registered service providers. This method should not
684 * be called from application code.
685 *
686 * @exception Throwable if an error occurs during superclass
687 * finalization.
688 */
689 public void finalize() throws Throwable {
690 deregisterAll();
691 super.finalize();
692 }
693 }
694
695
696 /**
697 * A portion of a registry dealing with a single superclass or
698 * interface.
699 */
700 class SubRegistry {
701
702 ServiceRegistry registry;
703
704 Class<?> category;
705
706 // Provider Objects organized by partial oridering
707 PartiallyOrderedSet<Object> poset = new PartiallyOrderedSet<>();
708
709 // Class -> Provider Object of that class
710 // No way to express heterogeneous map, we want
711 // Map<Class<T>, T>, where T is ?
712 Map<Class<?>, Object> map = new HashMap<>();
713
714 public SubRegistry(ServiceRegistry registry, Class<?> category) {
715 this.registry = registry;
716 this.category = category;
717 }
718
719 public boolean registerServiceProvider(Object provider) {
720 Object oprovider = map.get(provider.getClass());
721 boolean present = oprovider != null;
722
723 if (present) {
724 deregisterServiceProvider(oprovider);
725 }
726 map.put(provider.getClass(), provider);
727 poset.add(provider);
728 if (provider instanceof RegisterableService) {
729 RegisterableService rs = (RegisterableService)provider;
730 rs.onRegistration(registry, category);
731 }
732
733 return !present;
734 }
752 return true;
753 }
754 return false;
755 }
756
757 public boolean contains(Object provider) {
758 Object oprovider = map.get(provider.getClass());
759 return oprovider == provider;
760 }
761
762 public boolean setOrdering(Object firstProvider,
763 Object secondProvider) {
764 return poset.setOrdering(firstProvider, secondProvider);
765 }
766
767 public boolean unsetOrdering(Object firstProvider,
768 Object secondProvider) {
769 return poset.unsetOrdering(firstProvider, secondProvider);
770 }
771
772 public Iterator<Object> getServiceProviders(boolean useOrdering) {
773 if (useOrdering) {
774 return poset.iterator();
775 } else {
776 return map.values().iterator();
777 }
778 }
779
780 @SuppressWarnings("unchecked")
781 public <T> T getServiceProviderByClass(Class<T> providerClass) {
782 return (T)map.get(providerClass);
783 }
784
785 public void clear() {
786 Iterator<Object> iter = map.values().iterator();
787 while (iter.hasNext()) {
788 Object provider = iter.next();
789 iter.remove();
790
791 if (provider instanceof RegisterableService) {
792 RegisterableService rs = (RegisterableService)provider;
793 rs.onDeregistration(registry, category);
794 }
795 }
796 poset.clear();
797 }
798
799 public void finalize() {
800 clear();
801 }
802 }
803
804
805 /**
806 * A class for wrapping <code>Iterators</code> with a filter function.
807 * This provides an iterator for a subset without duplication.
808 */
809 class FilterIterator<T> implements Iterator<T> {
810
811 private Iterator<? extends T> iter;
812 private ServiceRegistry.Filter filter;
813
814 private T next = null;
815
816 public FilterIterator(Iterator<? extends T> iter,
817 ServiceRegistry.Filter filter) {
818 this.iter = iter;
819 this.filter = filter;
820 advance();
821 }
822
823 private void advance() {
824 while (iter.hasNext()) {
825 T elt = iter.next();
826 if (filter.filter(elt)) {
827 next = elt;
828 return;
829 }
830 }
831
832 next = null;
833 }
834
835 public boolean hasNext() {
836 return next != null;
|