mirror of
https://github.com/corda/corda.git
synced 2024-12-26 16:11:12 +00:00
Merge remote-tracking branch 'open/master' into mike-merge-80c00b920b
This commit is contained in:
commit
abc281056f
@ -563,15 +563,20 @@ public final class net.corda.core.contracts.TransactionStateKt extends java.lang
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ConflictingAttachmentsRejection extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String)
|
||||
@org.jetbrains.annotations.NotNull public final String getContractClass()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractConstraintRejection extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String)
|
||||
@org.jetbrains.annotations.NotNull public final String getContractClass()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractCreationError extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String, Throwable)
|
||||
@org.jetbrains.annotations.NotNull public final String getContractClass()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractRejection extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String, Throwable)
|
||||
public <init>(net.corda.core.crypto.SecureHash, net.corda.core.contracts.Contract, Throwable)
|
||||
@org.jetbrains.annotations.NotNull public final String getContractClass()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$Direction extends java.lang.Enum
|
||||
protected <init>(String, int)
|
||||
@ -594,12 +599,17 @@ public final class net.corda.core.contracts.TransactionStateKt extends java.lang
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$NotaryChangeInWrongTransactionType extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.identity.Party)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getOutputNotary()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party getTxNotary()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$SignersMissing extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, List)
|
||||
@org.jetbrains.annotations.NotNull public final List getMissing()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$TransactionMissingEncumbranceException extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, int, net.corda.core.contracts.TransactionVerificationException$Direction)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.TransactionVerificationException$Direction getInOut()
|
||||
public final int getMissing()
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.contracts.TypeOnlyCommandData extends java.lang.Object implements net.corda.core.contracts.CommandData
|
||||
public <init>()
|
||||
@ -1336,6 +1346,8 @@ public interface net.corda.core.flows.IdentifiableException
|
||||
##
|
||||
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.IllegalFlowLogicException extends java.lang.IllegalArgumentException
|
||||
public <init>(Class, String)
|
||||
public <init>(String, String)
|
||||
@org.jetbrains.annotations.NotNull public final String getType()
|
||||
##
|
||||
public @interface net.corda.core.flows.InitiatedBy
|
||||
public abstract Class value()
|
||||
|
@ -1,6 +1,6 @@
|
||||
gradlePluginsVersion=4.0.3
|
||||
kotlinVersion=1.2.20
|
||||
platformVersion=2
|
||||
platformVersion=4
|
||||
guavaVersion=21.0
|
||||
bouncycastleVersion=1.57
|
||||
typesafeConfigVersion=1.3.1
|
||||
|
@ -6,6 +6,6 @@ package net.corda.core
|
||||
* These fields are only meant to be used by Corda internally, and are not intended to be part of the public API.
|
||||
*/
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
@Target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FUNCTION)
|
||||
@MustBeDocumented
|
||||
annotation class CordaInternal
|
@ -1,10 +1,12 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
||||
/**
|
||||
* The public factory interface for creating validated FlowLogicRef instances as part of the scheduling framework.
|
||||
* The public factory interface for creating validated [FlowLogicRef] instances as part of the scheduling framework.
|
||||
*
|
||||
* Typically this would be used from within the nextScheduledActivity method of a QueryableState to specify
|
||||
* the flow to run at the scheduled time.
|
||||
*/
|
||||
@ -14,20 +16,40 @@ interface FlowLogicRefFactory {
|
||||
* Construct a FlowLogicRef. This is intended for cases where the calling code has the relevant class already
|
||||
* and can provide it directly.
|
||||
*/
|
||||
@Deprecated("This should be avoided, and the version which takes a class name used instead to avoid requiring the class on the classpath to deserialize calling code")
|
||||
fun create(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||
|
||||
/**
|
||||
* Construct a FlowLogicRef. This is intended for cases where the calling code does not want to require the flow
|
||||
* class on the classpath for all cases where the calling code is loaded.
|
||||
*/
|
||||
fun create(flowClassName: String, vararg args: Any?): FlowLogicRef
|
||||
|
||||
/**
|
||||
* @suppress
|
||||
* This is an internal method and should not be used: use [create] instead, which checks for the
|
||||
* [SchedulableFlow] annotation.
|
||||
*/
|
||||
@CordaInternal
|
||||
fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef
|
||||
|
||||
/**
|
||||
* Converts a [FlowLogicRef] object that was obtained from the calls above into a [FlowLogic], after doing some
|
||||
* validation to ensure it points to a legitimate flow class.
|
||||
*/
|
||||
fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*>
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown if the structure of a class implementing a flow is not correct. There can be several causes for this such as
|
||||
* not inheriting from [FlowLogic], not having a valid constructor and so on.
|
||||
*
|
||||
* @property type the fully qualified name of the class that failed checks.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentException(
|
||||
"${FlowLogicRef::class.java.simpleName} cannot be constructed for ${FlowLogic::class.java.simpleName} of type ${type.name} $msg")
|
||||
class IllegalFlowLogicException(val type: String, msg: String) :
|
||||
IllegalArgumentException("A FlowLogicRef cannot be constructed for FlowLogic of type $type: $msg") {
|
||||
constructor(type: Class<*>, msg: String) : this(type.name, msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* A handle interface representing a [FlowLogic] instance which would be possible to safely pass out of the contract sandbox.
|
||||
|
@ -77,7 +77,7 @@ You will now be able to browse the tables and row data within them.
|
||||
Monitoring your node
|
||||
--------------------
|
||||
|
||||
Like most Java servers, the node exports various useful metrics and management operations via the industry-standard
|
||||
Like most Java servers, the node can be configured to export various useful metrics and management operations via the industry-standard
|
||||
`JMX infrastructure <https://en.wikipedia.org/wiki/Java_Management_Extensions>`_. JMX is a standard API
|
||||
for registering so-called *MBeans* ... objects whose properties and methods are intended for server management. It does
|
||||
not require any particular network protocol for export. So this data can be exported from the node in various ways:
|
||||
@ -89,7 +89,7 @@ them out to a statistics collector over the network. For those systems, follow t
|
||||
agent `here <https://jolokia.org/agent/jvm.html>`_.
|
||||
|
||||
`Jolokia <https://jolokia.org/>`_ allows you to access the raw data and operations without connecting to the JMX port
|
||||
directly. The nodes export the data over HTTP on the ``/jolokia`` HTTP endpoint, Jolokia defines the JSON and REST
|
||||
directly. Nodes can be configured to export the data over HTTP on the ``/jolokia`` HTTP endpoint, Jolokia defines the JSON and REST
|
||||
formats for accessing MBeans, and provides client libraries to work with that protocol as well.
|
||||
|
||||
Here are a few ways to build dashboards and extract monitoring data for a node:
|
||||
@ -147,4 +147,4 @@ node is running out of memory, you can give it more by running the node like thi
|
||||
|
||||
The example command above would give a 1 gigabyte Java heap.
|
||||
|
||||
.. note:: Unfortunately the JVM does not let you limit the total memory usage of Java program, just the heap size.
|
||||
.. note:: Unfortunately the JVM does not let you limit the total memory usage of Java program, just the heap size.
|
||||
|
@ -323,22 +323,7 @@ And click submit. Upon clicking submit, the modal dialogue will close, and the n
|
||||
|
||||
Checking the output
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Assuming all went well, you should see some activity in PartyA's web-server terminal window:
|
||||
|
||||
.. sourcecode:: none
|
||||
|
||||
>> Signing transaction with our private key.
|
||||
>> Gathering the counterparty's signature.
|
||||
>> Collecting signatures from counter-parties.
|
||||
>> Verifying collected signatures.
|
||||
>> Done
|
||||
>> Obtaining notary signature and recording transaction.
|
||||
>> Requesting signature by notary service
|
||||
>> Broadcasting transaction to participants
|
||||
>> Done
|
||||
>> Done
|
||||
|
||||
You can view the newly-created IOU by accessing the vault of PartyA or PartyB:
|
||||
Assuming all went well, you can view the newly-created IOU by accessing the vault of PartyA or PartyB:
|
||||
|
||||
*Via the HTTP API:*
|
||||
|
||||
@ -505,4 +490,4 @@ Debugging is done via IntelliJ as follows:
|
||||
5. Set your breakpoints and interact with the node you've connected to. When the node hits a breakpoint, execution will
|
||||
pause
|
||||
|
||||
* The node webserver runs in a separate process, and is not attached to by the debugger
|
||||
* The node webserver runs in a separate process, and is not attached to by the debugger
|
||||
|
@ -153,7 +153,7 @@ class SerializerFingerPrinter : FingerPrinter {
|
||||
}.putUnencodedChars(type.name).putUnencodedChars(ENUM_HASH)
|
||||
} else {
|
||||
hasher.fingerprintWithCustomSerializerOrElse(factory!!, type, type) {
|
||||
if (type.kotlin.objectInstance != null) {
|
||||
if (type.objectInstance() != null) {
|
||||
// TODO: name collision is too likely for kotlin objects, we need to introduce some reference
|
||||
// to the CorDapp but maybe reference to the JAR in the short term.
|
||||
hasher.putUnencodedChars(type.name)
|
||||
|
@ -136,7 +136,16 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
||||
this.field = property
|
||||
}
|
||||
}
|
||||
clazz = clazz.superclass
|
||||
} while (clazz != null)
|
||||
|
||||
//
|
||||
// Running as two loops rather than one as we need to ensure we have captured all of the properties
|
||||
// before looking for interacting methods and need to cope with the class hierarchy introducing
|
||||
// new properties / methods
|
||||
//
|
||||
clazz = this
|
||||
do {
|
||||
// Note: It is possible for a class to have multiple instances of a function where the types
|
||||
// differ. For example:
|
||||
// interface I<out T> { val a: T }
|
||||
@ -148,14 +157,23 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
||||
//
|
||||
// In addition, only getters that take zero parameters and setters that take a single
|
||||
// parameter will be considered
|
||||
clazz.declaredMethods?.map { func ->
|
||||
clazz!!.declaredMethods?.map { func ->
|
||||
if (!Modifier.isPublic(func.modifiers)) return@map
|
||||
if (func.name == "getClass") return@map
|
||||
|
||||
PropertyDescriptorsRegex.re.find(func.name)?.apply {
|
||||
// take into account those constructor properties that don't directly map to a named
|
||||
// property which are, by default, already added to the map
|
||||
classProperties.computeIfAbsent(groups[2]!!.value.decapitalize()) { PropertyDescriptor() }.apply {
|
||||
// matching means we have an func getX where the property could be x or X
|
||||
// so having pre-loaded all of the properties we try to match to either case. If that
|
||||
// fails the getter doesn't refer to a property directly, but may to a cosntructor
|
||||
// parameter that shadows a property
|
||||
val properties =
|
||||
classProperties[groups[2]!!.value] ?:
|
||||
classProperties[groups[2]!!.value.decapitalize()] ?:
|
||||
// take into account those constructor properties that don't directly map to a named
|
||||
// property which are, by default, already added to the map
|
||||
classProperties.computeIfAbsent(groups[2]!!.value) { PropertyDescriptor() }
|
||||
|
||||
properties.apply {
|
||||
when (groups[1]!!.value) {
|
||||
"set" -> {
|
||||
if (func.parameterCount == 1) {
|
||||
@ -221,38 +239,38 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
|
||||
// it so just ignore it as it'll be supplied at runtime anyway on invocation
|
||||
val name = param.value.name ?: return@forEach
|
||||
|
||||
val propertyReader = if (name in classProperties) {
|
||||
if (classProperties[name]!!.getter != null) {
|
||||
// it's a publicly accessible property
|
||||
val matchingProperty = classProperties[name]!!
|
||||
// We will already have disambiguated getA for property A or a but we still need to cope
|
||||
// with the case we don't know the case of A when the parameter doesn't match a property
|
||||
// but has a getter
|
||||
val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()] ?:
|
||||
throw NotSerializableException(
|
||||
"Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"")
|
||||
|
||||
// Check that the method has a getter in java.
|
||||
val getter = matchingProperty.getter ?: throw NotSerializableException(
|
||||
"Property has no getter method for - \"$name\" - of \"$clazz\". If using Java and the parameter name"
|
||||
+ "looks anonymous, check that you have the -parameters option specified in the "
|
||||
+ "Java compiler. Alternately, provide a proxy serializer "
|
||||
+ "(SerializationCustomSerializer) if recompiling isn't an option.")
|
||||
// If the property has a getter we'll use that to retrieve it's value from the instance, if it doesn't
|
||||
// *for *know* we switch to a reflection based method
|
||||
val propertyReader = if (matchingProperty.getter != null) {
|
||||
val getter = matchingProperty.getter ?: throw NotSerializableException(
|
||||
"Property has no getter method for - \"$name\" - of \"$clazz\". If using Java and the parameter name"
|
||||
+ "looks anonymous, check that you have the -parameters option specified in the "
|
||||
+ "Java compiler. Alternately, provide a proxy serializer "
|
||||
+ "(SerializationCustomSerializer) if recompiling isn't an option.")
|
||||
|
||||
val returnType = resolveTypeVariables(getter.genericReturnType, type)
|
||||
if (!constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param.value)) {
|
||||
throw NotSerializableException(
|
||||
"Property - \"$name\" - has type \"$returnType\" on \"$clazz\" but differs from constructor " +
|
||||
"parameter type \"${param.value.type.javaType}\"")
|
||||
}
|
||||
val returnType = resolveTypeVariables(getter.genericReturnType, type)
|
||||
if (!constructorParamTakesReturnTypeOfGetter(returnType, getter.genericReturnType, param.value)) {
|
||||
throw NotSerializableException(
|
||||
"Property - \"$name\" - has type \"$returnType\" on \"$clazz\" but differs from constructor " +
|
||||
"parameter type \"${param.value.type.javaType}\"")
|
||||
}
|
||||
|
||||
Pair(PublicPropertyReader(getter), returnType)
|
||||
} else {
|
||||
val field = classProperties[name]!!.field ?:
|
||||
Pair(PublicPropertyReader(getter), returnType)
|
||||
} else {
|
||||
val field = classProperties[name]!!.field ?:
|
||||
throw NotSerializableException("No property matching constructor parameter named - \"$name\" - " +
|
||||
"of \"$clazz\". If using Java, check that you have the -parameters option specified " +
|
||||
"in the Java compiler. Alternately, provide a proxy serializer " +
|
||||
"(SerializationCustomSerializer) if recompiling isn't an option")
|
||||
|
||||
Pair(PrivatePropertyReader(field, type), field.genericType)
|
||||
}
|
||||
} else {
|
||||
throw NotSerializableException(
|
||||
"Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"")
|
||||
Pair(PrivatePropertyReader(field, type), field.genericType)
|
||||
}
|
||||
|
||||
this += PropertyAccessorConstructor(
|
||||
@ -513,3 +531,48 @@ fun ClassWhitelist.hasAnnotationInHierarchy(type: Class<*>): Boolean {
|
||||
|| type.interfaces.any { hasAnnotationInHierarchy(it) }
|
||||
|| (type.superclass != null && hasAnnotationInHierarchy(type.superclass))
|
||||
}
|
||||
|
||||
/**
|
||||
* By default use Kotlin reflection and grab the objectInstance. However, that doesn't play nicely with nested
|
||||
* private objects. Even setting the accessibility override (setAccessible) still causes an
|
||||
* [IllegalAccessException] when attempting to retrieve the value of the INSTANCE field.
|
||||
*
|
||||
* Whichever reference to the class Kotlin reflection uses, override (set from setAccessible) on that field
|
||||
* isn't set even when it was explicitly set as accessible before calling into the kotlin reflection routines.
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* clazz.getDeclaredField("INSTANCE")?.apply {
|
||||
* isAccessible = true
|
||||
* kotlin.objectInstance // This throws as the INSTANCE field isn't accessible
|
||||
* }
|
||||
*
|
||||
* Therefore default back to good old java reflection and simply look for the INSTANCE field as we are never going
|
||||
* to serialize a companion object.
|
||||
*
|
||||
* As such, if objectInstance fails access, revert to Java reflection and try that
|
||||
*/
|
||||
fun Class<*>.objectInstance() =
|
||||
try {
|
||||
this.kotlin.objectInstance
|
||||
} catch (e: IllegalAccessException) {
|
||||
// Check it really is an object (i.e. it has no constructor)
|
||||
if (constructors.isNotEmpty()) null
|
||||
else {
|
||||
try {
|
||||
this.getDeclaredField("INSTANCE")?.let { field ->
|
||||
// and must be marked as both static and final (>0 means they're set)
|
||||
if (modifiers and Modifier.STATIC == 0 || modifiers and Modifier.FINAL == 0) null
|
||||
else {
|
||||
val accessibility = field.isAccessible
|
||||
field.isAccessible = true
|
||||
val obj = field.get(null)
|
||||
field.isAccessible = accessibility
|
||||
obj
|
||||
}
|
||||
}
|
||||
} catch (e: NoSuchFieldException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,12 +267,15 @@ open class SerializerFactory(
|
||||
// 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)
|
||||
else ArraySerializer.make(type, this)
|
||||
} else if (clazz.kotlin.objectInstance != null) {
|
||||
whitelist.requireWhitelisted(clazz)
|
||||
SingletonSerializer(clazz, clazz.kotlin.objectInstance!!, this)
|
||||
} else {
|
||||
whitelist.requireWhitelisted(type)
|
||||
ObjectSerializer(type, this)
|
||||
val singleton = clazz.objectInstance()
|
||||
if (singleton != null) {
|
||||
whitelist.requireWhitelisted(clazz)
|
||||
SingletonSerializer(clazz, singleton, this)
|
||||
} else {
|
||||
whitelist.requireWhitelisted(type)
|
||||
ObjectSerializer(type, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,4 +196,32 @@ class PrivatePropertyTests {
|
||||
|
||||
assertEquals(c1, c2)
|
||||
}
|
||||
|
||||
//
|
||||
// Reproduces CORDA-1134
|
||||
//
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Test
|
||||
fun allCapsProprtyNotPrivate() {
|
||||
data class C (val CCC: String)
|
||||
|
||||
val output = SerializationOutput(factory).serializeAndReturnSchema(C("this is nice"))
|
||||
|
||||
val serializersByDescriptor = fields["serializersByDesc"]!!.get(factory) as ConcurrentHashMap<Any, AMQPSerializer<Any>>
|
||||
|
||||
val schemaDescriptor = output.schema.types.first().descriptor.name
|
||||
serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply {
|
||||
assertEquals(1, size)
|
||||
|
||||
assertTrue(this.first() is ObjectSerializer)
|
||||
val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.serializationOrder.map { it.getter }
|
||||
|
||||
// CCC is the only property to be serialised
|
||||
assertEquals(1, propertySerializers.size)
|
||||
|
||||
// and despite being all caps it should still be a public getter
|
||||
assertTrue(propertySerializers[0].propertyReader is PublicPropertyReader)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -47,6 +47,22 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
object AckWrapper {
|
||||
object Ack
|
||||
fun serialize() {
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
SerializationOutput(factory).serialize(Ack)
|
||||
}
|
||||
}
|
||||
|
||||
object PrivateAckWrapper {
|
||||
private object Ack
|
||||
fun serialize() {
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
SerializationOutput(factory).serialize(Ack)
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class SerializationOutputTests(private val compression: CordaSerializationEncoding?) {
|
||||
private companion object {
|
||||
@ -1148,4 +1164,18 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
||||
assertEquals(encodingNotPermittedFormat.format(compression), message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nestedObjects() {
|
||||
// The "test" is that this doesn't throw, anything else is a success
|
||||
AckWrapper.serialize()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun privateNestedObjects() {
|
||||
// The "test" is that this doesn't throw, anything else is a success
|
||||
PrivateAckWrapper.serialize()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -41,15 +41,21 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
|
||||
}
|
||||
|
||||
override fun create(flowClassName: String, vararg args: Any?): FlowLogicRef {
|
||||
val flowClass = Class.forName(flowClassName, true, classloader).asSubclass(FlowLogic::class.java)
|
||||
if (flowClass == null) {
|
||||
throw IllegalArgumentException("The class $flowClassName is not a subclass of FlowLogic.")
|
||||
} else {
|
||||
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
|
||||
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
|
||||
}
|
||||
return createForRPC(flowClass, *args)
|
||||
val flowClass = validatedFlowClassFromName(flowClassName)
|
||||
if (!flowClass.isAnnotationPresent(SchedulableFlow::class.java)) {
|
||||
throw IllegalFlowLogicException(flowClass, "because it's not a schedulable flow")
|
||||
}
|
||||
return createForRPC(flowClass, *args)
|
||||
}
|
||||
|
||||
private fun validatedFlowClassFromName(flowClassName: String): Class<out FlowLogic<*>> {
|
||||
val forName = try {
|
||||
Class.forName(flowClassName, true, classloader)
|
||||
} catch (e: ClassNotFoundException) {
|
||||
throw IllegalFlowLogicException(flowClassName, "Flow not found: $flowClassName")
|
||||
}
|
||||
return forName.asSubclass(FlowLogic::class.java) ?:
|
||||
throw IllegalFlowLogicException(flowClassName, "The class $flowClassName is not a subclass of FlowLogic.")
|
||||
}
|
||||
|
||||
override fun createForRPC(flowClass: Class<out FlowLogic<*>>, vararg args: Any?): FlowLogicRef {
|
||||
@ -93,7 +99,9 @@ class FlowLogicRefFactoryImpl(private val classloader: ClassLoader) : SingletonS
|
||||
|
||||
override fun toFlowLogic(ref: FlowLogicRef): FlowLogic<*> {
|
||||
if (ref !is FlowLogicRefImpl) throw IllegalFlowLogicException(ref.javaClass, "FlowLogicRef was not created via correct FlowLogicRefFactory interface")
|
||||
val klass = Class.forName(ref.flowLogicClassName, true, classloader).asSubclass(FlowLogic::class.java)
|
||||
// We re-validate here because a FlowLogicRefImpl could have arrived via deserialization and therefore the
|
||||
// class name could point to anything at all.
|
||||
val klass = validatedFlowClassFromName(ref.flowLogicClassName)
|
||||
return createConstructor(klass, ref.args)()
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,8 @@ object SimmFlow {
|
||||
@Suspendable
|
||||
private fun agreePortfolio(portfolio: Portfolio): SignedTransaction {
|
||||
logger.info("Handshake finished, awaiting Simm offer")
|
||||
require(offer.dealBeingOffered.portfolio == portfolio.refs)
|
||||
|
||||
require(offer.dealBeingOffered.portfolio.toSet() == portfolio.refs.toSet())
|
||||
|
||||
val seller = TwoPartyDealFlow.Instigator(
|
||||
replyToSession,
|
||||
|
Loading…
Reference in New Issue
Block a user