CORDA-1274: Migrated usage of FastClasspathScanner to ClassGraph (#4060)

FastClasspathScanner was renamed to ClassGraph for the version 4 release
This commit is contained in:
Shams Asari
2018-10-11 19:50:26 +01:00
committed by GitHub
parent 8c41ae208d
commit aced03df54
12 changed files with 89 additions and 72 deletions

View File

@ -68,7 +68,7 @@ buildscript {
ext.commons_cli_version = '1.4' ext.commons_cli_version = '1.4'
ext.protonj_version = '0.27.1' // This is now aligned with the Artemis version, but retaining in case we ever need to diverge again for a bug fix. ext.protonj_version = '0.27.1' // This is now aligned with the Artemis version, but retaining in case we ever need to diverge again for a bug fix.
ext.snappy_version = '0.4' ext.snappy_version = '0.4'
ext.fast_classpath_scanner_version = '2.12.3' ext.class_graph_version = '4.2.12'
ext.jcabi_manifests_version = '1.1' ext.jcabi_manifests_version = '1.1'
ext.picocli_version = '3.5.2' ext.picocli_version = '3.5.2'

View File

@ -36,8 +36,8 @@ dependencies {
compile "org.ow2.asm:asm-tree:$asm_version" compile "org.ow2.asm:asm-tree:$asm_version"
compile "org.ow2.asm:asm-commons:$asm_version" compile "org.ow2.asm:asm-commons:$asm_version"
// Classpath scanner // ClassGraph: classpath scanning
shadow "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version" shadow "io.github.classgraph:classgraph:$class_graph_version"
// Test utilities // Test utilities
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"

View File

@ -1,8 +1,9 @@
@file:JvmName("Utilities") @file:JvmName("Utilities")
package net.corda.djvm.tools.cli package net.corda.djvm.tools.cli
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import java.lang.reflect.Modifier import java.lang.reflect.Modifier.isAbstract
import java.lang.reflect.Modifier.isStatic
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
@ -92,13 +93,10 @@ val userClassPath: String = System.getProperty("java.class.path")
* Get a reference of each concrete class that implements interface or class [T]. * Get a reference of each concrete class that implements interface or class [T].
*/ */
inline fun <reified T> find(scanSpec: String = "net/corda/djvm"): List<Class<*>> { inline fun <reified T> find(scanSpec: String = "net/corda/djvm"): List<Class<*>> {
val references = mutableListOf<Class<*>>() return ClassGraph()
FastClasspathScanner(scanSpec) .whitelistPaths(scanSpec)
.matchClassesImplementing(T::class.java) { clazz -> .enableAllInfo()
if (!Modifier.isAbstract(clazz.modifiers) && !Modifier.isStatic(clazz.modifiers)) {
references.add(clazz)
}
}
.scan() .scan()
return references .use { it.getClassesImplementing(T::class.java.name).loadClasses(T::class.java) }
.filter { !isAbstract(it.modifiers) && !isStatic(it.modifiers) }
} }

View File

@ -1,6 +1,6 @@
package net.corda.djvm.utilities package net.corda.djvm.utilities
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
/** /**
@ -13,19 +13,19 @@ object Discovery {
* Get an instance of each concrete class that implements interface or class [T]. * Get an instance of each concrete class that implements interface or class [T].
*/ */
inline fun <reified T> find(): List<T> { inline fun <reified T> find(): List<T> {
val instances = mutableListOf<T>() return ClassGraph()
FastClasspathScanner("net/corda/djvm") .whitelistPaths("net/corda/djvm")
.matchClassesImplementing(T::class.java) { clazz -> .enableAllInfo()
if (clazz.modifiers and FORBIDDEN_CLASS_MASK == 0) {
try {
instances.add(clazz.newInstance())
} catch (exception: Throwable) {
throw Exception("Unable to instantiate ${clazz.name}", exception)
}
}
}
.scan() .scan()
return instances .use { it.getClassesImplementing(T::class.java.name).loadClasses(T::class.java) }
.filter { it.modifiers and FORBIDDEN_CLASS_MASK == 0 }
.map {
try {
it.newInstance()
} catch (exception: Throwable) {
throw Exception("Unable to instantiate ${it.name}", exception)
}
} }
} }
}

View File

@ -51,8 +51,8 @@ dependencies {
// JOptSimple: command line option parsing // JOptSimple: command line option parsing
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version" compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
// FastClasspathScanner: classpath scanning // ClassGraph: classpath scanning
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version" compile "io.github.classgraph:classgraph:$class_graph_version"
compile "commons-io:commons-io:$commonsio_version" compile "commons-io:commons-io:$commonsio_version"
compile "com.spotify:docker-client:$docker_client_version" compile "com.spotify:docker-client:$docker_client_version"

View File

@ -1,27 +1,29 @@
package net.corda.behave.scenarios package net.corda.behave.scenarios
import cucumber.api.java8.En import cucumber.api.java8.En
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import net.corda.behave.scenarios.api.StepsBlock import net.corda.behave.scenarios.api.StepsBlock
import net.corda.behave.scenarios.api.StepsProvider import net.corda.behave.scenarios.api.StepsProvider
import net.corda.behave.scenarios.steps.* import net.corda.behave.scenarios.steps.*
import net.corda.core.internal.objectOrNewInstance import net.corda.core.internal.objectOrNewInstance
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.contextLogger
@Suppress("KDocMissingDocumentation") @Suppress("KDocMissingDocumentation")
class StepsContainer(val state: ScenarioState) : En { class StepsContainer(val state: ScenarioState) : En {
companion object { companion object {
private val log = contextLogger()
val stepsProviders: List<StepsProvider> by lazy { val stepsProviders: List<StepsProvider> by lazy {
FastClasspathScanner().addClassLoader(this::class.java.classLoader).scan() ClassGraph()
.getNamesOfClassesImplementing(StepsProvider::class.java) .addClassLoader(this::class.java.classLoader)
.mapNotNull { this::class.java.classLoader.loadClass(it).asSubclass(StepsProvider::class.java) } .enableAllInfo()
.scan()
.use { it.getClassesImplementing(StepsProvider::class.java.name).loadClasses(StepsProvider::class.java) }
.map { it.kotlin.objectOrNewInstance() } .map { it.kotlin.objectOrNewInstance() }
} }
} }
private val log = loggerFor<StepsContainer>()
private val stepDefinitions: List<StepsBlock> = listOf( private val stepDefinitions: List<StepsBlock> = listOf(
CashSteps(), CashSteps(),
ConfigurationSteps(), ConfigurationSteps(),

View File

@ -27,8 +27,8 @@ dependencies {
compile "org.apache.qpid:proton-j:$protonj_version" compile "org.apache.qpid:proton-j:$protonj_version"
// FastClasspathScanner: classpath scanning - needed for the NetworkBootstrapper. // ClassGraph: classpath scanning
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version" compile "io.github.classgraph:classgraph:$class_graph_version"
// For caches rather than guava // For caches rather than guava
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version" compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"

View File

@ -1,6 +1,6 @@
package net.corda.nodeapi.internal package net.corda.nodeapi.internal
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.UpgradedContract import net.corda.core.contracts.UpgradedContract
@ -28,15 +28,13 @@ class ContractsJarFile(private val file: Path) : ContractsJar {
override val hash: SecureHash by lazy(LazyThreadSafetyMode.NONE, file::hash) override val hash: SecureHash by lazy(LazyThreadSafetyMode.NONE, file::hash)
override fun scan(): List<ContractClassName> { override fun scan(): List<ContractClassName> {
val scanResult = FastClasspathScanner() val scanResult = ClassGraph().overrideClasspath(singleton(file)).enableAllInfo().scan()
// A set of a single element may look odd, but if this is removed "Path" which itself is an `Iterable`
// is getting broken into pieces to scan individually, which doesn't yield desired effect.
.overrideClasspath(singleton(file))
.scan()
val contractClassNames = coreContractClasses val contractClassNames = scanResult.use {
.flatMap { scanResult.getNamesOfClassesImplementing(it.qualifiedName) } coreContractClasses
.flatMap { scanResult.getClassesImplementing(it.qualifiedName).names }
.toSet() .toSet()
}
return URLClassLoader(arrayOf(file.toUri().toURL()), Contract::class.java.classLoader).use { cl -> return URLClassLoader(arrayOf(file.toUri().toURL()), Contract::class.java.classLoader).use { cl ->
contractClassNames.mapNotNull { contractClassNames.mapNotNull {

View File

@ -1,7 +1,7 @@
package net.corda.node.internal.cordapp package net.corda.node.internal.cordapp
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult import io.github.classgraph.ScanResult
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
@ -95,7 +95,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
} }
private fun loadCordapps(): List<CordappImpl> { private fun loadCordapps(): List<CordappImpl> {
val cordapps = cordappJarPaths val cordapps = cordappJarPaths
.map { scanCordapp(it).toCordapp(it) } .map { url -> scanCordapp(url).use { it.toCordapp(url) } }
.filter { .filter {
if (it.info.minimumPlatformVersion > versionInfo.platformVersion) { if (it.info.minimumPlatformVersion > versionInfo.platformVersion) {
logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum " + logger.warn("Not loading CorDapp ${it.info.shortName} (${it.info.vendor}) as it requires minimum " +
@ -202,7 +202,8 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult { private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult {
logger.info("Scanning CorDapp in ${cordappJarPath.url}") logger.info("Scanning CorDapp in ${cordappJarPath.url}")
return cachedScanResult.computeIfAbsent(cordappJarPath) { return cachedScanResult.computeIfAbsent(cordappJarPath) {
RestrictedScanResult(FastClasspathScanner().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).scan(), cordappJarPath.qualifiedNamePrefix) val scanResult = ClassGraph().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).enableAllInfo().scan()
RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix)
} }
} }
@ -239,40 +240,49 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
return map { it.kotlin.objectOrNewInstance() } return map { it.kotlin.objectOrNewInstance() }
} }
private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) { private inner class RestrictedScanResult(private val scanResult: ScanResult, private val qualifiedNamePrefix: String) : AutoCloseable {
fun getNamesOfClassesImplementing(type: KClass<*>): List<String> { fun getNamesOfClassesImplementing(type: KClass<*>): List<String> {
return scanResult.getNamesOfClassesImplementing(type.java) return scanResult.getClassesImplementing(type.java.name).names.filter { it.startsWith(qualifiedNamePrefix) }
.filter { it.startsWith(qualifiedNamePrefix) }
} }
fun <T : Any> getClassesWithSuperclass(type: KClass<T>): List<Class<out T>> { fun <T : Any> getClassesWithSuperclass(type: KClass<T>): List<Class<out T>> {
return scanResult.getNamesOfSubclassesOf(type.java) return scanResult
.getSubclasses(type.java.name)
.names
.filter { it.startsWith(qualifiedNamePrefix) } .filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) } .mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) } .filterNot { it.isAbstractClass }
} }
fun <T : Any> getClassesImplementing(type: KClass<T>): List<T> { fun <T : Any> getClassesImplementing(type: KClass<T>): List<T> {
return scanResult.getNamesOfClassesImplementing(type.java) return scanResult
.getClassesImplementing(type.java.name)
.names
.filter { it.startsWith(qualifiedNamePrefix) } .filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) } .mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) } .filterNot { it.isAbstractClass }
.map { it.kotlin.objectOrNewInstance() } .map { it.kotlin.objectOrNewInstance() }
} }
fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> { fun <T : Any> getClassesWithAnnotation(type: KClass<T>, annotation: KClass<out Annotation>): List<Class<out T>> {
return scanResult.getNamesOfClassesWithAnnotation(annotation.java) return scanResult
.getClassesWithAnnotation(annotation.java.name)
.names
.filter { it.startsWith(qualifiedNamePrefix) } .filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) } .mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) } .filterNot { Modifier.isAbstract(it.modifiers) }
} }
fun <T : Any> getConcreteClassesOfType(type: KClass<T>): List<Class<out T>> { fun <T : Any> getConcreteClassesOfType(type: KClass<T>): List<Class<out T>> {
return scanResult.getNamesOfSubclassesOf(type.java) return scanResult
.getSubclasses(type.java.name)
.names
.filter { it.startsWith(qualifiedNamePrefix) } .filter { it.startsWith(qualifiedNamePrefix) }
.mapNotNull { loadClass(it, type) } .mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) } .filterNot { it.isAbstractClass }
} }
override fun close() = scanResult.close()
} }
} }

View File

@ -19,8 +19,8 @@ dependencies {
// For AMQP serialisation. // For AMQP serialisation.
compile "org.apache.qpid:proton-j:$protonj_version" compile "org.apache.qpid:proton-j:$protonj_version"
// FastClasspathScanner: classpath scanning // ClassGraph: classpath scanning
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version" compile "io.github.classgraph:classgraph:$class_graph_version"
// Pure-Java Snappy compression // Pure-Java Snappy compression
compile "org.iq80.snappy:snappy:$snappy_version" compile "org.iq80.snappy:snappy:$snappy_version"

View File

@ -2,11 +2,12 @@
package net.corda.serialization.internal.amqp package net.corda.serialization.internal.amqp
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.StubOutForDJVM import net.corda.core.StubOutForDJVM
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.isAbstractClass
import net.corda.core.internal.objectOrNewInstance import net.corda.core.internal.objectOrNewInstance
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.* import net.corda.core.serialization.*
@ -16,7 +17,6 @@ import net.corda.serialization.internal.CordaSerializationMagic
import net.corda.serialization.internal.DefaultWhitelist import net.corda.serialization.internal.DefaultWhitelist
import net.corda.serialization.internal.MutableClassWhitelist import net.corda.serialization.internal.MutableClassWhitelist
import net.corda.serialization.internal.SerializationScheme import net.corda.serialization.internal.SerializationScheme
import java.lang.reflect.Modifier
import java.util.* import java.util.*
val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic
@ -72,10 +72,16 @@ abstract class AbstractAMQPSerializationScheme(
@StubOutForDJVM @StubOutForDJVM
private fun scanClasspathForSerializers(scanSpec: String): List<SerializationCustomSerializer<*, *>> = private fun scanClasspathForSerializers(scanSpec: String): List<SerializationCustomSerializer<*, *>> =
this::class.java.classLoader.let { cl -> this::class.java.classLoader.let { cl ->
FastClasspathScanner(scanSpec).addClassLoader(cl).scan() ClassGraph()
.getNamesOfClassesImplementing(SerializationCustomSerializer::class.java) .whitelistPackages(scanSpec)
.map { cl.loadClass(it).asSubclass(SerializationCustomSerializer::class.java) } .addClassLoader(cl)
.filterNot { Modifier.isAbstract(it.modifiers) } .enableAllInfo()
.scan()
.use {
val serializerClass = SerializationCustomSerializer::class.java
it.getClassesImplementing(serializerClass.name).loadClasses(serializerClass)
}
.filterNot { it.isAbstractClass }
.map { it.kotlin.objectOrNewInstance() } .map { it.kotlin.objectOrNewInstance() }
} }

View File

@ -1,6 +1,6 @@
package net.corda.testing.node.internal package net.corda.testing.node.internal
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner import io.github.classgraph.ClassGraph
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.deleteIfExists import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.outputStream import net.corda.core.internal.outputStream
@ -69,9 +69,12 @@ fun Iterable<TestCorDapp>.packageInDirectory(directory: Path) {
* Returns all classes within the [targetPackage]. * Returns all classes within the [targetPackage].
*/ */
fun allClassesForPackage(targetPackage: String): Set<Class<*>> { fun allClassesForPackage(targetPackage: String): Set<Class<*>> {
return ClassGraph()
val scanResult = FastClasspathScanner(targetPackage).strictWhitelist().scan() .whitelistPackages(targetPackage)
return scanResult.namesOfAllClasses.filter { className -> className.startsWith(targetPackage) }.map(scanResult::classNameToClassRef).toSet() .enableAllInfo()
.scan()
.use { it.allClasses.loadClasses() }
.toSet()
} }
/** /**