Fail/Error in cordapp-template FlowTest progressTracker hasn't been started - testing

when I try to run this tests for the flows in the cordapp-template:
#Test
fun flowRecordTransactionInBothVaults() {
val flow = IOUFlow.Initiator(1,b.info.legalIdentity)
val future = a.services.startFlow(flow).resultFuture
net.runNetwork()
val signedTx = future.getOrThrow()
for (node in listOf(a,b)) {
assertEquals(signedTx, node.storage.validatedTransactions.getTransaction(signedTx.id))
}
}
I get this error: Progress tracker hasn't been started
[INFO ] 15:14:52.144 [Mock network] AbstractNetworkMapService.processRegistrationRequest - Added node CN=Mock Company 3,O=R3,L=New York,C=US to network map
[WARN ] 15:14:52.172 [Mock network] [a11087fc-381d-4547-8736-5265c334c71f].maybeWireUpProgressTracking - ProgressTracker has not been started
[WARN ] 15:14:52.191 [Mock network] [0dcfa270-b1af-40e9-92f1-411334cf0c73].run - Terminated by unexpected exceptionkotlin.NotImplementedError: An operation is not implemented: not implemented
at com.template.flow.IOUFlow$Acceptor$call$1.checkTransaction(TemplateFlow.kt:225) ~[main/:?]
at net.corda.flows.SignTransactionFlow.call(CollectSignaturesFlow.kt:201) ~[corda-core-0.13.0.jar:?]
at net.corda.flows.SignTransactionFlow.call(CollectSignaturesFlow.kt:177) ~[corda-core-0.13.0.jar:?]
[WARN ] 15:14:52.202 [Mock network] [a11087fc-381d-4547-8736-5265c334c71f].run - Terminated by unexpected exceptionnet.corda.core.flows.FlowSessionException: Counterparty flow on CN=Mock Company 3,O=R3,L=New York,C=US had an internal error and has terminated
at net.corda.node.services.statemachine.FlowStateMachineImpl.erroredEnd(FlowStateMachineImpl.kt:382) ~[corda-node
[WARN ] 15:14:52.203 [Mock network] [a11087fc-381d-4547-8736-5265c334c71f].uncaughtException - Caught exception from flowjava.lang.IllegalStateException: Progress tracker has already ended
at net.corda.core.utilities.ProgressTracker.endWithError
Actually the code is much longer but I think these are the relevant parts. Is it a known thing? How can I fix it?

When implementing SignTransactionFlow, you must override checkTransactions.

Related

Only on Windows: "Socket fail to connect address already in use" (Exposed, Koin, Flowables)

I only encounter this problem when I am on Windows and actively work on the project with IntelliJ Idea.
When I deploy it onto the Linux machine everything works fine.
I already updated exposed, but it didn't help.
The errors come in very quick succession and make using the app impossible.
Additional libraries I use: Flyway, RxJava, Koin, Exposed
Edit: To clarify, it works for a time, but at some point it always starts with the errors and then doesn't stop.
In the Application.kt
installKoin(
listOf(
postgresModule(postgresConfig),
mariaDBModule(mariaDBConfig),
)
)
TransactionManager.defaultDatabase = get("mariaDB")
Database connection:
fun mariaDBModule(databaseConfig: DatabaseConfig): Module {
val database = Database.connect(
url = databaseConfig.url,
driver = databaseConfig.driver,
user = databaseConfig.user,
password = databaseConfig.password
)
return module {
TransactionManager.defaultDatabase = database
single(name ="mariaDB") { database }
}
}
fun postgresModule(postgresConfig: PostgresConfig): Module {
val database = Database.connect(
url = postgresConfig.url,
driver = postgresConfig.driver,
user = postgresConfig.user,
password = postgresConfig.password
)
return module {
single(name = "postgres") {database}
}
}
Edit:
I also use a second external postgres Database and the Error only starts (I think, hard to say) coming when I use a function that queries the postgres Database. I added it above.
Where it is used with a Flowable:
private fun observeDevices() {
m2mClient.subscribe("devices/#")
.doOnNext { message ->
m2MRepository.persistMessage(message) //Error Here
changeState.onNext(message)
triggerOnMessage(message)
}
.ignoreElements()
.retry()
.subscribe()
}
Persist function:
fun persistMessage(message: M2MMessage) = transaction(mariaDB) {
M2MMessagesTable.replace {
it[M2MMessagesTable.broker] = message.broker
it[M2MMessagesTable.topic] = message.topic
it[M2MMessagesTable.message] = message.message
it[M2MMessagesTable.retained] = message.retained
it[M2MMessagesTable.timestamp] = message.timestamp
it[M2MMessagesTable.received] = message.received
}
}
Exception:
2022-06-28 17:23:37.044 [RxComputationThreadPool-3] WARN Exposed - Transaction attempt #2 failed: java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=3306)(type=master) : Socket fail to connect to host:localhost, port:3306. Address already in use: connect. Statement(s): REPLACE INTO m2m_message (broker, message, received, retained, `timestamp`, topic) VALUES (?, ?, ?, ?, ?, ?)
org.jetbrains.exposed.exceptions.ExposedSQLException: java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=3306)(type=master) : Socket fail to connect to host:localhost, port:3306. Address already in use: connect
at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:49)
at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:138)
at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:124)
at org.jetbrains.exposed.sql.statements.Statement.execute(Statement.kt:28)
at org.jetbrains.exposed.sql.QueriesKt.replace(Queries.kt:202)
at de.<redacted>.server.m2m.M2MRepository$persistMessage$1.invoke(M2MRepository.kt:55)
at de.<redacted>.server.m2m.M2MRepository$persistMessage$1.invoke(M2MRepository.kt:54)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:189)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.access$inTopLevelTransaction$run(ThreadLocalTransactionManager.kt:1)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$inTopLevelTransaction$1.invoke(ThreadLocalTransactionManager.kt:215)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.inTopLevelTransaction(ThreadLocalTransactionManager.kt:214)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$transaction$1.invoke(ThreadLocalTransactionManager.kt:165)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:223)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:135)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:132)
at de.<redacted>.server.m2m.M2MRepository.persistMessage(M2MRepository.kt:54)
at de.<redacted>.server.device.m2m.DeviceM2MService.observeDevices$lambda-0(DeviceM2MService.kt:48)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onNext(FlowableDoOnEach.java:86)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:68)
at io.reactivex.internal.operators.flowable.FlowableFilter$FilterSubscriber.tryOnNext(FlowableFilter.java:74)
at io.reactivex.internal.operators.flowable.FlowableFilter$FilterConditionalSubscriber.tryOnNext(FlowableFilter.java:139)
at io.reactivex.internal.operators.flowable.FlowableFilter$FilterConditionalSubscriber.onNext(FlowableFilter.java:117)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.tryEmit(FlowableFlatMap.java:282)
at io.reactivex.internal.operators.flowable.FlowableFlatMap$InnerSubscriber.onNext(FlowableFlatMap.java:668)
at io.reactivex.internal.operators.flowable.FlowableMap$MapSubscriber.onNext(FlowableMap.java:68)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onNext(FlowableDoOnEach.java:92)
at io.reactivex.internal.operators.flowable.FlowableDoOnLifecycle$SubscriptionLambdaSubscriber.onNext(FlowableDoOnLifecycle.java:79)
at com.hivemq.client.internal.rx.operators.FlowableWithSingleMap$MapSubscriber.onNext(FlowableWithSingleMap.java:130)
at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber$Default.tryOnNextActual(FlowableWithSingleCombine.java:235)
at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber.tryOnNext(FlowableWithSingleCombine.java:200)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnConditionalSubscriber.runAsync(FlowableObserveOn.java:649)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=3306)(type=master) : Socket fail to connect to host:localhost, port:3306. Address already in use: connect
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:197)
at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1394)
at org.mariadb.jdbc.internal.util.Utils.retrieveProxy(Utils.java:635)
at org.mariadb.jdbc.MariaDbConnection.newConnection(MariaDbConnection.java:150)
at org.mariadb.jdbc.Driver.connect(Driver.java:89)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:677)
at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:228)
at org.jetbrains.exposed.sql.Database$Companion$connect$10.invoke(Database.kt:206)
at org.jetbrains.exposed.sql.Database$Companion$connect$10.invoke(Database.kt:206)
at org.jetbrains.exposed.sql.Database$Companion$doConnect$3.invoke(Database.kt:127)
at org.jetbrains.exposed.sql.Database$Companion$doConnect$3.invoke(Database.kt:128)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction$connectionLazy$1.invoke(ThreadLocalTransactionManager.kt:69)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction$connectionLazy$1.invoke(ThreadLocalTransactionManager.kt:68)
at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction.getConnection(ThreadLocalTransactionManager.kt:75)
at org.jetbrains.exposed.sql.Transaction.getConnection(Transaction.kt)
at org.jetbrains.exposed.sql.statements.InsertStatement.prepared(InsertStatement.kt:157)
at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:47)
... 39 common frames omitted
Caused by: java.sql.SQLNonTransientConnectionException: Socket fail to connect to host:localhost, port:3306. Address already in use: connect
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:188)
at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.createSocket(AbstractConnectProtocol.java:257)
at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.createConnection(AbstractConnectProtocol.java:524)
at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1389)
... 55 common frames omitted
Caused by: java.net.BindException: Address already in use: connect
at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method)
at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:107)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.base/java.net.Socket.connect(Socket.java:609)
at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.createSocket(AbstractConnectProtocol.java:252)
... 57 common frames omitted
2022-06-28 17:23:37.044 [RxComputationThreadPool-3] DEBUG MqttClientV5 - subscribing to devices/#

RxJava Grouped Flowable with Conflation

I' m trying to create a Flow for a fast producer with slow consumer for FX (foreign exchange) prices. The basic idea is that prices coming from the source should be sent to the consumer as fast as possible.
The following is important for the working of this flow:
While the consumer is busy submitting prices, new prices must be received from the price source (in other words we don't want to slow down the producer at any stage).
When the consumer is finished processing it's current rate, it is then available to process another rate from the source.
The consumer is only ever interested in the latest published price for a ccy pair - in other words, we want prices to be conflated if the consumer does not keep up.
The consumer can (and should) process submissions in parallel, as long as these submissions are of a different ccy pair. For example, EUR/USD can be submitted in parallel with GBP/USD, but while EUR/USD is busy, other EUR/USD rates must be conflated.
Here is my current attempt:
public class RxTest {
private static final Logger LOG = LoggerFactory.getLogger(RxTest.class);
CcyPair EUR_USD = new CcyPair("EUR/USD");
CcyPair GBP_USD = new CcyPair("GBP/USD");
CcyPair USD_JPY = new CcyPair("USD/JPY");
List<CcyPair> ALL_CCY_PAIRS = Empty.<CcyPair>vector()
.plus(EUR_USD)
.plus(GBP_USD)
.plus(USD_JPY);
#Test
void testMyFlow() throws Exception {
AtomicInteger rateGenerater = new AtomicInteger(0);
Flowable.<Rate>generate(emitter -> {
MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(100));
final CcyPair ccyPair = ALL_CCY_PAIRS.get(ThreadLocalRandom.current().nextInt(3));
final String rate = String.valueOf(rateGenerater.incrementAndGet());
emitter.onNext(new Rate(ccyPair, rate));
})
.subscribeOn(Schedulers.newThread())
.doOnNext(rate -> LOG.info("Process: {}", rate))
.groupBy(Rate::ccyPair)
.map(Flowable::publish)
.doOnNext(ConnectableFlowable::connect)
.flatMap(grp -> grp.map(rate -> rate))
.onBackpressureLatest()
.observeOn(Schedulers.io())
.subscribe(onNext -> {
LOG.info("Long running process: {}", onNext);
MILLISECONDS.sleep(500);
LOG.info("Long running process complete: {}", onNext);
});
MILLISECONDS.sleep(5000);
}
record CcyPair(String name) {
public String toString() {
return name;
}
}
record Rate(CcyPair ccyPair, String rate) {
public String toString() {
return ccyPair + "->" + rate;
}
}
}
Which gives me this output:
09:27:05,743 INFO [RxTest] [RxNewThreadScheduler-1] - Process: EUR/USD->1
09:27:05,764 INFO [RxTest] [RxCachedThreadScheduler-1] - Long running process: EUR/USD->1
09:27:05,805 INFO [RxTest] [RxNewThreadScheduler-1] - Process: USD/JPY->2
...
09:27:06,127 INFO [RxTest] [RxNewThreadScheduler-1] - Process: GBP/USD->9
09:27:06,165 INFO [RxTest] [RxNewThreadScheduler-1] - Process: EUR/USD->10
09:27:06,214 INFO [RxTest] [RxNewThreadScheduler-1] - Process: USD/JPY->11
09:27:06,265 INFO [RxTest] [RxCachedThreadScheduler-1] - Long running process complete: EUR/USD->1
09:27:06,265 INFO [RxTest] [RxCachedThreadScheduler-1] - Long running process: USD/JPY->2
09:27:06,302 INFO [RxTest] [RxNewThreadScheduler-1] - Process: USD/JPY->12
09:27:06,315 INFO [RxTest] [RxNewThreadScheduler-1] - Process: EUR/USD->13
...
09:27:06,672 INFO [RxTest] [RxNewThreadScheduler-1] - Process: EUR/USD->23
09:27:06,695 INFO [RxTest] [RxNewThreadScheduler-1] - Process: GBP/USD->24
09:27:06,758 INFO [RxTest] [RxNewThreadScheduler-1] - Process: EUR/USD->25
09:27:06,773 INFO [RxTest] [RxCachedThreadScheduler-1] - Long running process complete: USD/JPY->2
09:27:06,773 INFO [RxTest] [RxCachedThreadScheduler-1] - Long running process: EUR/USD->3
There are a number problems here:
The consumer is not getting the "latest" rate per ccy pair, but the next one. This is obviously because there is an internal buffer and the backpressure has not kicked in yet. I don't want to wait for this backpressure to kick in, I simply want whatever is the latest emission to go to the consumer next.
The consumer is not processing in parallel - while EUR/USD is processing, for example, the other ccy pairs are being buffered.
Some notes/thoughts:
I'm very new at JavaRx and am struggling to find common patterns/idioms in how to tackle these kinds of problems, so please be patient :)
I'm not at all sure that backpressure is the right way to achieve this at all. RxJava has many interesting operators like window(), cache(), takeLast() etc, but none of them seem to work exactly like I want. I would have liked to have an operator like "conflate" or such - I'm sure there is something that can achieve this, just not sure how.
I struggle to understand how a slow consumer can tell the flow that "I'm busy, just conflate everything until I'm done". Can this only be done via scheduling on threads? That seems worrisome because what if the consumer is asynchronous - how will it tell the producer to hold on while it's busy?
You can use groupBy to create subflows for each currency conversions, but then you need to put those subflows into their own threads. RxJava has many buffers so skipping items while some part of the code is busy may be difficult.
source
.groupBy(Rate::ccyPair)
.flatMap(group -> {
return group
.onBackpressureLatest()
.delay(0, TimeUnit.MILLISECONDS)
.doOnNext(rate -> {
LOG.info("Long running process: {}", onNext);
MILLISECONDS.sleep(500);
LOG.info("Long running process complete: {}", onNext);
})
;
}, false, 128, 1)
.subscribe();

Retry is executed only once in Spring Cloud Stream Reactive

When I try again in Spring Cloud Stream Reactive, a situation that I don't understand arises, so I ask a question.
In case of sending String type data per second, after processing in s-c-stream Function, I intentionally caused RuntimeException according to conditions.
#Bean
fun test(): Function<Flux<String>, Flux<String>?> = Function{ input ->
input.map { sellerId ->
if(sellerId == "I-ZICGO")
throw RuntimeException("intentional")
else
log.info("do normal: {}", sellerId)
sellerId
}.retryWhen(Retry.from { companion ->
companion.map { rs ->
if (rs.totalRetries() < 3) { // retrying 3 times
log.info("retry!!!: {}", rs.totalRetries())
rs.totalRetries()
}
else
throw Exceptions.propagate(rs.failure())
}
})
}
However, the result of running the above logic is:
2021-02-25 16:14:29.319 INFO 36211 --- [container-0-C-1] o.s.c.s.m.DirectWithAttributesChannel : Channel 'consumer.processingSellerItem-in-0' has 0 subscriber(s).
2021-02-25 16:14:29.322 INFO 36211 --- [container-0-C-1] k.c.m.c.service.impl.ItemServiceImpl : retry!!!: 0
2021-02-25 16:14:29.322 INFO 36211 --- [container-0-C-1] o.s.c.s.m.DirectWithAttributesChannel : Channel 'consumer.processingSellerItem-in-0' has 1 subscriber(s).
Retry is processed only once.
Should I change from reactive to imperative to fix this?
In short, yes. The retry settings are meaningless for reactive functions. You can see a more details explanation on the similar SO question here

BleGattCharacteristicException: GATT exception status 129 when trying to Indicate->write->notify to BLE glucometer

I am trying to read all the records stored in a glucometer using rxAndroidBle library. According to several resources that I have found the process consist of three main steps after pairing/bonding and connecting to the device:
Setup indications in the Record Access Control Point characteristic (RACP)
Setup notifications on the Glucose measurement characteristic
Write on the RACP characteristic two bytes 0x01, 0x01.
Then the notifications should flow if there are any records.
Now, this flow has worked fine some times in an LG G5 with android 7.0, but on other phones that I have access to it just won't work. It will throw a gruesome GATT_INTERNAL_ERROR (status 129), which is kind of ambiguous. I found this article which describes kind of what I may be facing.
My concern is that this works but it may be firmware related, which is weird because I've seen it work flawlessly on other application that connects to the glucometer without issue in any device.
Here's what how my test code for this seems like right now:
fun loadRecords(rxBleDevice: RxBleDevice){
...
...
rxBleDevice.establishConnection(false)
.flatMap { rxBleConnection: RxBleConnection ->
rxBleConnection.setupIndication(racpUUID)
.flatMapSingle {
Single.just(rxBleConnection)
}
}
.flatMap { rxBleConnection ->
writeAndReadOnNotification(racpUUID,
glucoseUUID,
byteArrayOf(0x01, 0x01),
false,
rxBleConnection)
}
.subscribe(
{ it:ByteArray ->
decodeReading(it)
},Logger::logException)
}
private fun writeAndReadOnNotification(writeTo: UUID, readOn: UUID,
bytes: ByteArray,
isIndication: Boolean,
rxBleConnection:RxBleConnection)
: Observable<ByteArray> {
val notifObservable = if (isIndication)
rxBleConnection.setupIndication(readOn)
else
rxBleConnection.setupNotification(readOn)
return notifObservable.flatMap { notificationObservable ->
Observable.combineLatest(
notificationObservable,
rxBleConnection.writeCharacteristic(writeTo, bytes).toObservable(),
BiFunction { readBytes: ByteArray, writeBytes: ByteArray -> readBytes })
}
}
and here's what the log looks like for that piece of code:
18:28:58.058 D/BluetoothGatt: connect() - device: E0:7D:EA:FF:38:AB, auto: false
18:28:58.058 D/BluetoothGatt: registerApp()
18:28:58.058 D/BluetoothGatt: registerApp() - UUID=cca42db0-a88f-4b1c-acd0-f7fbe7be536d
18:28:58.065 D/BluetoothGatt: onClientRegistered() - status=0 clientIf=7
18:28:58.518 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=7 device=E0:7D:EA:FF:38:AB
18:28:58.527 D/BluetoothGatt: discoverServices() - device: E0:7D:EA:FF:38:AB
18:28:58.532 D/BluetoothGatt: onSearchComplete() = Device=E0:7D:EA:FF:38:AB Status=0
18:28:58.873 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a52-0000-1000-8000-00805f9b34fb enable: true
18:28:58.965 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a18-0000-1000-8000-00805f9b34fb enable: true
18:28:59.057 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a18-0000-1000-8000-00805f9b34fb enable: false
18:28:59.061 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a52-0000-1000-8000-00805f9b34fb enable: false
18:28:59.066 E/None: com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException: GATT exception from MAC='XX:XX:XX:XX:XX:XX', status 129 (GATT_INTERNAL_ERROR), type BleGattOperation{description='CHARACTERISTIC_WRITE'}. (Look up status 0x81 here https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback.propagateErrorIfOccurred(RxBleGattCallback.java:243)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback.access$800(RxBleGattCallback.java:35)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback$2.onCharacteristicWrite(RxBleGattCallback.java:125)
at android.bluetooth.BluetoothGatt$1$7.run(BluetoothGatt.java:438)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:770)
at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39)
at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:433)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:137)
at android.os.Binder.execTransact(Binder.java:731)
18:28:59.067 D/BluetoothManager: getConnectionState()
18:28:59.067 D/BluetoothManager: getConnectedDevices
18:28:59.074 D/BluetoothGatt: cancelOpen() - device: E0:7D:EA:FF:38:AB
18:28:59.080 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=7 device=E0:7D:EA:FF:38:AB
18:28:59.083 D/BluetoothGatt: close()
18:28:59.084 D/BluetoothGatt: unregisterApp() - mClientIf=7
18:28:59.507 V/FA: Inactivity, disconnecting from the service
Did I missed something in my code? Why does it work on some phones?
I managed to solve this myself. Looking into the logs I posted earlier I saw that both the indications and the notifications were being disabled right after setting them up, so I made a deep exploration into the library to see, why it was doing so. I turns out that you should not set indications before setting up notifications in whatever characteristic (even though they are two different characteristics). So the main steps to read this should be in this order:
Set up Glucose characteristic notifications and keep observing.
Set up indications on the RACP.
Write 0x01, 0x01 into the RACP.
Profit
Also I found this note in the library code:
/*
*NOTE: due to stateful nature of characteristics if one will setupIndication() before setupNotification()
* the notification will not be set up and will emit an BleCharacteristicNotificationOfOtherTypeAlreadySetException
*/
which led me to move the notification part before the indication part.
Here is what it looks like right now:
fun loadRecords(rxBleDevice: RxBleDevice){
...
//Do stuff
...
rxBleDevice.establishConnection(false)
.flatMap { rxBleConnection: RxBleConnection ->
rxBleConnection.setupNotification(glucoseUUID,
NotificationSetupMode.QUICK_SETUP)
.flatMapSingle {
Single.just(Pair(it, rxBleConnection))
}
}
.flatMap { (observable, rxBleConnection) ->
writeAndReadOnNotification(racpUUID,
racpUUID,
byteArrayOf(0x01, 0x01),
true,
rxBleConnection).subscribe()
observable
}
.subscribe(
{
decodeReading(it)
},Logger::logException)
}
I know it looks ugly and it needs polishing.

Corda: Party rejected session request as Requestor has not been registered

I've a Corda application that using M14 to build and run corda to run a TwoPartyProtocol where either parties can exchange data to reach a data validity consensus. I've followed Corda flow cookbook to build a flow.
Also, after reading the docs from several different corda milestones I've understood that M14 no longer needs flowSessions as mentioned in the release notes which also eliminates need to register services.
My TwoPartyFlow with inner FlowLogics:
class TwoPartyFlow{
#InitiatingFlow
#StartableByRPC
open class Requestor(val price: Long,
val otherParty: Party) : FlowLogic<SignedTransaction>(){
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
send(otherParty, price)
/*Some code to generate SignedTransaction*/
}
}
#InitiatedBy(Requestor::class)
open class Responder(val requestingParty : Party) : FlowLogic<SignedTransaction>(){
#Suspendable
override fun call(): SignedTransaction {
val request = receive<Long>(requestor).unwrap { price -> price }
println(request)
/*Some code to generate SignedTransaction*/
}
}
}
But, running the above using startTrackedFlow from Api causes the above error:
Party CN=Other,O=Other,L=NY,C=US rejected session request: com.testapp.flow.TwoPartyFlow$Requestor has not been registered
I had hard time finding the reason from corda docs or logs since Two Party flow implementations have changed among several Milestones of corda. Can someone help me understand the problem here.
My API Call:
#GET
#Path("start-flow")
fun requestOffering(#QueryParam(value = "price") price: String) : Response{
val price : Long = 10L
/*Code to get otherParty details*/
val otherPartyHostAndPort = HostAndPort.fromString("localhost:10031")
val client = CordaRPCClient(otherPartyHostAndPort)
val services : CordaRPCOps = client.start("user1","test").proxy
val otherParty: Party = services.nodeIdentity().legalIdentity
val (status, message) = try {
val flowHandle = services.startTrackedFlow(::Requestor, price, otherParty)
val result = flowHandle.use { it.returnValue.getOrThrow() }
// Return the response.
Response.Status.CREATED to "Transaction id ${result.id} committed to ledger.\n"
} catch (e: Exception) {
Response.Status.BAD_REQUEST to e.message
}
return Response.status(status).entity(message).build()
}
My Gradle deployNodes task:
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
directory "./build/nodes"
networkMap "CN=Controller,O=R3,OU=corda,L=London,C=UK"
node {
name "CN=Controller,O=R3,OU=corda,L=London,C=UK"
advertisedServices = ["corda.notary.validating"]
p2pPort 10021
rpcPort 10022
cordapps = []
}
node {
name "CN=Subject,O=Subject,L=NY,C=US"
advertisedServices = []
p2pPort 10027
rpcPort 10028
webPort 10029
cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
}
node {
name "CN=Other,O=Other,L=NY,C=US"
advertisedServices = []
p2pPort 10030
rpcPort 10031
webPort 10032
cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
}
There appears to be a couple of problems with the code you posted:
The annotation should be #StartableByRPC, not #StartableNByRPC
The price passed to startTrackedFlow should be a long, not an int
However, even after fixing these issues, I couldn't replicate your error. Can you apply these fixes, do a clean re-deploy of your nodes (gradlew clean deployNodes), and see whether the error changes?
You shouldn't be connecting to the other node via RPC. RPC is how a node's owner speaks to their node. In the real world, you wouldn't have the other node's RPC credentials, and couldn't log into the node in this way.
Instead, you should use your own node's RPC client to retrieve the counterparty's identity:
val otherParty = services.partyFromX500Name("CN=Other,O=Other,L=NY,C=US")!!
See an M14 example here: https://github.com/corda/cordapp-example/blob/release-M14/kotlin-source/src/main/kotlin/com/example/api/ExampleApi.kt.