mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
CORDA-599 RPCSecurityManager is no longer lateinit (#2347)
This commit is contained in:
parent
094e96d303
commit
75e74e67a1
@ -242,10 +242,14 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt {
|
||||
}
|
||||
|
||||
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel)
|
||||
|
||||
inline fun <reified T> Stream<out T>.toTypedArray() = toTypedArray(T::class.java)
|
||||
// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
|
||||
inline fun <reified T> Stream<out T>.toTypedArray(): Array<T> = uncheckedCast(toArray { size -> arrayOfNulls<T>(size) })
|
||||
fun <T> Stream<out T>.toTypedArray(componentType: Class<T>): Array<T> = toArray { size ->
|
||||
uncheckedCast<Any, Array<T?>>(java.lang.reflect.Array.newInstance(componentType, size))
|
||||
}
|
||||
|
||||
fun <T> Stream<out T?>.filterNotNull(): Stream<T> = uncheckedCast(filter(Objects::nonNull))
|
||||
fun <K, V> Stream<out Pair<K, V>>.toMap(): Map<K, V> = collect<LinkedHashMap<K, V>>(::LinkedHashMap, { m, (k, v) -> m.put(k, v) }, { m, t -> m.putAll(t) })
|
||||
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null
|
||||
|
||||
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */
|
||||
|
@ -3,6 +3,7 @@ package net.corda.core.internal
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Test
|
||||
import java.io.Serializable
|
||||
import java.util.stream.IntStream
|
||||
import java.util.stream.Stream
|
||||
import kotlin.test.assertEquals
|
||||
@ -87,5 +88,17 @@ class InternalUtilsTest {
|
||||
val b: Array<String?> = Stream.of("one", "two", null).toTypedArray()
|
||||
assertEquals(Array<String?>::class.java, b.javaClass)
|
||||
assertArrayEquals(arrayOf("one", "two", null), b)
|
||||
val c: Array<CharSequence> = Stream.of("x", "y").toTypedArray(CharSequence::class.java)
|
||||
assertEquals(Array<CharSequence>::class.java, c.javaClass)
|
||||
assertArrayEquals(arrayOf("x", "y"), c)
|
||||
val d: Array<CharSequence?> = Stream.of("x", "y", null).toTypedArray(uncheckedCast(CharSequence::class.java))
|
||||
assertEquals(Array<CharSequence?>::class.java, d.javaClass)
|
||||
assertArrayEquals(arrayOf("x", "y", null), d)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Stream of Pairs toMap works`() {
|
||||
val m: Map<Comparable<*>, Serializable> = Stream.of<Pair<Comparable<*>, Serializable>>("x" to "y", 0 to 1, "x" to '2').toMap()
|
||||
assertEquals<Map<*, *>>(mapOf("x" to '2', 0 to 1), m)
|
||||
}
|
||||
}
|
||||
|
109
node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt
Normal file
109
node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt
Normal file
@ -0,0 +1,109 @@
|
||||
package net.corda.lazyhub
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
/** Supertype of all exceptions thrown directly by [LazyHub]. */
|
||||
@CordaSerializable
|
||||
abstract class LazyHubException(message: String) : RuntimeException(message)
|
||||
|
||||
/** The type can't be instantiated because it is abstract, i.e. it's an interface or abstract class. */
|
||||
class AbstractTypeException(message: String) : LazyHubException(message)
|
||||
|
||||
/**
|
||||
* The class can't be instantiated because it has no public constructor.
|
||||
* This is so that you can easily hide a constructor from LazyHub by making it non-public.
|
||||
*/
|
||||
class NoPublicConstructorsException(message: String) : LazyHubException(message)
|
||||
|
||||
/**
|
||||
* Nullable factory return types are not supported, as LazyHub has no concept of a provider that MAY supply an object.
|
||||
* If you want an optional result, use logic to decide whether to add the factory to the lazyHub.
|
||||
*/
|
||||
class NullableReturnTypeException(message: String) : LazyHubException(message)
|
||||
|
||||
/** The parameter can't be satisfied and doesn't have a default and isn't nullable. */
|
||||
abstract class UnsatisfiableParamException(message: String) : LazyHubException(message)
|
||||
|
||||
/** No provider has been registered for the wanted type. */
|
||||
class NoSuchProviderException(message: String) : UnsatisfiableParamException(message)
|
||||
|
||||
/**
|
||||
* No provider has been registered for the component type of the wanted array.
|
||||
* Note that LazyHub does not create empty arrays, make the array param type nullable to accept no elements.
|
||||
* This allows you to express zero-or-more (nullable) or one-or-more via the parameter type.
|
||||
*/
|
||||
class UnsatisfiableArrayException(message: String) : UnsatisfiableParamException(message)
|
||||
|
||||
/** More than one provider has been registered for the type but at most one object is wanted. */
|
||||
class TooManyProvidersException(message: String) : UnsatisfiableParamException(message)
|
||||
|
||||
/**
|
||||
* More than one public constructor is satisfiable and there is no clear winner.
|
||||
* The winner is the constructor with the most params for which LazyHub actually supplies an arg.
|
||||
*/
|
||||
class NoUniqueGreediestSatisfiableConstructorException(message: String) : LazyHubException(message)
|
||||
|
||||
/** The object being created depends on itself, i.e. it's already being instantiated/factoried. */
|
||||
class CircularDependencyException(message: String) : LazyHubException(message)
|
||||
|
||||
/** Depend on this as a param (and add the [MutableLazyHub], which is a [LazyHubFactory], to itself) if you want to make child containers. */
|
||||
interface LazyHubFactory {
|
||||
fun child(): MutableLazyHub
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-only interface to the lazyHub.
|
||||
* Where possible, always obtain your object via a constructor/method param instead of directly from the [LazyHub].
|
||||
* This results in the greatest automatic benefits to the codebase e.g. separation of concerns and ease of testing.
|
||||
* A notable exception to this rule is `getAll(Unit::class)` to (idempotently) run all side-effects.
|
||||
*/
|
||||
interface LazyHub : LazyHubFactory {
|
||||
operator fun <T : Any> get(clazz: KClass<T>) = get(clazz.java)
|
||||
operator fun <T> get(clazz: Class<T>) = getOrNull(clazz) ?: throw NoSuchProviderException(clazz.toString())
|
||||
fun <T : Any> getAll(clazz: KClass<T>) = getAll(clazz.java)
|
||||
fun <T> getAll(clazz: Class<T>): List<T>
|
||||
fun <T : Any> getOrNull(clazz: KClass<T>) = getOrNull(clazz.java)
|
||||
fun <T> getOrNull(clazz: Class<T>): T?
|
||||
}
|
||||
|
||||
/** Fully-featured interface to the lazyHub. */
|
||||
interface MutableLazyHub : LazyHub {
|
||||
/** Register the given object against its class and all supertypes. */
|
||||
fun obj(obj: Any)
|
||||
|
||||
/** Like plain old [MutableLazyHub.obj] but removes all [service] providers first. */
|
||||
fun <T : Any> obj(service: KClass<T>, obj: T)
|
||||
|
||||
/**
|
||||
* Register the given class as a provider for itself and all supertypes.
|
||||
* The class is instantiated at most once, using the greediest public constructor satisfiable at the time.
|
||||
*/
|
||||
fun impl(impl: KClass<*>)
|
||||
|
||||
/**
|
||||
* Same as [MutableLazyHub.impl] if you don't have a static reference to the class.
|
||||
* Note that Kotlin features such as nullable params and default args will not be available.
|
||||
*/
|
||||
fun impl(impl: Class<*>)
|
||||
|
||||
/** Like plain old [MutableLazyHub.impl] but removes all [service] providers first. */
|
||||
fun <S : Any, T : S> impl(service: KClass<S>, impl: KClass<T>)
|
||||
|
||||
/** Like the [KClass] variant if you don't have a static reference fo the class. */
|
||||
fun <S : Any, T : S> impl(service: KClass<S>, impl: Class<T>)
|
||||
|
||||
/**
|
||||
* Register the given function as a provider for its **declared** return type and all supertypes.
|
||||
* The function is invoked at most once. Unlike constructors, the function may have any visibility.
|
||||
* By convention the function should have side-effects iff its return type is [Unit].
|
||||
*/
|
||||
fun factory(factory: KFunction<*>)
|
||||
|
||||
/** Register a factory that provides the given type from the given hub. */
|
||||
fun factory(lh: LazyHub, type: KClass<*>)
|
||||
|
||||
/** Like plain old [MutableLazyHub.factory] but removes all [service] providers first. */
|
||||
fun <S : Any, T : S> factory(service: KClass<S>, factory: KFunction<T>)
|
||||
}
|
200
node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt
Normal file
200
node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt
Normal file
@ -0,0 +1,200 @@
|
||||
package net.corda.lazyhub
|
||||
|
||||
import net.corda.core.internal.filterNotNull
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.lazyhub.JConcrete.Companion.validate
|
||||
import net.corda.lazyhub.KConcrete.Companion.validate
|
||||
import net.corda.lazyhub.KConstructor.Companion.validate
|
||||
import java.util.*
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.stream.Stream
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
/**
|
||||
* Create a new [MutableLazyHub] with no parent.
|
||||
*
|
||||
* Basic usage:
|
||||
* * Add classes/factories/objects to the LazyHub using [MutableLazyHub.impl], [MutableLazyHub.factory] and [MutableLazyHub.obj]
|
||||
* * Then ask it for a type using [LazyHub.get] and it will create (and cache) the object graph for you
|
||||
* * You can use [LazyHub.getAll] to get all objects of a type, e.g. by convention pass in [Unit] to run side-effects
|
||||
*
|
||||
* How it works:
|
||||
* * [LazyHub.get] finds the unique registered class/factory/object for the given type (or fails)
|
||||
* * If it's an object, that object is returned
|
||||
* * If it's a factory, it is executed with args obtained recursively from the same LazyHub
|
||||
* * If it's a class, it is instantiated using a public constructor in the same way as a factory
|
||||
* * Of the public constructors that can be satisfied, the one that consumes the most args is chosen
|
||||
*
|
||||
* Advanced usage:
|
||||
* * Use an array parameter to get one-or-more args of the component type, make it nullable for zero-or-more
|
||||
* * If a LazyHub can't satisfy a type (or array param) and has a parent, it asks the parent
|
||||
* * Typically the root LazyHub in the hierarchy will manage all singletons of the process
|
||||
*/
|
||||
fun lazyHub(): MutableLazyHub = LazyHubImpl(null)
|
||||
|
||||
private class SimpleProvider<T : Any>(override val obj: T) : Provider<T> {
|
||||
override val type get() = obj.javaClass
|
||||
}
|
||||
|
||||
private class LazyProvider<T>(private val busyProviders: BusyProviders, private val underlying: Any?, override val type: Class<T>, val chooseInvocation: () -> Callable<T>) : Provider<T> {
|
||||
override val obj by lazy { busyProviders.runFactory(this) }
|
||||
override fun toString() = underlying.toString()
|
||||
}
|
||||
|
||||
private class Invocation<P, T>(val constructor: PublicConstructor<P, T>, val argSuppliers: List<Pair<P, ArgSupplier>>) : Callable<T> {
|
||||
fun providerCount() = argSuppliers.stream().filter { (_, supplier) -> supplier.provider != null }.count() // Allow repeated providers.
|
||||
override fun call() = constructor(argSuppliers)
|
||||
override fun toString() = constructor.toString()
|
||||
}
|
||||
|
||||
private class BusyProviders {
|
||||
private val busyProviders = mutableMapOf<LazyProvider<*>, Callable<*>>()
|
||||
fun <T> runFactory(provider: LazyProvider<T>): T {
|
||||
if (busyProviders.contains(provider)) throw CircularDependencyException("Provider '$provider' is already busy: ${busyProviders.values}")
|
||||
val invocation = provider.chooseInvocation()
|
||||
busyProviders.put(provider, invocation)
|
||||
try {
|
||||
return invocation.call()
|
||||
} finally {
|
||||
busyProviders.remove(provider)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val autotypes: Map<Class<*>, Class<*>> = mutableMapOf<Class<*>, Class<*>>().apply {
|
||||
Arrays::class.java.declaredMethods.filter { it.name == "hashCode" }.map { it.parameterTypes[0].componentType }.filter { it.isPrimitive }.forEach {
|
||||
val boxed = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(it, 1), 0).javaClass
|
||||
put(it, boxed)
|
||||
put(boxed, it)
|
||||
}
|
||||
}
|
||||
|
||||
private infix fun Class<*>.isSatisfiedBy(clazz: Class<*>): Boolean {
|
||||
return isAssignableFrom(clazz) || autotypes[this] == clazz
|
||||
}
|
||||
|
||||
private class LazyHubImpl(private val parent: LazyHubImpl?, private val busyProviders: BusyProviders = parent?.busyProviders ?: BusyProviders()) : MutableLazyHub {
|
||||
private val providers = mutableMapOf<Class<*>, MutableList<Provider<*>>>()
|
||||
private fun add(provider: Provider<*>, type: Class<*> = provider.type, registered: MutableSet<Class<*>> = mutableSetOf()) {
|
||||
if (!registered.add(type)) return
|
||||
providers[type]?.add(provider) ?: providers.put(type, mutableListOf(provider))
|
||||
Stream.concat(Arrays.stream(type.interfaces), Stream.of(type.superclass, autotypes[type]).filterNotNull()).forEach {
|
||||
add(provider, it, registered)
|
||||
}
|
||||
}
|
||||
|
||||
/** The non-empty list of providers, or null. */
|
||||
private fun <T> findProviders(clazz: Class<T>): List<Provider<T>>? = uncheckedCast(providers[clazz]) ?: parent?.findProviders(clazz)
|
||||
|
||||
private fun dropAll(serviceClass: Class<*>) {
|
||||
val removed = mutableSetOf<Provider<*>>()
|
||||
providers.iterator().run {
|
||||
while (hasNext()) {
|
||||
val entry = next()
|
||||
if (serviceClass isSatisfiedBy entry.key) {
|
||||
removed.addAll(entry.value)
|
||||
remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
providers.values.iterator().run {
|
||||
while (hasNext()) {
|
||||
val providers = next()
|
||||
providers.removeAll(removed)
|
||||
if (providers.isEmpty()) remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T> getOrNull(clazz: Class<T>) = findProviders(clazz)?.run { (singleOrNull() ?: throw TooManyProvidersException(clazz.toString())).obj }
|
||||
override fun <T> getAll(clazz: Class<T>) = findProviders(clazz)?.map { it.obj } ?: emptyList()
|
||||
override fun child(): MutableLazyHub = LazyHubImpl(this)
|
||||
override fun obj(obj: Any) = add(SimpleProvider(obj))
|
||||
override fun <T : Any> obj(service: KClass<T>, obj: T) {
|
||||
dropAll(service.java)
|
||||
obj(obj)
|
||||
}
|
||||
|
||||
override fun <S : Any, T : S> factory(service: KClass<S>, factory: KFunction<T>) = factory.validate().let {
|
||||
dropAll(service.java)
|
||||
addFactory(it)
|
||||
}
|
||||
|
||||
override fun <S : Any, T : S> impl(service: KClass<S>, impl: KClass<T>) = impl.validate().let {
|
||||
dropAll(service.java)
|
||||
addConcrete(it)
|
||||
}
|
||||
|
||||
override fun <S : Any, T : S> impl(service: KClass<S>, impl: Class<T>) = impl.validate().let {
|
||||
dropAll(service.java)
|
||||
addConcrete(it)
|
||||
}
|
||||
|
||||
override fun factory(factory: KFunction<*>) = addFactory(factory.validate())
|
||||
private fun <T> addFactory(factory: KConstructor<T>) {
|
||||
val type = factory.kFunction.returnType.toJavaType().let { if (it == Void.TYPE) Unit::class.java else it as Class<*> }
|
||||
add(LazyProvider(busyProviders, factory, uncheckedCast(type)) { factory.toInvocation() })
|
||||
}
|
||||
|
||||
override fun factory(lh: LazyHub, type: KClass<*>) = addFactory(lh, type)
|
||||
private fun <T : Any> addFactory(lh: LazyHub, type: KClass<T>) {
|
||||
add(LazyProvider(busyProviders, lh, type.java) { Callable { lh[type] } })
|
||||
}
|
||||
|
||||
override fun impl(impl: KClass<*>) = implGeneric(impl)
|
||||
private fun <T : Any> implGeneric(type: KClass<T>) = addConcrete(type.validate())
|
||||
override fun impl(impl: Class<*>) = implGeneric(impl)
|
||||
private fun <T> implGeneric(type: Class<T>) = addConcrete(type.validate())
|
||||
private fun <P : Param, T, C : PublicConstructor<P, T>> addConcrete(concrete: Concrete<T, C>) {
|
||||
add(LazyProvider(busyProviders, concrete, concrete.clazz) {
|
||||
var fail: UnsatisfiableParamException? = null
|
||||
val satisfiable = concrete.publicConstructors.mapNotNull { constructor ->
|
||||
try {
|
||||
constructor.toInvocation()
|
||||
} catch (e: UnsatisfiableParamException) {
|
||||
fail?.addSuppressed(e) ?: run { fail = e }
|
||||
null
|
||||
}
|
||||
}
|
||||
if (satisfiable.isEmpty()) throw fail!!
|
||||
val greediest = mutableListOf(satisfiable[0])
|
||||
var providerCount = greediest[0].providerCount()
|
||||
satisfiable.stream().skip(1).forEach next@ {
|
||||
val pc = it.providerCount()
|
||||
if (pc < providerCount) return@next
|
||||
if (pc > providerCount) {
|
||||
greediest.clear()
|
||||
providerCount = pc
|
||||
}
|
||||
greediest += it
|
||||
}
|
||||
greediest.singleOrNull() ?: throw NoUniqueGreediestSatisfiableConstructorException(greediest.toString())
|
||||
})
|
||||
}
|
||||
|
||||
private fun <T> arrayProvider(arrayType: Class<*>, componentType: Class<T>): LazyProvider<Array<T>>? {
|
||||
val providers = findProviders(componentType) ?: return null
|
||||
return LazyProvider(busyProviders, null, uncheckedCast(arrayType)) {
|
||||
Callable { providers.stream().map { it.obj }.toTypedArray(componentType) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun <P : Param, T> PublicConstructor<P, T>.toInvocation() = Invocation(this, params.mapNotNull { param ->
|
||||
if (param.type.isArray) {
|
||||
val provider = arrayProvider(param.type, param.type.componentType)
|
||||
when (provider) {
|
||||
null -> param.supplierWhenUnsatisfiable()?.let { param to it }
|
||||
else -> param to ArgSupplier(provider)
|
||||
}
|
||||
} else {
|
||||
val providers = findProviders(param.type)
|
||||
when (providers?.size) {
|
||||
null -> param.supplierWhenUnsatisfiable()?.let { param to it }
|
||||
1 -> param to ArgSupplier(providers[0])
|
||||
else -> throw TooManyProvidersException(param.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
130
node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt
Normal file
130
node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt
Normal file
@ -0,0 +1,130 @@
|
||||
package net.corda.lazyhub
|
||||
|
||||
import net.corda.core.internal.toMap
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import java.lang.reflect.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KVisibility
|
||||
import kotlin.reflect.jvm.internal.ReflectProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
|
||||
private val javaTypeDelegateField = Class.forName("kotlin.reflect.jvm.internal.KTypeImpl").getDeclaredField("javaType\$delegate").apply { isAccessible = true }
|
||||
internal fun kotlin.reflect.KType.toJavaType() = (javaTypeDelegateField.get(this) as ReflectProperties.Val<*>)()
|
||||
internal interface Provider<T> {
|
||||
/** Most specific known type i.e. directly registered implementation class, or declared return type of factory method. */
|
||||
val type: Class<T>
|
||||
/** May be lazily computed. */
|
||||
val obj: T
|
||||
}
|
||||
|
||||
/** Like [Provider] but capable of supplying null. */
|
||||
internal class ArgSupplier(val provider: Provider<*>?) {
|
||||
companion object {
|
||||
val nullSupplier = ArgSupplier(null)
|
||||
}
|
||||
|
||||
operator fun invoke() = provider?.obj
|
||||
}
|
||||
|
||||
/** Common interface to Kotlin/Java params. */
|
||||
internal interface Param {
|
||||
val type: Class<*>
|
||||
/** The supplier, or null to supply nothing so the Kotlin default is used. */
|
||||
fun supplierWhenUnsatisfiable(): ArgSupplier? = throw (if (type.isArray) ::UnsatisfiableArrayException else ::NoSuchProviderException)(toString())
|
||||
}
|
||||
|
||||
internal class KParam(val kParam: KParameter) : Param {
|
||||
override val type = run {
|
||||
var jType = kParam.type.toJavaType()
|
||||
loop@ while (true) {
|
||||
jType = when (jType) {
|
||||
is ParameterizedType -> jType.rawType
|
||||
is TypeVariable<*> -> jType.bounds.first() // Potentially surprising but most consistent behaviour, see unit tests.
|
||||
else -> break@loop
|
||||
}
|
||||
}
|
||||
jType as Class<*>
|
||||
}
|
||||
|
||||
override fun supplierWhenUnsatisfiable() = when {
|
||||
kParam.isOptional -> null // Use default value, even if param is also nullable.
|
||||
kParam.type.isMarkedNullable -> ArgSupplier.nullSupplier
|
||||
else -> super.supplierWhenUnsatisfiable()
|
||||
}
|
||||
|
||||
override fun toString() = kParam.toString()
|
||||
}
|
||||
|
||||
internal class JParam(private val param: Parameter, private val index: Int, override val type: Class<*>) : Param {
|
||||
override fun toString() = "parameter #$index ${param.name} of ${param.declaringExecutable}"
|
||||
}
|
||||
|
||||
internal interface PublicConstructor<P, out T> {
|
||||
val params: List<P>
|
||||
operator fun invoke(argSuppliers: List<Pair<P, ArgSupplier>>): T
|
||||
}
|
||||
|
||||
internal class KConstructor<out T>(val kFunction: KFunction<T>) : PublicConstructor<KParam, T> {
|
||||
companion object {
|
||||
fun <T> KFunction<T>.validate() = run {
|
||||
if (returnType.isMarkedNullable) throw NullableReturnTypeException(toString())
|
||||
isAccessible = true
|
||||
KConstructor(this)
|
||||
}
|
||||
}
|
||||
|
||||
override val params = kFunction.parameters.map(::KParam)
|
||||
override fun invoke(argSuppliers: List<Pair<KParam, ArgSupplier>>): T {
|
||||
return kFunction.callBy(argSuppliers.stream().map { (param, supplier) -> param.kParam to supplier() }.toMap())
|
||||
}
|
||||
|
||||
override fun toString() = kFunction.toString()
|
||||
}
|
||||
|
||||
internal class JConstructor<out T>(private val constructor: Constructor<T>) : PublicConstructor<JParam, T> {
|
||||
// Much cheaper to get the types up-front than via the Parameter API:
|
||||
override val params = constructor.parameters.zip(constructor.parameterTypes).mapIndexed { i, (p, t) -> JParam(p, i, t) }
|
||||
|
||||
override fun invoke(argSuppliers: List<Pair<JParam, ArgSupplier>>): T {
|
||||
return constructor.newInstance(*argSuppliers.stream().map { (_, supplier) -> supplier() }.toTypedArray())
|
||||
}
|
||||
|
||||
override fun toString() = constructor.toString()
|
||||
}
|
||||
|
||||
internal interface Concrete<T, out C : PublicConstructor<*, T>> {
|
||||
val clazz: Class<T>
|
||||
val publicConstructors: List<C>
|
||||
}
|
||||
|
||||
internal class KConcrete<T : Any> private constructor(private val kClass: KClass<T>) : Concrete<T, KConstructor<T>> {
|
||||
companion object {
|
||||
fun <T : Any> KClass<T>.validate() = run {
|
||||
if (isAbstract) throw AbstractTypeException(toString())
|
||||
KConcrete(this).apply {
|
||||
if (publicConstructors.isEmpty()) throw NoPublicConstructorsException(toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val clazz get() = kClass.java
|
||||
override val publicConstructors = kClass.constructors.filter { it.visibility == KVisibility.PUBLIC }.map(::KConstructor)
|
||||
override fun toString() = kClass.toString()
|
||||
}
|
||||
|
||||
internal class JConcrete<T> private constructor(override val clazz: Class<T>) : Concrete<T, JConstructor<T>> {
|
||||
companion object {
|
||||
fun <T> Class<T>.validate() = run {
|
||||
if (Modifier.isAbstract(modifiers)) throw AbstractTypeException(toString())
|
||||
JConcrete(this).apply {
|
||||
if (publicConstructors.isEmpty()) throw NoPublicConstructorsException(toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val publicConstructors = uncheckedCast<Array<out Constructor<*>>, Array<Constructor<T>>>(clazz.constructors).map(::JConstructor)
|
||||
override fun toString() = clazz.toString()
|
||||
}
|
@ -28,12 +28,14 @@ import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.lazyhub.LazyHub
|
||||
import net.corda.lazyhub.MutableLazyHub
|
||||
import net.corda.lazyhub.lazyHub
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.classloading.requireAnnotation
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.internal.cordapp.CordappProviderInternal
|
||||
import net.corda.node.internal.security.RPCSecurityManager
|
||||
import net.corda.node.services.ContractUpgradeHandler
|
||||
import net.corda.node.services.FinalityHandler
|
||||
import net.corda.node.services.NotaryChangeHandler
|
||||
@ -56,7 +58,6 @@ import net.corda.node.services.transactions.*
|
||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||
import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.node.services.vault.VaultSoftLockManager
|
||||
import net.corda.node.shell.InteractiveShell
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
@ -144,9 +145,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
protected val runOnStop = ArrayList<() -> Any?>()
|
||||
private val _nodeReadyFuture = openFuture<Unit>()
|
||||
protected var networkMapClient: NetworkMapClient? = null
|
||||
|
||||
lateinit var securityManager: RPCSecurityManager get
|
||||
|
||||
/** Completes once the node has successfully registered with the network map service
|
||||
* or has loaded network map data from local database */
|
||||
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
|
||||
@ -200,22 +198,31 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun configure(lh: MutableLazyHub) {
|
||||
// TODO: Migrate classes and factories from start method.
|
||||
}
|
||||
|
||||
open fun start(): StartedNode<AbstractNode> {
|
||||
check(started == null) { "Node has already been started" }
|
||||
log.info("Node starting up ...")
|
||||
initCertificate()
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
val lh = lazyHub()
|
||||
configure(lh)
|
||||
val identityService = makeIdentityService(identity.certificate)
|
||||
lh.obj(identityService)
|
||||
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
|
||||
retrieveNetworkParameters(identityService.trustRoot)
|
||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||
lh.obj(database)
|
||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
||||
val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair)
|
||||
lh.obj(info)
|
||||
identityService.loadIdentities(info.legalIdentitiesAndCerts)
|
||||
val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes)
|
||||
val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache)
|
||||
val nodeServices = makeServices(lh, keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache)
|
||||
val notaryService = makeNotaryService(nodeServices, database)
|
||||
val smm = makeStateMachineManager(database)
|
||||
val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader)
|
||||
@ -238,13 +245,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
}
|
||||
makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService, flowLogicRefFactory)
|
||||
val rpcOps = makeRPCOps(flowStarter, database, smm)
|
||||
startMessagingService(rpcOps)
|
||||
lh.obj(rpcOps)
|
||||
lh.getAll(Unit::class) // Run side-effects.
|
||||
installCoreFlows()
|
||||
val cordaServices = installCordaServices(flowStarter)
|
||||
tokenizableServices = nodeServices + cordaServices + schedulerService
|
||||
registerCordappFlows(smm)
|
||||
_services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
|
||||
startShell(rpcOps)
|
||||
Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService)
|
||||
}
|
||||
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
|
||||
@ -280,10 +287,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
*/
|
||||
protected abstract fun getRxIoScheduler(): Scheduler
|
||||
|
||||
open fun startShell(rpcOps: CordaRPCOps) {
|
||||
InteractiveShell.startShell(configuration, rpcOps, securityManager, _services.identityService, _services.database)
|
||||
}
|
||||
|
||||
private fun initNodeInfo(networkMapCache: NetworkMapCacheBaseInternal,
|
||||
identity: PartyAndCertificate,
|
||||
identityKeyPair: KeyPair): Pair<Set<KeyPair>, NodeInfo> {
|
||||
@ -534,7 +537,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
* Builds node internal, advertised, and plugin services.
|
||||
* Returns a list of tokenizable services to be added to the serialisation context.
|
||||
*/
|
||||
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> {
|
||||
private fun makeServices(lh: LazyHub, keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> {
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
val metrics = MetricRegistry()
|
||||
attachments = NodeAttachmentService(metrics)
|
||||
@ -550,7 +553,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
database,
|
||||
info,
|
||||
networkMapCache)
|
||||
network = makeMessagingService(database, info)
|
||||
network = lh[MessagingService::class] // TODO: Retire the lateinit var.
|
||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService,
|
||||
services.keyManagementService, services.identityService, platformClock,
|
||||
services.auditService, services.monitoringService, services.networkMapCache, services.schemaService,
|
||||
@ -715,9 +718,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
_started = null
|
||||
}
|
||||
|
||||
protected abstract fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService
|
||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||
|
||||
private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> {
|
||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
|
||||
|
@ -5,6 +5,7 @@ import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.ServiceHub
|
||||
@ -14,8 +15,10 @@ import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.lazyhub.MutableLazyHub
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
import net.corda.node.internal.security.RPCSecurityManager
|
||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||
import net.corda.node.serialization.KryoServerSerializationScheme
|
||||
import net.corda.node.services.api.SchemaService
|
||||
@ -24,6 +27,7 @@ import net.corda.node.services.config.SecurityConfiguration
|
||||
import net.corda.node.services.config.VerifierType
|
||||
import net.corda.node.services.messaging.*
|
||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.node.shell.InteractiveShell
|
||||
import net.corda.node.utilities.AddressUtils
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.node.utilities.DemoClock
|
||||
@ -133,16 +137,27 @@ open class Node(configuration: NodeConfiguration,
|
||||
private var messageBroker: ArtemisMessagingServer? = null
|
||||
|
||||
private var shutdownHook: ShutdownHook? = null
|
||||
|
||||
override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService {
|
||||
override fun configure(lh: MutableLazyHub) {
|
||||
super.configure(lh)
|
||||
// Construct security manager reading users data either from the 'security' config section
|
||||
// if present or from rpcUsers list if the former is missing from config.
|
||||
val securityManagerConfig = configuration.security?.authService ?:
|
||||
SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers)
|
||||
lh.obj(configuration.security?.authService ?: SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers))
|
||||
lh.impl(RPCSecurityManagerImpl::class)
|
||||
configuration.messagingServerAddress?.also {
|
||||
lh.obj(MessagingServerAddress(it))
|
||||
} ?: run {
|
||||
lh.factory(this::makeLocalMessageBroker)
|
||||
}
|
||||
lh.factory(this::makeMessagingService)
|
||||
// Side-effects:
|
||||
lh.factory(this::startMessagingService)
|
||||
lh.factory(this::startShell)
|
||||
}
|
||||
|
||||
securityManager = RPCSecurityManagerImpl(securityManagerConfig)
|
||||
class MessagingServerAddress(val address: NetworkHostAndPort)
|
||||
|
||||
val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
|
||||
private fun makeMessagingService(database: CordaPersistence, info: NodeInfo, messagingServerAddress: MessagingServerAddress): MessagingService {
|
||||
val serverAddress = messagingServerAddress.address
|
||||
val advertisedAddress = info.addresses.single()
|
||||
|
||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||
@ -162,10 +177,10 @@ open class Node(configuration: NodeConfiguration,
|
||||
networkParameters.maxMessageSize)
|
||||
}
|
||||
|
||||
private fun makeLocalMessageBroker(): NetworkHostAndPort {
|
||||
private fun makeLocalMessageBroker(securityManager: RPCSecurityManager): MessagingServerAddress {
|
||||
with(configuration) {
|
||||
messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize)
|
||||
return NetworkHostAndPort("localhost", p2pAddress.port)
|
||||
return MessagingServerAddress(NetworkHostAndPort("localhost", p2pAddress.port))
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +228,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
override fun startMessagingService(rpcOps: RPCOps) {
|
||||
private fun startMessagingService(rpcOps: RPCOps, securityManager: RPCSecurityManager) {
|
||||
// Start up the embedded MQ server
|
||||
messageBroker?.apply {
|
||||
runOnStop += this::stop
|
||||
@ -234,6 +249,10 @@ open class Node(configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
private fun startShell(rpcOps: CordaRPCOps, securityManager: RPCSecurityManager, identityService: IdentityService, database: CordaPersistence) {
|
||||
InteractiveShell.startShell(configuration, rpcOps, securityManager, identityService, database)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node is persisting to an embedded H2 database, then expose this via TCP with a DB URL of the form:
|
||||
* jdbc:h2:tcp://<host>:<port>/node
|
||||
|
640
node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt
Normal file
640
node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt
Normal file
@ -0,0 +1,640 @@
|
||||
package net.corda.lazyhub
|
||||
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import org.assertj.core.api.Assertions.catchThrowable
|
||||
import org.hamcrest.CoreMatchers.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.Closeable
|
||||
import java.io.IOException
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.jvm.javaConstructor
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.fail
|
||||
|
||||
open class LazyHubTests {
|
||||
private val lh = lazyHub()
|
||||
|
||||
class Config(val info: String)
|
||||
interface A
|
||||
interface B {
|
||||
val a: A
|
||||
}
|
||||
|
||||
class AImpl(val config: Config) : A
|
||||
class BImpl(override val a: A) : B
|
||||
class Spectator {
|
||||
init {
|
||||
fail("Should not be instantiated.")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `basic functionality`() {
|
||||
val config = Config("woo")
|
||||
lh.obj(config)
|
||||
lh.impl(AImpl::class)
|
||||
lh.impl(BImpl::class)
|
||||
lh.impl(Spectator::class)
|
||||
val b = lh[B::class]
|
||||
// An impl is instantiated at most once per LazyHub:
|
||||
assertSame(b.a, lh[A::class])
|
||||
assertSame(b, lh[B::class])
|
||||
// More specific type to expose config without casting:
|
||||
val a = lh[AImpl::class]
|
||||
assertSame(b.a, a)
|
||||
assertSame(config, a.config)
|
||||
}
|
||||
|
||||
private fun createA(config: Config): A = AImpl(config) // Declared return type is significant.
|
||||
internal open fun createB(): B = fail("Should not be called.")
|
||||
@Test
|
||||
fun `factory works`() {
|
||||
lh.obj(Config("x"))
|
||||
lh.factory(this::createA) // Observe private is OK.
|
||||
assertSame(AImpl::class.java, lh[A::class].javaClass)
|
||||
// The factory declares A not AImpl as its return type, and lh doesn't try to be clever:
|
||||
catchThrowable { lh[AImpl::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(AImpl::class.toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
class Subclass : LazyHubTests() { // Should not run as tests.
|
||||
@Suppress("unused")
|
||||
private fun createA(@Suppress("UNUSED_PARAMETER") config: Config): A = fail("Should not be called.")
|
||||
|
||||
override fun createB() = BImpl(AImpl(Config("Subclass"))) // More specific return type is OK.
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
internal fun addCreateATo(lh: MutableLazyHub) {
|
||||
lh.factory(this::createA)
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
internal fun addCreateBTo(lh: MutableLazyHub) {
|
||||
lh.factory(this::createB)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `private factory is not virtual`() {
|
||||
val baseMethod = this::createA.javaMethod!!
|
||||
// Check the Subclass version would override if baseMethod wasn't private:
|
||||
Subclass::class.java.getDeclaredMethod(baseMethod.name, *baseMethod.parameterTypes)
|
||||
lh.obj(Config("x"))
|
||||
Subclass().addCreateATo(lh)
|
||||
lh[A::class] // Should not blow up.
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-private factory is virtual`() {
|
||||
Subclass().addCreateBTo(lh)
|
||||
assertEquals("Subclass", (lh[B::class].a as AImpl).config.info) // Check overridden function was called.
|
||||
// The signature that was added declares B not BImpl as its return type:
|
||||
catchThrowable { lh[BImpl::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(BImpl::class.toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun returnsYay() = "yay"
|
||||
class TakesString(@Suppress("UNUSED_PARAMETER") text: String)
|
||||
|
||||
@Test
|
||||
fun `too many providers`() {
|
||||
lh.obj("woo")
|
||||
lh.factory(this::returnsYay)
|
||||
lh.impl(TakesString::class)
|
||||
catchThrowable { lh[TakesString::class] }.run {
|
||||
assertSame(TooManyProvidersException::class.java, javaClass)
|
||||
assertEquals(TakesString::class.constructors.single().parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(TakesString::class.qualifiedName))
|
||||
}
|
||||
}
|
||||
|
||||
class TakesStringOrInt(val text: String) {
|
||||
@Suppress("unused")
|
||||
constructor(number: Int) : this(number.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `too many providers with alternate constructor`() {
|
||||
lh.obj("woo")
|
||||
lh.factory(this::returnsYay)
|
||||
lh.impl(TakesStringOrInt::class)
|
||||
val constructors = TakesStringOrInt::class.constructors.toList()
|
||||
catchThrowable { lh[TakesStringOrInt::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(constructors[0].parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(TakesStringOrInt::class.qualifiedName))
|
||||
suppressed.single().run {
|
||||
assertSame(TooManyProvidersException::class.java, javaClass)
|
||||
assertEquals(constructors[1].parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(TakesStringOrInt::class.qualifiedName))
|
||||
}
|
||||
}
|
||||
lh.obj(123)
|
||||
assertEquals("123", lh[TakesStringOrInt::class].text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genericClass() {
|
||||
class G<out T : Serializable>(val arg: T)
|
||||
lh.obj("arg")
|
||||
lh.impl(G::class)
|
||||
assertEquals("arg", lh[G::class].arg) // Can't inspect type arg T as no such thing exists.
|
||||
}
|
||||
|
||||
private fun <X : Closeable, Y : X> ntv(a: Y) = a.toString()
|
||||
@Test
|
||||
fun `nested type variable`() {
|
||||
// First check it's actually legal to pass any old Closeable into the function:
|
||||
val arg = Closeable {}
|
||||
assertEquals(arg.toString(), ntv(arg))
|
||||
// Good, now check LazyHub can do it:
|
||||
val ntv: Function1<Closeable, String> = this::ntv
|
||||
lh.factory(uncheckedCast<Any, KFunction<String>>(ntv))
|
||||
lh.obj(arg)
|
||||
assertEquals(arg.toString(), lh[String::class])
|
||||
}
|
||||
|
||||
class PTWMB<out Y>(val arg: Y) where Y : Closeable, Y : Serializable
|
||||
private class CloseableAndSerializable : Closeable, Serializable {
|
||||
override fun close() {}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parameter type with multiple bounds in java`() {
|
||||
// At compile time we must pass something Closeable and Serializable into the constructor:
|
||||
CloseableAndSerializable().let { assertSame(it, PTWMB(it).arg) }
|
||||
// But at runtime only Closeable is needed (and Serializable is not enough) due to the leftmost bound erasure rule:
|
||||
lh.impl(PTWMB::class.java)
|
||||
lh.obj(object : Serializable {})
|
||||
catchThrowable { lh[PTWMB::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(PTWMB::class.constructors.single().javaConstructor.toString()))
|
||||
}
|
||||
val arg = Closeable {}
|
||||
lh.obj(arg)
|
||||
assertSame(arg, lh[PTWMB::class].arg)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parameter type with multiple bounds in kotlin`() {
|
||||
lh.impl(PTWMB::class)
|
||||
lh.obj(object : Serializable {})
|
||||
catchThrowable { lh[PTWMB::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(PTWMB::class.constructors.single().parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, containsString(PTWMB::class.qualifiedName))
|
||||
}
|
||||
val arg = Closeable {}
|
||||
lh.obj(arg)
|
||||
assertSame(arg, lh[PTWMB::class].arg)
|
||||
}
|
||||
|
||||
private fun <Y> ptwmb(arg: Y) where Y : Closeable, Y : Serializable = arg.toString()
|
||||
@Test
|
||||
fun `factory parameter type with multiple bounds`() {
|
||||
val ptwmb: Function1<CloseableAndSerializable, String> = this::ptwmb
|
||||
val kFunction = uncheckedCast<Any, KFunction<String>>(ptwmb)
|
||||
lh.factory(kFunction)
|
||||
lh.obj(object : Serializable {})
|
||||
catchThrowable { lh[String::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(kFunction.parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(ptwmb.toString()))
|
||||
}
|
||||
val arg = Closeable {}
|
||||
lh.obj(arg)
|
||||
assertEquals(arg.toString(), lh[String::class])
|
||||
}
|
||||
|
||||
private fun <Y> upt(a: Y) = a.toString()
|
||||
@Test
|
||||
fun `unbounded parameter type`() {
|
||||
val upt: Function1<Any, String> = this::upt
|
||||
val kFunction: KFunction<String> = uncheckedCast(upt)
|
||||
lh.factory(kFunction)
|
||||
// The only provider for Any is the factory, which is busy:
|
||||
catchThrowable { lh[String::class] }.run {
|
||||
assertSame(CircularDependencyException::class.java, javaClass)
|
||||
assertThat(message, containsString("'$upt'"))
|
||||
assertThat(message, endsWith(listOf(upt).toString()))
|
||||
}
|
||||
lh.obj(Any())
|
||||
// This time the factory isn't attempted:
|
||||
catchThrowable { lh[String::class] }.run {
|
||||
assertSame(TooManyProvidersException::class.java, javaClass)
|
||||
assertEquals(kFunction.parameters[0].toString(), message)
|
||||
assertThat(message, containsString(" #0 "))
|
||||
assertThat(message, endsWith(upt.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
open class NoPublicConstructor protected constructor()
|
||||
|
||||
@Test
|
||||
fun `no public constructor`() {
|
||||
catchThrowable { lh.impl(NoPublicConstructor::class) }.run {
|
||||
assertSame(NoPublicConstructorsException::class.java, javaClass)
|
||||
assertEquals(NoPublicConstructor::class.toString(), message)
|
||||
}
|
||||
catchThrowable { lh.impl(NoPublicConstructor::class.java) }.run {
|
||||
assertSame(NoPublicConstructorsException::class.java, javaClass)
|
||||
assertEquals(NoPublicConstructor::class.toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun primitiveInt() = 1
|
||||
class IntConsumer(@Suppress("UNUSED_PARAMETER") i: Int)
|
||||
class IntegerConsumer(@Suppress("UNUSED_PARAMETER") i: Int?)
|
||||
|
||||
@Test
|
||||
fun `boxed satisfies primitive`() {
|
||||
lh.obj(1)
|
||||
lh.impl(IntConsumer::class)
|
||||
lh[IntConsumer::class]
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `primitive satisfies boxed`() {
|
||||
lh.factory(this::primitiveInt)
|
||||
lh.impl(IntegerConsumer::class.java)
|
||||
lh[IntegerConsumer::class]
|
||||
}
|
||||
|
||||
// The primary constructor takes two distinct providers:
|
||||
class TakesTwoThings(@Suppress("UNUSED_PARAMETER") first: String, @Suppress("UNUSED_PARAMETER") second: Int) {
|
||||
// This constructor takes one repeated provider but we count it both times so greediness is 2:
|
||||
@Suppress("unused")
|
||||
constructor(first: Int, second: Int) : this(first.toString(), second)
|
||||
|
||||
// This constructor would be greediest but is not satisfiable:
|
||||
@Suppress("unused")
|
||||
constructor(first: Int, second: String, @Suppress("UNUSED_PARAMETER") third: Config) : this(second, first)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `equally greedy constructors kotlin`() {
|
||||
lh.obj("str")
|
||||
lh.obj(123)
|
||||
lh.impl(TakesTwoThings::class)
|
||||
catchThrowable { lh[TakesTwoThings::class] }.run {
|
||||
assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, javaClass)
|
||||
val expected = TakesTwoThings::class.constructors.filter { it.parameters.size == 2 }
|
||||
assertEquals(2, expected.size)
|
||||
assertThat(message, endsWith(expected.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `equally greedy constructors java`() {
|
||||
lh.obj("str")
|
||||
lh.obj(123)
|
||||
lh.impl(TakesTwoThings::class.java)
|
||||
catchThrowable { lh[TakesTwoThings::class] }.run {
|
||||
assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, javaClass)
|
||||
val expected = TakesTwoThings::class.java.constructors.filter { it.parameters.size == 2 }
|
||||
assertEquals(2, expected.size)
|
||||
assertEquals(expected.toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun nrt(): String? = fail("Should not be invoked.")
|
||||
@Test
|
||||
fun `nullable return type is banned`() {
|
||||
catchThrowable { lh.factory(this::nrt) }.run {
|
||||
assertSame(NullableReturnTypeException::class.java, javaClass)
|
||||
assertThat(message, endsWith(this@LazyHubTests::nrt.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unsatisfiableArrayParam() {
|
||||
class Impl(@Suppress("UNUSED_PARAMETER") v: Array<String>)
|
||||
lh.impl(Impl::class)
|
||||
catchThrowable { lh[Impl::class] }.run {
|
||||
assertSame(UnsatisfiableArrayException::class.java, javaClass)
|
||||
assertEquals(Impl::class.constructors.single().parameters[0].toString(), message)
|
||||
}
|
||||
// Arrays are only special in real params, you should use getAll to get all the Strings:
|
||||
catchThrowable { lh[Array<String>::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(Array<String>::class.java.toString(), message)
|
||||
}
|
||||
assertEquals(emptyList(), lh.getAll(String::class))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun arrayParam1() {
|
||||
class Impl(val v: Array<String>)
|
||||
lh.impl(Impl::class)
|
||||
lh.obj("a")
|
||||
assertArrayEquals(arrayOf("a"), lh[Impl::class].v)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun arrayParam2() {
|
||||
class Impl(val v: Array<String>)
|
||||
lh.impl(Impl::class)
|
||||
lh.obj("y")
|
||||
lh.obj("x")
|
||||
assertArrayEquals(arrayOf("y", "x"), lh[Impl::class].v)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nullableArrayParam() {
|
||||
class Impl(val v: Array<String>?)
|
||||
lh.impl(Impl::class)
|
||||
assertEquals(null, lh[Impl::class].v)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun arraysAreNotCached() {
|
||||
class B(val v: Array<String>)
|
||||
class A(val v: Array<String>, val b: B)
|
||||
class C(val v: Array<String>)
|
||||
class D(val v: Array<String>)
|
||||
lh.obj("x")
|
||||
lh.obj("y")
|
||||
lh.impl(A::class)
|
||||
lh.impl(B::class)
|
||||
val a = lh[A::class]
|
||||
a.run {
|
||||
assertArrayEquals(arrayOf("x", "y"), v)
|
||||
assertArrayEquals(arrayOf("x", "y"), b.v)
|
||||
assertNotSame(v, b.v)
|
||||
}
|
||||
assertSame(lh[B::class].v, a.b.v) // Because it's the same (cached) instance of B.
|
||||
lh.impl(C::class)
|
||||
lh[C::class].run {
|
||||
assertArrayEquals(arrayOf("x", "y"), v)
|
||||
assertNotSame(v, a.v)
|
||||
assertNotSame(v, a.b.v)
|
||||
}
|
||||
lh.obj("z")
|
||||
lh.impl(D::class)
|
||||
lh[D::class].run {
|
||||
assertArrayEquals(arrayOf("x", "y", "z"), v)
|
||||
}
|
||||
}
|
||||
|
||||
class C1(@Suppress("UNUSED_PARAMETER") c2: C2)
|
||||
class C2(@Suppress("UNUSED_PARAMETER") c3: String)
|
||||
|
||||
private fun c3(@Suppress("UNUSED_PARAMETER") c2: C2): String {
|
||||
fail("Should not be called.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `circularity error kotlin`() {
|
||||
lh.impl(C1::class)
|
||||
lh.impl(C2::class)
|
||||
lh.factory(this::c3)
|
||||
catchThrowable { lh[C1::class] }.run {
|
||||
assertSame(CircularDependencyException::class.java, javaClass)
|
||||
assertThat(message, containsString("'${C2::class}'"))
|
||||
assertThat(message, endsWith(listOf(C1::class.constructors.single(), C2::class.constructors.single(), this@LazyHubTests::c3).toString()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `circularity error java`() {
|
||||
lh.impl(C1::class.java)
|
||||
lh.impl(C2::class.java)
|
||||
lh.factory(this::c3)
|
||||
catchThrowable { lh[C1::class] }.run {
|
||||
assertSame(CircularDependencyException::class.java, javaClass)
|
||||
assertThat(message, containsString("'${C2::class}'"))
|
||||
assertThat(message, endsWith(listOf(C1::class.constructors.single().javaConstructor, C2::class.constructors.single().javaConstructor, this@LazyHubTests::c3).toString()))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ancestor hub providers are visible`() {
|
||||
val c = Config("over here")
|
||||
lh.obj(c)
|
||||
lh.child().also {
|
||||
it.impl(AImpl::class)
|
||||
assertSame(c, it[AImpl::class].config)
|
||||
}
|
||||
lh.child().child().also {
|
||||
it.impl(AImpl::class)
|
||||
assertSame(c, it[AImpl::class].config)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `descendant hub providers are not visible`() {
|
||||
val child = lh.child()
|
||||
child.obj(Config("over here"))
|
||||
lh.impl(AImpl::class)
|
||||
catchThrowable { lh[AImpl::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(AImpl::class.constructors.single().parameters.single().toString(), message)
|
||||
}
|
||||
// Fails even though we go via the child, as the cached AImpl in lh shouldn't have collaborators from descendant hubs:
|
||||
catchThrowable { child[AImpl::class] }.run {
|
||||
assertSame(NoSuchProviderException::class.java, javaClass)
|
||||
assertEquals(AImpl::class.constructors.single().parameters.single().toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
class AllConfigs(val configs: Array<Config>)
|
||||
|
||||
@Test
|
||||
fun `nearest ancestor with at least one provider wins`() {
|
||||
lh.obj(Config("deep"))
|
||||
lh.child().also {
|
||||
it.child().also {
|
||||
it.impl(AllConfigs::class)
|
||||
assertEquals(listOf("deep"), it[AllConfigs::class].configs.map { it.info })
|
||||
}
|
||||
it.obj(Config("shallow1"))
|
||||
it.obj(Config("shallow2"))
|
||||
it.child().also {
|
||||
it.impl(AllConfigs::class)
|
||||
assertEquals(listOf("shallow1", "shallow2"), it[AllConfigs::class].configs.map { it.info })
|
||||
}
|
||||
it.child().also {
|
||||
it.obj(Config("local"))
|
||||
it.impl(AllConfigs::class)
|
||||
assertEquals(listOf("local"), it[AllConfigs::class].configs.map { it.info })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `abstract type`() {
|
||||
catchThrowable { lh.impl(Runnable::class) }.run {
|
||||
assertSame(AbstractTypeException::class.java, javaClass)
|
||||
assertEquals(Runnable::class.toString(), message)
|
||||
}
|
||||
catchThrowable { lh.impl(Runnable::class.java) }.run {
|
||||
assertSame(AbstractTypeException::class.java, javaClass)
|
||||
assertEquals(Runnable::class.java.toString(), message)
|
||||
}
|
||||
}
|
||||
|
||||
private interface Service
|
||||
open class GoodService : Service
|
||||
abstract class BadService1 : Service
|
||||
class BadService2 private constructor() : Service
|
||||
|
||||
private fun badService3(): Service? = fail("Should not be called.")
|
||||
@Test
|
||||
fun `existing providers not removed if new type is bad`() {
|
||||
lh.impl(GoodService::class)
|
||||
catchThrowable { lh.impl(Service::class, BadService1::class) }.run {
|
||||
assertSame(AbstractTypeException::class.java, javaClass)
|
||||
assertEquals(BadService1::class.toString(), message)
|
||||
}
|
||||
catchThrowable { lh.impl(Service::class, BadService2::class) }.run {
|
||||
assertSame(NoPublicConstructorsException::class.java, javaClass)
|
||||
assertEquals(BadService2::class.toString(), message)
|
||||
}
|
||||
catchThrowable { lh.impl(Service::class, BadService2::class.java) }.run {
|
||||
assertSame(NoPublicConstructorsException::class.java, javaClass)
|
||||
assertEquals(BadService2::class.toString(), message)
|
||||
}
|
||||
// Type system won't let you pass in badService3, but I still want validation up-front:
|
||||
catchThrowable { lh.factory(Service::class, uncheckedCast(this::badService3)) }.run {
|
||||
assertSame(NullableReturnTypeException::class.java, javaClass)
|
||||
assertEquals(this@LazyHubTests::badService3.toString(), message)
|
||||
}
|
||||
assertSame(GoodService::class.java, lh[Service::class].javaClass)
|
||||
}
|
||||
|
||||
class GoodService2 : GoodService()
|
||||
|
||||
@Test
|
||||
fun `service providers are removed completely`() {
|
||||
lh.impl(GoodService::class)
|
||||
assertSame(GoodService::class.java, lh[Service::class].javaClass)
|
||||
lh.impl(GoodService::class, GoodService2::class)
|
||||
// In particular, GoodService is no longer registered against Service (or Any):
|
||||
assertSame(GoodService2::class.java, lh[Service::class].javaClass)
|
||||
assertSame(GoodService2::class.java, lh[Any::class].javaClass)
|
||||
}
|
||||
|
||||
class JParamExample(@Suppress("UNUSED_PARAMETER") str: String, @Suppress("UNUSED_PARAMETER") num: Int)
|
||||
|
||||
@Test
|
||||
fun `JParam has useful toString`() {
|
||||
val c = JParamExample::class.java.constructors.single()
|
||||
// Parameter doesn't expose its index, here we deliberately pass in the wrong one to see what happens:
|
||||
val text = JParam(c.parameters[0], 1, IOException::class.java).toString()
|
||||
assertThat(text, containsString(" #1 "))
|
||||
assertThat(text, anyOf(containsString(" str "), containsString(" arg0 ")))
|
||||
assertThat(text, endsWith(c.toString()))
|
||||
}
|
||||
|
||||
private val sideEffects = mutableListOf<Int>()
|
||||
private fun sideEffect1() {
|
||||
sideEffects.add(1)
|
||||
}
|
||||
|
||||
private fun sideEffect2() {
|
||||
sideEffects.add(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `side-effects are idempotent as a consequence of caching of results`() {
|
||||
lh.factory(this::sideEffect1)
|
||||
assertEquals(listOf(Unit), lh.getAll(Unit::class))
|
||||
assertEquals(listOf(1), sideEffects)
|
||||
lh.factory(this::sideEffect2)
|
||||
assertEquals(listOf(Unit, Unit), lh.getAll(Unit::class)) // Get both results.
|
||||
assertEquals(listOf(1, 2), sideEffects) // sideEffect1 didn't run again.
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAll returns empty list when there is nothing to return`() {
|
||||
// This is in contrast to the exception thrown by an array param, which would not be useful to replicate here:
|
||||
assertEquals(emptyList(), lh.getAll(IOException::class))
|
||||
}
|
||||
|
||||
// Two params needed to make primary constructor the winner when both are satisfiable.
|
||||
// It's probably true that the secondary will always trigger a CircularDependencyException, but LazyHub isn't clever enough to tell.
|
||||
class InvocationSwitcher(@Suppress("UNUSED_PARAMETER") s: String, @Suppress("UNUSED_PARAMETER") t: String) {
|
||||
@Suppress("unused")
|
||||
constructor(same: InvocationSwitcher) : this(same.toString(), same.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `chosen constructor is not set in stone`() {
|
||||
lh.impl(InvocationSwitcher::class)
|
||||
assertSame(CircularDependencyException::class.java, catchThrowable { lh[InvocationSwitcher::class] }.javaClass)
|
||||
lh.obj("alt")
|
||||
lh[InvocationSwitcher::class] // Succeeds via other constructor.
|
||||
}
|
||||
|
||||
class GreedinessUnits(@Suppress("UNUSED_PARAMETER") v: Array<String>, @Suppress("UNUSED_PARAMETER") z: Int) {
|
||||
// Two greediness units even though it's one provider repeated:
|
||||
@Suppress("unused")
|
||||
constructor(z1: Int, z2: Int) : this(emptyArray(), z1 + z2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `array param counts as one greediness unit`() {
|
||||
lh.obj("x")
|
||||
lh.obj("y")
|
||||
lh.obj(100)
|
||||
lh.impl(GreedinessUnits::class)
|
||||
assertSame(NoUniqueGreediestSatisfiableConstructorException::class.java, catchThrowable { lh[GreedinessUnits::class] }.javaClass)
|
||||
}
|
||||
|
||||
interface TriangleBase
|
||||
interface TriangleSide : TriangleBase
|
||||
class TriangleImpl : TriangleBase, TriangleSide
|
||||
|
||||
@Test
|
||||
fun `provider registered exactly once against each supertype`() {
|
||||
lh.impl(TriangleImpl::class)
|
||||
lh[TriangleBase::class] // Don't throw TooManyProvidersException.
|
||||
}
|
||||
|
||||
interface Service1
|
||||
interface Service2
|
||||
class ServiceImpl1 : Service1, Service2
|
||||
class ServiceImpl2 : Service2
|
||||
|
||||
@Test
|
||||
fun `do not leak empty provider list`() {
|
||||
lh.impl(ServiceImpl1::class)
|
||||
lh.impl(Service2::class, ServiceImpl2::class)
|
||||
assertSame(NoSuchProviderException::class.java, catchThrowable { lh[Service1::class] }.javaClass)
|
||||
}
|
||||
|
||||
class Global
|
||||
class Session(val global: Global, val local: Int)
|
||||
|
||||
@Test
|
||||
fun `child can be used to create a scope`() {
|
||||
lh.impl(Global::class)
|
||||
lh.factory(lh.child().also {
|
||||
it.obj(1)
|
||||
it.impl(Session::class)
|
||||
}, Session::class)
|
||||
lh.factory(lh.child().also {
|
||||
it.obj(2)
|
||||
it.impl(Session::class)
|
||||
}, Session::class)
|
||||
val sessions = lh.getAll(Session::class)
|
||||
val g = lh[Global::class]
|
||||
sessions.forEach { assertSame(g, it.global) }
|
||||
assertEquals(listOf(1, 2), sessions.map { it.local })
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import com.google.common.jimfs.Jimfs
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -15,17 +14,15 @@ import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.createDirectory
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.lazyhub.MutableLazyHub
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.StartedNode
|
||||
@ -294,9 +291,14 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
||||
}
|
||||
}
|
||||
|
||||
override fun configure(lh: MutableLazyHub) {
|
||||
super.configure(lh)
|
||||
lh.factory(this::makeMessagingService)
|
||||
}
|
||||
|
||||
// We only need to override the messaging service here, as currently everything that hits disk does so
|
||||
// through the java.nio API which we are already mocking via Jimfs.
|
||||
override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService {
|
||||
private fun makeMessagingService(database: CordaPersistence): MessagingService {
|
||||
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
|
||||
return mockNet.messagingNetwork.createNodeWithID(
|
||||
!mockNet.threadPerNode,
|
||||
@ -315,14 +317,6 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
||||
return E2ETestKeyManagementService(identityService, keyPairs)
|
||||
}
|
||||
|
||||
override fun startShell(rpcOps: CordaRPCOps) {
|
||||
//No mock shell
|
||||
}
|
||||
|
||||
override fun startMessagingService(rpcOps: RPCOps) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
// This is not thread safe, but node construction is done on a single thread, so that should always be fine
|
||||
override fun generateKeyPair(): KeyPair {
|
||||
counter = counter.add(BigInteger.ONE)
|
||||
|
Loading…
Reference in New Issue
Block a user