diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index b5b8bc6f72..70724cb884 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -242,14 +242,10 @@ private fun IntProgression.toSpliterator(): Spliterator.OfInt { } fun IntProgression.stream(parallel: Boolean = false): IntStream = StreamSupport.intStream(toSpliterator(), parallel) -inline fun Stream.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 Stream.toTypedArray(componentType: Class): Array = toArray { size -> - uncheckedCast>(java.lang.reflect.Array.newInstance(componentType, size)) -} -fun Stream.filterNotNull(): Stream = uncheckedCast(filter(Objects::nonNull)) -fun Stream>.toMap(): Map = collect>(::LinkedHashMap, { m, (k, v) -> m.put(k, v) }, { m, t -> m.putAll(t) }) +// When toArray has filled in the array, the component type is no longer T? but T (that may itself be nullable): +inline fun Stream.toTypedArray(): Array = uncheckedCast(toArray { size -> arrayOfNulls(size) }) + fun Class.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]. */ diff --git a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt index 12818f9a81..0a2fb69f26 100644 --- a/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt +++ b/core/src/test/kotlin/net/corda/core/internal/InternalUtilsTest.kt @@ -3,7 +3,6 @@ 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 @@ -88,17 +87,5 @@ class InternalUtilsTest { val b: Array = Stream.of("one", "two", null).toTypedArray() assertEquals(Array::class.java, b.javaClass) assertArrayEquals(arrayOf("one", "two", null), b) - val c: Array = Stream.of("x", "y").toTypedArray(CharSequence::class.java) - assertEquals(Array::class.java, c.javaClass) - assertArrayEquals(arrayOf("x", "y"), c) - val d: Array = Stream.of("x", "y", null).toTypedArray(uncheckedCast(CharSequence::class.java)) - assertEquals(Array::class.java, d.javaClass) - assertArrayEquals(arrayOf("x", "y", null), d) - } - - @Test - fun `Stream of Pairs toMap works`() { - val m: Map, Serializable> = Stream.of, Serializable>>("x" to "y", 0 to 1, "x" to '2').toMap() - assertEquals>(mapOf("x" to '2', 0 to 1), m) } } diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt deleted file mode 100644 index 512fa1c74e..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHub.kt +++ /dev/null @@ -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 get(clazz: KClass) = get(clazz.java) - operator fun get(clazz: Class) = getOrNull(clazz) ?: throw NoSuchProviderException(clazz.toString()) - fun getAll(clazz: KClass) = getAll(clazz.java) - fun getAll(clazz: Class): List - fun getOrNull(clazz: KClass) = getOrNull(clazz.java) - fun getOrNull(clazz: Class): 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 obj(service: KClass, 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 impl(service: KClass, impl: KClass) - - /** Like the [KClass] variant if you don't have a static reference fo the class. */ - fun impl(service: KClass, impl: Class) - - /** - * 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 factory(service: KClass, factory: KFunction) -} diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt deleted file mode 100644 index 5edbb4027b..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHubImpl.kt +++ /dev/null @@ -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(override val obj: T) : Provider { - override val type get() = obj.javaClass -} - -private class LazyProvider(private val busyProviders: BusyProviders, private val underlying: Any?, override val type: Class, val chooseInvocation: () -> Callable) : Provider { - override val obj by lazy { busyProviders.runFactory(this) } - override fun toString() = underlying.toString() -} - -private class Invocation(val constructor: PublicConstructor, val argSuppliers: List>) : Callable { - 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, Callable<*>>() - fun runFactory(provider: LazyProvider): 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<*>> = mutableMapOf, 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, MutableList>>() - private fun add(provider: Provider<*>, type: Class<*> = provider.type, registered: MutableSet> = 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 findProviders(clazz: Class): List>? = uncheckedCast(providers[clazz]) ?: parent?.findProviders(clazz) - - private fun dropAll(serviceClass: Class<*>) { - val removed = mutableSetOf>() - 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 getOrNull(clazz: Class) = findProviders(clazz)?.run { (singleOrNull() ?: throw TooManyProvidersException(clazz.toString())).obj } - override fun getAll(clazz: Class) = findProviders(clazz)?.map { it.obj } ?: emptyList() - override fun child(): MutableLazyHub = LazyHubImpl(this) - override fun obj(obj: Any) = add(SimpleProvider(obj)) - override fun obj(service: KClass, obj: T) { - dropAll(service.java) - obj(obj) - } - - override fun factory(service: KClass, factory: KFunction) = factory.validate().let { - dropAll(service.java) - addFactory(it) - } - - override fun impl(service: KClass, impl: KClass) = impl.validate().let { - dropAll(service.java) - addConcrete(it) - } - - override fun impl(service: KClass, impl: Class) = impl.validate().let { - dropAll(service.java) - addConcrete(it) - } - - override fun factory(factory: KFunction<*>) = addFactory(factory.validate()) - private fun addFactory(factory: KConstructor) { - 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 addFactory(lh: LazyHub, type: KClass) { - add(LazyProvider(busyProviders, lh, type.java) { Callable { lh[type] } }) - } - - override fun impl(impl: KClass<*>) = implGeneric(impl) - private fun implGeneric(type: KClass) = addConcrete(type.validate()) - override fun impl(impl: Class<*>) = implGeneric(impl) - private fun implGeneric(type: Class) = addConcrete(type.validate()) - private fun

> addConcrete(concrete: Concrete) { - 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 arrayProvider(arrayType: Class<*>, componentType: Class): LazyProvider>? { - val providers = findProviders(componentType) ?: return null - return LazyProvider(busyProviders, null, uncheckedCast(arrayType)) { - Callable { providers.stream().map { it.obj }.toTypedArray(componentType) } - } - } - - private fun

PublicConstructor.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()) - } - } - }) -} diff --git a/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt b/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt deleted file mode 100644 index 7c3217f7d7..0000000000 --- a/node/src/main/kotlin/net/corda/lazyhub/LazyHubModel.kt +++ /dev/null @@ -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 { - /** Most specific known type i.e. directly registered implementation class, or declared return type of factory method. */ - val type: Class - /** 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 { - val params: List

- operator fun invoke(argSuppliers: List>): T -} - -internal class KConstructor(val kFunction: KFunction) : PublicConstructor { - companion object { - fun KFunction.validate() = run { - if (returnType.isMarkedNullable) throw NullableReturnTypeException(toString()) - isAccessible = true - KConstructor(this) - } - } - - override val params = kFunction.parameters.map(::KParam) - override fun invoke(argSuppliers: List>): T { - return kFunction.callBy(argSuppliers.stream().map { (param, supplier) -> param.kParam to supplier() }.toMap()) - } - - override fun toString() = kFunction.toString() -} - -internal class JConstructor(private val constructor: Constructor) : PublicConstructor { - // 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>): T { - return constructor.newInstance(*argSuppliers.stream().map { (_, supplier) -> supplier() }.toTypedArray()) - } - - override fun toString() = constructor.toString() -} - -internal interface Concrete> { - val clazz: Class - val publicConstructors: List -} - -internal class KConcrete private constructor(private val kClass: KClass) : Concrete> { - companion object { - fun KClass.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 private constructor(override val clazz: Class) : Concrete> { - companion object { - fun Class.validate() = run { - if (Modifier.isAbstract(modifiers)) throw AbstractTypeException(toString()) - JConcrete(this).apply { - if (publicConstructors.isEmpty()) throw NoPublicConstructorsException(toString()) - } - } - } - - override val publicConstructors = uncheckedCast>, Array>>(clazz.constructors).map(::JConstructor) - override fun toString() = clazz.toString() -} diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index da9523a507..6280ed522a 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -26,14 +26,12 @@ 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,6 +54,7 @@ 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 @@ -143,6 +142,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected val runOnStop = ArrayList<() -> Any?>() private val _nodeReadyFuture = openFuture() 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 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 { check(started == null) { "Node has already been started" } log.info("Node starting up ...") initCertificate() val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null) 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).start(), identityService) val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair) - lh.obj(info) identityService.loadIdentities(info.legalIdentitiesAndCerts) 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 smm = makeStateMachineManager(database) val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) @@ -244,13 +237,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } makeVaultObservers(schedulerService, database.hibernateConfig, smm, schemaService, flowLogicRefFactory) val rpcOps = makeRPCOps(flowStarter, database, smm) - lh.obj(rpcOps) - lh.getAll(Unit::class) // Run side-effects. + startMessagingService(rpcOps) 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, @@ -286,6 +279,10 @@ 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, NodeInfo> { @@ -536,7 +533,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(lh: LazyHub, keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { + private fun makeServices(keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() attachments = NodeAttachmentService(metrics) @@ -552,7 +549,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, database, info, networkMapCache) - network = lh[MessagingService::class] // TODO: Retire the lateinit var. + network = makeMessagingService(database, info) val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.keyManagementService, services.identityService, platformClock, services.auditService, services.monitoringService, services.networkMapCache, services.schemaService, @@ -715,6 +712,9 @@ 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 { val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword) diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index a36a8f78e7..c4149f74ac 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -5,7 +5,6 @@ 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 @@ -15,10 +14,8 @@ 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 @@ -27,7 +24,6 @@ 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 @@ -138,27 +134,16 @@ open class Node(configuration: NodeConfiguration, private var messageBroker: ArtemisMessagingServer? = 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 // if present or from rpcUsers list if the former is missing from config. - 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) - } + val securityManagerConfig = configuration.security?.authService ?: + SecurityConfiguration.AuthService.fromUsers(configuration.rpcUsers) - class MessagingServerAddress(val address: NetworkHostAndPort) + securityManager = RPCSecurityManagerImpl(securityManagerConfig) - private fun makeMessagingService(database: CordaPersistence, info: NodeInfo, messagingServerAddress: MessagingServerAddress): MessagingService { - val serverAddress = messagingServerAddress.address + val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val advertisedAddress = info.addresses.single() printBasicNodeInfo("Incoming connection address", advertisedAddress.toString()) @@ -181,10 +166,10 @@ open class Node(configuration: NodeConfiguration, networkParameters.maxMessageSize) } - private fun makeLocalMessageBroker(securityManager: RPCSecurityManager): MessagingServerAddress { + private fun makeLocalMessageBroker(): NetworkHostAndPort { with(configuration) { 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 messageBroker?.apply { 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: * jdbc:h2:tcp://:/node diff --git a/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt b/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt deleted file mode 100644 index 5d462585bc..0000000000 --- a/node/src/test/kotlin/net/corda/lazyhub/LazyHubTests.kt +++ /dev/null @@ -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(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 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 = this::ntv - lh.factory(uncheckedCast>(ntv)) - lh.obj(arg) - assertEquals(arg.toString(), lh[String::class]) - } - - class PTWMB(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 ptwmb(arg: Y) where Y : Closeable, Y : Serializable = arg.toString() - @Test - fun `factory parameter type with multiple bounds`() { - val ptwmb: Function1 = this::ptwmb - val kFunction = uncheckedCast>(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 upt(a: Y) = a.toString() - @Test - fun `unbounded parameter type`() { - val upt: Function1 = this::upt - val kFunction: KFunction = 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) - 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::class] }.run { - assertSame(NoSuchProviderException::class.java, javaClass) - assertEquals(Array::class.java.toString(), message) - } - assertEquals(emptyList(), lh.getAll(String::class)) - } - - @Test - fun arrayParam1() { - class Impl(val v: Array) - lh.impl(Impl::class) - lh.obj("a") - assertArrayEquals(arrayOf("a"), lh[Impl::class].v) - } - - @Test - fun arrayParam2() { - class Impl(val v: Array) - 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?) - lh.impl(Impl::class) - assertEquals(null, lh[Impl::class].v) - } - - @Test - fun arraysAreNotCached() { - class B(val v: Array) - class A(val v: Array, val b: B) - class C(val v: Array) - class D(val v: Array) - 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) - - @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() - 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, @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 }) - } -} diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index b9901a881c..f6a47a79f8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -5,6 +5,7 @@ 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 @@ -14,15 +15,17 @@ 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,14 +297,9 @@ open class MockNetwork(private val cordappPackages: List, } } - 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. - 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 } return mockNet.messagingNetwork.createNodeWithID( !mockNet.threadPerNode, @@ -320,6 +318,14 @@ open class MockNetwork(private val cordappPackages: List, 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)