/** * 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 the first provider in the registry for the specified category, using the specified map * of hints (if any). This method may {@linkplain #scanForPlugins scan for plugins} the first time * it is invoked. Except as a result of this scan, no new provider instance is created by the * default implementation of this method. The {@link FactoryCreator} class change this behavior * however. * * @param <T> The class represented by the {@code category} argument. * @param category The category to look for. Must be one of the categories declared to the * constructor. Usually an interface class (not the actual implementation class). * @param filter An optional filter, or {@code null} if none. This is used for example in order to * select the first factory for some {@linkplain * org.opengis.referencing.AuthorityFactory#getAuthority authority}. * @param hints A {@linkplain Hints map of hints}, or {@code null} if none. * @param key The key to use for looking for a user-provided instance in the hints, or {@code * null} if none. * @return A factory {@linkplain OptionalFactory#isAvailable available} for use for the specified * category and hints. The returns type is {@code Object} instead of {@link Factory} because * the factory implementation doesn't need to be a Geotools one. * @throws FactoryNotFoundException if no factory was found for the specified category, filter and * hints. * @throws FactoryRegistryException if a factory can't be returned for some other reason. * @see #getServiceProviders(Class, Filter, Hints) * @see FactoryCreator#getServiceProvider */ public <T> T getServiceProvider( final Class<T> category, final Filter filter, Hints hints, final Hints.Key key) throws FactoryRegistryException { synchronizeIteratorProviders(); final boolean debug = LOGGER.isLoggable(DEBUG_LEVEL); if (debug) { /* * We are not required to insert the method name ("GetServiceProvider") in the * message because it is part of the informations already stored by LogRecord, * and formatted by the default java.util.logging.SimpleFormatter. * * Conventions for the message part according java.util.logging.Logger javadoc: * - "ENTRY" at the begining of a method. * - "RETURN" at the end of a method, if successful. * - "THROW" in case of failure. * - "CHECK" ... is our own addition to Sun's convention for this method ... */ debug("ENTRY", category, key, null, null); } Class<?> implementation = null; if (key != null) { /* * Sanity check: make sure that the key class is appropriate for the category. */ final Class<?> valueClass = key.getValueClass(); if (!category.isAssignableFrom(valueClass)) { if (debug) { debug("THROW", category, key, "unexpected type:", valueClass); } throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_KEY_$1, key)); } if (hints != null) { final Object hint = hints.get(key); if (hint != null) { if (debug) { debug("CHECK", category, key, "user provided a", hint.getClass()); } if (category.isInstance(hint)) { /* * The factory implementation was given explicitly by the user. * Nothing to do; we are done. */ if (debug) { debug("RETURN", category, key, "return hint as provided.", null); } return category.cast(hint); } /* * Before to pass the hints to the private 'getServiceImplementation' method, * remove the hint for the user-supplied key. This is because this hint has * been processed by this public 'getServiceProvider' method, and the policy * is to remove the processed hints before to pass them to child dependencies * (see the "Check recursively in factory dependencies" comment elswhere in * this class). * * Use case: DefaultDataSourceTest invokes indirectly 'getServiceProvider' * with a "CRS_AUTHORITY_FACTORY = ThreadedEpsgFactory.class" hint. However * ThreadedEpsgFactory (in the org.geotools.referencing.factory.epsg package) * is a wrapper around DirectEpsgFactory, and defines this dependency through * a "CRS_AUTHORITY_FACTORY = DirectEpsgFactory.class" hint. There is no way * to match this hint for both factories in same time. Since we must choose * one, we assume that the user is interrested in the most top level one and * discart this particular hint for the dependencies. */ hints = new Hints(hints); if (hints.remove(key) != hint) { // Should never happen except on concurrent modification in an other thread. throw new AssertionError(key); } /* * If the user accepts many implementation classes, then try all of them in * the preference order given by the user. The last class (or the singleton * if the hint was not an array) will be tried using the "normal" path * (oustide the loop) in order to get the error message in case of failure. */ if (hint instanceof Class<?>[]) { final Class<?>[] types = (Class<?>[]) hint; final int length = types.length; for (int i = 0; i < length - 1; i++) { final Class<?> type = types[i]; if (debug) { debug("CHECK", category, key, "consider hint[" + i + ']', type); } final T candidate = getServiceImplementation(category, type, filter, hints); if (candidate != null) { if (debug) { debug("RETURN", category, key, "found implementation", candidate.getClass()); } return candidate; } } if (length != 0) { implementation = types[length - 1]; // Last try to be done below. } } else { implementation = (Class<?>) hint; } } } } if (debug && implementation != null) { debug("CHECK", category, key, "consider hint[last]", implementation); } final T candidate = getServiceImplementation(category, implementation, filter, hints); if (candidate != null) { if (debug) { debug("RETURN", category, key, "found implementation", candidate.getClass()); } return candidate; } if (debug) { debug("THROW", category, key, "could not find implementation.", null); } throw new FactoryNotFoundException( Errors.format( ErrorKeys.FACTORY_NOT_FOUND_$1, implementation != null ? implementation : category)); }