/** * Scans for factory plug-ins of the given category, with guard against recursivities. The * recursivity check make debugging easier than inspecting a {@link StackOverflowError}. * * @param loader The class loader to use. * @param category The category to scan for plug-ins. */ private <T> void scanForPlugins(final Collection<ClassLoader> loaders, final Class<T> category) { if (!scanningCategories.addAndCheck(category)) { throw new RecursiveSearchException(category); } try { final StringBuilder message = getLogHeader(category); boolean newServices = false; /* * First, scan META-INF/services directories (the default mechanism). */ for (final ClassLoader loader : loaders) { newServices |= register(lookupProviders(category, loader), category, message); newServices |= registerFromSystemProperty(loader, category, message); } /* * Next, query the user-provider iterators, if any. */ final FactoryIteratorProvider[] fip = FactoryIteratorProviders.getIteratorProviders(); for (int i = 0; i < fip.length; i++) { final Iterator<T> it = fip[i].iterator(category); if (it != null) { newServices |= register(it, category, message); } } /* * Finally, log the list of registered factories. */ if (newServices) { log("scanForPlugins", message); } } finally { scanningCategories.removeAndCheck(category); } }
/** Returns {@code true} if the specified factory is available. */ private boolean isAvailable(final Object provider) { if (!(provider instanceof OptionalFactory)) { return true; } final OptionalFactory factory = (OptionalFactory) provider; final Class<? extends OptionalFactory> type = factory.getClass(); if (!testingAvailability.addAndCheck(type)) { throw new RecursiveSearchException(type); } try { return factory.isAvailable(); } finally { testingAvailability.removeAndCheck(type); } }
/** * Implementation of {@link #getServiceProviders(Class, Filter, Hints)} without the filtering * applied by the {@link #isAcceptable(Object, Class, Hints, Filter)} method. If this filtering is * not already presents in the filter given to this method, then it must be applied on the * elements returned by the iterator. The later is preferrable when: * * <p> * * <ul> * <li>There is some cheaper tests to perform before {@code isAcceptable}. * <li>We don't want a restrictive filter in order to avoid trigging a classpath scan if this * method doesn't found any element to iterate. * </ul> * * <p><b>Note:</b> {@link #synchronizeIteratorProviders} should also be invoked once before this * method. * * @todo Use Hints to match Constructor. */ final <T> Iterator<T> getUnfilteredProviders(final Class<T> category) { /* * If the map is not empty, then this mean that a scanning is under progress, i.e. * 'scanForPlugins' is currently being executed. This is okay as long as the user * is not asking for one of the categories under scanning. Otherwise, the answer * returned by 'getServiceProviders' would be incomplete because not all plugins * have been found yet. This can lead to some bugs hard to spot because this methoud * could complete normally but return the wrong plugin. It is safer to thrown an * exception so the user is advised that something is wrong. */ if (scanningCategories.contains(category)) { throw new RecursiveSearchException(category); } scanForPluginsIfNeeded(category); return getServiceProviders(category, true); }
/** * Returns the providers in the registry for the specified category, filter and hints. Providers * that are not {@linkplain OptionalFactory#isAvailable available} will be ignored. This method * will {@linkplain #scanForPlugins() scan for plugins} the first time it is invoked for the given * category. * * @param <T> The class represented by the {@code category} argument. * @param category The category to look for. Usually an interface class (not the actual * implementation class). * @param filter The optional filter, or {@code null}. * @param hints The optional user requirements, or {@code null}. * @return Factories ready to use for the specified category, filter and hints. * @since 2.3 */ public synchronized <T> Iterator<T> getServiceProviders( final Class<T> category, final Filter filter, final Hints hints) { /* * The implementation of this method is very similar to the 'getUnfilteredProviders' * one except for filter handling. See the comments in 'getUnfilteredProviders' for * more implementation details. */ if (scanningCategories.contains(category)) { // Please note you will get errors here if you accidentally allow // more than one thread to use your FactoryRegistry at a time. throw new RecursiveSearchException(category); } final Filter hintsFilter = new Filter() { public boolean filter(final Object provider) { return isAcceptable(category.cast(provider), category, hints, filter); } }; synchronizeIteratorProviders(); scanForPluginsIfNeeded(category); return getServiceProviders(category, hintsFilter, true); }
/** * Returns {@code true} is the specified {@code factory} meets the requirements specified by a map * of {@code hints}. This method checks only the hints; it doesn't check the {@link Filter}, the * {@linkplain OptionalFactory#isAvailable availability} or the user-overrideable {@link * #isAcceptable(Object, Class, Hints)} method. This method invokes itself recursively. * * @param factory The factory to checks. * @param category The factory category. Usually an interface. * @param hints The user requirements ({@code null} not allowed). * @param alreadyDone Should be {@code null} except on recursive calls (for internal use only). * @return {@code true} if the {@code factory} meets the hints requirements. */ private boolean usesAcceptableHints( final Factory factory, final Class<?> category, final Hints hints, Set<Factory> alreadyDone) { /* * Ask for implementation hints with special care against infinite recursivity. * Some implementations use deferred algorithms fetching dependencies only when * first needed. The call to getImplementationHints() is sometime a trigger for * fetching dependencies (in order to return accurate hints). For example the * BufferedCoordinateOperationFactory implementation asks for an other instance * of CoordinateOperationFactory, the instance to cache behind a buffer, which * should not be itself. Of course BufferedCoordinateOperation will checks that * it is not caching itself, but its test happen too late for preventing a never- * ending loop if we don't put a 'testingHints' guard here. It is also a safety * against broken factory implementations. */ if (!testingHints.addAndCheck(factory)) { return false; } final Map<RenderingHints.Key, ?> implementationHints; try { implementationHints = Hints.stripNonKeys(factory.getImplementationHints()); } finally { testingHints.removeAndCheck(factory); } if (implementationHints == null) { // factory was bad and did not meet contract - assume it used no Hints return true; } /* * We got the implementation hints. Now tests their compatibility. */ Hints remaining = null; for (final Map.Entry<?, ?> entry : implementationHints.entrySet()) { final Object key = entry.getKey(); final Object value = entry.getValue(); final Object expected = hints.get(key); if (expected != null) { /* * We have found a hint that matter. Check if the * available factory meets the user's criterions. */ if (expected instanceof Class<?>) { if (!((Class<?>) expected).isInstance(value)) { return false; } } else if (expected instanceof Class<?>[]) { final Class<?>[] types = (Class<?>[]) expected; int i = 0; do if (i >= types.length) return false; while (!types[i++].isInstance(value)); } else if (!expected.equals(value)) { return false; } } /* * Checks recursively in factory dependencies, if any. Note that the dependencies * will be checked against a subset of user's hints. More specifically, all hints * processed by the current pass will NOT be passed to the factories dependencies. * This is because the same hint may appears in the "parent" factory and a "child" * dependency with different value. For example the FORCE_LONGITUDE_FIRST_AXIS_ORDER * hint has the value TRUE in OrderedAxisAuthorityFactory, but the later is basically * a wrapper around the ThreadedEpsgFactory (typically), which has the value FALSE * for the same hint. * * Additional note: The 'alreadyDone' set is a safety against cyclic dependencies, * in order to protect ourself against never-ending loops. This is not the same * kind of dependencies than 'testingHints'. It is a "factory A depends on factory * B which depends on factory A" loop, which is legal. */ if (value instanceof Factory) { final Factory dependency = (Factory) value; if (alreadyDone == null) { alreadyDone = new HashSet<Factory>(); } if (!alreadyDone.contains(dependency)) { alreadyDone.add(factory); if (remaining == null) { remaining = new Hints(hints); remaining.keySet().removeAll(implementationHints.keySet()); } final Class<?> type; if (key instanceof Hints.Key) { type = ((Hints.Key) key).getValueClass(); } else { type = Factory.class; // Kind of unknown factory type... } // Recursive call to this method for scanning dependencies. if (!usesAcceptableHints(dependency, type, remaining, alreadyDone)) { return false; } } } } return true; }