diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c20d199..5cee1f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,9 +30,6 @@ jobs: - name: Run code quality checks (detekt) run: ./gradlew detekt - - name: Run code formatting checks (kotlinter) - run: ./gradlew lintKotlinMain lintKotlinTest - - name: Build project run: ./gradlew build -x test diff --git a/build.gradle.kts b/build.gradle.kts index 58656e0..c1d4cf3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,13 +3,12 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { // Kotlin version bumped to be compatible with the Maven Publish plugin and Gradle 8.10 - kotlin("jvm") version "1.9.25" - id("io.gitlab.arturbosch.detekt") version "1.23.7" - id("org.jmailen.kotlinter") version "3.6.0" + kotlin("jvm") version "2.2.0" + id("io.gitlab.arturbosch.detekt") version "1.23.8" id("maven-publish") id("signing") id("com.vanniktech.maven.publish") version "0.34.0" - id("net.researchgate.release") version "3.0.2" + id("net.researchgate.release") version "3.1.0" jacoco } @@ -21,21 +20,18 @@ repositories { } dependencies { - // Logging dependencies are compileOnly (provided) to avoid transitive vulnerabilities - // Updated to 1.5.20 to fix CVE-2024-12798 (JaninoEventEvaluator vulnerability fixed in 1.5.13+) - // Available at runtime for local testing via testImplementation - compileOnly("io.github.microutils:kotlin-logging-jvm:3.0.5") - compileOnly("ch.qos.logback:logback-classic:1.5.20") - testImplementation("io.github.microutils:kotlin-logging-jvm:3.0.5") - testImplementation("ch.qos.logback:logback-classic:1.5.20") - testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1") - testImplementation("io.mockk:mockk:1.13.11") - testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.11.0") + // SLF4J API only — consumers choose their own logging implementation + implementation("org.slf4j:slf4j-api:2.0.17") + compileOnly("ch.qos.logback:logback-classic:1.5.32") + testImplementation("ch.qos.logback:logback-classic:1.5.32") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0") + testImplementation("io.mockk:mockk:1.14.3") + testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.12.2") testImplementation(kotlin("test")) // Jackson for JSON serialization - api("com.fasterxml.jackson.core:jackson-databind:2.17.2") - api("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2") + api("com.fasterxml.jackson.core:jackson-databind:2.19.0") + api("com.fasterxml.jackson.module:jackson-module-kotlin:2.19.0") } java { @@ -98,32 +94,8 @@ signing { } } -kotlinter { - ignoreFailures = false - reporters = arrayOf("html") - experimentalRules = false - disabledRules = arrayOf( - "no-wildcard-imports", - "import-ordering", - "indent", - "final-newline", - "no-multi-spaces", - "no-trailing-spaces", - "string-template" - ) -} - -// Automatic formatting before checking -tasks.named("lintKotlinMain") { - dependsOn("formatKotlinMain") -} -tasks.named("lintKotlinTest") { - dependsOn("formatKotlinTest") -} - detekt { - config = - files("$projectDir/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior + config.setFrom("$projectDir/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior } tasks.withType().configureEach { @@ -179,7 +151,9 @@ tasks.check { } tasks.withType { - kotlinOptions.jvmTarget = "11" + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) + } } // Kotlin DSL diff --git a/src/main/kotlin/io/github/ngirchev/fsm/impl/AbstractFsm.kt b/src/main/kotlin/io/github/ngirchev/fsm/impl/AbstractFsm.kt index 0774f85..e0c3909 100644 --- a/src/main/kotlin/io/github/ngirchev/fsm/impl/AbstractFsm.kt +++ b/src/main/kotlin/io/github/ngirchev/fsm/impl/AbstractFsm.kt @@ -1,9 +1,9 @@ package io.github.ngirchev.fsm.impl -import mu.KLogging import io.github.ngirchev.fsm.* import io.github.ngirchev.fsm.exception.FsmException import io.github.ngirchev.fsm.exception.FsmTransitionFailedException +import org.slf4j.LoggerFactory import java.util.concurrent.CopyOnWriteArrayList /** @@ -12,10 +12,13 @@ import java.util.concurrent.CopyOnWriteArrayList abstract class AbstractFsm, TRANSITION_TABLE : AbstractTransitionTable>( context: StateContext, open val transitionTable: TRANSITION_TABLE, - autoTransitionEnabled: Boolean? = null -) : StateSupport, TransitionSupport, Notifiable { - - companion object : KLogging() + autoTransitionEnabled: Boolean? = null, +) : StateSupport, + TransitionSupport, + Notifiable { + companion object { + private val logger = LoggerFactory.getLogger(AbstractFsm::class.java) + } /** * Enable auto transitions based on transition table. @@ -23,9 +26,10 @@ abstract class AbstractFsm, TRANSI val autoTransitionEnabled: Boolean init { - val overrideAutoTransition = autoTransitionEnabled ?: run { - transitionTable.autoTransitionEnabled - } + val overrideAutoTransition = + autoTransitionEnabled ?: run { + transitionTable.autoTransitionEnabled + } this.autoTransitionEnabled = overrideAutoTransition } @@ -36,14 +40,12 @@ abstract class AbstractFsm, TRANSI ) : this( DefaultStateContext(state), transitionTable, - autoTransitionEnabled + autoTransitionEnabled, ) internal val context: StateContext = context - override fun getState(): STATE { - return this.context.state - } + override fun getState(): STATE = this.context.state private val stateChangeListeners = CopyOnWriteArrayList>() @@ -55,13 +57,17 @@ abstract class AbstractFsm, TRANSI stateChangeListeners.remove(listener) } - override fun notify(context: StateContext, oldState: STATE, newState: STATE) { - logger.info { "Changed status $oldState -> $newState" } + override fun notify( + context: StateContext, + oldState: STATE, + newState: STATE, + ) { + logger.info("Changed status {} -> {}", oldState, newState) stateChangeListeners.forEach { listener -> try { listener.onStateChanged(context, oldState, newState) } catch (e: Exception) { - logger.error(e) { "Error in state change listener" } + logger.error("Error in state change listener", e) } } } @@ -82,10 +88,12 @@ abstract class AbstractFsm, TRANSI private fun executeSingleTransition(transition: TRANSITION) { val oldState = context.state - if (transition.from != oldState) throw FsmException( - "Current state $oldState doesn't fit " + - "to change, because transition from=[${transition.from}]" - ) + if (transition.from != oldState) { + throw FsmException( + "Current state $oldState doesn't fit " + + "to change, because transition from=[${transition.from}]", + ) + } transition.to.timeout?.value?.also { Thread.sleep(it * 1000) } @@ -104,7 +112,7 @@ abstract class AbstractFsm, TRANSI val newState = transition.to.state context.currentTransition = transition - logger.info { "Try to change status $oldState -> $newState" } + logger.info("Try to change status {} -> {}", oldState, newState) transition.to.actions.forEach { it.invoke(context) } context.state = newState @@ -114,6 +122,6 @@ abstract class AbstractFsm, TRANSI private class DefaultStateContext( override var state: STATE, - override var currentTransition: Transition? = null + override var currentTransition: Transition? = null, ) : StateContext } diff --git a/src/main/kotlin/io/github/ngirchev/fsm/impl/basic/BDomainFsm.kt b/src/main/kotlin/io/github/ngirchev/fsm/impl/basic/BDomainFsm.kt index 15387ad..66f45d0 100644 --- a/src/main/kotlin/io/github/ngirchev/fsm/impl/basic/BDomainFsm.kt +++ b/src/main/kotlin/io/github/ngirchev/fsm/impl/basic/BDomainFsm.kt @@ -1,22 +1,22 @@ package io.github.ngirchev.fsm.impl.basic -import mu.KLogging import io.github.ngirchev.fsm.StateContext import io.github.ngirchev.fsm.impl.AbstractDomainFsm open class BDomainFsm, STATE>( override val transitionTable: BTransitionTable, - private val autoTransitionEnabled: Boolean? = null + private val autoTransitionEnabled: Boolean? = null, ) : AbstractDomainFsm, BTransitionTable>( - transitionTable -) { - - companion object : KLogging() - - override fun changeState(domain: DOMAIN, newState: STATE) { - val overrideAutoTransition = autoTransitionEnabled ?: run { - transitionTable.autoTransitionEnabled - } + transitionTable, + ) { + override fun changeState( + domain: DOMAIN, + newState: STATE, + ) { + val overrideAutoTransition = + autoTransitionEnabled ?: run { + transitionTable.autoTransitionEnabled + } BFsm(domain, transitionTable, overrideAutoTransition).toState(newState) } } diff --git a/src/main/kotlin/io/github/ngirchev/fsm/impl/extended/ExDomainFsm.kt b/src/main/kotlin/io/github/ngirchev/fsm/impl/extended/ExDomainFsm.kt index 9692775..21359d2 100644 --- a/src/main/kotlin/io/github/ngirchev/fsm/impl/extended/ExDomainFsm.kt +++ b/src/main/kotlin/io/github/ngirchev/fsm/impl/extended/ExDomainFsm.kt @@ -1,18 +1,15 @@ package io.github.ngirchev.fsm.impl.extended -import mu.KLogging -import io.github.ngirchev.fsm.impl.AbstractDomainFsm -import io.github.ngirchev.fsm.StateContext import io.github.ngirchev.fsm.StateChangeListener +import io.github.ngirchev.fsm.StateContext +import io.github.ngirchev.fsm.impl.AbstractDomainFsm import java.util.concurrent.CopyOnWriteArrayList open class ExDomainFsm, STATE, EVENT>( override val transitionTable: ExTransitionTable, - private val autoTransitionEnabled: Boolean? = null -) : AbstractDomainFsm, ExTransitionTable>(transitionTable), StateChangeListener { - - companion object : KLogging() - + private val autoTransitionEnabled: Boolean? = null, +) : AbstractDomainFsm, ExTransitionTable>(transitionTable), + StateChangeListener { private val stateChangeListeners = CopyOnWriteArrayList>() fun addStateChangeListener(listener: StateChangeListener) { @@ -30,24 +27,34 @@ open class ExDomainFsm, STATE, EVENT>( * The returned instance does not have any listeners attached. */ fun getFsmForDomain(domain: DOMAIN): ExFsm { - val overrideAutoTransition = autoTransitionEnabled ?: run { - transitionTable.autoTransitionEnabled - } + val overrideAutoTransition = + autoTransitionEnabled ?: run { + transitionTable.autoTransitionEnabled + } return ExFsm(domain, transitionTable, overrideAutoTransition) } /** * handle event for passed document. */ - fun handle(domain: DOMAIN, event: EVENT) { + fun handle( + domain: DOMAIN, + event: EVENT, + ) { handleWithListeners(domain) { fsm -> fsm.onEvent(event) } } - override fun changeState(domain: DOMAIN, newState: STATE) { + override fun changeState( + domain: DOMAIN, + newState: STATE, + ) { handleWithListeners(domain) { fsm -> fsm.toState(newState) } } - fun handleWithListeners(domain: DOMAIN, action: (ExFsm) -> Unit) { + fun handleWithListeners( + domain: DOMAIN, + action: (ExFsm) -> Unit, + ) { val fsm = getFsmForDomain(domain) fsm.addStateChangeListener(this) try { @@ -57,7 +64,11 @@ open class ExDomainFsm, STATE, EVENT>( } } - override fun onStateChanged(context: StateContext, oldState: STATE, newState: STATE) { + override fun onStateChanged( + context: StateContext, + oldState: STATE, + newState: STATE, + ) { stateChangeListeners.forEach { listener -> listener.onStateChanged(context, oldState, newState) } diff --git a/src/test/kotlin/io/github/ngirchev/fsm/it/BFsmIT.kt b/src/test/kotlin/io/github/ngirchev/fsm/it/BFsmIT.kt index cc6af0e..88b65ad 100644 --- a/src/test/kotlin/io/github/ngirchev/fsm/it/BFsmIT.kt +++ b/src/test/kotlin/io/github/ngirchev/fsm/it/BFsmIT.kt @@ -1,6 +1,6 @@ package io.github.ngirchev.fsm.it -import mu.KLogging +import org.slf4j.LoggerFactory import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance @@ -20,7 +20,9 @@ import kotlin.test.assertFailsWith @TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class BFsmIT { - companion object : KLogging() + companion object { + private val logger = LoggerFactory.getLogger(BFsmIT::class.java) + } private var autoSendEnabled: Boolean = true private var successfullySent: Boolean = false @@ -36,7 +38,7 @@ internal class BFsmIT { To( "SIGNED", condition = { true }, - action = { logger.info { "SIGNED SUCCESSFUL" } }), + action = { logger.info("SIGNED SUCCESSFUL") }), To("CANCELLED") ) .add( @@ -45,7 +47,7 @@ internal class BFsmIT { To( state = "AUTO_SENT", condition = { autoSendEnabled }, - action = { successfullySent = true; logger.info { "AUTO SENT ACTION" } } + action = { successfullySent = true; logger.info("AUTO SENT ACTION") } ) )) .add(from = "SIGNED", "DONE", "CANCELED") @@ -56,11 +58,11 @@ internal class BFsmIT { .autoTransitionEnabled(false) .from("NEW").to("READY_FOR_SIGN").end() .from("READY_FOR_SIGN").toMultiple() - .to("SIGNED").condition { true }.action { logger.info { "SIGNED SUCCESSFUL" } }.end() + .to("SIGNED").condition { true }.action { logger.info("SIGNED SUCCESSFUL") }.end() .to("CANCELED").end().endMultiple() .from("SIGNED").to("AUTO_SENT") .condition { autoSendEnabled } - .action { successfullySent = true; logger.info { "AUTO SENT ACTION" } }.end() + .action { successfullySent = true; logger.info("AUTO SENT ACTION") }.end() .from("SIGNED").toMultiple().to("DONE").end().to("CANCELED").end().endMultiple() .from("AUTO_SENT").toMultiple().to("DONE").end().to("CANCELED").end().endMultiple() .build() diff --git a/src/test/kotlin/io/github/ngirchev/fsm/it/ExDomainFsmIT.kt b/src/test/kotlin/io/github/ngirchev/fsm/it/ExDomainFsmIT.kt index 5d72d42..5d52e26 100644 --- a/src/test/kotlin/io/github/ngirchev/fsm/it/ExDomainFsmIT.kt +++ b/src/test/kotlin/io/github/ngirchev/fsm/it/ExDomainFsmIT.kt @@ -1,6 +1,6 @@ package io.github.ngirchev.fsm.it -import mu.KLogging +import org.slf4j.LoggerFactory import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -33,7 +33,9 @@ import kotlin.test.assertTrue @TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class ExDomainFsmIT { - companion object : KLogging() + companion object { + private val logger = LoggerFactory.getLogger(ExDomainFsmIT::class.java) + } private lateinit var document: Document @@ -301,7 +303,7 @@ internal class ExDomainFsmIT { } private fun prt(stateContext: StateContext) { - logger.info { "Transition from=[" + stateContext.state + "] at=" + System.currentTimeMillis() } + logger.info("Transition from=[{}] at={}", stateContext.state, System.currentTimeMillis()) } private fun allTrue(futures: List>): Boolean {