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

com.labijie.application.ApplicationInitializationRunner.kt Maven / Gradle / Ivy

package com.labijie.application

import com.labijie.application.jackson.DescribeEnumDeserializer
import com.labijie.application.jackson.DescribeEnumSerializer
import com.labijie.infra.json.JacksonHelper
import com.labijie.infra.spring.configuration.getApplicationName
import com.labijie.infra.utils.ifNullOrBlank
import com.labijie.infra.utils.logger
import org.springframework.beans.BeansException
import org.springframework.beans.factory.ObjectProvider
import org.springframework.boot.SpringApplication
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.event.EventListener
import org.springframework.core.Ordered
import org.springframework.core.env.Environment
import org.springframework.util.ClassUtils
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import kotlin.concurrent.thread
import kotlin.reflect.KClass
import kotlin.streams.toList
import kotlin.system.exitProcess

/**
 * Created with IntelliJ IDEA.
 * @author Anders Xiao
 * @date 2019-09-11
 */
class ApplicationInitializationRunner(
    private val contextClass: KClass) : ApplicationContextAware, Ordered {
    override fun getOrder(): Int {
        return Int.MAX_VALUE
    }

    private lateinit var applicationContext: ApplicationContext
    private lateinit var applicationName: String
    private lateinit var profiles: String
    private lateinit var environment: Environment
    private lateinit var errorRegistrations: ObjectProvider
    private lateinit var errorRegistry: IErrorRegistry


    override fun setApplicationContext(applicationContext: ApplicationContext) {
        SpringContext.current = applicationContext
        environment = applicationContext.environment
        this.applicationContext = applicationContext
        applicationName = environment.getApplicationName()
        profiles = applicationContext.environment.activeProfiles.joinToString()
        errorRegistrations = applicationContext.getBeanProvider(IErrorRegistration::class.java)
        errorRegistry = applicationContext.getBean(IErrorRegistry::class.java)
    }

    private val isWebEnvironment by lazy {
        ClassUtils.isPresent(SpringApplication.DEFAULT_REACTIVE_WEB_CONTEXT_CLASS, null)
                || ClassUtils.isPresent(SpringApplication.DEFAULT_SERVLET_WEB_CONTEXT_CLASS, null)
    }

    private fun initJackson(){
        val eumnModule  = com.fasterxml.jackson.databind.module.SimpleModule("eumn-module")
        eumnModule.addDeserializer(Enum::class.java, DescribeEnumDeserializer())
        eumnModule.addSerializer(Enum::class.java, DescribeEnumSerializer)

        JacksonHelper.webCompatibilityMapper.registerModule(eumnModule)

        JacksonHelper.defaultObjectMapper.registerModule(eumnModule)
    }

    @EventListener(ApplicationReadyEvent::class)
    fun run(event: ApplicationReadyEvent) {
        if(!contextClass.isInstance(event.applicationContext)){
            return
        }
        this.initJackson()
        try {
            loadModules()
            thread {
                errorRegistry.registerErrors(ApplicationErrors)
                errorRegistrations.orderedStream().forEach {
                    it.register(errorRegistry)
                }
            }
        } catch (e: Throwable) {
            logger.error("Application '$applicationName' initialize fault.", e)
            exitProcess(-3333)
        }

    }

    private fun loadModules() {
        val modules = this.applicationContext.getBeanProvider(IModuleInitializer::class.java).orderedStream().map {
            val moduleClass = it::class.java
            val method = tryGetMethod(moduleClass)
            if (method == null) {
                logger.warn("Find bean implements this interface (bean type: ${moduleClass.name})., but no have 'initialize' method")
            } else {
                try {
                    val parameters = method.parameters.map { param ->
                        val isObjectProvider =
                            param.type == ObjectProvider::class.java && param.parameterizedType != null && param.parameterizedType is ParameterizedType
                        if (isObjectProvider) {
                            val type = (param.parameterizedType as ParameterizedType).actualTypeArguments[0]
                            applicationContext.getBeanProvider(type as Class<*>)
                        } else {
                            applicationContext.getBean(param.type)
                        }
                    }
                    method.invoke(it, *parameters.toTypedArray())
                } catch (e: BeansException) {
                    val error =
                        "Process data initialization fault ( initializer:  ${moduleClass.name} , app: '$applicationName')"
                    throw RuntimeException(error, e)
                }
            }
            moduleClass
        }

        val list = modules.toList()
        this.reportApplicationStatus(list)
    }

    private fun tryGetMethod(moduleClass: Class): Method? {
        return try {
            moduleClass.declaredMethods.firstOrNull {it.name == "initialize"}
        } catch (e: NoSuchMethodException) {
            null
        }
    }

    private fun reportApplicationStatus(modules: List>) {
        val contextPath = if(isWebEnvironment) "/${environment.getProperty("server.context-path").orEmpty().trimStart('/').trimEnd('/')}" else ""
        val context = if(contextPath == "/") "" else contextPath
        val moduleList = modules.joinToString { it.simpleName.substringBefore("ModuleInitializer") }
        println(
            """
        [${this.applicationName}] has been started !! 
        
        current profiles: $profiles
        module loaded:  ${moduleList.ifNullOrBlank("none module")}
        --------------------------------------------------------------------
        ${if (isWebEnvironment) "http://localhost:${environment.getProperty("server.port").ifNullOrBlank("80")}$context/swagger-ui.html" else "Command line application"}
        --------------------------------------------------------------------
        """
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy