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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 89 additions and 72 deletions

View File

@ -68,7 +68,7 @@ buildscript {
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.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.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-commons:$asm_version"
// Classpath scanner
shadow "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
// ClassGraph: classpath scanning
shadow "io.github.classgraph:classgraph:$class_graph_version"
// Test utilities
testCompile "junit:junit:$junit_version"

View File

@ -1,8 +1,9 @@
@file:JvmName("Utilities")
package net.corda.djvm.tools.cli
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import java.lang.reflect.Modifier
import io.github.classgraph.ClassGraph
import java.lang.reflect.Modifier.isAbstract
import java.lang.reflect.Modifier.isStatic
import java.nio.file.Files
import java.nio.file.Path
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].
*/
inline fun <reified T> find(scanSpec: String = "net/corda/djvm"): List<Class<*>> {
val references = mutableListOf<Class<*>>()
FastClasspathScanner(scanSpec)
.matchClassesImplementing(T::class.java) { clazz ->
if (!Modifier.isAbstract(clazz.modifiers) && !Modifier.isStatic(clazz.modifiers)) {
references.add(clazz)
}
}
return ClassGraph()
.whitelistPaths(scanSpec)
.enableAllInfo()
.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
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import io.github.classgraph.ClassGraph
import java.lang.reflect.Modifier
/**
@ -13,19 +13,19 @@ object Discovery {
* Get an instance of each concrete class that implements interface or class [T].
*/
inline fun <reified T> find(): List<T> {
val instances = mutableListOf<T>()
FastClasspathScanner("net/corda/djvm")
.matchClassesImplementing(T::class.java) { clazz ->
if (clazz.modifiers and FORBIDDEN_CLASS_MASK == 0) {
try {
instances.add(clazz.newInstance())
} catch (exception: Throwable) {
throw Exception("Unable to instantiate ${clazz.name}", exception)
}
return ClassGraph()
.whitelistPaths("net/corda/djvm")
.enableAllInfo()
.scan()
.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)
}
}
.scan()
return instances
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
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.ContractClassName
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 fun scan(): List<ContractClassName> {
val scanResult = FastClasspathScanner()
// 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 scanResult = ClassGraph().overrideClasspath(singleton(file)).enableAllInfo().scan()
val contractClassNames = coreContractClasses
.flatMap { scanResult.getNamesOfClassesImplementing(it.qualifiedName) }
.toSet()
val contractClassNames = scanResult.use {
coreContractClasses
.flatMap { scanResult.getClassesImplementing(it.qualifiedName).names }
.toSet()
}
return URLClassLoader(arrayOf(file.toUri().toURL()), Contract::class.java.classLoader).use { cl ->
contractClassNames.mapNotNull {

View File

@ -1,7 +1,7 @@
package net.corda.node.internal.cordapp
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult
import io.github.classgraph.ClassGraph
import io.github.classgraph.ScanResult
import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
@ -95,7 +95,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
}
private fun loadCordapps(): List<CordappImpl> {
val cordapps = cordappJarPaths
.map { scanCordapp(it).toCordapp(it) }
.map { url -> scanCordapp(url).use { it.toCordapp(url) } }
.filter {
if (it.info.minimumPlatformVersion > versionInfo.platformVersion) {
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 {
logger.info("Scanning CorDapp in ${cordappJarPath.url}")
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() }
}
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> {
return scanResult.getNamesOfClassesImplementing(type.java)
.filter { it.startsWith(qualifiedNamePrefix) }
return scanResult.getClassesImplementing(type.java.name).names.filter { it.startsWith(qualifiedNamePrefix) }
}
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) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
.filterNot { it.isAbstractClass }
}
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) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
.filterNot { it.isAbstractClass }
.map { it.kotlin.objectOrNewInstance() }
}
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) }
.mapNotNull { loadClass(it, type) }
.filterNot { Modifier.isAbstract(it.modifiers) }
}
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) }
.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.
compile "org.apache.qpid:proton-j:$protonj_version"
// FastClasspathScanner: classpath scanning
compile "io.github.lukehutch:fast-classpath-scanner:$fast_classpath_scanner_version"
// ClassGraph: classpath scanning
compile "io.github.classgraph:classgraph:$class_graph_version"
// Pure-Java Snappy compression
compile "org.iq80.snappy:snappy:$snappy_version"

View File

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

View File

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