Merge Open Source to Enterprise (#79)

* Check array size before accessing

* Review fixes

* CORDA-540: Make Verifier work in AMQP mode (#1870)

* reference to finance module via not hardcoded group ID (#1515)

*  generic way to reference to group id when loading finance.jar via cordapp

* Fixed the node shell to work with the DataFeed class

* Attempt to make NodeStatePersistenceTests more stable (#1895)

By ensuring that the nodes are properly started and aware of each other before firing any flows through them.
Also minor refactoring.

* Disable unstable test on Windows (#1899)

* CORDA-530 Don't soft-lock non-fungible states (#1794)

* Don't run unlock query if nothing was locked
* Constructors should not have side-effects

* [CORDA-442] let Driver run without network map (#1890)

* [CORDA-442] let Driver run without network map

- Nodes started by driver run without a networkMapNode.

- Driver does not take a networkMapStartStrategy anymore

- a new parameter in the configuration "noNetworkMapServiceMode" allows for a node not to be a networkMapNode nor to connect to one.

- Driver now waits for each node to write its own NodeInfo file to disk and then copies it into each other node.

- When driver starts a node N, it waits for every node to be have N nodes in their network map.

Note: the code to copy around the NodeInfo files was already in DemoBench, the NodeInfoFilesCopier class was just moved from DemoBench into core (I'm very open to core not being the best place, please advise)

* Added missing cordappPackage dependencies. (#1894)

* Eliminate circular dependency of NodeSchedulerService on ServiceHub. (#1891)

* Update customSchemas documentation. (#1902)

* [CORDA-694] Commands visibility for Oracles (without sacrificing privacy) (#1835)

new checkCommandVisibility feature for Oracles

* CORDA-599 PersistentNetworkMapCache no longer circularly depends on SH (#1652)

* CORDA-725 - Change AMQP identifier to officially assigned value

This does change our header format so pre-cached test files need
regenerating

* CORDA-725 - update changelog

* CORDA-680 Update cordapp packages documentation (#1901)

* Introduce MockNetworkParameters

* Cordformation in Kotlin (#1873)

Cordformation rewritten in kotlin.

* Kotlin migration

* Review Comments

* CORDA-704: Implement `@DoNotImplement` annotation (#1903)

* Enhance the API Scanner plugin to monitor class annotations.
* Implement @DoNotImplement annotation, and apply it.
* Update API definition.
* Update API change detection to handle @DoNotImplement.
* Document the `@DoNotImplement` annotation.

* Experimental support for PostgreSQL (#1525)

* Cash selection refactoring such that 3d party DB providers are only required to implement Coin Selection SQL logic.

* Re-added debug logging statement.

* Updated to include PR review feedback from VK

* Refactoring following rebase from master.

* Fix broken JUnits following rebase.

* Use JDBC ResultSet getBlob() and added custom serializer to address concern raised by tomtau in PR.

* Fix failing JUnits.

* Experimental support for PostgreSQL: CashSelection done using window functions

* Moved postgresql version information into corda/build.gradle

* Using a PreparedStatement in CashSelectionPostgreSQLImpl

* Changed the PostgreSQL Cash Selection implementation to use the new refactored AbstractCashSelection

* * Retire MockServiceHubInternal (#1909)

* Introduce rigorousMock
* Add test-utils and node-driver to generated documentation

* Fix-up: Bank Of Corda sample (#1912)

In the previous version when running with `--role ISSUER` the application failed to start.
The reason was that in spite of `quantity` and `currency` were optional,
un-necessary `requestParams` been constructed regardless.

* move SMM

* Interface changes for multi-threading

* CORDA-351: added dependency check plugin to gradle build script (#1911)

* CORDA-351: added dependency check plugin to gradle build script

* CORDA-351: Added suppression stub file with example

* CORDA-351: added suppresionFile property

* CORDA-435 - Ensure Kryo only tests use Kryo serializatin context

Also correct lambda typos (from lamba)

* Network map service REST API wrapper (#1907)

* Network map client - WIP

* Java doc and doc for doc site

* remove javax.ws dependency

* NetworkParameter -> NetworkParameters

* move network map client to node

* Fix jetty test dependencies

* NetworkParameter -> NetworkParameters

* Address PR issues

* Address PR issues and unit test fix

* Address PR issues

* Fixing Bank-Of-Corda Demo in `master` (#1922)

* Fix-up: Bank Of Corda sample

Use correct CorDapp packages to scan

(cherry picked from commit 2caa134)

* Set adequate permissions for the nodes such that NodeExplorer can connect

(cherry picked from commit ae88242)

* Set adequate permissions for the nodes such that NodeExplorer can connect

(cherry picked from commit ae88242)

* Correct run configuration

* Fix-up port numbers

* CORDA-435 - AMQP serialisation cannot work with private vals

They won't be reported as properties by the introspector and thus we
will fail to find a constructor for them. This makes sense as we will be
unable to serialise an object whose members we cannot read

* CORDA-435 - AMQP enablement fixes

AMQP has different serialization rules than Kryo surrounding the way we
introspect objects to work out how to construct them

* [CORDA-442] make MockNetwork not start a networkmap node (#1908)

* [CORDA-442] make MockNetwork not start a networkmap node

Now MockNetwork will put the appropriate NodeInfos inside each running node networkMapCache.

Tests relating to networkmap node starting and interaction have been removed since they where relaying on MockNetwork

* Minor fix for api checker script to support macOS

* Retrofit changes from Enterprise PR #61 (#1934)

* Introduce MockNodeParameters/Args (#1923)

* CORDA-736 Add some new features to corda.jar via node.conf for testing (#1926)

* CORDA-699 Add injection or modification of memory network messages (#1920)

* Updated API stability changeset to reflect new schema attribute name.
This commit is contained in:
josecoll
2017-10-25 13:54:34 +01:00
committed by GitHub
parent 01d8ad41b4
commit ef7ccd3147
200 changed files with 4549 additions and 3192 deletions

View File

@ -18,6 +18,8 @@ dependencies {
// TODO: Remove this dependency and the code that requires it
compile "commons-fileupload:commons-fileupload:$fileupload_version"
compile "net.corda.plugins:cordform-common:$gradle_plugins_version"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"

View File

@ -0,0 +1,165 @@
package net.corda.nodeapi
import net.corda.cordform.CordformNode
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.createDirectories
import net.corda.core.internal.isRegularFile
import net.corda.core.internal.list
import net.corda.core.utilities.loggerFor
import rx.Observable
import rx.Scheduler
import rx.Subscription
import rx.schedulers.Schedulers
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
import java.util.concurrent.TimeUnit
/**
* Utility class which copies nodeInfo files across a set of running nodes.
*
* This class will create paths that it needs to poll and to where it needs to copy files in case those
* don't exist yet.
*/
class NodeInfoFilesCopier(scheduler: Scheduler = Schedulers.io()) : AutoCloseable {
companion object {
private val log = loggerFor<NodeInfoFilesCopier>()
const val NODE_INFO_FILE_NAME_PREFIX = "nodeInfo-"
}
private val nodeDataMapBox = ThreadBox(mutableMapOf<Path, NodeData>())
/**
* Whether the NodeInfoFilesCopier is closed. When the NodeInfoFilesCopier is closed it will stop polling the
* filesystem and all the public methods except [#close] will throw.
*/
private var closed = false
private val subscription: Subscription
init {
this.subscription = Observable.interval(5, TimeUnit.SECONDS, scheduler)
.subscribe { poll() }
}
/**
* @param nodeDir a path to be watched for NodeInfos
* Add a path of a node which is about to be started.
* Its nodeInfo file will be copied to other nodes' additional-node-infos directory, and conversely,
* other nodes' nodeInfo files will be copied to this node additional-node-infos directory.
*/
fun addConfig(nodeDir: Path) {
require(!closed) { "NodeInfoFilesCopier is already closed" }
nodeDataMapBox.locked {
val newNodeFile = NodeData(nodeDir)
put(nodeDir, newNodeFile)
for (previouslySeenFile in allPreviouslySeenFiles()) {
atomicCopy(previouslySeenFile, newNodeFile.additionalNodeInfoDirectory.resolve(previouslySeenFile.fileName))
}
log.info("Now watching: $nodeDir")
}
}
/**
* @param nodeConfig the configuration to be removed.
* Remove the configuration of a node which is about to be stopped or already stopped.
* No files written by that node will be copied to other nodes, nor files from other nodes will be copied to this
* one.
*/
fun removeConfig(nodeDir: Path) {
require(!closed) { "NodeInfoFilesCopier is already closed" }
nodeDataMapBox.locked {
remove(nodeDir) ?: return
log.info("Stopped watching: $nodeDir")
}
}
fun reset() {
require(!closed) { "NodeInfoFilesCopier is already closed" }
nodeDataMapBox.locked {
clear()
}
}
/**
* Stops polling the filesystem.
* This function can be called as many times as one wants.
*/
override fun close() {
if (!closed) {
closed = true
subscription.unsubscribe()
}
}
private fun allPreviouslySeenFiles() = nodeDataMapBox.alreadyLocked { values.flatMap { it.previouslySeenFiles.keys } }
private fun poll() {
nodeDataMapBox.locked {
for (nodeData in values) {
nodeData.nodeDir.list { paths ->
paths.filter { it.isRegularFile() }
.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }
.forEach { path -> processPath(nodeData, path) }
}
}
}
}
// Takes a path under nodeData config dir and decides whether the file represented by that path needs to
// be copied.
private fun processPath(nodeData: NodeData, path: Path) {
nodeDataMapBox.alreadyLocked {
val newTimestamp = Files.readAttributes(path, BasicFileAttributes::class.java).lastModifiedTime()
val previousTimestamp = nodeData.previouslySeenFiles.put(path, newTimestamp) ?: FileTime.fromMillis(-1)
if (newTimestamp > previousTimestamp) {
for (destination in this.values.filter { it.nodeDir != nodeData.nodeDir }.map { it.additionalNodeInfoDirectory }) {
val fullDestinationPath = destination.resolve(path.fileName)
atomicCopy(path, fullDestinationPath)
}
}
}
}
private fun atomicCopy(source: Path, destination: Path) {
val tempDestination = try {
Files.createTempFile(destination.parent, "", null)
} catch (exception: IOException) {
log.warn("Couldn't create a temporary file to copy $source", exception)
throw exception
}
try {
// First copy the file to a temporary file within the appropriate directory.
Files.copy(source, tempDestination, COPY_ATTRIBUTES, REPLACE_EXISTING)
} catch (exception: IOException) {
log.warn("Couldn't copy $source to $tempDestination.", exception)
Files.delete(tempDestination)
throw exception
}
try {
// Then rename it to the desired name. This way the file 'appears' on the filesystem as an atomic operation.
Files.move(tempDestination, destination, REPLACE_EXISTING)
} catch (exception: IOException) {
log.warn("Couldn't move $tempDestination to $destination.", exception)
Files.delete(tempDestination)
throw exception
}
}
/**
* Convenience holder for all the paths and files relative to a single node.
*/
private class NodeData(val nodeDir: Path) {
val additionalNodeInfoDirectory: Path = nodeDir.resolve(CordformNode.NODE_INFO_DIRECTORY)
// Map from Path to its lastModifiedTime.
val previouslySeenFiles = mutableMapOf<Path, FileTime>()
init {
additionalNodeInfoDirectory.createDirectories()
}
}
}

View File

@ -1,8 +1,8 @@
package net.corda.nodeapi
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.serialization.*
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.sequence
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.ClientMessage
import org.apache.activemq.artemis.reader.MessageUtil
@ -20,12 +20,15 @@ object VerifierApi {
val responseAddress: SimpleString
) {
companion object {
fun fromClientMessage(message: ClientMessage): VerificationRequest {
return VerificationRequest(
fun fromClientMessage(message: ClientMessage): ObjectWithCompatibleContext<VerificationRequest> {
val bytes = ByteArray(message.bodySize).apply { message.bodyBuffer.readBytes(this) }
val bytesSequence = bytes.sequence()
val (transaction, context) = bytesSequence.deserializeWithCompatibleContext<LedgerTransaction>()
val request = VerificationRequest(
message.getLongProperty(VERIFICATION_ID_FIELD_NAME),
ByteArray(message.bodySize).apply { message.bodyBuffer.readBytes(this) }.deserialize(),
MessageUtil.getJMSReplyTo(message)
)
transaction,
MessageUtil.getJMSReplyTo(message))
return ObjectWithCompatibleContext(request, context)
}
}
@ -49,11 +52,11 @@ object VerifierApi {
}
}
fun writeToClientMessage(message: ClientMessage) {
fun writeToClientMessage(message: ClientMessage, context: SerializationContext) {
message.putLongProperty(VERIFICATION_ID_FIELD_NAME, verificationId)
if (exception != null) {
message.putBytesProperty(RESULT_EXCEPTION_FIELD_NAME, exception.serialize().bytes)
message.putBytesProperty(RESULT_EXCEPTION_FIELD_NAME, exception.serialize(context = context).bytes)
}
}
}
}
}

View File

@ -3,6 +3,6 @@ package net.corda.nodeapi.internal.serialization
import net.corda.core.crypto.sha256
import net.corda.core.internal.AbstractAttachment
class GeneratedAttachment(bytes: ByteArray) : AbstractAttachment({ bytes }) {
class GeneratedAttachment(val bytes: ByteArray) : AbstractAttachment({ bytes }) {
override val id = bytes.sha256()
}

View File

@ -19,6 +19,7 @@ import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.AttachmentsClassLoader
import org.slf4j.LoggerFactory
import java.io.ByteArrayOutputStream
import java.io.NotSerializableException
import java.util.*
@ -37,7 +38,7 @@ object NotSupportedSerializationScheme : SerializationScheme {
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> = doThrow()
}
data class SerializationContextImpl(override val preferredSerializationVersion: ByteSequence,
data class SerializationContextImpl(override val preferredSerializationVersion: VersionHeader,
override val deserializationClassLoader: ClassLoader,
override val whitelist: ClassWhitelist,
override val properties: Map<Any, Any>,
@ -88,36 +89,54 @@ data class SerializationContextImpl(override val preferredSerializationVersion:
})
}
override fun withPreferredSerializationVersion(versionHeader: ByteSequence) = copy(preferredSerializationVersion = versionHeader)
override fun withPreferredSerializationVersion(versionHeader: VersionHeader) = copy(preferredSerializationVersion = versionHeader)
}
private const val HEADER_SIZE: Int = 8
fun ByteSequence.obtainHeaderSignature(): VersionHeader = take(HEADER_SIZE).copy()
open class SerializationFactoryImpl : SerializationFactory() {
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
private val registeredSchemes: MutableCollection<SerializationScheme> = Collections.synchronizedCollection(mutableListOf())
private val logger = LoggerFactory.getLogger(javaClass)
// TODO: This is read-mostly. Probably a faster implementation to be found.
private val schemes: ConcurrentHashMap<Pair<ByteSequence, SerializationContext.UseCase>, SerializationScheme> = ConcurrentHashMap()
private fun schemeFor(byteSequence: ByteSequence, target: SerializationContext.UseCase): SerializationScheme {
private fun schemeFor(byteSequence: ByteSequence, target: SerializationContext.UseCase): Pair<SerializationScheme, VersionHeader> {
// truncate sequence to 8 bytes, and make sure it's a copy to avoid holding onto large ByteArrays
return schemes.computeIfAbsent(byteSequence.take(HEADER_SIZE).copy() to target) {
val lookupKey = byteSequence.obtainHeaderSignature() to target
val scheme = schemes.computeIfAbsent(lookupKey) {
registeredSchemes
.filter { scheme -> scheme.canDeserializeVersion(it.first, it.second) }
.forEach { return@computeIfAbsent it }
logger.warn("Cannot find serialization scheme for: $lookupKey, registeredSchemes are: $registeredSchemes")
NotSupportedSerializationScheme
}
return scheme to lookupKey.first
}
@Throws(NotSerializableException::class)
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
return asCurrent { withCurrentContext(context) { schemeFor(byteSequence, context.useCase).deserialize(byteSequence, clazz, context) } }
return asCurrent { withCurrentContext(context) { schemeFor(byteSequence, context.useCase).first.deserialize(byteSequence, clazz, context) } }
}
@Throws(NotSerializableException::class)
override fun <T : Any> deserializeWithCompatibleContext(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): ObjectWithCompatibleContext<T> {
return asCurrent {
withCurrentContext(context) {
val (scheme, versionHeader) = schemeFor(byteSequence, context.useCase)
val deserializedObject = scheme.deserialize(byteSequence, clazz, context)
ObjectWithCompatibleContext(deserializedObject, context.withPreferredSerializationVersion(versionHeader))
}
}
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
return asCurrent { withCurrentContext(context) { schemeFor(context.preferredSerializationVersion, context.useCase).serialize(obj, context) } }
return asCurrent { withCurrentContext(context) { schemeFor(context.preferredSerializationVersion, context.useCase).first.serialize(obj, context) } }
}
fun registerScheme(scheme: SerializationScheme) {

View File

@ -18,8 +18,15 @@ import java.util.*
import net.corda.nodeapi.internal.serialization.carpenter.Field as CarpenterField
import net.corda.nodeapi.internal.serialization.carpenter.Schema as CarpenterSchema
// TODO: get an assigned number as per AMQP spec
const val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000
/**
* R3 AMQP assigned enterprise number
*
* see [here](https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers)
*
* Repeated here for brevity:
* 50530 - R3 - Mike Hearn - mike&r3.com
*/
const val DESCRIPTOR_TOP_32BITS: Long = 0xc5620000
const val DESCRIPTOR_DOMAIN: String = "net.corda"

View File

@ -0,0 +1,139 @@
package net.corda.nodeapi
import net.corda.cordform.CordformNode
import net.corda.testing.eventually
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import rx.schedulers.TestScheduler
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration
import java.util.concurrent.TimeUnit
import kotlin.streams.toList
import kotlin.test.assertEquals
/**
* tests for [NodeInfoFilesCopier]
*/
class NodeInfoFilesCopierTest {
@Rule @JvmField var folder = TemporaryFolder()
private val rootPath get() = folder.root.toPath()
private val scheduler = TestScheduler()
companion object {
private const val ORGANIZATION = "Organization"
private const val NODE_1_PATH = "node1"
private const val NODE_2_PATH = "node2"
private val content = "blah".toByteArray(Charsets.UTF_8)
private val GOOD_NODE_INFO_NAME = "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}test"
private val GOOD_NODE_INFO_NAME_2 = "${NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX}anotherNode"
private val BAD_NODE_INFO_NAME = "something"
}
private fun nodeDir(nodeBaseDir : String) = rootPath.resolve(nodeBaseDir).resolve(ORGANIZATION.toLowerCase())
private val node1RootPath by lazy { nodeDir(NODE_1_PATH) }
private val node2RootPath by lazy { nodeDir(NODE_2_PATH) }
private val node1AdditionalNodeInfoPath by lazy { node1RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) }
private val node2AdditionalNodeInfoPath by lazy { node2RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) }
lateinit var nodeInfoFilesCopier: NodeInfoFilesCopier
@Before
fun setUp() {
nodeInfoFilesCopier = NodeInfoFilesCopier(scheduler)
}
@Test
fun `files created before a node is started are copied to that node`() {
// Configure the first node.
nodeInfoFilesCopier.addConfig(node1RootPath)
// Ensure directories are created.
advanceTime()
// Create 2 files, a nodeInfo and another file in node1 folder.
Files.write(node1RootPath.resolve(GOOD_NODE_INFO_NAME), content)
Files.write(node1RootPath.resolve(BAD_NODE_INFO_NAME), content)
// Configure the second node.
nodeInfoFilesCopier.addConfig(node2RootPath)
advanceTime()
eventually<AssertionError, Unit>(Duration.ofMinutes(1)) {
// Check only one file is copied.
checkDirectoryContainsSingleFile(node2AdditionalNodeInfoPath, GOOD_NODE_INFO_NAME)
}
}
@Test
fun `polling of running nodes`() {
// Configure 2 nodes.
nodeInfoFilesCopier.addConfig(node1RootPath)
nodeInfoFilesCopier.addConfig(node2RootPath)
advanceTime()
// Create 2 files, one of which to be copied, in a node root path.
Files.write(node2RootPath.resolve(GOOD_NODE_INFO_NAME), content)
Files.write(node2RootPath.resolve(BAD_NODE_INFO_NAME), content)
advanceTime()
eventually<AssertionError, Unit>(Duration.ofMinutes(1)) {
// Check only one file is copied to the other node.
checkDirectoryContainsSingleFile(node1AdditionalNodeInfoPath, GOOD_NODE_INFO_NAME)
}
}
@Test
fun `remove nodes`() {
// Configure 2 nodes.
nodeInfoFilesCopier.addConfig(node1RootPath)
nodeInfoFilesCopier.addConfig(node2RootPath)
advanceTime()
// Create a file, in node 2 root path.
Files.write(node2RootPath.resolve(GOOD_NODE_INFO_NAME), content)
advanceTime()
// Remove node 2
nodeInfoFilesCopier.removeConfig(node2RootPath)
// Create another file in node 2 directory.
Files.write(node2RootPath.resolve(GOOD_NODE_INFO_NAME_2), content)
advanceTime()
eventually<AssertionError, Unit>(Duration.ofMinutes(1)) {
// Check only one file is copied to the other node.
checkDirectoryContainsSingleFile(node1AdditionalNodeInfoPath, GOOD_NODE_INFO_NAME)
}
}
@Test
fun `clear`() {
// Configure 2 nodes.
nodeInfoFilesCopier.addConfig(node1RootPath)
nodeInfoFilesCopier.addConfig(node2RootPath)
advanceTime()
nodeInfoFilesCopier.reset()
advanceTime()
Files.write(node2RootPath.resolve(GOOD_NODE_INFO_NAME_2), content)
// Give some time to the filesystem to report the change.
Thread.sleep(100)
assertEquals(0, Files.list(node1AdditionalNodeInfoPath).toList().size)
}
private fun advanceTime() {
scheduler.advanceTimeBy(1, TimeUnit.HOURS)
}
private fun checkDirectoryContainsSingleFile(path: Path, filename: String) {
assertEquals(1, Files.list(path).toList().size)
val onlyFileName = Files.list(path).toList().first().fileName.toString()
assertEquals(filename, onlyFileName)
}
}

View File

@ -1,6 +1,6 @@
package net.corda.nodeapi.internal
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
@ -17,10 +17,7 @@ import net.corda.nodeapi.DummyContractBackdoor
import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl
import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName
import net.corda.nodeapi.internal.serialization.withTokenContext
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.MEGA_CORP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.kryoSpecific
import net.corda.testing.*
import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.node.MockServices
import org.apache.commons.io.IOUtils
@ -41,8 +38,8 @@ class AttachmentsClassLoaderTests : TestDependencyInjectionBase() {
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.finance.contracts.isolated.AnotherDummyContract"
private fun SerializationContext.withAttachmentStorage(attachmentStorage: AttachmentStorage): SerializationContext {
val serviceHub = mock<ServiceHub>()
whenever(serviceHub.attachments).thenReturn(attachmentStorage)
val serviceHub = rigorousMock<ServiceHub>()
doReturn(attachmentStorage).whenever(serviceHub).attachments
return this.withServiceHub(serviceHub)
}

View File

@ -5,15 +5,13 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.util.DefaultClassResolver
import com.esotericsoftware.kryo.util.MapReferenceResolver
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever
import com.nhaarman.mockito_kotlin.*
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.AttachmentsClassLoader
import net.corda.nodeapi.internal.AttachmentsClassLoaderTests
import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.rigorousMock
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
@ -108,16 +106,6 @@ class CordaClassResolverTests {
val emptyMapClass = mapOf<Any, Any>().javaClass
}
val factory: SerializationFactory = object : SerializationFactory() {
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
TODO("not implemented")
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
TODO("not implemented")
}
}
private val emptyWhitelistContext: SerializationContext = SerializationContextImpl(KryoHeaderV0_1, this.javaClass.classLoader, EmptyWhitelist, emptyMap(), true, SerializationContext.UseCase.P2P)
private val allButBlacklistedContext: SerializationContext = SerializationContextImpl(KryoHeaderV0_1, this.javaClass.classLoader, AllButBlacklisted, emptyMap(), true, SerializationContext.UseCase.P2P)
@ -252,10 +240,11 @@ class CordaClassResolverTests {
@Test
fun `Kotlin EmptyList registers as Java emptyList`() {
val javaEmptyListClass = Collections.emptyList<Any>().javaClass
val kryo = mock<Kryo>()
val kryo = rigorousMock<Kryo>()
val resolver = CordaClassResolver(allButBlacklistedContext).apply { setKryo(kryo) }
whenever(kryo.getDefaultSerializer(javaEmptyListClass)).thenReturn(DefaultSerializableSerializer())
doReturn(DefaultSerializableSerializer()).whenever(kryo).getDefaultSerializer(javaEmptyListClass)
doReturn(false).whenever(kryo).references
doReturn(false).whenever(kryo).references = any()
val registration = resolver.registerImplicit(emptyListClass)
assertNotNull(registration)
assertEquals(javaEmptyListClass, registration.type)
@ -273,10 +262,11 @@ class CordaClassResolverTests {
@Test
fun `Kotlin EmptySet registers as Java emptySet`() {
val javaEmptySetClass = Collections.emptySet<Any>().javaClass
val kryo = mock<Kryo>()
val kryo = rigorousMock<Kryo>()
val resolver = CordaClassResolver(allButBlacklistedContext).apply { setKryo(kryo) }
whenever(kryo.getDefaultSerializer(javaEmptySetClass)).thenReturn(DefaultSerializableSerializer())
doReturn(DefaultSerializableSerializer()).whenever(kryo).getDefaultSerializer(javaEmptySetClass)
doReturn(false).whenever(kryo).references
doReturn(false).whenever(kryo).references = any()
val registration = resolver.registerImplicit(emptySetClass)
assertNotNull(registration)
assertEquals(javaEmptySetClass, registration.type)
@ -294,10 +284,11 @@ class CordaClassResolverTests {
@Test
fun `Kotlin EmptyMap registers as Java emptyMap`() {
val javaEmptyMapClass = Collections.emptyMap<Any, Any>().javaClass
val kryo = mock<Kryo>()
val kryo = rigorousMock<Kryo>()
val resolver = CordaClassResolver(allButBlacklistedContext).apply { setKryo(kryo) }
whenever(kryo.getDefaultSerializer(javaEmptyMapClass)).thenReturn(DefaultSerializableSerializer())
doReturn(DefaultSerializableSerializer()).whenever(kryo).getDefaultSerializer(javaEmptyMapClass)
doReturn(false).whenever(kryo).references
doReturn(false).whenever(kryo).references = any()
val registration = resolver.registerImplicit(emptyMapClass)
assertNotNull(registration)
assertEquals(javaEmptyMapClass, registration.type)

View File

@ -3,10 +3,10 @@ package net.corda.nodeapi.internal.serialization
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.io.Output
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.serialization.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.rigorousMock
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
@ -35,8 +35,7 @@ class SerializationTokenTest : TestDependencyInjectionBase() {
override fun equals(other: Any?) = other is LargeTokenizable && other.bytes.size == this.bytes.size
}
private fun serializeAsTokenContext(toBeTokenized: Any) = SerializeAsTokenContextImpl(toBeTokenized, factory, context, mock())
private fun serializeAsTokenContext(toBeTokenized: Any) = SerializeAsTokenContextImpl(toBeTokenized, factory, context, rigorousMock())
@Test
fun `write token and read tokenizable`() {
val tokenizableBefore = LargeTokenizable()

View File

@ -30,6 +30,7 @@ class EvolvabilityTests {
// data class C (val a: Int, val b: Int)
// val sc = SerializationOutput(sf).serialize(C(A, B))
// f.writeBytes(sc.bytes)
// println (path)
// new version of the class, in this case the order of the parameters has been swapped
data class C(val b: Int, val a: Int)
@ -54,6 +55,7 @@ class EvolvabilityTests {
// data class C (val a: Int, val b: String)
// val sc = SerializationOutput(sf).serialize(C(A, B))
// f.writeBytes(sc.bytes)
// println (path)
// new version of the class, in this case the order of the parameters has been swapped
data class C(val b: String, val a: Int)
@ -78,7 +80,6 @@ class EvolvabilityTests {
// val sc = SerializationOutput(sf).serialize(C(A))
// f.writeBytes(sc.bytes)
// println ("Path = $path")
data class C(val a: Int, val b: Int?)
val sc2 = f.readBytes()
@ -300,9 +301,6 @@ class EvolvabilityTests {
val path2 = EvolvabilityTests::class.java.getResource("EvolvabilityTests.multiVersion.2")
val path3 = EvolvabilityTests::class.java.getResource("EvolvabilityTests.multiVersion.3")
@Suppress("UNUSED_VARIABLE")
val f = File(path1.toURI())
val a = 100
val b = 200
val c = 300
@ -312,14 +310,24 @@ class EvolvabilityTests {
//
// Version 1:
// data class C (val a: Int, val b: Int)
//
// val scc = SerializationOutput(sf).serialize(C(a, b))
// File(path1.toURI()).writeBytes(scc.bytes)
// println ("Path = $path1")
//
// Version 2 - add param c
// data class C (val c: Int, val b: Int, val a: Int)
//
// val scc = SerializationOutput(sf).serialize(C(c, b, a))
// File(path2.toURI()).writeBytes(scc.bytes)
// println ("Path = $path2")
//
// Version 3 - add param d
// data class C (val b: Int, val c: Int, val d: Int, val a: Int)
//
// val scc = SerializationOutput(sf).serialize(C(b, c, d, a))
// f.writeBytes(scc.bytes)
// println ("Path = $path1")
// File(path3.toURI()).writeBytes(scc.bytes)
// println ("Path = $path3")
@Suppress("UNUSED")
data class C(val e: Int, val c: Int, val b: Int, val a: Int, val d: Int) {
@ -409,14 +417,24 @@ class EvolvabilityTests {
//
// Version 1:
// data class C (val a: Int, val b: Int, val c: Int)
//
// val scc = SerializationOutput(sf).serialize(C(a, b, c))
// File(path1.toURI()).writeBytes(scc.bytes)
// println ("Path = $path1")
//
// Version 2 - add param c
// data class C (val b: Int, val c: Int, val d: Int, val e: Int)
//
// val scc = SerializationOutput(sf).serialize(C(b, c, d, e))
// File(path2.toURI()).writeBytes(scc.bytes)
// println ("Path = $path2")
//
// Version 3 - add param d
// data class C (val b: Int, val c: Int, val d: Int, val e: Int, val f: Int)
//
// val scc = SerializationOutput(sf).serialize(C(b, c, d, e, f))
// File(path1.toURI()).writeBytes(scc.bytes)
// println ("Path = $path1")
// File(path3.toURI()).writeBytes(scc.bytes)
// println ("Path = $path3")
@Suppress("UNUSED")
data class C(val b: Int, val c: Int, val d: Int, val e: Int, val f: Int, val g: Int) {