Replace code only used in 1 test with existing general mechanism. (#600)

This commit is contained in:
Andrzej Cichocki 2017-04-28 15:50:24 +01:00 committed by GitHub
parent 9dde0db407
commit f2d138cdab
8 changed files with 92 additions and 85 deletions

View File

@ -328,6 +328,7 @@ interface FileUploader {
interface AttachmentsStorageService {
/** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */
val attachments: AttachmentStorage
val attachmentsClassLoaderEnabled: Boolean
}
/**

View File

@ -4,20 +4,17 @@ import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.pool.KryoPool
import com.esotericsoftware.kryo.serializers.JavaSerializer
import com.esotericsoftware.kryo.util.MapReferenceResolver
import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.transactions.WireTransaction
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.x500.X500Name
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -308,6 +305,18 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
kryo.writeClassAndObject(output, obj.timestamp)
}
private fun attachmentsClassLoader(kryo: Kryo, attachmentHashes: List<SecureHash>): ClassLoader? {
val serializationContext = kryo.serializationContext() ?: return null // Some tests don't set one.
serializationContext.serviceHub.storageService.attachmentsClassLoaderEnabled || return null
val missing = ArrayList<SecureHash>()
val attachments = ArrayList<Attachment>()
attachmentHashes.forEach { id ->
serializationContext.serviceHub.storageService.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id }
}
missing.isNotEmpty() && throw MissingAttachmentsException(missing)
return AttachmentsClassLoader(attachments)
}
@Suppress("UNCHECKED_CAST")
override fun read(kryo: Kryo, input: Input, type: Class<WireTransaction>): WireTransaction {
val inputs = kryo.readClassAndObject(input) as List<StateRef>
@ -315,30 +324,13 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
// If we're deserialising in the sandbox context, we use our special attachments classloader.
// Otherwise we just assume the code we need is on the classpath already.
val attachmentStorage = kryo.attachmentStorage
val classLoader = if (attachmentStorage != null) {
val missing = ArrayList<SecureHash>()
val attachments = ArrayList<Attachment>()
for (id in attachmentHashes) {
val attachment = attachmentStorage.openAttachment(id)
if (attachment == null)
missing += id
else
attachments += attachment
}
if (missing.isNotEmpty())
throw MissingAttachmentsException(missing)
AttachmentsClassLoader(attachments)
} else javaClass.classLoader
kryo.useClassLoader(classLoader) {
kryo.useClassLoader(attachmentsClassLoader(kryo, attachmentHashes) ?: javaClass.classLoader) {
val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>>
val commands = kryo.readClassAndObject(input) as List<Command>
val notary = kryo.readClassAndObject(input) as Party?
val signers = kryo.readClassAndObject(input) as List<PublicKey>
val transactionType = kryo.readClassAndObject(input) as TransactionType
val timestamp = kryo.readClassAndObject(input) as Timestamp?
return WireTransaction(inputs, attachmentHashes, outputs, commands, notary, signers, transactionType, timestamp)
}
}
@ -509,21 +501,6 @@ fun <T> Kryo.withoutReferences(block: () -> T): T {
}
}
val ATTACHMENT_STORAGE = "ATTACHMENT_STORAGE"
val Kryo.attachmentStorage: AttachmentStorage?
get() = this.context.get(ATTACHMENT_STORAGE, null) as AttachmentStorage?
fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage?, block: () -> T): T {
val priorAttachmentStorage = this.attachmentStorage
this.context.put(ATTACHMENT_STORAGE, attachmentStorage)
try {
return block()
} finally {
this.context.put(ATTACHMENT_STORAGE, priorAttachmentStorage)
}
}
/** For serialising a MetaData object. */
@ThreadSafe
object MetaDataSerializer : Serializer<MetaData>() {

View File

@ -41,29 +41,31 @@ interface SerializationToken {
*/
class SerializeAsTokenSerializer<T : SerializeAsToken> : Serializer<T>() {
override fun write(kryo: Kryo, output: Output, obj: T) {
kryo.writeClassAndObject(output, obj.toToken(getContext(kryo) ?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context")))
kryo.writeClassAndObject(output, obj.toToken(kryo.serializationContext() ?: throw KryoException("Attempt to write a ${SerializeAsToken::class.simpleName} instance of ${obj.javaClass.name} without initialising a context")))
}
override fun read(kryo: Kryo, input: Input, type: Class<T>): T {
val token = (kryo.readClassAndObject(input) as? SerializationToken) ?: throw KryoException("Non-token read for tokenized type: ${type.name}")
val fromToken = token.fromToken(getContext(kryo) ?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context"))
val fromToken = token.fromToken(kryo.serializationContext() ?: throw KryoException("Attempt to read a token for a ${SerializeAsToken::class.simpleName} instance of ${type.name} without initialising a context"))
if (type.isAssignableFrom(fromToken.javaClass)) {
return type.cast(fromToken)
} else {
throw KryoException("Token read ($token) did not return expected tokenized type: ${type.name}")
}
}
}
companion object {
private fun getContext(kryo: Kryo): SerializeAsTokenContext? = kryo.context.get(SerializeAsTokenContext::class.java) as? SerializeAsTokenContext
private val serializationContextKey = SerializeAsTokenContext::class.java
fun setContext(kryo: Kryo, context: SerializeAsTokenContext) {
kryo.context.put(SerializeAsTokenContext::class.java, context)
}
fun Kryo.serializationContext() = context.get(serializationContextKey) as? SerializeAsTokenContext
fun clearContext(kryo: Kryo) {
kryo.context.remove(SerializeAsTokenContext::class.java)
}
fun <T> Kryo.withSerializationContext(serializationContext: SerializeAsTokenContext, block: () -> T) = run {
context.containsKey(serializationContextKey) && throw IllegalStateException("There is already a serialization context.")
context.put(serializationContextKey, serializationContext)
try {
block()
} finally {
context.remove(serializationContextKey)
}
}
@ -76,7 +78,15 @@ class SerializeAsTokenSerializer<T : SerializeAsToken> : Serializer<T>() {
* Then it is a case of using the companion object methods on [SerializeAsTokenSerializer] to set and clear context as necessary
* on the Kryo instance when serializing to enable/disable tokenization.
*/
class SerializeAsTokenContext(toBeTokenized: Any, kryoPool: KryoPool, val serviceHub: ServiceHub) {
class SerializeAsTokenContext internal constructor(val serviceHub: ServiceHub, init: SerializeAsTokenContext.() -> Unit) {
constructor(toBeTokenized: Any, kryoPool: KryoPool, serviceHub: ServiceHub) : this(serviceHub, {
kryoPool.run { kryo ->
kryo.withSerializationContext(this) {
toBeTokenized.serialize(kryo)
}
}
})
private val classNameToSingleton = mutableMapOf<String, SerializeAsToken>()
private var readOnly = false
@ -90,11 +100,7 @@ class SerializeAsTokenContext(toBeTokenized: Any, kryoPool: KryoPool, val servic
* accidental registrations from occuring as these could not be deserialized in a deserialization-first
* scenario if they are not part of this iniital context construction serialization.
*/
kryoPool.run { kryo ->
SerializeAsTokenSerializer.setContext(kryo, this)
toBeTokenized.serialize(kryo)
SerializeAsTokenSerializer.clearContext(kryo)
}
init(this)
readOnly = true
}

View File

@ -1,10 +1,13 @@
package net.corda.core.node
import com.esotericsoftware.kryo.Kryo
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.StorageService
import net.corda.core.serialization.*
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.DUMMY_NOTARY
@ -35,6 +38,15 @@ val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContr
class AttachmentClassLoaderTests {
companion object {
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
private fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage, block: () -> T) = run {
val serviceHub = mock<ServiceHub>()
val storageService = mock<StorageService>()
whenever(serviceHub.storageService).thenReturn(storageService)
whenever(storageService.attachmentsClassLoaderEnabled).thenReturn(true)
whenever(storageService.attachments).thenReturn(attachmentStorage)
withSerializationContext(SerializeAsTokenContext(serviceHub) {}, block)
}
}
class AttachmentDummyContract : Contract {

View File

@ -22,7 +22,6 @@ class SerializationTokenTest {
@After
fun cleanup() {
SerializeAsTokenSerializer.clearContext(kryo)
storageKryo().release(kryo)
}
@ -46,12 +45,13 @@ class SerializationTokenTest {
fun `write token and read tokenizable`() {
val tokenizableBefore = LargeTokenizable()
val context = serializeAsTokenContext(tokenizableBefore)
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
assertThat(serializedBytes.size).isLessThan(tokenizableBefore.numBytes)
val tokenizableAfter = serializedBytes.deserialize(kryo)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
}
private class UnitSerializeAsToken : SingletonSerializeAsToken()
@ -59,28 +59,31 @@ class SerializationTokenTest {
fun `write and read singleton`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
val tokenizableAfter = serializedBytes.deserialize(kryo)
assertThat(tokenizableAfter).isSameAs(tokenizableBefore)
}
}
@Test(expected = UnsupportedOperationException::class)
fun `new token encountered after context init`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
tokenizableBefore.serialize(kryo)
}
}
@Test(expected = UnsupportedOperationException::class)
fun `deserialize unregistered token`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(emptyList<Any>())
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.toToken(serializeAsTokenContext(emptyList<Any>())).serialize(kryo)
serializedBytes.deserialize(kryo)
}
}
@Test(expected = KryoException::class)
fun `no context set`() {
@ -92,7 +95,7 @@ class SerializationTokenTest {
fun `deserialize non-token`() {
val tokenizableBefore = UnitSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
val stream = ByteArrayOutputStream()
Output(stream).use {
kryo.writeClass(it, SingletonSerializeAsToken::class.java)
@ -101,6 +104,7 @@ class SerializationTokenTest {
val serializedBytes = SerializedBytes<Any>(stream.toByteArray())
serializedBytes.deserialize(kryo)
}
}
private class WrongTypeSerializeAsToken : SerializeAsToken {
override fun toToken(context: SerializeAsTokenContext): SerializationToken {
@ -114,8 +118,9 @@ class SerializationTokenTest {
fun `token returns unexpected type`() {
val tokenizableBefore = WrongTypeSerializeAsToken()
val context = serializeAsTokenContext(tokenizableBefore)
SerializeAsTokenSerializer.setContext(kryo, context)
kryo.withSerializationContext(context) {
val serializedBytes = tokenizableBefore.serialize(kryo)
serializedBytes.deserialize(kryo)
}
}
}

View File

@ -7,6 +7,8 @@ open class StorageServiceImpl(override val attachments: AttachmentStorage,
override val validatedTransactions: TransactionStorage,
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage)
: SingletonSerializeAsToken(), TxWritableStorageService {
override val attachmentsClassLoaderEnabled = false
lateinit override var uploaders: List<FileUploader>
fun initUploaders(uploadersList: List<FileUploader>) {

View File

@ -374,16 +374,18 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun serializeFiber(fiber: FlowStateMachineImpl<*>): SerializedBytes<FlowStateMachineImpl<*>> {
return quasarKryo().run { kryo ->
// add the map of tokens -> tokenizedServices to the kyro context
SerializeAsTokenSerializer.setContext(kryo, serializationContext)
kryo.withSerializationContext(serializationContext) {
fiber.serialize(kryo)
}
}
}
private fun deserializeFiber(checkpoint: Checkpoint): FlowStateMachineImpl<*> {
return quasarKryo().run { kryo ->
// put the map of token -> tokenized into the kryo context
SerializeAsTokenSerializer.setContext(kryo, serializationContext)
checkpoint.serializedFiber.deserialize(kryo).apply { fromCheckpoint = true }
kryo.withSerializationContext(serializationContext) {
checkpoint.serializedFiber.deserialize(kryo)
}.apply { fromCheckpoint = true }
}
}

View File

@ -173,7 +173,9 @@ class MockStorageService(override val attachments: AttachmentStorage = MockAttac
override val validatedTransactions: TransactionStorage = MockTransactionStorage(),
override val uploaders: List<FileUploader> = listOf<FileUploader>(),
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage())
: SingletonSerializeAsToken(), TxWritableStorageService
: SingletonSerializeAsToken(), TxWritableStorageService {
override val attachmentsClassLoaderEnabled = false
}
/**
* Make properties appropriate for creating a DataSource for unit tests.