Merge remote-tracking branch 'open/master' into kat-merge-20180508

This commit is contained in:
Katelyn Baker 2018-05-08 12:10:22 +01:00
commit 406cdf39bb
143 changed files with 2552 additions and 735 deletions

2
.idea/compiler.xml generated
View File

@ -14,6 +14,8 @@
<module name="behave_main" target="1.8" />
<module name="behave_scenario" target="1.8" />
<module name="behave_test" target="1.8" />
<module name="blobinspector_main" target="1.8" />
<module name="blobinspector_test" target="1.8" />
<module name="bootstrapper_main" target="1.8" />
<module name="bootstrapper_test" target="1.8" />
<module name="bridge_integrationTest" target="1.8" />

View File

@ -37,6 +37,7 @@ see changes to this list.
* Benoit Lafontaine (OCTO)
* Berit Bourgonje (ING)
* BitcoinErrorLog
* BMO
* Bob Crozier (AIA)
* Bogdan Paunescu (R3)
* C-Otto
@ -49,15 +50,16 @@ see changes to this list.
* Chris Akers (R3)
* Chris Burlinchon (R3)
* Chris Rankin (R3)
* Christian Kaufmann (Credit Suisse)
* Christian Kaufmann
* Christian Sailer (R3)
* Christopher Saunders (Credit Suisse)
* Christopher Saunders
* Christopher Swanson (US Bank)
* Clark Thompson (R3)
* Clay Ratliff (Thoughtworks)
* Clemens Wan (R3)
* Clinton Alexander (R3)
* cncorda
* Credit Suisse
* cyrsis
* Daniel Roig (SEB)
* Dave Hudson (R3)
@ -79,7 +81,7 @@ see changes to this list.
* Ian Cusden (UBS)
* Ian Grigg (R3)
* Igor Nitto (R3)
* Igor Panov (CIBC)
* Igor Panov
* Ivan Schasny (R3)
* James Brown (R3)
* James Carlyle (R3)
@ -94,7 +96,7 @@ see changes to this list.
* Jose Luu (Natixis)
* Josh Lindl (BCS)
* Justin Chapman (Northern Trust)
* Kai-Michael Schramm (Credit Suisse)
* Kai-Michael Schramm
* Karel Hajek (Barclays Capital)
* karnauskas
* Kasia Streich (R3)
@ -114,7 +116,7 @@ see changes to this list.
* Mark Raynes (Thomson Reuters)
* Mark Simpson (RBS)
* Mark Tiggas (Wells Fargo)
* Massimo Morini (Banca IMI)
* Massimo Morini
* Mat Rizzo (R3)
* Matt Britton (BCS)
* Matthew Nesbit (R3)
@ -130,7 +132,7 @@ see changes to this list.
* Nuam Athaweth (MUFG)
* Oscar Zibordi de Paiva (Bradesco)
* Patrick Kuo (R3)
* Pekka Kaipio (OP Financial)
* Pekka Kaipio
* Phillip Griffin
* Piotr Piskorski (Nordea)
* Przemyslaw Bak (R3)
@ -156,12 +158,11 @@ see changes to this list.
* Sajindra Jayasena (Deutsche Bank)
* Saket Sharma (BNY Mellon)
* Sam Chadwick (Thomson Reuters)
* Sasmit Sahu (Credit Suisse)
* Scott James (Credit Suisse)
* Sasmit Sahu
* Scott James
* Shams Asari (R3)
* Simon Taylor (Barclays)
* Sofus Mortensen (Digital Asset Holdings)
* Stephen Lane-Smith (BMO)
* stevenroose
* Szymon Sztuka (R3)
* tb-pq
@ -169,7 +170,7 @@ see changes to this list.
* Thomas O'Donnell (Macquarie)
* Thomas Schroeter (R3)
* Tim Swanson (R3)
* Timothy Smith (Credit Suisse)
* Timothy Smith
* Tom Menner (R3)
* tomconte
* Tommy Lillehagen (R3)

View File

@ -36,16 +36,17 @@ Corda is a decentralised database system in which nodes trust each other as litt
## Useful links
* [Project Website](https://corda.net)
* [Mailing Lists](https://www.corda.net/mailing-lists/)
* [Documentation](https://docs.corda.net)
* [Stack Overflow Tag](https://stackoverflow.com/questions/tagged/corda)
* [Slack Channel](https://slack.corda.net/)
* [Stack Overflow tag](https://stackoverflow.com/questions/tagged/corda)
* [Forum](https://discourse.corda.net)
* [Twitter](https://twitter.com/cordadlt)
* [Meetups](https://www.meetup.com/pro/corda/)
* [Training Courses](https://www.corda.net/corda-training/)
## Contributing
Please read [here](./CONTRIBUTING.md).
We welcome contributions to Corda! Please see our [CONTRIBUTING.md](./CONTRIBUTING.md).
## License

View File

@ -94,6 +94,7 @@ buildscript {
ext.curator_version = '4.0.0'
ext.jsch_version = '0.1.54'
ext.protonj_version = '0.27.1'
ext.commons_cli_version = '1.4'
// Update 121 is required for ObjectInputFilter and at time of writing 131 was latest:
ext.java8_minUpdateVersion = '131'

View File

@ -17,6 +17,8 @@ import net.corda.core.internal.uncheckedCast
import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable
import java.lang.reflect.Field
import kotlin.jvm.internal.CallableReference
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.jvm.javaGetter
@ -77,8 +79,21 @@ sealed class CriteriaExpression<O, out T> {
@CordaSerializable
class Column<O, out C>(val name: String, val declaringClass: Class<*>) {
@Deprecated("Does not support fields from a MappedSuperclass. Use the equivalent that accepts a FieldInfo.")
constructor(field: Field) : this(field.name, field.declaringClass)
constructor(property: KProperty1<O, C?>) : this(property.name, property.javaGetter!!.declaringClass)
constructor(field: FieldInfo) : this(field.name, field.entityClass)
constructor(property: KProperty1<O, C?>) : this(property.name, declaringClass(property))
private companion object {
fun <O, C> declaringClass(property: KProperty1<O, C?>): Class<*> {
return when (property) {
// This is to ensure that, for a JPA Entity, a field declared in a MappedSuperclass will not cause Hibernate to reject a query referencing it.
// TODO remove the cast and access the owner properly after it will be exposed as Kotlin's public API (https://youtrack.jetbrains.com/issue/KT-24170).
is CallableReference -> ((property as CallableReference).owner as KClass<*>).javaObjectType
else -> property.javaGetter!!.declaringClass
}
}
}
}
@CordaSerializable
@ -227,16 +242,23 @@ object Builder {
fun <R : Comparable<R>> compare(operator: BinaryComparisonOperator, value: R) = ColumnPredicate.BinaryComparison(operator, value)
fun <O, R> KProperty1<O, R?>.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column(this), predicate)
fun <R> Field.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column<Any, R>(this), predicate)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.predicate(predicate: ColumnPredicate<R>) = info().predicate(predicate)
fun <R> FieldInfo.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column<Any, R>(this), predicate)
fun <O, R> KProperty1<O, R?>.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<O, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column(this), predicate, groupByColumns, orderBy)
fun <R> Field.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<Any, R>>? = null, orderBy: Sort.Direction? = null)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<Any, R>>? = null, orderBy: Sort.Direction? = null) = info().functionPredicate(predicate, groupByColumns, orderBy)
fun <R> FieldInfo.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<Any, R>>? = null, orderBy: Sort.Direction? = null)
= CriteriaExpression.AggregateFunctionExpression(Column<Any, R>(this), predicate, groupByColumns, orderBy)
fun <O, R : Comparable<R>> KProperty1<O, R?>.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
fun <R : Comparable<R>> Field.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.comparePredicate(operator: BinaryComparisonOperator, value: R) = info().comparePredicate(operator, value)
fun <R : Comparable<R>> FieldInfo.comparePredicate(operator: BinaryComparisonOperator, value: R) = predicate(compare(operator, value))
fun <O, R> KProperty1<O, R?>.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
fun <O, R> KProperty1<O, R?>.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
@ -249,31 +271,57 @@ object Builder {
fun <O, R : Comparable<R>> KProperty1<O, R?>.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
@JvmStatic
fun <R> Field.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.equal(value: R) = info().equal(value)
@JvmStatic
fun <R> FieldInfo.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
@JvmStatic
fun <R> Field.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.notEqual(value: R) = info().notEqual(value)
@JvmStatic
fun <R> FieldInfo.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
@JvmStatic
fun <R : Comparable<R>> Field.lessThan(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN, value)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.lessThan(value: R) = info().lessThan(value)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.lessThan(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN, value)
@JvmStatic
fun <R : Comparable<R>> Field.lessThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.lessThanOrEqual(value: R) = info().lessThanOrEqual(value)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.lessThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
@JvmStatic
fun <R : Comparable<R>> Field.greaterThan(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN, value)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.greaterThan(value: R) = info().greaterThan(value)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.greaterThan(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN, value)
@JvmStatic
fun <R : Comparable<R>> Field.greaterThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.greaterThanOrEqual(value: R) = info().greaterThanOrEqual(value)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.greaterThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
@JvmStatic
fun <R : Comparable<R>> Field.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.between(from: R, to: R) = info().between(from, to)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
@JvmStatic
fun <R : Comparable<R>> Field.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.`in`(collection: Collection<R>) = info().`in`(collection)
fun <R : Comparable<R>> FieldInfo.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
@JvmStatic
fun <R : Comparable<R>> Field.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R : Comparable<R>> Field.notIn(collection: Collection<R>) = info().notIn(collection)
@JvmStatic
fun <R : Comparable<R>> FieldInfo.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
fun <R> equal(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value)
fun <R> notEqual(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value)
@ -292,19 +340,31 @@ object Builder {
fun <O> KProperty1<O, String?>.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
@JvmStatic
fun Field.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.like(string: String) = info().like(string)
@JvmStatic
fun FieldInfo.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
fun <O> KProperty1<O, String?>.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
@JvmStatic
fun Field.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.notLike(string: String) = info().notLike(string)
@JvmStatic
fun FieldInfo.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
fun <O, R> KProperty1<O, R?>.isNull() = predicate(ColumnPredicate.NullExpression(NullOperator.IS_NULL))
@JvmStatic
fun Field.isNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.IS_NULL))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.isNull() = info().isNull()
@JvmStatic
fun FieldInfo.isNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.IS_NULL))
fun <O, R> KProperty1<O, R?>.notNull() = predicate(ColumnPredicate.NullExpression(NullOperator.NOT_NULL))
@JvmStatic
fun Field.notNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.NOT_NULL))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.notNull() = info().notNull()
@JvmStatic
fun FieldInfo.notNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.NOT_NULL))
/** aggregate functions */
fun <O, R> KProperty1<O, R?>.sum(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
@ -312,19 +372,31 @@ object Builder {
@JvmStatic
@JvmOverloads
fun <R> Field.sum(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.sum(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) = info().sum<R>(groupByColumns?.map { it.info() }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> FieldInfo.sum(groupByColumns: List<FieldInfo>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.SUM), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.count() = functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.COUNT))
@JvmStatic
fun Field.count() = functionPredicate(ColumnPredicate.AggregateFunction<Any>(AggregateFunctionType.COUNT))
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun Field.count() = info().count()
@JvmStatic
fun FieldInfo.count() = functionPredicate(ColumnPredicate.AggregateFunction<Any>(AggregateFunctionType.COUNT))
fun <O, R> KProperty1<O, R?>.avg(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.AVG), groupByColumns?.map { Column(it) }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> Field.avg(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.avg(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) = info().avg<R>(groupByColumns?.map { it.info() }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> FieldInfo.avg(groupByColumns: List<FieldInfo>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.AVG), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.min(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
@ -332,7 +404,12 @@ object Builder {
@JvmStatic
@JvmOverloads
fun <R> Field.min(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.min(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) = info().min<R>(groupByColumns?.map { it.info() }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> FieldInfo.min(groupByColumns: List<FieldInfo>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MIN), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
fun <O, R> KProperty1<O, R?>.max(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
@ -340,8 +417,50 @@ object Builder {
@JvmStatic
@JvmOverloads
fun <R> Field.max(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
fun <R> Field.max(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) = info().max<R>(groupByColumns?.map { it.info() }, orderBy)
@JvmStatic
@JvmOverloads
fun <R> FieldInfo.max(groupByColumns: List<FieldInfo>? = null, orderBy: Sort.Direction? = null) =
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MAX), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
private fun Field.info(): FieldInfo = FieldInfo(name, declaringClass)
}
inline fun <A> builder(block: Builder.() -> A) = block(Builder)
/**
* Contains information about a field from an entity class.
* Used as part of query criteria construction through [Builder], produced by function [getField].
* The constructor should not be invoked manually.
*
* @param name field name
* @param entityClass JPA entity class for the query
*/
class FieldInfo internal constructor(val name: String, val entityClass: Class<*>)
/**
* Returns a [FieldInfo] for field with name [fieldName] in [entityClass].
*
* @param fieldName name of the field
* @param entityClass JPA entity class containing the field
* @throws NoSuchFieldException if no field with name [fieldName] is found in the class hierarchy of [entityClass]
*/
@Throws(NoSuchFieldException::class)
fun getField(fieldName: String, entityClass: Class<*>): FieldInfo {
return getField(fieldName, entityClass, entityClass)
}
@Throws(NoSuchFieldException::class)
private fun getField(fieldName: String, clazz: Class<*>?, invokingClazz: Class<*>): FieldInfo {
if (clazz == null) {
throw NoSuchFieldException(fieldName)
}
return try {
val field = clazz.getDeclaredField(fieldName)
return FieldInfo(field.name, invokingClazz)
} catch (e: NoSuchFieldException) {
getField(fieldName, clazz.superclass, invokingClazz)
}
}

View File

@ -12,6 +12,7 @@ package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
import net.corda.nodeapi.internal.serialization.AMQP_RPC_CLIENT_CONTEXT
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.DeserializationInput
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
@ -29,13 +30,17 @@ class TransactionVerificationExceptionSerialisationTests {
ClassLoader.getSystemClassLoader()
)
private val context get() = AMQP_RPC_CLIENT_CONTEXT
private val txid = SecureHash.allOnesHash
private val factory = defaultFactory()
@Test
fun contractConstraintRejectionTest() {
val excp = TransactionVerificationException.ContractConstraintRejection(txid, "This is only a test")
val excp2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(excp))
val excp2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(excp, context),
context)
assertEquals(excp.message, excp2.message)
assertEquals(excp.cause, excp2.cause)
@ -52,7 +57,9 @@ class TransactionVerificationExceptionSerialisationTests {
val cause = Throwable("wibble")
val exception = TransactionVerificationException.ContractRejection(txid, contract, cause)
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)
@ -62,7 +69,9 @@ class TransactionVerificationExceptionSerialisationTests {
@Test
fun missingAttachmentRejectionTest() {
val exception = TransactionVerificationException.MissingAttachmentRejection(txid, "Some contract class")
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)
@ -72,7 +81,9 @@ class TransactionVerificationExceptionSerialisationTests {
@Test
fun conflictingAttachmentsRejectionTest() {
val exception = TransactionVerificationException.ContractConstraintRejection(txid, "Some contract class")
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)
@ -83,7 +94,9 @@ class TransactionVerificationExceptionSerialisationTests {
fun contractCreationErrorTest() {
val cause = Throwable("wibble")
val exception = TransactionVerificationException.ContractCreationError(txid, "Some contract class", cause)
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)
@ -94,7 +107,9 @@ class TransactionVerificationExceptionSerialisationTests {
fun transactionMissingEncumbranceTest() {
val exception = TransactionVerificationException.TransactionMissingEncumbranceException(
txid, 12, TransactionVerificationException.Direction.INPUT)
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)
@ -109,7 +124,9 @@ class TransactionVerificationExceptionSerialisationTests {
val factory = defaultFactory()
factory.register(PublicKeySerializer)
val exception = TransactionVerificationException.NotaryChangeInWrongTransactionType(txid, dummyBankA, dummyNotary)
val exception2 = DeserializationInput(factory).deserialize(SerializationOutput(factory).serialize(exception))
val exception2 = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(exception, context),
context)
assertEquals(exception.message, exception2.message)
assertEquals(exception.cause?.message, exception2.cause?.message)

View File

@ -165,6 +165,40 @@ Nodes are created on the ``MockNetwork`` using:
}
}
Nodes added using ``createPartyNode`` are provided a default set of node parameters. However, it is also possible to
provide different parameters to each node using the following methods on ``MockNetwork``:
.. container:: codeset
.. sourcecode:: kotlin
/**
* Create a started node with the given parameters.
*
* @param legalName The node's legal name.
* @param forcedID A unique identifier for the node.
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
* @param extraCordappPackages Extra CorDapp packages to add for this node.
*/
@JvmOverloads
fun createNode(legalName: CordaX500Name? = null,
forcedID: Int? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {},
extraCordappPackages: List<String> = emptyList()
): StartedMockNode
/** Create a started node with the given parameters. **/
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode
As you can see above, parameters can be added individually or encapsulated within a ``MockNodeParameters`` object. Of
particular interest are ``configOverrides`` which allow you to override any default config option specified within the
``NodeConfiguration`` object. Also, the ``extraCordappPackages`` parameter allows you to add extra CorDapps to a
specific node. This is useful when you wish for all nodes to load a common CorDapp but for a subset of nodes to load
CorDapps specific to their role in the network.
Running the network
^^^^^^^^^^^^^^^^^^^

View File

@ -7,8 +7,13 @@ release, see :doc:`upgrade-notes`.
Unreleased
==========
* Refactor AMQP Serializer to pass context object down the serialization call hierarchy. Will allow per thread
extensions to be set and used by the RPC work (Observable Context Key)
* Refactor RPC Server Kryo observable serializer into it's own sub module
* The Vault Criteria API has been extended to take a more precise specification of which class contains a field. This primarily impacts Java users; Kotlin users need take no action. The old methods have been deprecated but still work - the new methods avoid bugs that can occur when JPA schemas inherit from each other.
* Refactor RPC Client Kryo observable serializer into it's own sub module
* Fix CORDA-1403 where a property of a class that implemented a generic interface could not be deserialized in

View File

@ -0,0 +1,102 @@
Supported cipher suites
=======================
.. contents::
The set of signature schemes supported forms a part of the consensus rules for a Corda DLT network.
Thus, it is important that implementations do not support pluggability of any crypto algorithms and do take measures
to prevent algorithms supported by any underlying cryptography library from becoming accidentally accessible.
Signing a transaction with an algorithm that is not a part of the base specification would result in a transaction
being considered invalid by peer nodes and thus a loss of consensus occurring. The introduction of new algorithms
over time will require a global upgrade of all nodes.
Corda has been designed to be cryptographically agile, in the sense that the available set of signature schemes is
carefully selected based on various factors, such as provided security-level and cryptographic strength, compatibility
with various HSM vendors, algorithm standardisation, variety of cryptographic primitives, business demand, option for
post-quantum resistance, side channel security, efficiency and rigorous testing.
Before we present the pool of supported schemes it is useful to be familiar with :doc:`key-concepts-identity`,
:doc:`permissioning` and :doc:`api-identity`. An important design decision in Corda is its shared hierarchy
between the TLS and Node Identity certificates.
Certificate hierarchy
---------------------
A Corda network has 8 types of keys and a regular node requires 4 of them:
* The **root network CA** key
* The **doorman CA** key
* The **network map** key
* The **service identity** key(s) (per service, such as a notary cluster; it can be a Composite Key)
-- **Node Keys** --
* The **node CA** key(s) (one per node)
* The **legal identity** key(s) (one per node)
* The **tls** key(s) (per node)
* The **confidential identity** key(s) (per node)
We can visualise the certificate structure as follows (for a detailed description of cert-hierarchy,
see :doc:`permissioning`):
.. image:: resources/certificate_structure.png
:scale: 55%
:align: center
Supported cipher suites
-----------------------
Due to the shared certificate hierarchy, the following 4 key/certificate types: **root network CA**, **doorman CA**,
**node CA** and **tls** should be compatible with the standard TLS 1.2 protocol. The latter is a requirement from the
TLS certificate-path validator. It is highlighted that the rest of the keys can be any of the 5 supported cipher suites.
For instance, **network map** is ECDSA NIST P-256 (secp256r1) in the Corda Network (CN) as it is well-supported by the
underlying HSM device, but the default for dev-mode is Pure EdDSA (ed25519).
The following table presents the 5 signature schemes currently supported by Corda. The TLS column shows which of them
are compatible with TLS 1.2, while the default scheme per key type is also shown.
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
| Cipher suite | Description | TLS | Default for |
+=========================+=============================================================|=====+=======================+
| Pure EdDSA using the | EdDSA represents the current state of the art in mainstream | NO | node identity |
| ed25519 curve | cryptography. It implements elliptic curve cryptography | | confidential identity |
| and SHA-512 | with deterministic signatures a fast implementation, | | network map (dev) |
| | explained constants, side channel resistance and many other | | |
| | desirable characteristics. However, it is relatively new | | |
| | and not widely supported, for example, you can't use it in | | |
| | TLS yet (a draft RFC exists but is not standardised yet). | | |
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
| ECDSA using the | This is the default choice for most systems that support | YES | root network CA |
| NIST P-256 curve | elliptic curve cryptography today and is recommended by | | doorman CA |
| (secp256r1) | NIST. It is also supported by the majority of the HSM | | node CA |
| and SHA-256 | vendors. | | tls |
| | | | network map (CN) |
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
| ECDSA using the | secp256k1 is the curve adopted by Bitcoin and as such there | YES | |
| Koblitz k1 curve | is a wealth of infrastructure, code and advanced algorithms | | |
| (secp256k1) | designed for use with it. This curve is standardised by | | |
| and SHA-256 | NIST as part of the "Suite B" cryptographic algorithms and | | |
| | as such is more widely supported than ed25519. By | | |
| | supporting it we gain access to the ecosystem of advanced | | |
| | cryptographic techniques and devices pioneered by the | | |
| | Bitcoin community. | | |
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
| RSA (3072bit) PKCS#1 | RSA is well supported by any sort of hardware or software | YES | |
| and SHA-256 | as a signature algorithm no matter how old, for example, | | |
| | legacy HSMs will support this along with obsolete operating | | |
| | systems. RSA is using bigger keys than ECDSA and thus it is | | |
| | recommended for inclusion only for its backwards | | |
| | compatibility properties, and only for usage where legacy | | |
| | constraints or government regulation forbids the usage of | | |
| | more modern approaches. | | |
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
| SPHINCS-256 | SPHINCS-256 is a post-quantum secure algorithm that relies | NO | |
| and SHA-512 | only on hash functions. It is included as a hedge against | | |
| | the possibility of a malicious adversary obtaining a | | |
| | quantum computer capable of running Shor's algorithm in | | |
| | future. SPHINCS is based ultimately on a clever usage of | | |
| | Merkle hash trees. Hash functions are a very heavily | | |
| | studied and well understood area of cryptography. Thus, it | | |
| | is assumed that there is a much lower chance of | | |
| | breakthrough attacks on the underlying mathematical | | |
| | problems. However, SPHINCS uses relatively big public keys, | | |
| | it is slower and outputs bigger signatures than EdDSA, | | |
| | ECDSA and RSA algorithms. | | |
+-------------------------+-------------------------------------------------------------+-----+-----------------------+

View File

@ -8,3 +8,4 @@ Corda networks
permissioning
network-map
versioning
cipher-suites

View File

@ -61,8 +61,8 @@ This command line will start the node with JMX metrics accessible via HTTP on po
See :ref:`Monitoring your node <jolokia_ref>` for further details.
Starting all nodes at once from the command line
------------------------------------------------
Starting all nodes at once from the command line (native)
---------------------------------------------------------
If you created your nodes using ``deployNodes``, a ``runnodes`` shell script (or batch file on Windows) will have been
generated to allow you to quickly start up all nodes and their webservers. ``runnodes`` should only be used for testing
purposes.
@ -73,4 +73,20 @@ Start the nodes with ``runnodes`` by running the following command from the root
* Windows: ``call build\nodes\runnodes.bat``
.. warning:: On macOS, do not click/change focus until all the node terminal windows have opened, or some processes may
fail to start.
fail to start.
If you receive an ``OutOfMemoryError`` exception when interacting with the nodes, you need to increase the amount of
Java heap memory available to them, which you can do when running them individually. See
:ref:`starting-an-individual-corda-node`.
Starting all nodes at once from the command line (docker-compose)
-----------------------------------------------------------------
If you created your nodes using ``Dockerform``, the ``docker-compose.yml`` file and corresponding ``Dockerfile`` for
nodes has been created and configured appropriately. Navigate to ``build/nodes`` directory and run ``docker-compose up``
command. This will startup nodes inside new, internal network.
After the nodes are started up, you can use ``docker ps`` command to see how the ports are mapped.
.. warning:: You need both ``Docker`` and ``docker-compose`` installed and enabled to use this method. Docker CE
(Community Edition) is enough. Please refer to `Docker CE documentation <https://www.docker.com/community-edition>`_
and `Docker Compose documentation <https://docs.docker.com/compose/install/>`_ for installation instructions for all
major operating systems.

View File

@ -0,0 +1,52 @@
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = 'net.corda.blobinspector.MainKt'
dependencies {
compile project(':core')
compile project(':node-api')
compile "commons-cli:commons-cli:$commons_cli_version"
testCompile project(':test-utils')
testCompile "junit:junit:$junit_version"
}
/**
* To run from within gradle use
*
* ./gradlew -PrunArgs="<cmd> <line> <args>" :experimental:blobinspector:run
*
* For example, to parse a file from the command line and print out the deserialized properties
*
* ./gradlew -PrunArgs="-f <path/to/file> -d" :experimental:blobinspector:run
*
* at the command line.
*/
run {
if (project.hasProperty('runArgs')) {
args = [ project.findProperty('runArgs').toString().split(" ") ].flatten()
}
if (System.properties.getProperty('consoleLogLevel') != null) {
logging.captureStandardOutput(LogLevel.valueOf(System.properties.getProperty('consoleLogLevel')))
logging.captureStandardError(LogLevel.valueOf(System.properties.getProperty('consoleLogLevel')))
systemProperty "consoleLogLevel", System.properties.getProperty('consoleLogLevel')
}
}
/**
* Build a executable jar
*/
jar {
baseName 'blobinspector'
manifest {
attributes(
'Automatic-Module-Name': 'net.corda.experimental.blobinspector',
'Main-Class': 'net.corda.blobinspector.MainKt'
)
}
}

View File

@ -0,0 +1,399 @@
package net.corda.blobinspector
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.EncodingWhitelist
import net.corda.core.serialization.SerializationEncoding
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.amqp.DescribedType
import org.apache.qpid.proton.amqp.Symbol
/**
* Print a string to the console only if the verbose config option is set.
*/
fun String.debug(config: Config) {
if (config.verbose) {
println(this)
}
}
/**
*
*/
interface Stringify {
fun stringify(sb: IndentingStringBuilder)
}
/**
* Makes classnames easier to read by stripping off the package names from the class and separating nested
* classes
*
* For example:
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2>
* Class1 <Class2>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2, net.corda.blobinspector.Class3>
* Class1 <Class2, Class3>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2<net.corda.blobinspector.Class3>>
* Class1 <Class2 <Class3>>
*
* net.corda.blobinspector.Class1<net.corda.blobinspector.Class2<net.corda.blobinspector.Class3>>
* Class1 :: C <Class2 <Class3>>
*/
fun String.simplifyClass(): String {
return if (this.endsWith('>')) {
val templateStart = this.indexOf('<')
val clazz = (this.substring(0, templateStart))
val params = this.substring(templateStart+1, this.length-1).split(',').map { it.simplifyClass() }.joinToString()
"${clazz.simplifyClass()} <$params>"
}
else {
substring(this.lastIndexOf('.') + 1).replace("$", " :: ")
}
}
/**
* Represents the deserialized form of the property of an Object
*
* @param name
* @param type
*/
abstract class Property(
val name: String,
val type: String) : Stringify
/**
* Derived class of [Property], represents properties of an object that are non compelex, such
* as any POD type or String
*/
class PrimProperty(
name: String,
type: String,
private val value: String) : Property(name, type) {
override fun toString(): String = "$name : $type : $value"
override fun stringify(sb: IndentingStringBuilder) {
sb.appendln("$name : $type : $value")
}
}
/**
* Derived class of [Property] that represents a binary blob. Specifically useful because printing
* a stream of bytes onto the screen isn't very use friendly
*/
class BinaryProperty(
name: String,
type: String,
val value: ByteArray) : Property(name, type) {
override fun toString(): String = "$name : $type : <<<BINARY BLOB>>>"
override fun stringify(sb: IndentingStringBuilder) {
sb.appendln("$name : $type : <<<BINARY BLOB>>>")
}
}
/**
* Derived class of [Property] that represent a list property. List could be either PoD types or
* composite types.
*/
class ListProperty(
name: String,
type: String,
private val values: MutableList<Any> = mutableListOf()) : Property(name, type) {
override fun stringify(sb: IndentingStringBuilder) {
sb.apply {
if (values.isEmpty()) {
appendln("$name : $type : [ << EMPTY LIST >> ]")
} else if (values.first() is Stringify) {
appendln("$name : $type : [")
values.forEach {
(it as Stringify).stringify(this)
}
appendln("]")
} else {
appendln("$name : $type : [")
values.forEach {
appendln(it.toString())
}
appendln("]")
}
}
}
}
class MapProperty(
name: String,
type: String,
private val map: MutableMap<*, *>
) : Property(name, type) {
override fun stringify(sb: IndentingStringBuilder) {
if (map.isEmpty()) {
sb.appendln("$name : $type : { << EMPTY MAP >> }")
return
}
// TODO this will not produce pretty output
sb.apply {
appendln("$name : $type : {")
map.forEach {
try {
(it.key as Stringify).stringify(this)
} catch (e: ClassCastException) {
append (it.key.toString() + " : ")
}
try {
(it.value as Stringify).stringify(this)
} catch (e: ClassCastException) {
appendln("\"${it.value.toString()}\"")
}
}
appendln("}")
}
}
}
/**
* Derived class of [Property] that represents class properties that are themselves instances of
* some complex type.
*/
class InstanceProperty(
name: String,
type: String,
val value: Instance) : Property(name, type) {
override fun stringify(sb: IndentingStringBuilder) {
sb.append("$name : ")
value.stringify(sb)
}
}
/**
* Represents an instance of a composite type.
*/
class Instance(
val name: String,
val type: String,
val fields: MutableList<Property> = mutableListOf()) : Stringify {
override fun stringify(sb: IndentingStringBuilder) {
sb.apply {
appendln("${name.simplifyClass()} : {")
fields.forEach {
it.stringify(this)
}
appendln("}")
}
}
}
/**
*
*/
fun inspectComposite(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
obj: DescribedType): Instance {
if (obj.described !is List<*>) throw MalformedBlob("")
val name = (typeMap[obj.descriptor] as CompositeType).name
"composite: $name".debug(config)
val inst = Instance(
typeMap[obj.descriptor]?.name ?: "",
typeMap[obj.descriptor]?.label ?: "")
(typeMap[obj.descriptor] as CompositeType).fields.zip(obj.described as List<*>).forEach {
" field: ${it.first.name}".debug(config)
inst.fields.add(
if (it.second is DescribedType) {
" - is described".debug(config)
val d = inspectDescribed(config, typeMap, it.second as DescribedType)
when (d) {
is Instance ->
InstanceProperty(
it.first.name,
it.first.type,
d)
is List<*> -> {
" - List".debug(config)
ListProperty(
it.first.name,
it.first.type,
d as MutableList<Any>)
}
is Map<*, *> -> {
MapProperty(
it.first.name,
it.first.type,
d as MutableMap<*, *>)
}
else -> {
" skip it".debug(config)
return@forEach
}
}
} else {
" - is prim".debug(config)
when (it.first.type) {
// Note, as in the case of SHA256 we can treat particular binary types
// as different properties with a little coercion
"binary" -> {
if (name == "net.corda.core.crypto.SecureHash\$SHA256") {
PrimProperty(
it.first.name,
it.first.type,
SecureHash.SHA256((it.second as Binary).array).toString())
} else {
BinaryProperty(it.first.name, it.first.type, (it.second as Binary).array)
}
}
else -> PrimProperty(it.first.name, it.first.type, it.second.toString())
}
})
}
return inst
}
fun inspectRestricted(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
obj: DescribedType): Any {
return when ((typeMap[obj.descriptor] as RestrictedType).source) {
"list" -> inspectRestrictedList(config, typeMap, obj)
"map" -> inspectRestrictedMap(config, typeMap, obj)
else -> throw NotImplementedError()
}
}
fun inspectRestrictedList(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
obj: DescribedType
) : List<Any> {
if (obj.described !is List<*>) throw MalformedBlob("")
return mutableListOf<Any>().apply {
(obj.described as List<*>).forEach {
when (it) {
is DescribedType -> add(inspectDescribed(config, typeMap, it))
is RestrictedType -> add(inspectRestricted(config, typeMap, it))
else -> add (it.toString())
}
}
}
}
fun inspectRestrictedMap(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
obj: DescribedType
) : Map<Any, Any> {
if (obj.described !is Map<*,*>) throw MalformedBlob("")
return mutableMapOf<Any, Any>().apply {
(obj.described as Map<*, *>).forEach {
val key = when (it.key) {
is DescribedType -> inspectDescribed(config, typeMap, it.key as DescribedType)
is RestrictedType -> inspectRestricted(config, typeMap, it.key as RestrictedType)
else -> it.key.toString()
}
val value = when (it.value) {
is DescribedType -> inspectDescribed(config, typeMap, it.value as DescribedType)
is RestrictedType -> inspectRestricted(config, typeMap, it.value as RestrictedType)
else -> it.value.toString()
}
this[key] = value
}
}
}
/**
* Every element of the blob stream will be a ProtonJ [DescribedType]. When inspecting the blob stream
* the two custom Corda types we're interested in are [CompositeType]'s, representing the instance of
* some object (class), and [RestrictedType]'s, representing containers and enumerations.
*
* @param config The configuration object that controls the behaviour of the BlobInspector
* @param typeMap
* @param obj
*/
fun inspectDescribed(
config: Config,
typeMap: Map<Symbol?, TypeNotation>,
obj: DescribedType): Any {
"${obj.descriptor} in typeMap? = ${obj.descriptor in typeMap}".debug(config)
return when (typeMap[obj.descriptor]) {
is CompositeType -> {
"* It's composite".debug(config)
inspectComposite(config, typeMap, obj)
}
is RestrictedType -> {
"* It's restricted".debug(config)
inspectRestricted(config, typeMap, obj)
}
else -> {
"${typeMap[obj.descriptor]?.name} is neither Composite or Restricted".debug(config)
}
}
}
internal object NullEncodingWhitelist : EncodingWhitelist {
override fun acceptEncoding(encoding: SerializationEncoding) = false
}
// TODO : Refactor to generically poerate on arbitrary blobs, not a single workflow
fun inspectBlob(config: Config, blob: ByteArray) {
val bytes = ByteSequence.of(blob)
val headerSize = SerializationFactoryImpl.magicSize
// TODO written to only understand one version, when we support multiple this will need to change
val headers = listOf(ByteSequence.of(amqpMagic.bytes))
val blobHeader = bytes.take(headerSize)
if (blobHeader !in headers) {
throw MalformedBlob("Blob is not a Corda AMQP serialised object graph")
}
val e = DeserializationInput.getEnvelope(bytes, NullEncodingWhitelist)
if (config.schema) {
println(e.schema)
}
if (config.transforms) {
println(e.transformsSchema)
}
val typeMap = e.schema.types.associateBy({ it.descriptor.name }, { it })
if (config.data) {
val inspected = inspectDescribed(config, typeMap, e.obj as DescribedType)
println("\n${IndentingStringBuilder().apply { (inspected as Instance).stringify(this) }}")
(inspected as Instance).fields.find {
it.type.startsWith("net.corda.core.serialization.SerializedBytes<")
}?.let {
"Found field of SerializedBytes".debug(config)
(it as InstanceProperty).value.fields.find { it.name == "bytes" }?.let { raw ->
inspectBlob(config, (raw as BinaryProperty).value)
}
}
}
}

View File

@ -0,0 +1,40 @@
package net.corda.blobinspector
import java.io.File
import java.net.URL
/**
*
*/
class FileBlobHandler(config_: Config) : BlobHandler(config_) {
private val path = File(URL((config_ as FileConfig).file).toURI())
override fun getBytes(): ByteArray {
return path.readBytes()
}
}
/**
*
*/
class InMemoryBlobHandler(config_: Config) : BlobHandler(config_) {
private val localBytes = (config_ as InMemoryConfig).blob?.bytes ?: kotlin.ByteArray(0)
override fun getBytes(): ByteArray = localBytes
}
/**
*
*/
abstract class BlobHandler (val config: Config) {
companion object {
fun make(config: Config) : BlobHandler {
return when (config.mode) {
Mode.file -> FileBlobHandler(config)
Mode.inMem -> InMemoryBlobHandler(config)
}
}
}
abstract fun getBytes() : ByteArray
}

View File

@ -0,0 +1,137 @@
package net.corda.blobinspector
import org.apache.commons.cli.CommandLine
import net.corda.core.serialization.SerializedBytes
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
/**
* Enumeration of the modes in which the blob inspector can be run.
*
* @property make lambda function that takes no parameters and returns a specific instance of the configuration
* object for that mode.
*
* @property options A lambda function that takes no parameters and returns an [Options] instance that define
* the command line flags related to this mode. For example ``file`` mode would have an option to pass in
* the name of the file to read.
*
*/
enum class Mode(
val make : () -> Config,
val options : (Options) -> Unit
) {
file(
{
FileConfig(Mode.file)
},
{ o ->
o.apply{
addOption(
Option ("f", "file", true, "path to file").apply {
isRequired = true
}
)
}
}
),
inMem(
{
InMemoryConfig(Mode.inMem)
},
{
// The in memory only mode has no specific option assocaited with it as it's intended for
// testing purposes only within the unit test framework and not use on the command line
}
)
}
/**
* Configuration data class for the Blob Inspector.
*
* @property mode
*/
abstract class Config (val mode: Mode) {
var schema: Boolean = false
var transforms: Boolean = false
var data: Boolean = false
var verbose: Boolean = false
abstract fun populateSpecific(cmdLine: CommandLine)
abstract fun withVerbose() : Config
fun populate(cmdLine: CommandLine) {
schema = cmdLine.hasOption('s')
transforms = cmdLine.hasOption('t')
data = cmdLine.hasOption('d')
verbose = cmdLine.hasOption('v')
populateSpecific(cmdLine)
}
fun options() = Options().apply {
// install generic options
addOption(Option("s", "schema", false, "print the blob's schema").apply {
isRequired = false
})
addOption(Option("t", "transforms", false, "print the blob's transforms schema").apply {
isRequired = false
})
addOption(Option("d", "data", false, "Display the serialised data").apply {
isRequired = false
})
addOption(Option("v", "verbose", false, "Enable debug output").apply {
isRequired = false
})
// install the mode specific options
mode.options(this)
}
}
/**
* Configuration object when running in "File" mode, i.e. the object has been specified at
* the command line
*/
class FileConfig (
mode: Mode
) : Config(mode) {
var file: String = "unset"
override fun populateSpecific(cmdLine : CommandLine) {
file = cmdLine.getParsedOptionValue("f") as String
}
override fun withVerbose() : FileConfig {
return FileConfig(mode).apply {
this.schema = schema
this.transforms = transforms
this.data = data
this.verbose = true
}
}
}
/**
* Placeholder config objet used when running unit tests and the inspected blob is being fed in
* via some mechanism directly. Normally this will be the direct serialisation of an object in a unit
* test and then dumping that blob into the inspector for visual comparison of the output
*/
class InMemoryConfig (
mode: Mode
) : Config(mode) {
var blob: SerializedBytes<*>? = null
override fun populateSpecific(cmdLine: CommandLine) {
throw UnsupportedOperationException("In memory config is for testing only and cannot set specific flags")
}
override fun withVerbose(): Config {
throw UnsupportedOperationException("In memory config is for testing headlessly, cannot be verbose")
}
}

View File

@ -0,0 +1,3 @@
package net.corda.blobinspector
class MalformedBlob(msg: String) : Exception(msg)

View File

@ -0,0 +1,45 @@
package net.corda.blobinspector
/**
* Wrapper around a [StringBuilder] that automates the indenting of lines as they're appended to facilitate
* pretty printing of deserialized blobs.
*
* @property sb The wrapped [StringBuilder]
* @property indenting Boolean flag that indicates weather we need to pad the start of whatever text
* currently being added to the string.
* @property indent How deeply the next line should be offset from the first column
*/
class IndentingStringBuilder(s : String = "", private val offset : Int = 4) {
private val sb = StringBuilder(s)
private var indenting = true
private var indent = 0
private fun wrap(ln: String, appender: (String) -> Unit) {
if ((ln.endsWith("}") || ln.endsWith("]")) && indent > 0 && ln.length == 1) {
indent -= offset
}
appender(ln)
if (ln.endsWith("{") || ln.endsWith("[")){
indent += offset
}
}
fun appendln(ln: String) {
wrap(ln) { s -> sb.appendln("${"".padStart(if (indenting) indent else 0, ' ')}$s") }
indenting = true
}
fun append(ln: String) {
indenting = false
wrap(ln) { s -> sb.append("${"".padStart(indent, ' ')}$s") }
}
override fun toString(): String {
return sb.toString()
}
}

View File

@ -0,0 +1,81 @@
package net.corda.blobinspector
import org.apache.commons.cli.*
import java.lang.IllegalArgumentException
/**
* Mode isn't a required property as we default it to [Mode.file]
*/
private fun modeOption() = Option("m", "mode", true, "mode, file is the default").apply {
isRequired = false
}
/**
*
* Parse the command line arguments looking for the main mode into which the application is
* being put. Note, this defaults to [Mode.file] if not set meaning we will look for a file path
* being passed as a parameter and parse that file.
*
* @param args reflects the command line arguments
*
* @return An instantiated but unpopulated [Config] object instance suitable for the mode into
* which we've been placed. This Config object should be populated via [loadModeSpecificOptions]
*/
fun getMode(args: Array<String>) : Config {
// For now we only care what mode we're being put in, we can build the rest of the args and parse them
// later
val options = Options().apply {
addOption(modeOption())
}
val cmd = try {
DefaultParser().parse(options, args, true)
} catch (e: org.apache.commons.cli.ParseException) {
println (e)
HelpFormatter().printHelp("blobinspector", options)
throw IllegalArgumentException("OH NO!!!")
}
return try {
Mode.valueOf(cmd.getParsedOptionValue("m") as? String ?: "file")
} catch (e: IllegalArgumentException) {
Mode.file
}.make()
}
/**
*
* @param config an instance of a [Config] specialisation suitable for the mode into which
* the application has been put.
* @param args The command line arguments
*/
fun loadModeSpecificOptions(config: Config, args: Array<String>) {
config.apply {
// load that modes specific command line switches, needs to include the mode option
val modeSpecificOptions = config.options().apply {
addOption(modeOption())
}
populate (try {
DefaultParser().parse(modeSpecificOptions, args, false)
} catch (e: org.apache.commons.cli.ParseException) {
println ("Error: ${e.message}")
HelpFormatter().printHelp("blobinspector", modeSpecificOptions)
System.exit(1)
return
})
}
}
/**
* Executable entry point
*/
fun main(args: Array<String>) {
println ("<<< WARNING: this tool is experimental and under active development >>>")
getMode(args).let { mode ->
loadModeSpecificOptions(mode, args)
BlobHandler.make(mode)
}.apply {
inspectBlob(config, getBytes())
}
}

View File

@ -0,0 +1,84 @@
package net.corda.blobinspector
import java.net.URI
import org.junit.Test
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
class FileParseTests {
@Suppress("UNUSED")
var localPath : URI = projectRootDir.toUri().resolve(
"tools/blobinspector/src/test/resources/net/corda/blobinspector")
fun setupArgsWithFile(path: String) = Array<String>(5) {
when (it) {
0 -> "-m"
1 -> "file"
2 -> "-f"
3 -> path
4 -> "-d"
else -> "error"
}
}
private val filesToTest = listOf (
"FileParseTests.1Int",
"FileParseTests.2Int",
"FileParseTests.3Int",
"FileParseTests.1String",
"FileParseTests.1Composite",
"FileParseTests.2Composite",
"FileParseTests.IntList",
"FileParseTests.StringList",
"FileParseTests.MapIntString",
"FileParseTests.MapIntClass"
)
fun testFile(file : String) {
val path = FileParseTests::class.java.getResource(file)
val args = setupArgsWithFile(path.toString())
val handler = getMode(args).let { mode ->
loadModeSpecificOptions(mode, args)
BlobHandler.make(mode)
}
inspectBlob(handler.config, handler.getBytes())
}
@Test
fun simpleFiles() {
filesToTest.forEach { testFile(it) }
}
@Test
fun specificTest() {
testFile(filesToTest[4])
testFile(filesToTest[5])
testFile(filesToTest[6])
}
@Test
fun networkParams() {
val file = "networkParams"
val path = FileParseTests::class.java.getResource(file)
val verbose = false
val args = verbose.let {
if (it)
Array(4) { when (it) { 0 -> "-f" ; 1 -> path.toString(); 2 -> "-d"; 3 -> "-vs"; else -> "error" } }
else
Array(3) { when (it) { 0 -> "-f" ; 1 -> path.toString(); 2 -> "-d"; else -> "error" } }
}
val handler = getMode(args).let { mode ->
loadModeSpecificOptions(mode, args)
BlobHandler.make(mode)
}
inspectBlob(handler.config, handler.getBytes())
}
}

View File

@ -0,0 +1,91 @@
package net.corda.blobinspector
import net.corda.core.serialization.SerializedBytes
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.SerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
import org.junit.Test
class InMemoryTests {
private val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
private fun inspect (b: SerializedBytes<*>) {
BlobHandler.make(
InMemoryConfig(Mode.inMem).apply { blob = b; data = true}
).apply {
inspectBlob(config, getBytes())
}
}
@Test
fun test1() {
data class C (val a: Int, val b: Long, val c: String)
inspect (SerializationOutput(factory).serialize(C(100, 567L, "this is a test"), AMQP_P2P_CONTEXT))
}
@Test
fun test2() {
data class C (val i: Int, val c: C?)
inspect (SerializationOutput(factory).serialize(C(1, C(2, C(3, C(4, null)))), AMQP_P2P_CONTEXT))
}
@Test
fun test3() {
data class C (val a: IntArray, val b: Array<String>)
val a = IntArray(10) { i -> i }
val c = C(a, arrayOf("aaa", "bbb", "ccc"))
inspect (SerializationOutput(factory).serialize(c, AMQP_P2P_CONTEXT))
}
@Test
fun test4() {
data class Elem(val e1: Long, val e2: String)
data class Wrapper (val name: String, val elementes: List<Elem>)
inspect (SerializationOutput(factory).serialize(
Wrapper("Outer Class",
listOf(
Elem(1L, "First element"),
Elem(2L, "Second element"),
Elem(3L, "Third element")
)), AMQP_P2P_CONTEXT))
}
@Test
fun test4b() {
data class Elem(val e1: Long, val e2: String)
data class Wrapper (val name: String, val elementes: List<List<Elem>>)
inspect (SerializationOutput(factory).serialize(
Wrapper("Outer Class",
listOf (
listOf(
Elem(1L, "First element"),
Elem(2L, "Second element"),
Elem(3L, "Third element")
),
listOf(
Elem(4L, "Fourth element"),
Elem(5L, "Fifth element"),
Elem(6L, "Sixth element")
)
)), AMQP_P2P_CONTEXT))
}
@Test
fun test5() {
data class C (val a: Map<String, String>)
inspect (SerializationOutput(factory).serialize(
C(mapOf(
"a" to "a a a",
"b" to "b b b",
"c" to "c c c")),
AMQP_P2P_CONTEXT
))
}
}

View File

@ -0,0 +1,77 @@
package net.corda.blobinspector
import org.junit.Test
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import kotlin.test.assertFalse
class ModeParse {
@Test
fun fileIsSetToFile() {
val opts1 = Array<String>(2) {
when (it) {
0 -> "-m"
1 -> "file"
else -> "error"
}
}
assertEquals(Mode.file, getMode(opts1).mode)
}
@Test
fun nothingIsSetToFile() {
val opts1 = Array<String>(0) { "" }
assertEquals(Mode.file, getMode(opts1).mode)
}
@Test
fun filePathIsSet() {
val opts1 = Array<String>(4) {
when (it) {
0 -> "-m"
1 -> "file"
2 -> "-f"
3 -> "path/to/file"
else -> "error"
}
}
val config = getMode(opts1)
assertTrue (config is FileConfig)
assertEquals(Mode.file, config.mode)
assertEquals("unset", (config as FileConfig).file)
loadModeSpecificOptions(config, opts1)
assertEquals("path/to/file", config.file)
}
@Test
fun schemaIsSet() {
Array(2) { when (it) { 0 -> "-f"; 1 -> "path/to/file"; else -> "error" } }.let { options ->
getMode(options).apply {
loadModeSpecificOptions(this, options)
assertFalse (schema)
}
}
Array(3) { when (it) { 0 -> "--schema"; 1 -> "-f"; 2 -> "path/to/file"; else -> "error" } }.let {
getMode(it).apply {
loadModeSpecificOptions(this, it)
assertTrue (schema)
}
}
Array(3) { when (it) { 0 -> "-f"; 1 -> "path/to/file"; 2 -> "-s"; else -> "error" } }.let {
getMode(it).apply {
loadModeSpecificOptions(this, it)
assertTrue (schema)
}
}
}
}

View File

@ -0,0 +1,28 @@
package net.corda.blobinspector
import org.junit.Test
class SimplifyClassTests {
@Test
fun test1() {
data class A(val a: Int)
println (A::class.java.name)
println (A::class.java.name.simplifyClass())
}
@Test
fun test2() {
val p = this.javaClass.`package`.name
println("$p.Class1<$p.Class2>")
println("$p.Class1<$p.Class2>".simplifyClass())
println("$p.Class1<$p.Class2, $p.Class3>")
println("$p.Class1<$p.Class2, $p.Class3>".simplifyClass())
println("$p.Class1<$p.Class2<$p.Class3>>")
println("$p.Class1<$p.Class2<$p.Class3>>".simplifyClass())
println("$p.Class1<$p.Class2<$p.Class3>>")
println("$p.Class1\$C<$p.Class2<$p.Class3>>".simplifyClass())
}
}

View File

@ -60,12 +60,12 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
issuerRef = this.amount.token.issuer.reference.bytes
)
is SampleCashSchemaV2 -> SampleCashSchemaV2.PersistentCashState(
_participants = this.participants.toMutableSet(),
_owner = this.owner,
_quantity = this.amount.quantity,
participants = this.participants.toMutableSet(),
owner = this.owner,
quantity = this.amount.quantity,
currency = this.amount.token.product.currencyCode,
_issuerParty = this.amount.token.issuer.party,
_issuerRef = this.amount.token.issuer.reference
issuerParty = this.amount.token.issuer.party,
issuerRef = this.amount.token.issuer.reference
)
is SampleCashSchemaV3 -> SampleCashSchemaV3.PersistentCashState(
participants = this.participants.toMutableSet(),

View File

@ -17,8 +17,7 @@ import net.corda.core.utilities.OpaqueBytes
import javax.persistence.*
/**
* Second version of a cash contract ORM schema that extends the common
* [VaultFungibleState] abstract schema
* Second version of a cash contract ORM schema that extends the [CommonSchemaV1.FungibleState] abstract schema.
*/
object SampleCashSchemaV2 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 2,
mappedTypes = listOf(PersistentCashState::class.java)) {
@ -26,31 +25,21 @@ object SampleCashSchemaV2 : MappedSchema(schemaFamily = CashSchema.javaClass, ve
override val migrationResource = "sample-cash-v2.changelog-init"
@Entity
@Table(name = "cash_states_v2",
indexes = arrayOf(Index(name = "ccy_code_idx2", columnList = "ccy_code")))
@Table(name = "cash_states_v2", indexes = [Index(name = "ccy_code_idx2", columnList = "ccy_code")])
class PersistentCashState(
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cash_states_v2_participants", joinColumns = arrayOf(
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null,
/** product type */
@Column(name = "ccy_code", length = 3)
var currency: String,
participants: Set<AbstractParty>,
owner: AbstractParty,
quantity: Long,
issuerParty: AbstractParty,
issuerRef: OpaqueBytes
) : CommonSchemaV1.FungibleState(participants.toMutableSet(), owner, quantity, issuerParty, issuerRef.bytes) {
/** parent attributes */
@Transient
val _participants: Set<AbstractParty>,
@Transient
val _owner: AbstractParty,
@Transient
val _quantity: Long,
@Transient
val _issuerParty: AbstractParty,
@Transient
val _issuerRef: OpaqueBytes
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef.bytes)
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cash_states_v2_participants", joinColumns = [JoinColumn(name = "output_index", referencedColumnName = "output_index"), JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")])
override var participants: MutableSet<AbstractParty>? = null
}
}

View File

@ -31,17 +31,8 @@ object SampleCommercialPaperSchemaV2 : MappedSchema(schemaFamily = CommercialPap
@Entity
@Table(name = "cp_states_v2",
indexes = arrayOf(Index(name = "ccy_code_index2", columnList = "ccy_code"),
Index(name = "maturity_index2", columnList = "maturity_instant")))
indexes = [Index(name = "ccy_code_index2", columnList = "ccy_code"), Index(name = "maturity_index2", columnList = "maturity_instant")])
class PersistentCommercialPaperState(
@ElementCollection
@Column(name = "participants")
@CollectionTable(name="cp_states_v2_participants", joinColumns = arrayOf(
JoinColumn(name = "output_index", referencedColumnName = "output_index"),
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")))
override var participants: MutableSet<AbstractParty>? = null,
@Column(name = "maturity_instant")
var maturity: Instant,
@ -55,17 +46,16 @@ object SampleCommercialPaperSchemaV2 : MappedSchema(schemaFamily = CommercialPap
@Type(type = "corda-wrapper-binary")
var faceValueIssuerRef: ByteArray,
/** parent attributes */
@Transient
val _participants: Set<AbstractParty>,
@Transient
val _owner: AbstractParty,
@Transient
// face value
val _quantity: Long,
@Transient
val _issuerParty: AbstractParty,
@Transient
val _issuerRef: OpaqueBytes
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef.bytes)
participants: Set<AbstractParty>,
owner: AbstractParty,
quantity: Long,
issuerParty: AbstractParty,
issuerRef: OpaqueBytes
) : CommonSchemaV1.FungibleState(participants.toMutableSet(), owner, quantity, issuerParty, issuerRef.bytes) {
@ElementCollection
@Column(name = "participants")
@CollectionTable(name = "cp_states_v2_participants", joinColumns = [JoinColumn(name = "output_index", referencedColumnName = "output_index"), JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id")])
override var participants: MutableSet<AbstractParty>? = null
}
}

View File

@ -361,7 +361,7 @@ internal class ConnectionStateMachine(serverMode: Boolean,
val connection = event.connection
val channel = connection?.context as? Channel
if (channel != null) {
val appProperties = HashMap(amqpMessage.applicationProperties.value)
val appProperties = HashMap(amqpMessage.applicationProperties.value as Map<String, Any?>)
appProperties["_AMQ_VALIDATED_USER"] = remoteLegalName
val localAddress = channel.localAddress() as InetSocketAddress
val remoteAddress = channel.remoteAddress() as InetSocketAddress
@ -438,7 +438,6 @@ internal class ConnectionStateMachine(serverMode: Boolean,
}
fun transportWriteMessage(msg: SendableMessageImpl) {
log.debug { "Queue application message write uuid: ${msg.applicationProperties["_AMQ_DUPL_ID"]} ${javax.xml.bind.DatatypeConverter.printHexBinary(msg.payload)}" }
msg.buf = encodePayloadBytes(msg)
val messageQueue = messageQueues.getOrPut(msg.topic, { LinkedList() })
messageQueue.offer(msg)

View File

@ -130,7 +130,6 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
try {
log.debug { "Received $msg" }
if (msg is ByteBuf) {
eventProcessor!!.transportProcessInput(msg)
}
@ -143,7 +142,6 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
try {
try {
log.debug { "Sent $msg" }
when (msg) {
// Transfers application packet into the AMQP engine.
is SendableMessageImpl -> {

View File

@ -17,8 +17,8 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.serialization.OrdinalBits.OrdinalWriter
import org.iq80.snappy.SnappyFramedInputStream
import org.iq80.snappy.SnappyFramedOutputStream
import java.io.OutputStream
import java.io.InputStream
import java.io.OutputStream
import java.nio.ByteBuffer
import java.util.zip.DeflaterOutputStream
import java.util.zip.InflaterInputStream

View File

@ -49,13 +49,15 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
*/
override fun withAttachmentsClassLoader(attachmentHashes: List<SecureHash>): SerializationContext {
properties[attachmentsClassLoaderEnabledPropertyName] as? Boolean == true || return this
val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContextImpl ?: return this // Some tests don't set one.
val serializationContext = properties[serializationContextKey] as? SerializeAsTokenContextImpl
?: return this // Some tests don't set one.
try {
return withClassLoader(cache.get(attachmentHashes) {
val missing = ArrayList<SecureHash>()
val attachments = ArrayList<Attachment>()
attachmentHashes.forEach { id ->
serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id }
serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it }
?: run { missing += id }
}
missing.isNotEmpty() && throw MissingAttachmentsException(missing)
AttachmentsClassLoader(attachments, parent = deserializationClassLoader)
@ -90,7 +92,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe
open class SerializationFactoryImpl : SerializationFactory() {
companion object {
private val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single()
val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single()
}
private val creator: List<StackTraceElement> = Exception().stackTrace.asList()
@ -153,8 +155,6 @@ open class SerializationFactoryImpl : SerializationFactory() {
}
interface SerializationScheme {
fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean
@Throws(NotSerializableException::class)

View File

@ -62,5 +62,6 @@ class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: Ser
}
}
override fun getSingleton(className: String) = classNameToSingleton[className] ?: throw IllegalStateException("Unable to find tokenized instance of $className in context $this")
override fun getSingleton(className: String) = classNameToSingleton[className]
?: throw IllegalStateException("Unable to find tokenized instance of $className in context $this")
}

View File

@ -15,7 +15,8 @@ import net.corda.core.serialization.SerializationFactory
import java.util.*
internal fun checkUseCase(allowedUseCases: EnumSet<SerializationContext.UseCase>) {
val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext ?: throw IllegalStateException("Current context is not set")
val currentContext: SerializationContext = SerializationFactory.currentFactory?.currentContext
?: throw IllegalStateException("Current context is not set")
if (!allowedUseCases.contains(currentContext.useCase)) {
throw IllegalStateException("UseCase '${currentContext.useCase}' is not within '$allowedUseCases'")
}

View File

@ -20,7 +20,7 @@ import org.apache.qpid.proton.amqp.UnsignedLong
* Repeated here for brevity:
* 50530 - R3 - Mike Hearn - mike&r3.com
*/
const val DESCRIPTOR_TOP_32BITS: Long = 0xc562L shl(32 + 16)
const val DESCRIPTOR_TOP_32BITS: Long = 0xc562L shl (32 + 16)
/**
* AMQP descriptor ID's for our custom types.

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
@ -28,7 +29,14 @@ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer<Any> {
override fun writeClassInfo(output: SerializationOutput) {
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(
obj: Any,
data: Data,
type: Type,
output: SerializationOutput,
context: SerializationContext,
debugIndent: Int
) {
if (obj is ByteArray) {
data.putObject(Binary(obj))
} else {
@ -39,5 +47,6 @@ class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer<Any> {
override fun readObject(
obj: Any,
schemas: SerializationSchemas,
input: DeserializationInput): Any = (obj as? Binary)?.array ?: obj
input: DeserializationInput,
context: SerializationContext): Any = (obj as? Binary)?.array ?: obj
}

View File

@ -16,8 +16,8 @@ import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.objectOrNewInstance
import net.corda.core.serialization.*
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.DefaultWhitelist
import net.corda.nodeapi.internal.serialization.MutableClassWhitelist
import net.corda.nodeapi.internal.serialization.SerializationScheme
@ -46,7 +46,7 @@ open class SerializerFactoryFactory {
abstract class AbstractAMQPSerializationScheme(
val cordappLoader: List<Cordapp>,
val sff : SerializerFactoryFactory = SerializerFactoryFactory()
val sff: SerializerFactoryFactory = SerializerFactoryFactory()
) : SerializationScheme {
// TODO: This method of initialisation for the Whitelist and plugin serializers will have to change
// when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way
@ -62,7 +62,7 @@ abstract class AbstractAMQPSerializationScheme(
val scanSpec: String? = System.getProperty(SCAN_SPEC_PROP_NAME)
if(scanSpec == null) {
if (scanSpec == null) {
emptyList()
} else {
FastClasspathScanner(scanSpec).addClassLoader(this::class.java.classLoader).scan()
@ -74,7 +74,7 @@ abstract class AbstractAMQPSerializationScheme(
}
}
private fun registerCustomSerializers(factory: SerializerFactory) {
private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) {
with(factory) {
register(publicKeySerializer)
register(net.corda.nodeapi.internal.serialization.amqp.custom.PrivateKeySerializer)
@ -131,8 +131,7 @@ abstract class AbstractAMQPSerializationScheme(
protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory
protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
open protected val publicKeySerializer: CustomSerializer.Implements<PublicKey>
= net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer
protected open val publicKeySerializer: CustomSerializer.Implements<PublicKey> = net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer
private fun getSerializerFactory(context: SerializationContext): SerializerFactory {
return serializerFactoriesForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
@ -145,19 +144,20 @@ abstract class AbstractAMQPSerializationScheme(
rpcServerSerializerFactory(context)
else -> sff.make(context)
}.also {
registerCustomSerializers(it)
registerCustomSerializers(context, it)
}
}
}
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val serializerFactory = getSerializerFactory(context)
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz)
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context)
}
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val serializerFactory = getSerializerFactory(context)
return SerializationOutput(serializerFactory).serialize(obj)
return SerializationOutput(serializerFactory).serialize(obj, context)
}
protected fun canDeserializeVersion(magic: CordaSerializationMagic) = magic == amqpMagic

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -40,10 +41,11 @@ interface AMQPSerializer<out T> {
/**
* Write the given object, with declared type, to the output.
*/
fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int = 0)
fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int = 0)
/**
* Read the given object from the input. The envelope is provided in case the schema is required.
*/
fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T
fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): T
}

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
@ -42,7 +43,8 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
}
override val typeDescriptor by lazy {
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") }
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
}
internal val elementType: Type by lazy { type.componentType() }
internal open val typeName by lazy { calcTypeName(type) }
@ -56,20 +58,24 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
}
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
// Write described
data.withDescribed(typeNotation.descriptor) {
withList {
for (entry in obj as Array<*>) {
output.writeObjectOrNull(entry, this, elementType, debugIndent)
output.writeObjectOrNull(entry, this, elementType, context, debugIndent)
}
}
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any {
if (obj is List<*>) {
return obj.map { input.readObjectOrNull(it, schemas, elementType) }.toArrayOfType(elementType)
return obj.map { input.readObjectOrNull(it, schemas, elementType, context) }.toArrayOfType(elementType)
} else throw NotSerializableException("Expected a List but found $obj")
}
@ -118,20 +124,24 @@ abstract class PrimArraySerializer(type: Type, factory: SerializerFactory) : Arr
}
}
class PrimIntArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(IntArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
class PrimIntArraySerializer(factory: SerializerFactory) : PrimArraySerializer(IntArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as IntArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as IntArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}
class PrimCharArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(CharArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
localWriteObject(data) { (obj as CharArray).forEach {
output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
class PrimCharArraySerializer(factory: SerializerFactory) : PrimArraySerializer(CharArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as CharArray).forEach {
output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1)
}
}
}
@ -145,47 +155,55 @@ class PrimCharArraySerializer(factory: SerializerFactory) :
}
}
class PrimBooleanArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(BooleanArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
class PrimBooleanArraySerializer(factory: SerializerFactory) : PrimArraySerializer(BooleanArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as BooleanArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as BooleanArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}
class PrimDoubleArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(DoubleArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as DoubleArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as DoubleArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}
class PrimFloatArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(FloatArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int) {
localWriteObject(data) {
(obj as FloatArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as FloatArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}
class PrimShortArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(ShortArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as ShortArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as ShortArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}
class PrimLongArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(LongArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
localWriteObject(data) {
(obj as LongArray).forEach { output.writeObjectOrNull(it, data, elementType, debugIndent+1) }
(obj as LongArray).forEach { output.writeObjectOrNull(it, data, elementType, context, debugIndent + 1) }
}
}
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.NonEmptySet
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
@ -18,15 +19,14 @@ import java.io.NotSerializableException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
import kotlin.collections.Collection
import kotlin.collections.LinkedHashSet
import kotlin.collections.Set
/**
* Serialization / deserialization of predefined set of supported [Collection] types covering mostly [List]s and [Set]s.
*/
class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType))
override val type: Type = declaredType as? DeserializedParameterizedType
?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType))
override val typeDescriptor by lazy {
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
}
@ -60,7 +60,8 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
}
private fun deriveParametrizedType(declaredType: Type, collectionClass: Class<out Collection<*>>): ParameterizedType =
(declaredType as? ParameterizedType) ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType))
(declaredType as? ParameterizedType)
?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType))
private fun findMostSuitableCollectionType(actualClass: Class<*>): Class<out Collection<*>> =
@ -83,12 +84,13 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
data: Data,
type: Type,
output: SerializationOutput,
context: SerializationContext,
debugIndent: Int) = ifThrowsAppend({ declaredType.typeName }) {
// Write described
data.withDescribed(typeNotation.descriptor) {
withList {
for (entry in obj as Collection<*>) {
output.writeObjectOrNull(entry, this, declaredType.actualTypeArguments[0], debugIndent)
output.writeObjectOrNull(entry, this, declaredType.actualTypeArguments[0], context, debugIndent)
}
}
}
@ -97,8 +99,11 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
override fun readObject(
obj: Any,
schemas: SerializationSchemas,
input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) {
input: DeserializationInput,
context: SerializationContext): Any = ifThrowsAppend({ declaredType.typeName }) {
// TODO: Can we verify the entries in the list?
concreteBuilder((obj as List<*>).map { input.readObjectOrNull(it, schemas, declaredType.actualTypeArguments[0]) })
concreteBuilder((obj as List<*>).map {
input.readObjectOrNull(it, schemas, declaredType.actualTypeArguments[0], context)
})
}
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
import org.apache.qpid.proton.amqp.Symbol
@ -74,22 +75,25 @@ class CorDappCustomSerializer(
override fun writeClassInfo(output: SerializationOutput) {}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
val proxy = uncheckedCast<SerializationCustomSerializer<*, *>,
SerializationCustomSerializer<Any?, Any?>>(serializer).toProxy(obj)
data.withDescribed(descriptor) {
data.withList {
proxySerializer.propertySerializers.serializationOrder.forEach {
it.getter.writeProperty(proxy, this, output)
it.getter.writeProperty(proxy, this, output, context)
}
}
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput) =
uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input)))!!
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
) = uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)))!!
override fun isSerializerFor(clazz: Class<*>) = clazz == type
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
@ -50,13 +51,16 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
*/
override val revealSubclassesInSchema: Boolean get() = false
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
data.withDescribed(descriptor) {
writeDescribedObject(uncheckedCast(obj), data, type, output)
writeDescribedObject(uncheckedCast(obj), data, type, output, context)
}
}
abstract fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput)
abstract fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext)
/**
* This custom serializer represents a sort of symbolic link from a subclass to a super class, where the super
@ -87,12 +91,16 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
override val descriptor: Descriptor = Descriptor(typeDescriptor)
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) {
superClassSerializer.writeDescribedObject(obj, data, type, output)
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
superClassSerializer.writeDescribedObject(obj, data, type, output, context)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T {
return superClassSerializer.readObject(obj, schemas, input)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): T {
return superClassSerializer.readObject(obj, schemas, input, context)
}
}
@ -134,7 +142,12 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyClass, factory) }
override val schemaForDocumentation: Schema by lazy {
val typeNotations = mutableSetOf<TypeNotation>(CompositeType(nameForType(type), null, emptyList(), descriptor, (proxySerializer.typeNotation as CompositeType).fields))
val typeNotations = mutableSetOf<TypeNotation>(
CompositeType(
nameForType(type),
null,
emptyList(),
descriptor, (proxySerializer.typeNotation as CompositeType).fields))
for (additional in additionalSerializers) {
typeNotations.addAll(additional.schemaForDocumentation.types)
}
@ -148,17 +161,21 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
protected abstract fun fromProxy(proxy: P): T
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
val proxy = toProxy(obj)
data.withList {
proxySerializer.propertySerializers.serializationOrder.forEach {
it.getter.writeProperty(proxy, this, output)
it.getter.writeProperty(proxy, this, output, context)
}
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T {
val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schemas, input))
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): T {
val proxy: P = uncheckedCast(proxySerializer.readObject(obj, schemas, input, context))
return fromProxy(proxy)
}
}
@ -186,11 +203,15 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
SerializerFactory.primitiveTypeName(String::class.java)!!,
descriptor, emptyList())))
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: T, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
data.putString(unmaker(obj))
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): T {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): T {
val proxy = obj as String
return maker(proxy)
}

View File

@ -14,6 +14,7 @@ import com.esotericsoftware.kryo.io.ByteBufferInputStream
import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.getStackTraceAsString
import net.corda.core.serialization.EncodingWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding
@ -45,7 +46,7 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
private val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist) {
private val objectHistory: MutableList<Any> = mutableListOf()
internal companion object {
companion object {
private val BYTES_NEEDED_TO_PEEK: Int = 23
fun peekSize(bytes: ByteArray): Int {
@ -70,9 +71,10 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
@VisibleForTesting
@Throws(NotSerializableException::class)
internal fun <T> withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T {
fun <T> withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T {
// Check that the lead bytes match expected header
val amqpSequence = amqpMagic.consume(byteSequence) ?: throw NotSerializableException("Serialization header does not match.")
val amqpSequence = amqpMagic.consume(byteSequence)
?: throw NotSerializableException("Serialization header does not match.")
var stream: InputStream = ByteBufferInputStream(amqpSequence)
try {
while (true) {
@ -89,17 +91,9 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
stream.close()
}
}
}
@Throws(NotSerializableException::class)
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>): T = deserialize(bytes, T::class.java)
@Throws(NotSerializableException::class)
inline internal fun <reified T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>): ObjectAndEnvelope<T> =
deserializeAndReturnEnvelope(bytes, T::class.java)
@Throws(NotSerializableException::class)
internal fun getEnvelope(byteSequence: ByteSequence): Envelope {
fun getEnvelope(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist): Envelope {
return withDataBytes(byteSequence, encodingWhitelist) { dataBytes ->
val data = Data.Factory.create()
val expectedSize = dataBytes.remaining()
@ -107,6 +101,16 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
Envelope.get(data)
}
}
}
@Throws(NotSerializableException::class)
fun getEnvelope(byteSequence: ByteSequence) = Companion.getEnvelope(byteSequence, encodingWhitelist)
@Throws(NotSerializableException::class)
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>, context: SerializationContext): T =
deserialize(bytes, T::class.java, context)
@Throws(NotSerializableException::class)
private fun <R> des(generator: () -> R): R {
@ -127,23 +131,37 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
* be deserialized and a schema describing the types of the objects.
*/
@Throws(NotSerializableException::class)
fun <T : Any> deserialize(bytes: ByteSequence, clazz: Class<T>): T = des {
val envelope = getEnvelope(bytes)
clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz))
fun <T : Any> deserialize(bytes: ByteSequence, clazz: Class<T>, context: SerializationContext): T =
des {
val envelope = getEnvelope(bytes, encodingWhitelist)
clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema),
clazz, context))
}
@Throws(NotSerializableException::class)
fun <T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>, clazz: Class<T>): ObjectAndEnvelope<T> = des {
val envelope = getEnvelope(bytes)
fun <T : Any> deserializeAndReturnEnvelope(
bytes: SerializedBytes<T>,
clazz: Class<T>,
context: SerializationContext
): ObjectAndEnvelope<T> = des {
val envelope = getEnvelope(bytes, encodingWhitelist)
// Now pick out the obj and schema from the envelope.
ObjectAndEnvelope(clazz.cast(readObjectOrNull(envelope.obj, SerializationSchemas(envelope.schema, envelope.transformsSchema), clazz)), envelope)
ObjectAndEnvelope(
clazz.cast(readObjectOrNull(
envelope.obj,
SerializationSchemas(envelope.schema, envelope.transformsSchema),
clazz,
context)),
envelope)
}
internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, offset: Int = 0): Any? {
return if (obj == null) null else readObject(obj, schema, type, offset)
internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, context: SerializationContext,
offset: Int = 0
): Any? {
return if (obj == null) null else readObject(obj, schema, type, context, offset)
}
internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, debugIndent: Int = 0): Any =
internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, context: SerializationContext, debugIndent: Int = 0): Any =
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()
@ -169,14 +187,15 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
throw NotSerializableException("Described type with descriptor ${obj.descriptor} was " +
"expected to be of type $type but was ${serializer.type}")
}
serializer.readObject(obj.described, schemas, this)
serializer.readObject(obj.described, schemas, this, context)
}
is Binary -> obj.array
else -> obj // this will be the case for primitive types like [boolean] et al.
}
// Store the reference in case we need it later on.
// Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content
// Skip for primitive types as they are too small and overhead of referencing them will be much higher
// than their content
if (suitableForObjectReference(objectRead.javaClass)) {
objectHistory.add(objectRead)
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
@ -75,7 +76,8 @@ class EnumEvolutionSerializer(
new: AMQPSerializer<Any>,
factory: SerializerFactory,
schemas: SerializationSchemas): AMQPSerializer<Any> {
val wireTransforms = schemas.transforms.types[old.name] ?: EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
val wireTransforms = schemas.transforms.types[old.name]
?: EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
val localTransforms = TransformsSchema.get(old.name, factory)
// remember, the longer the list the newer we're assuming the transform set it as we assume
@ -127,7 +129,9 @@ class EnumEvolutionSerializer(
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any {
val enumName = (obj as List<*>)[0] as String
if (enumName !in conversions) {
@ -141,7 +145,9 @@ class EnumEvolutionSerializer(
throw UnsupportedOperationException("It should be impossible to write an evolution serializer")
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
throw UnsupportedOperationException("It should be impossible to write an evolution serializer")
}
}

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
@ -38,7 +39,9 @@ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: Seria
output.writeTypeNotations(typeNotation)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any {
val enumName = (obj as List<*>)[0] as String
val enumOrd = obj[1] as Int
val fromOrd = type.asClass()!!.enumConstants[enumOrd] as Enum<*>?
@ -50,7 +53,9 @@ class EnumSerializer(declaredType: Type, declaredClass: Class<*>, factory: Seria
return fromOrd
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
if (obj !is Enum<*>) throw NotSerializableException("Serializing $obj as enum when it isn't")
data.withDescribed(typeNotation.descriptor) {

View File

@ -39,7 +39,7 @@ data class Envelope(val obj: Any?, val schema: Schema, val transformsSchema: Tra
fun get(data: Data): Envelope {
val describedType = data.`object` as DescribedType
if (describedType.descriptor != DESCRIPTOR) {
throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}.")
throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}, should be $DESCRIPTOR.")
}
val list = describedType.described as List<*>

View File

@ -11,10 +11,11 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.carpenter.getTypeAsClass
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
import java.io.NotSerializableException
import java.lang.reflect.Type
import kotlin.reflect.KFunction
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.javaType
@ -49,9 +50,10 @@ abstract class EvolutionSerializer(
* @param property object to read the actual property value
*/
data class OldParam(var resultsIndex: Int, val property: PropertySerializer) {
fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, new: Array<Any?>) =
property.readProperty(obj, schemas, input).apply {
if(resultsIndex >= 0) {
fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput,
new: Array<Any?>, context: SerializationContext
) = property.readProperty(obj, schemas, input, context).apply {
if (resultsIndex >= 0) {
new[resultsIndex] = this
}
}
@ -106,7 +108,7 @@ abstract class EvolutionSerializer(
"New parameter ${it.value.name} is mandatory, should be nullable for evolution to worK")
}
}
return EvolutionSerializerViaConstructor (new.type, factory, readersAsSerialized, constructor, constructorArgs)
return EvolutionSerializerViaConstructor(new.type, factory, readersAsSerialized, constructor, constructorArgs)
}
private fun makeWithSetters(
@ -118,7 +120,7 @@ abstract class EvolutionSerializer(
val setters = propertiesForSerializationFromSetters(classProperties,
new.type,
factory).associateBy({ it.getter.name }, { it })
return EvolutionSerializerViaSetters (new.type, factory, readersAsSerialized, constructor, setters)
return EvolutionSerializerViaSetters(new.type, factory, readersAsSerialized, constructor, setters)
}
/**
@ -153,14 +155,15 @@ abstract class EvolutionSerializer(
return if (classProperties.isNotEmpty() && constructor.parameters.isEmpty()) {
makeWithSetters(new, factory, constructor, readersAsSerialized, classProperties)
}
else {
} else {
makeWithConstructor(new, factory, constructor, readersAsSerialized)
}
}
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
throw UnsupportedOperationException("It should be impossible to write an evolution serializer")
}
}
@ -170,7 +173,7 @@ class EvolutionSerializerViaConstructor(
factory: SerializerFactory,
oldReaders: Map<String, EvolutionSerializer.OldParam>,
kotlinConstructor: KFunction<Any>?,
private val constructorArgs: Array<Any?>) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) {
private val constructorArgs: Array<Any?>) : EvolutionSerializer(clazz, factory, oldReaders, kotlinConstructor) {
/**
* Unlike a normal [readObject] call where we simply apply the parameter deserialisers
* to the object list of values we need to map that list, which is ordered per the
@ -180,14 +183,15 @@ class EvolutionSerializerViaConstructor(
*
* TODO: Object references
*/
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any {
if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj")
// *must* read all the parameters in the order they were serialized
oldReaders.values.zip(obj).map { it.first.readProperty(it.second, schemas, input, constructorArgs) }
oldReaders.values.zip(obj).map { it.first.readProperty(it.second, schemas, input, constructorArgs, context) }
return javaConstructor?.newInstance(*(constructorArgs)) ?:
throw NotSerializableException(
return javaConstructor?.newInstance(*(constructorArgs)) ?: throw NotSerializableException(
"Attempt to deserialize an interface: $clazz. Serialized form is invalid.")
}
}
@ -201,18 +205,20 @@ class EvolutionSerializerViaSetters(
factory: SerializerFactory,
oldReaders: Map<String, EvolutionSerializer.OldParam>,
kotlinConstructor: KFunction<Any>?,
private val setters: Map<String, PropertyAccessor>) : EvolutionSerializer (clazz, factory, oldReaders, kotlinConstructor) {
private val setters: Map<String, PropertyAccessor>) : EvolutionSerializer(clazz, factory, oldReaders, kotlinConstructor) {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any {
if (obj !is List<*>) throw NotSerializableException("Body of described type is unexpected $obj")
val instance : Any = javaConstructor?.newInstance() ?: throw NotSerializableException (
val instance: Any = javaConstructor?.newInstance() ?: throw NotSerializableException(
"Failed to instantiate instance of object $clazz")
// *must* read all the parameters in the order they were serialized
oldReaders.values.zip(obj).forEach {
// if that property still exists on the new object then set it
it.first.property.readProperty(it.second, schemas, input).apply {
it.first.property.readProperty(it.second, schemas, input, context).apply {
setters[it.first.property.name]?.set(instance, this)
}
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.io.NotSerializableException
@ -18,9 +19,6 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
import kotlin.collections.LinkedHashMap
import kotlin.collections.Map
import kotlin.collections.iterator
import kotlin.collections.map
private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *>
@ -28,8 +26,8 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *>
* Serialization / deserialization of certain supported [Map] types.
*/
class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
override val type: Type = (declaredType as? DeserializedParameterizedType) ?:
DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType), factory.classloader)
override val type: Type = (declaredType as? DeserializedParameterizedType)
?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType), factory.classloader)
override val typeDescriptor: Symbol = Symbol.valueOf(
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
@ -67,7 +65,8 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
}
private fun deriveParametrizedType(declaredType: Type, collectionClass: Class<out Map<*, *>>): ParameterizedType =
(declaredType as? ParameterizedType) ?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType, SerializerFactory.AnyType))
(declaredType as? ParameterizedType)
?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType, SerializerFactory.AnyType))
private fun findMostSuitableMapType(actualClass: Class<*>): Class<out Map<*, *>> =
@ -90,6 +89,7 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
data: Data,
type: Type,
output: SerializationOutput,
context: SerializationContext,
debugIndent: Int) = ifThrowsAppend({ declaredType.typeName }) {
obj.javaClass.checkSupportedMapType()
// Write described
@ -98,22 +98,25 @@ class MapSerializer(private val declaredType: ParameterizedType, factory: Serial
data.putMap()
data.enter()
for ((key, value) in obj as Map<*, *>) {
output.writeObjectOrNull(key, data, declaredType.actualTypeArguments[0], debugIndent)
output.writeObjectOrNull(value, data, declaredType.actualTypeArguments[1], debugIndent)
output.writeObjectOrNull(key, data, declaredType.actualTypeArguments[0], context, debugIndent)
output.writeObjectOrNull(value, data, declaredType.actualTypeArguments[1], context, debugIndent)
}
data.exit() // exit map
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any = ifThrowsAppend({ declaredType.typeName }) {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): Any = ifThrowsAppend({ declaredType.typeName }) {
// TODO: General generics question. Do we need to validate that entries in Maps and Collections match the generic type? Is it a security hole?
val entries: Iterable<Pair<Any?, Any?>> = (obj as Map<*, *>).map { readEntry(schemas, input, it) }
val entries: Iterable<Pair<Any?, Any?>> = (obj as Map<*, *>).map { readEntry(schemas, input, it, context) }
concreteBuilder(entries.toMap())
}
private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry<Any?, Any?>) =
input.readObjectOrNull(entry.key, schemas, declaredType.actualTypeArguments[0]) to
input.readObjectOrNull(entry.value, schemas, declaredType.actualTypeArguments[1])
private fun readEntry(schemas: SerializationSchemas, input: DeserializationInput, entry: Map.Entry<Any?, Any?>,
context: SerializationContext
) = input.readObjectOrNull(entry.key, schemas, declaredType.actualTypeArguments[0], context) to
input.readObjectOrNull(entry.value, schemas, declaredType.actualTypeArguments[1], context)
// Cannot use * as a bound for EnumMap and EnumSet since * is not an enum. So, we use a sample enum instead.
// We don't actually care about the type, we just need to make the compiler happier.

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.trace
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.nameForType
@ -67,6 +68,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
data: Data,
type: Type,
output: SerializationOutput,
context: SerializationContext,
debugIndent: Int) = ifThrowsAppend({ clazz.typeName }
) {
if (propertySerializers.size != javaConstructor?.parameterCount &&
@ -82,7 +84,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
// Write list
withList {
propertySerializers.serializationOrder.forEach { property ->
property.getter.writeProperty(obj, this, output, debugIndent + 1)
property.getter.writeProperty(obj, this, output, context, debugIndent + 1)
}
}
}
@ -91,16 +93,17 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
override fun readObject(
obj: Any,
schemas: SerializationSchemas,
input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) {
input: DeserializationInput,
context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) {
if (obj is List<*>) {
if (obj.size > propertySerializers.size) {
throw NotSerializableException("Too many properties in described type $typeName")
}
return if (propertySerializers.byConstructor) {
readObjectBuildViaConstructor(obj, schemas, input)
readObjectBuildViaConstructor(obj, schemas, input, context)
} else {
readObjectBuildViaSetters(obj, schemas, input)
readObjectBuildViaSetters(obj, schemas, input, context)
}
} else {
throw NotSerializableException("Body of described type is unexpected $obj")
@ -110,12 +113,13 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
private fun readObjectBuildViaConstructor(
obj: List<*>,
schemas: SerializationSchemas,
input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) {
input: DeserializationInput,
context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) {
logger.trace { "Calling construction based construction for ${clazz.typeName}" }
return construct(propertySerializers.serializationOrder
.zip(obj)
.map { Pair(it.first.initialPosition, it.first.getter.readProperty(it.second, schemas, input)) }
.map { Pair(it.first.initialPosition, it.first.getter.readProperty(it.second, schemas, input, context)) }
.sortedWith(compareBy({ it.first }))
.map { it.second })
}
@ -123,7 +127,8 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
private fun readObjectBuildViaSetters(
obj: List<*>,
schemas: SerializationSchemas,
input: DeserializationInput): Any = ifThrowsAppend({ clazz.typeName }) {
input: DeserializationInput,
context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) {
logger.trace { "Calling setter based construction for ${clazz.typeName}" }
val instance: Any = javaConstructor?.newInstance() ?: throw NotSerializableException(
@ -133,7 +138,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
// do it in doesn't matter
val propertiesFromBlob = obj
.zip(propertySerializers.serializationOrder)
.map { it.second.getter.readProperty(it.first, schemas, input) }
.map { it.second.getter.readProperty(it.first, schemas, input, context) }
// one by one take a property and invoke the setter on the class
propertySerializers.serializationOrder.zip(propertiesFromBlob).forEach {

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -19,8 +20,8 @@ import java.lang.reflect.Type
*/
sealed class PropertySerializer(val name: String, val propertyReader: PropertyReader, val resolvedType: Type) {
abstract fun writeClassInfo(output: SerializationOutput)
abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int = 0)
abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any?
abstract fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, context: SerializationContext, debugIndent: Int = 0)
abstract fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any?
val type: String = generateType()
val requires: List<String> = generateRequires()
@ -86,12 +87,15 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe
override fun readProperty(
obj: Any?,
schemas: SerializationSchemas,
input: DeserializationInput): Any? = ifThrowsAppend({ nameForDebug }) {
input.readObjectOrNull(obj, schemas, resolvedType)
input: DeserializationInput,
context: SerializationContext): Any? = ifThrowsAppend({ nameForDebug }) {
input.readObjectOrNull(obj, schemas, resolvedType, context)
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) = ifThrowsAppend({ nameForDebug }) {
output.writeObjectOrNull(propertyReader.read(obj), data, resolvedType, debugIndent)
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput,
context: SerializationContext, debugIndent: Int) = ifThrowsAppend({ nameForDebug }
) {
output.writeObjectOrNull(propertyReader.read(obj), data, resolvedType, context, debugIndent)
}
private val nameForDebug = "$name(${resolvedType.typeName})"
@ -106,11 +110,15 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe
resolvedType: Type) : PropertySerializer(name, readMethod, resolvedType) {
override fun writeClassInfo(output: SerializationOutput) {}
override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? {
override fun readProperty(obj: Any?, schemas: SerializationSchemas,
input: DeserializationInput, context: SerializationContext
): Any? {
return if (obj is Binary) obj.array else obj
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) {
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
val value = propertyReader.read(obj)
if (value is ByteArray) {
data.putObject(Binary(value))
@ -122,18 +130,22 @@ sealed class PropertySerializer(val name: String, val propertyReader: PropertyRe
/**
* A property serializer for the AMQP char type, needed as a specialisation as the underlying
* value of the character is stored in numeric UTF-16 form and on deserialisation requires explicit
* value of the character is stored in numeric UTF-16 form and on deserialization requires explicit
* casting back to a char otherwise it's treated as an Integer and a TypeMismatch occurs
*/
class AMQPCharPropertySerializer(name: String, readMethod: PropertyReader) :
PropertySerializer(name, readMethod, Character::class.java) {
override fun writeClassInfo(output: SerializationOutput) {}
override fun readProperty(obj: Any?, schemas: SerializationSchemas, input: DeserializationInput): Any? {
override fun readProperty(obj: Any?, schemas: SerializationSchemas,
input: DeserializationInput, context: SerializationContext
): Any? {
return if (obj == null) null else (obj as Short).toChar()
}
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput, debugIndent: Int) {
override fun writeProperty(obj: Any?, data: Data, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
val input = propertyReader.read(obj)
if (input != null) data.putShort((input as Char).toShort()) else data.putNull()
}

View File

@ -12,12 +12,12 @@ package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.utilities.loggerFor
import java.io.NotSerializableException
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Type
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaGetter
import kotlin.reflect.jvm.kotlinProperty
import java.lang.reflect.Field
abstract class PropertyReader {
abstract fun read(obj: Any?): Any?
@ -151,6 +151,7 @@ class PropertyAccessorGetterSetter(
*/
setter.isAccessible = true
}
/**
* Invokes the setter on the underlying object passing in the serialized value.
*/
@ -172,7 +173,7 @@ class PropertyAccessorConstructor(
* 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 " +
NotSerializableException("Attempting to access a setter on an object being instantiated " +
"via its constructor.")
}
}
@ -197,7 +198,7 @@ abstract class PropertySerializers(
is PropertyAccessorGetterSetter -> PropertySerializersSetter(serializationOrder)
null -> PropertySerializersNoProperties()
else -> {
throw NotSerializableException ("Unknown Property Accessor type, cannot create set")
throw NotSerializableException("Unknown Property Accessor type, cannot create set")
}
}
}
@ -206,7 +207,7 @@ abstract class PropertySerializers(
abstract val byConstructor: Boolean
}
class PropertySerializersNoProperties : PropertySerializers (emptyList()) {
class PropertySerializersNoProperties : PropertySerializers(emptyList()) {
override val byConstructor get() = true
}

View File

@ -105,7 +105,7 @@ data class PropertyDescriptor(var field: Field?, var setter: Method?, var getter
constructor() : this(null, null, null, null)
fun preferredGetter() : Method? = getter ?: iser
fun preferredGetter(): Method? = getter ?: iser
}
object PropertyDescriptorsRegex {
@ -173,8 +173,7 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
// fails the getter doesn't refer to a property directly, but may refer to a constructor
// parameter that shadows a property
val properties =
classProperties[groups[2]!!.value] ?:
classProperties[groups[2]!!.value.decapitalize()] ?:
classProperties[groups[2]!!.value] ?: classProperties[groups[2]!!.value.decapitalize()] ?:
// take into account those constructor properties that don't directly map to a named
// property which are, by default, already added to the map
classProperties.computeIfAbsent(groups[2]!!.value) { PropertyDescriptor() }
@ -255,8 +254,8 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
// We will already have disambiguated getA for property A or a but we still need to cope
// with the case we don't know the case of A when the parameter doesn't match a property
// but has a getter
val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()] ?:
throw NotSerializableException(
val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()]
?: throw NotSerializableException(
"Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"")
// If the property has a getter we'll use that to retrieve it's value from the instance, if it doesn't
@ -277,8 +276,8 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
Pair(PublicPropertyReader(getter), returnType)
} else {
val field = classProperties[name]!!.field ?:
throw NotSerializableException("No property matching constructor parameter named - \"$name\" - " +
val field = classProperties[name]!!.field
?: throw NotSerializableException("No property matching constructor parameter named - \"$name\" - " +
"of \"$clazz\". If using Java, check that you have the -parameters option specified " +
"in the Java compiler. Alternately, provide a proxy serializer " +
"(SerializationCustomSerializer) if recompiling isn't an option")
@ -325,7 +324,7 @@ fun propertiesForSerializationFromSetters(
}
// Make sure the getter returns the same type (within inheritance bounds) the setter accepts.
if (!(TypeToken.of (getter.genericReturnType).isSupertypeOf(setterType))) {
if (!(TypeToken.of(getter.genericReturnType).isSupertypeOf(setterType))) {
throw NotSerializableException("Defined setter for parameter ${property.value.field?.name} " +
"takes parameter of type $setterType yet the defined getter returns a value of type " +
"${getter.returnType} [${getter.genericReturnType}]")

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationEncoding
import net.corda.core.serialization.SerializedBytes
import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding
@ -33,7 +34,10 @@ data class BytesAndSchemas<T : Any>(
* @param serializerFactory This is the factory for [AMQPSerializer] instances and can be shared across multiple
* instances and threads.
*/
open class SerializationOutput @JvmOverloads constructor(internal val serializerFactory: SerializerFactory, private val encoding: SerializationEncoding? = null) {
open class SerializationOutput @JvmOverloads constructor(
internal val serializerFactory: SerializerFactory,
private val encoding: SerializationEncoding? = null
) {
private val objectHistory: MutableMap<Any, Int> = IdentityHashMap()
private val serializerHistory: MutableSet<AMQPSerializer<*>> = LinkedHashSet()
internal val schemaHistory: MutableSet<TypeNotation> = LinkedHashSet()
@ -44,19 +48,18 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
* of AMQP serialization constructed the serialized form.
*/
@Throws(NotSerializableException::class)
fun <T : Any> serialize(obj: T): SerializedBytes<T> {
fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
try {
return _serialize(obj)
return _serialize(obj, context)
} finally {
andFinally()
}
}
@Throws(NotSerializableException::class)
fun <T : Any> serializeAndReturnSchema(obj: T): BytesAndSchemas<T> {
fun <T : Any> serializeAndReturnSchema(obj: T, context: SerializationContext): BytesAndSchemas<T> {
try {
val blob = _serialize(obj)
val blob = _serialize(obj, context)
val schema = Schema(schemaHistory.toList())
return BytesAndSchemas(blob, schema, TransformsSchema.build(schema, serializerFactory))
} finally {
@ -70,11 +73,11 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
schemaHistory.clear()
}
internal fun <T : Any> _serialize(obj: T): SerializedBytes<T> {
internal fun <T : Any> _serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
val data = Data.Factory.create()
data.withDescribed(Envelope.DESCRIPTOR_OBJECT) {
withList {
writeObject(obj, this)
writeObject(obj, this, context)
val schema = Schema(schemaHistory.toList())
writeSchema(schema, this)
writeTransformSchema(TransformsSchema.build(schema, serializerFactory), this)
@ -97,8 +100,8 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
})
}
internal fun writeObject(obj: Any, data: Data) {
writeObject(obj, data, obj.javaClass)
internal fun writeObject(obj: Any, data: Data, context: SerializationContext) {
writeObject(obj, data, obj.javaClass, context)
}
open fun writeSchema(schema: Schema, data: Data) {
@ -109,15 +112,15 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
data.putObject(transformsSchema)
}
internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, debugIndent: Int) {
internal fun writeObjectOrNull(obj: Any?, data: Data, type: Type, context: SerializationContext, debugIndent: Int) {
if (obj == null) {
data.putNull()
} else {
writeObject(obj, data, if (type == SerializerFactory.AnyType) obj.javaClass else type, debugIndent)
writeObject(obj, data, if (type == SerializerFactory.AnyType) obj.javaClass else type, context, debugIndent)
}
}
internal fun writeObject(obj: Any, data: Data, type: Type, debugIndent: Int = 0) {
internal fun writeObject(obj: Any, data: Data, type: Type, context: SerializationContext, debugIndent: Int = 0) {
val serializer = serializerFactory.get(obj.javaClass, type)
if (serializer !in serializerHistory) {
serializerHistory.add(serializer)
@ -126,7 +129,7 @@ open class SerializationOutput @JvmOverloads constructor(internal val serializer
val retrievedRefCount = objectHistory[obj]
if (retrievedRefCount == null) {
serializer.writeObject(obj, data, type, this, debugIndent)
serializer.writeObject(obj, data, type, this, context, debugIndent)
// Important to do it after serialization such that dependent object will have preceding reference numbers
// assigned to them first as they will be first read from the stream on receiving end.
// Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content

View File

@ -15,9 +15,11 @@ import com.google.common.reflect.TypeResolver
import net.corda.core.internal.getStackTraceAsString
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.loggerFor
import net.corda.nodeapi.internal.serialization.carpenter.*
import net.corda.nodeapi.internal.serialization.carpenter.CarpenterMetaSchema
import net.corda.nodeapi.internal.serialization.carpenter.ClassCarpenter
import net.corda.nodeapi.internal.serialization.carpenter.MetaCarpenter
import net.corda.nodeapi.internal.serialization.carpenter.MetaCarpenterException
import org.apache.qpid.proton.amqp.*
import java.io.NotSerializableException
import java.lang.reflect.*
@ -69,8 +71,7 @@ open class SerializerFactory(
get() = classCarpenter.classloader
private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas)
= evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas)
schemas: SerializationSchemas) = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas)
fun getSerializersByDescriptor() = serializersByDescriptor
@ -109,7 +110,8 @@ open class SerializerFactory(
makeMapSerializer(declaredTypeAmended)
}
}
Enum::class.java.isAssignableFrom(actualClass ?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) {
Enum::class.java.isAssignableFrom(actualClass
?: declaredClass) -> serializersByType.computeIfAbsent(actualClass ?: declaredClass) {
whitelist.requireWhitelisted(actualType)
EnumSerializer(actualType, actualClass ?: declaredClass, this)
}
@ -254,8 +256,8 @@ open class SerializerFactory(
} catch (e: MetaCarpenterException) {
// preserve the actual message locally
loggerFor<SerializerFactory>().apply {
error ("${e.message} [hint: enable trace debugging for the stack trace]")
trace (e.getStackTraceAsString())
error("${e.message} [hint: enable trace debugging for the stack trace]")
trace(e.getStackTraceAsString())
}
// prevent carpenter exceptions escaping into the world, convert things into a nice

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext
import org.apache.qpid.proton.amqp.Symbol
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -33,13 +34,16 @@ class SingletonSerializer(override val type: Class<*>, val singleton: Any, facto
output.writeTypeNotations(typeNotation)
}
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput, debugIndent: Int) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext, debugIndent: Int
) {
data.withDescribed(typeNotation.descriptor) {
data.putBoolean(false)
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): Any {
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext
): Any {
return singleton
}
}

View File

@ -284,12 +284,12 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
throw NotSerializableException("Unexpected descriptor ${describedType.descriptor}.")
}
val map = describedType.described as? Map<*, *> ?:
throw NotSerializableException("Transform schema must be encoded as a map")
val map = describedType.described as? Map<*, *>
?: throw NotSerializableException("Transform schema must be encoded as a map")
map.forEach { type ->
val fingerprint = type.key as? String ?:
throw NotSerializableException("Fingerprint must be encoded as a string")
val fingerprint = type.key as? String
?: throw NotSerializableException("Fingerprint must be encoded as a string")
rtn[fingerprint] = EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
@ -298,8 +298,8 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
rtn[fingerprint]!![transform] = mutableListOf()
(transforms as List<*>).forEach {
rtn[fingerprint]!![TransformTypes.newInstance(transformType)]?.add(Transform.newInstance(it)) ?:
throw NotSerializableException("De-serialization error with transform for class "
rtn[fingerprint]!![TransformTypes.newInstance(transformType)]?.add(Transform.newInstance(it))
?: throw NotSerializableException("De-serialization error with transform for class "
+ "${type.key} ${transform.name}")
}
}

View File

@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import net.corda.nodeapi.internal.serialization.amqp.custom.ClassSerializer.ClassProxy
/**
* A serializer for [Class] that uses [ClassProxy] proxy object to write out

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.amqp.Binary
import org.apache.qpid.proton.codec.Data
@ -25,7 +26,9 @@ object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStr
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: InputStream, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: InputStream, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
val startingSize = maxOf(4096, obj.available() + 1)
var buffer = ByteArray(startingSize)
var pos = 0
@ -44,8 +47,10 @@ object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStr
}
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): InputStream {
val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): InputStream {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return bits.inputStream()
}
}

View File

@ -12,12 +12,15 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.MonthDay
/**
* A serializer for [MonthDay] that uses a proxy object to write out the integer form.
*/
class MonthDaySerializer(factory: SerializerFactory) : CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(MonthDay::class.java, MonthDayProxy::class.java, factory) {
class MonthDaySerializer(factory: SerializerFactory)
: CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(
MonthDay::class.java, MonthDayProxy::class.java, factory
) {
override fun toProxy(obj: MonthDay): MonthDayProxy = MonthDayProxy(obj.monthValue.toByte(), obj.dayOfMonth.toByte())
override fun fromProxy(proxy: MonthDayProxy): MonthDay = MonthDay.of(proxy.month.toInt(), proxy.day.toInt())

View File

@ -12,7 +12,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
/**
* A serializer for [OffsetDateTime] that uses a proxy object to write out the date and zone offset.

View File

@ -12,7 +12,9 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.LocalTime
import java.time.OffsetTime
import java.time.ZoneOffset
/**
* A serializer for [OffsetTime] that uses a proxy object to write out the time and zone offset.

View File

@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.Period
/**
* A serializer for [Period] that uses a proxy object to write out the integer form.

View File

@ -11,7 +11,9 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.core.serialization.SerializationContext.UseCase.*
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase.Checkpoint
import net.corda.core.serialization.SerializationContext.UseCase.Storage
import net.corda.nodeapi.internal.serialization.amqp.*
import net.corda.nodeapi.internal.serialization.checkUseCase
import org.apache.qpid.proton.codec.Data
@ -25,13 +27,17 @@ object PrivateKeySerializer : CustomSerializer.Implements<PrivateKey>(PrivateKey
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
checkUseCase(allowedUseCases)
output.writeObject(obj.encoded, data, clazz)
output.writeObject(obj.encoded, data, clazz, context)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PrivateKey {
val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): PrivateKey {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return Crypto.decodePrivateKey(bits)
}
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.crypto.Crypto
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type
@ -22,13 +23,17 @@ import java.security.PublicKey
object PublicKeySerializer : CustomSerializer.Implements<PublicKey>(PublicKey::class.java) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), SerializerFactory.primitiveTypeName(ByteArray::class.java)!!, descriptor, emptyList())))
override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput) {
override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
// TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser.
output.writeObject(obj.encoded, data, clazz)
output.writeObject(obj.encoded, data, clazz, context)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): PublicKey {
val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): PublicKey {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return Crypto.decodePublicKey(bits)
}
}

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
@ -26,12 +27,16 @@ object X509CRLSerializer : CustomSerializer.Implements<X509CRL>(X509CRL::class.j
emptyList()
)))
override fun writeDescribedObject(obj: X509CRL, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
override fun writeDescribedObject(obj: X509CRL, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
output.writeObject(obj.encoded, data, clazz, context)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509CRL {
val bytes = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): X509CRL {
val bytes = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return X509CertificateFactory().delegate.generateCRL(bytes.inputStream()) as X509CRL
}
}

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.serialization.amqp.*
import org.apache.qpid.proton.codec.Data
@ -26,12 +27,16 @@ object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(
emptyList()
)))
override fun writeDescribedObject(obj: X509Certificate, data: Data, type: Type, output: SerializationOutput) {
output.writeObject(obj.encoded, data, clazz)
override fun writeDescribedObject(obj: X509Certificate, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext
) {
output.writeObject(obj.encoded, data, clazz, context)
}
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput): X509Certificate {
val bits = input.readObject(obj, schemas, ByteArray::class.java) as ByteArray
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
context: SerializationContext
): X509Certificate {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return X509CertificateFactory().generateCertificate(bits.inputStream())
}
}

View File

@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.YearMonth
/**
* A serializer for [YearMonth] that uses a proxy object to write out the integer form.

View File

@ -12,7 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.Year
/**
* A serializer for [Year] that uses a proxy object to write out the integer form.

View File

@ -12,7 +12,10 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
import java.time.*
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
/**
* A serializer for [ZonedDateTime] that uses a proxy object to write out the date, time, offset and zone.

View File

@ -12,11 +12,11 @@
package net.corda.nodeapi.internal.serialization.carpenter
import net.corda.core.serialization.SerializationContext
import net.corda.nodeapi.internal.serialization.amqp.CompositeType
import net.corda.nodeapi.internal.serialization.amqp.RestrictedType
import net.corda.nodeapi.internal.serialization.amqp.Field as AMQPField
import net.corda.nodeapi.internal.serialization.amqp.Schema as AMQPSchema
import net.corda.core.serialization.SerializationContext
fun AMQPSchema.carpenterSchema(classloader: ClassLoader): CarpenterMetaSchema {
val rtn = CarpenterMetaSchema.newInstance()
@ -130,7 +130,7 @@ val typeStrToType: Map<Pair<String, Boolean>, Class<out Any?>> = mapOf(
Pair("byte", false) to Byte::class.javaObjectType
)
fun String.stripGenerics() : String = if(this.endsWith('>')) {
fun String.stripGenerics(): String = if (this.endsWith('>')) {
this.substring(0, this.indexOf('<'))
} else this

View File

@ -36,10 +36,10 @@ class CarpenterClassLoader(parentClassLoader: ClassLoader = Thread.currentThread
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
class InterfaceMismatchNonGetterException (val clazz: Class<*>, val method: Method) : InterfaceMismatchException(
class InterfaceMismatchNonGetterException(val clazz: Class<*>, val method: Method) : InterfaceMismatchException(
"Requested interfaces must consist only of methods that start with 'get': ${clazz.name}.${method.name}")
class InterfaceMismatchMissingAMQPFieldException (val clazz: Class<*>, val field: String) : InterfaceMismatchException(
class InterfaceMismatchMissingAMQPFieldException(val clazz: Class<*>, val field: String) : InterfaceMismatchException(
"Interface ${clazz.name} requires a field named $field but that isn't found in the schema or any superclass schemas")
/**

View File

@ -23,7 +23,7 @@ abstract class ClassCarpenterException(msg: String) : CordaRuntimeException(msg)
*/
abstract class InterfaceMismatchException(msg: String) : ClassCarpenterException(msg)
class DuplicateNameException(val name : String) : ClassCarpenterException (
class DuplicateNameException(val name: String) : ClassCarpenterException(
"An attempt was made to register two classes with the name '$name' within the same ClassCarpenter namespace.")
class NullablePrimitiveException(val name: String, val field: Class<out Any>) : ClassCarpenterException(

View File

@ -268,7 +268,7 @@ object NotaryChangeWireTransactionSerializer : Serializer<NotaryChangeWireTransa
}
override fun read(kryo: Kryo, input: Input, type: Class<NotaryChangeWireTransaction>): NotaryChangeWireTransaction {
val components : List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
val components: List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
return NotaryChangeWireTransaction(components)
}
}

View File

@ -10,7 +10,6 @@
package net.corda.nodeapi.internal.serialization.kryo
import java.util.concurrent.ConcurrentHashMap
import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
import com.esotericsoftware.kryo.Kryo
@ -22,14 +21,14 @@ import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.esotericsoftware.kryo.serializers.ClosureSerializer
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.utilities.ByteSequence
import net.corda.core.serialization.*
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.SectionId
import net.corda.nodeapi.internal.serialization.SerializationScheme
import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.SectionId
import java.security.PublicKey
import java.util.concurrent.ConcurrentHashMap
val kryoMagic = CordaSerializationMagic("corda".toByteArray() + byteArrayOf(0, 0))
@ -96,7 +95,8 @@ abstract class AbstractKryoSerializationScheme : SerializationScheme {
}
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
val dataBytes = kryoMagic.consume(byteSequence) ?: throw KryoException("Serialized bytes header does not match expected format.")
val dataBytes = kryoMagic.consume(byteSequence)
?: throw KryoException("Serialized bytes header does not match expected format.")
return context.kryo {
kryoInput(ByteBufferInputStream(dataBytes)) {
val result: T

View File

@ -13,7 +13,10 @@ package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import net.corda.core.internal.LazyPool
import java.io.*
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.io.SequenceInputStream
import java.nio.ByteBuffer
class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) {

View File

@ -24,12 +24,16 @@ import net.corda.core.serialization.SerializeAsToken
*/
class SerializeAsTokenSerializer<T : SerializeAsToken> : Serializer<T>() {
override fun write(kryo: Kryo, output: Output, obj: T) {
kryo.writeClassAndObject(output, obj.toToken(kryo.serializationContext() ?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context")))
kryo.writeClassAndObject(output, obj.toToken(kryo.serializationContext()
?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context")))
}
override fun read(kryo: Kryo, input: Input, type: Class<T>): T {
val token = (kryo.readClassAndObject(input) as? SerializationToken) ?: throw KryoException("Non-token read for tokenized type: ${type.name}")
val fromToken = token.fromToken(kryo.serializationContext() ?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context"))
return type.castIfPossible(fromToken) ?: throw KryoException("Token read ($token) did not return expected tokenized type: ${type.name}")
val token = (kryo.readClassAndObject(input) as? SerializationToken)
?: throw KryoException("Non-token read for tokenized type: ${type.name}")
val fromToken = token.fromToken(kryo.serializationContext()
?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context"))
return type.castIfPossible(fromToken)
?: throw KryoException("Token read ($token) did not return expected tokenized type: ${type.name}")
}
}

View File

@ -11,6 +11,8 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContextKt;
import org.assertj.core.api.Assertions;
import org.junit.Ignore;
import org.junit.Test;
@ -51,7 +53,7 @@ public class ErrorMessageTests {
SerializationOutput ser = new SerializationOutput(factory1);
Assertions.assertThatThrownBy(() -> ser.serialize(new C(1)))
Assertions.assertThatThrownBy(() -> ser.serialize(new C(1), TestSerializationContext.testSerializationContext))
.isInstanceOf(NotSerializableException.class)
.hasMessage(errMsg("a", getClass().getName()));
}

View File

@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.core.serialization.SerializedBytes;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.junit.Test;
import java.io.NotSerializableException;
@ -43,10 +44,10 @@ public class JavaGenericsTest {
new SerializerFingerPrinter());
SerializationOutput ser = new SerializationOutput(factory);
SerializedBytes<?> bytes = ser.serialize(a1);
SerializedBytes<?> bytes = ser.serialize(a1, TestSerializationContext.testSerializationContext);
DeserializationInput des = new DeserializationInput(factory);
A a2 = des.deserialize(bytes, A.class);
A a2 = des.deserialize(bytes, A.class, TestSerializationContext.testSerializationContext);
assertEquals(1, a2.getT());
}
@ -58,13 +59,13 @@ public class JavaGenericsTest {
new EvolutionSerializerGetter(),
new SerializerFingerPrinter());
return (new SerializationOutput(factory)).serialize(a);
return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext);
}
private SerializedBytes<?> forceWildcardSerializeFactory(
A<?> a,
SerializerFactory factory) throws NotSerializableException {
return (new SerializationOutput(factory)).serialize(a);
return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext);
}
private A<?> forceWildcardDeserialize(SerializedBytes<?> bytes) throws NotSerializableException {
@ -75,13 +76,14 @@ public class JavaGenericsTest {
new SerializerFingerPrinter());
DeserializationInput des = new DeserializationInput(factory);
return des.deserialize(bytes, A.class);
return des.deserialize(bytes, A.class, TestSerializationContext.testSerializationContext);
}
private A<?> forceWildcardDeserializeFactory(
SerializedBytes<?> bytes,
SerializerFactory factory) throws NotSerializableException {
return (new DeserializationInput(factory)).deserialize(bytes, A.class);
return (new DeserializationInput(factory)).deserialize(bytes, A.class,
TestSerializationContext.testSerializationContext);
}
@Test

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty;
import net.corda.core.serialization.SerializedBytes;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.assertj.core.api.Assertions;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
@ -39,17 +40,17 @@ class OuterClass1 {
}
public void run() throws NotSerializableException {
SerializedBytes b = ser.serialize(new DummyState());
desExisting.deserialize(b, DummyState.class);
desRegen.deserialize(b, DummyState.class);
SerializedBytes b = ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext);
desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
}
}
class Inherator1 extends OuterClass1 {
public void iRun() throws NotSerializableException {
SerializedBytes b = ser.serialize(new DummyState());
desExisting.deserialize(b, DummyState.class);
desRegen.deserialize(b, DummyState.class);
SerializedBytes b = ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext);
desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
}
}
@ -85,17 +86,17 @@ class OuterClass2 {
}
public void run() throws NotSerializableException {
SerializedBytes b = ser.serialize(new DummyState(12));
desExisting.deserialize(b, DummyState.class);
desRegen.deserialize(b, DummyState.class);
SerializedBytes b = ser.serialize(new DummyState(12), TestSerializationContext.testSerializationContext);
desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
}
}
class Inherator2 extends OuterClass2 {
public void iRun() throws NotSerializableException {
SerializedBytes b = ser.serialize(new DummyState(12));
desExisting.deserialize(b, DummyState.class);
desRegen.deserialize(b, DummyState.class);
SerializedBytes b = ser.serialize(new DummyState(12), TestSerializationContext.testSerializationContext);
desExisting.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
desRegen.deserialize(b, DummyState.class, TestSerializationContext.testSerializationContext);
}
}
@ -120,7 +121,7 @@ abstract class AbstractClass2 {
class Inherator4 extends AbstractClass2 {
public void run() throws NotSerializableException {
ser.serialize(new DummyState());
ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext);
}
}
@ -139,7 +140,7 @@ class Inherator5 extends AbstractClass3 {
new SerializerFingerPrinter());
SerializationOutput ser = new SerializationOutput(factory);
ser.serialize(new DummyState());
ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext);
}
}
@ -159,7 +160,7 @@ class Inherator6 extends AbstractClass3 {
new SerializerFingerPrinter());
SerializationOutput ser = new SerializationOutput(factory);
ser.serialize(new Wrapper(new DummyState()));
ser.serialize(new Wrapper(new DummyState()), TestSerializationContext.testSerializationContext);
}
}

View File

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.assertj.core.api.Assertions;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
@ -38,7 +39,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase {
SerializationOutput ser = new SerializationOutput(factory);
Assertions.assertThatThrownBy(() -> ser.serialize(new DummyState())).isInstanceOf(
Assertions.assertThatThrownBy(() -> ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext)).isInstanceOf(
NotSerializableException.class).hasMessageContaining(
"has synthetic fields and is likely a nested inner class");
}
@ -50,7 +51,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase {
new SerializerFingerPrinter());
SerializationOutput ser = new SerializationOutput(factory);
Assertions.assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()))).isInstanceOf(
Assertions.assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf(
NotSerializableException.class).hasMessageContaining(
"has synthetic fields and is likely a nested inner class");
}
@ -63,7 +64,7 @@ public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase {
SerializationOutput ser = new SerializationOutput(factory1);
Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper<ContractState> (new DummyState()))).isInstanceOf(
Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper<ContractState> (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf(
NotSerializableException.class).hasMessageContaining(
"has synthetic fields and is likely a nested inner class");
}

View File

@ -11,6 +11,7 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.junit.Test;
import static org.junit.Assert.*;
@ -93,7 +94,7 @@ public class JavaPrivatePropertyTests {
DeserializationInput des = new DeserializationInput(factory);
B b = new B(true);
B b2 = des.deserialize(ser.serialize(b), B.class);
B b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B.class, TestSerializationContext.testSerializationContext);
assertEquals (b.b, b2.b);
}
@ -108,7 +109,7 @@ public class JavaPrivatePropertyTests {
B2 b = new B2();
b.setB(false);
B2 b2 = des.deserialize(ser.serialize(b), B2.class);
B2 b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B2.class, TestSerializationContext.testSerializationContext);
assertEquals (b.b, b2.b);
}
@ -122,7 +123,7 @@ public class JavaPrivatePropertyTests {
B3 b = new B3();
b.setB(false);
B3 b2 = des.deserialize(ser.serialize(b), B3.class);
B3 b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B3.class, TestSerializationContext.testSerializationContext);
// since we can't find a getter for b (isb != isB) then we won't serialize that parameter
assertEquals (null, b2.b);
@ -138,7 +139,7 @@ public class JavaPrivatePropertyTests {
C3 c = new C3();
c.setA(12345);
C3 c2 = des.deserialize(ser.serialize(c), C3.class);
C3 c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C3.class, TestSerializationContext.testSerializationContext);
assertEquals (c.a, c2.a);
}
@ -153,7 +154,7 @@ public class JavaPrivatePropertyTests {
DeserializationInput des = new DeserializationInput(factory);
C c = new C("dripping taps");
C c2 = des.deserialize(ser.serialize(c), C.class);
C c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C.class, TestSerializationContext.testSerializationContext);
assertEquals (c.a, c2.a);
@ -185,7 +186,7 @@ public class JavaPrivatePropertyTests {
DeserializationInput des = new DeserializationInput(factory);
C2 c = new C2("dripping taps");
C2 c2 = des.deserialize(ser.serialize(c), C2.class);
C2 c2 = des.deserialize(ser.serialize(c, TestSerializationContext.testSerializationContext), C2.class, TestSerializationContext.testSerializationContext);
assertEquals (c.a, c2.a);

View File

@ -10,6 +10,7 @@
package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.junit.Test;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
@ -43,6 +44,6 @@ public class JavaSerialiseEnumTests {
new EvolutionSerializerGetter(),
new SerializerFingerPrinter());
SerializationOutput ser = new SerializationOutput(factory1);
SerializedBytes<Object> bytes = ser.serialize(bra);
SerializedBytes<Object> bytes = ser.serialize(bra, TestSerializationContext.testSerializationContext);
}
}

View File

@ -16,6 +16,7 @@ import net.corda.core.identity.AbstractParty;
import net.corda.core.serialization.ConstructorForDeserialization;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.core.serialization.SerializedBytes;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.apache.qpid.proton.codec.DecoderImpl;
import org.apache.qpid.proton.codec.EncoderImpl;
import org.jetbrains.annotations.NotNull;
@ -199,7 +200,7 @@ public class JavaSerializationOutputTests {
evolutionSerialiserGetter,
fingerPrinter);
SerializationOutput ser = new SerializationOutput(factory1);
SerializedBytes<Object> bytes = ser.serialize(obj);
SerializedBytes<Object> bytes = ser.serialize(obj, TestSerializationContext.testSerializationContext);
DecoderImpl decoder = new DecoderImpl();
@ -219,13 +220,15 @@ public class JavaSerializationOutputTests {
assertTrue(result != null);
DeserializationInput des = new DeserializationInput(factory2);
Object desObj = des.deserialize(bytes, Object.class);
Object desObj = des.deserialize(bytes, Object.class, TestSerializationContext.testSerializationContext);
assertTrue(Objects.deepEquals(obj, desObj));
// Now repeat with a re-used factory
SerializationOutput ser2 = new SerializationOutput(factory1);
DeserializationInput des2 = new DeserializationInput(factory1);
Object desObj2 = des2.deserialize(ser2.serialize(obj), Object.class);
Object desObj2 = des2.deserialize(ser2.serialize(obj, TestSerializationContext.testSerializationContext),
Object.class, TestSerializationContext.testSerializationContext);
assertTrue(Objects.deepEquals(obj, desObj2));
// TODO: check schema is as expected
return desObj2;

View File

@ -13,6 +13,7 @@ package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.core.serialization.CordaSerializable;
import net.corda.core.serialization.SerializedBytes;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.junit.Assert;
import org.junit.Test;
@ -142,9 +143,9 @@ public class ListsSerializationJavaTest {
evolutionSerializerGetter,
fingerPrinter);
SerializationOutput ser = new SerializationOutput(factory1);
SerializedBytes<Object> bytes = ser.serialize(container);
SerializedBytes<Object> bytes = ser.serialize(container, TestSerializationContext.testSerializationContext);
DeserializationInput des = new DeserializationInput(factory1);
T deserialized = des.deserialize(bytes, clazz);
T deserialized = des.deserialize(bytes, clazz, TestSerializationContext.testSerializationContext);
Assert.assertEquals(container, deserialized);
}
}

View File

@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp;
import net.corda.core.serialization.SerializedBytes;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import static org.junit.Assert.*;
@ -142,7 +143,7 @@ public class SetterConstructorTests {
c1.setA(1);
c1.setB(2);
c1.setC(3);
Schema schemas = ser.serializeAndReturnSchema(c1).component2();
Schema schemas = ser.serializeAndReturnSchema(c1, TestSerializationContext.testSerializationContext).component2();
assertEquals(1, schemas.component1().size());
assertEquals(this.getClass().getName() + "$C", schemas.component1().get(0).getName());
@ -157,7 +158,7 @@ public class SetterConstructorTests {
C2 c2 = new C2();
c2.setA(1);
c2.setB(2);
schemas = ser.serializeAndReturnSchema(c2).component2();
schemas = ser.serializeAndReturnSchema(c2, TestSerializationContext.testSerializationContext).component2();
assertEquals(1, schemas.component1().size());
assertEquals(this.getClass().getName() + "$C2", schemas.component1().get(0).getName());
@ -174,7 +175,7 @@ public class SetterConstructorTests {
c3.setA(1);
c3.setB(2);
c3.setC(3);
schemas = ser.serializeAndReturnSchema(c3).component2();
schemas = ser.serializeAndReturnSchema(c3, TestSerializationContext.testSerializationContext).component2();
assertEquals(1, schemas.component1().size());
assertEquals(this.getClass().getName() + "$C3", schemas.component1().get(0).getName());
@ -190,7 +191,7 @@ public class SetterConstructorTests {
c4.setA(1);
c4.setB(2);
c4.setC(3);
schemas = ser.serializeAndReturnSchema(c4).component2();
schemas = ser.serializeAndReturnSchema(c4, TestSerializationContext.testSerializationContext).component2();
assertEquals(1, schemas.component1().size());
assertEquals(this.getClass().getName() + "$C4", schemas.component1().get(0).getName());
@ -222,9 +223,9 @@ public class SetterConstructorTests {
cPre1.setB(b);
cPre1.setC(c);
SerializedBytes bytes = new SerializationOutput(factory1).serialize(cPre1);
SerializedBytes bytes = new SerializationOutput(factory1).serialize(cPre1, TestSerializationContext.testSerializationContext);
C cPost1 = new DeserializationInput(factory1).deserialize(bytes, C.class);
C cPost1 = new DeserializationInput(factory1).deserialize(bytes, C.class, TestSerializationContext.testSerializationContext);
assertEquals(a, cPost1.a);
assertEquals(b, cPost1.b);
@ -234,8 +235,8 @@ public class SetterConstructorTests {
cPre2.setA(1);
cPre2.setB(2);
C2 cPost2 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre2),
C2.class);
C2 cPost2 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre2, TestSerializationContext.testSerializationContext),
C2.class, TestSerializationContext.testSerializationContext);
assertEquals(a, cPost2.a);
assertEquals(b, cPost2.b);
@ -249,8 +250,9 @@ public class SetterConstructorTests {
cPre3.setB(2);
cPre3.setC(3);
C3 cPost3 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre3),
C3.class);
C3 cPost3 = new DeserializationInput(factory1).deserialize(
new SerializationOutput(factory1).serialize(cPre3, TestSerializationContext.testSerializationContext),
C3.class, TestSerializationContext.testSerializationContext);
assertEquals(a, cPost3.a);
@ -263,8 +265,11 @@ public class SetterConstructorTests {
cPre4.setB(2);
cPre4.setC(3);
C4 cPost4 = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cPre4),
C4.class);
C4 cPost4 = new DeserializationInput(factory1).deserialize(
new SerializationOutput(factory1).serialize(cPre4,
TestSerializationContext.testSerializationContext),
C4.class,
TestSerializationContext.testSerializationContext);
assertEquals(0, cPost4.a);
assertEquals(0, cPost4.b);
@ -290,8 +295,10 @@ public class SetterConstructorTests {
o.setB("World");
o.setC(i2);
Outer post = new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(o),
Outer.class);
Outer post = new DeserializationInput(factory1).deserialize(
new SerializationOutput(factory1).serialize(
o, TestSerializationContext.testSerializationContext),
Outer.class, TestSerializationContext.testSerializationContext);
assertEquals("Hello", post.a.a);
assertEquals("World", post.b);
@ -300,7 +307,7 @@ public class SetterConstructorTests {
}
@Test
public void typeMistmatch() throws NotSerializableException {
public void typeMistmatch() {
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
SerializerFactory factory1 = new SerializerFactory(
@ -313,12 +320,13 @@ public class SetterConstructorTests {
tm.setA(10);
assertEquals("10", tm.getA());
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf (
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm,
TestSerializationContext.testSerializationContext)).isInstanceOf (
NotSerializableException.class);
}
@Test
public void typeMistmatch2() throws NotSerializableException {
public void typeMistmatch2() {
EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
FingerPrinter fingerPrinter = new SerializerFingerPrinter();
SerializerFactory factory1 = new SerializerFactory(
@ -331,7 +339,8 @@ public class SetterConstructorTests {
tm.setA("10");
assertEquals((Integer)10, tm.getA());
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm)).isInstanceOf(
Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm,
TestSerializationContext.testSerializationContext)).isInstanceOf(
NotSerializableException.class);
}
@ -357,6 +366,10 @@ public class SetterConstructorTests {
// if we've got super / sub types on the setter vs the underlying type the wrong way around this will
// explode. See CORDA-1229 (https://r3-cev.atlassian.net/browse/CORDA-1229)
new DeserializationInput(factory1).deserialize(new SerializationOutput(factory1).serialize(cil), CIntList.class);
new DeserializationInput(factory1).deserialize(
new SerializationOutput(factory1).serialize(
cil, TestSerializationContext.testSerializationContext),
CIntList.class,
TestSerializationContext.testSerializationContext);
}
}

View File

@ -0,0 +1,24 @@
package net.corda.nodeapi.internal.serialization.amqp.testutils;
import net.corda.core.serialization.SerializationContext;
import net.corda.core.utilities.ByteSequence;
import net.corda.nodeapi.internal.serialization.AllWhitelist;
import net.corda.nodeapi.internal.serialization.SerializationContextImpl;
import java.util.HashMap;
import java.util.Map;
public class TestSerializationContext {
static private Map<Object, Object> serializationProperties = new HashMap<Object, Object>();
public static SerializationContext testSerializationContext = new SerializationContextImpl(
ByteSequence.of(new byte[] { 'c', 'o', 'r', 'd', 'a', (byte)0, (byte)0, (byte)1}),
ClassLoader.getSystemClassLoader(),
AllWhitelist.INSTANCE,
serializationProperties,
false,
SerializationContext.UseCase.Testing,
null);
}

View File

@ -1,42 +0,0 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.nodeapi.internal.serialization.amqp
import org.apache.qpid.proton.codec.Data
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(),
EvolutionSerializerGetterTesting())
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
class TestSerializationOutput(
private val verbose: Boolean,
serializerFactory: SerializerFactory = testDefaultFactory())
: SerializationOutput(serializerFactory) {
override fun writeSchema(schema: Schema, data: Data) {
if (verbose) println(schema)
super.writeSchema(schema, data)
}
override fun writeTransformSchema(transformsSchema: TransformsSchema, data: Data) {
if(verbose) {
println ("Writing Transform Schema")
println (transformsSchema)
}
super.writeTransformSchema(transformsSchema, data)
}
}
fun testName(): String = Thread.currentThread().stackTrace[2].methodName

View File

@ -13,9 +13,15 @@ package net.corda.nodeapi.internal.serialization.amqp
import org.junit.Test
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory
import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import kotlin.test.assertEquals
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class CorDappSerializerTests {
data class NeedsProxy (val a: String)

View File

@ -11,10 +11,17 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.CordaSerializable
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryWithWhitelist
import net.corda.nodeapi.internal.serialization.amqp.testutils.testName
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class DeserializeAndReturnEnvelopeTests {
// the 'this' reference means we can't just move this to the common test utils

View File

@ -10,9 +10,15 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import org.assertj.core.api.Assertions
import org.junit.Test
import java.util.*
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class DeserializeMapTests {
companion object {

View File

@ -11,9 +11,16 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryWithWhitelist
import org.junit.Test
import kotlin.test.*
import net.corda.nodeapi.internal.serialization.carpenter.*
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
class DeserializeNeedingCarpentryOfEnumsTest : AmqpCarpenterBase(AllWhitelist) {
companion object {

View File

@ -11,9 +11,15 @@
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
import org.junit.Test
import kotlin.test.*
import net.corda.nodeapi.internal.serialization.carpenter.*
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
// These tests work by having the class carpenter build the classes we serialise and then deserialise. Because
// those classes don't exist within the system's Class Loader the deserialiser will be forced to carpent

Some files were not shown because too many files have changed in this diff Show More