Java SAM interface created from Kotlin gives ClassCastException - kotlin

I have a java method:
addHandler(HttpServiceHandler handler)
HttpServiceHandler is
interface HttpServiceHandler extends Consumer<HttpHandlerContext>
The point is to avoid Consumer<HttpHandlerContext> copy-paste across the project, so it's kind of a type alias.
In Java code, this works all right:
addHandler({ context -> context.blah(); })
Now, in Kotlin, I have this method that generates handlers:
private companion object {
fun newHandler(notimportant: Long): HttpServiceHandler {
return HttpServiceHandler { context -> context.blah() }
}
}
HttpServiceHandler {} is important, it doesn't compile if I don't specify HttpServiceHandler for the lambda.
And this compiles:
addHandler(newHandler(1L))
But at runtime, throws:
java.lang.ClassCastException: blah.BlahTest$Companion$newHandler$1 cannot be cast to kotlin.jvm.functions.Function1
at blah.BlahTest.test(BlahTest.kt:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
And I can't figure out why. Help please?
Update: The class cast exception is gone if I write it like this:
addHandler(
object : HttpServiceHandler {
override fun accept(c: HttpHandlerContext) {
c.complete()
}
}
)
But still throws the exception when written like this:
fun newHandler(blah: Long): HttpServiceHandler {
return object : HttpServiceHandler {
override fun accept(c: HttpHandlerContext) {
c.complete()
}
}
}
addHandler(newHandler(1L))
And I have no idea why.
Update2: Test code at https://github.com/wilem82/testcases/tree/master/kotlinsam1 . Does not reproduce the problem, sadly.

the error has already said exactly:
java.lang.ClassCastException: blah.BlahTest$Companion$newHandler$1 cannot be cast to kotlin.jvm.functions.Function1
you try to cast a Consumer<HttpHandlerContext> to a Function1 in addHandler method or somewhere, for example:
fun addHandler(handler: Consumer<HttpHandlerContext>) {
val it: Function1<*, *> = handler as Function1<*, *>
// ^
// ClassCastException was thrown since it is not a Function1
}
you should using method reference expression in java / function reference expression in kotlin to convert a SAM interface to another SAM interface, for example:
fun addHandler(handler: Consumer<HttpHandlerContext>) {
val it: Function1<HttpHandlerContext, Unit> = handler::accept
// using function reference expression here ---^
//...
}
IF you call java method addHandler in kotlin, you needn't to create such a bridge method newHandler, you can just call it with lambda in kotlin, for example:
addHandler{context->
context.blah()
// additional works ...
}

Related

Getting java.net.SocketException: No buffer space available and BindException using Jsoup while connecting to url using Kotlin Coroutines

I was trying to do parser for a website, and because there's a lot of content, I used Kotlin coroutines to make parsing asynchronous, but I've ran into a problem where I constantly get java.net.SocketException: No buffer space available (maximum connections reached?): connect at java.base/sun.nio.ch.Net.connect0(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.connect(Net.java:579) ~[na:na] at java.base/sun.nio.ch.Net.connect(Net.java:568) ~[na:na] and Suppressed: java.net.BindException: Address already in use: no further information at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na] at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542) ~[na:na]
I use the function below to get a document
fun connect(url: String): Document {
return try {
Jsoup.connect(url).newRequest().get()
} catch (_: SocketTimeoutException) {
println("RETRYING $url")
connect(url)
}
}
and
runBlocking {
withContext(Dispatchers.IO) {
val updated: ArrayList<Deferred<List<Book>>> = arrayListOf()
for (booklist in booklists) {
updated.add(async {
booklist.forEach { book -> book.sections = ru.parseBook(book) }
return#async booklist
})
}
updated.awaitAll().forEach { u -> bookRepository.saveAll(u) }
}
}
to run parser
Worked it out via HttpClient which is being created for every Coroutine:
for (bookList in chunked) {
updated.add(async {
val client = HttpClient.newHttpClient()
bookList.forEach { book -> book.sections = engine.parseBook(book, client) }
return#async bookList
})
}
and passed as a variable into a function
val request = HttpRequest.newBuilder(URI(url)).GET().build()
withContext(Dispatchers.IO) {
val send = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
val doc = Jsoup.parse(send.body())
}

Spring AuthenticationManager StackOverflowError with Kotlin

I have SecurityConfig for my application on Kotlin with Spring. So when I try to authenticate I get java.lang.StackOverflowError: null. I found it returns when runs this:
authenticationManager.authenticate(new
UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));
So this is my config.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig() : WebSecurityConfigurerAdapter() {
#Override
override fun configure(http: HttpSecurity) {
http
.csrf().disable()
}
#Bean
fun passwordEncoder(): BCryptPasswordEncoder? = BCryptPasswordEncoder()
#Override
#Bean
override fun authenticationManagerBean(): AuthenticationManager =
super.authenticationManagerBean()
}
Ant this is exception:
java.lang.StackOverflowError: null
at java.base/java.lang.String.equals(String.java:1827) ~[na:na]
at org.springframework.util.ReflectionUtils.isEqualsMethod(ReflectionUtils.java:514) ~[spring-core-5.3.12.jar:5.3.12]
at org.springframework.aop.support.AopUtils.isEqualsMethod(AopUtils.java:151) ~[spring-aop-5.3.12.jar:5.3.12]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:167) ~[spring-aop-5.3.12.jar:5.3.12]
at jdk.proxy2/jdk.proxy2.$Proxy99.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201) ~[spring-security-core-5.5.3.jar:5.5.3]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:510) ~[spring-security-config-5.5.3.jar:5.5.3]
at jdk.internal.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.12.jar:5.3.12]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.12.jar:5.3.12]
at jdk.proxy2/jdk.proxy2.$Proxy99.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201) ~[spring-security-core-5.5.3.jar:5.5.3]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:510) ~[spring-security-config-5.5.3.jar:5.5.3]
at jdk.internal.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.12.jar:5.3.12]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.12.jar:5.3.12]
at jdk.proxy2/jdk.proxy2.$Proxy99.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201) ~[spring-security-core-5.5.3.jar:5.5.3]
I found a lot of same problems but none of them have helped me.
Where the problem is?
The same config on Java work correctly
So, I solved my problem.
A basic authenticationManager check username and password
fun createAuthToken(#RequestBody authRequest: JwtRequest): ResponseEntity<Any?> {
try {
authenticationManager.authenticate(
UsernamePasswordAuthenticationToken(
authRequest.username,
authRequest.password
))
So it is necessary authenticationManager to know how to check username and password. We should override special method loadUserByUsername in UserService and make UserService extends UserDetailsService like this/
enter code here
#Override
#Transactional
override fun loadUserByUsername(username: String): UserDetails {
val user: User = findByUsername(username) ?: throw
UsernameNotFoundException(("User ${username} not found"))
return org.springframework.security.core.userdetails.User(
user.userName,
user.password,
mapRolesToAuthorities(user.getRoles())
)}

InvocationHandler in Kotlin

I'm reading Head First: Design Patterns (2nd ed) and I followed the code sample but instead of using Java, I used Kotlin. Currently, I'm in a chapter tackling about proxy protection pattern and having difficulty to run it with Kotlin. Please see the code and exceptions below.
Sample code
interface Person {
fun getName(): String
fun setName(name: String)
}
class PersonImpl : Person {
private var _name: String = ""
override fun getName(): String = _name
override fun setName(name: String) {
_name = name
}
}
class OwnerInvocationHandler(private val person: Person) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any? {
try {
val methodName = method.name
if (methodName.isNullOrEmpty())
return null
if (methodName.startsWith("get")) {
// return method.invoke(proxy, *(args ?: arrayOfNulls<Any>(0))) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *(args ?: emptyArray())) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *args.orEmpty()) // << Encountered "EXCEPTION B" below
return method.invoke(proxy, args) // << From the code sample, encountered "EXCEPTION A" below
} else if (methodName.startsWith("set")) {
return method.invoke(person, args)
}
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
return null
}
}
// main.kt
val listOfPeople = arrayListOf<Person>()
fun main(array: Array<String>) {
initializeDatabase()
val joe = getPersonFromDatabase("Joe Javabean") ?: return
val ownerProxy = getOwnerProxy(joe)
println("Name is ${ownerProxy.getName()}")
}
fun initializeDatabase() {
val p1 = PersonImpl()
p1.setName("Joe Javabean")
listOfPeople.add(p1)
}
fun getOwnerProxy(person: Person): Person {
return Proxy.newProxyInstance(
person.javaClass.classLoader,
person.javaClass.interfaces,
OwnerInvocationHandler(person)
) as Person
}
fun getPersonFromDatabase(name: String): Person? {
return listOfPeople.firstOrNull { p -> name.contentEquals(p.getName()) }
}
Exception
Exception A
/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10a5ef4e0). One of the two will be used. Which one is undefined.
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at com.helloworld.app.MainKt.main(main.kt:12)
Exception B
objc[64094]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x109e744c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x109eee4e0). One of the two will be used. Which one is undefined.
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
java.lang.reflect.InvocationTargetExceptionjava.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.helloworld.app.OwnerInvocationHandler.invoke(OwnerInvocationHandler.kt:17)
at com.sun.proxy.$Proxy0.getName(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
...
I found the link below but didn't solve my issue.
Kotlin: Argument Type Mismatch when passing Array as vararg parameter
This is just a user error (embarrassing). As #broot pointed out:
Code Sample
class OwnerInvocationHandler(private val person: Person) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any? {
try {
val methodName = method.name
if (methodName.isNullOrEmpty())
return null
if (methodName.startsWith("get")) {
// return method.invoke(proxy, *(args ?: arrayOfNulls<Any>(0))) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *(args ?: emptyArray())) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, *args.orEmpty()) // << Encountered "EXCEPTION B" below
// return method.invoke(proxy, args) // << From the code sample, encountered "EXCEPTION A" below
// `person` should be used not `proxy`
return method.invoke(person, *args.orEmpty())
} else if (methodName.startsWith("set")) {
return method.invoke(person, args)
}
} catch (e: InvocationTargetException) {
e.printStackTrace()
}
return null
}
}

Corda persistence: unknown entity: PersistentFungibleToken

General description
Good day!
I'm experimenting with corda version 4.3 token SDK version 1.1 and want to try various scenarios with issuance/redeeming custom Fungible tokens.
I wrote a simple flow which describes token issuance for party to itself:
While the very basic flow seems to be working fine, in breaks on the subFlow(IssueTokensFlow), on an attempt to save an PersistentFungibleToken to the database (stack trace below).
I tried to implement my own schema, but with no success, as function supportedScema() requires only List.
I also tried to Issue some tokens without using customized FungilbeToken, with no success either.
How to fix this?
Flow
import co.paralleluniverse.fibers.Suspendable
import com.maker.loyalty.states.NativeToken
import com.maker.loyalty.states.ParticipantNativeFungibleToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.utilities.heldBy
import com.r3.corda.lib.tokens.contracts.utilities.of
import com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow
import com.r3.corda.lib.tokens.workflows.utilities.getPreferredNotary
import net.corda.core.flows.*
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
object IssueTokenExperiment {
private fun tracker() = ProgressTracker(
NotaryInfo,
CreateTransaction,
Verify,
Finalize,
ExchangeValuesSingleParty.ExecuteExchange,
SigningByOtherParty
)
private const val defaultAmount: Long = 2
private val otherPartyDefaultName = CordaX500Name(organisation = "PartyA", locality = "London", country = "GB")
//Scenario - issue token onto the ledger, verify it was issued
#InitiatingFlow
#StartableByRPC
/**
* Issues token to self an notifies the counter party about issuance
* */
class IssueNativeTokenToSelfInitializer(private val amount: Long = defaultAmount,
private val otherPartyX500Name: CordaX500Name = otherPartyDefaultName) : FlowLogic<SignedTransaction>() {
override val progressTracker: ProgressTracker = tracker()
#Suspendable
override fun call(): SignedTransaction {
progressTracker.currentStep = NotaryInfo
getPreferredNotary(serviceHub)
val otherParty: Party = run {
serviceHub.networkMapCache.getPeerByLegalName(otherPartyX500Name)
?: throw FlowException("Other party not found")
}
val otherPartySession = initiateFlow(otherParty)
progressTracker.currentStep = CreateTransaction
val issuedSupplyAmount = NativeToken issuedBy ourIdentity
val supplyAmount = amount of issuedSupplyAmount
// val liability = supplyAmount heldBy ourIdentity
val participantNativeAccountState = ParticipantNativeFungibleToken(
amount = supplyAmount,
issuer = ourIdentity,
participants = listOf(ourIdentity, otherParty),
observers = emptyList(),
owner = ourIdentity
)
return subFlow(IssueTokensFlow(
token = participantNativeAccountState as FungibleToken,
participantSessions = listOf(otherPartySession)
))
}
}
}
Stack trace
java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
at net.corda.core.internal.concurrent.CordaFutureImpl.get(CordaFutureImpl.kt)
at com.maker.loyalty.TwoPartyCoreTests.Create token test EXPERIMENT(TwoPartyCoreTests.kt:91)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:175)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:157)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Unknown entity: com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:807)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:788)
at net.corda.node.services.schema.PersistentStateService.persistStatesWithSchema$node(PersistentStateService.kt:48)
at net.corda.node.services.schema.PersistentStateService.persist(PersistentStateService.kt:40)
at net.corda.node.services.vault.NodeVaultService.processAndNotify(NodeVaultService.kt:393)
at net.corda.node.services.vault.NodeVaultService.access$processAndNotify(NodeVaultService.kt:54)
at net.corda.node.services.vault.NodeVaultService$notifyAll$1.invoke(NodeVaultService.kt:224)
at net.corda.node.services.vault.NodeVaultService$notifyAll$2.invoke(NodeVaultService.kt:235)
at net.corda.node.services.vault.NodeVaultService.notifyAll(NodeVaultService.kt:239)
at net.corda.node.services.api.ServiceHubInternal$Companion$recordTransactions$1.invoke(ServiceHubInternal.kt:128)
at net.corda.node.services.api.ServiceHubInternal$Companion$recordTransactions$1.invoke(ServiceHubInternal.kt:53)
at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:235)
at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:214)
at net.corda.nodeapi.internal.persistence.CordaPersistence.transaction(CordaPersistence.kt:220)
at net.corda.node.services.api.ServiceHubInternal$Companion.recordTransactions(ServiceHubInternal.kt:72)
at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt:158)
at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
at net.corda.core.node.ServiceHub$DefaultImpls.recordTransactions(ServiceHub.kt:214)
at net.corda.core.internal.ServiceHubCoreInternal$DefaultImpls.recordTransactions(ServiceHubCoreInternal.kt)
at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt)
at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
at net.corda.core.node.ServiceHub$DefaultImpls.recordTransactions(ServiceHub.kt:206)
at net.corda.core.internal.ServiceHubCoreInternal$DefaultImpls.recordTransactions(ServiceHubCoreInternal.kt)
at net.corda.node.services.api.ServiceHubInternal$DefaultImpls.recordTransactions(ServiceHubInternal.kt)
at net.corda.node.internal.AbstractNode$ServiceHubInternalImpl.recordTransactions(AbstractNode.kt:1008)
at net.corda.core.flows.FinalityFlow.notariseAndRecord(FinalityFlow.kt:205)
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:147)
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:39)
at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
at com.r3.corda.lib.tokens.workflows.internal.flows.finality.ObserverAwareFinalityFlow.call(ObserverAwareFinalityFlow.kt:75)
at com.r3.corda.lib.tokens.workflows.internal.flows.finality.ObserverAwareFinalityFlow.call(ObserverAwareFinalityFlow.kt:35)
at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
at com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow.call(IssueTokensFlow.kt:84)
at com.r3.corda.lib.tokens.workflows.flows.issue.IssueTokensFlow.call(IssueTokensFlow.kt:46)
at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330)
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326)
at com.maker.loyalty.flows.IssueTokenExperiment$IssueNativeTokenToSelfInitializer.call(IssueTokenExperiment.kt:64)
at com.maker.loyalty.flows.IssueTokenExperiment$IssueNativeTokenToSelfInitializer.call(IssueTokenExperiment.kt:37)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46)
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63)
Fungible Token
import com.maker.loyalty.contracts.ParticipantNativeAccountContract
import com.maker.loyalty.schemas.ParticipantNativeTokenSchemaV1
import com.r3.corda.lib.tokens.contracts.internal.schemas.FungibleTokenSchema
import com.r3.corda.lib.tokens.contracts.internal.schemas.FungibleTokenSchemaV1
import com.r3.corda.lib.tokens.contracts.internal.schemas.PersistentFungibleToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType
import com.r3.corda.lib.tokens.contracts.types.TokenType
import net.corda.core.contracts.Amount
import net.corda.core.contracts.BelongsToContract
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import java.util.*
#BelongsToContract(ParticipantNativeAccountContract::class)
data class ParticipantNativeFungibleToken(override val amount: Amount<IssuedTokenType>,
override val issuer: Party,
val owner: Party,
val observers: List<AbstractParty> = listOf(),
override val participants: List<AbstractParty> = (mutableListOf(owner, issuer) + observers)
) : FungibleToken(amount, issuer), QueryableState {
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is FungibleTokenSchemaV1 -> PersistentFungibleToken(
issuer = issuer,
tokenIdentifier = amount.token.tokenIdentifier,
holder = owner,
tokenClass = amount.token.tokenClass,
amount = amount.quantity
)
// is ParticipantNativeTokenSchemaV1 -> ParticipantNativeTokenSchemaV1.PersistentParticipantNativeToken(
// issuer = issuer,
// tokenIdentifier = amount.token.tokenIdentifier,
// holder = owner,
// tokenClass = amount.token.tokenClass,
// amount = amount.quantity
// )
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas() = listOf(FungibleTokenSchemaV1)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ParticipantNativeFungibleToken
if (amount != other.amount) return false
if (holder != other.holder) return false
if (tokenTypeJarHash != other.tokenTypeJarHash) return false
return true
}
override fun hashCode(): Int {
var result = amount.hashCode()
result = 31 * result + holder.hashCode()
result = 31 * result + (tokenTypeJarHash?.hashCode() ?: 0)
return result
}
}
Test
#Test
fun `Issue token for self and notify the other party`() {
val resultFuture = a.startFlow(IssueTokenExperiment.IssueNativeTokenToSelfInitializer(
amount = 10L,
otherPartyX500Name = bNodeName
))
network.runNetwork()
val res = resultFuture.get()
assertNotNull(res)
}
#Before
fun setup() {
network.runNetwork()
setupClients()
addBonusToClientAccount()
}
#After
fun tearDown() = network.stopNodes()
private fun setupClients() {
a.startFlow(ClientAccountFlows.InitiateClientAccountCreation(person = defaultPerson))
b.startFlow(ClientAccountFlows.InitiateClientAccountCreation(person = defaultPerson))
network.runNetwork()
}
private fun addBonusToClientAccount() {
a.startFlow(ClientAccountFlows.AddBonusToClientAccount(person = defaultPerson, addedBonus = 100L))
b.startFlow(ClientAccountFlows.AddBonusToClientAccount(person = defaultPerson, addedBonus = 100L))
network.runNetwork()
}
gradle
apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'net.corda.plugins.quasar-utils'
cordapp {
targetPlatformVersion corda_platform_version
minimumPlatformVersion corda_platform_version
workflow {
name "Loyalty Flows"
versionId 1
}
}
sourceSets {
main {
resources {
srcDir rootProject.file("config/dev")
}
}
test {
resources {
srcDir rootProject.file("config/test")
}
}
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integrationTest/kotlin')
}
}
}
repositories {
maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda-lib' }
maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda-lib-dev' }
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile "junit:junit:$junit_version"
// Corda dependencies.
cordaCompile "$corda_release_group:corda-core:$corda_release_version"
cordaRuntime "$corda_release_group:corda:$corda_release_version"
cordaCompile "$tokens_release_group:tokens-workflows:$tokens_release_version"
testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
//tokens sdk
cordaCompile "$tokens_release_group:tokens-workflows:$tokens_release_version"
cordaCompile "$tokens_release_group:tokens-money:$tokens_release_version"
cordapp "$tokens_release_group:tokens-selection:$tokens_release_version"
// CorDapp dependencies.
cordapp project(":contracts")
}
task integrationTest(type: Test, dependsOn: []) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
You probably forgot to include the tokens-contracts dependency (which has the missing custom schema class mentioned in your error message) inside your node definition (i.e. inside deployNodes task in the root build.gradle file).
Add this line:
cordapp("$tokens_release_group:tokens-contracts:$tokens_release_version")
Remember to also include that dependency in the MockService of your contract tests and MockNetwork of your flow tests:
// Contract tests.
private final MockServices ledgerServices = new MockServices(
Collections.singletonList("com.r3.corda.lib.tokens.contracts"));
// Flow tests.
private final MockNetwork network = new MockNetwork(new MockNetworkParameters()
.withCordappsForAllNodes(ImmutableList.of(
TestCordapp.findCordapp("com.r3.corda.lib.tokens.contracts"),

Serializer for interface / implementation

Suppose there is one interface , and 2 (or more) implementations :
interface IRunnable {
fun run()
}
class Horse : IRunnable {
override fun run() { println("horse running") }
}
class Dog : IRunnable {
override fun run() { println("dog running") }
}
I want to achieve the most succinct JSON for network transmission, that is , {"runnable":"H"} for Horse and {"runnable":"D"} for Dog
If I cannot modify the interface and implementations , I want to serialize the interface/implementations to JSON , according to Kotlin's documentation , I have to write a custom serializer , and use SerializersModule to achieve the goal.
Suppose there are only Horse and Dog implementing IRunnable , This is what I've done :
class RunnableSerializer : KSerializer<IRunnable> {
override val descriptor: SerialDescriptor
get() = StringDescriptor.withName("runnable")
override fun serialize(encoder: Encoder, obj: IRunnable) {
val stringValue = when (obj) {
is Horse -> { "H" }
is Dog -> { "D" }
else -> { null }
}
stringValue?.also {
encoder.encodeString(it)
}
}
override fun deserialize(decoder: Decoder): IRunnable {
return decoder.decodeString().let { value ->
when(value) {
"H" -> Horse()
"D" -> Dog()
else -> throw RuntimeException("invalid $value")
}
}
}
}
But when I try to transform a Horse to JSON ...
class RunnableTest {
private val logger = KotlinLogging.logger { }
#ImplicitReflectionSerializer
#Test
fun testJson() {
val module1 = serializersModuleOf(IRunnable::class, RunnableSerializer())
Json(context = module1).stringify(IRunnable::class.serializer(), Horse()).also {
logger.info("json = {}", it)
}
}
}
It outputs error :
kotlinx.serialization.SerializationException:
Can't locate argument-less serializer for class IRunnable. For generic classes, such as lists, please provide serializer explicitly.
How to achieve something like
{"runnable":"H"} or {"runnable":"D"} in this case ?
Thanks.
environments :
<kotlin.version>1.3.60</kotlin.version>
<serialization.version>0.14.0</serialization.version>
updated , full error message :
kotlinx.serialization.SerializationException: Can't locate argument-less serializer for class IRunnable. For generic classes, such as lists, please provide serializer explicitly.
at kotlinx.serialization.PlatformUtilsKt.serializer(PlatformUtils.kt:12)
at RunnableTest.testJson(RunnableTest.kt:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
What you are trying to achieve is called polymorphic serialization, which kotlinx.serialization supports. In short: it requires you to register all deriving types of IRunnable as polymorphic in the SerialModule. For your code it should look somewhat as follows:
val serialModule = SerializersModule {
polymorphic(IRunnable::class) {
Horse::class with HorseSerializer
Dog::class with DogSerializer
}
}
Currently, you only register IRunnable, which as the exception correctly indicates, cannot be initialized. How would you initialize an interface? You can't. Instead, you want to know which specific deriving type to initialize. This requires to embed type information within the JSON data, which is what polymorphic serialization does.
In addition, you do not necessarily have to implement a custom serializer. As per the documentation you link to, you can create external serializers for library classes. The following might suffice, as the kotlinx.serialization compiler plugin will try to generate a suitable serializer for you, similar to if you were to add #Serializable on the class directly:
#Serializer(forClass = Horse::class)
object HorseSerializer {}
#Serializer(forClass = Dog::class)
object DogSerializer {}