public AnalysisSPILoader(Class<S> clazz, String[] suffixes, ClassLoader classloader) { this.clazz = clazz; this.suffixes = suffixes; // if clazz' classloader is not a parent of the given one, we scan clazz's classloader, too: final ClassLoader clazzClassloader = clazz.getClassLoader(); if (clazzClassloader != null && !SPIClassIterator.isParentClassLoader(clazzClassloader, classloader)) { reload(clazzClassloader); } reload(classloader); }
/** * Reloads the internal SPI list from the given {@link ClassLoader}. * Changes to the service list are visible after the method ends, all * iterators (e.g., from {@link #availableServices()},...) stay consistent. * * <p><b>NOTE:</b> Only new service providers are added, existing ones are * never removed or replaced. * * <p><em>This method is expensive and should only be called for discovery * of new service providers on the given classpath/classloader!</em> */ public synchronized void reload(ClassLoader classloader) { final LinkedHashMap<String,Class<? extends S>> services = new LinkedHashMap<>(this.services); final SPIClassIterator<S> loader = SPIClassIterator.get(clazz, classloader); while (loader.hasNext()) { final Class<? extends S> service = loader.next(); final String clazzName = service.getSimpleName(); String name = null; for (String suffix : suffixes) { if (clazzName.endsWith(suffix)) { name = clazzName.substring(0, clazzName.length() - suffix.length()).toLowerCase(Locale.ROOT); break; } } if (name == null) { throw new ServiceConfigurationError("The class name " + service.getName() + " has wrong suffix, allowed are: " + Arrays.toString(suffixes)); } // only add the first one for each name, later services will be ignored // this allows to place services before others in classpath to make // them used instead of others // // TODO: Should we disallow duplicate names here? // Allowing it may get confusing on collisions, as different packages // could contain same factory class, which is a naming bug! // When changing this be careful to allow reload()! if (!services.containsKey(name)) { services.put(name, service); } } this.services = Collections.unmodifiableMap(services); }
/** * Reloads the internal SPI list from the given {@link ClassLoader}. * Changes to the service list are visible after the method ends, all * iterators (e.g., from {@link #availableServices()},...) stay consistent. * * <p><b>NOTE:</b> Only new service providers are added, existing ones are * never removed or replaced. * * <p><em>This method is expensive and should only be called for discovery * of new service providers on the given classpath/classloader!</em> */ public synchronized void reload(ClassLoader classloader) { final LinkedHashMap<String,Class<? extends S>> services = new LinkedHashMap<String,Class<? extends S>>(this.services); final SPIClassIterator<S> loader = SPIClassIterator.get(clazz, classloader); while (loader.hasNext()) { final Class<? extends S> service = loader.next(); final String clazzName = service.getSimpleName(); String name = null; for (String suffix : suffixes) { if (clazzName.endsWith(suffix)) { name = clazzName.substring(0, clazzName.length() - suffix.length()).toLowerCase(Locale.ROOT); break; } } if (name == null) { throw new ServiceConfigurationError("The class name " + service.getName() + " has wrong suffix, allowed are: " + Arrays.toString(suffixes)); } // only add the first one for each name, later services will be ignored // this allows to place services before others in classpath to make // them used instead of others // // TODO: Should we disallow duplicate names here? // Allowing it may get confusing on collisions, as different packages // could contain same factory class, which is a naming bug! // When changing this be careful to allow reload()! if (!services.containsKey(name)) { services.put(name, service); } } this.services = Collections.unmodifiableMap(services); }