diff --git a/.gitignore b/.gitignore index 4bf02c6b29..7c94864509 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,13 @@ TODO +# Eclipse, ctags, Mac metadata, log files +.classpath +.project +.settings +tags +.DS_Store +*.log + # Created by .ignore support plugin (hsz.mobi) .gradle @@ -66,4 +74,4 @@ atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties -crashlytics-build.properties \ No newline at end of file +crashlytics-build.properties diff --git a/src/main/kotlin/core/messaging/StateMachineManager.kt b/src/main/kotlin/core/messaging/StateMachineManager.kt index beb667bb87..22d036f117 100644 --- a/src/main/kotlin/core/messaging/StateMachineManager.kt +++ b/src/main/kotlin/core/messaging/StateMachineManager.kt @@ -61,7 +61,7 @@ import javax.annotation.concurrent.ThreadSafe class StateMachineManager(val serviceHub: ServiceHub, val runInThread: Executor) { // This map is backed by a database and will be used to store serialised state machines to disk, so we can resurrect // them across node restarts. - private val checkpointsMap = serviceHub.storageService.getMap("state machines") + private val checkpointsMap = serviceHub.storageService.stateMachines // A list of all the state machines being managed by this class. We expose snapshots of it via the stateMachines // property. private val _stateMachines = Collections.synchronizedList(ArrayList>()) diff --git a/src/main/kotlin/core/node/AbstractNode.kt b/src/main/kotlin/core/node/AbstractNode.kt index bfe3b61c32..3e637f8603 100644 --- a/src/main/kotlin/core/node/AbstractNode.kt +++ b/src/main/kotlin/core/node/AbstractNode.kt @@ -147,31 +147,12 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration, val attachments = makeAttachmentStorage(dir) _servicesThatAcceptUploads += attachments val (identity, keypair) = obtainKeyPair(dir) - return constructStorageService(attachments, identity, keypair) + return constructStorageService(attachments, keypair, identity, contractFactory) } - protected open fun constructStorageService(attachments: NodeAttachmentService, identity: Party, keypair: KeyPair) = - StorageServiceImpl(attachments, identity, keypair) - - open inner class StorageServiceImpl(attachments: NodeAttachmentService, identity: Party, keypair: KeyPair) : StorageService { - protected val tables = HashMap>() - - @Suppress("UNCHECKED_CAST") - override fun getMap(tableName: String): MutableMap { - // TODO: This should become a database. - synchronized(tables) { - return tables.getOrPut(tableName) { Collections.synchronizedMap(HashMap()) } as MutableMap - } - } - - override val validatedTransactions: MutableMap - get() = getMap("validated-transactions") - - override val attachments: AttachmentStorage = attachments - override val contractPrograms = contractFactory - override val myLegalIdentity = identity - override val myLegalIdentityKey = keypair - } + protected open fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party, + contractFactory: ContractFactory) = + StorageServiceImpl(attachments, contractFactory, keypair, identity) private fun obtainKeyPair(dir: Path): Pair { // Load the private identity key, creating it if necessary. The identity key is a long term well known key that @@ -213,4 +194,5 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration, } return NodeAttachmentService(attachmentsDir, services.monitoringService.metrics) } -} \ No newline at end of file +} + diff --git a/src/main/kotlin/core/node/services/Services.kt b/src/main/kotlin/core/node/services/Services.kt index 367e106a65..6974af1c4e 100644 --- a/src/main/kotlin/core/node/services/Services.kt +++ b/src/main/kotlin/core/node/services/Services.kt @@ -104,9 +104,6 @@ interface KeyManagementService { * anything like that, this interface is only big enough to support the prototyping work. */ interface StorageService { - /** TODO: Temp scaffolding that will go away eventually. */ - fun getMap(tableName: String): MutableMap - /** * A map of hash->tx where tx has been signature/contract validated and the states are known to be correct. * The signatures aren't technically needed after that point, but we keep them around so that we can relay @@ -114,6 +111,8 @@ interface StorageService { */ val validatedTransactions: MutableMap + val stateMachines: MutableMap + /** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */ val attachments: AttachmentStorage diff --git a/src/main/kotlin/core/node/services/StorageServiceImpl.kt b/src/main/kotlin/core/node/services/StorageServiceImpl.kt new file mode 100644 index 0000000000..b0a0490282 --- /dev/null +++ b/src/main/kotlin/core/node/services/StorageServiceImpl.kt @@ -0,0 +1,45 @@ +package core.node.services + +import core.ContractFactory +import core.Party +import core.SignedTransaction +import core.crypto.SecureHash +import core.utilities.RecordingMap +import org.slf4j.LoggerFactory +import java.security.KeyPair +import java.util.* + +open class StorageServiceImpl(attachments: AttachmentStorage, + contractFactory: ContractFactory, + keypair: KeyPair, + identity: Party = Party("Unit test party", keypair.public), + // This parameter is for unit tests that want to observe operation details. + val recordingAs: (String) -> String = { tableName -> "" }) +: StorageService { + protected val tables = HashMap>() + + private fun getMapOriginal(tableName: String): MutableMap { + synchronized(tables) { + return tables.getOrPut(tableName) { + recorderWrap(Collections.synchronizedMap(HashMap()), tableName); + } as MutableMap + } + } + + private fun recorderWrap(map: MutableMap, tableName: String): MutableMap { + if (recordingAs(tableName) != "") + return RecordingMap(map, LoggerFactory.getLogger("recordingmap.${recordingAs(tableName)}")) + else + return map + } + + override val validatedTransactions: MutableMap + get() = getMapOriginal("validated-transactions") + override val stateMachines: MutableMap + get() = getMapOriginal("state-machines") + + override val attachments: AttachmentStorage = attachments + override val contractPrograms = contractFactory + override val myLegalIdentity = identity + override val myLegalIdentityKey = keypair +} \ No newline at end of file diff --git a/src/test/kotlin/core/testutils/RecordingMap.kt b/src/main/kotlin/core/utilities/RecordingMap.kt similarity index 86% rename from src/test/kotlin/core/testutils/RecordingMap.kt rename to src/main/kotlin/core/utilities/RecordingMap.kt index 8c1546b8ba..d0aff7e7a6 100644 --- a/src/test/kotlin/core/testutils/RecordingMap.kt +++ b/src/main/kotlin/core/utilities/RecordingMap.kt @@ -6,15 +6,7 @@ * All other rights reserved. */ -/* - * Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members - * pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms - * set forth therein. - * - * All other rights reserved. - */ - -package core.testutils +package core.utilities import core.utilities.loggerFor import org.slf4j.Logger diff --git a/src/test/kotlin/core/MockServices.kt b/src/test/kotlin/core/MockServices.kt index be482753e4..277b5a656e 100644 --- a/src/test/kotlin/core/MockServices.kt +++ b/src/test/kotlin/core/MockServices.kt @@ -14,9 +14,10 @@ import core.messaging.MessagingService import core.messaging.MockNetworkMapService import core.messaging.NetworkMapService import core.node.services.* +import core.node.AbstractNode +import core.node.services.StorageServiceImpl import core.serialization.SerializedBytes import core.serialization.deserialize -import core.testutils.RecordingMap import core.testutils.TEST_KEYS_TO_CORP_MAP import core.testutils.TEST_PROGRAM_MAP import core.testutils.TEST_TX_TIME @@ -114,31 +115,7 @@ class MockAttachmentStorage : AttachmentStorage { } @ThreadSafe -class MockStorageService(val recordingAs: Map? = null) : StorageService { - override val myLegalIdentityKey: KeyPair = generateKeyPair() - override val myLegalIdentity: Party = Party("Unit test party", myLegalIdentityKey.public) - - private val tables = HashMap>() - - override val validatedTransactions: MutableMap - get() = getMap("validated-transactions") - - override val contractPrograms = MockContractFactory - - override val attachments: AttachmentStorage = MockAttachmentStorage() - - @Suppress("UNCHECKED_CAST") - override fun getMap(tableName: String): MutableMap { - synchronized(tables) { - return tables.getOrPut(tableName) { - val map = Collections.synchronizedMap(HashMap()) - if (recordingAs != null && recordingAs[tableName] != null) - RecordingMap(map, LoggerFactory.getLogger("recordingmap.${recordingAs[tableName]}")) - else - map - } as MutableMap - } - } +class MockStorageService : StorageServiceImpl(MockAttachmentStorage(), MockContractFactory, generateKeyPair()) { } object MockContractFactory : ContractFactory { diff --git a/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt b/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt index 6ce6107398..20080756ca 100644 --- a/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt +++ b/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt @@ -14,8 +14,10 @@ import core.* import core.crypto.SecureHash import core.node.MockNetwork import core.node.services.* +import core.node.services.StorageServiceImpl import core.testutils.* import core.utilities.BriefLogFormatter +import core.utilities.RecordingMap import org.junit.After import org.junit.Before import org.junit.Test @@ -151,7 +153,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() { // OK, now Bob has sent the partial transaction back to Alice and is waiting for Alice's signature. // Save the state machine to "disk" (i.e. a variable, here) - val savedCheckpoints = HashMap(bobNode.storage.getMap("state machines")) + val savedCheckpoints = HashMap(bobNode.storage.stateMachines) assertEquals(1, savedCheckpoints.size) // .. and let's imagine that Bob's computer has a power cut. He now has nothing now beyond what was on disk. @@ -167,7 +169,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() { object : MockNetwork.MockNode(path, nodeConfiguration, net, timestamper, bobAddr.id) { override fun initialiseStorageService(dir: Path): StorageService { val ss = super.initialiseStorageService(dir) - val smMap = ss.getMap("state machines") + val smMap = ss.stateMachines smMap.putAll(savedCheckpoints) return ss } @@ -195,20 +197,10 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() { return net.createNode(null) { path, config, net, tsNode -> object : MockNetwork.MockNode(path, config, net, tsNode) { // That constructs the storage service object in a customised way ... - override fun constructStorageService(attachments: NodeAttachmentService, identity: Party, keypair: KeyPair): StorageServiceImpl { - // By tweaking the standard StorageServiceImpl class ... - return object : StorageServiceImpl(attachments, identity, keypair) { - // To use RecordingMaps instead of ordinary HashMaps. - @Suppress("UNCHECKED_CAST") - override fun getMap(tableName: String): MutableMap { - synchronized(tables) { - return tables.getOrPut(tableName) { - val map = Collections.synchronizedMap(HashMap()) - RecordingMap(map, LoggerFactory.getLogger("recordingmap.$name")) - } as MutableMap - } - } - } + override fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party, + contractFactory: ContractFactory): StorageServiceImpl { + // To use RecordingMaps instead of ordinary HashMaps. + return StorageServiceImpl(attachments, contractFactory, keypair, identity, { tableName -> name }) } } }