Skip to content

Commit

Permalink
feat(corda-connector): dsl to support collections, enums #622
Browse files Browse the repository at this point in the history
Primary change
============

You can now express via the JSON DSL otherwise non-constructable
types such as ByteArray, DoubleArrays, enums, lists, maps, sets,
etc.

The collections are being powered by a pre-populated map of so called
"exotic" types which are named as such because they do not follow
the Java OOP ideals of having a constructor responsible for creating
the class instances.

Added test coverage for all the above as well.

Miscellaneous changes:
===================

Deleted the Kotlin test that was dependent on a corda network being
pulled up and cordapps deployed because we are covering all
of this via the existing tests written in Typescript anyway.
With the above done, now the dockerfile can be switched back
to execute the tests of the corda plugin server (which are only
unit tests at this point that is more than enough since we
have coverage elsewhere).

Tagged the container image built from this revision as:
hyperledger/cactus-connector-corda-server:2021-03-25-feat-622

Fixes #622

Signed-off-by: Peter Somogyvari <peter.somogyvari@accenture.com>
  • Loading branch information
petermetz committed Mar 31, 2021
1 parent b03611f commit 78e6754
Show file tree
Hide file tree
Showing 8 changed files with 513 additions and 304 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ WORKDIR /
COPY ./kotlin/gen/kotlin-spring /kotlin-spring/

WORKDIR /kotlin-spring/
# We skip the tests because they require a working
# Corda network which we do not want to set up.
RUN gradle build -x test

RUN gradle build
WORKDIR /

FROM openjdk:8u275-jre-slim-buster
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#!docs/README.md

build.gradle.kts
src/test/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/api/ApiPluginLedgerConnectorCordaTest.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/impl/ApiPluginLedgerConnectorCordaServiceImpl.kt
src/main/kotlin/org/hyperledger/cactus/plugin/ledger/connector/corda/server/Application.kt
.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.hyperledger.cactus.plugin.ledger.connector.corda.server.impl

import java.lang.RuntimeException


open class InstantiationException(override val message: String, override val cause: Throwable? = null)
: RuntimeException(message, cause)

open class ConstructorLookupException(override val message: String, override val cause: Throwable? = null)
: InstantiationException(message, cause)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import java.lang.RuntimeException
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import java.util.*
import kotlin.collections.ArrayList

// FIXME: Make it so that this has a memory, remembering the .jar files that were added before (file-system?) or
// maybe use the keychain to save it there and then it can pre-populate at boot?
Expand Down Expand Up @@ -73,20 +74,92 @@ class JsonJvmObjectDeserializer(
val constructorArgs: Array<Any?> = jvmObject.jvmCtorArgs.map { x -> instantiate(x) }.toTypedArray()

when {
List::class.java.isAssignableFrom(clazz) -> {
return listOf(*constructorArgs)
DoubleArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Double }
.toDoubleArray()
}
FloatArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Float }
.toFloatArray()
}
LongArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Long }
.toLongArray()
}
ShortArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Short }
.toShortArray()
}
CharArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Char }
.toCharArray()
}
BooleanArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Boolean }
.toBooleanArray()
}
IntArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Int }
.toIntArray()
}
ByteArray::class.java.isAssignableFrom(clazz) -> {
return constructorArgs
.map { ca -> ca as Byte }
.toByteArray()
}
ArrayList::class.java.isAssignableFrom(clazz) -> {
return arrayListOf(*constructorArgs)
}
Array<Any>::class.java.isAssignableFrom(clazz) -> {
// TODO verify that this actually works and also
// if we need it at all since we already have lists covered
return arrayOf(*constructorArgs)
}
List::class.java.isAssignableFrom(clazz) -> {
return listOf(*constructorArgs)
}
Set::class.java.isAssignableFrom(clazz) -> {
return setOf(*constructorArgs)
}
Map::class.java.isAssignableFrom(clazz) -> {
val constructorArgsCasted = constructorArgs
.map { ca -> ca as Pair<*, *> }
.toTypedArray()

return mapOf(*constructorArgsCasted)
}
jvmObject.jvmType.constructorName != null -> {
val methodArgTypes: List<Class<*>> =
jvmObject.jvmCtorArgs.map { x -> getOrInferType(x.jvmType.fqClassName) }

var invocationTarget: Any? = null
if (jvmObject.jvmType.invocationTarget != null) {
try {
logger.debug("Instantiating InvocationTarget: ${jvmObject.jvmType.invocationTarget}")
invocationTarget = instantiate(jvmObject.jvmType.invocationTarget)
logger.debug("Instantiated OK InvocationTarget: ${jvmObject.jvmType.invocationTarget}")
logger.debug("InvocationTarget: $invocationTarget")
} catch (ex: Exception) {
val argTypes = jvmObject.jvmCtorArgs.joinToString(",") { x -> x.jvmType.fqClassName }
val className = jvmObject.jvmType.fqClassName
val constructorName = jvmObject.jvmType.constructorName
val message = "Failed to instantiate invocation target for " +
"JvmType:${className}${constructorName}(${argTypes}) with an " +
"InvocationTarget: ${jvmObject.jvmType.invocationTarget}"
throw InstantiationException(message, ex)
}
}

val factoryClass: Class<*> = if (invocationTarget == null) clazz else invocationTarget::class.java

val factoryMethod: Method
try {
factoryMethod = clazz.methods
factoryMethod = factoryClass.methods
.filter { c -> c.name == jvmObject.jvmType.constructorName }
.filter { c -> c.parameterCount == methodArgTypes.size }
.single { c ->
Expand All @@ -96,35 +169,18 @@ class JsonJvmObjectDeserializer(
}
} catch (ex: NoSuchElementException) {
val argTypes = jvmObject.jvmCtorArgs.joinToString(",") { x -> x.jvmType.fqClassName }
val className = jvmObject.jvmType.fqClassName
val methodsAsStrings = clazz.constructors
.mapIndexed { i, c -> "$className->Method#${i + 1}(${c.parameterTypes.joinToString { p -> p.name }})" }
.joinToString(" ;; ")
val targetMethod = "Cannot find matching method for ${className}(${argTypes})"
val className = factoryClass.name
val methodsAsStrings =
factoryClass.methods.joinToString("\n") { c -> "$className#${c.name}(${c.parameterTypes.joinToString { p -> p.name }})" }
val targetMethod = "Cannot find matching method for ${className}#${jvmObject.jvmType.constructorName}(${argTypes})"
val availableMethods =
"Searched among the ${clazz.constructors.size} available methods: $methodsAsStrings"
throw RuntimeException("$targetMethod --- $availableMethods")
"Searched among the ${clazz.methods.size} available methods: $methodsAsStrings"
throw ConstructorLookupException("$targetMethod --- $availableMethods")
}

logger.info("Constructor=${factoryMethod}")
constructorArgs.forEachIndexed { index, it -> logger.info("Constructor ARGS: #${index} -> $it") }

var invocationTarget: Any? = null
if (jvmObject.jvmType.invocationTarget != null) {
try {
logger.debug("Instantiating InvocationTarget: ${jvmObject.jvmType.invocationTarget}")
invocationTarget = instantiate(jvmObject.jvmType.invocationTarget)
logger.debug("Instantiated OK InvocationTarget: ${jvmObject.jvmType.invocationTarget}")
} catch (ex: Exception) {
val argTypes = jvmObject.jvmCtorArgs.joinToString(",") { x -> x.jvmType.fqClassName }
val className = jvmObject.jvmType.fqClassName
val constructorName = jvmObject.jvmType.constructorName
val message = "Failed to instantiate invocation target for " +
"JvmType:${className}${constructorName}(${argTypes}) with an " +
"InvocationTarget: ${jvmObject.jvmType.invocationTarget}"
throw RuntimeException(message, ex)
}
}
val instance = factoryMethod.invoke(invocationTarget, *constructorArgs)
logger.info("Instantiated REFERENCE OK {}", instance)
return instance
Expand Down
Loading

0 comments on commit 78e6754

Please sign in to comment.