mirror of
https://github.com/corda/corda.git
synced 2025-03-15 08:41:04 +00:00
Merge pull request #1469 from corda/anthonyk-os-merge-20181009
O/S Merge 9/10/2018
This commit is contained in:
commit
4b9dd514c2
@ -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()))
|
||||
},
|
||||
|
@ -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" }
|
||||
|
@ -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()))
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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>
|
||||
|
||||
/**
|
||||
|
@ -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
|
@ -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)
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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) ->
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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 {
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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>()
|
||||
|
@ -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 " +
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 })
|
||||
}
|
||||
|
||||
|
@ -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() }
|
||||
|
@ -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()) {
|
||||
|
@ -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()
|
||||
|
@ -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 = "?"
|
||||
}
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
||||
return SerializerFactory(
|
||||
AllWhitelist,
|
||||
ClassLoader.getSystemClassLoader(),
|
||||
evolutionSerializerGetter = EvolutionSerializerGetterTesting()
|
||||
evolutionSerializerProvider = FailIfEvolutionAttempted
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ fun testDefaultFactoryNoEvolution(): SerializerFactory {
|
||||
return SerializerFactory(
|
||||
AllWhitelist,
|
||||
ClassLoader.getSystemClassLoader(),
|
||||
evolutionSerializerGetter = EvolutionSerializerGetterTesting())
|
||||
evolutionSerializerProvider = FailIfEvolutionAttempted)
|
||||
}
|
||||
|
||||
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()))
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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()))
|
||||
},
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user