mirror of
https://github.com/corda/corda.git
synced 2025-02-07 03:29:19 +00:00
Merge remote-tracking branch 'open/master' into my-merge-july-11-11-27
# Conflicts: # CONTRIBUTORS.md
This commit is contained in:
commit
480698bc46
@ -6,24 +6,24 @@ import net.corda.core.serialization.SerializationContext
|
|||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.serialization.internal.CordaSerializationMagic
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.amqp.amqpMagic
|
import net.corda.serialization.internal.amqp.amqpMagic
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class AMQPFirewallSerializationScheme(
|
class AMQPFirewallSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun rpcServerSerializerFactory(context: SerializationContext) : SerializerFactory {
|
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase)
|
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase) = (magic == amqpMagic && target == SerializationContext.UseCase.P2P)
|
||||||
= (magic == amqpMagic && target == SerializationContext.UseCase.P2P)
|
|
||||||
}
|
}
|
13
build.gradle
13
build.gradle
@ -184,7 +184,6 @@ allprojects {
|
|||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
javaParameters = true // Useful for reflection.
|
javaParameters = true // Useful for reflection.
|
||||||
freeCompilerArgs = ['-Xjvm-default=compatibility']
|
freeCompilerArgs = ['-Xjvm-default=compatibility']
|
||||||
allWarningsAsErrors = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,20 +435,20 @@ artifactory {
|
|||||||
|
|
||||||
defaults {
|
defaults {
|
||||||
// Root project applies the plugin (for this block) but does not need to be published
|
// Root project applies the plugin (for this block) but does not need to be published
|
||||||
if(project != rootProject) {
|
if (project != rootProject) {
|
||||||
publications(project.extensions.publish.name())
|
publications(project.extensions.publish.name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task generateApi(type: net.corda.plugins.GenerateApi){
|
task generateApi(type: net.corda.plugins.GenerateApi) {
|
||||||
baseName = "api-corda"
|
baseName = "api-corda"
|
||||||
}
|
}
|
||||||
|
|
||||||
// This exists to reduce CI build time when the envvar is set (can save up to 40 minutes)
|
// This exists to reduce CI build time when the envvar is set (can save up to 40 minutes)
|
||||||
if(file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BUILD') != null)) {
|
if (file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BUILD') != null)) {
|
||||||
if(file('corda-docs-only-build').exists()) {
|
if (file('corda-docs-only-build').exists()) {
|
||||||
logger.info("Tests are disabled due to presence of file 'corda-docs-only-build' in the project root")
|
logger.info("Tests are disabled due to presence of file 'corda-docs-only-build' in the project root")
|
||||||
} else {
|
} else {
|
||||||
logger.info("Tests are disabled due to the presence of envvar CORDA_DOCS_ONLY_BUILD")
|
logger.info("Tests are disabled due to the presence of envvar CORDA_DOCS_ONLY_BUILD")
|
||||||
@ -461,7 +460,7 @@ if(file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
it.afterEvaluate {
|
it.afterEvaluate {
|
||||||
if(it.tasks.findByName("integrationTest") != null) {
|
if (it.tasks.findByName("integrationTest") != null) {
|
||||||
integrationTest {
|
integrationTest {
|
||||||
exclude '*/**'
|
exclude '*/**'
|
||||||
}
|
}
|
||||||
@ -469,7 +468,7 @@ if(file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BUI
|
|||||||
}
|
}
|
||||||
|
|
||||||
it.afterEvaluate {
|
it.afterEvaluate {
|
||||||
if(it.tasks.findByName("smokeTest") != null) {
|
if (it.tasks.findByName("smokeTest") != null) {
|
||||||
smokeTest {
|
smokeTest {
|
||||||
exclude '*/**'
|
exclude '*/**'
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ def originalJar = configurations.proguard.files.find { it.name.startsWith("kotli
|
|||||||
|
|
||||||
import proguard.gradle.ProGuardTask
|
import proguard.gradle.ProGuardTask
|
||||||
task metadata(type: ProGuardTask) {
|
task metadata(type: ProGuardTask) {
|
||||||
injars originalJar, filter: '!META-INF/native/**'
|
injars originalJar, filter: 'META-INF/MANIFEST.MF,META-INF/metadata*.kotlin_module,**.class'
|
||||||
outjars "$buildDir/libs/${project.name}-${kotlin_version}.jar"
|
outjars "$buildDir/libs/${project.name}-${kotlin_version}.jar"
|
||||||
|
|
||||||
libraryjars "$javaHome/lib/rt.jar"
|
libraryjars "$javaHome/lib/rt.jar"
|
||||||
@ -49,7 +49,10 @@ task metadata(type: ProGuardTask) {
|
|||||||
dontnote
|
dontnote
|
||||||
|
|
||||||
keep 'class org.jetbrains.kotlin.load.java.JvmAnnotationNames { *; }'
|
keep 'class org.jetbrains.kotlin.load.java.JvmAnnotationNames { *; }'
|
||||||
keep 'class org.jetbrains.kotlin.metadata.** { *; }', includedescriptorclasses: true
|
keep 'class org.jetbrains.kotlin.metadata.ProtoBuf { *; }', includedescriptorclasses: true
|
||||||
|
keep 'class org.jetbrains.kotlin.metadata.ProtoBuf$* { *; }', includedescriptorclasses: true
|
||||||
|
keep 'class org.jetbrains.kotlin.metadata.deserialization.** { *; }', includedescriptorclasses: true
|
||||||
|
keep 'class org.jetbrains.kotlin.metadata.jvm.** { *; }', includedescriptorclasses: true
|
||||||
keep 'class org.jetbrains.kotlin.protobuf.** { *; }', includedescriptorclasses: true
|
keep 'class org.jetbrains.kotlin.protobuf.** { *; }', includedescriptorclasses: true
|
||||||
}
|
}
|
||||||
def metadataJar = metadata.outputs.files.singleFile
|
def metadataJar = metadata.outputs.files.singleFile
|
||||||
|
@ -2,16 +2,21 @@
|
|||||||
package net.corda.gradle.jarfilter
|
package net.corda.gradle.jarfilter
|
||||||
|
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||||
|
import org.jetbrains.kotlin.metadata.deserialization.Flags.*
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
|
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.returnType
|
import org.jetbrains.kotlin.metadata.deserialization.returnType
|
||||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||||
import org.objectweb.asm.Opcodes.ACC_SYNTHETIC
|
import org.objectweb.asm.Opcodes.ACC_SYNTHETIC
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
private const val DEFAULT_CONSTRUCTOR_MARKER = "ILkotlin/jvm/internal/DefaultConstructorMarker;"
|
||||||
private const val DUMMY_PASSES = 1
|
private const val DUMMY_PASSES = 1
|
||||||
|
|
||||||
|
private val DECLARES_DEFAULT_VALUE_MASK: Int = DECLARES_DEFAULT_VALUE.toFlags(true).inv()
|
||||||
|
|
||||||
internal abstract class Element(val name: String, val descriptor: String) {
|
internal abstract class Element(val name: String, val descriptor: String) {
|
||||||
private var lifetime: Int = DUMMY_PASSES
|
private var lifetime: Int = DUMMY_PASSES
|
||||||
|
|
||||||
@ -36,6 +41,7 @@ internal class MethodElement(name: String, descriptor: String, val access: Int =
|
|||||||
|
|
||||||
private val suffix: String
|
private val suffix: String
|
||||||
val visibleName: String
|
val visibleName: String
|
||||||
|
val signature: String = name + descriptor
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val idx = name.indexOf('$')
|
val idx = name.indexOf('$')
|
||||||
@ -44,6 +50,14 @@ internal class MethodElement(name: String, descriptor: String, val access: Int =
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isKotlinSynthetic(vararg tags: String): Boolean = (access and ACC_SYNTHETIC) != 0 && tags.contains(suffix)
|
fun isKotlinSynthetic(vararg tags: String): Boolean = (access and ACC_SYNTHETIC) != 0 && tags.contains(suffix)
|
||||||
|
fun asKotlinNonDefaultConstructor(): MethodElement? {
|
||||||
|
val markerIdx = descriptor.indexOf(DEFAULT_CONSTRUCTOR_MARKER)
|
||||||
|
return if (markerIdx >= 0) {
|
||||||
|
MethodElement(name, descriptor.removeRange(markerIdx, markerIdx + DEFAULT_CONSTRUCTOR_MARKER.length))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -108,3 +122,23 @@ internal fun JvmProtoBuf.JvmPropertySignature.toFieldElement(property: ProtoBuf.
|
|||||||
|
|
||||||
return FieldElement(nameResolver.getString(nameId), descriptor)
|
return FieldElement(nameResolver.getString(nameId), descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites metadata for constructor parameters.
|
||||||
|
*/
|
||||||
|
internal fun ProtoBuf.Constructor.Builder.updateValueParameters(
|
||||||
|
updater: (ProtoBuf.ValueParameter) -> ProtoBuf.ValueParameter
|
||||||
|
): ProtoBuf.Constructor.Builder {
|
||||||
|
for (idx in 0 until valueParameterList.size) {
|
||||||
|
setValueParameter(idx, updater(valueParameterList[idx]))
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ProtoBuf.ValueParameter.clearDeclaresDefaultValue(): ProtoBuf.ValueParameter {
|
||||||
|
return if (DECLARES_DEFAULT_VALUE.get(flags)) {
|
||||||
|
toBuilder().setFlags(flags and DECLARES_DEFAULT_VALUE_MASK).build()
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -248,7 +248,7 @@ class FilterTransformer private constructor (
|
|||||||
logger.info("-- also identified property or typealias {},{} for deletion", method.visibleName, extensionType)
|
logger.info("-- also identified property or typealias {},{} for deletion", method.visibleName, extensionType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (stubAnnotations.contains(descriptor) && (method.access and (ACC_ABSTRACT or ACC_SYNTHETIC)) == 0) {
|
} else if (stubAnnotations.contains(descriptor) && (method.access and ACC_ABSTRACT) == 0) {
|
||||||
if (stubbedMethods.add(method)) {
|
if (stubbedMethods.add(method)) {
|
||||||
logger.info("- Identified method {}{} for stubbing out", method.name, method.descriptor)
|
logger.info("- Identified method {}{} for stubbing out", method.name, method.descriptor)
|
||||||
}
|
}
|
||||||
|
@ -95,17 +95,30 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
|||||||
private fun filterConstructors(): Int = deletedConstructors.count(::filterConstructor)
|
private fun filterConstructors(): Int = deletedConstructors.count(::filterConstructor)
|
||||||
|
|
||||||
private fun filterConstructor(deleted: MethodElement): Boolean {
|
private fun filterConstructor(deleted: MethodElement): Boolean {
|
||||||
|
/*
|
||||||
|
* Constructors with the default parameter marker are synthetic and DO NOT have
|
||||||
|
* entries in the metadata. So we construct an element for the "primary" one
|
||||||
|
* that it was synthesised for, and which we DO expect to find.
|
||||||
|
*/
|
||||||
|
val deletedPrimary = deleted.asKotlinNonDefaultConstructor()
|
||||||
|
|
||||||
for (idx in 0 until constructors.size) {
|
for (idx in 0 until constructors.size) {
|
||||||
val constructor = constructors[idx]
|
val constructor = constructors[idx]
|
||||||
val signature = JvmProtoBufUtil.getJvmConstructorSignature(constructor, nameResolver, typeTable)
|
val signature = JvmProtoBufUtil.getJvmConstructorSignature(constructor, nameResolver, typeTable)
|
||||||
if (signature == deleted.name + deleted.descriptor) {
|
if (signature == deleted.signature) {
|
||||||
if (IS_SECONDARY.get(constructor.flags)) {
|
if (IS_SECONDARY.get(constructor.flags)) {
|
||||||
logger.info("-- removing constructor: {}{}", deleted.name, deleted.descriptor)
|
logger.info("-- removing constructor: {}", deleted.signature)
|
||||||
} else {
|
} else {
|
||||||
logger.warn("Removing primary constructor: {}{}", className, deleted.descriptor)
|
logger.warn("Removing primary constructor: {}{}", className, deleted.descriptor)
|
||||||
}
|
}
|
||||||
constructors.removeAt(idx)
|
constructors.removeAt(idx)
|
||||||
return true
|
return true
|
||||||
|
} else if (signature == deletedPrimary?.signature) {
|
||||||
|
constructors[idx] = constructor.toBuilder()
|
||||||
|
.updateValueParameters(ProtoBuf.ValueParameter::clearDeclaresDefaultValue)
|
||||||
|
.build()
|
||||||
|
logger.info("-- removing default parameter values: {}", signature)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -118,8 +131,8 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
|||||||
val function = functions[idx]
|
val function = functions[idx]
|
||||||
if (nameResolver.getString(function.name) == deleted.name) {
|
if (nameResolver.getString(function.name) == deleted.name) {
|
||||||
val signature = JvmProtoBufUtil.getJvmMethodSignature(function, nameResolver, typeTable)
|
val signature = JvmProtoBufUtil.getJvmMethodSignature(function, nameResolver, typeTable)
|
||||||
if (signature == deleted.name + deleted.descriptor) {
|
if (signature == deleted.signature) {
|
||||||
logger.info("-- removing function: {}{}", deleted.name, deleted.descriptor)
|
logger.info("-- removing function: {}", deleted.signature)
|
||||||
functions.removeAt(idx)
|
functions.removeAt(idx)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -157,7 +170,7 @@ internal abstract class MetadataTransformer<out T : MessageLite>(
|
|||||||
|
|
||||||
private fun deleteExtra(func: MethodElement) {
|
private fun deleteExtra(func: MethodElement) {
|
||||||
if (!deletedFunctions.contains(func)) {
|
if (!deletedFunctions.contains(func)) {
|
||||||
logger.info("-- identified extra method {}{} for deletion", func.name, func.descriptor)
|
logger.info("-- identified extra method {} for deletion", func.signature)
|
||||||
handleExtraMethod(func)
|
handleExtraMethod(func)
|
||||||
filterFunction(func)
|
filterFunction(func)
|
||||||
}
|
}
|
||||||
@ -244,12 +257,11 @@ internal class ClassMetadataTransformer(
|
|||||||
override val typeAliases = mutableList(message.typeAliasList)
|
override val typeAliases = mutableList(message.typeAliasList)
|
||||||
|
|
||||||
override fun rebuild(): ProtoBuf.Class = message.toBuilder().apply {
|
override fun rebuild(): ProtoBuf.Class = message.toBuilder().apply {
|
||||||
|
clearConstructor().addAllConstructor(constructors)
|
||||||
|
|
||||||
if (nestedClassNames.size != nestedClassNameCount) {
|
if (nestedClassNames.size != nestedClassNameCount) {
|
||||||
clearNestedClassName().addAllNestedClassName(nestedClassNames)
|
clearNestedClassName().addAllNestedClassName(nestedClassNames)
|
||||||
}
|
}
|
||||||
if (constructors.size != constructorCount) {
|
|
||||||
clearConstructor().addAllConstructor(constructors)
|
|
||||||
}
|
|
||||||
if (functions.size != functionCount) {
|
if (functions.size != functionCount) {
|
||||||
clearFunction().addAllFunction(functions)
|
clearFunction().addAllFunction(functions)
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,13 @@ import kotlin.jvm.kotlin
|
|||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
class SanitiseConstructorTest {
|
class SanitiseDeleteConstructorTest {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val COMPLEX_CONSTRUCTOR_CLASS = "net.corda.gradle.HasOverloadedComplexConstructorToDelete"
|
||||||
private const val COUNT_INITIAL_OVERLOADED = 1
|
private const val COUNT_INITIAL_OVERLOADED = 1
|
||||||
private const val COUNT_INITIAL_MULTIPLE = 2
|
private const val COUNT_INITIAL_MULTIPLE = 2
|
||||||
private val testProjectDir = TemporaryFolder()
|
private val testProjectDir = TemporaryFolder()
|
||||||
private val testProject = JarFilterProject(testProjectDir, "sanitise-constructor")
|
private val testProject = JarFilterProject(testProjectDir, "sanitise-delete-constructor")
|
||||||
|
|
||||||
@ClassRule
|
@ClassRule
|
||||||
@JvmField
|
@JvmField
|
||||||
@ -32,13 +33,13 @@ class SanitiseConstructorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteOverloadedLongConstructor() = checkClassWithLongParameter(
|
fun deleteOverloadedLongConstructor() = checkClassWithLongParameter(
|
||||||
"net.corda.gradle.HasOverloadedLongConstructor",
|
"net.corda.gradle.HasOverloadedLongConstructorToDelete",
|
||||||
COUNT_INITIAL_OVERLOADED
|
COUNT_INITIAL_OVERLOADED
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteMultipleLongConstructor() = checkClassWithLongParameter(
|
fun deleteMultipleLongConstructor() = checkClassWithLongParameter(
|
||||||
"net.corda.gradle.HasMultipleLongConstructors",
|
"net.corda.gradle.HasMultipleLongConstructorsToDelete",
|
||||||
COUNT_INITIAL_MULTIPLE
|
COUNT_INITIAL_MULTIPLE
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,9 +58,9 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||||
|
|
||||||
newInstance().also {
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
assertEquals(0, it.longData())
|
assertThat(noArg.callBy(emptyMap()).longData()).isEqualTo(0)
|
||||||
}
|
assertThat(newInstance().longData()).isEqualTo(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +76,7 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||||
|
|
||||||
|
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,13 +84,13 @@ class SanitiseConstructorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteOverloadedIntConstructor() = checkClassWithIntParameter(
|
fun deleteOverloadedIntConstructor() = checkClassWithIntParameter(
|
||||||
"net.corda.gradle.HasOverloadedIntConstructor",
|
"net.corda.gradle.HasOverloadedIntConstructorToDelete",
|
||||||
COUNT_INITIAL_OVERLOADED
|
COUNT_INITIAL_OVERLOADED
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteMultipleIntConstructor() = checkClassWithIntParameter(
|
fun deleteMultipleIntConstructor() = checkClassWithIntParameter(
|
||||||
"net.corda.gradle.HasMultipleIntConstructors",
|
"net.corda.gradle.HasMultipleIntConstructorsToDelete",
|
||||||
COUNT_INITIAL_MULTIPLE
|
COUNT_INITIAL_MULTIPLE
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -107,10 +109,9 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||||
|
|
||||||
//assertThat("", constructors, hasItem(isConstructor(""))
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
newInstance().also {
|
assertThat(noArg.callBy(emptyMap()).intData()).isEqualTo(0)
|
||||||
assertEquals(0, it.intData())
|
assertThat(newInstance().intData()).isEqualTo(0)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +127,7 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||||
|
|
||||||
|
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,13 +135,13 @@ class SanitiseConstructorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteOverloadedStringConstructor() = checkClassWithStringParameter(
|
fun deleteOverloadedStringConstructor() = checkClassWithStringParameter(
|
||||||
"net.corda.gradle.HasOverloadedStringConstructor",
|
"net.corda.gradle.HasOverloadedStringConstructorToDelete",
|
||||||
COUNT_INITIAL_OVERLOADED
|
COUNT_INITIAL_OVERLOADED
|
||||||
)
|
)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deleteMultipleStringConstructor() = checkClassWithStringParameter(
|
fun deleteMultipleStringConstructor() = checkClassWithStringParameter(
|
||||||
"net.corda.gradle.HasMultipleStringConstructors",
|
"net.corda.gradle.HasMultipleStringConstructorsToDelete",
|
||||||
COUNT_INITIAL_MULTIPLE
|
COUNT_INITIAL_MULTIPLE
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -158,9 +160,9 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||||
|
|
||||||
newInstance().also {
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
assertEquals(DEFAULT_MESSAGE, it.stringData())
|
assertThat(noArg.callBy(emptyMap()).stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||||
}
|
assertThat(newInstance().stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +178,57 @@ class SanitiseConstructorTest {
|
|||||||
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||||
|
|
||||||
|
assertNull("no-arg constructor exists", kotlin.noArgConstructor)
|
||||||
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteOverloadedComplexConstructor() {
|
||||||
|
val complexConstructor = isConstructor(COMPLEX_CONSTRUCTOR_CLASS, Int::class, String::class)
|
||||||
|
|
||||||
|
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||||
|
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||||
|
assertEquals(1, this.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||||
|
}
|
||||||
|
|
||||||
|
primary.callBy(mapOf(primary.parameters[1] to MESSAGE)).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||||
|
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||||
|
assertEquals(1, this.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(assertFailsWith<IllegalArgumentException> { primary.callBy(mapOf(primary.parameters[1] to MESSAGE)) })
|
||||||
|
.hasMessageContaining("No argument provided for a required parameter")
|
||||||
|
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor(String::class.java) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
package net.corda.gradle.jarfilter
|
||||||
|
|
||||||
|
import net.corda.gradle.jarfilter.matcher.*
|
||||||
|
import net.corda.gradle.unwanted.HasInt
|
||||||
|
import net.corda.gradle.unwanted.HasLong
|
||||||
|
import net.corda.gradle.unwanted.HasString
|
||||||
|
import org.assertj.core.api.Assertions.*
|
||||||
|
import org.hamcrest.core.IsCollectionContaining.hasItem
|
||||||
|
import org.junit.Assert.*
|
||||||
|
import org.junit.ClassRule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.RuleChain
|
||||||
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import org.junit.rules.TestRule
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import kotlin.jvm.kotlin
|
||||||
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
class SanitiseStubConstructorTest {
|
||||||
|
companion object {
|
||||||
|
private const val COMPLEX_CONSTRUCTOR_CLASS = "net.corda.gradle.HasOverloadedComplexConstructorToStub"
|
||||||
|
private const val COUNT_OVERLOADED = 1
|
||||||
|
private const val COUNT_MULTIPLE = 2
|
||||||
|
private val testProjectDir = TemporaryFolder()
|
||||||
|
private val testProject = JarFilterProject(testProjectDir, "sanitise-stub-constructor")
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
@JvmField
|
||||||
|
val rules: TestRule = RuleChain
|
||||||
|
.outerRule(testProjectDir)
|
||||||
|
.around(testProject)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubOverloadedLongConstructor() = checkClassWithLongParameter(
|
||||||
|
"net.corda.gradle.HasOverloadedLongConstructorToStub",
|
||||||
|
COUNT_OVERLOADED
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubMultipleLongConstructor() = checkClassWithLongParameter(
|
||||||
|
"net.corda.gradle.HasMultipleLongConstructorsToStub",
|
||||||
|
COUNT_MULTIPLE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun checkClassWithLongParameter(longClass: String, constructorCount: Int) {
|
||||||
|
val longConstructor = isConstructor(longClass, Long::class)
|
||||||
|
|
||||||
|
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||||
|
cl.load<HasLong>(longClass).apply {
|
||||||
|
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
|
||||||
|
assertEquals(BIG_NUMBER, it.longData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(J) not found", this, hasItem(longConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(noArg.callBy(emptyMap()).longData()).isEqualTo(0)
|
||||||
|
assertThat(newInstance().longData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||||
|
cl.load<HasLong>(longClass).apply {
|
||||||
|
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
|
||||||
|
assertEquals(BIG_NUMBER, it.longData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(J) not found", this, hasItem(longConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||||
|
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubOverloadedIntConstructor() = checkClassWithIntParameter(
|
||||||
|
"net.corda.gradle.HasOverloadedIntConstructorToStub",
|
||||||
|
COUNT_OVERLOADED
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubMultipleIntConstructor() = checkClassWithIntParameter(
|
||||||
|
"net.corda.gradle.HasMultipleIntConstructorsToStub",
|
||||||
|
COUNT_MULTIPLE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun checkClassWithIntParameter(intClass: String, constructorCount: Int) {
|
||||||
|
val intConstructor = isConstructor(intClass, Int::class)
|
||||||
|
|
||||||
|
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||||
|
cl.load<HasInt>(intClass).apply {
|
||||||
|
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
|
||||||
|
assertEquals(NUMBER, it.intData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(I) not found", this, hasItem(intConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(noArg.callBy(emptyMap()).intData()).isEqualTo(0)
|
||||||
|
assertThat(newInstance().intData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||||
|
cl.load<HasInt>(intClass).apply {
|
||||||
|
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
|
||||||
|
assertEquals(NUMBER, it.intData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(I) not found", this, hasItem(intConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||||
|
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubOverloadedStringConstructor() = checkClassWithStringParameter(
|
||||||
|
"net.corda.gradle.HasOverloadedStringConstructorToStub",
|
||||||
|
COUNT_OVERLOADED
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubMultipleStringConstructor() = checkClassWithStringParameter(
|
||||||
|
"net.corda.gradle.HasMultipleStringConstructorsToStub",
|
||||||
|
COUNT_MULTIPLE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun checkClassWithStringParameter(stringClass: String, constructorCount: Int) {
|
||||||
|
val stringConstructor = isConstructor(stringClass, String::class)
|
||||||
|
|
||||||
|
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||||
|
cl.load<HasString>(stringClass).apply {
|
||||||
|
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
|
||||||
|
assertEquals(MESSAGE, it.stringData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(noArg.callBy(emptyMap()).stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||||
|
assertThat(newInstance().stringData()).isEqualTo(DEFAULT_MESSAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||||
|
cl.load<HasString>(stringClass).apply {
|
||||||
|
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
|
||||||
|
assertEquals(MESSAGE, it.stringData())
|
||||||
|
}
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
|
||||||
|
assertEquals(constructorCount, this.size)
|
||||||
|
}
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
|
||||||
|
|
||||||
|
val noArg = kotlin.noArgConstructor ?: throw AssertionError("no-arg constructor missing")
|
||||||
|
assertThat(assertFailsWith<InvocationTargetException> { noArg.callBy(emptyMap()) }.targetException)
|
||||||
|
.isInstanceOf(UnsupportedOperationException::class.java)
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
assertThat(assertFailsWith<UnsupportedOperationException> { newInstance() })
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stubOverloadedComplexConstructor() {
|
||||||
|
val complexConstructor = isConstructor(COMPLEX_CONSTRUCTOR_CLASS, Int::class, String::class)
|
||||||
|
|
||||||
|
classLoaderFor(testProject.sourceJar).use { cl ->
|
||||||
|
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||||
|
assertEquals(1, this.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||||
|
}
|
||||||
|
|
||||||
|
primary.callBy(mapOf(primary.parameters[1] to MESSAGE)).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classLoaderFor(testProject.filteredJar).use { cl ->
|
||||||
|
cl.load<Any>(COMPLEX_CONSTRUCTOR_CLASS).apply {
|
||||||
|
kotlin.constructors.apply {
|
||||||
|
assertThat("<init>(Int,String) not found", this, hasItem(complexConstructor))
|
||||||
|
assertEquals(1, this.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
|
||||||
|
primary.call(NUMBER, MESSAGE).also { complex ->
|
||||||
|
assertThat((complex as HasString).stringData()).isEqualTo(MESSAGE)
|
||||||
|
assertThat((complex as HasInt).intData()).isEqualTo(NUMBER)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(assertFailsWith<InvocationTargetException> { primary.callBy(mapOf(primary.parameters[1] to MESSAGE)) }.targetException)
|
||||||
|
.isInstanceOf(kotlin.UnsupportedOperationException::class.java)
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
assertThat(assertFailsWith<InvocationTargetException> { getDeclaredConstructor(String::class.java).newInstance(MESSAGE) }.targetException)
|
||||||
|
.hasMessage("Method has been deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,6 +14,8 @@ import java.nio.file.Paths
|
|||||||
import java.util.stream.Collectors.*
|
import java.util.stream.Collectors.*
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KFunction
|
||||||
|
import kotlin.reflect.KParameter
|
||||||
|
|
||||||
const val DEFAULT_MESSAGE = "<default-value>"
|
const val DEFAULT_MESSAGE = "<default-value>"
|
||||||
const val MESSAGE = "Goodbye, Cruel World!"
|
const val MESSAGE = "Goodbye, Cruel World!"
|
||||||
@ -65,6 +67,9 @@ fun arrayOfJunk(size: Int) = ByteArray(size).apply {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val <T : Any> KClass<T>.noArgConstructor: KFunction<T>?
|
||||||
|
get() = constructors.firstOrNull { it.parameters.all(KParameter::isOptional) }
|
||||||
|
|
||||||
@Throws(MalformedURLException::class)
|
@Throws(MalformedURLException::class)
|
||||||
fun classLoaderFor(jar: Path) = URLClassLoader(arrayOf(jar.toUri().toURL()), classLoader)
|
fun classLoaderFor(jar: Path) = URLClassLoader(arrayOf(jar.toUri().toURL()), classLoader)
|
||||||
|
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
@file:Suppress("UNUSED")
|
|
||||||
package net.corda.gradle
|
|
||||||
|
|
||||||
import net.corda.gradle.jarfilter.DeleteMe
|
|
||||||
import net.corda.gradle.unwanted.*
|
|
||||||
|
|
||||||
private const val DEFAULT_MESSAGE = "<default-value>"
|
|
||||||
|
|
||||||
class HasOverloadedStringConstructor @JvmOverloads @DeleteMe constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
|
||||||
override fun stringData(): String = message
|
|
||||||
}
|
|
||||||
|
|
||||||
class HasOverloadedLongConstructor @JvmOverloads @DeleteMe constructor(private val data: Long = 0) : HasLong {
|
|
||||||
override fun longData(): Long = data
|
|
||||||
}
|
|
||||||
|
|
||||||
class HasOverloadedIntConstructor @JvmOverloads @DeleteMe constructor(private val data: Int = 0) : HasInt {
|
|
||||||
override fun intData(): Int = data
|
|
||||||
}
|
|
||||||
|
|
||||||
class HasMultipleStringConstructors(private val message: String) : HasString {
|
|
||||||
@DeleteMe constructor() : this(DEFAULT_MESSAGE)
|
|
||||||
override fun stringData(): String = message
|
|
||||||
}
|
|
||||||
|
|
||||||
class HasMultipleLongConstructors(private val data: Long) : HasLong {
|
|
||||||
@DeleteMe constructor() : this(0)
|
|
||||||
override fun longData(): Long = data
|
|
||||||
}
|
|
||||||
|
|
||||||
class HasMultipleIntConstructors(private val data: Int) : HasInt {
|
|
||||||
@DeleteMe constructor() : this(0)
|
|
||||||
override fun intData(): Int = data
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ sourceSets {
|
|||||||
main {
|
main {
|
||||||
kotlin {
|
kotlin {
|
||||||
srcDir files(
|
srcDir files(
|
||||||
'../resources/test/sanitise-constructor/kotlin',
|
'../resources/test/sanitise-delete-constructor/kotlin',
|
||||||
'../resources/test/annotations/kotlin'
|
'../resources/test/annotations/kotlin'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName = 'sanitise-constructor'
|
baseName = 'sanitise-delete-constructor'
|
||||||
}
|
}
|
||||||
|
|
||||||
import net.corda.gradle.jarfilter.JarFilterTask
|
import net.corda.gradle.jarfilter.JarFilterTask
|
@ -0,0 +1,48 @@
|
|||||||
|
@file:JvmName("HasOverloadedConstructorsToDelete")
|
||||||
|
@file:Suppress("UNUSED")
|
||||||
|
package net.corda.gradle
|
||||||
|
|
||||||
|
import net.corda.gradle.jarfilter.DeleteMe
|
||||||
|
import net.corda.gradle.unwanted.*
|
||||||
|
|
||||||
|
private const val DEFAULT_MESSAGE = "<default-value>"
|
||||||
|
|
||||||
|
class HasOverloadedStringConstructorToDelete @JvmOverloads @DeleteMe constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
||||||
|
override fun stringData(): String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasOverloadedLongConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Long = 0) : HasLong {
|
||||||
|
override fun longData(): Long = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasOverloadedIntConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Int = 0) : HasInt {
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This case is complex because:
|
||||||
|
* - The primary constructor has two parameters.
|
||||||
|
* - The first constructor parameter has a default value.
|
||||||
|
* - The second constructor parameter is mandatory.
|
||||||
|
*/
|
||||||
|
class HasOverloadedComplexConstructorToDelete @JvmOverloads @DeleteMe constructor(private val data: Int = 0, private val message: String)
|
||||||
|
: HasInt, HasString
|
||||||
|
{
|
||||||
|
override fun stringData(): String = message
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleStringConstructorsToDelete(private val message: String) : HasString {
|
||||||
|
@DeleteMe constructor() : this(DEFAULT_MESSAGE)
|
||||||
|
override fun stringData(): String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleLongConstructorsToDelete(private val data: Long) : HasLong {
|
||||||
|
@DeleteMe constructor() : this(0)
|
||||||
|
override fun longData(): Long = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleIntConstructorsToDelete(private val data: Int) : HasInt {
|
||||||
|
@DeleteMe constructor() : this(0)
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version '$kotlin_version'
|
||||||
|
id 'net.corda.plugins.jar-filter'
|
||||||
|
}
|
||||||
|
apply from: 'repositories.gradle'
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
kotlin {
|
||||||
|
srcDir files(
|
||||||
|
'../resources/test/sanitise-stub-constructor/kotlin',
|
||||||
|
'../resources/test/annotations/kotlin'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||||
|
compileOnly files('../../unwanteds/build/libs/unwanteds.jar')
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
baseName = 'sanitise-stub-constructor'
|
||||||
|
}
|
||||||
|
|
||||||
|
import net.corda.gradle.jarfilter.JarFilterTask
|
||||||
|
task jarFilter(type: JarFilterTask) {
|
||||||
|
jars jar
|
||||||
|
annotations {
|
||||||
|
forStub = ["net.corda.gradle.jarfilter.StubMeOut"]
|
||||||
|
forSanitise = ["net.corda.gradle.jarfilter.StubMeOut"]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
@file:JvmName("HasOverloadedConstructorsToStub")
|
||||||
|
@file:Suppress("UNUSED")
|
||||||
|
package net.corda.gradle
|
||||||
|
|
||||||
|
import net.corda.gradle.jarfilter.StubMeOut
|
||||||
|
import net.corda.gradle.unwanted.*
|
||||||
|
|
||||||
|
private const val DEFAULT_MESSAGE = "<default-value>"
|
||||||
|
|
||||||
|
class HasOverloadedStringConstructorToStub @JvmOverloads @StubMeOut constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
|
||||||
|
override fun stringData(): String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasOverloadedLongConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Long = 0) : HasLong {
|
||||||
|
override fun longData(): Long = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasOverloadedIntConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Int = 0) : HasInt {
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This case is complex because:
|
||||||
|
* - The primary constructor has two parameters.
|
||||||
|
* - The first constructor parameter has a default value.
|
||||||
|
* - The second constructor parameter is mandatory.
|
||||||
|
*/
|
||||||
|
class HasOverloadedComplexConstructorToStub @JvmOverloads @StubMeOut constructor(private val data: Int = 0, private val message: String)
|
||||||
|
: HasInt, HasString
|
||||||
|
{
|
||||||
|
override fun stringData(): String = message
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleStringConstructorsToStub(private val message: String) : HasString {
|
||||||
|
@StubMeOut constructor() : this(DEFAULT_MESSAGE)
|
||||||
|
override fun stringData(): String = message
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleLongConstructorsToStub(private val data: Long) : HasLong {
|
||||||
|
@StubMeOut constructor() : this(0)
|
||||||
|
override fun longData(): Long = data
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasMultipleIntConstructorsToStub(private val data: Int) : HasInt {
|
||||||
|
@StubMeOut constructor() : this(0)
|
||||||
|
override fun intData(): Int = data
|
||||||
|
}
|
@ -10,6 +10,7 @@ import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
|||||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||||
import net.corda.serialization.internal.*
|
import net.corda.serialization.internal.*
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.amqp.amqpMagic
|
import net.corda.serialization.internal.amqp.amqpMagic
|
||||||
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
||||||
@ -21,12 +22,12 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
*/
|
*/
|
||||||
class AMQPClientSerializationScheme(
|
class AMQPClientSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
constructor() : this(emptySet(), ConcurrentHashMap())
|
constructor() : this(emptySet(), AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Call from main only. */
|
/** Call from main only. */
|
||||||
@ -52,7 +53,7 @@ class AMQPClientSerializationScheme(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
return SerializerFactory(context.whitelist, ClassLoader.getSystemClassLoader(), context.lenientCarpenterEnabled).apply {
|
return SerializerFactory(context.whitelist, context.deserializationClassLoader, context.lenientCarpenterEnabled).apply {
|
||||||
register(RpcClientObservableSerializer)
|
register(RpcClientObservableSerializer)
|
||||||
register(RpcClientCordaFutureSerializer(this))
|
register(RpcClientCordaFutureSerializer(this))
|
||||||
register(RxNotificationSerializer(this))
|
register(RxNotificationSerializer(this))
|
||||||
|
@ -30,10 +30,10 @@ object IdentitySyncFlow {
|
|||||||
* recipient, enabling them to verify that identity.
|
* recipient, enabling them to verify that identity.
|
||||||
*/
|
*/
|
||||||
// TODO: Can this be triggered automatically from [SendTransactionFlow]?
|
// TODO: Can this be triggered automatically from [SendTransactionFlow]?
|
||||||
class Send(val otherSideSessions: Set<FlowSession>,
|
class Send @JvmOverloads constructor(val otherSideSessions: Set<FlowSession>,
|
||||||
val tx: WireTransaction,
|
val tx: WireTransaction,
|
||||||
override val progressTracker: ProgressTracker) : FlowLogic<Unit>() {
|
override val progressTracker: ProgressTracker = tracker()) : FlowLogic<Unit>() {
|
||||||
constructor(otherSide: FlowSession, tx: WireTransaction) : this(setOf(otherSide), tx, tracker())
|
constructor(otherSide: FlowSession, tx: WireTransaction) : this(setOf(otherSide), tx)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
object SYNCING_IDENTITIES : ProgressTracker.Step("Syncing identities")
|
object SYNCING_IDENTITIES : ProgressTracker.Step("Syncing identities")
|
||||||
|
@ -184,6 +184,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
|
|||||||
defaultTasks "determinise"
|
defaultTasks "determinise"
|
||||||
determinise.finalizedBy metafix
|
determinise.finalizedBy metafix
|
||||||
metafix.finalizedBy checkDeterminism
|
metafix.finalizedBy checkDeterminism
|
||||||
|
assemble.dependsOn checkDeterminism
|
||||||
|
|
||||||
def deterministicJar = metafix.outputs.files.singleFile
|
def deterministicJar = metafix.outputs.files.singleFile
|
||||||
artifacts {
|
artifacts {
|
||||||
|
@ -2,12 +2,13 @@ package net.corda.deterministic.common
|
|||||||
|
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
import net.corda.core.serialization.ClassWhitelist
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationContext.UseCase.*
|
import net.corda.core.serialization.SerializationContext.UseCase.P2P
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||||
import net.corda.serialization.internal.*
|
import net.corda.serialization.internal.*
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.amqp.amqpMagic
|
import net.corda.serialization.internal.amqp.amqpMagic
|
||||||
import org.junit.rules.TestRule
|
import org.junit.rules.TestRule
|
||||||
@ -59,7 +60,7 @@ class LocalSerializationRule(private val label: String) : TestRule {
|
|||||||
|
|
||||||
private fun createTestSerializationEnv(): SerializationEnvironmentImpl {
|
private fun createTestSerializationEnv(): SerializationEnvironmentImpl {
|
||||||
val factory = SerializationFactoryImpl(mutableMapOf()).apply {
|
val factory = SerializationFactoryImpl(mutableMapOf()).apply {
|
||||||
registerScheme(AMQPSerializationScheme(emptySet(), mutableMapOf()))
|
registerScheme(AMQPSerializationScheme(emptySet(), AccessOrderLinkedHashMap(128)))
|
||||||
}
|
}
|
||||||
return object : SerializationEnvironmentImpl(factory, AMQP_P2P_CONTEXT) {
|
return object : SerializationEnvironmentImpl(factory, AMQP_P2P_CONTEXT) {
|
||||||
override fun toString() = "testSerializationEnv($label)"
|
override fun toString() = "testSerializationEnv($label)"
|
||||||
@ -68,7 +69,7 @@ class LocalSerializationRule(private val label: String) : TestRule {
|
|||||||
|
|
||||||
private class AMQPSerializationScheme(
|
private class AMQPSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
|
@ -184,7 +184,6 @@ interface SerializationContext {
|
|||||||
/**
|
/**
|
||||||
* Helper method to return a new context based on this context with the deserialization class loader changed.
|
* Helper method to return a new context based on this context with the deserialization class loader changed.
|
||||||
*/
|
*/
|
||||||
@DeleteForDJVM
|
|
||||||
fun withClassLoader(classLoader: ClassLoader): SerializationContext
|
fun withClassLoader(classLoader: ClassLoader): SerializationContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Binary file not shown.
@ -6,6 +6,7 @@ import net.corda.core.serialization.SerializationContext
|
|||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.serialization.internal.CordaSerializationMagic
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -16,11 +17,11 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
*/
|
*/
|
||||||
class AMQPServerSerializationScheme(
|
class AMQPServerSerializationScheme(
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
|
||||||
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
constructor() : this(emptySet(), ConcurrentHashMap())
|
constructor() : this(emptySet(), AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.node.internal.serialization.testutils.serializationContext
|
|||||||
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
|
||||||
import net.corda.node.services.messaging.ObservableSubscription
|
import net.corda.node.services.messaging.ObservableSubscription
|
||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.serialization.internal.amqp.DeserializationInput
|
import net.corda.serialization.internal.amqp.DeserializationInput
|
||||||
import net.corda.serialization.internal.amqp.SerializationOutput
|
import net.corda.serialization.internal.amqp.SerializationOutput
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
@ -59,7 +60,7 @@ class RoundTripObservableSerializerTests {
|
|||||||
@Test
|
@Test
|
||||||
fun roundTripTest1() {
|
fun roundTripTest1() {
|
||||||
val serializationScheme = AMQPRoundTripRPCSerializationScheme(
|
val serializationScheme = AMQPRoundTripRPCSerializationScheme(
|
||||||
serializationContext, emptySet(), ConcurrentHashMap())
|
serializationContext, emptySet(), AccessOrderLinkedHashMap { 128 })
|
||||||
|
|
||||||
// Fake up a message ID, needs to be used on both "sides". The server setting it in the subscriptionMap,
|
// Fake up a message ID, needs to be used on both "sides". The server setting it in the subscriptionMap,
|
||||||
// the client as a property of the deserializer which, in the actual RPC client, is pulled off of
|
// the client as a property of the deserializer which, in the actual RPC client, is pulled off of
|
||||||
|
@ -11,6 +11,7 @@ import net.corda.serialization.internal.CordaSerializationMagic
|
|||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import net.corda.serialization.internal.AllWhitelist
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
|
import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap
|
||||||
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
|
import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +23,7 @@ import net.corda.client.rpc.internal.ObservableContext as ClientObservableContex
|
|||||||
class AMQPRoundTripRPCSerializationScheme(
|
class AMQPRoundTripRPCSerializationScheme(
|
||||||
private val serializationContext: SerializationContext,
|
private val serializationContext: SerializationContext,
|
||||||
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>)
|
serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>)
|
||||||
: AbstractAMQPSerializationScheme(
|
: AbstractAMQPSerializationScheme(
|
||||||
cordappCustomSerializers, serializerFactoriesForContexts
|
cordappCustomSerializers, serializerFactoriesForContexts
|
||||||
) {
|
) {
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package net.corda.serialization.internal.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
|
import org.hamcrest.CoreMatchers.`is`
|
||||||
|
import org.hamcrest.CoreMatchers.nullValue
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class OptionalSerializerTest {
|
||||||
|
@Test
|
||||||
|
fun `should convert optional with item to proxy`() {
|
||||||
|
val opt = Optional.of("GenericTestString")
|
||||||
|
val proxy = OptionalSerializer(Mockito.mock(SerializerFactory::class.java)).toProxy(opt)
|
||||||
|
Assert.assertThat(proxy.item, `is`<Any>("GenericTestString"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should convert optional without item to empty proxy`() {
|
||||||
|
val opt = Optional.ofNullable<String>(null)
|
||||||
|
val proxy = OptionalSerializer(Mockito.mock(SerializerFactory::class.java)).toProxy(opt)
|
||||||
|
Assert.assertThat(proxy.item, `is`(nullValue()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should convert proxy without item to empty optional `() {
|
||||||
|
val proxy = OptionalSerializer.OptionalProxy(null)
|
||||||
|
val opt = OptionalSerializer(Mockito.mock(SerializerFactory::class.java)).fromProxy(proxy)
|
||||||
|
Assert.assertThat(opt.isPresent, `is`(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should convert proxy with item to empty optional `() {
|
||||||
|
val proxy = OptionalSerializer.OptionalProxy("GenericTestString")
|
||||||
|
val opt = OptionalSerializer(Mockito.mock(SerializerFactory::class.java)).fromProxy(proxy)
|
||||||
|
Assert.assertThat(opt.get(), `is`<Any>("GenericTestString"))
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,7 @@ task patchSerialization(type: Zip, dependsOn: serializationJarTask) {
|
|||||||
exclude 'net/corda/serialization/internal/DefaultWhitelist*'
|
exclude 'net/corda/serialization/internal/DefaultWhitelist*'
|
||||||
exclude 'net/corda/serialization/internal/amqp/AMQPSerializerFactories*'
|
exclude 'net/corda/serialization/internal/amqp/AMQPSerializerFactories*'
|
||||||
exclude 'net/corda/serialization/internal/amqp/AMQPStreams*'
|
exclude 'net/corda/serialization/internal/amqp/AMQPStreams*'
|
||||||
|
exclude 'net/corda/serialization/internal/amqp/AMQPSerializationThreadContext*'
|
||||||
}
|
}
|
||||||
|
|
||||||
reproducibleFileOrder = true
|
reproducibleFileOrder = true
|
||||||
@ -166,6 +167,7 @@ task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
|
|||||||
defaultTasks "determinise"
|
defaultTasks "determinise"
|
||||||
determinise.finalizedBy metafix
|
determinise.finalizedBy metafix
|
||||||
metafix.finalizedBy checkDeterminism
|
metafix.finalizedBy checkDeterminism
|
||||||
|
assemble.dependsOn checkDeterminism
|
||||||
|
|
||||||
def deterministicJar = metafix.outputs.files.singleFile
|
def deterministicJar = metafix.outputs.files.singleFile
|
||||||
artifacts {
|
artifacts {
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
@file:JvmName("AMQPSerializationThreadContext")
|
||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
fun getContextClassLoader(): ClassLoader {
|
||||||
|
return ClassLoader.getSystemClassLoader()
|
||||||
|
}
|
@ -21,10 +21,13 @@ import net.corda.core.internal.objectOrNewInstance
|
|||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.serialization.internal.*
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
|
import net.corda.serialization.internal.DefaultWhitelist
|
||||||
|
import net.corda.serialization.internal.MutableClassWhitelist
|
||||||
|
import net.corda.serialization.internal.SerializationScheme
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
|
|
||||||
val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic
|
val AMQP_ENABLED get() = SerializationDefaults.P2P_CONTEXT.preferredSerializationVersion == amqpMagic
|
||||||
|
|
||||||
@ -46,18 +49,20 @@ interface SerializerFactoryFactory {
|
|||||||
|
|
||||||
@KeepForDJVM
|
@KeepForDJVM
|
||||||
abstract class AbstractAMQPSerializationScheme(
|
abstract class AbstractAMQPSerializationScheme(
|
||||||
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
|
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
|
||||||
private val serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>,
|
private val serializerFactoriesForContexts: AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>,
|
||||||
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
|
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
|
||||||
) : SerializationScheme {
|
) : SerializationScheme {
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
|
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, AccessOrderLinkedHashMap(128))
|
||||||
|
|
||||||
// TODO: This method of initialisation for the Whitelist and plugin serializers will have to change
|
// TODO: This method of initialisation for the Whitelist and plugin serializers will have to change
|
||||||
// when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way
|
// when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way
|
||||||
companion object {
|
companion object {
|
||||||
const val SCAN_SPEC_PROP_NAME = "amqp.custom.serialization.scanSpec"
|
const val SCAN_SPEC_PROP_NAME = "amqp.custom.serialization.scanSpec"
|
||||||
|
|
||||||
|
private val logger = contextLogger()
|
||||||
|
|
||||||
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
private val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
||||||
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
|
ServiceLoader.load(SerializationWhitelist::class.java, this::class.java.classLoader).toList() + DefaultWhitelist
|
||||||
}
|
}
|
||||||
@ -66,8 +71,10 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
val scanSpec: String? = System.getProperty(SCAN_SPEC_PROP_NAME)
|
val scanSpec: String? = System.getProperty(SCAN_SPEC_PROP_NAME)
|
||||||
|
|
||||||
if (scanSpec == null) {
|
if (scanSpec == null) {
|
||||||
|
logger.debug("scanSpec not set, not scanning for Custom Serializers")
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
} else {
|
||||||
|
logger.debug("scanSpec = \"$scanSpec\", scanning for Custom Serializers")
|
||||||
scanClasspathForSerializers(scanSpec)
|
scanClasspathForSerializers(scanSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +90,8 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
val List<Cordapp>.customSerializers get() = flatMap { it.serializationCustomSerializers }.toSet()
|
val List<Cordapp>.customSerializers
|
||||||
|
get() = flatMap { it.serializationCustomSerializers }.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter "context" is unused directly but passed in by reflection. Removing it will cause failures.
|
// Parameter "context" is unused directly but passed in by reflection. Removing it will cause failures.
|
||||||
@ -105,6 +113,7 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
register(net.corda.serialization.internal.amqp.custom.ZoneIdSerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.ZoneIdSerializer(this))
|
||||||
register(net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer(this))
|
||||||
register(net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer(this))
|
||||||
|
register(net.corda.serialization.internal.amqp.custom.OptionalSerializer(this))
|
||||||
register(net.corda.serialization.internal.amqp.custom.YearSerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.YearSerializer(this))
|
||||||
register(net.corda.serialization.internal.amqp.custom.YearMonthSerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.YearMonthSerializer(this))
|
||||||
register(net.corda.serialization.internal.amqp.custom.MonthDaySerializer(this))
|
register(net.corda.serialization.internal.amqp.custom.MonthDaySerializer(this))
|
||||||
@ -131,6 +140,7 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
|
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logger.debug("Custom Serializer list loaded - not scanning classpath")
|
||||||
cordappCustomSerializers.forEach { customSerializer ->
|
cordappCustomSerializers.forEach { customSerializer ->
|
||||||
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
|
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
|
||||||
}
|
}
|
||||||
@ -157,10 +167,11 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
|
protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
|
||||||
|
|
||||||
// Not used as a simple direct import to facilitate testing
|
// Not used as a simple direct import to facilitate testing
|
||||||
open val publicKeySerializer : CustomSerializer<*> = net.corda.serialization.internal.amqp.custom.PublicKeySerializer
|
open val publicKeySerializer: CustomSerializer<*> = net.corda.serialization.internal.amqp.custom.PublicKeySerializer
|
||||||
|
|
||||||
private fun getSerializerFactory(context: SerializationContext): SerializerFactory {
|
private fun getSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
return serializerFactoriesForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
|
return synchronized(serializerFactoriesForContexts) {
|
||||||
|
serializerFactoriesForContexts.computeIfAbsent(Pair(context.whitelist, context.deserializationClassLoader)) {
|
||||||
when (context.useCase) {
|
when (context.useCase) {
|
||||||
SerializationContext.UseCase.Checkpoint ->
|
SerializationContext.UseCase.Checkpoint ->
|
||||||
throw IllegalStateException("AMQP should not be used for checkpoint serialization.")
|
throw IllegalStateException("AMQP should not be used for checkpoint serialization.")
|
||||||
@ -174,15 +185,23 @@ abstract class AbstractAMQPSerializationScheme(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
override fun <T : Any> deserialize(byteSequence: ByteSequence, clazz: Class<T>, context: SerializationContext): T {
|
||||||
val serializerFactory = getSerializerFactory(context)
|
var contextToUse = context
|
||||||
|
if (context.useCase == SerializationContext.UseCase.RPCClient) {
|
||||||
|
contextToUse = context.withClassLoader(getContextClassLoader())
|
||||||
|
}
|
||||||
|
val serializerFactory = getSerializerFactory(contextToUse)
|
||||||
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context)
|
return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
|
override fun <T : Any> serialize(obj: T, context: SerializationContext): SerializedBytes<T> {
|
||||||
val serializerFactory = getSerializerFactory(context)
|
var contextToUse = context
|
||||||
|
if (context.useCase == SerializationContext.UseCase.RPCClient) {
|
||||||
|
contextToUse = context.withClassLoader(getContextClassLoader())
|
||||||
|
}
|
||||||
|
val serializerFactory = getSerializerFactory(contextToUse)
|
||||||
return SerializationOutput(serializerFactory).serialize(obj, context)
|
return SerializationOutput(serializerFactory).serialize(obj, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
@file:JvmName("AMQPSerializationThreadContext")
|
||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
fun getContextClassLoader(): ClassLoader {
|
||||||
|
return Thread.currentThread().contextClassLoader
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
import net.corda.core.KeepForDJVM
|
||||||
|
|
||||||
|
@KeepForDJVM
|
||||||
|
class AccessOrderLinkedHashMap<K, V>(private val maxSize: Int) : LinkedHashMap<K, V>(16, 0.75f, true) {
|
||||||
|
constructor(loader: () -> Int) : this(loader.invoke())
|
||||||
|
|
||||||
|
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
|
||||||
|
return this.size > maxSize
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
@ -55,8 +56,10 @@ const val PROXY_TYPE = 1
|
|||||||
*/
|
*/
|
||||||
class CorDappCustomSerializer(
|
class CorDappCustomSerializer(
|
||||||
private val serializer: SerializationCustomSerializer<*, *>,
|
private val serializer: SerializationCustomSerializer<*, *>,
|
||||||
factory: SerializerFactory) : AMQPSerializer<Any>, SerializerFor {
|
factory: SerializerFactory
|
||||||
|
) : AMQPSerializer<Any>, SerializerFor {
|
||||||
override val revealSubclassesInSchema: Boolean get() = false
|
override val revealSubclassesInSchema: Boolean get() = false
|
||||||
|
|
||||||
private val types = serializer::class.supertypes.filter { it.jvmErasure == SerializationCustomSerializer::class }
|
private val types = serializer::class.supertypes.filter { it.jvmErasure == SerializationCustomSerializer::class }
|
||||||
.flatMap { it.arguments }
|
.flatMap { it.arguments }
|
||||||
.map { it.type!!.javaType }
|
.map { it.type!!.javaType }
|
||||||
@ -95,6 +98,12 @@ class CorDappCustomSerializer(
|
|||||||
) = uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
|
) = uncheckedCast<SerializationCustomSerializer<*, *>, SerializationCustomSerializer<Any?, Any?>>(
|
||||||
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)))!!
|
serializer).fromProxy(uncheckedCast(proxySerializer.readObject(obj, schemas, input, context)))!!
|
||||||
|
|
||||||
override fun isSerializerFor(clazz: Class<*>) = clazz == type
|
/**
|
||||||
|
* For 3rd party plugin serializers we are going to exist on exact type matching. i.e. we will
|
||||||
|
* not support base class serializers for derivedtypes
|
||||||
|
*/
|
||||||
|
override fun isSerializerFor(clazz: Class<*>) : Boolean {
|
||||||
|
return type.asClass()?.let { TypeToken.of(it) == TypeToken.of(clazz) } ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
|
|||||||
"in the Java compiler. Alternately, provide a proxy serializer " +
|
"in the Java compiler. Alternately, provide a proxy serializer " +
|
||||||
"(SerializationCustomSerializer) if recompiling isn't an option")
|
"(SerializationCustomSerializer) if recompiling isn't an option")
|
||||||
|
|
||||||
Pair(PrivatePropertyReader(field, type), field.genericType)
|
Pair(PrivatePropertyReader(field, type), resolveTypeVariables(field.genericType, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
this += PropertyAccessorConstructor(
|
this += PropertyAccessorConstructor(
|
||||||
|
@ -42,6 +42,8 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ
|
|||||||
* @property evolutionSerializerGetter controls how evolution serializers are generated by the factory. The normal
|
* @property evolutionSerializerGetter controls how evolution serializers are generated by the factory. The normal
|
||||||
* use case is an [EvolutionSerializer] type is returned. However, in some scenarios, primarily testing, this
|
* use case is an [EvolutionSerializer] type is returned. However, in some scenarios, primarily testing, this
|
||||||
* can be altered to fit the requirements of the test.
|
* can be altered to fit the requirements of the test.
|
||||||
|
* @property onlyCustomSerializers used for testing, when set will cause the factory to throw a
|
||||||
|
* [NotSerializableException] if it cannot find a registered custom serializer for a given type
|
||||||
*/
|
*/
|
||||||
// TODO: support for intern-ing of deserialized objects for some core types (e.g. PublicKey) for memory efficiency
|
// TODO: support for intern-ing of deserialized objects for some core types (e.g. PublicKey) for memory efficiency
|
||||||
// TODO: maybe support for caching of serialized form of some core types for performance
|
// TODO: maybe support for caching of serialized form of some core types for performance
|
||||||
@ -64,13 +66,15 @@ open class SerializerFactory(
|
|||||||
private val serializersByType: MutableMap<Type, AMQPSerializer<Any>>,
|
private val serializersByType: MutableMap<Type, AMQPSerializer<Any>>,
|
||||||
val serializersByDescriptor: MutableMap<Any, AMQPSerializer<Any>>,
|
val serializersByDescriptor: MutableMap<Any, AMQPSerializer<Any>>,
|
||||||
private val customSerializers: MutableList<SerializerFor>,
|
private val customSerializers: MutableList<SerializerFor>,
|
||||||
val transformsCache: MutableMap<String, EnumMap<TransformTypes, MutableList<Transform>>>
|
val transformsCache: MutableMap<String, EnumMap<TransformTypes, MutableList<Transform>>>,
|
||||||
|
private val onlyCustomSerializers: Boolean = false
|
||||||
) {
|
) {
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
constructor(whitelist: ClassWhitelist,
|
constructor(whitelist: ClassWhitelist,
|
||||||
classCarpenter: ClassCarpenter,
|
classCarpenter: ClassCarpenter,
|
||||||
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
|
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
|
||||||
fingerPrinter: FingerPrinter = SerializerFingerPrinter()
|
fingerPrinter: FingerPrinter = SerializerFingerPrinter(),
|
||||||
|
onlyCustomSerializers: Boolean = false
|
||||||
) : this(
|
) : this(
|
||||||
whitelist,
|
whitelist,
|
||||||
classCarpenter,
|
classCarpenter,
|
||||||
@ -79,7 +83,8 @@ open class SerializerFactory(
|
|||||||
ConcurrentHashMap(),
|
ConcurrentHashMap(),
|
||||||
ConcurrentHashMap(),
|
ConcurrentHashMap(),
|
||||||
CopyOnWriteArrayList(),
|
CopyOnWriteArrayList(),
|
||||||
ConcurrentHashMap()
|
ConcurrentHashMap(),
|
||||||
|
onlyCustomSerializers
|
||||||
)
|
)
|
||||||
|
|
||||||
@DeleteForDJVM
|
@DeleteForDJVM
|
||||||
@ -87,8 +92,14 @@ open class SerializerFactory(
|
|||||||
carpenterClassLoader: ClassLoader,
|
carpenterClassLoader: ClassLoader,
|
||||||
lenientCarpenter: Boolean = false,
|
lenientCarpenter: Boolean = false,
|
||||||
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
|
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
|
||||||
fingerPrinter: FingerPrinter = SerializerFingerPrinter()
|
fingerPrinter: FingerPrinter = SerializerFingerPrinter(),
|
||||||
) : this(whitelist, ClassCarpenterImpl(whitelist, carpenterClassLoader, lenientCarpenter), evolutionSerializerGetter, fingerPrinter)
|
onlyCustomSerializers: Boolean = false
|
||||||
|
) : this(
|
||||||
|
whitelist,
|
||||||
|
ClassCarpenterImpl(whitelist, carpenterClassLoader, lenientCarpenter),
|
||||||
|
evolutionSerializerGetter,
|
||||||
|
fingerPrinter,
|
||||||
|
onlyCustomSerializers)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fingerPrinter.setOwner(this)
|
fingerPrinter.setOwner(this)
|
||||||
@ -246,6 +257,7 @@ open class SerializerFactory(
|
|||||||
* that expects to find getters and a constructor with a parameter for each property.
|
* that expects to find getters and a constructor with a parameter for each property.
|
||||||
*/
|
*/
|
||||||
open fun register(customSerializer: CustomSerializer<out Any>) {
|
open fun register(customSerializer: CustomSerializer<out Any>) {
|
||||||
|
logger.debug ("action=\"Registering custom serializer\", class=\"${customSerializer.type}\"")
|
||||||
if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) {
|
if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) {
|
||||||
customSerializers += customSerializer
|
customSerializers += customSerializer
|
||||||
serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer
|
serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer
|
||||||
@ -256,6 +268,7 @@ open class SerializerFactory(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun registerExternal(customSerializer: CorDappCustomSerializer) {
|
fun registerExternal(customSerializer: CorDappCustomSerializer) {
|
||||||
|
logger.debug ("action=\"Registering external serializer\", class=\"${customSerializer.type}\"")
|
||||||
if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) {
|
if (!serializersByDescriptor.containsKey(customSerializer.typeDescriptor)) {
|
||||||
customSerializers += customSerializer
|
customSerializers += customSerializer
|
||||||
serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer
|
serializersByDescriptor[customSerializer.typeDescriptor] = customSerializer
|
||||||
@ -276,7 +289,7 @@ open class SerializerFactory(
|
|||||||
// doesn't match that of the serialised object then we are dealing with different
|
// doesn't match that of the serialised object then we are dealing with different
|
||||||
// instance of the class, as such we need to build an EvolutionSerializer
|
// instance of the class, as such we need to build an EvolutionSerializer
|
||||||
if (serialiser.typeDescriptor != typeNotation.descriptor.name) {
|
if (serialiser.typeDescriptor != typeNotation.descriptor.name) {
|
||||||
logger.info("typeNotation=${typeNotation.name} action=\"requires Evolution\"")
|
logger.debug("typeNotation=${typeNotation.name} action=\"requires Evolution\"")
|
||||||
getEvolutionSerializer(typeNotation, serialiser, schemaAndDescriptor.schemas)
|
getEvolutionSerializer(typeNotation, serialiser, schemaAndDescriptor.schemas)
|
||||||
}
|
}
|
||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
@ -285,7 +298,7 @@ open class SerializerFactory(
|
|||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info("typeNotation=\"${typeNotation.name}\" action=\"carpentry required\"")
|
logger.debug("typeNotation=\"${typeNotation.name}\" action=\"carpentry required\"")
|
||||||
}
|
}
|
||||||
metaSchema.buildFor(typeNotation, classloader)
|
metaSchema.buildFor(typeNotation, classloader)
|
||||||
}
|
}
|
||||||
@ -338,8 +351,12 @@ open class SerializerFactory(
|
|||||||
return get(type.asClass() ?: throw NotSerializableException("Unable to build composite type for $type"), type)
|
return get(type.asClass() ?: throw NotSerializableException("Unable to build composite type for $type"), type)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeClassSerializer(clazz: Class<*>, type: Type, declaredType: Type): AMQPSerializer<Any> = serializersByType.computeIfAbsent(type) {
|
private fun makeClassSerializer(
|
||||||
logger.debug("class=${clazz.simpleName}, type=$type is a composite type")
|
clazz: Class<*>,
|
||||||
|
type: Type,
|
||||||
|
declaredType: Type
|
||||||
|
): AMQPSerializer<Any> = serializersByType.computeIfAbsent(type) {
|
||||||
|
logger.debug { "class=${clazz.simpleName}, type=$type is a composite type" }
|
||||||
if (clazz.isSynthetic) {
|
if (clazz.isSynthetic) {
|
||||||
// Explicitly ban synthetic classes, we have no way of recreating them when deserializing. This also
|
// Explicitly ban synthetic classes, we have no way of recreating them when deserializing. This also
|
||||||
// captures Lambda expressions and other anonymous functions
|
// captures Lambda expressions and other anonymous functions
|
||||||
@ -348,6 +365,9 @@ open class SerializerFactory(
|
|||||||
AMQPPrimitiveSerializer(clazz)
|
AMQPPrimitiveSerializer(clazz)
|
||||||
} else {
|
} else {
|
||||||
findCustomSerializer(clazz, declaredType) ?: run {
|
findCustomSerializer(clazz, declaredType) ?: run {
|
||||||
|
if (onlyCustomSerializers) {
|
||||||
|
throw NotSerializableException("Only allowing custom serializers")
|
||||||
|
}
|
||||||
if (type.isArray()) {
|
if (type.isArray()) {
|
||||||
// Don't need to check the whitelist since each element will come back through the whitelisting process.
|
// Don't need to check the whitelist since each element will come back through the whitelisting process.
|
||||||
if (clazz.componentType.isPrimitive) PrimArraySerializer.make(type, this)
|
if (clazz.componentType.isPrimitive) PrimArraySerializer.make(type, this)
|
||||||
@ -373,9 +393,15 @@ open class SerializerFactory(
|
|||||||
for (customSerializer in customSerializers) {
|
for (customSerializer in customSerializers) {
|
||||||
if (customSerializer.isSerializerFor(clazz)) {
|
if (customSerializer.isSerializerFor(clazz)) {
|
||||||
val declaredSuperClass = declaredType.asClass()?.superclass
|
val declaredSuperClass = declaredType.asClass()?.superclass
|
||||||
|
|
||||||
|
|
||||||
return if (declaredSuperClass == null
|
return if (declaredSuperClass == null
|
||||||
|| !customSerializer.isSerializerFor(declaredSuperClass)
|
|| !customSerializer.isSerializerFor(declaredSuperClass)
|
||||||
|| !customSerializer.revealSubclassesInSchema) {
|
|| !customSerializer.revealSubclassesInSchema
|
||||||
|
) {
|
||||||
|
logger.debug ("action=\"Using custom serializer\", class=${clazz.typeName}, " +
|
||||||
|
"declaredType=${declaredType.typeName}")
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
customSerializer as? AMQPSerializer<Any>
|
customSerializer as? AMQPSerializer<Any>
|
||||||
} else {
|
} else {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.corda.serialization.internal.amqp.custom
|
||||||
|
|
||||||
|
import net.corda.core.KeepForDJVM
|
||||||
|
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||||
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
|
import java.time.OffsetTime
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializer for [OffsetTime] that uses a proxy object to write out the time and zone offset.
|
||||||
|
*/
|
||||||
|
class OptionalSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Optional<*>, OptionalSerializer.OptionalProxy>(Optional::class.java, OptionalProxy::class.java, factory) {
|
||||||
|
|
||||||
|
public override fun toProxy(obj: java.util.Optional<*>): OptionalProxy {
|
||||||
|
return OptionalProxy(obj.orElse(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun fromProxy(proxy: OptionalProxy): Optional<*> {
|
||||||
|
return Optional.ofNullable(proxy.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
@KeepForDJVM
|
||||||
|
data class OptionalProxy(val item: Any?)
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package net.corda.serialization.internal.amqp;
|
||||||
|
|
||||||
|
import net.corda.core.serialization.CordaSerializable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@CordaSerializable
|
||||||
|
public class DummyOptional<T> {
|
||||||
|
|
||||||
|
private final T item;
|
||||||
|
|
||||||
|
public boolean isPresent() {
|
||||||
|
return item != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DummyOptional(T item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
DummyOptional<?> that = (DummyOptional<?>) o;
|
||||||
|
return Objects.equals(item, that.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
|
||||||
|
return Objects.hash(item);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.core.serialization.ClassWhitelist
|
||||||
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.utilities.ByteSequence
|
||||||
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
|
import net.corda.serialization.internal.SerializationContextImpl
|
||||||
|
import net.corda.serialization.internal.amqp.testutils.serializationProperties
|
||||||
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
|
import org.hamcrest.CoreMatchers
|
||||||
|
import org.hamcrest.CoreMatchers.`is`
|
||||||
|
import org.hamcrest.Matchers
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.util.concurrent.ThreadLocalRandom
|
||||||
|
import java.util.stream.IntStream
|
||||||
|
|
||||||
|
class AbstractAMQPSerializationSchemeTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `number of cached factories must be bounded by maxFactories`() {
|
||||||
|
val genesisContext = SerializationContextImpl(
|
||||||
|
ByteSequence.of(byteArrayOf('c'.toByte(), 'o'.toByte(), 'r'.toByte(), 'd'.toByte(), 'a'.toByte(), 0.toByte(), 0.toByte(), 1.toByte())),
|
||||||
|
ClassLoader.getSystemClassLoader(),
|
||||||
|
AllWhitelist,
|
||||||
|
serializationProperties,
|
||||||
|
false,
|
||||||
|
SerializationContext.UseCase.RPCClient,
|
||||||
|
null)
|
||||||
|
|
||||||
|
|
||||||
|
val factory = TestSerializerFactory(TESTING_CONTEXT.whitelist, TESTING_CONTEXT.deserializationClassLoader)
|
||||||
|
val maxFactories = 512
|
||||||
|
val backingMap = AccessOrderLinkedHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>({ maxFactories })
|
||||||
|
val scheme = object : AbstractAMQPSerializationScheme(emptySet(), backingMap, createSerializerFactoryFactory()) {
|
||||||
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IntStream.range(0, 2048).parallel().forEach {
|
||||||
|
val context = if (ThreadLocalRandom.current().nextBoolean()) {
|
||||||
|
genesisContext.withClassLoader(URLClassLoader(emptyArray()))
|
||||||
|
} else {
|
||||||
|
genesisContext
|
||||||
|
}
|
||||||
|
val testString = "TEST${ThreadLocalRandom.current().nextInt()}"
|
||||||
|
val serialized = scheme.serialize(testString, context)
|
||||||
|
val deserialized = serialized.deserialize(context = context, serializationFactory = testSerialization.serializationFactory)
|
||||||
|
Assert.assertThat(testString, `is`(deserialized))
|
||||||
|
Assert.assertThat(backingMap.size, `is`(Matchers.lessThanOrEqualTo(maxFactories)))
|
||||||
|
}
|
||||||
|
Assert.assertThat(backingMap.size, CoreMatchers.`is`(Matchers.lessThanOrEqualTo(maxFactories)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,21 +10,29 @@
|
|||||||
|
|
||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
import net.corda.core.serialization.ClassWhitelist
|
import net.corda.core.serialization.ClassWhitelist
|
||||||
import net.corda.core.serialization.SerializationCustomSerializer
|
import net.corda.core.serialization.SerializationCustomSerializer
|
||||||
import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
import net.corda.serialization.internal.amqp.testutils.deserialize
|
import net.corda.serialization.internal.amqp.testutils.*
|
||||||
import net.corda.serialization.internal.amqp.testutils.serializeAndReturnSchema
|
|
||||||
import net.corda.serialization.internal.amqp.testutils.serialize
|
|
||||||
import net.corda.serialization.internal.amqp.testutils.testDefaultFactory
|
|
||||||
import org.assertj.core.api.Assertions
|
import org.assertj.core.api.Assertions
|
||||||
|
import org.junit.Test
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
class CorDappSerializerTests {
|
class CorDappSerializerTests {
|
||||||
data class NeedsProxy (val a: String)
|
data class NeedsProxy(val a: String)
|
||||||
|
|
||||||
|
private fun proxyFactory(
|
||||||
|
serializers: List<SerializationCustomSerializer<*, *>>
|
||||||
|
) = SerializerFactory(
|
||||||
|
AllWhitelist,
|
||||||
|
ClassLoader.getSystemClassLoader(),
|
||||||
|
onlyCustomSerializers = true
|
||||||
|
).apply {
|
||||||
|
serializers.forEach {
|
||||||
|
registerExternal(CorDappCustomSerializer(it, this))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NeedsProxyProxySerializer : SerializationCustomSerializer<NeedsProxy, NeedsProxyProxySerializer.Proxy> {
|
class NeedsProxyProxySerializer : SerializationCustomSerializer<NeedsProxy, NeedsProxyProxySerializer.Proxy> {
|
||||||
data class Proxy(val proxy_a_: String)
|
data class Proxy(val proxy_a_: String)
|
||||||
@ -35,7 +43,7 @@ class CorDappSerializerTests {
|
|||||||
|
|
||||||
// Standard proxy serializer used internally, here for comparison purposes
|
// Standard proxy serializer used internally, here for comparison purposes
|
||||||
class InternalProxySerializer(factory: SerializerFactory) :
|
class InternalProxySerializer(factory: SerializerFactory) :
|
||||||
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy> (
|
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy>(
|
||||||
NeedsProxy::class.java,
|
NeedsProxy::class.java,
|
||||||
InternalProxySerializer.Proxy::class.java,
|
InternalProxySerializer.Proxy::class.java,
|
||||||
factory) {
|
factory) {
|
||||||
@ -58,14 +66,14 @@ class CorDappSerializerTests {
|
|||||||
|
|
||||||
val msg = "help"
|
val msg = "help"
|
||||||
|
|
||||||
proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
|
proxyFactory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
|
||||||
internalProxyFactory.register (InternalProxySerializer(internalProxyFactory))
|
internalProxyFactory.register(InternalProxySerializer(internalProxyFactory))
|
||||||
|
|
||||||
val needsProxy = NeedsProxy(msg)
|
val needsProxy = NeedsProxy(msg)
|
||||||
|
|
||||||
val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema (needsProxy)
|
val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema(needsProxy)
|
||||||
val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema (needsProxy)
|
val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema(needsProxy)
|
||||||
val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema (needsProxy)
|
val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema(needsProxy)
|
||||||
|
|
||||||
val objFromDefault = DeserializationInput(defaultFactory).deserializeAndReturnEnvelope(bAndSDefault.obj)
|
val objFromDefault = DeserializationInput(defaultFactory).deserializeAndReturnEnvelope(bAndSDefault.obj)
|
||||||
val objFromInternal = DeserializationInput(internalProxyFactory).deserializeAndReturnEnvelope(bAndSInternal.obj)
|
val objFromInternal = DeserializationInput(internalProxyFactory).deserializeAndReturnEnvelope(bAndSInternal.obj)
|
||||||
@ -78,14 +86,14 @@ class CorDappSerializerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun proxiedTypeIsNested() {
|
fun proxiedTypeIsNested() {
|
||||||
data class A (val a: Int, val b: NeedsProxy)
|
data class A(val a: Int, val b: NeedsProxy)
|
||||||
|
|
||||||
val factory = testDefaultFactory()
|
val factory = testDefaultFactory()
|
||||||
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
||||||
|
|
||||||
val tv1 = 100
|
val tv1 = 100
|
||||||
val tv2 = "pants schmants"
|
val tv2 = "pants schmants"
|
||||||
val bAndS = SerializationOutput(factory).serializeAndReturnSchema (A(tv1, NeedsProxy(tv2)))
|
val bAndS = SerializationOutput(factory).serializeAndReturnSchema(A(tv1, NeedsProxy(tv2)))
|
||||||
|
|
||||||
val objFromDefault = DeserializationInput(factory).deserializeAndReturnEnvelope(bAndS.obj)
|
val objFromDefault = DeserializationInput(factory).deserializeAndReturnEnvelope(bAndS.obj)
|
||||||
|
|
||||||
@ -95,7 +103,7 @@ class CorDappSerializerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWithWhitelistNotAllowed() {
|
fun testWithWhitelistNotAllowed() {
|
||||||
data class A (val a: Int, val b: NeedsProxy)
|
data class A(val a: Int, val b: NeedsProxy)
|
||||||
|
|
||||||
class WL : ClassWhitelist {
|
class WL : ClassWhitelist {
|
||||||
private val allowedClasses = emptySet<String>()
|
private val allowedClasses = emptySet<String>()
|
||||||
@ -104,7 +112,7 @@ class CorDappSerializerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
||||||
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
||||||
|
|
||||||
val tv1 = 100
|
val tv1 = 100
|
||||||
val tv2 = "pants schmants"
|
val tv2 = "pants schmants"
|
||||||
@ -115,7 +123,7 @@ class CorDappSerializerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWithWhitelistAllowed() {
|
fun testWithWhitelistAllowed() {
|
||||||
data class A (val a: Int, val b: NeedsProxy)
|
data class A(val a: Int, val b: NeedsProxy)
|
||||||
|
|
||||||
class WL : ClassWhitelist {
|
class WL : ClassWhitelist {
|
||||||
private val allowedClasses = setOf<String>(
|
private val allowedClasses = setOf<String>(
|
||||||
@ -126,7 +134,7 @@ class CorDappSerializerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
||||||
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
||||||
|
|
||||||
val tv1 = 100
|
val tv1 = 100
|
||||||
val tv2 = "pants schmants"
|
val tv2 = "pants schmants"
|
||||||
@ -141,7 +149,7 @@ class CorDappSerializerTests {
|
|||||||
// custom serializer bypasses the whitelist
|
// custom serializer bypasses the whitelist
|
||||||
@Test
|
@Test
|
||||||
fun testWithWhitelistAllowedOuterOnly() {
|
fun testWithWhitelistAllowedOuterOnly() {
|
||||||
data class A (val a: Int, val b: NeedsProxy)
|
data class A(val a: Int, val b: NeedsProxy)
|
||||||
|
|
||||||
class WL : ClassWhitelist {
|
class WL : ClassWhitelist {
|
||||||
// explicitly don't add NeedsProxy
|
// explicitly don't add NeedsProxy
|
||||||
@ -151,7 +159,7 @@ class CorDappSerializerTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
|
||||||
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
|
||||||
|
|
||||||
val tv1 = 100
|
val tv1 = 100
|
||||||
val tv2 = "pants schmants"
|
val tv2 = "pants schmants"
|
||||||
@ -161,4 +169,136 @@ class CorDappSerializerTests {
|
|||||||
assertEquals(tv1, obj.a)
|
assertEquals(tv1, obj.a)
|
||||||
assertEquals(tv2, obj.b.a)
|
assertEquals(tv2, obj.b.a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class NeedsProxyGen<T>(val a: T)
|
||||||
|
|
||||||
|
class NeedsProxyGenProxySerializer :
|
||||||
|
SerializationCustomSerializer<NeedsProxyGen<*>, NeedsProxyGenProxySerializer.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: Any?)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = NeedsProxyGen(proxy.proxy_a_)
|
||||||
|
override fun toProxy(obj: NeedsProxyGen<*>) = Proxy(obj.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests CORDA-1747
|
||||||
|
@Test
|
||||||
|
fun proxiedGeneric() {
|
||||||
|
val proxyFactory = proxyFactory(listOf(NeedsProxyGenProxySerializer()))
|
||||||
|
|
||||||
|
val msg = "help"
|
||||||
|
|
||||||
|
|
||||||
|
val blob = SerializationOutput(proxyFactory).serialize(NeedsProxyGen(msg))
|
||||||
|
val objFromProxy = DeserializationInput(proxyFactory).deserialize(blob)
|
||||||
|
|
||||||
|
assertEquals(msg, objFromProxy.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need an interface to restrict the generic to in the following test
|
||||||
|
interface Bound {
|
||||||
|
fun wibbleIt(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
// test class to be serialized whose generic arg is restricted to instances
|
||||||
|
// of the Bound interface declared above
|
||||||
|
data class NeedsProxyGenBounded<T : Bound>(val a: T)
|
||||||
|
|
||||||
|
// Proxy for our test class
|
||||||
|
class NeedsProxyGenBoundedProxySerializer :
|
||||||
|
SerializationCustomSerializer<NeedsProxyGenBounded<*>,
|
||||||
|
NeedsProxyGenBoundedProxySerializer.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: Bound)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = NeedsProxyGenBounded(proxy.proxy_a_)
|
||||||
|
override fun toProxy(obj: NeedsProxyGenBounded<*>) = Proxy(obj.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we need a value for our test class that implements the interface
|
||||||
|
// we're restricting its generic property to, this is that class.
|
||||||
|
data class HasWibble(val a: String) : Bound {
|
||||||
|
override fun wibbleIt() = "wibble it, just a little bit!."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because we're enforcing all classes have proxy serializers we
|
||||||
|
// have to implement this to avoid the factory erroneously failing
|
||||||
|
class HasWibbleProxy :
|
||||||
|
SerializationCustomSerializer<HasWibble, HasWibbleProxy.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: String)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = HasWibble(proxy.proxy_a_)
|
||||||
|
override fun toProxy(obj: HasWibble) = Proxy(obj.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests CORDA-1747 - Finally the actual bound generics test, on failure it will throw
|
||||||
|
@Test
|
||||||
|
fun proxiedBoundedGeneric() {
|
||||||
|
val proxyFactory = proxyFactory(listOf(NeedsProxyGenBoundedProxySerializer(), HasWibbleProxy()))
|
||||||
|
|
||||||
|
val blob = SerializationOutput(proxyFactory).serialize(NeedsProxyGenBounded(HasWibble("A")))
|
||||||
|
val objFromProxy = DeserializationInput(proxyFactory).deserialize(blob)
|
||||||
|
|
||||||
|
assertEquals("A", objFromProxy.a.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class NeedsProxyGenContainer<T>(val a: List<T>)
|
||||||
|
|
||||||
|
class NeedsProxyGenContainerProxySerializer :
|
||||||
|
SerializationCustomSerializer<NeedsProxyGenContainer<*>,
|
||||||
|
NeedsProxyGenContainerProxySerializer.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: List<*>)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = NeedsProxyGenContainer(proxy.proxy_a_)
|
||||||
|
override fun toProxy(obj: NeedsProxyGenContainer<*>) = Proxy(obj.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests CORDA-1747
|
||||||
|
@Test
|
||||||
|
fun proxiedGenericContainer() {
|
||||||
|
val proxyFactory = proxyFactory(listOf(NeedsProxyGenContainerProxySerializer()))
|
||||||
|
|
||||||
|
val blob1 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf(1, 2, 3)))
|
||||||
|
val obj1 = DeserializationInput(proxyFactory).deserialize(blob1)
|
||||||
|
|
||||||
|
assertEquals(listOf(1, 2, 3), obj1.a)
|
||||||
|
|
||||||
|
val blob2 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf("1", "2", "3")))
|
||||||
|
val obj2 = DeserializationInput(proxyFactory).deserialize(blob2)
|
||||||
|
|
||||||
|
assertEquals(listOf("1", "2", "3"), obj2.a)
|
||||||
|
|
||||||
|
val blob3 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf("1", 2, "3")))
|
||||||
|
val obj3 = DeserializationInput(proxyFactory).deserialize(blob3)
|
||||||
|
|
||||||
|
assertEquals(listOf("1", 2, "3"), obj3.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Base<T>(val a: T)
|
||||||
|
class Derived<T>(a: T, val b: String) : Base<T>(a)
|
||||||
|
|
||||||
|
class BaseProxy :
|
||||||
|
SerializationCustomSerializer<Base<*>, BaseProxy.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: Any?)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = Base(proxy.proxy_a_)
|
||||||
|
override fun toProxy(obj: Base<*>) = Proxy(obj.a)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DerivedProxy :
|
||||||
|
SerializationCustomSerializer<Derived<*>, DerivedProxy.Proxy> {
|
||||||
|
data class Proxy(val proxy_a_: Any?, val proxy_b_: String)
|
||||||
|
|
||||||
|
override fun fromProxy(proxy: Proxy) = Derived(proxy.proxy_a_, proxy.proxy_b_)
|
||||||
|
override fun toProxy(obj: Derived<*>) = Proxy(obj.a, obj.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests CORDA-1747
|
||||||
|
@Test
|
||||||
|
fun proxiedInheritableGenerics() {
|
||||||
|
val proxyFactory = proxyFactory(listOf(BaseProxy(), DerivedProxy()))
|
||||||
|
|
||||||
|
val blob1 = SerializationOutput(proxyFactory).serialize(Base(100L))
|
||||||
|
DeserializationInput(proxyFactory).deserialize(blob1)
|
||||||
|
val blob2 = SerializationOutput(proxyFactory).serialize(Derived(100L, "Hey pants"))
|
||||||
|
DeserializationInput(proxyFactory).deserialize(blob2)
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.serialization.internal.AllWhitelist
|
||||||
|
import net.corda.serialization.internal.amqp.custom.OptionalSerializer
|
||||||
|
import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput
|
||||||
|
import net.corda.serialization.internal.amqp.testutils.deserialize
|
||||||
|
import net.corda.serialization.internal.amqp.testutils.testDefaultFactory
|
||||||
|
import org.hamcrest.Matchers.`is`
|
||||||
|
import org.hamcrest.Matchers.equalTo
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Test
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class OptionalSerializationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setupEnclosedSerializationTest() {
|
||||||
|
@Test
|
||||||
|
fun `java optionals should serialize`() {
|
||||||
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
factory.register(OptionalSerializer(factory))
|
||||||
|
val obj = Optional.ofNullable("YES")
|
||||||
|
val bytes = TestSerializationOutput(true, factory).serialize(obj)
|
||||||
|
val deserializerFactory = testDefaultFactory().apply {
|
||||||
|
register(OptionalSerializer(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
val deserialized = DeserializationInput(factory).deserialize(bytes)
|
||||||
|
val deserialized2 = DeserializationInput(deserializerFactory).deserialize(bytes)
|
||||||
|
Assert.assertThat(deserialized, `is`(equalTo(deserialized2)))
|
||||||
|
Assert.assertThat(obj, `is`(equalTo(deserialized2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
`java optionals should serialize`()
|
||||||
|
}
|
||||||
|
}
|
@ -520,6 +520,12 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
serdes(obj, SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()))
|
serdes(obj, SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `generics from java are supported`() {
|
||||||
|
val obj = DummyOptional<String>("YES")
|
||||||
|
serdes(obj, SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test throwables serialize`() {
|
fun `test throwables serialize`() {
|
||||||
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
@ -979,6 +985,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
|
|
||||||
class Spike private constructor(val a: String) {
|
class Spike private constructor(val a: String) {
|
||||||
constructor() : this("a")
|
constructor() : this("a")
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean = other is Spike && other.a == this.a
|
override fun equals(other: Any?): Boolean = other is Spike && other.a == this.a
|
||||||
override fun hashCode(): Int = a.hashCode()
|
override fun hashCode(): Int = a.hashCode()
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package net.corda.serialization.internal.amqp
|
|||||||
|
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
import net.corda.core.utilities.ByteSequence
|
import net.corda.core.utilities.ByteSequence
|
||||||
import net.corda.serialization.internal.*
|
|
||||||
import net.corda.serialization.internal.BuiltInExceptionsWhitelist
|
import net.corda.serialization.internal.BuiltInExceptionsWhitelist
|
||||||
|
import net.corda.serialization.internal.CordaSerializationMagic
|
||||||
import net.corda.serialization.internal.GlobalTransientClassWhiteList
|
import net.corda.serialization.internal.GlobalTransientClassWhiteList
|
||||||
|
import net.corda.serialization.internal.SerializationContextImpl
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
// Make sure all serialization calls in this test don't get stomped on by anything else
|
// Make sure all serialization calls in this test don't get stomped on by anything else
|
||||||
@ -46,7 +46,7 @@ class TestSerializerFactoryFactory : SerializerFactoryFactoryImpl() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptySet(), ConcurrentHashMap(), TestSerializerFactoryFactory()) {
|
class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptySet(), AccessOrderLinkedHashMap { 128 }, TestSerializerFactoryFactory()) {
|
||||||
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ class SerializationSchemaTests {
|
|||||||
|
|
||||||
val c = C(1)
|
val c = C(1)
|
||||||
val testSerializationFactory = TestSerializationFactory()
|
val testSerializationFactory = TestSerializationFactory()
|
||||||
val expectedCustomSerializerCount = 40
|
val expectedCustomSerializerCount = 41
|
||||||
|
|
||||||
assertEquals(0, testFactory.registerCount)
|
assertEquals(0, testFactory.registerCount)
|
||||||
c.serialize(testSerializationFactory, TESTING_CONTEXT)
|
c.serialize(testSerializationFactory, TESTING_CONTEXT)
|
||||||
|
@ -17,9 +17,14 @@ import java.io.NotSerializableException
|
|||||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||||
|
|
||||||
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
|
||||||
fun testDefaultFactoryNoEvolution(): SerializerFactory {
|
fun testDefaultFactoryNoEvolution(): SerializerFactory {
|
||||||
return SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), evolutionSerializerGetter = EvolutionSerializerGetterTesting())
|
return SerializerFactory(
|
||||||
|
AllWhitelist,
|
||||||
|
ClassLoader.getSystemClassLoader(),
|
||||||
|
evolutionSerializerGetter = EvolutionSerializerGetterTesting())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
|
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
|
||||||
|
|
||||||
class TestSerializationOutput(
|
class TestSerializationOutput(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user