Revert "CORDA-599 RPCSecurityManager is no longer lateinit (#2347)"

This reverts commit 75e74e67a1.
This commit is contained in:
Mike Hearn 2018-01-19 16:59:31 +01:00
parent cfc5c6709a
commit ac7637e2b4
9 changed files with 42 additions and 1151 deletions

View File

@ -242,14 +242,10 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt {
} }
fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel) 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):
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)) // When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable):
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) }) inline fun <reified T> Stream<out T>.toTypedArray(): Array<T> = uncheckedCast(toArray { size -> arrayOfNulls<T>(size) })
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null 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]. */ /** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */

View File

@ -3,7 +3,6 @@ package net.corda.core.internal
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals
import org.junit.Test import org.junit.Test
import java.io.Serializable
import java.util.stream.IntStream import java.util.stream.IntStream
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -88,17 +87,5 @@ class InternalUtilsTest {
val b: Array<String?> = Stream.of("one", "two", null).toTypedArray() val b: Array<String?> = Stream.of("one", "two", null).toTypedArray()
assertEquals(Array<String?>::class.java, b.javaClass) assertEquals(Array<String?>::class.java, b.javaClass)
assertArrayEquals(arrayOf("one", "two", null), b) 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)
} }
} }

View File

@ -1,109 +0,0 @@
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>)
}

View File

@ -1,200 +0,0 @@
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())
}
}
})
}

View File

@ -1,130 +0,0 @@
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()
}

View File

@ -26,14 +26,12 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
import net.corda.core.utilities.getOrThrow 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.VersionInfo
import net.corda.node.internal.classloading.requireAnnotation import net.corda.node.internal.classloading.requireAnnotation
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.CordappProviderInternal 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.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler import net.corda.node.services.NotaryChangeHandler
@ -56,6 +54,7 @@ import net.corda.node.services.transactions.*
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.services.vault.VaultSoftLockManager
import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.nodeapi.internal.DevIdentityGenerator import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
@ -143,6 +142,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
protected val runOnStop = ArrayList<() -> Any?>() protected val runOnStop = ArrayList<() -> Any?>()
private val _nodeReadyFuture = openFuture<Unit>() private val _nodeReadyFuture = openFuture<Unit>()
protected var networkMapClient: NetworkMapClient? = null protected var networkMapClient: NetworkMapClient? = null
lateinit var securityManager: RPCSecurityManager get
/** Completes once the node has successfully registered with the network map service /** Completes once the node has successfully registered with the network map service
* or has loaded network map data from local database */ * or has loaded network map data from local database */
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
@ -197,31 +199,22 @@ 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> { open fun start(): StartedNode<AbstractNode> {
check(started == null) { "Node has already been started" } check(started == null) { "Node has already been started" }
log.info("Node starting up ...") log.info("Node starting up ...")
initCertificate() initCertificate()
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null) val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null)
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
val lh = lazyHub()
configure(lh)
val identityService = makeIdentityService(identity.certificate) val identityService = makeIdentityService(identity.certificate)
lh.obj(identityService)
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) } networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
retrieveNetworkParameters(identityService.trustRoot) retrieveNetworkParameters(identityService.trustRoot)
// Do all of this in a database transaction so anything that might need a connection has one. // Do all of this in a database transaction so anything that might need a connection has one.
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database -> val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
lh.obj(database)
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService) val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService)
val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair) val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair)
lh.obj(info)
identityService.loadIdentities(info.legalIdentitiesAndCerts) identityService.loadIdentities(info.legalIdentitiesAndCerts)
val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes) val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes)
val nodeServices = makeServices(lh, keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache) val nodeServices = makeServices(keyPairs, schemaService, transactionStorage, database, info, identityService, networkMapCache)
val notaryService = makeNotaryService(nodeServices, database) val notaryService = makeNotaryService(nodeServices, database)
val smm = makeStateMachineManager(database) val smm = makeStateMachineManager(database)
val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader)
@ -244,13 +237,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
} }
makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService, flowLogicRefFactory) makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService, flowLogicRefFactory)
val rpcOps = makeRPCOps(flowStarter, database, smm) val rpcOps = makeRPCOps(flowStarter, database, smm)
lh.obj(rpcOps) startMessagingService(rpcOps)
lh.getAll(Unit::class) // Run side-effects.
installCoreFlows() installCoreFlows()
val cordaServices = installCordaServices(flowStarter) val cordaServices = installCordaServices(flowStarter)
tokenizableServices = nodeServices + cordaServices + schedulerService tokenizableServices = nodeServices + cordaServices + schedulerService
registerCordappFlows(smm) registerCordappFlows(smm)
_services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows } _services.rpcFlows += cordappLoader.cordapps.flatMap { it.rpcFlows }
startShell(rpcOps)
Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService) Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService)
} }
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache, val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
@ -286,6 +279,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
*/ */
protected abstract fun getRxIoScheduler(): Scheduler protected abstract fun getRxIoScheduler(): Scheduler
open fun startShell(rpcOps: CordaRPCOps) {
InteractiveShell.startShell(configuration, rpcOps, securityManager, _services.identityService, _services.database)
}
private fun initNodeInfo(networkMapCache: NetworkMapCacheBaseInternal, private fun initNodeInfo(networkMapCache: NetworkMapCacheBaseInternal,
identity: PartyAndCertificate, identity: PartyAndCertificate,
identityKeyPair: KeyPair): Pair<Set<KeyPair>, NodeInfo> { identityKeyPair: KeyPair): Pair<Set<KeyPair>, NodeInfo> {
@ -536,7 +533,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
* Builds node internal, advertised, and plugin services. * Builds node internal, advertised, and plugin services.
* Returns a list of tokenizable services to be added to the serialisation context. * Returns a list of tokenizable services to be added to the serialisation context.
*/ */
private fun makeServices(lh: LazyHub, keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> { private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> {
checkpointStorage = DBCheckpointStorage() checkpointStorage = DBCheckpointStorage()
val metrics = MetricRegistry() val metrics = MetricRegistry()
attachments = NodeAttachmentService(metrics) attachments = NodeAttachmentService(metrics)
@ -552,7 +549,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
database, database,
info, info,
networkMapCache) networkMapCache)
network = lh[MessagingService::class] // TODO: Retire the lateinit var. network = makeMessagingService(database, info)
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, val tokenizableServices = mutableListOf(attachments, network, services.vaultService,
services.keyManagementService, services.identityService, platformClock, services.keyManagementService, services.identityService, platformClock,
services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, services.auditService, services.monitoringService, services.networkMapCache, services.schemaService,
@ -715,6 +712,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
_started = null _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> { private fun obtainIdentity(notaryConfig: NotaryConfig?): Pair<PartyAndCertificate, KeyPair> {
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)

View File

@ -5,7 +5,6 @@ import net.corda.core.concurrent.CordaFuture
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.concurrent.thenMatch import net.corda.core.internal.concurrent.thenMatch
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -15,10 +14,8 @@ import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.lazyhub.MutableLazyHub
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.cordapp.CordappLoader 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.internal.security.RPCSecurityManagerImpl
import net.corda.node.serialization.KryoServerSerializationScheme import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.node.services.api.SchemaService import net.corda.node.services.api.SchemaService
@ -27,7 +24,6 @@ import net.corda.node.services.config.SecurityConfiguration
import net.corda.node.services.config.VerifierType import net.corda.node.services.config.VerifierType
import net.corda.node.services.messaging.* import net.corda.node.services.messaging.*
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.shell.InteractiveShell
import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AddressUtils
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.DemoClock import net.corda.node.utilities.DemoClock
@ -138,27 +134,16 @@ open class Node(configuration: NodeConfiguration,
private var messageBroker: ArtemisMessagingServer? = null private var messageBroker: ArtemisMessagingServer? = null
private var shutdownHook: ShutdownHook? = null private var shutdownHook: ShutdownHook? = null
override fun configure(lh: MutableLazyHub) {
super.configure(lh) override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService {
// Construct security manager reading users data either from the 'security' config section // 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. // if present or from rpcUsers list if the former is missing from config.
lh.obj(configuration.security?.authService ?: SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers)) val securityManagerConfig = configuration.security?.authService ?:
lh.impl(RPCSecurityManagerImpl::class) SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers)
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)
}
class MessagingServerAddress(val address: NetworkHostAndPort) securityManager = RPCSecurityManagerImpl(securityManagerConfig)
private fun makeMessagingService(database: CordaPersistence, info: NodeInfo, messagingServerAddress: MessagingServerAddress): MessagingService { val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker()
val serverAddress = messagingServerAddress.address
val advertisedAddress = info.addresses.single() val advertisedAddress = info.addresses.single()
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
@ -181,10 +166,10 @@ open class Node(configuration: NodeConfiguration,
networkParameters.maxMessageSize) networkParameters.maxMessageSize)
} }
private fun makeLocalMessageBroker(securityManager: RPCSecurityManager): MessagingServerAddress { private fun makeLocalMessageBroker(): NetworkHostAndPort {
with(configuration) { with(configuration) {
messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize) messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize)
return MessagingServerAddress(NetworkHostAndPort("localhost", p2pAddress.port)) return NetworkHostAndPort("localhost", p2pAddress.port)
} }
} }
@ -232,7 +217,7 @@ open class Node(configuration: NodeConfiguration,
} }
} }
private fun startMessagingService(rpcOps: RPCOps, securityManager: RPCSecurityManager) { override fun startMessagingService(rpcOps: RPCOps) {
// Start up the embedded MQ server // Start up the embedded MQ server
messageBroker?.apply { messageBroker?.apply {
runOnStop += this::stop runOnStop += this::stop
@ -253,10 +238,6 @@ 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: * 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 * jdbc:h2:tcp://<host>:<port>/node

View File

@ -1,640 +0,0 @@
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 })
}
}

View File

@ -5,6 +5,7 @@ import com.google.common.jimfs.Jimfs
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.random63BitValue import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -14,15 +15,17 @@ import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.createDirectory import net.corda.core.internal.createDirectory
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient 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.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.lazyhub.MutableLazyHub
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.internal.StartedNode import net.corda.node.internal.StartedNode
@ -294,14 +297,9 @@ 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 // 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. // through the java.nio API which we are already mocking via Jimfs.
private fun makeMessagingService(database: CordaPersistence): MessagingService { override fun makeMessagingService(database: CordaPersistence, info: NodeInfo): MessagingService {
require(id >= 0) { "Node ID must be zero or positive, was passed: " + id } require(id >= 0) { "Node ID must be zero or positive, was passed: " + id }
return mockNet.messagingNetwork.createNodeWithID( return mockNet.messagingNetwork.createNodeWithID(
!mockNet.threadPerNode, !mockNet.threadPerNode,
@ -320,6 +318,14 @@ open class MockNetwork(private val cordappPackages: List<String>,
return E2ETestKeyManagementService(identityService, keyPairs) 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 // This is not thread safe, but node construction is done on a single thread, so that should always be fine
override fun generateKeyPair(): KeyPair { override fun generateKeyPair(): KeyPair {
counter = counter.add(BigInteger.ONE) counter = counter.add(BigInteger.ONE)