Corda persistence: unknown entity: PersistentFungibleToken - kotlin

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"),

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())
}

Proguard causing error on org.jetbrains.exposed

941 / 5.000
Resultados de tradução
I'm trying to use Proguard to obfuscate my application's code, but this is an almost impossible task. It is a desktop application developed in Kotlin language using TornadoFX (JavaFX), and org.jetbrains.exposed. After a lot of work, I managed to make the application compile. But when trying to run the system it gives an error message when trying to use the database functions using org.jetbrains.exposed. My application has a set of packages but I want to obfuscate only one package that contains 6 files, which is the important part of the software. But even using "keep", Proguard changes the classes and the application stops working.
I would like to know if there is a way to obfuscate only some files and tell ProGuard not to change the other files.
If it is not possible, does anyone know how to fix the problem when using the org.jetbrains.exposed library together with Proguard?
build.gradle task
task proguard(type: ProGuardTask) {
doFirst {
waveCounterJar
}
println("Performing obfuscation..")
configuration 'proguard.conf'
injars "$inputJar", filter: "!META-INF/NOTICE,!META-INF/NOTICE.txt,!META-INF/LICENSE,!META-INF/LICENSE.txt"
outjars "$outputJar"
libraryjars "${System.getProperty('java.home')}\\lib\\rt.jar"
libraryjars "${dependsDir}"
}
proguard.conf
-dontshrink
-dontoptimize
-keepkotlinmetadata
# Exclude all other class
-dontwarn !br.com.mawan.waveCounter.waveDetectionUtils.**
-keep class !br.com.mawan.waveCounter.waveDetectionUtils.** { *; }
-keep class * extends org.jetbrains.exposed.dao.IntIdTable { *; }
-keep class * extends org.jetbrains.exposed.dao.IntEntityClass { *; }
-keep class * extends org.jetbrains.exposed.dao.IntEntity { *; }
Pallet.class
package br.com.mawan.waveCounter.models
import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.dao.IntEntity
import org.jetbrains.exposed.dao.IntEntityClass
import org.jetbrains.exposed.dao.IntIdTable
import org.joda.time.DateTime
object PalletsTable: IntIdTable() {
val palletNr = varchar("palletNr", 50)
val op = varchar("op", 50).nullable()
val palletOp = varchar("palletOp", 50).nullable()
val wave = varchar("wave", 5)
val stacks = integer("stacks")
val totalWaves = integer("totalWaves").default(0)
val totalBoards = integer("totalBoards").default(0)
val estimatedHeight = integer("estimatedHeight").default(0)
val quantity1 = integer("quantity1").default(0)
val quantity2 = integer("quantity2").default(0)
val message = varchar("message", 1000)
val createdDate = datetime("createdDate").default(DateTime.now())
val exported = bool("exported").default(false)
val disabled = bool("disabled").default(false)
}
class Pallet(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Pallet>(PalletsTable)
var palletNr by PalletsTable.palletNr
var op by PalletsTable.op
var palletOp by PalletsTable.palletOp
var wave by PalletsTable.wave
var stacks by PalletsTable.stacks
var totalWaves by PalletsTable.totalWaves
var totalBoards by PalletsTable.totalBoards
var createdDate by PalletsTable.createdDate
var estimatedHeight by PalletsTable.estimatedHeight
var quantity1 by PalletsTable.quantity1
var quantity2 by PalletsTable.quantity2
var message by PalletsTable.message
var disabled by PalletsTable.disabled
var exported by PalletsTable.exported
}
Code where occurs error
transaction {
exec("SET ##global.time_zone = '+00:00'")
// The error occurs here
SchemaUtils.createMissingTablesAndColumns(PalletsTable, LogWebTable, ActionScannerTable, ConfigTable, ScannerDataTable)
exec("ALTER TABLE pallets CHANGE createddate createddate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP")
}
Error
Caused by: kotlin.KotlinNullPointerException: null
at org.jetbrains.exposed.sql.Table.clone(Unknown Source)
at org.jetbrains.exposed.sql.Table.cloneWithAutoInc(Unknown Source)
at org.jetbrains.exposed.sql.Table.autoIncrement(Unknown Source)
at org.jetbrains.exposed.sql.Table.autoIncrement$default(Unknown Source)
at org.jetbrains.exposed.dao.IntIdTable.<init>(Unknown Source)
at org.jetbrains.exposed.dao.IntIdTable.<init>(Unknown Source)
at br.com.mawan.waveCounter.models.PalletsTable.<init>(Unknown Source)
at br.com.mawan.waveCounter.models.PalletsTable.<clinit>(Unknown Source)
... 26 common frames omitted

Encrypting existing Room database -> Exception Occur: file is not a database: , while compiling: select count(*) from sqlite_master;

I'm encrypting the existing Room database. When I call insert() method to insert data to Room and findAll() method to fetch data from Room After Encrypting the Room DB and run the app an exception occurs at both the methods and the app crashes. I researched a lot but the problem is not resolved. Please help me to solve this exception. And tell me what am I missing. Thanks a lot!
Code UserDB.kt:
#Database(entities = [User::class], version = 1)
abstract class UserDB : RoomDatabase() {
abstract fun userDao(): UserDAO
companion object {
#Volatile
private var instance: UserDB? = null
fun getInstance(context: Context, charArray: CharArray): UserDB? {
if (instance == null) {
synchronized(UserDB::class) {
val passphrase: ByteArray =
SQLiteDatabase.getBytes(charArray)
val factory = SupportFactory(passphrase)
instance = Room.databaseBuilder(
context.applicationContext,
UserDB::class.java,
"RoomDB"
).openHelperFactory(factory).build()
}
}
return instance
}
}
}
Code UserDAO.kt
#Dao
interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(user: User)
#Query("SELECT * FROM User")
fun findAll(): List<User>
}
Code MainActivity.kt:
class MainActivity : AppCompatActivity() {
private var userDB: UserDB? = null
private var pass: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pass = "!##ABC"
SQLiteDatabase.loadLibs(this)
userDB = UserDB.getInstance(this, pass!!.toCharArray())
userDB?.userDao()?.insert(User(0, "Ahmer"))
val textView1 = findViewById<TextView>(R.id.textData)
// TODO Display from Room
val u: User = userDB?.userDao()!!.findAll()[0]
Log.v("RoomProduct", u.id.toString() + " : " + u.name)
textView1.text = StringBuilder("${u.id} : ${u.name}")
}
}
Exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.roomwithcipher, PID: 32748
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.roomwithcipher/com.example.roomwithcipher.MainActivity}: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7050)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
Caused by: net.sqlcipher.database.SQLiteException: file is not a database: , while compiling: select count(*) from sqlite_master;
at net.sqlcipher.database.SQLiteCompiledSql.native_compile(Native Method)
at net.sqlcipher.database.SQLiteCompiledSql.compile(SQLiteCompiledSql.java:91)
at net.sqlcipher.database.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:64)
at net.sqlcipher.database.SQLiteProgram.<init>(SQLiteProgram.java:91)
at net.sqlcipher.database.SQLiteQuery.<init>(SQLiteQuery.java:48)
at net.sqlcipher.database.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:60)
at net.sqlcipher.database.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2016)
at net.sqlcipher.database.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1902)
at net.sqlcipher.database.SQLiteDatabase.keyDatabase(SQLiteDatabase.java:2673)
at net.sqlcipher.database.SQLiteDatabase.openDatabaseInternal(SQLiteDatabase.java:2603)
at net.sqlcipher.database.SQLiteDatabase.openDatabase(SQLiteDatabase.java:1247)
at net.sqlcipher.database.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1322)
at net.sqlcipher.database.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:166)
at net.sqlcipher.database.SupportHelper.getWritableDatabase(SupportHelper.java:83)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:622)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:399)
at com.example.roomwithcipher.UserDAO_Impl.insert(UserDAO_Impl.java:46)
at com.example.roomwithcipher.MainActivity.onCreate(MainActivity.kt:23)
at android.app.Activity.performCreate(Activity.java:7327)
at android.app.Activity.performCreate(Activity.java:7318)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3094)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:214) 
at android.app.ActivityThread.main(ActivityThread.java:7050) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 
I added the following dependencies in build.gradle(:app)
implementation 'net.zetetic:android-database-sqlcipher:4.3.0#aar'
implementation "androidx.sqlite:sqlite:2.0.1"
implementation 'androidx.room:room-runtime:2.3.0'
annotationProcessor("androidx.room:room-compiler:2.3.0")
kapt("androidx.room:room-compiler:2.3.0")

How to solver error for kotlin coroutines flow?

I tried to use NetworkBoundResource for my MVVM Model and after i follow some tutorial, i'm having an error look this
10-21 14:15:04.073 31376-31376/com.example.mvvmsecondmodel E/Process: android_os_Process_getProcessNameByPid pid is 31376
10-21 14:15:04.074 31376-31376/com.example.mvvmsecondmodel E/Process: android_os_Process_getProcessNameByPid value is mvvmsecondmodel
10-21 14:15:04.416 31376-31421/com.example.mvvmsecondmodel E/SQLiteLog: (283) recovered 8 frames from WAL file /data/data/com.example.mvvmsecondmodel/databases/movie_db-wal
10-21 14:15:04.640 31376-31376/com.example.mvvmsecondmodel E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.mvvmsecondmodel, PID: 31376
java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.flow.Flow<com.example.mvvmsecondmodel.data.respository.ApiResponse<com.example.mvvmsecondmodel.data.model.MovieResponse$Movie>>
for method ApiService.getMyMovie
at retrofit2.Utils.methodError(Utils.java:52)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:105)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:66)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:37)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
at retrofit2.Retrofit$1.invoke(Retrofit.java:149)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy0.getMyMovie(Unknown Source)
at com.example.mvvmsecondmodel.data.remote.ApiService$DefaultImpls.getMyMovie$default(ApiService.kt:11)
at com.example.mvvmsecondmodel.data.respository.MovieRespository$getListMovie$$inlined$networkBoundResource$1.invokeSuspend(NetworkBoundResource.kt:49)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:55)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.lang.IllegalArgumentException: Could not locate call adapter for kotlinx.coroutines.flow.Flow<com.example.mvvmsecondmodel.data.respository.ApiResponse<com.example.mvvmsecondmodel.data.model.MovieResponse$Movie>>.
Tried:
* retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
* retrofit2.DefaultCallAdapterFactory
at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:241)
at retrofit2.Retrofit.callAdapter(Retrofit.java:205)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:103)
... 14 more
At first i thought there's red sign at my code so i checked ApiService and Respository class and nothing wrong cause i already follow the tutorial
ApiService:
interface ApiService {
#GET("3/movie/popular")
fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : Flow<ApiResponse<MovieResponse.Movie>>
}
MovieRespository:
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return networkBoundResource(
fetchFromLocal = { movieDao.getMyMovie() },
shouldFetchFromRemote = {true},
fetchFromRemote = {apiService.getMyMovie()},
processRemoteResponse = {},
saveRemoteData = {movieDao.insert(
it.results.let {
it.map { data -> Movie.from(data) }
}
)},
onFetchFailed = {_, _ ->}
).flowOn(Dispatchers.IO)
}
You should define your api service as suspend function.
Api Service:
interface ApiService {
#GET("3/movie/popular")
suspend fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : ApiResponse<MovieResponse.Movie>
}
Movie Repository:
class MovieRepository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return flow {
// do your networkBoundResource functions
}.flowOn(Dispatchers.IO)
}
I can't test the code because you didn't share your NetworkBoundResource class. I hope the code help to fix your problem.

Retrofit-Vertx with RxJava2 in Kotlin IllegalStateException message == null

I'm building a very simple application in Kotlin with Vertx and RxJava 2 (RxKotlin), using Kovert REST framework and Retrofit. I have retrofit-vertx adapter and the RxJava2 Retrofit adapter. I can return an arbitrary list from my listUndergroundStations() method, but whenever I try to load from the remote API I get the following error:
Jun 23, 2017 2:16:29 PM uk.amb85.rxweb.api.UndergroundRestController
SEVERE: HTTP CODE 500 - /api/underground/stations - java.io.IOException: java.lang.IllegalStateException: message == null
java.lang.RuntimeException: java.io.IOException: java.lang.IllegalStateException: message == null
at io.reactivex.internal.util.ExceptionHelper.wrapOrThrow(ExceptionHelper.java:45)
at io.reactivex.internal.observers.BlockingMultiObserver.blockingGet(BlockingMultiObserver.java:91)
at io.reactivex.Single.blockingGet(Single.java:2148)
at uk.amb85.rxweb.api.UndergroundRestController$listUndergroundStations$1.invoke(UndergroundRestController.kt:35)
at uk.amb85.rxweb.api.UndergroundRestController$listUndergroundStations$1.invoke(UndergroundRestController.kt:13)
at nl.komponents.kovenant.TaskPromise$wrapper$1.invoke(promises-jvm.kt:138)
at nl.komponents.kovenant.TaskPromise$wrapper$1.invoke(promises-jvm.kt:130)
at nl.komponents.kovenant.NonBlockingDispatcher$ThreadContext.run(dispatcher-jvm.kt:327)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: java.lang.IllegalStateException: message == null
at com.julienviet.retrofit.vertx.VertxCallFactory$VertxCall.lambda$enqueue$0(VertxCallFactory.java:90)
at io.vertx.core.impl.FutureImpl.tryFail(FutureImpl.java:170)
at io.vertx.core.http.impl.HttpClientResponseImpl.handleException(HttpClientResponseImpl.java:270)
at io.vertx.core.http.impl.HttpClientResponseImpl.handleEnd(HttpClientResponseImpl.java:259)
at io.vertx.core.http.impl.ClientConnection.handleResponseEnd(ClientConnection.java:361)
at io.vertx.core.http.impl.ClientHandler.doMessageReceived(ClientHandler.java:80)
at io.vertx.core.http.impl.ClientHandler.doMessageReceived(ClientHandler.java:38)
at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$0(VertxHttpHandler.java:71)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:335)
at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:193)
at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:71)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:122)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1228)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1039)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:411)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:642)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:565)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:479)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:441)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
... 1 more
Caused by: java.lang.IllegalStateException: message == null
at okhttp3.Response$Builder.build(Response.java:431)
at com.julienviet.retrofit.vertx.VertxCallFactory$VertxCall.lambda$null$1(VertxCallFactory.java:109)
at io.vertx.core.http.impl.HttpClientResponseImpl$BodyHandler.notifyHandler(HttpClientResponseImpl.java:301)
at io.vertx.core.http.impl.HttpClientResponseImpl.lambda$bodyHandler$0(HttpClientResponseImpl.java:193)
at io.vertx.core.http.impl.HttpClientResponseImpl.handleEnd(HttpClientResponseImpl.java:257)
... 36 more
I can't for the life of me work out what is causing the IllegalStateException and have googled it to death. I don't think it's Rx related because I get the same error if I make the method return Observable<List<UndergroundLine>> or even get rid of Rx entirely and return Call<List<UndergroundLine>> (adjusting the controller accordingly). However, beyond that, I'm beating my head against a wall! Is anyone able to point out the error of my ways (besides putting a cushion under my head)?
Main Verticle:
class ApiVerticle : AbstractVerticle() {
override fun start(startFuture: Future<Void>?) {
// Initialise injection.
configureKodein()
val apiRouter = configureRouter(vertx)
vertx.createHttpServer()
.requestHandler { apiRouter.accept(it) }
.listen(8080)
}
private fun configureKodein() {
Kodein.global.addImport(Kodein.Module {
import(TflUndergroundService.module)
})
}
private fun configureRouter(vertx: Vertx): Router {
val apiMountPoint = "api"
val routerInit = fun Router.() {
bindController(UndergroundRestController(), apiMountPoint)
}
val router = Router.router(vertx) initializedBy { router ->
router.routerInit()
}
return router
}
}
TflService:
interface TflService {
#GET("/Line/Mode/tube")
fun getAllUndergroundLines(): Observable<UndergroundLine>
#GET("/Line/{lineName}/StopPoints")
fun getStationsForUndergroundLine(
#Path("lineName") lineName: String
): Observable<UndergroundStation>
#GET("/Line/{lineName}/Arrivals?stopPointId={stationNaptanId")
fun getArrivalsFor(
#Path("lineName") lineName: String,
#Path("stationNaptanId") stationNaptanId: String
) : Observable<Arrival>
}
data class UndergroundLine(val id: String, val name: String)
data class UndergroundStation(val naptanId: String, val commonName: String)
data class Arrival(
val platformName: String,
val towards: String,
val currentLocation: String,
val expectedArrival: LocalDateTime)
object TflUndergroundService {
val module = Kodein.Module {
val vertx: Vertx = Vertx.currentContext().owner()
val client: HttpClient = vertx.createHttpClient()
val jacksonMapper: ObjectMapper = ObjectMapper()
jacksonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://api.tfl.gov.uk/")
.callFactory(VertxCallFactory(client))
.addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
.addConverterFactory(JacksonConverterFactory.create(jacksonMapper))
.build()
val tflService: TflService = retrofit.create(TflService::class.java)
bind<TflService>() with instance(tflService)
}
}
ApiKeySecured (Just requires "appid" to be a parameter):
class ApiKeySecured(private val routingContext: RoutingContext) : KodeinGlobalAware {
val user: String = routingContext.request().getParam("appid") ?: throw HttpErrorUnauthorized()
}
The offending REST controller (in Kovert, Promise's are executed on Vertx worker thread):
class UndergroundRestController(val undergroundService: TflService = Kodein.global.instance()) {
fun ApiKeySecured.listUndergroundStations(): Promise<List<UndergroundLine>, Exception> {
//TODO: This is blocking, fix it!??
return task {
undergroundService
.getAllUndergroundLines()
.doOnError { println(it) }
.toList()
.blockingGet()
}
}
}
build.gradle:
mainClassName = "io.vertx.core.Launcher"
def mainVerticleName = "uk.amb85.rxweb.verticles.ApiVerticle"
def configurationFile = "conf/development.json"
run {
args = ["run",
mainVerticleName,
"--launcher-class=$mainClassName",
"-conf $configurationFile"
]
}
There's an issue with retrofit-vertx you are using. OkHttp3's ResponseBuilder requires message to be not null, but VertxCallFactory doesn't set it.
It's fixed in the latest version, but as it's still in development, you have to use snapshot:
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
dependencies {
compile 'com.julienviet:retrofit-vertx:1.0.2-SNAPSHOT'
}
Switching to snapshot dependency fixes the issue you mention in your question, but there's an issue with json mapping, which can be easily fixed by switching code from:
#GET("/Line/Mode/tube")
fun getAllUndergroundLines(): Observable<UndergroundLine>
to:
#GET("/Line/Mode/tube")
fun getAllUndergroundLines(): Observable<List<UndergroundLine>>
And updating your data classes to have default empty constructor to let Jackson instantiate using reflection:
data class UndergroundLine(var id: String = "", var name: String = "")
More on emtpy default constructor for data classes.
But it's another question related to how to parse response from API you're using to Observable and should be asked if you don't find a workaround.