mirror of
https://github.com/corda/corda.git
synced 2025-03-29 15:16:19 +00:00
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:
parent
67623f04a3
commit
f68c4b3308
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)!!
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user