Ktorm Repository without duplications - kotlin

I am creating the repository layer using Ktorm, so far I only have insert and update functions.
override fun create(
entity: ObjectEntity
): Either<DatabaseError, Entity> = try {
val id = database.insertAndGenerateKey(TableName) {
set(TableName.createdTimestamp, entity.createdTimestamp)
set(TableName.updatedTimestamp, entity.updatedTimestamp)
set(TableName.status, entity.status.id)
....
} as Int
entity.id = id
Either.Right(entity)
} catch (e: Exception) {
logger.error(e) {
...
}
Either.Left(DatabaseError(e.message))
}
override fun update(
entity: ObjectEntity
): Either<DatabaseError, Entity> = try {
val effectedRowCount = database.update(TableName) {
set(TableName.updatedTimestamp, entity.updatedTimestamp)
set(TableName.status, entity.status.id)
...
where { it.csmUUID eq entity.csmUUID }
}
if (effectedRowCount < 0) {
Either.Left(MissingEntityError(Entity::class.java, "csmUUID"))
} else {
Either.Right(entity)
}
} catch (e: Exception) {
logger.error(e) {
....
}
Either.Left(DatabaseError(e.message))
}
This code block works like a charm with no issues. But the thing is, there are a lot of fields (more than 20) that are being set in both functions. This causes a code duplication and because of this I can't pass the code quality gate and merge it. Also, I don't like having this much duplicate code. Any suggestions to prevent code duplication in a scenario like this?

Related

kotlin object lock

I want to use synchronized(object lock) at Kotlin, but idk how to use synchronized at Kotlin. I already search for the usage of synchronizing at Kotlin but ReentrantLock can't lock objects that I guess. please help me I am stuck with this 2 days ago.
override fun run() {
var active = false
while (true) {
while (queue.isEmpty()) {
if (!running) {
return
}
synchronized(this) {
try {
active = false
wait() //<< here's error
} catch (e: InterruptedException) {
LogUtils.getLogger()
.log(Level.SEVERE, "There was a exception with SQL")
LogUtils.logThrowable(e)
}
}
}
if (!active) {
con.refresh()
active = true
}
val rec = queue.poll()
con.updateSQL(rec.getType(), *rec.getArgs())
}
}
/**
* Adds new record to the queue, where it will be saved to the database.
*
* #param rec Record to save
*/
fun add(rec: Record) {
synchronized(this) {
queue.add(rec)
notifyAll() //<< here's too
}
}
/**
* Ends this saver's job, letting it save all remaining data.
*/
fun end() {
synchronized(this) {
running = false
notifyAll() //<< and here
}
}```
Well, the solution mentioned in Correctly implementing wait and notify in Kotlin will work here. Replacing wait() //<< here's error with (this as Object).work() and notifyAll() //<< and here with (this as Object).notifyAll() will lead to behavior that is identical to Java's one.

How can I override logRequest/logResponse to log custom message in Ktor client logging?

Currently, the ktor client logging implementation is as below, and it works as intended but not what I wanted to have.
public class Logging(
public val logger: Logger,
public var level: LogLevel,
public var filters: List<(HttpRequestBuilder) -> Boolean> = emptyList()
)
....
private suspend fun logRequest(request: HttpRequestBuilder): OutgoingContent? {
if (level.info) {
logger.log("REQUEST: ${Url(request.url)}")
logger.log("METHOD: ${request.method}")
}
val content = request.body as OutgoingContent
if (level.headers) {
logger.log("COMMON HEADERS")
logHeaders(request.headers.entries())
logger.log("CONTENT HEADERS")
logHeaders(content.headers.entries())
}
return if (level.body) {
logRequestBody(content)
} else null
}
Above creates a nightmare while looking at the logs because it's logging in each line. Since I'm a beginner in Kotlin and Ktor, I'd love to know the way to change the behaviour of this. Since in Kotlin, all classes are final unless opened specifically, I don't know how to approach on modifying the logRequest function behaviour. What I ideally wanted to achieve is something like below for an example.
....
private suspend fun logRequest(request: HttpRequestBuilder): OutgoingContent? {
...
if (level.body) {
val content = request.body as OutgoingContent
return logger.log(value("url", Url(request.url)),
value("method", request.method),
value("body", content))
}
Any help would be appreciative
No way to actually override a private method in a non-open class, but if you just want your logging to work differently, you're better off with a custom interceptor of the same stage in the pipeline:
val client = HttpClient(CIO) {
install("RequestLogging") {
sendPipeline.intercept(HttpSendPipeline.Monitoring) {
logger.info(
"Request: {} {} {} {}",
context.method,
Url(context.url),
context.headers.entries(),
context.body
)
}
}
}
runBlocking {
client.get<String>("https://google.com")
}
This will produce the logging you want. Of course, to properly log POST you will need to do some extra work.
Maybe this will be useful for someone:
HttpClient() {
install("RequestLogging") {
responsePipeline.intercept(HttpResponsePipeline.After) {
val request = context.request
val response = context.response
kermit.d(tag = "Network") {
"${request.method} ${request.url} ${response.status}"
}
GlobalScope.launch(Dispatchers.Unconfined) {
val responseBody =
response.content.tryReadText(response.contentType()?.charset() ?: Charsets.UTF_8)
?: "[response body omitted]"
kermit.d(tag = "Network") {
"${request.method} ${request.url} ${response.status}\nBODY START" +
"\n$responseBody" +
"\nBODY END"
}
}
}
}
}
You also need to add a method from the Ktor Logger.kt class to your calss with HttpClient:
internal suspend inline fun ByteReadChannel.tryReadText(charset: Charset): String? = try {
readRemaining().readText(charset = charset)
} catch (cause: Throwable) {
null
}

How query firebase database and retrieve specific data

If I query database child Readings, I have all items, but if I add a condition, I don't have results.
Someone can help me make the query in order to get all the elements that have the value "2020-9-23" (this will be chosen by the user)?
Thank you
mDatabase!!.child("Readings").child("dia").equalTo("2020-9-23").orderByValue().addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data: ArrayList<EnergyReading> = ArrayList()
if (dataSnapshot != null) {
for (snapshot: DataSnapshot in dataSnapshot.children) {
try {
data.add(EnergyReading(snapshot.child("reading")))
} catch (e: Exception) {
e.printStackTrace()
}
}
mReadingList = data
Log.i("ReadingModel","data updated there are " + mReadingList!!.size + " Reading in the list")
} else {
throw Exception("data snapshot is null line 31")
}
}
Since the dia property is under reading, you need to order on reading/dia:
mDatabase!!.child("Readings").orderByChild("reading/dia").equalTo("2020-9-23").addListenerForSingleValueEvent(object : ValueEventListener {

Create a callback function with another callback inside

My apologies for the bad title, I'm fairly new to callbacks and I'm not sure how to explain what I'm trying to achieve.
I have a class called MyClass that has a function connectToService inside of it.
The function connectToService does some calculations and then calls a function with a callback, like this:
fun connectToService() {
//Whatever calculations
val a = 7
var b = 3
var c = a + b
val token = MyToken()
token.actionCallback = object: SuperSecretObject {
override fun onSuccess(asyncActionToken: MyToken) {
c++
}
override fun onFailure(asyncActionToken: MyToken) {
c--
}
}
}
I want to create another class, YourClass which creates an object of MyClass and then calls the connectToService function. When the connectToService function finishes either the onSuccess or onFailurefunctions, I want to do something depending on which one was triggered (something different each time, thats why I can't put it inside the onSuccess or onFailure blocks of code).
Something like this:
//Inside `yourClass`
private fun myFunc() {
val yourClassObj = YourClass()
youClassObj.connectToService {
if(onSuccess)
reinventTheWheel()
else
squareIt()
}
youClassObj.connectToService {
combAWatermelon()
}
youClassObj.connectToService {
sharpenMyHammer()
}
}
Is this possible? If so, how can I achieve it? If it's not, what would be the closest solution to this requirement?
EDIT:
More detailed information has been requested, so while I can't provide exact details, I'll do my best to explain what's going on.
I'm basically working on a library to simplify petitions. For example, MQTT petitions. This is something tht resembles what I want to achieve:
/**
* Subscribes to a list of topics and handles the results
*/
fun subscribe(client: MqttAndroidClient, list: MutableList<String>, onMsg: ((String, MqttMessage)->Unit)?=null, conLost: ((Throwable)->Unit)?=null, delComp: ((IMqttDeliveryToken)->Unit)?=null) {
if (client.isConnected) { //Assert connection
for(x in list.iterator()) { //Subscribe to events
client.subscribe(x, 0)
}
client.setCallback(object : MqttCallback {
override fun connectionLost(cause: Throwable) { //Lost connection
Log.i("TAG", "Connection lost")
conLost?.let { it(cause) }
}
#Throws(java.lang.Exception::class)
override fun messageArrived(topic: String, message: MqttMessage) { //Arrived message
Log.i("TAG", "Message arrived: topic => $topic, message => $message")
onMsg?.let { it(topic, message) }
}
override fun deliveryComplete(token: IMqttDeliveryToken) { //Delivery complete
Log.i("TAG", "Delivery complete")
delComp?.let { it(token) }
}
})
}
}
The messageArrived function must have a behaviour that can be customized depending on the app it's being used on.
For example, on one app I want the onMsg() function to be like this:
when(topic) {
"firstTopic" -> {
localVariable++
}
"secondTopic" -> {
localMethod()
}
"thirdTopic" -> {
localClass.variable.method()
}
}
If I'm using it on an Android device, I'd like to be able to update the interface, doing Android API calls, etc.
I'm not sure I got your question correctly. I think what you are looking for is passing lambdas.
fun connectToService(onSucc: ()->Unit, onFail: ()->Unit) {
//Whatever calculations
MyToken().actionCallback = object: SuperSecretObject {
override fun onSuccess(asyncActionToken: MyToken) {
onSucc()
}
override fun onFailure(asyncActionToken: MyToken) {
onFail()
}
}
}
Then you can call the function like this:
connectToService({ /* Something */ }, { /* Something else */ })

channel.asFlux() seems to cause deadlock in high concurrency scenario

Ok I've edited the whole question and produced two version of code which seems more interesting to show up the issue..
there are 2 functions here, one cause deadlock the other not..
the problem seems to be inside channel.asFlux() conversion but I don't understand what is the best practice to avoid such locks..
thank you,
Francesco
#Test
#Disabled("investigating..")
fun tt() = runBlocking<Unit>{
(0..3000).map { async {
//noDeadLock()
yesDeadLock()
}}.forEach { it.await()}
}
val thPool = newSingleThreadContext("single")
suspend fun yesDeadLock(){
withContext(Dispatchers.IO){ DEADLOCK
var channel = Channel<Int>(Channel.RENDEZVOUS)
val producer =
launch(newSingleThreadContext("nconte")){
while (isActive && !channel.isClosedForSend && !channel.isClosedForReceive ){
try{
channel.send(1)
}catch (t:Throwable){
}
}
}
withContext(Dispatchers.Default){
// withContext(thPool){ // <<-- with any other context, even a single thread, this long cause deadlocks
channel.asFlux()
.publishOn(Schedulers.elastic(),1)
.doFinally { producer.cancel() }
.limitRate(1)
.take(30)
.blockLast()
}
1
}
}
suspend fun noDeadLock(){
withContext(Dispatchers.Default){
var channel = Channel<Int>(Channel.RENDEZVOUS)
val producer =
launch(newSingleThreadContext("nconte")){
while (isActive && !channel.isClosedForSend && !channel.isClosedForReceive ){
try{
channel.send(1)
}catch (t:Throwable){
}
}
}
withContext(Dispatchers.IO){
channel.asFlux()
.publishOn(Schedulers.elastic(),1)
.doFinally { producer.cancel() }
.limitRate(1)
.take(30)
.blockLast()
}
1
}
}