I just wanted a simple classpath scanner in Groovy - no library, no extra jars, no callback interfaces. I couldn't find any, so I wrote one. I'm posting it here and hopefully it would be useful to somebody. It is licensed under the established and permissive MIT License (if it precludes you from using it, let me know).
import java.util.zip.ZipFile /** * <pre><code> * def cps = new GroovyClasspathScanner(packagePrefix: 'com.company.application') * cps.scanClasspathRoots(classLoader1) // optional * cps.scanClasspathRoots(classLoader2) // optional * ... * List<Class> classes = cps.scanClasses { Class it -> * Event.isAssignableFrom(it) || * Command.isAssignableFrom(it) || * it.isAnnotationPresent(MessageDescriptor) * } * </code></pre> */ class GroovyClasspathScanner { String packagePrefix = '' List<File> classpathRoots @SuppressWarnings("GroovyAssignabilityCheck") List<File> scanClasspathRoots(ClassLoader classLoader) { if (!classLoader) classLoader = getClass().classLoader def prefixPath = packagePrefix.replace((char) '.', (char) '/') + '/' def List<URL> urls = [] for (URLClassLoader cl = classLoader; cl; cl = cl.parent) { urls.addAll cl.URLs } return urls .each { assert it.protocol == 'file' } .collect { new File(it.path) } .each { File it -> if (it.isFile()) assert it.name =~ /.*\.(?:jar|zip)$/ } .findAll { File it -> (it.isDirectory() && new File(it, prefixPath).exists()) || (it.isFile() && new ZipFile(it).entries().find { it.name == prefixPath}) } } List<String> scanClassNames() { if (!classpathRoots) classpathRoots = scanClasspathRoots() def classNames = [] def collect = { it, String pathProp -> def normalizedPath = it[pathProp].replaceAll('[\\\\/]', '.') def packageRegex = packagePrefix.replace('.', '\\.') def classRegex = "\\.($packageRegex\\..+)\\.class\$" def match = normalizedPath =~ classRegex if (match) classNames << match[0][1] } classpathRoots.each { if (it.isDirectory()) { it.eachFileRecurse { collect it, 'canonicalPath' } } else { new ZipFile(it).entries().each { collect it, 'name' } } } return classNames } List<Class> scanClasses(Closure predicate = { true } ) { return scanClassNames() .collect { try { Class.forName it } catch(Throwable e) { println "$it -> $e" } } .findAll { it } .findAll { predicate(it) } } }
No comments:
Post a Comment