class ZuulFiltersModule extends AbstractModule {

    @VisibleForTesting
    String[] findClassNames(AbstractConfiguration config) {

        // Find individually-specified filter classes.
        String filterClassNamesStr = config.getString("zuul.filters.classes", "").trim();
        Stream<String> classNameStream = Pattern.compile(",")
                .splitAsStream(filterClassNamesStr)
                .map(String::trim)
                .filter(blank.negate());

        // Find filter classes in specified packages.
        String packageNamesStr = config.getString("zuul.filters.packages", "").trim();
        ClassPath cp;
        try {
            cp = ClassPath.from(this.getClass().getClassLoader());
        } catch (IOException e) {
            throw new RuntimeException("Error attempting to read classpath to find filters!", e);
        }
        Stream<String> packageStream = Pattern.compile(",")
                .splitAsStream(packageNamesStr)
                .map(String::trim)
                .filter(blank.negate())
                .flatMap( packageName -> cp.getTopLevelClasses(packageName).stream())
                .map(ClassPath.ClassInfo::load)
                .filter(ZuulFilter.class::isAssignableFrom)
                .map(Class::getCanonicalName);


        String[] filterClassNames = Stream.concat(classNameStream, packageStream).toArray(String[]::new);
        if (filterClassNames.length != 0) {
            LOG.info("Using filter classnames: ");
            for (String location : filterClassNames) {
                LOG.info("  " + location);
            }
        }

        return filterClassNames;
    }

}