Webflux a BlockHound

BlockHound je java agent (software nahraný JVM před zavoláním main metody), který způsobí to, že pokud v rámci neblokujícího vlákna (vlákno, které nemá volat blokující volání) zavoláte metodu, která blokuje vlákno, vyhodí se chyba.

Závislost

implementation("io.projectreactor.tools:blockhound:1.0.4.RELEASE")

Pokud si chceme BlockHound přizpůsobit, vytvoříme třídu, která implementuje BlockHoundIntegration

Číst dálWebflux a BlockHound

Posílání chyb do Sentry

Sentry je primárně zaměřeno na chyby. Je to místo, kam budou všechny pády systému reportovány. Na sentry si musíte vytvořit účet a nastavit projekt tak, aby chyby posílal do sentry a mít je na jednom místě.

build.gradle

implementation("io.sentry:sentry-spring-boot-starter:4.3.0")

application.yml
Tato data dostanete při registraci do sentry.

sentry:
    dsn: https://xxx@yyy.ingest.sentry.io/zzz

Kód, který pošle chybu do sentry.

try {
    throw Exception("This is a v1 test.")
} catch (e: Exception) {
    Sentry.captureException(e)
}

Je možné přidat tento kód do GlobalErrorHandleru.

@ControllerAdvice
@RestController
class GlobalErrorHandler {
    private val logger: Logger = LoggerFactory.getLogger(javaClass)

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler
    fun handle(e: Exception) {
        logger.error("Error", e)
        Sentry.captureException(e)
    }
}

Zobrazení datového typu v Idea IDE v Kotlinu

Kotlin na rozdíl od Javy nenutí vývojáře aby psal typ proměnné, protože tuto informaci si je schopen sám odvodit. Nevýhodou může být, že v kódu není u proměnné vidět její typ. To lze ale změnit v nastavení vývojového prostředí.

File -> Settings -> Editor -> Inlay Hints Zde je možno specifikovat pro jaký jazyk se mají hinty zobrazovat.

File -> Settings -> Editor -> Inlay Hints -> Kotlin -> Parameter hints -> Types Kde se určuje jaké typy hintů chcete vidět.

Zdroj: stackoverflow.com/questions/54851861/how-do-i-activate-type-annotations-hints-in-kotlin-like-depicted

Kotlin JavaFX Hello World

Build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.4.10"
    id("org.openjfx.javafxplugin") version "0.0.9"
}

group = "cz.vitfo.redgui"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

tasks.withType() {
    kotlinOptions.jvmTarget = "11"
}

javafx {
    version = "15.0.1"
    modules = listOf("javafx.controls")
}

Třída dědící z javafx.application.Application

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

class FXApplication : Application() {
    override fun start(stage: Stage) {
        val javaVersion = System.getProperty("java.version")
        val javafxVersion = System.getProperty("javafx.version")
        val l = Label("Hello, JavaFX $javafxVersion, running on Java $javaVersion.")
        val scene = Scene(StackPane(l), 640.0, 480.0)
        stage.scene = scene
        stage.show()
    }

    fun main(args: Array) {
        launch()
    }
}

fun main(args: Array) {
    FXApplication().main(args)
}

Výsledná aplikace

Zdroj: github.com/openjfx/samples/blob/master/HelloFX/Gradle/hellofx/src/main/java/HelloFX.java

Kotlin Swing Hello World

import java.awt.EventQueue
import javax.swing.JFrame

class Application : JFrame() {

    init {
        title = "Hello world"
        defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        setSize(400, 300)
        setLocationRelativeTo(null)
    }
}

fun main() {
    EventQueue.invokeLater {
        val window = Application()
        window.isVisible = true
    }
}

Přehled jednotlivých komponent Swingu: web.mit.edu/6.005/www/sp14/psets/ps4/java-6-tutorial/components.html

Výpis chyby při použítí vykřičníků v Kotlinu

Kotlin kompilátor kontroluje, zda hodnota výrazu může být null a pokud tomu tak je, kód nezkompiluje dokud se v daném místě neprovede kontrola na not null. Programátor si může vynutit, že chce danou hodnotu použít i bez této kontroly pokud si je jist, že hodnota není null pomocí dvou vykřičníků. Pokud hodnota je null, dojde k vyhození kotlin.KotlinNullPointerException.

Nedoporučuje se používat více !! (one celkově se !! mají používat pouze vyjímečně ve zdůvodněných případech) na jednom řádku, protože výpis v logu vám neřekne, která hodnota byla null.

Příklad

class Aa (var b: Bb?)

class Bb (var text: String?)

Výsledky volání

println(a!!.b!!.text!!.toUpperCase())

Exception in thread "main" kotlin.KotlinNullPointerException
	at cz.vitfo.NullKt.main(Null.kt:8)
	at cz.vitfo.NullKt.main(Null.kt)
println(aWithB!!.b!!.text!!.toUpperCase())

Exception in thread "main" kotlin.KotlinNullPointerException
	at cz.vitfo.NullKt.main(Null.kt:8)
	at cz.vitfo.NullKt.main(Null.kt)

Je vidět, že v logu je pouze řádek, kde došlo k vyhození výjimky, nikoliv přesně která část výrazu.

Parsování času z requestu pomocí Jackson v Kotlinu

Pokud vytváříte restovou službu a používáte Spring Boot s Kolinem, je pravděpodobné, že časem budete potřebovat parsovat čas z requestů. Nejdříve je třeba přidat závislost:

implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.7")

Ukázky anotací a requestů

Číst dálParsování času z requestu pomocí Jackson v Kotlinu

Funkce groupBy v Kotlinu

Kotliní standardní klihovna nabízí funkce pro groupování elementů kolekcí. Základní groupBy() funkce přijímá lambdu a vrací mapu. V této mapě je každý klíč (key) výsledkem lambda funkce a hodnota (value) je seznam elementů z kolekce podle daného klíče.

    val names = listOf("Petr", "Jan", "Marie", "Matouš", "Petra", "Soňa", "Patricie")

    println(names.groupBy { it.first() })
    println(names.groupBy(keySelector = { it.first() }))
    // {P=[Petr, Petra, Patricie], J=[Jan], M=[Marie, Matouš], S=[Soňa]}

    println(names.groupBy { it.length })
    println(names.groupBy(keySelector = { it.length }))
    // {4=[Petr, Soňa], 3=[Jan], 5=[Marie, Petra], 6=[Matouš], 8=[Patricie]}

    println(names.groupBy(keySelector = { it.length }, valueTransform = { it.toUpperCase() }))
    // {4=[PETR, SOŇA], 3=[JAN], 5=[MARIE, PETRA], 6=[MATOUŠ], 8=[PATRICIE]}

    println(names.groupBy(keySelector = { it.substring(0, 2) }, valueTransform = { if (it.length == 4) it.toUpperCase() else it.toLowerCase() }))
    // {Pe=[PETR, petra], Ja=[jan], Ma=[marie, matouš], So=[SOŇA], Pa=[patricie]}

Číst dálFunkce groupBy v Kotlinu

Redis health check

Většinou není problém s připojením k redis databázi. Ani když používáte docker image a contejner s redisem zastavíte. Po jeho spuštění se aplikace zase korektně připojí do redis databáze. Měl jsem ale problém v případě kubernetu, kdy po znovu nasazení a spuštění docker kontejneru s redisem se aplikace nemohla sama připojit. Řešením bylo udělat health actuator a v případě, že je down, aplikaci restartovat. Jako klienta používám redisson.

import org.redisson.api.RedissonClient
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.health.Health
import org.springframework.boot.actuate.health.ReactiveHealthIndicator
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono

@Component
class RedisHealthChecker : ReactiveHealthIndicator {
    private val log = LoggerFactory.getLogger(RedisHealthChecker::class.java)

    @Autowired
    lateinit var redissonClient: RedissonClient

    override fun health(): Mono {
        return try {
            val bucket = redissonClient.getBucket("healthBucket")
            bucket.set("Ok")
            Mono.just(Health.Builder().up().build())
        } catch (e: Exception) {
            log.error("Error in redis connection", e)
            Mono.just(Health.Builder().down().build())
        }
    }
}

Kotlin let není if

Konstrukce ?.let {} ?: run {} se sice používá jakoby místo if - else, ale není s ní zaměnitelná. Pokud máte nullable objekt, použitím ?.let {} zpracujete případ, kdy je not null a ?: run {} v případě, že je null. Dalo by se říct, klasické if – else. Níže je ukázka. Vytvoříme outer a inner mapu, inicializujeme jí a pak máme dva bloky. První s if – else a druhý s ?.let a ?: run. V blocích jde o to, že nejdříve se zkusí vytáhnout hodnota pro klíč z outer mapy a pokud je hodnota nalezena, totéž proběhne pro inner mapu.

Číst dálKotlin let není if