mirror of
https://github.com/corda/corda.git
synced 2025-02-07 11:30:22 +00:00
Merge remote-tracking branch 'open/master' into kat-merge-20180508
This commit is contained in:
commit
406cdf39bb
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -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" />
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -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
|
||||
|
102
docs/source/cipher-suites.rst
Normal file
102
docs/source/cipher-suites.rst
Normal 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. | | |
|
||||
+-------------------------+-------------------------------------------------------------+-----+-----------------------+
|
@ -8,3 +8,4 @@ Corda networks
|
||||
permissioning
|
||||
network-map
|
||||
versioning
|
||||
cipher-suites
|
||||
|
@ -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.
|
||||
|
52
experimental/blobinspector/build.gradle
Normal file
52
experimental/blobinspector/build.gradle
Normal 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'
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package net.corda.blobinspector
|
||||
|
||||
class MalformedBlob(msg: String) : Exception(msg)
|
@ -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()
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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())
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
))
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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(),
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 -> {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}
|
@ -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'")
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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<*>
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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}]")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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())
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
||||
/**
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user