orbit.client.addressable.CapabilitiesScanner.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orbit-client Show documentation
Show all versions of orbit-client Show documentation
Orbit is a system to make building highly scalable realtime services easier.
/*
Copyright (C) 2015 - 2019 Electronic Arts Inc. All rights reserved.
This file is part of the Orbit Project .
See license in LICENSE.
*/
package orbit.client.addressable
import io.github.classgraph.ClassGraph
import mu.KotlinLogging
import orbit.client.OrbitClientConfig
import orbit.util.time.Clock
import orbit.util.time.stopwatch
internal class CapabilitiesScanner(
private val clock: Clock,
config: OrbitClientConfig
) {
private val logger = KotlinLogging.logger {}
private val packagePaths = config.packages.toTypedArray()
lateinit var addressableInterfaces: List private set
lateinit var addressableClasses: List private set
lateinit var interfaceLookup: Map private set
fun scan() {
logger.info("Scanning for node capabilities...")
stopwatch(clock) {
val classGraph = ClassGraph()
.enableAllInfo()
.whitelistPackages(*packagePaths)
classGraph.scan().use { scan ->
addressableInterfaces = scan
.getClassesImplementing(Addressable::class.java.name)
.interfaces
.filter {
!it.hasAnnotation(NonConcrete::class.java.name)
}
.map {
@Suppress("UNCHECKED_CAST")
it.loadClass() as AddressableClass
}
addressableClasses = scan
.getClassesImplementing(Addressable::class.java.name)
.standardClasses
.filter {
!it.hasAnnotation(NonConcrete::class.java.name)
}
.filter {
!it.isAbstract
}
.map {
@Suppress("UNCHECKED_CAST")
it.loadClass() as AddressableClass
}
interfaceLookup = mutableMapOf().apply {
addressableClasses.forEach { implClass ->
resolveMapping(implClass).also { mapped ->
check(!mapped.isEmpty()) { "Could not find mapping for ${implClass.name}" }
mapped.forEach { iface ->
check(!this.containsKey(iface)) { "Multiple implementations of concrete interface ${iface.name} found." }
this[iface] = implClass
}
}
}
}
}
}.also { (elapsed, _) ->
logger.debug { "Addressable Interfaces: $addressableInterfaces" }
logger.debug { "Addressable Classes: $addressableClasses" }
logger.debug { "Implemented Addressables: $interfaceLookup" }
logger.info {
"Node capabilities scan complete in ${elapsed}ms. " +
"${interfaceLookup.size} implemented addressable(s) found. " +
"${addressableInterfaces.size} addressable interface(s) found. " +
"${addressableClasses.size} addressable class(es) found. "
}
}
}
private fun resolveMapping(
crawl: Class<*>,
list: MutableList = mutableListOf()
): Collection {
if (crawl.interfaces.isEmpty()) return list
for (iface in crawl.interfaces) {
if (Addressable::class.java.isAssignableFrom(iface)) {
if (!iface.isAnnotationPresent(NonConcrete::class.java)) {
@Suppress("UNCHECKED_CAST")
list.add(iface as AddressableClass)
}
if (iface.interfaces.isNotEmpty()) resolveMapping(iface, list)
}
}
return list
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy