diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 9f989db79a..8b17db03ac 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -36,6 +36,10 @@ UNRELEASED * Cordformation node building DSL can have an additional parameter `configFile` with the path to a properties file to be appended to node.conf. +* ``CordaService`` annotated classes should be upgraded to take a constructor parameter of type ``AppServiceHub`` which + allows services to start flows marked with the ``StartableByService`` annotation. For backwards compatability + service classes with only ``ServiceHub`` constructors will still work. + .. _changelog_v1: Release 1.0 diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt index 8d1c0d0274..d7331d146c 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TransactionVerificationException import net.corda.core.flows.* -import net.corda.core.node.ServiceHub +import net.corda.core.node.AppServiceHub import net.corda.core.node.services.CordaService import net.corda.core.node.services.TimeWindowChecker import net.corda.core.node.services.TrustedAuthorityNotaryService @@ -15,7 +15,7 @@ import java.security.SignatureException // START 1 @CordaService -class MyCustomValidatingNotaryService(override val services: ServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { +class MyCustomValidatingNotaryService(override val services: AppServiceHub, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() { override val timeWindowChecker = TimeWindowChecker(services.clock) override val uniquenessProvider = PersistentUniquenessProvider() diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt index 47a5ca00a0..1ae88a6015 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/CustomVaultQuery.kt @@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Amount import net.corda.core.flows.* import net.corda.core.identity.Party -import net.corda.core.node.ServiceHub +import net.corda.core.node.AppServiceHub import net.corda.core.node.services.CordaService import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SingletonSerializeAsToken @@ -23,7 +23,7 @@ import java.util.* object CustomVaultQuery { @CordaService - class Service(val services: ServiceHub) : SingletonSerializeAsToken() { + class Service(val services: AppServiceHub) : SingletonSerializeAsToken() { private companion object { val log = loggerFor() } @@ -49,7 +49,7 @@ object CustomVaultQuery { val session = services.jdbcSession() val prepStatement = session.prepareStatement(nativeQuery) val rs = prepStatement.executeQuery() - var topUpLimits: MutableList> = mutableListOf() + val topUpLimits: MutableList> = mutableListOf() while (rs.next()) { val currencyStr = rs.getString(1) val amount = rs.getLong(2) diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index 01e5da1b71..cabefdd203 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -9,7 +9,7 @@ Writing a custom notary service Similarly to writing an oracle service, the first step is to create a service class in your CorDapp and annotate it with ``@CordaService``. The Corda node scans for any class with this annotation and initialises them. The only requirement -is that the class provide a constructor with a single parameter of type ``ServiceHub``. +is that the class provide a constructor with a single parameter of type ``AppServiceHub``. .. literalinclude:: example-code/src/main/kotlin/net/corda/docs/CustomNotaryTutorial.kt :language: kotlin diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index a28d299d24..579de8cde6 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -2,11 +2,10 @@ package net.corda.irs.api import co.paralleluniverse.fibers.Suspendable import net.corda.core.contracts.Command -import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.TransactionSignature import net.corda.core.flows.* import net.corda.core.internal.ThreadBox -import net.corda.core.node.ServiceHub +import net.corda.core.node.AppServiceHub import net.corda.core.node.services.CordaService import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.FilteredTransaction @@ -78,7 +77,7 @@ object NodeInterestRates { @ThreadSafe // DOCSTART 3 @CordaService - class Oracle(private val services: ServiceHub) : SingletonSerializeAsToken() { + class Oracle(private val services: AppServiceHub) : SingletonSerializeAsToken() { private val mutex = ThreadBox(InnerState()) init { diff --git a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 38fc4e0b56..3af7044caa 100644 --- a/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -24,6 +24,7 @@ import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDatabaseProperties import net.corda.testing.node.MockServices.Companion.makeTestIdentityService +import net.corda.testing.node.createMockCordaService import org.junit.After import org.junit.Assert.* import org.junit.Before @@ -65,7 +66,8 @@ class NodeInterestRatesTest : TestDependencyInjectionBase() { setCordappPackages("net.corda.finance.contracts") database = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), createIdentityService = ::makeTestIdentityService) database.transaction { - oracle = NodeInterestRates.Oracle(services).apply { knownFixes = TEST_DATA } + oracle = createMockCordaService(services, NodeInterestRates::Oracle) + oracle.knownFixes = TEST_DATA } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 3d49484fef..ae838119a2 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -1,10 +1,15 @@ package net.corda.testing.node +import com.google.common.collect.MutableClassToInstanceMap import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.* +import net.corda.core.flows.FlowLogic import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.DataFeed +import net.corda.core.messaging.FlowHandle +import net.corda.core.messaging.FlowProgressHandle +import net.corda.core.node.AppServiceHub import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.node.StateLoader @@ -173,7 +178,11 @@ open class MockServices( return vaultService } - override fun cordaService(type: Class): T = throw IllegalArgumentException("${type.name} not found") + val cordappServices = MutableClassToInstanceMap.create() + override fun cordaService(type: Class): T { + require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" } + return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist") + } override fun jdbcSession(): Connection = throw UnsupportedOperationException() } @@ -244,3 +253,23 @@ open class MockTransactionStorage : WritableTransactionStorage, SingletonSeriali override fun getTransaction(id: SecureHash): SignedTransaction? = txns[id] } + +fun createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T { + class MockAppServiceHubImpl(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub { + val serviceInstance: T + + init { + serviceInstance = serviceConstructor(this) + serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance) + } + + override fun startFlow(flow: FlowLogic): FlowHandle { + throw UnsupportedOperationException() + } + + override fun startTrackedFlow(flow: FlowLogic): FlowProgressHandle { + throw UnsupportedOperationException() + } + } + return MockAppServiceHubImpl(serviceHub, serviceConstructor).serviceInstance +} \ No newline at end of file