Exemple #1
0
 /**
  * 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);
   }
 }
Exemple #2
0
 /** 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);
   }
 }
Exemple #3
0
 /**
  * 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);
 }
Exemple #4
0
 /**
  * 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);
 }
Exemple #5
0
 /**
  * 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;
 }