[CORDA-2431] - Small refactorings following-up on PR-4551 (#4564)

* Small refactorings following-up on PR-4551

* Adjust thread context class loader

* Address Shams' comments
This commit is contained in:
Dimos Raptis
2019-01-15 14:34:11 +00:00
committed by GitHub
parent 05ffb3d101
commit fbb00bff9c
9 changed files with 127 additions and 40 deletions

View File

@ -40,6 +40,7 @@ import net.corda.node.internal.cordapp.*
import net.corda.node.internal.rpc.proxies.AuthenticatedRpcOpsProxy
import net.corda.node.internal.rpc.proxies.ExceptionMaskingRpcOpsProxy
import net.corda.node.internal.rpc.proxies.ExceptionSerialisingRpcOpsProxy
import net.corda.node.internal.rpc.proxies.ThreadContextAdjustingRpcOpsProxy
import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
@ -254,7 +255,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
}
/** The implementation of the [CordaRPCOps] interface used by this node. */
open fun makeRPCOps(): CordaRPCOps {
open fun makeRPCOps(cordappLoader: CordappLoader): CordaRPCOps {
val ops: CordaRPCOps = CordaRPCOpsImpl(services, smm, flowStarter) { shutdownExecutor.submit { stop() } }.also { it.closeOnStop() }
val proxies = mutableListOf<(CordaRPCOps) -> CordaRPCOps>()
// Mind that order is relevant here.
@ -263,6 +264,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
proxies += { it -> ExceptionMaskingRpcOpsProxy(it, true) }
}
proxies += { it -> ExceptionSerialisingRpcOpsProxy(it, configuration.devMode) }
proxies += { it -> ThreadContextAdjustingRpcOpsProxy(it, cordappLoader.appClassLoader) }
return proxies.fold(ops) { delegate, decorate -> decorate(delegate) }
}
@ -323,7 +325,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
installCoreFlows()
registerCordappFlows()
services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
val rpcOps = makeRPCOps()
val rpcOps = makeRPCOps(cordappLoader)
startShell()
networkMapClient?.start(trustRoot)

View File

@ -0,0 +1,29 @@
package net.corda.node.internal.rpc.proxies
import net.corda.core.internal.executeWithThreadContextClassLoader
import net.corda.core.messaging.CordaRPCOps
import net.corda.node.internal.InvocationHandlerTemplate
import java.lang.reflect.Method
import java.lang.reflect.Proxy
/**
* A [CordaRPCOps] proxy that adjusts the thread context's class loader temporarily on every invocation with the provided classloader.
* As an example, this can be used to work-around cases, where 3rd party libraries prioritise the thread context's class loader over the current one,
* without sensible fallbacks to the classloader of the current instance.
* If clients' CorDapps use one of these libraries, this temporary adjustment can ensure that any provided classes from these libraries will be available during RPC calls.
*/
internal class ThreadContextAdjustingRpcOpsProxy(private val delegate: CordaRPCOps, private val classLoader: ClassLoader): CordaRPCOps by proxy(delegate, classLoader) {
private companion object {
private fun proxy(delegate: CordaRPCOps, classLoader: ClassLoader): CordaRPCOps {
val handler = ThreadContextAdjustingRpcOpsProxy.ThreadContextAdjustingInvocationHandler(delegate, classLoader)
return Proxy.newProxyInstance(delegate::class.java.classLoader, arrayOf(CordaRPCOps::class.java), handler) as CordaRPCOps
}
}
private class ThreadContextAdjustingInvocationHandler(override val delegate: CordaRPCOps, private val classLoader: ClassLoader) : InvocationHandlerTemplate {
override fun invoke(proxy: Any, method: Method, arguments: Array<out Any?>?): Any? {
return executeWithThreadContextClassLoader(this.classLoader) { super.invoke(proxy, method, arguments) }
}
}
}

View File

@ -0,0 +1,32 @@
package net.corda.node.internal.rpc.proxies
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.CordaRPCOps
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.Mockito.`when`
class ThreadContextAdjustingRpcOpsProxyTest {
private val coreOps = mock<InstrumentedCordaRPCOps>()
private val mockClassloader = mock<ClassLoader>()
private val proxy = ThreadContextAdjustingRpcOpsProxy(coreOps, mockClassloader)
private interface InstrumentedCordaRPCOps: CordaRPCOps {
fun getThreadContextClassLoader(): ClassLoader = Thread.currentThread().contextClassLoader
}
@Test
fun verifyThreadContextIsAdjustedTemporarily() {
`when`(coreOps.killFlow(any())).thenAnswer {
assertThat(Thread.currentThread().contextClassLoader).isEqualTo(mockClassloader)
true
}
val result = proxy.killFlow(StateMachineRunId.createRandom())
assertThat(Thread.currentThread().contextClassLoader).isNotEqualTo(mockClassloader)
}
}