CORDA-3174: Extend serialisation to include InputStream and OpaqueBytesSubSequence. (#60)

* Update DJVM Example project for serialisation.
* Add serializers for InputStream and OpaqueBytesSubSequence.
* Support ZIP Inflater and CRC32 inside the sandbox.
* Allow the DJVM to wrap java.io.InputStream as sandbox.java.io.InputStream.
* Configure tests also to preserve @DeprecatedConstructorForDeserialization.
This commit is contained in:
Chris Rankin 2019-09-03 11:35:48 +01:00 committed by GitHub
parent 67623f04a3
commit f68c4b3308
10 changed files with 198 additions and 5 deletions

View File

@ -33,8 +33,8 @@ allprojects {
cacheChangingModulesFor 0, 'seconds'
dependencySubstitution {
substitute module("net.corda:corda-core:$corda_version") with module("net.corda:corda-core-deterministic:$corda_version")
substitute module("net.corda:corda-serialization:$corda_version") with module("net.corda:corda-serialization-deterministic:$corda_version")
substitute module("net.corda:corda-core") with module("net.corda:corda-core-deterministic:$corda_version")
substitute module("net.corda:corda-serialization") with module("net.corda:corda-serialization-deterministic:$corda_version")
}
}
}

View File

@ -0,0 +1,11 @@
package net.corda.djvm.serialization.deserializers
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.util.function.Function
class InputStreamDeserializer : Function<ByteArray, InputStream?> {
override fun apply(bytes: ByteArray): InputStream? {
return ByteArrayInputStream(bytes)
}
}

View File

@ -0,0 +1,11 @@
package net.corda.djvm.serialization.deserializers
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.OpaqueBytesSubSequence
import java.util.function.Function
class OpaqueBytesSubSequenceDeserializer : Function<OpaqueBytes, OpaqueBytesSubSequence> {
override fun apply(proxy: OpaqueBytes): OpaqueBytesSubSequence {
return OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size)
}
}

View File

@ -28,6 +28,7 @@ class AMQPSerializationScheme(
register(SandboxCertPathSerializer(classLoader, executor, this))
register(SandboxDurationSerializer(classLoader, executor, this))
register(SandboxEnumSetSerializer(classLoader, executor, this))
register(SandboxInputStreamSerializer(classLoader, executor))
register(SandboxInstantSerializer(classLoader, executor, this))
register(SandboxLocalDateSerializer(classLoader, executor, this))
register(SandboxLocalDateTimeSerializer(classLoader, executor, this))
@ -40,6 +41,7 @@ class AMQPSerializationScheme(
register(SandboxYearSerializer(classLoader, executor, this))
register(SandboxZonedDateTimeSerializer(classLoader, executor, this))
register(SandboxZoneIdSerializer(classLoader, executor, this))
register(SandboxOpaqueBytesSubSequenceSerializer(classLoader, executor, this))
register(SandboxOptionalSerializer(classLoader, executor, this))
register(SandboxPrimitiveSerializer(UUID::class.java, classLoader, sandboxBasicInput))
register(SandboxPrimitiveSerializer(String::class.java, classLoader, sandboxBasicInput))

View File

@ -6,6 +6,7 @@ import net.corda.core.serialization.SerializationContext.UseCase
import net.corda.core.serialization.SerializationFactory
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.utilities.ByteSequence
import net.corda.djvm.execution.SandboxRuntimeException
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.djvm.serialization.serializers.PrimitiveSerializer
@ -73,11 +74,11 @@ inline fun <reified T : Any> SerializedBytes<T>.deserializeFor(classLoader: Sand
return deserializeTo(clazz, classLoader)
}
fun SerializedBytes<*>.deserializeTo(clazz: Class<*>, classLoader: SandboxClassLoader): Any {
fun ByteSequence.deserializeTo(clazz: Class<*>, classLoader: SandboxClassLoader): Any {
return deserializeTo(clazz, classLoader, SerializationFactory.defaultFactory)
}
fun SerializedBytes<*>.deserializeTo(clazz: Class<*>, classLoader: SandboxClassLoader, factory: SerializationFactory): Any {
fun ByteSequence.deserializeTo(clazz: Class<*>, classLoader: SandboxClassLoader, factory: SerializationFactory): Any {
val obj = factory.deserialize(this, Any::class.java, factory.defaultContext)
return if (clazz.isInstance(obj)) {
obj

View File

@ -0,0 +1,40 @@
package net.corda.djvm.serialization.serializers
import net.corda.core.serialization.SerializationContext
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.djvm.serialization.deserializers.InputStreamDeserializer
import net.corda.djvm.serialization.loadClassForSandbox
import net.corda.serialization.internal.amqp.*
import org.apache.qpid.proton.codec.Data
import java.io.InputStream
import java.lang.reflect.Type
import java.util.Collections.singleton
import java.util.function.BiFunction
import java.util.function.Function
class SandboxInputStreamSerializer(
classLoader: SandboxClassLoader,
executor: BiFunction<in Any, in Any?, out Any?>
) : CustomSerializer.Implements<Any>(classLoader.loadClassForSandbox(InputStream::class.java)) {
private val decoder: Function<ByteArray, out Any?>
init {
val decodeTask = classLoader.loadClassForSandbox(InputStreamDeserializer::class.java).newInstance()
decoder = Function { inputs ->
executor.apply(decodeTask, inputs)
}
}
override val schemaForDocumentation: Schema = Schema(emptyList())
override val deserializationAliases: Set<Class<*>> = singleton(InputStream::class.java)
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, context: SerializationContext): Any {
val bits = input.readObject(obj, schemas, ByteArray::class.java, context) as ByteArray
return decoder.apply(bits)!!
}
override fun writeDescribedObject(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext) {
abortReadOnly()
}
}

View File

@ -0,0 +1,31 @@
package net.corda.djvm.serialization.serializers
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.OpaqueBytesSubSequence
import net.corda.djvm.rewiring.SandboxClassLoader
import net.corda.djvm.serialization.deserializers.OpaqueBytesSubSequenceDeserializer
import net.corda.djvm.serialization.loadClassForSandbox
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory
import java.util.Collections.singleton
import java.util.function.BiFunction
class SandboxOpaqueBytesSubSequenceSerializer(
classLoader: SandboxClassLoader,
private val executor: BiFunction<in Any, in Any?, out Any?>,
factory: SerializerFactory
) : CustomSerializer.Proxy<Any, Any>(
clazz = classLoader.loadClassForSandbox(OpaqueBytesSubSequence::class.java),
proxyClass = classLoader.loadClassForSandbox(OpaqueBytes::class.java),
factory = factory
) {
private val task = classLoader.loadClassForSandbox(OpaqueBytesSubSequenceDeserializer::class.java).newInstance()
override val deserializationAliases: Set<Class<*>> = singleton(OpaqueBytesSubSequence::class.java)
override fun toProxy(obj: Any): Any = abortReadOnly()
override fun fromProxy(proxy: Any): Any {
return executor.apply(task, proxy)!!
}
}

View File

@ -0,0 +1,45 @@
package net.corda.djvm.serialization
import net.corda.core.internal.readFully
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.djvm.serialization.SandboxType.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.io.InputStream
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeInputStreamTest : TestBase(KOTLIN) {
companion object {
const val MESSAGE = "Round and round the rugged rocks..."
}
@Test
fun `test deserializing input stream`() {
val data = MESSAGE.byteInputStream().serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxStream = data.deserializeFor(classLoader)
val executor = createExecutorFor(classLoader)
val result = executor.apply(
classLoader.loadClassForSandbox(ShowInputStream::class.java).newInstance(),
sandboxStream
) ?: fail("Result cannot be null")
assertEquals(String(MESSAGE.byteInputStream().readFully()), result.toString())
assertEquals(SANDBOX_STRING, result::class.java.name)
}
}
class ShowInputStream : Function<InputStream, String> {
override fun apply(input: InputStream): String {
return String(input.readFully())
}
}
}

View File

@ -0,0 +1,50 @@
package net.corda.djvm.serialization
import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytesSubSequence
import net.corda.djvm.serialization.SandboxType.*
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import java.util.function.Function
@ExtendWith(LocalSerialization::class)
class DeserializeOpaqueBytesSubSequenceTest : TestBase(KOTLIN) {
companion object {
const val MESSAGE = "The rain in spain falls mainly on the plain."
const val OFFSET = MESSAGE.length / 2
}
@Test
fun `test deserializing opaquebytes subsequence`() {
val subSequence = OpaqueBytesSubSequence(
bytes = MESSAGE.toByteArray(),
offset = OFFSET,
size = MESSAGE.length - OFFSET
)
val data = subSequence.serialize()
sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxBytes = data.deserializeFor(classLoader)
val executor = createExecutorFor(classLoader)
val result = executor.apply(
classLoader.loadClassForSandbox(ShowOpaqueBytesSubSequence::class.java).newInstance(),
sandboxBytes
) ?: fail("Result cannot be null")
assertEquals(MESSAGE.substring(OFFSET), String(result as ByteArray))
assertEquals(String(subSequence.copyBytes()), String(result))
}
}
class ShowOpaqueBytesSubSequence : Function<OpaqueBytesSubSequence, ByteArray> {
override fun apply(sequence: OpaqueBytesSubSequence): ByteArray {
return sequence.copyBytes()
}
}
}

View File

@ -2,6 +2,7 @@ package net.corda.djvm.serialization
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
import net.corda.djvm.SandboxConfiguration
import net.corda.djvm.SandboxConfiguration.Companion.ALL_DEFINITION_PROVIDERS
import net.corda.djvm.SandboxConfiguration.Companion.ALL_EMITTERS
@ -52,7 +53,8 @@ abstract class TestBase(type: SandboxType) {
whitelist = MINIMAL,
visibleAnnotations = setOf(
CordaSerializable::class.java,
ConstructorForDeserialization::class.java
ConstructorForDeserialization::class.java,
DeprecatedConstructorForDeserialization::class.java
),
bootstrapSource = BootstrapClassLoader(DETERMINISTIC_RT)
)