Merge pull request #1469 from corda/anthonyk-os-merge-20181009

O/S Merge 9/10/2018
This commit is contained in:
Anthony Keenan 2018-10-10 11:00:25 +02:00 committed by GitHub
commit 4b9dd514c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 822 additions and 566 deletions

View File

@ -13,7 +13,7 @@ import net.corda.core.internal.div
import net.corda.core.internal.exists
import net.corda.core.internal.readObject
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.contextLogger
@ -57,7 +57,7 @@ class FirewallInstance(val conf: FirewallConfiguration,
}
if (!serializationExists) {
val classloader = this.javaClass.classLoader
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPFirewallSerializationScheme(emptyList()))
},

View File

@ -98,7 +98,7 @@ private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier()
val ctor = constructorForDeserialization(beanClass)
val amqpProperties = propertiesForSerialization(ctor, beanClass, serializerFactory)
.serializationOrder
.map { it.serializer.name }
.mapNotNull { if (it.isCalculated) null else it.serializer.name }
val propertyRenames = beanDesc.findProperties().associateBy({ it.name }, { it.internalName })
(amqpProperties - propertyRenames.values).let {
check(it.isEmpty()) { "Jackson didn't provide serialisers for $it" }

View File

@ -6,7 +6,6 @@ import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.*
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.serialization.internal.*
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
@ -35,7 +34,7 @@ class AMQPClientSerializationScheme(
}
fun createSerializationEnv(classLoader: ClassLoader? = null): SerializationEnvironment {
return SerializationEnvironmentImpl(
return SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPClientSerializationScheme(emptyList()))
},

View File

@ -20,7 +20,6 @@ dependencies {
testCompile "com.google.guava:guava-testlib:$guava_version"
// Bring in the MockNode infrastructure for writing protocol unit tests.
testCompile project(":node")
testCompile project(":node-driver")
// AssertJ: for fluent assertions for testing

View File

@ -1,74 +0,0 @@
package net.corda.core.serialization.internal
import net.corda.core.KeepForDJVM
import net.corda.core.serialization.SerializedBytes
import net.corda.core.utilities.ByteSequence
import java.io.NotSerializableException
/**
* A deterministic version of [CheckpointSerializationFactory] that does not use thread-locals to manage serialization
* context.
*/
@KeepForDJVM
class CheckpointSerializationFactory(
private val scheme: CheckpointSerializationScheme
) {
val defaultContext: CheckpointSerializationContext get() = _currentContext ?: effectiveSerializationEnv.checkpointContext
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
/**
* Deserialize the bytes in to an object, using the prefixed bytes to determine the format.
*
* @param byteSequence The bytes to deserialize, including a format header prefix.
* @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown.
* @param context A context that configures various parameters to deserialization.
*/
@Throws(NotSerializableException::class)
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T {
return withCurrentContext(context) { scheme.deserialize(byteSequence, clazz, context) }
}
/**
* Serialize an object to bytes using the preferred serialization format version from the context.
*
* @param obj The object to be serialized.
* @param context A context that configures various parameters to serialization, including the serialization format version.
*/
fun <T : Any> serialize(obj: T, context: CheckpointSerializationContext): SerializedBytes<T> {
return withCurrentContext(context) { scheme.serialize(obj, context) }
}
override fun toString(): String {
return "${this.javaClass.name} scheme=$scheme ${creator.joinToString("\n")}"
}
override fun equals(other: Any?): Boolean {
return other is CheckpointSerializationFactory && other.scheme == this.scheme
}
override fun hashCode(): Int = scheme.hashCode()
private var _currentContext: CheckpointSerializationContext? = null
/**
* Change the current context inside the block to that supplied.
*/
fun <T> withCurrentContext(context: CheckpointSerializationContext?, block: () -> T): T {
val priorContext = _currentContext
if (context != null) _currentContext = context
try {
return block()
} finally {
if (context != null) _currentContext = priorContext
}
}
companion object {
/**
* A default factory for serialization/deserialization.
*/
val defaultFactory: CheckpointSerializationFactory get() = effectiveSerializationEnv.checkpointSerializationFactory
}
}

View File

@ -22,6 +22,9 @@ test {
// Running this class is the whole point, so include it explicitly.
includeTestsMatching "net.corda.deterministic.data.GenerateData"
}
// force execution of these tests to generate artifacts required by other module (eg. VerifyTransactionTest)
// note: required by Gradle Build Cache.
outputs.upToDateWhen { false }
}
assemble.finalizedBy test

View File

@ -4,7 +4,7 @@ import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase.P2P
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.serialization.internal.*
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
@ -58,13 +58,11 @@ class LocalSerializationRule(private val label: String) : TestRule {
_contextSerializationEnv.set(null)
}
private fun createTestSerializationEnv(): SerializationEnvironmentImpl {
private fun createTestSerializationEnv(): SerializationEnvironment {
val factory = SerializationFactoryImpl(mutableMapOf()).apply {
registerScheme(AMQPSerializationScheme(emptySet(), AccessOrderLinkedHashMap(128)))
}
return object : SerializationEnvironmentImpl(factory, AMQP_P2P_CONTEXT) {
override fun toString() = "testSerializationEnv($label)"
}
return SerializationEnvironment.with(factory, AMQP_P2P_CONTEXT)
}
private class AMQPSerializationScheme(

View File

@ -3,6 +3,7 @@ package net.corda.core.contracts
import net.corda.core.KeepForDJVM
import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty
import net.corda.core.serialization.SerializableCalculatedProperty
import java.security.PublicKey
/**
@ -38,6 +39,7 @@ interface FungibleAsset<T : Any> : OwnableState {
* There must be an ExitCommand signed by these keys to destroy the amount. While all states require their
* owner to sign, some (i.e. cash) also require the issuer.
*/
@get:SerializableCalculatedProperty
val exitKeys: Collection<PublicKey>
/**

View File

@ -19,4 +19,12 @@ import java.lang.annotation.Inherited
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Inherited
annotation class CordaSerializable
annotation class CordaSerializable
/**
* Used to annotate methods which expose calculated values that we want to be serialized for use by the class carpenter.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.FUNCTION)
annotation class SerializableCalculatedProperty

View File

@ -13,75 +13,12 @@ import java.io.NotSerializableException
object CheckpointSerializationDefaults {
@DeleteForDJVM
val CHECKPOINT_CONTEXT get() = effectiveSerializationEnv.checkpointContext
val CHECKPOINT_SERIALIZATION_FACTORY get() = effectiveSerializationEnv.checkpointSerializationFactory
}
/**
* A class for serializing and deserializing objects at checkpoints, using Kryo serialization.
*/
@KeepForDJVM
class CheckpointSerializationFactory(
private val scheme: CheckpointSerializationScheme
) {
val defaultContext: CheckpointSerializationContext get() = _currentContext.get() ?: effectiveSerializationEnv.checkpointContext
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
/**
* Deserialize the bytes in to an object, using the prefixed bytes to determine the format.
*
* @param byteSequence The bytes to deserialize, including a format header prefix.
* @param clazz The class or superclass or the object to be deserialized, or [Any] or [Object] if unknown.
* @param context A context that configures various parameters to deserialization.
*/
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T {
return withCurrentContext(context) { scheme.deserialize(byteSequence, clazz, context) }
}
/**
* Serialize an object to bytes using the preferred serialization format version from the context.
*
* @param obj The object to be serialized.
* @param context A context that configures various parameters to serialization, including the serialization format version.
*/
fun <T : Any> serialize(obj: T, context: CheckpointSerializationContext): SerializedBytes<T> {
return withCurrentContext(context) { scheme.serialize(obj, context) }
}
override fun toString(): String {
return "${this.javaClass.name} scheme=$scheme ${creator.joinToString("\n")}"
}
override fun equals(other: Any?): Boolean {
return other is CheckpointSerializationFactory && other.scheme == this.scheme
}
override fun hashCode(): Int = scheme.hashCode()
private val _currentContext = ThreadLocal<CheckpointSerializationContext?>()
/**
* Change the current context inside the block to that supplied.
*/
fun <T> withCurrentContext(context: CheckpointSerializationContext?, block: () -> T): T {
val priorContext = _currentContext.get()
if (context != null) _currentContext.set(context)
try {
return block()
} finally {
if (context != null) _currentContext.set(priorContext)
}
}
companion object {
val defaultFactory: CheckpointSerializationFactory get() = effectiveSerializationEnv.checkpointSerializationFactory
}
val CHECKPOINT_SERIALIZER get() = effectiveSerializationEnv.checkpointSerializer
}
@KeepForDJVM
@DoNotImplement
interface CheckpointSerializationScheme {
interface CheckpointSerializer {
@Throws(NotSerializableException::class)
fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: CheckpointSerializationContext): T
@ -167,32 +104,36 @@ interface CheckpointSerializationContext {
/*
* Convenience extension method for deserializing a ByteSequence, utilising the default factory.
*/
inline fun <reified T : Any> ByteSequence.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
context: CheckpointSerializationContext): T {
return serializationFactory.deserialize(this, T::class.java, context)
@JvmOverloads
inline fun <reified T : Any> ByteSequence.checkpointDeserialize(
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
return effectiveSerializationEnv.checkpointSerializer.deserialize(this, T::class.java, context)
}
/**
* Convenience extension method for deserializing SerializedBytes with type matching, utilising the default factory.
*/
inline fun <reified T : Any> SerializedBytes<T>.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
context: CheckpointSerializationContext): T {
return serializationFactory.deserialize(this, T::class.java, context)
@JvmOverloads
inline fun <reified T : Any> SerializedBytes<T>.checkpointDeserialize(
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
return effectiveSerializationEnv.checkpointSerializer.deserialize(this, T::class.java, context)
}
/**
* Convenience extension method for deserializing a ByteArray, utilising the default factory.
*/
inline fun <reified T : Any> ByteArray.checkpointDeserialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
context: CheckpointSerializationContext): T {
@JvmOverloads
inline fun <reified T : Any> ByteArray.checkpointDeserialize(
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): T {
require(isNotEmpty()) { "Empty bytes" }
return this.sequence().checkpointDeserialize(serializationFactory, context)
return this.sequence().checkpointDeserialize(context)
}
/**
* Convenience extension method for serializing an object of type T, utilising the default factory.
*/
fun <T : Any> T.checkpointSerialize(serializationFactory: CheckpointSerializationFactory = CheckpointSerializationFactory.defaultFactory,
context: CheckpointSerializationContext): SerializedBytes<T> {
return serializationFactory.serialize(this, context)
@JvmOverloads
fun <T : Any> T.checkpointSerialize(
context: CheckpointSerializationContext = effectiveSerializationEnv.checkpointContext): SerializedBytes<T> {
return effectiveSerializationEnv.checkpointSerializer.serialize(this, context)
}

View File

@ -11,38 +11,63 @@ import net.corda.core.serialization.SerializationFactory
@KeepForDJVM
interface SerializationEnvironment {
companion object {
fun with(
serializationFactory: SerializationFactory,
p2pContext: SerializationContext,
rpcServerContext: SerializationContext? = null,
rpcClientContext: SerializationContext? = null,
storageContext: SerializationContext? = null,
checkpointContext: CheckpointSerializationContext? = null,
checkpointSerializer: CheckpointSerializer? = null
): SerializationEnvironment =
SerializationEnvironmentImpl(
serializationFactory = serializationFactory,
p2pContext = p2pContext,
optionalRpcServerContext = rpcServerContext,
optionalRpcClientContext = rpcClientContext,
optionalStorageContext = storageContext,
optionalCheckpointContext = checkpointContext,
optionalCheckpointSerializer = checkpointSerializer
)
}
val serializationFactory: SerializationFactory
val checkpointSerializationFactory: CheckpointSerializationFactory
val p2pContext: SerializationContext
val rpcServerContext: SerializationContext
val rpcClientContext: SerializationContext
val storageContext: SerializationContext
val checkpointSerializer: CheckpointSerializer
val checkpointContext: CheckpointSerializationContext
}
@KeepForDJVM
open class SerializationEnvironmentImpl(
private class SerializationEnvironmentImpl(
override val serializationFactory: SerializationFactory,
override val p2pContext: SerializationContext,
rpcServerContext: SerializationContext? = null,
rpcClientContext: SerializationContext? = null,
storageContext: SerializationContext? = null,
checkpointContext: CheckpointSerializationContext? = null,
checkpointSerializationFactory: CheckpointSerializationFactory? = null) : SerializationEnvironment {
// Those that are passed in as null are never inited:
override lateinit var rpcServerContext: SerializationContext
override lateinit var rpcClientContext: SerializationContext
override lateinit var storageContext: SerializationContext
override lateinit var checkpointContext: CheckpointSerializationContext
override lateinit var checkpointSerializationFactory: CheckpointSerializationFactory
private val optionalRpcServerContext: SerializationContext? = null,
private val optionalRpcClientContext: SerializationContext? = null,
private val optionalStorageContext: SerializationContext? = null,
private val optionalCheckpointContext: CheckpointSerializationContext? = null,
private val optionalCheckpointSerializer: CheckpointSerializer? = null) : SerializationEnvironment {
init {
rpcServerContext?.let { this.rpcServerContext = it }
rpcClientContext?.let { this.rpcClientContext = it }
storageContext?.let { this.storageContext = it }
checkpointContext?.let { this.checkpointContext = it }
checkpointSerializationFactory?.let { this.checkpointSerializationFactory = it }
}
override val rpcServerContext: SerializationContext get() = optionalRpcServerContext ?:
throw UnsupportedOperationException("RPC server serialization not supported in this environment")
override val rpcClientContext: SerializationContext get() = optionalRpcClientContext ?:
throw UnsupportedOperationException("RPC client serialization not supported in this environment")
override val storageContext: SerializationContext get() = optionalStorageContext ?:
throw UnsupportedOperationException("Storage serialization not supported in this environment")
override val checkpointContext: CheckpointSerializationContext get() = optionalCheckpointContext ?:
throw UnsupportedOperationException("Checkpoint serialization not supported in this environment")
override val checkpointSerializer: CheckpointSerializer get() = optionalCheckpointSerializer ?:
throw UnsupportedOperationException("Checkpoint serialization not supported in this environment")
}
private val _nodeSerializationEnv = SimpleToggleField<SerializationEnvironment>("nodeSerializationEnv", true)

View File

@ -29,7 +29,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
*/
abstract val bytes: ByteArray
/** Returns a [ByteArrayInputStream] of the bytes */
/** Returns a [ByteArrayInputStream] of the bytes. */
fun open() = ByteArrayInputStream(_bytes, offset, size)
/**
@ -109,7 +109,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
return Integer.signum(unsignedThis - unsignedOther)
}
}
// First min bytes is the same, so now resort to size
// First min bytes is the same, so now resort to size.
return Integer.signum(this.size - other.size)
}
@ -191,12 +191,12 @@ fun ByteArray.toHexString(): String = DatatypeConverter.printHexBinary(this)
fun String.parseAsHex(): ByteArray = DatatypeConverter.parseHexBinary(this)
/**
* Class is public for serialization purposes
* Class is public for serialization purposes.
*/
@KeepForDJVM
class OpaqueBytesSubSequence(override val bytes: ByteArray, offset: Int, size: Int) : ByteSequence(bytes, offset, size) {
init {
require(offset >= 0 && offset < bytes.size)
require(size >= 0 && size <= bytes.size)
require(size >= 0 && offset + size <= bytes.size)
}
}

View File

@ -1,7 +1,5 @@
package net.corda.core.flows;
import net.corda.core.serialization.internal.CheckpointSerializationDefaults;
import net.corda.core.serialization.internal.CheckpointSerializationFactory;
import net.corda.core.serialization.SerializationDefaults;
import net.corda.core.serialization.SerializationFactory;
import net.corda.testing.core.SerializationEnvironmentRule;
@ -32,12 +30,10 @@ public class SerializationApiInJavaTest {
SerializationDefaults defaults = SerializationDefaults.INSTANCE;
SerializationFactory factory = defaults.getSERIALIZATION_FACTORY();
CheckpointSerializationDefaults checkpointDefaults = CheckpointSerializationDefaults.INSTANCE;
CheckpointSerializationFactory checkpointSerializationFactory = checkpointDefaults.getCHECKPOINT_SERIALIZATION_FACTORY();
serialize("hello", factory, defaults.getP2P_CONTEXT());
serialize("hello", factory, defaults.getRPC_SERVER_CONTEXT());
serialize("hello", factory, defaults.getRPC_CLIENT_CONTEXT());
serialize("hello", factory, defaults.getSTORAGE_CONTEXT());
checkpointSerialize("hello", checkpointSerializationFactory, checkpointDefaults.getCHECKPOINT_CONTEXT());
checkpointSerialize("hello");
}
}

View File

@ -560,10 +560,33 @@ be able to use reflection over the deserialized data, for scripting languages th
ensuring classes not on the classpath can be deserialized without loading potentially malicious code.
If the original class implements some interfaces then the carpenter will make sure that all of the interface methods are
backed by feilds. If that's not the case then an exception will be thrown during deserialization. This check can
backed by fields. If that's not the case then an exception will be thrown during deserialization. This check can
be turned off with ``SerializationContext.withLenientCarpenter``. This can be useful if only the field getters are needed,
say in an object viewer.
Calculated values
`````````````````
In some cases, for example the `exitKeys` field in ``FungibleState``, a property in an interface may normally be implemented
as a *calculated* value, with a "getter" method for reading it but neither a corresponding constructor parameter nor a
"setter" method for writing it. In this case, it will not automatically be included among the properties to be serialized,
since the receiving class would ordinarily be able to re-calculate it on demand. However, a synthesized class will not
have the method implementation which knows how to calculate the value, and a cast to the interface will fail because the
property is not serialized and so the "getter" method present in the interface will not be synthesized.
The solution is to annotate the method with the ``SerializableCalculatedProperty`` annotation, which will cause the value
exposed by the method to be read and transmitted during serialization, but discarded during normal deserialization. The
synthesized class will then include a backing field together with a "getter" for the serialized calculated value, and will
remain compatible with the interface.
If the annotation is added to the method in the *interface*, then all implementing classes must calculate the value and
none may have a corresponding backing field; alternatively, it can be added to the overriding method on each implementing
class where the value is calculated and there is no backing field. If the field is a Kotlin ``val``, then the annotation
should be targeted at its getter method, e.g. ``@get:SerializableCalculatedProperty``.
Future enhancements
```````````````````
Possible future enhancements include:
#. Java singleton support. We will add support for identifying classes which are singletons and identifying the

View File

@ -22,10 +22,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.node.services.CordaService
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.serialization.internal.*
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.node.CordaClock
@ -38,7 +35,6 @@ import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT
import net.corda.node.serialization.kryo.KryoSerializationScheme
import net.corda.node.services.ContractUpgradeHandler
import net.corda.node.services.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
@ -360,12 +356,11 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, overri
}
if (!serializationExists) {
val classloader = cordappLoader.appClassLoader
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps))
registerScheme(AMQPClientSerializationScheme(cordappLoader.cordapps))
},
checkpointSerializationFactory = CheckpointSerializationFactory(KryoSerializationScheme),
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
rpcServerContext = AMQP_RPC_SERVER_CONTEXT.withClassLoader(classloader),
storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader),

View File

@ -14,10 +14,7 @@ import net.corda.core.node.services.ContractUpgradeService
import net.corda.core.node.services.TransactionVerifierService
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.serialization.internal.*
import net.corda.core.utilities.contextLogger
import net.corda.node.CordaClock
import net.corda.node.SimpleClock
@ -28,7 +25,6 @@ import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT
import net.corda.node.serialization.kryo.KryoSerializationScheme
import net.corda.node.services.api.AuditService
import net.corda.node.services.api.MonitoringService
import net.corda.node.services.api.ServiceHubInternal
@ -195,12 +191,11 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, overrid
}
if (!serializationExists) {
val classloader = cordappLoader.appClassLoader
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps))
registerScheme(AMQPClientSerializationScheme(cordappLoader.cordapps))
},
checkpointSerializationFactory = CheckpointSerializationFactory(KryoSerializationScheme),
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
rpcServerContext = AMQP_RPC_SERVER_CONTEXT.withClassLoader(classloader),
storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader),

View File

@ -16,6 +16,7 @@ import net.corda.core.node.ServiceHub
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.finance.contracts.asset.cash.selection.AbstractCashSelection

View File

@ -14,7 +14,7 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.utilities.days
import net.corda.core.utilities.getOrThrow
@ -393,7 +393,7 @@ internal constructor(private val initSerEnv: Boolean,
// We need to to set serialization env, because generation of parameters is run from Cordform.
private fun initialiseSerialization() {
_contextSerializationEnv.set(SerializationEnvironmentImpl(
_contextSerializationEnv.set(SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPParametersSerializationScheme)
},

View File

@ -4,7 +4,6 @@ import net.corda.core.cordapp.Cordapp
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.internal.CheckpointSerializationDefaults
import net.corda.core.serialization.internal.checkpointDeserialize
import net.corda.node.services.api.CheckpointStorage
@ -21,7 +20,7 @@ object CheckpointVerifier {
*/
fun verifyCheckpointsCompatible(checkpointStorage: CheckpointStorage, currentCordapps: List<Cordapp>, platformVersion: Int, serviceHub: ServiceHub, tokenizableServices: List<Any>) {
val checkpointSerializationContext = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT.withTokenContext(
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZATION_FACTORY, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZER, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
)
checkpointStorage.getAllCheckpoints().forEach { (_, serializedCheckpoint) ->

View File

@ -21,8 +21,7 @@ import net.corda.core.messaging.RPCOps
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
@ -38,7 +37,7 @@ import net.corda.node.internal.security.RPCSecurityManagerImpl
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT
import net.corda.node.serialization.kryo.KryoSerializationScheme
import net.corda.node.serialization.kryo.KryoCheckpointSerializer
import net.corda.node.services.Permissions
import net.corda.node.services.api.FlowStarter
import net.corda.node.services.api.ServiceHubInternal
@ -495,17 +494,19 @@ open class Node(configuration: NodeConfiguration,
private fun initialiseSerialization() {
if (!initialiseSerialization) return
val classloader = cordappLoader.appClassLoader
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPServerSerializationScheme(cordappLoader.cordapps))
registerScheme(AMQPClientSerializationScheme(cordappLoader.cordapps))
},
checkpointSerializationFactory = CheckpointSerializationFactory(KryoSerializationScheme),
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
rpcServerContext = AMQP_RPC_SERVER_CONTEXT.withClassLoader(classloader),
rpcClientContext = if (configuration.shouldInitCrashShell()) AMQP_RPC_CLIENT_CONTEXT.withClassLoader(classloader) else null, //even Shell embeded in the node connects via RPC to the node
storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader),
checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader),
rpcClientContext = if (configuration.shouldInitCrashShell()) AMQP_RPC_CLIENT_CONTEXT.withClassLoader(classloader) else null) //even Shell embeded in the node connects via RPC to the node
checkpointSerializer = KryoCheckpointSerializer,
checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader)
)
}
/** Starts a blocking event loop for message dispatch. */

View File

@ -12,7 +12,7 @@ import com.esotericsoftware.kryo.serializers.ClosureSerializer
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.CheckpointSerializationScheme
import net.corda.core.serialization.internal.CheckpointSerializer
import net.corda.core.utilities.ByteSequence
import net.corda.serialization.internal.*
import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY
@ -32,7 +32,7 @@ private object AutoCloseableSerialisationDetector : Serializer<AutoCloseable>()
override fun read(kryo: Kryo, input: Input, type: Class<AutoCloseable>) = throw IllegalStateException("Should not reach here!")
}
object KryoSerializationScheme : CheckpointSerializationScheme {
object KryoCheckpointSerializer : CheckpointSerializer {
private val kryoPoolsForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, KryoPool>()
private fun getPool(context: CheckpointSerializationContext): KryoPool {

View File

@ -139,7 +139,7 @@ class MultiThreadedStateMachineManager(
checkQuasarJavaAgentPresence()
this.tokenizableServices = tokenizableServices
val checkpointSerializationContext = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT.withTokenContext(
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZATION_FACTORY, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZER, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
)
this.checkpointSerializationContext = checkpointSerializationContext
this.actionExecutor = makeActionExecutor(checkpointSerializationContext)

View File

@ -127,7 +127,7 @@ class SingleThreadedStateMachineManager(
override fun start(tokenizableServices: List<Any>) {
checkQuasarJavaAgentPresence()
val checkpointSerializationContext = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT.withTokenContext(
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZATION_FACTORY, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
CheckpointSerializeAsTokenContextImpl(tokenizableServices, CheckpointSerializationDefaults.CHECKPOINT_SERIALIZER, CheckpointSerializationDefaults.CHECKPOINT_CONTEXT, serviceHub)
)
this.checkpointSerializationContext = checkpointSerializationContext
this.actionExecutor = makeActionExecutor(checkpointSerializationContext)

View File

@ -22,7 +22,7 @@ import net.corda.core.internal.notary.isConsumedByTheSameTx
import net.corda.core.internal.notary.validateTimeWindow
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationDefaults
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.checkpointSerialize
import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.contextLogger
@ -201,7 +201,7 @@ class RaftTransactionCommitLog<E, EK>(
class CordaKryoSerializer<T : Any> : TypeSerializer<T> {
private val context = CheckpointSerializationDefaults.CHECKPOINT_CONTEXT.withEncoding(CordaSerializationEncoding.SNAPPY)
private val factory = CheckpointSerializationFactory.defaultFactory
private val checkpointSerializer = CheckpointSerializationDefaults.CHECKPOINT_SERIALIZER
override fun write(obj: T, buffer: BufferOutput<*>, serializer: Serializer) {
val serialized = obj.checkpointSerialize(context = context)
@ -213,7 +213,7 @@ class RaftTransactionCommitLog<E, EK>(
val size = buffer.readInt()
val serialized = ByteArray(size)
buffer.read(serialized)
return factory.deserialize(ByteSequence.of(serialized), type, context)
return checkpointSerializer.deserialize(ByteSequence.of(serialized), type, context)
}
}
}

View File

@ -13,7 +13,6 @@ import net.corda.core.crypto.*
import net.corda.core.internal.FetchDataFlow
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.checkpointDeserialize
import net.corda.core.serialization.internal.checkpointSerialize
import net.corda.core.utilities.ByteSequence
@ -23,11 +22,13 @@ import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.serialization.internal.*
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule
import net.corda.testing.internal.rigorousMock
import org.assertj.core.api.Assertions.*
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@ -48,12 +49,12 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
fun compression() = arrayOf<CordaSerializationEncoding?>(null) + CordaSerializationEncoding.values()
}
private lateinit var factory: CheckpointSerializationFactory
@get:Rule
val serializationRule = CheckpointSerializationEnvironmentRule()
private lateinit var context: CheckpointSerializationContext
@Before
fun setup() {
factory = CheckpointSerializationFactory(KryoSerializationScheme)
context = CheckpointSerializationContextImpl(
javaClass.classLoader,
AllWhitelist,
@ -69,15 +70,15 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
fun `simple data class`() {
val birthday = Instant.parse("1984-04-17T00:30:00.00Z")
val mike = Person("mike", birthday)
val bits = mike.checkpointSerialize(factory, context)
assertThat(bits.checkpointDeserialize(factory, context)).isEqualTo(Person("mike", birthday))
val bits = mike.checkpointSerialize(context)
assertThat(bits.checkpointDeserialize(context)).isEqualTo(Person("mike", birthday))
}
@Test
fun `null values`() {
val bob = Person("bob", null)
val bits = bob.checkpointSerialize(factory, context)
assertThat(bits.checkpointDeserialize(factory, context)).isEqualTo(Person("bob", null))
val bits = bob.checkpointSerialize(context)
assertThat(bits.checkpointDeserialize(context)).isEqualTo(Person("bob", null))
}
@Test
@ -85,10 +86,10 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val noReferencesContext = context.withoutReferences()
val obj : ByteSequence = Ints.toByteArray(0x01234567).sequence()
val originalList : ArrayList<ByteSequence> = ArrayList<ByteSequence>().apply { this += obj }
val deserialisedList = originalList.checkpointSerialize(factory, noReferencesContext).checkpointDeserialize(factory, noReferencesContext)
val deserialisedList = originalList.checkpointSerialize(noReferencesContext).checkpointDeserialize(noReferencesContext)
originalList += obj
deserialisedList += obj
assertThat(deserialisedList.checkpointSerialize(factory, noReferencesContext)).isEqualTo(originalList.checkpointSerialize(factory, noReferencesContext))
assertThat(deserialisedList.checkpointSerialize(noReferencesContext)).isEqualTo(originalList.checkpointSerialize(noReferencesContext))
}
@Test
@ -105,14 +106,14 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
this += instant
this += instant
}
assertThat(listWithSameInstances.checkpointSerialize(factory, noReferencesContext)).isEqualTo(listWithCopies.checkpointSerialize(factory, noReferencesContext))
assertThat(listWithSameInstances.checkpointSerialize(noReferencesContext)).isEqualTo(listWithCopies.checkpointSerialize(noReferencesContext))
}
@Test
fun `cyclic object graph`() {
val cyclic = Cyclic(3)
val bits = cyclic.checkpointSerialize(factory, context)
assertThat(bits.checkpointDeserialize(factory, context)).isEqualTo(cyclic)
val bits = cyclic.checkpointSerialize(context)
assertThat(bits.checkpointDeserialize(context)).isEqualTo(cyclic)
}
@Test
@ -124,7 +125,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
signature.verify(bitsToSign)
assertThatThrownBy { signature.verify(wrongBits) }
val deserialisedKeyPair = keyPair.checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val deserialisedKeyPair = keyPair.checkpointSerialize(context).checkpointDeserialize(context)
val deserialisedSignature = deserialisedKeyPair.sign(bitsToSign)
deserialisedSignature.verify(bitsToSign)
assertThatThrownBy { deserialisedSignature.verify(wrongBits) }
@ -132,28 +133,28 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test
fun `write and read Kotlin object singleton`() {
val serialised = TestSingleton.checkpointSerialize(factory, context)
val deserialised = serialised.checkpointDeserialize(factory, context)
val serialised = TestSingleton.checkpointSerialize(context)
val deserialised = serialised.checkpointDeserialize(context)
assertThat(deserialised).isSameAs(TestSingleton)
}
@Test
fun `check Kotlin EmptyList can be serialised`() {
val deserialisedList: List<Int> = emptyList<Int>().checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val deserialisedList: List<Int> = emptyList<Int>().checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(0, deserialisedList.size)
assertEquals<Any>(Collections.emptyList<Int>().javaClass, deserialisedList.javaClass)
}
@Test
fun `check Kotlin EmptySet can be serialised`() {
val deserialisedSet: Set<Int> = emptySet<Int>().checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val deserialisedSet: Set<Int> = emptySet<Int>().checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(0, deserialisedSet.size)
assertEquals<Any>(Collections.emptySet<Int>().javaClass, deserialisedSet.javaClass)
}
@Test
fun `check Kotlin EmptyMap can be serialised`() {
val deserialisedMap: Map<Int, Int> = emptyMap<Int, Int>().checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val deserialisedMap: Map<Int, Int> = emptyMap<Int, Int>().checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(0, deserialisedMap.size)
assertEquals<Any>(Collections.emptyMap<Int, Int>().javaClass, deserialisedMap.javaClass)
}
@ -161,7 +162,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test
fun `InputStream serialisation`() {
val rubbish = ByteArray(12345) { (it * it * 0.12345).toByte() }
val readRubbishStream: InputStream = rubbish.inputStream().checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val readRubbishStream: InputStream = rubbish.inputStream().checkpointSerialize(context).checkpointDeserialize(context)
for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte())
}
@ -171,7 +172,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test
fun `InputStream serialisation does not write trailing garbage`() {
val byteArrays = listOf("123", "456").map { it.toByteArray() }
val streams = byteArrays.map { it.inputStream() }.checkpointSerialize(factory, context).checkpointDeserialize(factory, context).iterator()
val streams = byteArrays.map { it.inputStream() }.checkpointSerialize(context).checkpointDeserialize(context).iterator()
byteArrays.forEach { assertArrayEquals(it, streams.next().readBytes()) }
assertFalse(streams.hasNext())
}
@ -182,8 +183,8 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val testBytes = testString.toByteArray()
val meta = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(ALICE_PUBKEY).schemeNumberID))
val serializedMetaData = meta.checkpointSerialize(factory, context).bytes
val meta2 = serializedMetaData.checkpointDeserialize<SignableData>(factory, context)
val serializedMetaData = meta.checkpointSerialize(context).bytes
val meta2 = serializedMetaData.checkpointDeserialize<SignableData>(context)
assertEquals(meta2, meta)
}
@ -191,7 +192,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
fun `serialize - deserialize Logger`() {
val storageContext: CheckpointSerializationContext = context
val logger = LoggerFactory.getLogger("aName")
val logger2 = logger.checkpointSerialize(factory, storageContext).checkpointDeserialize(factory, storageContext)
val logger2 = logger.checkpointSerialize(storageContext).checkpointDeserialize(storageContext)
assertEquals(logger.name, logger2.name)
assertTrue(logger === logger2)
}
@ -203,7 +204,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
SecureHash.sha256(rubbish),
rubbish.size,
rubbish.inputStream()
).checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
).checkpointSerialize(context).checkpointDeserialize(context)
for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte())
}
@ -230,8 +231,8 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32
))
val serializedBytes = expected.checkpointSerialize(factory, context)
val actual = serializedBytes.checkpointDeserialize(factory, context)
val serializedBytes = expected.checkpointSerialize(context)
val actual = serializedBytes.checkpointDeserialize(context)
assertEquals(expected, actual)
}
@ -278,14 +279,13 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
}
}
Tmp()
val factory = CheckpointSerializationFactory(KryoSerializationScheme)
val context = CheckpointSerializationContextImpl(
javaClass.classLoader,
AllWhitelist,
emptyMap(),
true,
null)
pt.checkpointSerialize(factory, context)
pt.checkpointSerialize(context)
}
@Test
@ -293,7 +293,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val exception = IllegalArgumentException("fooBar")
val toBeSuppressedOnSenderSide = IllegalStateException("bazz1")
exception.addSuppressed(toBeSuppressedOnSenderSide)
val exception2 = exception.checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val exception2 = exception.checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(exception.message, exception2.message)
assertEquals(1, exception2.suppressed.size)
@ -308,7 +308,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test
fun `serialize - deserialize Exception no suppressed`() {
val exception = IllegalArgumentException("fooBar")
val exception2 = exception.checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val exception2 = exception.checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(exception.message, exception2.message)
assertEquals(0, exception2.suppressed.size)
@ -322,7 +322,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
fun `serialize - deserialize HashNotFound`() {
val randomHash = SecureHash.randomSHA256()
val exception = FetchDataFlow.HashNotFound(randomHash)
val exception2 = exception.checkpointSerialize(factory, context).checkpointDeserialize(factory, context)
val exception2 = exception.checkpointSerialize(context).checkpointDeserialize(context)
assertEquals(randomHash, exception2.requested)
}
@ -330,17 +330,17 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
fun `compression has the desired effect`() {
compression ?: return
val data = ByteArray(12345).also { Random(0).nextBytes(it) }.let { it + it }
val compressed = data.checkpointSerialize(factory, context)
val compressed = data.checkpointSerialize(context)
assertEquals(.5, compressed.size.toDouble() / data.size, .03)
assertArrayEquals(data, compressed.checkpointDeserialize(factory, context))
assertArrayEquals(data, compressed.checkpointDeserialize(context))
}
@Test
fun `a particular encoding can be banned for deserialization`() {
compression ?: return
doReturn(false).whenever(context.encodingWhitelist).acceptEncoding(compression)
val compressed = "whatever".checkpointSerialize(factory, context)
catchThrowable { compressed.checkpointDeserialize(factory, context) }.run {
val compressed = "whatever".checkpointSerialize(context)
catchThrowable { compressed.checkpointDeserialize(context) }.run {
assertSame<Any>(KryoException::class.java, javaClass)
assertEquals(encodingNotPermittedFormat.format(compression), message)
}
@ -351,8 +351,8 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
class Holder(val holder: ByteArray)
val obj = Holder(ByteArray(20000))
val uncompressedSize = obj.checkpointSerialize(factory, context.withEncoding(null)).size
val compressedSize = obj.checkpointSerialize(factory, context.withEncoding(CordaSerializationEncoding.SNAPPY)).size
val uncompressedSize = obj.checkpointSerialize(context.withEncoding(null)).size
val compressedSize = obj.checkpointSerialize(context.withEncoding(CordaSerializationEncoding.SNAPPY)).size
// If these need fixing, sounds like Kryo wire format changed and checkpoints might not surive an upgrade.
assertEquals(20222, uncompressedSize)
assertEquals(1111, compressedSize)

View File

@ -34,6 +34,7 @@ import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.startFlow
import org.junit.After
import org.junit.Test
import java.security.PublicKey
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.reflect.jvm.jvmName
@ -127,7 +128,7 @@ class VaultSoftLockManagerTest {
override val owner get() = participants[0]
override fun withNewOwner(newOwner: AbstractParty) = throw UnsupportedOperationException()
override val amount get() = Amount(1, Issued(PartyAndReference(owner, OpaqueBytes.of(1)), Unit))
override val exitKeys get() = throw UnsupportedOperationException()
override val exitKeys get() = emptyList<PublicKey>()
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Unit>>, newOwner: AbstractParty) = throw UnsupportedOperationException()
override fun equals(other: Any?) = other is FungibleAssetImpl && participants == other.participants
override fun hashCode() = participants.hashCode()

View File

@ -5,7 +5,7 @@ import sys, traceback
# {{{ Representation of a command-line program
class Program:
# Create a new command-line program represenation, provided an optional name and description
# Create a new command-line program representation, provided an optional name and description
def __init__(self, name=None, description=None):
self.parser = ArgumentParser(name, description=description)
self.subparsers = self.parser.add_subparsers(title='commands')
@ -39,7 +39,10 @@ class Program:
t, exception, tb = sys.exc_info()
self.parser.error('{}\n\n{}'.format(error.message, '\n'.join(traceback.format_tb(tb))))
else:
self.parser.error(error.message)
try:
self.parser.error(error.message)
except AttributeError:
self.parser.error(str(error))
# }}}
# {{{ Representation of a sub-command of a command-line program

View File

@ -3,6 +3,13 @@ from jira import JIRA
from jira.exceptions import JIRAError
# }}}
# {{{ Python 2 and 3 interoperability
try:
unicode('')
except NameError:
unicode = str
# }}}
# {{{ Class for interacting with a hosted JIRA system
class Jira:

View File

@ -36,7 +36,8 @@ except:
product_map = {
'OS' : 'Corda',
'ENT' : 'Corda Enterprise',
'NS' : 'Corda Network Services',
'NS' : 'ENM',
'ENM' : 'ENM',
'TEST' : 'Corda', # for demo and test purposes
}
# }}}
@ -235,10 +236,15 @@ def create_release_candidate(args):
print()
has_tests = False
for issue in jira.search(QUERY_LIST_TEST_INSTANCES, args.PRODUCT, version):
print(u' - {} {}'.format(blue(issue.key), issue.fields.summary))
test_status = issue.fields.status['name']
print(u' - {} {} ({})'.format(blue(issue.key), issue.fields.summary, test_status))
epic_field = jira.custom_fields_by_name['Epic Link']
epic = issue.fields[epic_field] if epic_field in issue.fields.to_dict() else ''
labels = issue.fields.labels + [epic]
if test_status in ['Pass', 'Fail', 'Descope']:
print(u' {} - Parent test is marked as {}'.format(yellow('SKIPPED'), test_status))
print()
continue
print()
has_tests = True
print(u' - Creating test run ticket for release candidate {} ...'.format(yellow('RC{:02d}'.format(CANDIDATE))))
@ -278,7 +284,7 @@ def main():
def mixin_version_and_product(command):
mixin_product(command)
command.add('VERSION', help='the target version of the release, e.g., 4.0', type=float)
command.add('VERSION', help='the target version of the release, e.g., 4.0', type=str)
def mixin_candidate(command, optional=False):
if optional:

View File

@ -5,7 +5,7 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.CheckpointSerializer
val serializationContextKey = SerializeAsTokenContext::class.java
@ -70,8 +70,8 @@ class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: Ser
*/
@DeleteForDJVM
class CheckpointSerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: SerializeAsTokenContext.() -> Unit) : SerializeAsTokenContext {
constructor(toBeTokenized: Any, serializationFactory: CheckpointSerializationFactory, context: CheckpointSerializationContext, serviceHub: ServiceHub) : this(serviceHub, {
serializationFactory.serialize(toBeTokenized, context.withTokenContext(this))
constructor(toBeTokenized: Any, serializer: CheckpointSerializer, context: CheckpointSerializationContext, serviceHub: ServiceHub) : this(serviceHub, {
serializer.serialize(toBeTokenized, context.withTokenContext(this))
})
private val classNameToSingleton = mutableMapOf<String, SerializeAsToken>()

View File

@ -117,7 +117,7 @@ class DeserializationInput constructor(
des {
val envelope = getEnvelope(bytes, context.encodingWhitelist)
logger.trace("deserialize blob scheme=\"${envelope.schema.toString()}\"")
logger.trace("deserialize blob scheme=\"${envelope.schema}\"")
clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema),
clazz, context))
@ -149,7 +149,7 @@ class DeserializationInput constructor(
if (obj is DescribedType && ReferencedObject.DESCRIPTOR == obj.descriptor) {
// It must be a reference to an instance that has already been read, cheaply and quickly returning it by reference.
val objectIndex = (obj.described as UnsignedInteger).toInt()
if (objectIndex !in 0..objectHistory.size)
if (objectIndex >= objectHistory.size)
throw AMQPNotSerializableException(
type,
"Retrieval of existing reference failed. Requested index $objectIndex " +

View File

@ -125,7 +125,7 @@ abstract class EvolutionSerializer(
// any particular NonNullable annotation type to indicate cross
// compiler nullability
val isKotlin = (new.type.javaClass.declaredAnnotations.any {
it.annotationClass.qualifiedName == "kotlin.Metadata"
it.annotationClass.qualifiedName == "kotlin.Metadata"
})
constructor.parameters.withIndex().forEach {
@ -270,8 +270,8 @@ class EvolutionSerializerViaSetters(
* be an object that returns an [EvolutionSerializer]. Of course, any implementation that
* extends this class can be written to invoke whatever behaviour is desired.
*/
abstract class EvolutionSerializerGetterBase {
abstract fun getEvolutionSerializer(
interface EvolutionSerializerProvider {
fun getEvolutionSerializer(
factory: SerializerFactory,
typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>,
@ -283,12 +283,12 @@ abstract class EvolutionSerializerGetterBase {
* between the received schema and the class as it exists now on the class path,
*/
@KeepForDJVM
class EvolutionSerializerGetter : EvolutionSerializerGetterBase() {
object DefaultEvolutionSerializerProvider : EvolutionSerializerProvider {
override fun getEvolutionSerializer(factory: SerializerFactory,
typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> {
return factory.serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) {
return factory.registerByDescriptor(typeNotation.descriptor.name!!) {
when (typeNotation) {
is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, factory)
is RestrictedType -> {
@ -310,5 +310,4 @@ class EvolutionSerializerGetter : EvolutionSerializerGetterBase() {
}
}
}
}
}

View File

@ -2,6 +2,7 @@ package net.corda.serialization.internal.amqp
import net.corda.core.internal.isConcreteClass
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.serialize
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.trace
import net.corda.serialization.internal.amqp.SerializerFactory.Companion.nameForType
@ -61,7 +62,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
context: SerializationContext,
debugIndent: Int) = ifThrowsAppend({ clazz.typeName }
) {
if (propertySerializers.size != javaConstructor?.parameterCount &&
if (propertySerializers.deserializableSize != javaConstructor?.parameterCount &&
javaConstructor?.parameterCount ?: 0 > 0
) {
throw AMQPNotSerializableException(type, "Serialization constructor for class $type expects "
@ -86,8 +87,9 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
input: DeserializationInput,
context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) {
if (obj is List<*>) {
if (obj.size > propertySerializers.size) {
throw AMQPNotSerializableException(type, "Too many properties in described type $typeName")
if (obj.size != propertySerializers.size) {
throw AMQPNotSerializableException(type, "${obj.size} objects to deserialize, but " +
"${propertySerializers.size} properties in described type $typeName")
}
return if (propertySerializers.byConstructor) {
@ -109,8 +111,19 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
return construct(propertySerializers.serializationOrder
.zip(obj)
.map { Pair(it.first.initialPosition, it.first.serializer.readProperty(it.second, schemas, input, context)) }
.sortedWith(compareBy({ it.first }))
.mapNotNull { (accessor, obj) ->
// Ensure values get read out of input no matter what
val value = accessor.serializer.readProperty(obj, schemas, input, context)
when(accessor) {
is PropertyAccessorConstructor -> accessor.initialPosition to value
is CalculatedPropertyAccessor -> null
else -> throw UnsupportedOperationException(
"${accessor::class.simpleName} accessor not supported " +
"for constructor-based object building")
}
}
.sortedWith(compareBy { it.first })
.map { it.second })
}

View File

@ -3,6 +3,7 @@ package net.corda.serialization.internal.amqp
import com.google.common.reflect.TypeToken
import net.corda.core.KeepForDJVM
import net.corda.core.internal.isPublic
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.serialization.internal.amqp.MethodClassifier.*
import java.lang.reflect.Field
import java.lang.reflect.Method
@ -84,7 +85,7 @@ private val propertyMethodRegex = Regex("(?<type>get|set|is)(?<var>\\p{Lu}.*)")
* take a single parameter of a type compatible with exampleProperty and isExampleProperty must
* return a boolean
*/
fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
internal fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
val fieldProperties = superclassChain().declaredFields().byFieldName()
return superclassChain().declaredMethods()
@ -96,9 +97,23 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
.validated()
}
/**
* Obtain [PropertyDescriptor]s for those calculated properties of a class which are annotated with
* [SerializableCalculatedProperty]
*/
internal fun Class<out Any?>.calculatedPropertyDescriptors(): Map<String, PropertyDescriptor> =
superclassChain().withInterfaces().declaredMethods()
.thatArePublic()
.thatAreCalculated()
.toCalculatedProperties()
// Generate the sequence of classes starting with this class and ascending through it superclasses.
private fun Class<*>.superclassChain() = generateSequence(this, Class<*>::getSuperclass)
private fun Sequence<Class<*>>.withInterfaces() = flatMap {
sequenceOf(it) + it.genericInterfaces.asSequence().map { it.asClass() }
}
// Obtain the fields declared by all classes in this sequence of classes.
private fun Sequence<Class<*>>.declaredFields() = flatMap { it.declaredFields.asSequence() }
@ -108,18 +123,45 @@ private fun Sequence<Class<*>>.declaredMethods() = flatMap { it.declaredMethods.
// Map a sequence of fields by field name.
private fun Sequence<Field>.byFieldName() = map { it.name to it }.toMap()
// Select only those methods that are public (and are not the "getClass" method)
// Select only those methods that are public (and are not the "getClass" method).
private fun Sequence<Method>.thatArePublic() = filter { it.isPublic && it.name != "getClass" }
// Select only those methods that are annotated with [SerializableCalculatedProperty].
private fun Sequence<Method>.thatAreCalculated() = filter {
it.isAnnotationPresent(SerializableCalculatedProperty::class.java)
}
// Convert a sequence of calculated property methods to a map of property descriptors by property name.
private fun Sequence<Method>.toCalculatedProperties(): Map<String, PropertyDescriptor> {
val methodsByName = mutableMapOf<String, Method>()
for (method in this) {
val propertyNamedMethod = getPropertyNamedMethod(method)
?: throw IllegalArgumentException("Calculated property method must have a name beginning with 'get' or 'is'")
require(propertyNamedMethod.hasValidSignature()) {
"Calculated property name must have no parameters, and a non-void return type"
}
val propertyName = propertyNamedMethod.fieldName.decapitalize()
methodsByName.compute(propertyName) { _, existingMethod ->
if (existingMethod == null) method
else leastGenericBy({ genericReturnType }, existingMethod, method)
}
}
return methodsByName.mapValues { (_, method) -> PropertyDescriptor(null, null, method) }
}
// Select only those methods that are isX/getX/setX methods
private fun Sequence<Method>.thatArePropertyMethods() = map { method ->
propertyMethodRegex.find(method.name)?.let { result ->
private fun Sequence<Method>.thatArePropertyMethods() = mapNotNull(::getPropertyNamedMethod)
private fun getPropertyNamedMethod(method: Method): PropertyNamedMethod? {
return propertyMethodRegex.find(method.name)?.let { result ->
PropertyNamedMethod(
result.groups[2]!!.value,
MethodClassifier.valueOf(result.groups[1]!!.value.toUpperCase()),
method)
}
}.filterNotNull()
}
// Pick only those methods whose signatures are valid, discarding the remainder without warning.
private fun Sequence<PropertyNamedMethod>.withValidSignature() = filter { it.hasValidSignature() }

View File

@ -1,6 +1,7 @@
package net.corda.serialization.internal.amqp
import net.corda.core.KeepForDJVM
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.core.utilities.loggerFor
import java.io.NotSerializableException
import java.lang.reflect.Field
@ -19,9 +20,9 @@ abstract class PropertyReader {
* Accessor for those properties of a class that have defined getter functions.
*/
@KeepForDJVM
class PublicPropertyReader(private val readMethod: Method?) : PropertyReader() {
class PublicPropertyReader(private val readMethod: Method) : PropertyReader() {
init {
readMethod?.isAccessible = true
readMethod.isAccessible = true
}
private fun Method.returnsNullable(): Boolean {
@ -47,10 +48,10 @@ class PublicPropertyReader(private val readMethod: Method?) : PropertyReader() {
}
override fun read(obj: Any?): Any? {
return readMethod!!.invoke(obj)
return readMethod.invoke(obj)
}
override fun isNullable(): Boolean = readMethod?.returnsNullable() ?: false
override fun isNullable(): Boolean = readMethod.returnsNullable()
}
/**
@ -112,7 +113,6 @@ class EvolutionPropertyReader : PropertyReader() {
* making the property accessible.
*/
abstract class PropertyAccessor(
val initialPosition: Int,
open val serializer: PropertySerializer) {
companion object : Comparator<PropertyAccessor> {
override fun compare(p0: PropertyAccessor?, p1: PropertyAccessor?): Int {
@ -120,13 +120,15 @@ abstract class PropertyAccessor(
}
}
open val isCalculated get() = false
/**
* Override to control how the property is set on the object.
*/
abstract fun set(instance: Any, obj: Any?)
override fun toString(): String {
return "${serializer.name}($initialPosition)"
return serializer.name
}
}
@ -135,9 +137,8 @@ abstract class PropertyAccessor(
* is serialized and deserialized via JavaBean getter and setter style methods.
*/
class PropertyAccessorGetterSetter(
initialPosition: Int,
getter: PropertySerializer,
private val setter: Method) : PropertyAccessor(initialPosition, getter) {
private val setter: Method) : PropertyAccessor(getter) {
init {
/**
* Play nicely with Java interop, public methods aren't marked as accessible
@ -159,16 +160,34 @@ class PropertyAccessorGetterSetter(
* of the object the property belongs to.
*/
class PropertyAccessorConstructor(
initialPosition: Int,
override val serializer: PropertySerializer) : PropertyAccessor(initialPosition, serializer) {
val initialPosition: Int,
override val serializer: PropertySerializer) : PropertyAccessor(serializer) {
/**
* Because the property should be being set on the obejct through the constructor any
* Because the property should be being set on the object through the constructor any
* calls to the explicit setter should be an error.
*/
override fun set(instance: Any, obj: Any?) {
NotSerializableException("Attempting to access a setter on an object being instantiated " +
"via its constructor.")
}
override fun toString(): String =
"${serializer.name}($initialPosition)"
}
/**
* Implementation of [PropertyAccessor] representing a calculated property of an object that is serialized
* so that it can be used by the class carpenter, but ignored on deserialisation as there is no setter or
* constructor parameter to receive its value.
*
* This will only be created for calculated properties that are accessible via no-argument methods annotated
* with [SerializableCalculatedProperty].
*/
class CalculatedPropertyAccessor(override val serializer: PropertySerializer): PropertyAccessor(serializer) {
override val isCalculated: Boolean
get() = true
override fun set(instance: Any, obj: Any?) = Unit // do nothing, as it's a calculated value
}
/**
@ -186,7 +205,7 @@ abstract class PropertySerializers(
val serializationOrder: List<PropertyAccessor>) {
companion object {
fun make(serializationOrder: List<PropertyAccessor>) =
when (serializationOrder.firstOrNull()) {
when (serializationOrder.find { !it.isCalculated }) {
is PropertyAccessorConstructor -> PropertySerializersConstructor(serializationOrder)
is PropertyAccessorGetterSetter -> PropertySerializersSetter(serializationOrder)
null -> PropertySerializersNoProperties()
@ -198,6 +217,7 @@ abstract class PropertySerializers(
val size get() = serializationOrder.size
abstract val byConstructor: Boolean
val deserializableSize = serializationOrder.count { !it.isCalculated }
}
class PropertySerializersNoProperties : PropertySerializers(emptyList()) {

View File

@ -3,10 +3,7 @@ package net.corda.serialization.internal.amqp
import com.google.common.primitives.Primitives
import com.google.common.reflect.TypeToken
import net.corda.core.internal.isConcreteClass
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.*
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.*
import java.lang.reflect.Field
@ -68,12 +65,33 @@ fun <T : Any> propertiesForSerialization(
kotlinConstructor: KFunction<T>?,
type: Type,
factory: SerializerFactory): PropertySerializers = PropertySerializers.make(
if (kotlinConstructor != null) {
propertiesForSerializationFromConstructor(kotlinConstructor, type, factory)
} else {
propertiesForSerializationFromAbstract(type.asClass(), type, factory)
}.sortedWith(PropertyAccessor)
)
getValueProperties(kotlinConstructor, type, factory)
.addCalculatedProperties(factory, type)
.sortedWith(PropertyAccessor))
fun <T : Any> getValueProperties(kotlinConstructor: KFunction<T>?, type: Type, factory: SerializerFactory)
: List<PropertyAccessor> =
if (kotlinConstructor != null) {
propertiesForSerializationFromConstructor(kotlinConstructor, type, factory)
} else {
propertiesForSerializationFromAbstract(type.asClass(), type, factory)
}
private fun List<PropertyAccessor>.addCalculatedProperties(factory: SerializerFactory, type: Type)
: List<PropertyAccessor> {
val nonCalculated = map { it.serializer.name }.toSet()
return this + type.asClass().calculatedPropertyDescriptors().mapNotNull { (name, descriptor) ->
if (name in nonCalculated) null else {
val calculatedPropertyMethod = descriptor.getter
?: throw IllegalStateException("Property $name is not a calculated property")
CalculatedPropertyAccessor(PropertySerializer.make(
name,
PublicPropertyReader(calculatedPropertyMethod),
calculatedPropertyMethod.genericReturnType,
factory))
}
}
}
/**
* From a constructor, determine which properties of a class are to be serialized.
@ -145,7 +163,7 @@ fun propertiesForSerializationFromSetters(
properties: Map<String, PropertyDescriptor>,
type: Type,
factory: SerializerFactory): List<PropertyAccessor> =
properties.asSequence().withIndex().map { (index, entry) ->
properties.asSequence().map { entry ->
val (name, property) = entry
val getter = property.getter
@ -154,7 +172,6 @@ fun propertiesForSerializationFromSetters(
if (getter == null || setter == null) return@map null
PropertyAccessorGetterSetter(
index,
PropertySerializer.make(
name,
PublicPropertyReader(getter),
@ -191,9 +208,9 @@ private fun propertiesForSerializationFromAbstract(
clazz: Class<*>,
type: Type,
factory: SerializerFactory): List<PropertyAccessor> =
clazz.propertyDescriptors().asSequence().withIndex().map { (index, entry) ->
clazz.propertyDescriptors().asSequence().withIndex().mapNotNull { (index, entry) ->
val (name, property) = entry
if (property.getter == null || property.field == null) return@map null
if (property.getter == null || property.field == null) return@mapNotNull null
val getter = property.getter
val returnType = resolveTypeVariables(getter.genericReturnType, type)
@ -201,7 +218,7 @@ private fun propertiesForSerializationFromAbstract(
PropertyAccessorConstructor(
index,
PropertySerializer.make(name, PublicPropertyReader(getter), returnType, factory))
}.filterNotNull().toList()
}.toList()
internal fun interfacesForSerialization(type: Type, serializerFactory: SerializerFactory): List<Type> =
exploreType(type, serializerFactory).toList()

View File

@ -7,10 +7,7 @@ import net.corda.core.StubOutForDJVM
import net.corda.core.internal.kotlinObjectInstance
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import net.corda.core.utilities.*
import net.corda.serialization.internal.carpenter.*
import org.apache.qpid.proton.amqp.*
import java.io.NotSerializableException
@ -30,7 +27,7 @@ data class CustomSerializersCacheKey(val clazz: Class<*>, val declaredType: Type
/**
* Factory of serializers designed to be shared across threads and invocations.
*
* @property evolutionSerializerGetter controls how evolution serializers are generated by the factory. The normal
* @property evolutionSerializerProvider controls how evolution serializers are generated by the factory. The normal
* use case is an [EvolutionSerializer] type is returned. However, in some scenarios, primarily testing, this
* can be altered to fit the requirements of the test.
* @property onlyCustomSerializers used for testing, when set will cause the factory to throw a
@ -52,7 +49,7 @@ data class CustomSerializersCacheKey(val clazz: Class<*>, val declaredType: Type
open class SerializerFactory(
val whitelist: ClassWhitelist,
val classCarpenter: ClassCarpenter,
private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
private val evolutionSerializerProvider: EvolutionSerializerProvider = DefaultEvolutionSerializerProvider,
val fingerPrinterConstructor: (SerializerFactory) -> FingerPrinter = ::SerializerFingerPrinter,
private val serializersByType: MutableMap<Type, AMQPSerializer<Any>>,
val serializersByDescriptor: MutableMap<Any, AMQPSerializer<Any>>,
@ -64,13 +61,13 @@ open class SerializerFactory(
@DeleteForDJVM
constructor(whitelist: ClassWhitelist,
classCarpenter: ClassCarpenter,
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
evolutionSerializerProvider: EvolutionSerializerProvider = DefaultEvolutionSerializerProvider,
fingerPrinterConstructor: (SerializerFactory) -> FingerPrinter = ::SerializerFingerPrinter,
onlyCustomSerializers: Boolean = false
) : this(
whitelist,
classCarpenter,
evolutionSerializerGetter,
evolutionSerializerProvider,
fingerPrinterConstructor,
ConcurrentHashMap(),
ConcurrentHashMap(),
@ -84,13 +81,13 @@ open class SerializerFactory(
constructor(whitelist: ClassWhitelist,
carpenterClassLoader: ClassLoader,
lenientCarpenter: Boolean = false,
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
evolutionSerializerProvider: EvolutionSerializerProvider = DefaultEvolutionSerializerProvider,
fingerPrinterConstructor: (SerializerFactory) -> FingerPrinter = ::SerializerFingerPrinter,
onlyCustomSerializers: Boolean = false
) : this(
whitelist,
ClassCarpenterImpl(whitelist, carpenterClassLoader, lenientCarpenter),
evolutionSerializerGetter,
evolutionSerializerProvider,
fingerPrinterConstructor,
onlyCustomSerializers)
@ -98,12 +95,6 @@ open class SerializerFactory(
val classloader: ClassLoader get() = classCarpenter.classloader
private fun getEvolutionSerializer(typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> {
return evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas)
}
/**
* Look up, and manufacture if necessary, a serializer for the given type.
*
@ -117,11 +108,11 @@ open class SerializerFactory(
val declaredClass = declaredType.asClass()
val actualType: Type = if (actualClass == null) declaredType
else inferTypeVariables(actualClass, declaredClass, declaredType) ?: declaredType
else inferTypeVariables(actualClass, declaredClass, declaredType) ?: declaredType
val serializer = when {
// Declared class may not be set to Collection, but actual class could be a collection.
// In this case use of CollectionSerializer is perfectly appropriate.
// Declared class may not be set to Collection, but actual class could be a collection.
// In this case use of CollectionSerializer is perfectly appropriate.
(Collection::class.java.isAssignableFrom(declaredClass) ||
(actualClass != null && Collection::class.java.isAssignableFrom(actualClass))) &&
!EnumSet::class.java.isAssignableFrom(actualClass ?: declaredClass) -> {
@ -130,8 +121,8 @@ open class SerializerFactory(
CollectionSerializer(declaredTypeAmended, this)
}
}
// Declared class may not be set to Map, but actual class could be a map.
// In this case use of MapSerializer is perfectly appropriate.
// Declared class may not be set to Map, but actual class could be a map.
// In this case use of MapSerializer is perfectly appropriate.
(Map::class.java.isAssignableFrom(declaredClass) ||
(actualClass != null && Map::class.java.isAssignableFrom(actualClass))) -> {
val declaredTypeAmended = MapSerializer.deriveParameterizedType(declaredType, declaredClass, actualClass)
@ -142,8 +133,8 @@ open class SerializerFactory(
Enum::class.java.isAssignableFrom(actualClass ?: declaredClass) -> {
logger.trace {
"class=[${actualClass?.simpleName} | $declaredClass] is an enumeration " +
"declaredType=${declaredType.typeName} " +
"isEnum=${declaredType::class.java.isEnum}"
"declaredType=${declaredType.typeName} " +
"isEnum=${declaredType::class.java.isEnum}"
}
serializersByType.computeIfAbsent(actualClass ?: declaredClass) {
@ -203,33 +194,86 @@ open class SerializerFactory(
* if not, use the [ClassCarpenter] to generate a class to use in its place.
*/
private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
val metaSchema = CarpenterMetaSchema.newInstance()
for (typeNotation in schemaAndDescriptor.schemas.schema.types) {
logger.trace { "descriptor=${schemaAndDescriptor.typeDescriptor}, typeNotation=${typeNotation.name}" }
val requiringCarpentry = schemaAndDescriptor.schemas.schema.types.mapNotNull { typeNotation ->
try {
val serialiser = processSchemaEntry(typeNotation)
// if we just successfully built a serializer for the type but the type fingerprint
// doesn't match that of the serialised object then we are dealing with different
// instance of the class, as such we need to build an EvolutionSerializer
if (serialiser.typeDescriptor != typeNotation.descriptor.name) {
logger.trace { "typeNotation=${typeNotation.name} action=\"requires Evolution\"" }
getEvolutionSerializer(typeNotation, serialiser, schemaAndDescriptor.schemas)
}
getOrRegisterSerializer(schemaAndDescriptor, typeNotation)
return@mapNotNull null
} catch (e: ClassNotFoundException) {
if (sentinel) {
logger.error("typeNotation=${typeNotation.name} error=\"after Carpentry attempt failed to load\"")
throw e
}
else {
logger.trace { "typeNotation=\"${typeNotation.name}\" action=\"carpentry required\"" }
}
metaSchema.buildFor(typeNotation, classloader)
logger.trace { "typeNotation=\"${typeNotation.name}\" action=\"carpentry required\"" }
return@mapNotNull typeNotation
}
}.toList()
if (requiringCarpentry.isEmpty()) return
runCarpentry(schemaAndDescriptor, CarpenterMetaSchema.buildWith(classloader, requiringCarpentry))
}
private fun getOrRegisterSerializer(schemaAndDescriptor: FactorySchemaAndDescriptor, typeNotation: TypeNotation) {
logger.trace { "descriptor=${schemaAndDescriptor.typeDescriptor}, typeNotation=${typeNotation.name}" }
val serialiser = processSchemaEntry(typeNotation)
// if we just successfully built a serializer for the type but the type fingerprint
// doesn't match that of the serialised object then we may be dealing with different
// instance of the class, and such we need to build an EvolutionSerializer
if (serialiser.typeDescriptor == typeNotation.descriptor.name) return
logger.trace { "typeNotation=${typeNotation.name} action=\"requires Evolution\"" }
evolutionSerializerProvider.getEvolutionSerializer(this, typeNotation, serialiser, schemaAndDescriptor.schemas)
}
private fun processSchemaEntry(typeNotation: TypeNotation) = when (typeNotation) {
// java.lang.Class (whether a class or interface)
is CompositeType -> {
logger.trace("typeNotation=${typeNotation.name} amqpType=CompositeType")
processCompositeType(typeNotation)
}
// Collection / Map, possibly with generics
is RestrictedType -> {
logger.trace("typeNotation=${typeNotation.name} amqpType=RestrictedType")
processRestrictedType(typeNotation)
}
}
// TODO: class loader logic, and compare the schema.
private fun processRestrictedType(typeNotation: RestrictedType) =
get(null, typeForName(typeNotation.name, classloader))
private fun processCompositeType(typeNotation: CompositeType): AMQPSerializer<Any> {
// TODO: class loader logic, and compare the schema.
val type = typeForName(typeNotation.name, classloader)
return get(type.asClass(), type)
}
private fun typeForName(name: String, classloader: ClassLoader): Type = when {
name.endsWith("[]") -> {
val elementType = typeForName(name.substring(0, name.lastIndex - 1), classloader)
if (elementType is ParameterizedType || elementType is GenericArrayType) {
DeserializedGenericArrayType(elementType)
} else if (elementType is Class<*>) {
java.lang.reflect.Array.newInstance(elementType, 0).javaClass
} else {
throw AMQPNoTypeNotSerializableException("Not able to deserialize array type: $name")
}
}
if (metaSchema.isNotEmpty()) {
runCarpentry(schemaAndDescriptor, metaSchema)
}
name.endsWith("[p]") -> // There is no need to handle the ByteArray case as that type is coercible automatically
// to the binary type and is thus handled by the main serializer and doesn't need a
// special case for a primitive array of bytes
when (name) {
"int[p]" -> IntArray::class.java
"char[p]" -> CharArray::class.java
"boolean[p]" -> BooleanArray::class.java
"float[p]" -> FloatArray::class.java
"double[p]" -> DoubleArray::class.java
"short[p]" -> ShortArray::class.java
"long[p]" -> LongArray::class.java
else -> throw AMQPNoTypeNotSerializableException("Not able to deserialize array type: $name")
}
else -> DeserializedParameterizedType.make(name, classloader)
}
@StubOutForDJVM
@ -251,31 +295,6 @@ open class SerializerFactory(
processSchema(schemaAndDescriptor, true)
}
private fun processSchemaEntry(typeNotation: TypeNotation) = when (typeNotation) {
// java.lang.Class (whether a class or interface)
is CompositeType -> {
logger.trace("typeNotation=${typeNotation.name} amqpType=CompositeType")
processCompositeType(typeNotation)
}
// Collection / Map, possibly with generics
is RestrictedType -> {
logger.trace("typeNotation=${typeNotation.name} amqpType=RestrictedType")
processRestrictedType(typeNotation)
}
}
// TODO: class loader logic, and compare the schema.
private fun processRestrictedType(typeNotation: RestrictedType) = get(null,
typeForName(typeNotation.name, classloader))
private fun processCompositeType(typeNotation: CompositeType): AMQPSerializer<Any> {
// TODO: class loader logic, and compare the schema.
val type = typeForName(typeNotation.name, classloader)
return get(
type.asClass(),
type)
}
private fun makeClassSerializer(
clazz: Class<*>,
type: Type,
@ -352,6 +371,9 @@ open class SerializerFactory(
return MapSerializer(declaredType, this)
}
fun registerByDescriptor(name: Symbol, serializerCreator: () -> AMQPSerializer<Any>): AMQPSerializer<Any> =
serializersByDescriptor.computeIfAbsent(name) { _ -> serializerCreator() }
companion object {
private val logger = contextLogger()
@ -405,35 +427,6 @@ open class SerializerFactory(
is TypeVariable<*> -> "?"
else -> throw AMQPNotSerializableException(type, "Unable to render type $type to a string.")
}
private fun typeForName(name: String, classloader: ClassLoader): Type {
return if (name.endsWith("[]")) {
val elementType = typeForName(name.substring(0, name.lastIndex - 1), classloader)
if (elementType is ParameterizedType || elementType is GenericArrayType) {
DeserializedGenericArrayType(elementType)
} else if (elementType is Class<*>) {
java.lang.reflect.Array.newInstance(elementType, 0).javaClass
} else {
throw AMQPNoTypeNotSerializableException("Not able to deserialize array type: $name")
}
} else if (name.endsWith("[p]")) {
// There is no need to handle the ByteArray case as that type is coercible automatically
// to the binary type and is thus handled by the main serializer and doesn't need a
// special case for a primitive array of bytes
when (name) {
"int[p]" -> IntArray::class.java
"char[p]" -> CharArray::class.java
"boolean[p]" -> BooleanArray::class.java
"float[p]" -> FloatArray::class.java
"double[p]" -> DoubleArray::class.java
"short[p]" -> ShortArray::class.java
"long[p]" -> LongArray::class.java
else -> throw AMQPNoTypeNotSerializableException("Not able to deserialize array type: $name")
}
} else {
DeserializedParameterizedType.make(name, classloader)
}
}
}
object AnyType : WildcardType {
@ -443,4 +436,4 @@ open class SerializerFactory(
override fun toString(): String = "?"
}
}
}

View File

@ -32,6 +32,11 @@ data class CarpenterMetaSchema(
val dependencies: MutableMap<String, Pair<TypeNotation, MutableList<String>>>,
val dependsOn: MutableMap<String, MutableList<String>>) {
companion object CarpenterSchemaConstructor {
fun buildWith(classLoader: ClassLoader, types: List<TypeNotation>) =
newInstance().apply {
types.forEach { buildFor(it, classLoader) }
}
fun newInstance(): CarpenterMetaSchema {
return CarpenterMetaSchema(mutableListOf(), mutableMapOf(), mutableMapOf())
}

View File

@ -2,14 +2,14 @@ package net.corda.serialization.internal;
import net.corda.core.serialization.*;
import net.corda.core.serialization.internal.CheckpointSerializationContext;
import net.corda.core.serialization.internal.CheckpointSerializationFactory;
import net.corda.core.serialization.internal.CheckpointSerializer;
import net.corda.node.serialization.kryo.CordaClosureSerializer;
import net.corda.testing.core.SerializationEnvironmentRule;
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.io.NotSerializableException;
import java.io.Serializable;
import java.util.Collections;
import java.util.concurrent.Callable;
@ -23,12 +23,11 @@ public final class LambdaCheckpointSerializationTest {
public final CheckpointSerializationEnvironmentRule testCheckpointSerialization =
new CheckpointSerializationEnvironmentRule();
private CheckpointSerializationFactory factory;
private CheckpointSerializationContext context;
private CheckpointSerializer serializer;
@Before
public void setup() {
factory = testCheckpointSerialization.getCheckpointSerializationFactory();
context = new CheckpointSerializationContextImpl(
getClass().getClassLoader(),
AllWhitelist.INSTANCE,
@ -36,6 +35,8 @@ public final class LambdaCheckpointSerializationTest {
true,
null
);
serializer = testCheckpointSerialization.getCheckpointSerializer();
}
@Test
@ -63,11 +64,11 @@ public final class LambdaCheckpointSerializationTest {
assertThat(throwable).hasMessage(CordaClosureSerializer.ERROR_MESSAGE);
}
private <T> SerializedBytes<T> serialize(final T target) {
return factory.serialize(target, context);
private <T> SerializedBytes<T> serialize(final T target) throws NotSerializableException {
return serializer.serialize(target, context);
}
private <T> T deserialize(final SerializedBytes<? extends T> bytes, final Class<T> type) {
return factory.deserialize(bytes, type, context);
private <T> T deserialize(final SerializedBytes<? extends T> bytes, final Class<T> type) throws NotSerializableException {
return serializer.deserialize(bytes, type, context);
}
}

View File

@ -0,0 +1,107 @@
package net.corda.serialization.internal.carpenter;
import net.corda.core.serialization.SerializableCalculatedProperty;
import net.corda.core.serialization.SerializationContext;
import net.corda.core.serialization.SerializationFactory;
import net.corda.core.serialization.SerializedBytes;
import net.corda.serialization.internal.AllWhitelist;
import net.corda.serialization.internal.amqp.*;
import net.corda.serialization.internal.amqp.Schema;
import net.corda.testing.core.SerializationEnvironmentRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static java.util.Collections.singletonList;
import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactoryNoEvolution;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class JavaCalculatedValuesToClassCarpenterTest extends AmqpCarpenterBase {
public JavaCalculatedValuesToClassCarpenterTest() {
super(AllWhitelist.INSTANCE);
}
public interface Parent {
@SerializableCalculatedProperty
int getDoubled();
}
public static final class C implements Parent {
private final int i;
public C(int i) {
this.i = i;
}
@SerializableCalculatedProperty
public String getSquared() {
return Integer.toString(i * i);
}
@Override
public int getDoubled() {
return i * 2;
}
public int getI() {
return i;
}
}
@Rule
public final SerializationEnvironmentRule serializationEnvironmentRule = new SerializationEnvironmentRule();
private SerializationContext context;
@Before
public void initSerialization() {
SerializationFactory factory = serializationEnvironmentRule.getSerializationFactory();
context = factory.getDefaultContext();
}
@Test
public void calculatedValues() throws Exception {
SerializerFactory factory = testDefaultFactoryNoEvolution();
SerializedBytes<C> serialized = serialise(new C(2));
ObjectAndEnvelope<C> objAndEnv = new DeserializationInput(factory)
.deserializeAndReturnEnvelope(serialized, C.class, context);
C amqpObj = objAndEnv.getObj();
Schema schema = objAndEnv.getEnvelope().getSchema();
assertEquals(2, amqpObj.getI());
assertEquals("4", amqpObj.getSquared());
assertEquals(2, schema.getTypes().size());
assertTrue(schema.getTypes().get(0) instanceof CompositeType);
CompositeType concrete = (CompositeType) schema.getTypes().get(0);
assertEquals(3, concrete.getFields().size());
assertEquals("doubled", concrete.getFields().get(0).getName());
assertEquals("int", concrete.getFields().get(0).getType());
assertEquals("i", concrete.getFields().get(1).getName());
assertEquals("int", concrete.getFields().get(1).getType());
assertEquals("squared", concrete.getFields().get(2).getName());
assertEquals("string", concrete.getFields().get(2).getType());
assertEquals(0, AMQPSchemaExtensions.carpenterSchema(schema, ClassLoader.getSystemClassLoader()).getSize());
Schema mangledSchema = ClassCarpenterTestUtilsKt.mangleNames(schema, singletonList(C.class.getTypeName()));
CarpenterMetaSchema l2 = AMQPSchemaExtensions.carpenterSchema(mangledSchema, ClassLoader.getSystemClassLoader());
String mangledClassName = ClassCarpenterTestUtilsKt.mangleName(C.class.getTypeName());
assertEquals(1, l2.getSize());
net.corda.serialization.internal.carpenter.Schema carpenterSchema = l2.getCarpenterSchemas().stream()
.filter(s -> s.getName().equals(mangledClassName))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No schema found for mangled class name " + mangledClassName));
Class<?> pinochio = new ClassCarpenterImpl(AllWhitelist.INSTANCE).build(carpenterSchema);
Object p = pinochio.getConstructors()[0].newInstance(4, 2, "4");
assertEquals(pinochio.getMethod("getI").invoke(p), amqpObj.getI());
assertEquals(pinochio.getMethod("getSquared").invoke(p), amqpObj.getSquared());
assertEquals(pinochio.getMethod("getDoubled").invoke(p), amqpObj.getDoubled());
Parent upcast = (Parent) p;
assertEquals(upcast.getDoubled(), amqpObj.getDoubled());
}
}

View File

@ -4,11 +4,9 @@ import net.corda.core.contracts.ContractAttachment
import net.corda.core.identity.CordaX500Name
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.checkpointDeserialize
import net.corda.core.serialization.internal.checkpointSerialize
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices
@ -27,24 +25,25 @@ class ContractAttachmentSerializerTest {
@JvmField
val testCheckpointSerialization = CheckpointSerializationEnvironmentRule()
private lateinit var factory: CheckpointSerializationFactory
private lateinit var context: CheckpointSerializationContext
private lateinit var contextWithToken: CheckpointSerializationContext
private val mockServices = MockServices(emptyList(), CordaX500Name("MegaCorp", "London", "GB"), rigorousMock())
@Before
fun setup() {
factory = testCheckpointSerialization.checkpointSerializationFactory
context = testCheckpointSerialization.checkpointSerializationContext
contextWithToken = context.withTokenContext(CheckpointSerializeAsTokenContextImpl(Any(), factory, context, mockServices))
contextWithToken = testCheckpointSerialization.checkpointSerializationContext.withTokenContext(
CheckpointSerializeAsTokenContextImpl(
Any(),
testCheckpointSerialization.checkpointSerializer,
testCheckpointSerialization.checkpointSerializationContext,
mockServices))
}
@Test
fun `write contract attachment and read it back`() {
val contractAttachment = ContractAttachment(GeneratedAttachment(EMPTY_BYTE_ARRAY), DummyContract.PROGRAM_ID)
// no token context so will serialize the whole attachment
val serialized = contractAttachment.checkpointSerialize(factory, context)
val deserialized = serialized.checkpointDeserialize(factory, context)
val serialized = contractAttachment.checkpointSerialize()
val deserialized = serialized.checkpointDeserialize()
assertEquals(contractAttachment.id, deserialized.attachment.id)
assertEquals(contractAttachment.contract, deserialized.contract)
@ -59,8 +58,8 @@ class ContractAttachmentSerializerTest {
mockServices.attachments.importAttachment(attachment.open(), "test", null)
val contractAttachment = ContractAttachment(attachment, DummyContract.PROGRAM_ID)
val serialized = contractAttachment.checkpointSerialize(factory, contextWithToken)
val deserialized = serialized.checkpointDeserialize(factory, contextWithToken)
val serialized = contractAttachment.checkpointSerialize(contextWithToken)
val deserialized = serialized.checkpointDeserialize(contextWithToken)
assertEquals(contractAttachment.id, deserialized.attachment.id)
assertEquals(contractAttachment.contract, deserialized.contract)
@ -76,7 +75,7 @@ class ContractAttachmentSerializerTest {
mockServices.attachments.importAttachment(attachment.open(), "test", null)
val contractAttachment = ContractAttachment(attachment, DummyContract.PROGRAM_ID)
val serialized = contractAttachment.checkpointSerialize(factory, contextWithToken)
val serialized = contractAttachment.checkpointSerialize(contextWithToken)
assertThat(serialized.size).isLessThan(largeAttachmentSize)
}
@ -88,8 +87,8 @@ class ContractAttachmentSerializerTest {
// don't importAttachment in mockService
val contractAttachment = ContractAttachment(attachment, DummyContract.PROGRAM_ID)
val serialized = contractAttachment.checkpointSerialize(factory, contextWithToken)
val deserialized = serialized.checkpointDeserialize(factory, contextWithToken)
val serialized = contractAttachment.checkpointSerialize(contextWithToken)
val deserialized = serialized.checkpointDeserialize(contextWithToken)
assertThatThrownBy { deserialized.attachment.open() }.isInstanceOf(MissingAttachmentsException::class.java)
}
@ -100,8 +99,8 @@ class ContractAttachmentSerializerTest {
// don't importAttachment in mockService
val contractAttachment = ContractAttachment(attachment, DummyContract.PROGRAM_ID)
val serialized = contractAttachment.checkpointSerialize(factory, contextWithToken)
serialized.checkpointDeserialize(factory, contextWithToken)
val serialized = contractAttachment.checkpointSerialize(contextWithToken)
serialized.checkpointDeserialize(contextWithToken)
// MissingAttachmentsException thrown if we try to open attachment
}

View File

@ -5,7 +5,6 @@ import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.io.Output
import net.corda.core.serialization.*
import net.corda.core.serialization.internal.CheckpointSerializationContext
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.checkpointDeserialize
import net.corda.core.serialization.internal.checkpointSerialize
import net.corda.core.utilities.OpaqueBytes
@ -14,7 +13,6 @@ import net.corda.node.serialization.kryo.CordaKryo
import net.corda.node.serialization.kryo.DefaultKryoCustomizer
import net.corda.node.serialization.kryo.kryoMagic
import net.corda.testing.internal.rigorousMock
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
@ -28,12 +26,10 @@ class SerializationTokenTest {
@JvmField
val testCheckpointSerialization = CheckpointSerializationEnvironmentRule()
private lateinit var factory: CheckpointSerializationFactory
private lateinit var context: CheckpointSerializationContext
@Before
fun setup() {
factory = testCheckpointSerialization.checkpointSerializationFactory
context = testCheckpointSerialization.checkpointSerializationContext.withWhitelisted(SingletonSerializationToken::class.java)
}
@ -49,16 +45,16 @@ class SerializationTokenTest {
override fun equals(other: Any?) = other is LargeTokenizable && other.bytes.size == this.bytes.size
}
private fun serializeAsTokenContext(toBeTokenized: Any) = CheckpointSerializeAsTokenContextImpl(toBeTokenized, factory, context, rigorousMock())
private fun serializeAsTokenContext(toBeTokenized: Any) = CheckpointSerializeAsTokenContextImpl(toBeTokenized, testCheckpointSerialization.checkpointSerializer, context, rigorousMock())
@Test
fun `write token and read tokenizable`() {
val tokenizableBefore = LargeTokenizable()
val context = serializeAsTokenContext(tokenizableBefore)
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.checkpointSerialize(factory, testContext)
val serializedBytes = tokenizableBefore.checkpointSerialize(testContext)
assertThat(serializedBytes.size).isLessThan(tokenizableBefore.numBytes)
val tokenizableAfter = serializedBytes.checkpointDeserialize(factory, testContext)
val tokenizableAfter = serializedBytes.checkpointDeserialize(testContext)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
@ -69,8 +65,8 @@ class SerializationTokenTest {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.checkpointSerialize(factory, testContext)
val tokenizableAfter = serializedBytes.checkpointDeserialize(factory, testContext)
val serializedBytes = tokenizableBefore.checkpointSerialize(testContext)
val tokenizableAfter = serializedBytes.checkpointDeserialize(testContext)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
@ -79,7 +75,7 @@ class SerializationTokenTest {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
val testContext = this.context.withTokenContext(context)
tokenizableBefore.checkpointSerialize(factory, testContext)
tokenizableBefore.checkpointSerialize(testContext)
}
@Test(expected = UnsupportedOperationException::class)
@ -87,14 +83,14 @@ class SerializationTokenTest {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.toToken(serializeAsTokenContext(emptyList<Any>())).checkpointSerialize(factory, testContext)
serializedBytes.checkpointDeserialize(factory, testContext)
val serializedBytes = tokenizableBefore.toToken(serializeAsTokenContext(emptyList<Any>())).checkpointSerialize(testContext)
serializedBytes.checkpointDeserialize(testContext)
}
@Test(expected = KryoException::class)
fun `no context set`() {
val tokenizableBefore = UnitSerializeAsToken()
tokenizableBefore.checkpointSerialize(factory, context)
tokenizableBefore.checkpointSerialize(context)
}
@Test(expected = KryoException::class)
@ -112,7 +108,7 @@ class SerializationTokenTest {
kryo.writeObject(it, emptyList<Any>())
}
val serializedBytes = SerializedBytes<Any>(stream.toByteArray())
serializedBytes.checkpointDeserialize(factory, testContext)
serializedBytes.checkpointDeserialize(testContext)
}
private class WrongTypeSerializeAsToken : SerializeAsToken {
@ -128,7 +124,7 @@ class SerializationTokenTest {
val tokenizableBefore = WrongTypeSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
val testContext = this.context.withTokenContext(context)
val serializedBytes = tokenizableBefore.checkpointSerialize(factory, testContext)
serializedBytes.checkpointDeserialize(factory, testContext)
val serializedBytes = tokenizableBefore.checkpointSerialize(testContext)
serializedBytes.checkpointDeserialize(testContext)
}
}

View File

@ -3,12 +3,12 @@ package net.corda.serialization.internal.amqp
import java.io.NotSerializableException
/**
* An implementation of [EvolutionSerializerGetterBase] that disables all evolution within a
* An implementation of [EvolutionSerializerProvider] that disables all evolution within a
* [SerializerFactory]. This is most useful in testing where it is known that evolution should not be
* occurring and where bugs may be hidden by transparent invocation of an [EvolutionSerializer]. This
* prevents that by simply throwing an exception whenever such a serializer is requested.
*/
class EvolutionSerializerGetterTesting : EvolutionSerializerGetterBase() {
object FailIfEvolutionAttempted : EvolutionSerializerProvider {
override fun getEvolutionSerializer(factory: SerializerFactory,
typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>,

View File

@ -42,7 +42,7 @@ class FingerPrinterTestingTests {
val factory = SerializerFactory(
AllWhitelist,
ClassLoader.getSystemClassLoader(),
evolutionSerializerGetter = EvolutionSerializerGetterTesting(),
evolutionSerializerProvider = FailIfEvolutionAttempted,
fingerPrinterConstructor = { _ -> FingerPrinterTesting() })
val blob = TestSerializationOutput(VERBOSE, factory).serializeAndReturnSchema(C(1, 2L))

View File

@ -1,6 +1,7 @@
package net.corda.serialization.internal.amqp
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.serialization.internal.amqp.testutils.deserialize
import net.corda.serialization.internal.amqp.testutils.serialize
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
@ -61,4 +62,62 @@ class RoundTripTests {
val newC = DeserializationInput(factory).deserialize(bytes)
newC.copy(l = (newC.l + "d"))
}
@Test
fun calculatedValues() {
data class C(val i: Int) {
@get:SerializableCalculatedProperty
val squared = i * i
}
val factory = testDefaultFactoryNoEvolution()
val bytes = SerializationOutput(factory).serialize(C(2))
val deserialized = DeserializationInput(factory).deserialize(bytes)
assertThat(deserialized.squared).isEqualTo(4)
}
@Test
fun calculatedFunction() {
class C {
var i: Int = 0
@SerializableCalculatedProperty
fun getSquared() = i * i
}
val instance = C().apply { i = 2 }
val factory = testDefaultFactoryNoEvolution()
val bytes = SerializationOutput(factory).serialize(instance)
val deserialized = DeserializationInput(factory).deserialize(bytes)
assertThat(deserialized.getSquared()).isEqualTo(4)
}
interface I {
@get:SerializableCalculatedProperty
val squared: Int
}
@Test
fun inheritedCalculatedFunction() {
class C: I {
var i: Int = 0
override val squared get() = i * i
}
val instance = C().apply { i = 2 }
val factory = testDefaultFactoryNoEvolution()
val bytes = SerializationOutput(factory).serialize(instance)
val deserialized = DeserializationInput(factory).deserialize(bytes) as I
assertThat(deserialized.squared).isEqualTo(4)
}
@Test
fun inheritedCalculatedFunctionIsNotCalculated() {
class C(override val squared: Int): I
val instance = C(2)
val factory = testDefaultFactoryNoEvolution()
val bytes = SerializationOutput(factory).serialize(instance)
val deserialized = DeserializationInput(factory).deserialize(bytes) as I
assertThat(deserialized.squared).isEqualTo(2)
}
}

View File

@ -210,7 +210,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
return SerializerFactory(
AllWhitelist,
ClassLoader.getSystemClassLoader(),
evolutionSerializerGetter = EvolutionSerializerGetterTesting()
evolutionSerializerProvider = FailIfEvolutionAttempted
)
}

View File

@ -23,7 +23,7 @@ fun testDefaultFactoryNoEvolution(): SerializerFactory {
return SerializerFactory(
AllWhitelist,
ClassLoader.getSystemClassLoader(),
evolutionSerializerGetter = EvolutionSerializerGetterTesting())
evolutionSerializerProvider = FailIfEvolutionAttempted)
}
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())

View File

@ -0,0 +1,101 @@
package net.corda.serialization.internal.carpenter
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.CompositeType
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
import org.junit.Test
import kotlin.test.assertEquals
class CalculatedValuesToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
interface Parent {
@get:SerializableCalculatedProperty
val doubled: Int
}
@Test
fun calculatedValues() {
data class C(val i: Int): Parent {
@get:SerializableCalculatedProperty
val squared = (i * i).toString()
override val doubled get() = i * 2
}
val factory = testDefaultFactoryNoEvolution()
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(C(2)))
val amqpObj = obj.obj
val serSchema = obj.envelope.schema
assertEquals(2, amqpObj.i)
assertEquals("4", amqpObj.squared)
assertEquals(2, serSchema.types.size)
require(serSchema.types[0] is CompositeType)
val concrete = serSchema.types[0] as CompositeType
assertEquals(3, concrete.fields.size)
assertEquals("doubled", concrete.fields[0].name)
assertEquals("int", concrete.fields[0].type)
assertEquals("i", concrete.fields[1].name)
assertEquals("int", concrete.fields[1].type)
assertEquals("squared", concrete.fields[2].name)
assertEquals("string", concrete.fields[2].type)
val l1 = serSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
assertEquals(0, l1.size)
val mangleSchema = serSchema.mangleNames(listOf((classTestName("C"))))
val l2 = mangleSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
val aName = mangleName(classTestName("C"))
assertEquals(1, l2.size)
val aSchema = l2.carpenterSchemas.find { it.name == aName }!!
val pinochio = ClassCarpenterImpl(whitelist = AllWhitelist).build(aSchema)
val p = pinochio.constructors[0].newInstance(4, 2, "4")
assertEquals(pinochio.getMethod("getI").invoke(p), amqpObj.i)
assertEquals(pinochio.getMethod("getSquared").invoke(p), amqpObj.squared)
assertEquals(pinochio.getMethod("getDoubled").invoke(p), amqpObj.doubled)
val upcast = p as Parent
assertEquals(upcast.doubled, amqpObj.doubled)
}
@Test
fun implementingClassDoesNotCalculateValue() {
class C(override val doubled: Int): Parent
val factory = testDefaultFactoryNoEvolution()
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(C(5)))
val amqpObj = obj.obj
val serSchema = obj.envelope.schema
assertEquals(2, serSchema.types.size)
require(serSchema.types[0] is CompositeType)
val concrete = serSchema.types[0] as CompositeType
assertEquals(1, concrete.fields.size)
assertEquals("doubled", concrete.fields[0].name)
assertEquals("int", concrete.fields[0].type)
val l1 = serSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
assertEquals(0, l1.size)
val mangleSchema = serSchema.mangleNames(listOf((classTestName("C"))))
val l2 = mangleSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
val aName = mangleName(classTestName("C"))
assertEquals(1, l2.size)
val aSchema = l2.carpenterSchemas.find { it.name == aName }!!
val pinochio = ClassCarpenterImpl(whitelist = AllWhitelist).build(aSchema)
val p = pinochio.constructors[0].newInstance(5)
assertEquals(pinochio.getMethod("getDoubled").invoke(p), amqpObj.doubled)
val upcast = p as Parent
assertEquals(upcast.doubled, amqpObj.doubled)
}
}

View File

@ -1,6 +1,7 @@
package net.corda.serialization.internal.carpenter
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializedBytes
import net.corda.serialization.internal.amqp.*
import net.corda.serialization.internal.amqp.Field
import net.corda.serialization.internal.amqp.Schema
@ -47,7 +48,7 @@ open class AmqpCarpenterBase(whitelist: ClassWhitelist) {
var cc = ClassCarpenterImpl(whitelist = whitelist)
var factory = SerializerFactoryExternalCarpenter(cc)
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
fun <T: Any> serialise(obj: T): SerializedBytes<T> = SerializationOutput(factory).serialize(obj)
@Suppress("NOTHING_TO_INLINE")
inline fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz"
}

View File

@ -30,9 +30,7 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
require(obj.obj is B)
val amqpObj = obj.obj as B
val amqpObj = obj.obj
assertEquals(testB, amqpObj.b)
assertEquals(testA, amqpObj.a.a)
@ -92,8 +90,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
val amqpSchema = obj.envelope.schema.mangleNames(listOf(classTestName("A")))
require(obj.obj is B)
amqpSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
}
@ -111,8 +107,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
require(obj.obj is B)
val amqpSchema = obj.envelope.schema.mangleNames(listOf(classTestName("B")))
val carpenterSchema = amqpSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
@ -138,9 +132,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val b = B(A(testA), testB)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
require(obj.obj is B)
val amqpSchema = obj.envelope.schema.mangleNames(listOf(classTestName("A"), classTestName("B")))
val carpenterSchema = amqpSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
@ -198,8 +189,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
require(obj.obj is C)
val amqpSchema = obj.envelope.schema.mangleNames(listOf(classTestName("A"), classTestName("B")))
amqpSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
@ -224,8 +213,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
require(obj.obj is C)
val amqpSchema = obj.envelope.schema.mangleNames(listOf(classTestName("A"), classTestName("B")))
amqpSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
@ -250,8 +237,6 @@ class CompositeMembers : AmqpCarpenterBase(AllWhitelist) {
val c = C(B(testA, testB), testC)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(c))
require(obj.obj is C)
val carpenterSchema = obj.envelope.schema.mangleNames(listOf(classTestName("A"), classTestName("B")))
TestMetaCarpenter(carpenterSchema.carpenterSchema(
ClassLoader.getSystemClassLoader()), ClassCarpenterImpl(whitelist = AllWhitelist))

View File

@ -44,7 +44,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
assertEquals(testJ, a.j)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
assertTrue(obj.obj is A)
val serSchema = obj.envelope.schema
assertEquals(2, serSchema.types.size)
val l1 = serSchema.carpenterSchema(ClassLoader.getSystemClassLoader())
@ -84,8 +83,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
assertTrue(obj.obj is A)
val serSchema = obj.envelope.schema
assertEquals(2, serSchema.types.size)
@ -128,8 +125,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val a = A(testI, testII)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
assertTrue(obj.obj is A)
val serSchema = obj.envelope.schema
assertEquals(3, serSchema.types.size)
@ -176,8 +171,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val a = A(testI, testIII)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
assertTrue(obj.obj is A)
val serSchema = obj.envelope.schema
assertEquals(3, serSchema.types.size)
@ -225,8 +218,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val b = B(a, testIIII)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
assertTrue(obj.obj is B)
val serSchema = obj.envelope.schema
// Expected classes are
@ -281,8 +272,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val b = B(a, testIIII)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b))
assertTrue(obj.obj is B)
val serSchema = obj.envelope.schema
// The classes we're expecting to find:
@ -305,8 +294,6 @@ class InheritanceSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhitelist) {
val a = A(testI)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
assertTrue(obj.obj is A)
val serSchema = obj.envelope.schema
// The classes we're expecting to find:

View File

@ -21,8 +21,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
val a = A(testA, testB)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(testA, amqpObj.a)
assertEquals(testB, amqpObj.b)
@ -65,8 +64,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
val a = A(testA, testB)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(testA, amqpObj.a)
assertEquals(testB, amqpObj.b)

View File

@ -17,9 +17,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val test = 10
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)
@ -53,9 +51,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)
@ -83,9 +79,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val test = 10L
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)
@ -118,9 +112,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val test = 10.toShort()
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)
@ -153,9 +145,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val test = 10.0
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)
@ -188,9 +178,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWh
val test = 10.0F
val a = A(test)
val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a))
require(obj.obj is A)
val amqpObj = obj.obj as A
val amqpObj = obj.obj
assertEquals(test, amqpObj.a)
assertEquals(1, obj.envelope.schema.types.size)

View File

@ -3,7 +3,7 @@ package net.corda.behave.service.proxy
import net.corda.behave.service.proxy.RPCProxyServer.Companion.initialiseSerialization
import net.corda.behave.service.proxy.RPCProxyServer.Companion.log
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
@ -71,7 +71,7 @@ class RPCProxyServer(hostAndPort: NetworkHostAndPort, val webService: RPCProxyWe
fun initialiseSerialization() {
try {
nodeSerializationEnv =
SerializationEnvironmentImpl(
SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPClientSerializationScheme(emptyList()))
},

View File

@ -3,9 +3,7 @@ package net.corda.testing.core
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doAnswer
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.DoNotImplement
import net.corda.core.internal.staticField
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.effectiveSerializationEnv
import net.corda.testing.common.internal.asContextEnv
@ -40,7 +38,7 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
/** Do not call, instead use [SerializationEnvironmentRule] as a [org.junit.Rule]. */
fun <T> run(taskLabel: String, task: (SerializationEnvironment) -> T): T {
return SerializationEnvironmentRule().apply { init(taskLabel) }.runTask(task)
return SerializationEnvironmentRule().apply { init() }.runTask(task)
}
}
@ -48,14 +46,14 @@ class SerializationEnvironmentRule(private val inheritable: Boolean = false) : T
val serializationFactory get() = env.serializationFactory
override fun apply(base: Statement, description: Description): Statement {
init(description.toString())
init()
return object : Statement() {
override fun evaluate() = runTask { base.evaluate() }
}
}
private fun init(envLabel: String) {
env = createTestSerializationEnv(envLabel)
private fun init() {
env = createTestSerializationEnv()
}
private fun <T> runTask(task: (SerializationEnvironment) -> T): T {

View File

@ -39,7 +39,7 @@ class CheckpointSerializationEnvironmentRule(private val inheritable: Boolean =
/** Do not call, instead use [SerializationEnvironmentRule] as a [org.junit.Rule]. */
fun <T> run(taskLabel: String, task: (SerializationEnvironment) -> T): T {
return CheckpointSerializationEnvironmentRule().apply { init(taskLabel) }.runTask(task)
return CheckpointSerializationEnvironmentRule().apply { init() }.runTask(task)
}
}
@ -47,14 +47,14 @@ class CheckpointSerializationEnvironmentRule(private val inheritable: Boolean =
private lateinit var env: SerializationEnvironment
override fun apply(base: Statement, description: Description): Statement {
init(description.toString())
init()
return object : Statement() {
override fun evaluate() = runTask { base.evaluate() }
}
}
private fun init(envLabel: String) {
env = createTestSerializationEnv(envLabel)
private fun init() {
env = createTestSerializationEnv()
}
private fun <T> runTask(task: (SerializationEnvironment) -> T): T {
@ -65,7 +65,6 @@ class CheckpointSerializationEnvironmentRule(private val inheritable: Boolean =
}
}
val checkpointSerializationFactory get() = env.checkpointSerializationFactory
val checkpointSerializationContext get() = env.checkpointContext
val checkpointSerializer get() = env.checkpointSerializer
}

View File

@ -4,11 +4,10 @@ import com.nhaarman.mockito_kotlin.doNothing
import com.nhaarman.mockito_kotlin.whenever
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.core.DoNotImplement
import net.corda.core.serialization.internal.CheckpointSerializationFactory
import net.corda.core.serialization.internal.*
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT
import net.corda.node.serialization.kryo.KryoSerializationScheme
import net.corda.node.serialization.kryo.KryoCheckpointSerializer
import net.corda.serialization.internal.*
import net.corda.testing.core.SerializationEnvironmentRule
import java.util.concurrent.ConcurrentHashMap
@ -30,22 +29,20 @@ fun <T> withoutTestSerialization(callable: () -> T): T { // TODO: Delete this, s
}
}
internal fun createTestSerializationEnv(label: String): SerializationEnvironmentImpl {
internal fun createTestSerializationEnv(): SerializationEnvironment {
val factory = SerializationFactoryImpl().apply {
registerScheme(AMQPClientSerializationScheme(emptyList()))
registerScheme(AMQPServerSerializationScheme(emptyList()))
}
return object : SerializationEnvironmentImpl(
return SerializationEnvironment.with(
factory,
AMQP_P2P_CONTEXT,
AMQP_RPC_SERVER_CONTEXT,
AMQP_RPC_CLIENT_CONTEXT,
AMQP_STORAGE_CONTEXT,
KRYO_CHECKPOINT_CONTEXT,
CheckpointSerializationFactory(KryoSerializationScheme)
) {
override fun toString() = "testSerializationEnv($label)"
}
KryoCheckpointSerializer
)
}
/**
@ -54,7 +51,7 @@ internal fun createTestSerializationEnv(label: String): SerializationEnvironment
*/
fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment {
return if (armed) {
object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv("<global>") {
object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv() {
override fun unset() {
_globalSerializationEnv.set(null)
inVMExecutors.remove(this)

View File

@ -8,11 +8,10 @@ import net.corda.cliutils.CordaCliWrapper
import net.corda.cliutils.ExitCodes
import net.corda.cliutils.start
import net.corda.core.internal.isRegularFile
import net.corda.core.internal.rootMessage
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.utilities.base64ToByteArray
import net.corda.core.utilities.hexToByteArray
@ -22,7 +21,6 @@ import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.amqpMagic
import org.slf4j.event.Level
import picocli.CommandLine
import picocli.CommandLine.*
import java.io.PrintStream
import java.net.MalformedURLException
@ -36,8 +34,8 @@ fun main(args: Array<String>) {
}
class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised binary blobs to text") {
@Parameters(index = "*..0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class])
var source: MutableList<URL> = mutableListOf()
@Parameters(index = "0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class])
var source: URL? = null
@Option(names = ["--format"], paramLabel = "type", description = ["Output format. Possible values: [YAML, JSON]"])
private var formatType: OutputFormatType = OutputFormatType.YAML
@ -63,8 +61,7 @@ class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised
}
fun run(out: PrintStream): Int {
require(source.count() == 1) { "You must specify URL or file path to the blob" }
val inputBytes = source.first().readBytes()
val inputBytes = source!!.readBytes()
val bytes = parseToBinaryRelaxed(inputFormatType, inputBytes)
?: throw IllegalArgumentException("Error: this input does not appear to be encoded in Corda's AMQP extended format, sorry.")
@ -128,7 +125,7 @@ class BlobInspector : CordaCliWrapper("blob-inspector", "Convert AMQP serialised
private fun initialiseSerialization() {
// Deserialise with the lenient carpenter as we only care for the AMQP field getters
_contextSerializationEnv.set(SerializationEnvironmentImpl(
_contextSerializationEnv.set(SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPInspectorSerializationScheme)
},

View File

@ -53,7 +53,7 @@ class BlobInspectorTest {
}
private fun run(resourceName: String): String {
blobInspector.source = mutableListOf(javaClass.getResource(resourceName))
blobInspector.source = javaClass.getResource(resourceName)
val writer = StringWriter()
blobInspector.run(PrintStream(WriterOutputStream(writer, UTF_8)))
val output = writer.toString()

View File

@ -21,8 +21,6 @@ import java.util.concurrent.Callable
interface Validated {
companion object {
val logger = contextLogger()
const val RED = "\u001B[31m"
const val RESET = "\u001B[0m"
}
/**
@ -36,8 +34,8 @@ interface Validated {
fun validate() {
val errors = validator()
if (errors.isNotEmpty()) {
logger.error(RED + "Exceptions when parsing command line arguments:")
logger.error(errors.joinToString("\n") + RESET)
logger.error(ShellConstants.RED + "Exceptions when parsing command line arguments:")
logger.error(errors.joinToString("\n") + ShellConstants.RESET)
CommandLine(this).usage(System.err)
exitProcess(ExitCodes.FAILURE)
}
@ -55,6 +53,11 @@ object CordaSystemUtils {
fun getOsName(): String = System.getProperty(OS_NAME)
}
object ShellConstants {
const val RED = "\u001B[31m"
const val RESET = "\u001B[0m"
}
fun CordaCliWrapper.start(args: Array<String>) {
this.args = args
@ -66,27 +69,47 @@ fun CordaCliWrapper.start(args: Array<String>) {
cmd.registerConverter(Path::class.java) { Paths.get(it).toAbsolutePath().normalize() }
cmd.commandSpec.name(alias)
cmd.commandSpec.usageMessage().description(description)
cmd.commandSpec.parser().collectErrors(true)
try {
val defaultAnsiMode = if (CordaSystemUtils.isOsWindows()) { Help.Ansi.ON } else { Help.Ansi.AUTO }
val results = cmd.parseWithHandlers(RunLast().useOut(System.out).useAnsi(defaultAnsiMode),
DefaultExceptionHandler<List<Any>>().useErr(System.err).useAnsi(defaultAnsiMode),
*args)
// If an error code has been returned, use this and exit
results?.firstOrNull()?.let {
if (it is Int) {
exitProcess(it)
} else {
val defaultAnsiMode = if (CordaSystemUtils.isOsWindows()) {
Help.Ansi.ON
} else {
Help.Ansi.AUTO
}
val results = cmd.parse(*args)
val app = cmd.getCommand<CordaCliWrapper>()
if (cmd.isUsageHelpRequested) {
cmd.usage(System.out, defaultAnsiMode)
exitProcess(ExitCodes.SUCCESS)
}
if (cmd.isVersionHelpRequested) {
cmd.printVersionHelp(System.out, defaultAnsiMode)
exitProcess(ExitCodes.SUCCESS)
}
if (app.installShellExtensionsParser.installShellExtensions) {
System.out.println("Install shell extensions: ${app.installShellExtensionsParser.installShellExtensions}")
// ignore any parsing errors and run the program
exitProcess(app.call())
}
val allErrors = results.flatMap { it.parseResult?.errors() ?: emptyList() }
if (allErrors.any()) {
val parameterExceptions = allErrors.asSequence().filter { it is ParameterException }
if (parameterExceptions.any()) {
System.err.println("${ShellConstants.RED}${parameterExceptions.map{ it.message }.joinToString()}${ShellConstants.RESET}")
parameterExceptions.filter { it is UnmatchedArgumentException}.forEach { (it as UnmatchedArgumentException).printSuggestions(System.out) }
usage(cmd, System.out, defaultAnsiMode)
exitProcess(ExitCodes.FAILURE)
}
throw allErrors.first()
}
// If no results returned, picocli ran something without invoking the main program, e.g. --help or --version, so exit successfully
exitProcess(ExitCodes.SUCCESS)
} catch (e: ExecutionException) {
exitProcess(app.call())
} catch (e: Exception) {
val throwable = e.cause ?: e
if (this.verbose) {
throwable.printStackTrace()
} else {
System.err.println("*ERROR*: ${throwable.rootMessage ?: "Use --verbose for more details"}")
System.err.println("${ShellConstants.RED}${throwable.rootMessage ?: "Use --verbose for more details"}${ShellConstants.RESET}")
}
exitProcess(ExitCodes.FAILURE)
}
@ -126,7 +149,7 @@ abstract class CordaCliWrapper(val alias: String, val description: String) : Cal
var loggingLevel: Level = Level.INFO
@Mixin
private lateinit var installShellExtensionsParser: InstallShellExtensionsParser
lateinit var installShellExtensionsParser: InstallShellExtensionsParser
// This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before).
// Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used.

View File

@ -2,7 +2,7 @@ package net.corda.demobench
import javafx.scene.image.Image
import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.demobench.views.DemoBenchView
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
@ -56,7 +56,7 @@ class DemoBench : App(DemoBenchView::class) {
}
private fun initialiseSerialization() {
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPClientSerializationScheme(emptyList()))
},

View File

@ -1,9 +1,10 @@
package net.corda.bootstrapper.serialization
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT
import net.corda.node.serialization.kryo.KryoCheckpointSerializer
import net.corda.serialization.internal.AMQP_P2P_CONTEXT
import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT
import net.corda.serialization.internal.SerializationFactoryImpl
@ -14,14 +15,16 @@ class SerializationEngine {
synchronized(this) {
if (nodeSerializationEnv == null) {
val classloader = this::class.java.classLoader
nodeSerializationEnv = SerializationEnvironmentImpl(
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPServerSerializationScheme(emptyList()))
},
p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
rpcServerContext = AMQP_P2P_CONTEXT.withClassLoader(classloader),
storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader),
checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader)
checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader),
checkpointSerializer = KryoCheckpointSerializer
)
}
}