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

com.itangcent.common.spi.ProxyBean.kt Maven / Gradle / Ivy

package com.itangcent.common.spi

import com.itangcent.common.logger.Log
import com.itangcent.common.logger.traceError
import com.itangcent.common.utils.notNullOrEmpty
import com.itangcent.common.utils.privileged
import java.lang.reflect.*
import java.util.*
import kotlin.reflect.KClass

open class ProxyBean(private val delegates: Array) : InvocationHandler {

    companion object : Log()

    override fun invoke(proxy: Any?, method: Method, args: Array?): Any? {
        return invoke(method, args ?: emptyArray())
    }

    fun invoke(method: Method, args: Array): Any? {
        beforeInvoke(method, args)
        var availableRet: Any? = null
        var throwables: MutableList? = null
        for (delegate in delegates) {
            try {
                val ret = method.privileged { it.invoke(delegate, *args) }
                if (ret != null) {
                    if (ret.isInvalidResult()) {
                        availableRet = ret
                        continue
                    } else {
                        return ret
                    }
                }
            } catch (e: Throwable) {
                //ignore NotImplemented
                if (e.isNotImplemented()) {
                    continue
                }
                LOG.traceError("failed call $delegate.${method.name}", e)
                if (throwables == null) {
                    throwables = LinkedList()
                }
                throwables.add(e)
            }
        }
        var setRet = false
        afterInvoke(method, args, availableRet, throwables) {
            availableRet = it
            setRet = true
        }
        if (setRet) {
            return availableRet
        }
        availableRet?.let { return it }
        throwables?.let { throw it.first() }
        return null
    }

    open fun beforeInvoke(method: Method, args: Array) {

    }

    open fun afterInvoke(
        method: Method,
        args: Array,
        ret: Any?,
        throwables: List?,
        resultSetter: (Any?) -> Unit
    ) {
    }

    private fun Throwable.isNotImplemented(): Boolean {
        return when (this) {
            is UndeclaredThrowableException -> this.undeclaredThrowable.isNotImplemented()
            is InvocationTargetException -> this.targetException.isNotImplemented()
            else -> this is NotImplementedError
        }
    }

    private fun Any?.isInvalidResult(): Boolean {
        return when {
            this == null -> true
            this is Boolean -> !this
            this is Number -> this.toDouble() != 0.0
            this is String && this.isEmpty() -> true
            this is Array<*> && this.isEmpty() -> true
            this is Collection<*> && this.isEmpty() -> true
            this is Map<*, *> && this.isEmpty() -> true
            else -> false
        }
    }
}

open class SafeProxyBean(private val delegates: Array) : ProxyBean(delegates) {
    override fun afterInvoke(
        method: Method,
        args: Array,
        ret: Any?,
        throwables: List?,
        resultSetter: (Any?) -> Unit
    ) {
        //return null instead of throw an exception
        if (ret == null && throwables.notNullOrEmpty()) {
            resultSetter(null)
        }
    }
}

@Suppress("UNCHECKED_CAST")
fun  ProxyBean.createProxy(cls: KClass): S {
    return Proxy.newProxyInstance(cls.java.classLoader, arrayOf(cls.java), this) as S
}