All Downloads are FREE. Search and download functionalities are using the official Maven repository.

orbit.client.addressable.CapabilitiesScanner.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0a19
Show newest version
/*
 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