diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt index d6538d1ffc..389609f84c 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt @@ -50,7 +50,7 @@ class AMQPClientSerializationScheme( target == SerializationContext.UseCase.RPCClient || target == SerializationContext.UseCase.P2P) override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory { - return SerializerFactory(context.whitelist, ClassLoader.getSystemClassLoader()).apply { + return SerializerFactory(context.whitelist, ClassLoader.getSystemClassLoader(), context.lenientCarpenterEnabled).apply { register(RpcClientObservableSerializer) register(RpcClientCordaFutureSerializer(this)) register(RxNotificationSerializer(this)) diff --git a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt index 7e92f3cf15..cb6a4f86d0 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/SerializationAPI.kt @@ -142,6 +142,14 @@ interface SerializationContext { * otherwise they appear as new copies of the object. */ val objectReferencesEnabled: Boolean + /** + * If true the carpenter will happily synthesis classes that implement interfaces containing methods that are not + * getters for any AMQP fields. Invoking these methods will throw an [AbstractMethodError]. If false then an exception + * will be thrown during deserialization instead. + * + * The default is false. + */ + val lenientCarpenterEnabled: Boolean /** * The use case we are serializing or deserializing for. See [UseCase]. */ @@ -157,6 +165,12 @@ interface SerializationContext { */ fun withoutReferences(): SerializationContext + /** + * Return a new context based on this one but with a lenient carpenter. + * @see lenientCarpenterEnabled + */ + fun withLenientCarpenter(): SerializationContext + /** * Helper method to return a new context based on this context with the deserialization class loader changed. */ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 131b06a46e..3381a3fd01 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -7,6 +7,10 @@ release, see :doc:`upgrade-notes`. Unreleased ---------- +* The class carpenter has a "lenient" mode where it will, during deserialisation, happily synthesis classes that implement + interfaces that will have unimplemented methods. This is useful, for example, for object viewers. This can be turned on + with ``SerializationContext.withLenientCarpenter``. + * Introduced a grace period before the initial node registration fails if the node cannot connect to the Doorman. It retries 10 times with a 1 minute interval in between each try. At the moment this is not configurable. diff --git a/docs/source/serialization.rst b/docs/source/serialization.rst index 5a26d81cec..2ae4495202 100644 --- a/docs/source/serialization.rst +++ b/docs/source/serialization.rst @@ -557,6 +557,11 @@ without the supporting classes being present on the classpath. This can be usef be able to use reflection over the deserialized data, for scripting languages that run on the JVM, and also for ensuring classes not on the classpath can be deserialized without loading potentially malicious code. +If the original class implements some interfaces then the carpenter will make sure that all of the interface methods are +backed by feilds. If that's not the case then an exception will be thrown during deserialization. This check can +be turned off with ``SerializationContext.withLenientCarpenter``. This can be useful if only the field getters are needed, +say in an object viewer. + Possible future enhancements include: #. Java singleton support. We will add support for identifying classes which are singletons and identifying the diff --git a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt b/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt index 70bcaaf0da..2f28d692a2 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt +++ b/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt @@ -26,15 +26,13 @@ class AMQPServerSerializationScheme( throw UnsupportedOperationException() } - override fun rpcServerSerializerFactory(context: SerializationContext) = - SerializerFactory( - context.whitelist, - context.deserializationClassLoader - ).apply { + override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory { + return SerializerFactory(context.whitelist, context.deserializationClassLoader, context.lenientCarpenterEnabled).apply { register(RpcServerObservableSerializer()) register(RpcServerCordaFutureSerializer(this)) register(RxNotificationSerializer(this)) } + } override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean { return canDeserializeVersion(magic) && diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt index f37091df49..c3f9f1e31e 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/SerializationScheme.kt @@ -30,7 +30,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override val objectReferencesEnabled: Boolean, override val useCase: SerializationContext.UseCase, override val encoding: SerializationEncoding?, - override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist) : SerializationContext { + override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist, + override val lenientCarpenterEnabled: Boolean = false) : SerializationContext { private val builder = AttachmentsClassLoaderBuilder(properties, deserializationClassLoader) /** @@ -52,6 +53,8 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe return copy(objectReferencesEnabled = false) } + override fun withLenientCarpenter(): SerializationContext = copy(lenientCarpenterEnabled = true) + override fun withClassLoader(classLoader: ClassLoader): SerializationContext { return copy(deserializationClassLoader = classLoader) } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt index ac5aab1ed8..aacde49dac 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPSerializerFactories.kt @@ -7,6 +7,7 @@ import net.corda.core.serialization.SerializationContext fun createSerializerFactoryFactory(): SerializerFactoryFactory = SerializerFactoryFactoryImpl() open class SerializerFactoryFactoryImpl : SerializerFactoryFactory { - override fun make(context: SerializationContext) = - SerializerFactory(context.whitelist, context.deserializationClassLoader) + override fun make(context: SerializationContext): SerializerFactory { + return SerializerFactory(context.whitelist, context.deserializationClassLoader, context.lenientCarpenterEnabled) + } } diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt index 97ff897798..aa00063ad3 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/ObjectSerializer.kt @@ -7,6 +7,8 @@ import net.corda.serialization.internal.amqp.SerializerFactory.Companion.nameFor import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException +import java.lang.reflect.Constructor +import java.lang.reflect.InvocationTargetException import java.lang.reflect.Type import kotlin.reflect.jvm.javaConstructor @@ -29,8 +31,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS private val typeName = nameForType(clazz) - override val typeDescriptor = Symbol.valueOf( - "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!! + override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") // We restrict to only those annotated or whitelisted private val interfaces = interfacesForSerialization(clazz, factory) @@ -119,7 +120,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS context: SerializationContext): Any = ifThrowsAppend({ clazz.typeName }) { logger.trace { "Calling setter based construction for ${clazz.typeName}" } - val instance: Any = javaConstructor?.newInstance() ?: throw NotSerializableException( + val instance: Any = javaConstructor?.newInstanceUnwrapped() ?: throw NotSerializableException( "Failed to instantiate instance of object $clazz") // read the properties out of the serialised form, since we're invoking the setters the order we @@ -153,7 +154,15 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS + "serialized properties.") } - return javaConstructor?.newInstance(*properties.toTypedArray()) + return javaConstructor?.newInstanceUnwrapped(*properties.toTypedArray()) ?: throw NotSerializableException("Attempt to deserialize an interface: $clazz. Serialized form is invalid.") } + + private fun Constructor.newInstanceUnwrapped(vararg args: Any?): T { + try { + return newInstance(*args) + } catch (e: InvocationTargetException) { + throw e.cause!! + } + } } \ No newline at end of file diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt index 32c958d0d2..b45336bc2c 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt @@ -8,6 +8,7 @@ import net.corda.core.StubOutForDJVM import net.corda.core.internal.kotlinObjectInstance import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.ClassWhitelist +import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug import net.corda.core.utilities.loggerFor import net.corda.core.utilities.trace @@ -53,40 +54,43 @@ open class SerializerFactory( private val serializersByType: MutableMap>, val serializersByDescriptor: MutableMap>, private val customSerializers: MutableList, - val transformsCache: MutableMap>>) { + val transformsCache: MutableMap>> +) { @DeleteForDJVM constructor(whitelist: ClassWhitelist, classCarpenter: ClassCarpenter, evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(), fingerPrinter: FingerPrinter = SerializerFingerPrinter() - ) : this(whitelist, classCarpenter, evolutionSerializerGetter, fingerPrinter, - serializersByType = ConcurrentHashMap(), - serializersByDescriptor = ConcurrentHashMap(), - customSerializers = CopyOnWriteArrayList(), - transformsCache = ConcurrentHashMap()) + ) : this( + whitelist, + classCarpenter, + evolutionSerializerGetter, + fingerPrinter, + ConcurrentHashMap(), + ConcurrentHashMap(), + CopyOnWriteArrayList(), + ConcurrentHashMap() + ) @DeleteForDJVM constructor(whitelist: ClassWhitelist, classLoader: ClassLoader, + lenientCarpenter: Boolean = false, evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(), fingerPrinter: FingerPrinter = SerializerFingerPrinter() - ) : this(whitelist, ClassCarpenterImpl(classLoader, whitelist), evolutionSerializerGetter, fingerPrinter, - serializersByType = ConcurrentHashMap(), - serializersByDescriptor = ConcurrentHashMap(), - customSerializers = CopyOnWriteArrayList(), - transformsCache = ConcurrentHashMap()) + ) : this(whitelist, ClassCarpenterImpl(classLoader, whitelist, lenientCarpenter), evolutionSerializerGetter, fingerPrinter) init { fingerPrinter.setOwner(this) } - val classloader: ClassLoader - get() = classCarpenter.classloader + val classloader: ClassLoader get() = classCarpenter.classloader - private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer, - schemas: SerializationSchemas) = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) - - private val logger = loggerFor() + private fun getEvolutionSerializer(typeNotation: TypeNotation, + newSerializer: AMQPSerializer, + schemas: SerializationSchemas): AMQPSerializer { + return evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) + } /** * Look up, and manufacture if necessary, a serializer for the given type. @@ -380,6 +384,8 @@ open class SerializerFactory( } companion object { + private val logger = contextLogger() + fun isPrimitive(type: Type): Boolean = primitiveTypeName(type) != null fun primitiveTypeName(type: Type): String? { @@ -469,4 +475,3 @@ open class SerializerFactory( override fun toString(): String = "?" } } - diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/AMQPSchemaExtensions.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/AMQPSchemaExtensions.kt index 174a4f666b..37736abcde 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/AMQPSchemaExtensions.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/AMQPSchemaExtensions.kt @@ -63,7 +63,7 @@ fun CompositeType.carpenterSchema(classloader: ClassLoader, } try { - providesList.add(classloader.loadClass(it)) + providesList.add(classloader.loadClass(it.stripGenerics())) } catch (e: ClassNotFoundException) { carpenterSchemas.addDepPair(this, name, it) isCreatable = false diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt index 7545d97c47..742f34482a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt @@ -6,6 +6,8 @@ import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.debug import org.objectweb.asm.ClassWriter import org.objectweb.asm.MethodVisitor import org.objectweb.asm.Opcodes.* @@ -102,8 +104,9 @@ interface ClassCarpenter { * Equals/hashCode methods are not yet supported. */ @DeleteForDJVM -class ClassCarpenterImpl(cl: ClassLoader, override val whitelist: ClassWhitelist) : ClassCarpenter { - constructor(whitelist: ClassWhitelist) : this(Thread.currentThread().contextClassLoader, whitelist) +class ClassCarpenterImpl(cl: ClassLoader = Thread.currentThread().contextClassLoader, + override val whitelist: ClassWhitelist, + private val lenient: Boolean = false) : ClassCarpenter { // TODO: Generics. // TODO: Sandbox the generated code when a security manager is in use. @@ -439,25 +442,36 @@ class ClassCarpenterImpl(cl: ClassLoader, override val whitelist: ClassWhitelist // actually called, which is a bit too dynamic for my tastes. val allFields = schema.fieldsIncludingSuperclasses() for (itf in schema.interfaces) { - itf.methods.forEach { - val fieldNameFromItf = when { - it.name.startsWith("get") -> it.name.substring(3).decapitalize() - else -> throw InterfaceMismatchNonGetterException(itf, it) + methodLoop@ + for (method in itf.methods) { + val fieldNameFromItf = if (method.name.startsWith("get")) { + method.name.substring(3).decapitalize() + } else if (lenient) { + logger.debug { "Ignoring interface $method which is not a getter" } + continue@methodLoop + } else { + throw InterfaceMismatchNonGetterException(itf, method) } // If we're trying to carpent a class that prior to serialisation / deserialization // was made by a carpenter then we can ignore this (it will implement a plain get // method from SimpleFieldAccess). - if (fieldNameFromItf.isEmpty() && SimpleFieldAccess::class.java in schema.interfaces) return@forEach + if (fieldNameFromItf.isEmpty() && SimpleFieldAccess::class.java in schema.interfaces) continue@methodLoop if ((schema is ClassSchema) and (fieldNameFromItf !in allFields)) { - throw InterfaceMismatchMissingAMQPFieldException(itf, fieldNameFromItf) + if (lenient) { + logger.debug { "Ignoring interface $method which is not backed by an AMQP field" } + } else { + throw InterfaceMismatchMissingAMQPFieldException(itf, fieldNameFromItf) + } } } } } companion object { + private val logger = contextLogger() + @JvmStatic @Suppress("UNUSED") fun getField(obj: Any, name: String): Any? = obj.javaClass.getMethod("get" + name.capitalize()).invoke(obj) diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/ErrorMessageTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/ErrorMessageTests.java index 24a64ff8fb..df9cb5b046 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/ErrorMessageTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/ErrorMessageTests.java @@ -8,6 +8,9 @@ import org.junit.Test; import java.io.NotSerializableException; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; +import static org.assertj.core.api.Assertions.*; + @Ignore("Current behaviour allows for the serialization of objects with private members, this will be disallowed at some point in the future") public class ErrorMessageTests { private String errMsg(String property, String testname) { @@ -32,19 +35,10 @@ public class ErrorMessageTests { @Test public void testJavaConstructorAnnotations() { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializationOutput ser = new SerializationOutput(testDefaultFactory()); - SerializationOutput ser = new SerializationOutput(factory1); - - Assertions.assertThatThrownBy(() -> ser.serialize(new C(1), TestSerializationContext.testSerializationContext)) + assertThatThrownBy(() -> ser.serialize(new C(1), TestSerializationContext.testSerializationContext)) .isInstanceOf(NotSerializableException.class) .hasMessage(errMsg("a", getClass().getName())); } - } diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaCustomSerializerTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaCustomSerializerTests.java index b6248d9c16..8c1bd4e665 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaCustomSerializerTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaCustomSerializerTests.java @@ -1,7 +1,6 @@ package net.corda.serialization.internal.amqp; import net.corda.core.serialization.SerializationCustomSerializer; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.junit.Test; @@ -9,6 +8,8 @@ import java.io.NotSerializableException; import java.util.ArrayList; import java.util.List; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; + public class JavaCustomSerializerTests { /** * The class lacks a public constructor that takes parameters it can associate @@ -87,10 +88,8 @@ public class JavaCustomSerializerTests { } @Test - public void serializeExample() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void serializeExample() throws NotSerializableException { + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); List l = new ArrayList(2); @@ -102,7 +101,5 @@ public class JavaCustomSerializerTests { factory.registerExternal(ccs); ser.serialize(e, TestSerializationContext.testSerializationContext); - - } } diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaGenericsTest.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaGenericsTest.java index d3ab33fbc0..d788948b98 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaGenericsTest.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaGenericsTest.java @@ -1,11 +1,11 @@ package net.corda.serialization.internal.amqp; import net.corda.core.serialization.SerializedBytes; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.junit.Test; import java.io.NotSerializableException; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; import static org.jgroups.util.Util.assertEquals; public class JavaGenericsTest { @@ -27,11 +27,7 @@ public class JavaGenericsTest { public void basicGeneric() throws NotSerializableException { A a1 = new A(1); - SerializerFactory factory = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); SerializedBytes bytes = ser.serialize(a1, TestSerializationContext.testSerializationContext); @@ -43,13 +39,9 @@ public class JavaGenericsTest { } private SerializedBytes forceWildcardSerialize(A a) throws NotSerializableException { - SerializerFactory factory = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); - return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext); + return (new SerializationOutput(factory)).serialize(a, TestSerializationContext.testSerializationContext); } private SerializedBytes forceWildcardSerializeFactory( @@ -59,11 +51,7 @@ public class JavaGenericsTest { } private A forceWildcardDeserialize(SerializedBytes bytes) throws NotSerializableException { - SerializerFactory factory = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); DeserializationInput des = new DeserializationInput(factory); return des.deserialize(bytes, A.class, TestSerializationContext.testSerializationContext); @@ -85,11 +73,7 @@ public class JavaGenericsTest { @Test public void forceWildcardSharedFactory() throws NotSerializableException { - SerializerFactory factory = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializedBytes bytes = forceWildcardSerializeFactory(new A(new Inner(29)), factory); Inner i = (Inner)forceWildcardDeserializeFactory(bytes, factory).getT(); diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedClassesTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedClassesTests.java index 40fdc40cfe..603f6c1590 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedClassesTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedClassesTests.java @@ -4,28 +4,25 @@ import com.google.common.collect.ImmutableList; import net.corda.core.contracts.ContractState; import net.corda.core.identity.AbstractParty; import net.corda.core.serialization.SerializedBytes; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; -import org.assertj.core.api.Assertions; import org.jetbrains.annotations.NotNull; import org.junit.Test; import java.io.NotSerializableException; import java.util.List; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + class OuterClass1 { protected SerializationOutput ser; DeserializationInput desExisting; DeserializationInput desRegen; OuterClass1() { - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory1 = testDefaultFactory(); - SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory2 = testDefaultFactory(); this.ser = new SerializationOutput(factory1); this.desExisting = new DeserializationInput(factory1); @@ -59,13 +56,9 @@ class OuterClass2 { DeserializationInput desRegen; OuterClass2() { - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory1 = testDefaultFactory(); - SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory2 = testDefaultFactory(); this.ser = new SerializationOutput(factory1); this.desExisting = new DeserializationInput(factory1); @@ -104,9 +97,7 @@ abstract class AbstractClass2 { protected SerializationOutput ser; AbstractClass2() { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); this.ser = new SerializationOutput(factory); } @@ -134,9 +125,7 @@ abstract class AbstractClass3 { class Inherator5 extends AbstractClass3 { public void run() throws NotSerializableException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext); @@ -154,9 +143,7 @@ class Inherator6 extends AbstractClass3 { } public void run() throws NotSerializableException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); ser.serialize(new Wrapper(new DummyState()), TestSerializationContext.testSerializationContext); @@ -166,57 +153,57 @@ class Inherator6 extends AbstractClass3 { public class JavaNestedClassesTests { @Test public void publicNested() { - Assertions.assertThatThrownBy(() -> new OuterClass1().run()).isInstanceOf( + assertThatThrownBy(() -> new OuterClass1().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void privateNested() { - Assertions.assertThatThrownBy(() -> new OuterClass2().run()).isInstanceOf( + assertThatThrownBy(() -> new OuterClass2().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void publicNestedInherited() { - Assertions.assertThatThrownBy(() -> new Inherator1().run()).isInstanceOf( + assertThatThrownBy(() -> new Inherator1().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); - Assertions.assertThatThrownBy(() -> new Inherator1().iRun()).isInstanceOf( + assertThatThrownBy(() -> new Inherator1().iRun()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void protectedNestedInherited() { - Assertions.assertThatThrownBy(() -> new Inherator2().run()).isInstanceOf( + assertThatThrownBy(() -> new Inherator2().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); - Assertions.assertThatThrownBy(() -> new Inherator2().iRun()).isInstanceOf( + assertThatThrownBy(() -> new Inherator2().iRun()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void abstractNested() { - Assertions.assertThatThrownBy(() -> new Inherator4().run()).isInstanceOf( + assertThatThrownBy(() -> new Inherator4().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void abstractNestedFactoryOnNested() { - Assertions.assertThatThrownBy(() -> new Inherator5().run()).isInstanceOf( + assertThatThrownBy(() -> new Inherator5().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void abstractNestedFactoryOnNestedInWrapper() { - Assertions.assertThatThrownBy(() -> new Inherator6().run()).isInstanceOf( + assertThatThrownBy(() -> new Inherator6().run()).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedInheritenceTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedInheritenceTests.java index 039dacd688..5331b95096 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedInheritenceTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaNestedInheritenceTests.java @@ -3,15 +3,16 @@ package net.corda.serialization.internal.amqp; import com.google.common.collect.ImmutableList; import net.corda.core.contracts.ContractState; import net.corda.core.identity.AbstractParty; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; -import org.assertj.core.api.Assertions; import org.jetbrains.annotations.NotNull; import org.junit.Test; import java.io.NotSerializableException; import java.util.List; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + abstract class JavaNestedInheritenceTestsBase { class DummyState implements ContractState { @Override @NotNull public List getParticipants() { @@ -33,38 +34,32 @@ class TemplateWrapper { public class JavaNestedInheritenceTests extends JavaNestedInheritenceTestsBase { @Test public void serializeIt() { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); - Assertions.assertThatThrownBy(() -> ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext)).isInstanceOf( + assertThatThrownBy(() -> ser.serialize(new DummyState(), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test public void serializeIt2() { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); - Assertions.assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( + assertThatThrownBy(() -> ser.serialize(new Wrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } @Test - public void serializeIt3() throws NotSerializableException { - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void serializeIt3() { + SerializerFactory factory1 = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory1); - Assertions.assertThatThrownBy(() -> ser.serialize(new TemplateWrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( + assertThatThrownBy(() -> ser.serialize(new TemplateWrapper (new DummyState()), TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class).hasMessageContaining( "has synthetic fields and is likely a nested inner class"); } diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaPrivatePropertyTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaPrivatePropertyTests.java index 656b2224d7..72b8395b6b 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaPrivatePropertyTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaPrivatePropertyTests.java @@ -1,14 +1,15 @@ package net.corda.serialization.internal.amqp; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.junit.Test; -import static org.junit.Assert.*; import java.io.NotSerializableException; import java.lang.reflect.Field; import java.util.Map; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; +import static org.junit.Assert.*; + public class JavaPrivatePropertyTests { static class C { private String a; @@ -76,10 +77,8 @@ public class JavaPrivatePropertyTests { } @Test - public void singlePrivateBooleanWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void singlePrivateBooleanWithConstructor() throws NotSerializableException { + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -89,10 +88,8 @@ public class JavaPrivatePropertyTests { } @Test - public void singlePrivateBooleanWithNoConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void singlePrivateBooleanWithNoConstructor() throws NotSerializableException { + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -104,10 +101,8 @@ public class JavaPrivatePropertyTests { } @Test - public void testCapitilsationOfIs() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void testCapitilsationOfIs() throws NotSerializableException { + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -120,10 +115,8 @@ public class JavaPrivatePropertyTests { } @Test - public void singlePrivateIntWithBoolean() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + public void singlePrivateIntWithBoolean() throws NotSerializableException { + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -136,9 +129,7 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); @@ -166,10 +157,7 @@ public class JavaPrivatePropertyTests { @Test public void singlePrivateWithConstructorAndGetter() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { - SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); + SerializerFactory factory = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory); DeserializationInput des = new DeserializationInput(factory); diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerialiseEnumTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerialiseEnumTests.java index 6037440a0c..e09d1f4da2 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerialiseEnumTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerialiseEnumTests.java @@ -1,13 +1,13 @@ package net.corda.serialization.internal.amqp; +import net.corda.core.serialization.SerializedBytes; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.junit.Test; -import net.corda.serialization.internal.AllWhitelist; -import net.corda.core.serialization.SerializedBytes; - import java.io.NotSerializableException; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; + public class JavaSerialiseEnumTests { public enum Bras { @@ -30,10 +30,7 @@ public class JavaSerialiseEnumTests { public void testJavaConstructorAnnotations() throws NotSerializableException { Bra bra = new Bra(Bras.UNDERWIRE); - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - new EvolutionSerializerGetter(), - new SerializerFingerPrinter()); - SerializationOutput ser = new SerializationOutput(factory1); - SerializedBytes bytes = ser.serialize(bra, TestSerializationContext.testSerializationContext); + SerializationOutput ser = new SerializationOutput(testDefaultFactory()); + ser.serialize(bra, TestSerializationContext.testSerializationContext); } } diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java index 375e894d2c..37c3afa53f 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaSerializationOutputTests.java @@ -1,7 +1,6 @@ package net.corda.serialization.internal.amqp; import net.corda.core.serialization.ConstructorForDeserialization; -import net.corda.serialization.internal.AllWhitelist; import net.corda.core.serialization.SerializedBytes; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.apache.qpid.proton.codec.DecoderImpl; @@ -13,6 +12,7 @@ import java.io.NotSerializableException; import java.nio.ByteBuffer; import java.util.Objects; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; import static org.junit.Assert.assertTrue; public class JavaSerializationOutputTests { @@ -176,14 +176,8 @@ public class JavaSerializationOutputTests { } private Object serdes(Object obj) throws NotSerializableException { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); - SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); + SerializerFactory factory2 = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(obj, TestSerializationContext.testSerializationContext); diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/ListsSerializationJavaTest.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/ListsSerializationJavaTest.java index a521960e55..7e46adef38 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/ListsSerializationJavaTest.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/ListsSerializationJavaTest.java @@ -2,7 +2,6 @@ package net.corda.serialization.internal.amqp; import net.corda.core.serialization.CordaSerializable; import net.corda.core.serialization.SerializedBytes; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; import org.junit.Assert; import org.junit.Test; @@ -10,6 +9,8 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; + public class ListsSerializationJavaTest { @CordaSerializable @@ -126,12 +127,7 @@ public class ListsSerializationJavaTest { // Have to have own version as Kotlin inline functions cannot be easily called from Java private static void assertEqualAfterRoundTripSerialization(T container, Class clazz) throws Exception { - EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), - evolutionSerializerGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(container, TestSerializationContext.testSerializationContext); DeserializationInput des = new DeserializationInput(factory1); diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/SetterConstructorTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/SetterConstructorTests.java index 66df2726b8..25a28c7115 100644 --- a/serialization/src/test/java/net/corda/serialization/internal/amqp/SetterConstructorTests.java +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/SetterConstructorTests.java @@ -1,16 +1,17 @@ package net.corda.serialization.internal.amqp; import net.corda.core.serialization.SerializedBytes; -import net.corda.serialization.internal.AllWhitelist; import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; -import org.assertj.core.api.Assertions; import org.junit.Test; -import static org.junit.Assert.*; import java.io.NotSerializableException; import java.util.ArrayList; import java.util.List; +import static net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt.testDefaultFactory; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; + public class SetterConstructorTests { static class C { @@ -119,13 +120,7 @@ public class SetterConstructorTests { // despite having no constructor we should still be able to serialise an instance of C @Test public void serialiseC() throws NotSerializableException { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); SerializationOutput ser = new SerializationOutput(factory1); @@ -195,13 +190,7 @@ public class SetterConstructorTests { @Test public void deserialiseC() throws NotSerializableException { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); C cPre1 = new C(); @@ -268,13 +257,7 @@ public class SetterConstructorTests { @Test public void serialiseOuterAndInner() throws NotSerializableException { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); Inner1 i1 = new Inner1("Hello"); Inner2 i2 = new Inner2(); @@ -298,38 +281,26 @@ public class SetterConstructorTests { @Test public void typeMistmatch() { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); TypeMismatch tm = new TypeMismatch(); tm.setA(10); assertEquals("10", tm.getA()); - Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, + assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, TestSerializationContext.testSerializationContext)).isInstanceOf ( NotSerializableException.class); } @Test public void typeMistmatch2() { - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); TypeMismatch2 tm = new TypeMismatch2(); tm.setA("10"); assertEquals((Integer)10, tm.getA()); - Assertions.assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, + assertThatThrownBy(() -> new SerializationOutput(factory1).serialize(tm, TestSerializationContext.testSerializationContext)).isInstanceOf( NotSerializableException.class); } @@ -346,13 +317,7 @@ public class SetterConstructorTests { cil.setL(l); - EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); - FingerPrinter fingerPrinter = new SerializerFingerPrinter(); - SerializerFactory factory1 = new SerializerFactory( - AllWhitelist.INSTANCE, - ClassLoader.getSystemClassLoader(), - evolutionSerialiserGetter, - fingerPrinter); + SerializerFactory factory1 = testDefaultFactory(); // 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) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt index 67c6cf25d5..d8e3fc57df 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt @@ -1,11 +1,8 @@ package net.corda.serialization.internal.amqp import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.amqp.testutils.* import net.corda.serialization.internal.carpenter.* -import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput -import net.corda.serialization.internal.amqp.testutils.deserialize -import net.corda.serialization.internal.amqp.testutils.serialize -import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution import org.junit.Test import kotlin.test.* diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt index d2f8947bcf..1b6642bdd5 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeNeedingCarpentryTests.kt @@ -1,15 +1,13 @@ package net.corda.serialization.internal.amqp import net.corda.core.serialization.CordaSerializable -import net.corda.serialization.internal.carpenter.* import net.corda.serialization.internal.AllWhitelist -import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput -import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution -import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryWithWhitelist -import net.corda.serialization.internal.amqp.testutils.serialize -import net.corda.serialization.internal.amqp.testutils.deserialize +import net.corda.serialization.internal.amqp.testutils.* +import net.corda.serialization.internal.carpenter.* import org.junit.Test -import kotlin.test.* +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue @CordaSerializable interface I { diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/FingerPrinterTesting.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/FingerPrinterTesting.kt index 288b95e912..cc180e52c2 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/FingerPrinterTesting.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/FingerPrinterTesting.kt @@ -46,8 +46,8 @@ class FingerPrinterTestingTests { val factory = SerializerFactory( AllWhitelist, ClassLoader.getSystemClassLoader(), - EvolutionSerializerGetterTesting(), - FingerPrinterTesting()) + evolutionSerializerGetter = EvolutionSerializerGetterTesting(), + fingerPrinter = FingerPrinterTesting()) val blob = TestSerializationOutput(VERBOSE, factory).serializeAndReturnSchema(C(1, 2L)) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt index 536c29eaef..cab56596d1 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt @@ -7,13 +7,7 @@ import com.nhaarman.mockito_kotlin.whenever import net.corda.client.rpc.RPCException import net.corda.core.CordaException import net.corda.core.CordaRuntimeException -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Contract -import net.corda.core.contracts.ContractAttachment -import net.corda.core.contracts.ContractState -import net.corda.core.contracts.PrivacySalt -import net.corda.core.contracts.StateRef -import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.* import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.secureRandomBytes @@ -22,48 +16,25 @@ import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.x500Name -import net.corda.core.serialization.ConstructorForDeserialization -import net.corda.core.serialization.CordaSerializable -import net.corda.core.serialization.EncodingWhitelist -import net.corda.core.serialization.MissingAttachmentsException -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.SerializationFactory +import net.corda.core.serialization.* import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.OpaqueBytes import net.corda.node.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA import net.corda.nodeapi.internal.crypto.ContentSignerBuilder -import net.corda.serialization.internal.AllWhitelist -import net.corda.serialization.internal.CordaSerializationEncoding -import net.corda.serialization.internal.EmptyWhitelist -import net.corda.serialization.internal.GeneratedAttachment +import net.corda.serialization.internal.* import net.corda.serialization.internal.amqp.SerializerFactory.Companion.isPrimitive -import net.corda.serialization.internal.amqp.testutils.deserialize -import net.corda.serialization.internal.amqp.testutils.serialize -import net.corda.serialization.internal.amqp.testutils.testDefaultFactory -import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution -import net.corda.serialization.internal.amqp.testutils.testSerializationContext -import net.corda.serialization.internal.encodingNotPermittedFormat +import net.corda.serialization.internal.amqp.testutils.* import net.corda.testing.contracts.DummyContract import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity import net.corda.testing.internal.rigorousMock import org.apache.activemq.artemis.api.core.SimpleString -import org.apache.qpid.proton.amqp.Decimal128 -import org.apache.qpid.proton.amqp.Decimal32 -import org.apache.qpid.proton.amqp.Decimal64 -import org.apache.qpid.proton.amqp.Symbol -import org.apache.qpid.proton.amqp.UnsignedByte -import org.apache.qpid.proton.amqp.UnsignedInteger -import org.apache.qpid.proton.amqp.UnsignedLong -import org.apache.qpid.proton.amqp.UnsignedShort +import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl import org.apache.qpid.proton.codec.EncoderImpl -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.assertj.core.api.Assertions.catchThrowable +import org.assertj.core.api.Assertions.* import org.bouncycastle.cert.X509v2CRLBuilder import org.bouncycastle.cert.jcajce.JcaX509CRLConverter import org.bouncycastle.jce.provider.BouncyCastleProvider @@ -79,20 +50,7 @@ import java.io.NotSerializableException import java.math.BigDecimal import java.math.BigInteger import java.security.cert.X509CRL -import java.time.DayOfWeek -import java.time.Duration -import java.time.Instant -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import java.time.Month -import java.time.MonthDay -import java.time.OffsetDateTime -import java.time.OffsetTime -import java.time.Period -import java.time.Year -import java.time.YearMonth -import java.time.ZonedDateTime +import java.time.* import java.time.temporal.ChronoUnit import java.util.* import kotlin.reflect.full.superclasses @@ -249,9 +207,13 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi if (compression != null) doReturn(true).whenever(it).acceptEncoding(compression) } - private fun defaultFactory() = SerializerFactory( - AllWhitelist, ClassLoader.getSystemClassLoader(), - EvolutionSerializerGetterTesting()) + private fun defaultFactory(): SerializerFactory { + return SerializerFactory( + AllWhitelist, + ClassLoader.getSystemClassLoader(), + evolutionSerializerGetter = EvolutionSerializerGetterTesting() + ) + } private inline fun serdes(obj: T, factory: SerializerFactory = defaultFactory(), diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/testutils/AMQPTestUtils.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/testutils/AMQPTestUtils.kt index 8d78ed61c6..025bebdfe0 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/testutils/AMQPTestUtils.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/testutils/AMQPTestUtils.kt @@ -1,16 +1,25 @@ package net.corda.serialization.internal.amqp.testutils +import net.corda.core.internal.copyTo +import net.corda.core.internal.div +import net.corda.core.internal.packageName import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializedBytes -import org.apache.qpid.proton.codec.Data +import net.corda.core.utilities.OpaqueBytes import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.EmptyWhitelist import net.corda.serialization.internal.amqp.* +import net.corda.testing.common.internal.ProjectStructure +import org.apache.qpid.proton.codec.Data +import org.junit.Test +import java.io.File.separatorChar import java.io.NotSerializableException +import java.nio.file.StandardCopyOption.REPLACE_EXISTING fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) -fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), - EvolutionSerializerGetterTesting()) +fun testDefaultFactoryNoEvolution(): SerializerFactory { + return SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), evolutionSerializerGetter = EvolutionSerializerGetterTesting()) +} fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()) class TestSerializationOutput( @@ -41,8 +50,25 @@ class TestSerializationOutput( } } -fun testName(): String = Thread.currentThread().stackTrace[2].methodName +fun testName(): String { + val classLoader = Thread.currentThread().contextClassLoader + return Thread.currentThread().stackTrace.first { + try { + classLoader.loadClass(it.className).getMethod(it.methodName).isAnnotationPresent(Test::class.java) + } catch (e: Exception) { + false + } + }.methodName +} +fun Any.testResourceName(): String = "${javaClass.simpleName}.${testName()}" + +fun Any.writeTestResource(bytes: OpaqueBytes) { + val dir = ProjectStructure.projectRootDir / "serialization" / "src" / "test" / "resources" / javaClass.packageName.replace('.', separatorChar) + bytes.open().copyTo(dir / testResourceName(), REPLACE_EXISTING) +} + +fun Any.readTestResource(): ByteArray = javaClass.getResourceAsStream(testResourceName()).readBytes() @Throws(NotSerializableException::class) inline fun DeserializationInput.deserializeAndReturnEnvelope( diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt index 123df44182..e74206e5b0 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt @@ -2,6 +2,7 @@ package net.corda.serialization.internal.carpenter import net.corda.core.internal.uncheckedCast import net.corda.serialization.internal.AllWhitelist +import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.Test import java.beans.Introspector import java.lang.reflect.Field @@ -145,21 +146,40 @@ class ClassCarpenterTest { assertEquals(1, i.b) } - @Test(expected = InterfaceMismatchException::class) - fun `mismatched interface`() { - val schema1 = ClassSchema( + @Test + fun `unimplemented interface method with lenient = false`() { + val schemaA = ClassSchema( "gen.A", mapOf("a" to NonNullableField(String::class.java))) - val schema2 = ClassSchema( + val schemaB = ClassSchema( "gen.B", mapOf("c" to NonNullableField(Int::class.java)), - schema1, + schemaA, interfaces = listOf(DummyInterface::class.java)) - val clazz = cc.build(schema2) - val i = clazz.constructors[0].newInstance("xa", 1) as DummyInterface - assertEquals(1, i.b) + assertThatExceptionOfType(InterfaceMismatchException::class.java).isThrownBy { cc.build(schemaB) } + } + + @Test + fun `unimplemented interface method with lenient = true`() { + val cc = ClassCarpenterImpl(whitelist = AllWhitelist, lenient = true) + + val schemaA = ClassSchema( + "gen.A", + mapOf("a" to NonNullableField(String::class.java))) + + val schemaB = ClassSchema( + "gen.B", + mapOf("c" to NonNullableField(Int::class.java)), + schemaA, + interfaces = listOf(DummyInterface::class.java)) + + val classB = cc.build(schemaB) + val b = classB.constructors[0].newInstance("xa", 1) as DummyInterface + assertEquals("xa", b.a) + assertEquals(1, classB.getMethod("getC").invoke(b)) + assertThatExceptionOfType(AbstractMethodError::class.java).isThrownBy { b.b } } @Test diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt index bb52d4c4e6..22f93a5b16 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTestUtils.kt @@ -5,6 +5,7 @@ import net.corda.serialization.internal.amqp.* import net.corda.serialization.internal.amqp.Field import net.corda.serialization.internal.amqp.Schema import net.corda.serialization.internal.amqp.testutils.serialize +import net.corda.serialization.internal.amqp.testutils.testName fun mangleName(name: String) = "${name}__carpenter" @@ -47,7 +48,6 @@ open class AmqpCarpenterBase(whitelist: ClassWhitelist) { var factory = SerializerFactoryExternalCarpenter(cc) fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) - fun testName(): String = Thread.currentThread().stackTrace[2].methodName @Suppress("NOTHING_TO_INLINE") inline fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz" } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.kt new file mode 100644 index 0000000000..fe4c06dfb9 --- /dev/null +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.kt @@ -0,0 +1,49 @@ +package net.corda.serialization.internal.carpenter + +import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.SerializationFactory +import net.corda.core.serialization.deserialize +import net.corda.serialization.internal.amqp.testutils.readTestResource +import net.corda.testing.core.SerializationEnvironmentRule +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.Rule +import org.junit.Test + +class SerDeserCarpentryTest { + @Rule + @JvmField + val testSerialization = SerializationEnvironmentRule() + + @Test + fun implementingGenericInterface() { + // Original class that was serialised +// data class GenericData(val a: Int) : GenericInterface +// writeTestResource(GenericData(123).serialize()) + + val data = readTestResource().deserialize>() + assertThat(data.javaClass.getMethod("getA").invoke(data)).isEqualTo(123) + } + + @Test + fun lenientCarpenter() { + // Original class that was serialised +// data class Data(val b: Int) : AInterface { +// override val a: Int get() = b +// } +// writeTestResource(Data(123).serialize()) + + val data = readTestResource().deserialize(context = SerializationFactory.defaultFactory.defaultContext.withLenientCarpenter()) + assertThat(data.javaClass.getMethod("getB").invoke(data)).isEqualTo(123) + assertThatExceptionOfType(AbstractMethodError::class.java).isThrownBy { data.a } + } + + @Suppress("unused") + @CordaSerializable + interface GenericInterface + + @CordaSerializable + interface AInterface { + val a: Int + } +} diff --git a/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.implementingGenericInterface b/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.implementingGenericInterface new file mode 100644 index 0000000000..6b20aa9377 Binary files /dev/null and b/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.implementingGenericInterface differ diff --git a/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.lenientCarpenter b/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.lenientCarpenter new file mode 100644 index 0000000000..112ce85fd8 Binary files /dev/null and b/serialization/src/test/resources/net/corda/serialization/internal/carpenter/SerDeserCarpentryTest.lenientCarpenter differ diff --git a/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt b/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt index 31368eb228..9590f39d8e 100644 --- a/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt +++ b/tools/blobinspector/src/main/kotlin/net/corda/blobinspector/Main.kt @@ -7,6 +7,7 @@ import net.corda.client.jackson.JacksonSupport import net.corda.core.internal.isRegularFile import net.corda.core.internal.rootMessage import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.deserialize import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal._contextSerializationEnv @@ -33,7 +34,7 @@ fun main(args: Array) { if (main.verbose) { throwable.printStackTrace() } else { - System.err.println("*ERROR*: ${throwable.rootMessage ?: "Use --verbose for more details"}") + System.err.println("*ERROR*: ${throwable.rootMessage}. Use --verbose for more details") } exitProcess(1) } @@ -91,7 +92,8 @@ class Main : Runnable { } val mapper = JacksonSupport.createNonRpcMapper(factory, fullParties) - val deserialized = bytes.deserialize() + // Deserialise with the lenient carpenter as we only care for the AMQP field getters + val deserialized = bytes.deserialize(context = SerializationFactory.defaultFactory.defaultContext.withLenientCarpenter()) println(deserialized.javaClass.name) mapper.writeValue(System.out, deserialized) }