/** * 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); } }
/** * 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; }