(рд╕реНрдкреНрд░рд┐рдВрдЧ) рд░рд╛рдЬреНрдп (рд╕реНрдкреНрд░рд┐рдВрдЧ) рд╢реЗрд▓ рдореЗрдВ: рдПрдХрд▓ рдЙрддреНрдкрд╛рджрди рдирд╣реАрдВ

рдЕрд░реЗ, рдПрдХ рдСрдЯреЛ-рдкреВрд░реНрдг рдЗрдВрдЯрд░реЗрдХреНрдЯрд┐рд╡ рд╢реЗрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛, рдорджрдж, рдФрд░ рдЕрдзрд┐рдХ? рдФрд░ рдореБрд╕реАрдмрддреЛрдВ рдХреЗ рдмрд┐рдирд╛, рдФрд░ рдЬреЗрд╡реАрдПрдо рдкрд░ рднреА?


рдкреЛрд╕реНрдЯрдореИрди рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд╛ рд╕рд┐рд░рджрд░реНрдж рдмрди рдЧрдпрд╛ред рдпрджреНрдпрдкрд┐ рд╢рдмреНрджреЛрдВ рдореЗрдВ рд╣рдо рд╕рднреА рдЯреАрдбреАрдбреА рд╣реИрдВ рдФрд░ рдХрдХрдбрд╝реА рдХреА рд▓рд╛рд▓ рдмрддреНрддреА рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рдЯреВрдЯ рдЧрдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ рдореБрдЭреЗ рдкреЛрд╕реНрдЯрдореИрди рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдореЗрдВ рдЕрдиреНрдп рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдЕрдзрд┐рдХ рдмрд╛рд░ рднреЗрдЬрдирд╛ рдкрдбрд╝рд╛ред рдХрд╛рдо рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ, рдПрдХ рдкреНрд░рдорд╛рдгреАрдХрд░рдг рдЯреЛрдХрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдерд╛ (рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдиреБрд░реЛрдз рдФрд░ рд╕рддреНрдпрд╛рдкрди рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдиреБрд░реЛрдз, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрд▓рдЧ рд╣реИрдВ), рдФрд░ рдлрд┐рд░ рдмреБрдХрдорд╛рд░реНрдХ рд╕реЗ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реЗрдВ, рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ рдФрд░ рдЕрдиреНрдп рдЕрдиреБрд░реЛрдз рд╢реБрд░реВ рдХрд░реЗрдВред рдХреНрд▓рд┐рдХ-рдХреНрд▓рд┐рдХ-рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред рдПрдХ рдЕрд▓рдЧ рдХреНрд░рдо рдореЗрдВред рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рд╣реА cURL рдХреЗ рд╕рд╛рде рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд▓рд┐рдЦреА рдереА, рдФрд░ рдореИрдВрдиреЗ IDEA рдореЗрдВ рдЕрдиреБрд░реЛрдз рдХрд┐рдП - рдпрд╣ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдирд╣реАрдВ рд╣реИред рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдкрд░реАрдХреНрд╖рдгреЛрдВ рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрджрд░реНрд╢ рдкреНрд░реЗрд░рдХ, рдпреЗ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рдереЗ рдХрд┐ рд╕реЗрд╡рд╛ рдХреА рд╕реНрдерд┐рддрд┐, рдЗрд╕рдХреЗ рд╕рдВрд╕реНрдХрд░рдг, рдЗрд╕рдХреЗ рдкреАрдЫреЗ рдореЗрдирдлреНрд░реЗрдо, рдШрд░ рдореЗрдВ рдореМрд╕рдо, рдФрд░ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдкреНрд░рддрд┐рдЧрдорди рдкрд░реАрдХреНрд╖рдг рдХреЗ рдЕрдВрддрд░реНрдЧрдд рдирд╣реАрдВ рдЖрдпрд╛ рдерд╛ред рдХреНрд▓рд┐рдХ-рдХреНрд▓рд┐рдХ рдиреЗ рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╕рдордп рдЦрд╛рдирд╛ рд╢реБрд░реВ рдХрд░ рджрд┐рдпрд╛ рдФрд░ рдорд╛рдЙрд╕ рдХрд╛ рдорд╛рдЗрд▓реЗрдЬ рдмрдврд╝рд╛ рджрд┐рдпрд╛ред


Spring Shell, shell , Spring. , . , тАФ . тАФ shell, . , тАФ Spring State Machine. , тАФ , тАФ , , Kotlin, Akka "" . State Machine , .



Spring Initializr Gradle Kotlin. .


curl 'https://start.spring.io/starter.zip?type=gradle-project&language=kotlin&bootVersion=2.2.6.RELEASE&baseDir=shell-state&groupId=me.votez.spring&artifactId=shell-state&name=shell-state&description=Demo%20project%20for%20Spring%20State%20Machine%20and%20Shell&packageName=me.votez.spring.shellstate&packaging=jar&javaVersion=1.8' --compressed --output shellstate.zip && unzip shellstate.zip

Spring Shell gradle


    implementation("org.springframework.shell:spring-shell-starter:2.0.0.RELEASE")


(shell). , ShellComponent ( ) , ShellMethod , . , ShellOption. , Bean Validation. , Enum ( , help).


тАФ . тАФ , , actuator .тАж
( runBlocking тАФ )


package me.votez.spring.shellstate

import org.springframework.shell.standard.ShellComponent
import org.springframework.shell.standard.ShellMethod
import org.springframework.shell.standard.ShellOption
import kotlinx.coroutines.*
import org.springframework.shell.standard.ShellMethodAvailability
import java.util.*
import kotlin.random.Random

@ShellComponent
class ServerCommands {
    private var token: String? = null

    @ShellMethod("Authenticate and obtain token")
    fun login(
           @ShellOption name:String, 
           @ShellOption password:String, 
           @ShellOption(defaultValue = "admin") scope: String) = runBlocking {
            token = UUID.randomUUID().toString()
            delay(2_000)
            "Connected"
        }
    }

    @ShellMethod("List projects regstered on server")
    fun list(
        @ShellOption(defaultValue = "PROJECT", help = "Possible values are PROJECT and USER") type:EntityType) = when(type) {
        EntityType.PROJECT -> listOf("Roga i Kopita", "Svetliy Put", "NIICHAVO")
        EntityType.USER -> listOf( "Ivanov", "Petrov", "Sidorov")
    }
}

enum class EntityType {
    PROJECT,
    USER
}

. IntelliJ IDEA, Spring Shell JLine, Linux, MacOS Windows, IDE тАФ .


$ bash ./gradlew build
$ java -jar build/libs/shell-state-0.0.1-SNAPSHOT.jar 

help, . clear, exit, quit, script, stacktrace.


2020-05-03 12:49:35.442  INFO 23211 --- [           main] m.v.s.s.ShellStateApplicationKt          : Started ShellStateApplicationKt in 1.036 seconds (JVM running for 0.936)
shell:>help
AVAILABLE COMMANDS

Built-In Commands
        clear: Clear the shell screen.
        exit, quit: Exit the shell.
        help: Display help about available commands.
        script: Read and execute commands from a file.
        stacktrace: Display the full stacktrace of the last error.

Server Commands
        list: List projects registered on server
        login: Authenticate and obtain token

shell:>help list

NAME
        list - List projects registered on server

SYNOPSYS
        list [[--type] entity-type]  

OPTIONS
        --type  entity-type
                Possible values are PROJECT and USER
                [Optional, default = PROJECT]

. login list L, . l TAB , o TAB, login. . , > login root 123456 , тАФ Connected. list .



list , list . :



    @ShellMethodAvailability("list")
    fun listAvailable() = if (token != null) Availability.available()  else Availability.unavailable("cannot run without auth token")

shell, , . help , .


2020-05-02 13:30:12.593  INFO 4443 --- [           main] m.v.s.s.ShellStateApplicationKt          : Started ShellStateApplicationKt in 1.525 seconds (JVM running for 1.252)
shell:>list
Command 'list' exists but is not currently available because cannot run without auth token
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
shell:>help
AVAILABLE COMMANDS

Built-In Commands
        clear: Clear the shell screen.
        exit, quit: Exit the shell.
        help: Display help about available commands.
        script: Read and execute commands from a file.
        stacktrace: Display the full stacktrace of the last error.

Server Commands
      * list: List projects regstered on server
        login: Authenticate and obtain token

Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

shell:>help list

NAME
        list - List projects registered on server

SYNOPSYS
        list [[--type] entity-type]  

OPTIONS
        --type  entity-type
                Possible values are PROJECT and USER
                [Optional, default = PROJECT]

CURRENTLY UNAVAILABLE
        This command is currently not available because cannot run without auth token.


, . , , , . command prompt, . , application.properties тАФ , Spring Framework ( ). prompt , .


PrompProvider, , JLine.


@ConfigurationProperties(prefix="shell")
class ServerCommands : PromptProvider

    lateinit var env: String //   application.properties

    override fun getPrompt(): AttributedString =
            AttributedString("${env}:>",
                    AttributedStyle.DEFAULT.foreground(
                            if (token == null) AttributedStyle.YELLOW else AttributedStyle.GREEN))

, . .


, , (soap, protobuf ) тАФ , - . , .


Spring State Machine


, . . , , , . , тАж , .


Spring State Machine.


, (), -. тАФ The Evolution of Trust. , () . xml тАФ , - .


, . , "" . Spring State Machine , , .


, . , . тАФ Spring SM , . " ", - , - , , тАФ .


, , . - тАФ , .


?


, .

, "" . . ( ) , Thread Pool, shell, SSM - тАФ .


command prompt " ", .


, . Quick Start.


, тАФ . тАФ command prompt. , , тАФ , token. null-safe lateinit.
.


override fun configure(transitions: StateMachineTransitionConfigurer<State, Event>) {
        transitions
                .withExternal()
                .source(State.INIT).target(State.CONNECTING)
                .event(Event.LOGIN)
                .and()
                .withExternal()
                .source(State.CONNECTING).target(State.READY).event(Event.CONNECTED)
                .action { context -> context.extendedState.variables["token"] = context.messageHeaders["token"] }.and()
                .withExternal()
                .source(State.CONNECTING).target(State.INIT).event(Event.FAILED).and()
                .withInternal()
                .source(State.READY).event(Event.COMMAND).and()
                .withExternal()
                .source(State.READY).target(State.CONNECTING).event(Event.LOGIN)
    }

....
enum class State {    INIT, CONNECTING, READY }
enum class Event {  LOGIN, CONNECTED, FAILED, COMMAND }

тАФ , тАФ . Spring. , , . .


. login:


    @Autowired
    lateinit var stateMachine: StateMachine<State, Event>

   //    null
    lateinit var token: String

    @ShellMethod("Authenticate and obtain token")
    fun login(
            @ShellOption name: String,
            @ShellOption password: String,
            @ShellOption(defaultValue = "admin") scope: String) =
            runBlocking {
                stateMachine.sendEvent(
                        MessageBuilder.createMessage(Event.LOGIN, MessageHeaders(mapOf("login" to name))))
                delay(1_000)
                token = UUID.randomUUID().toString()
                stateMachine.sendEvent(
                        MessageBuilder.createMessage(Event.CONNECTED, MessageHeaders(mapOf("token" to token))))
                "Connected"
            }
...

command prompt:


    private val colors = mapOf(State.READY to AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN))
            .withDefault { AttributedStyle.DEFAULT.foreground(AttributedStyle.YELLOW) }

    override fun getPrompt(): AttributedString =
            AttributedString("${env}:>", colors.getValue(stateMachine.state.id))

:


    @ShellMethodAvailability("list")
    fun listAvailable() =
            if (stateMachine.state.id == State.READY) Availability.available()
            else Availability.unavailable("requires authentication performed first")

Spring State Machine тАФ (guards), , , , , .



рд╕реНрдкреНрд░рд┐рдВрдЧ рд╢реИрд▓ рдкрд░рд┐рдпреЛрдЬрдирд╛ рд╡рд┐рдХрд╛рд╕ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЙрдкрдпреЛрдЧрд┐рддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЙрддреНрдХреГрд╖реНрдЯ рд╕рдорд╛рдзрд╛рди рд╣реИ, рдЬрдм рдЧреИрд░-рддреБрдЪреНрдЫ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдЖрдВрд╢рд┐рдХ рд╕реНрд╡рдЪрд╛рд▓рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред


рд╕реНрдкреНрд░рд┐рдВрдЧ рд╕реНрдЯреЗрдЯ рдорд╢реАрди рдХрдИ рд╕рд╛рдореБрджрд╛рдпрд┐рдХ рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд╣реИ рдЬреЛ рдЕрдзрд┐рдХ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдФрд░ рдкреВрд░реНрд╡рд╛рдЧреНрд░рд╣ рдХреЗ рдмрд╛рд╡рдЬреВрдж рдЕрдк-рдЯреВ-рдбреЗрдЯ рддрдХрдиреАрдХ рд╣реИред


рдирдореВрдирд╛ рдХреЛрдб GitHub рдкрд░ рдЙрдкрд▓рдмреНрдз рд╣реИ ред



All Articles