[[statemachine-getting-started]]
= Getting started

If you are just getting started with Spring Statemachine,
this is the section for you! Here, we answer the basic
“`what?`”, “`how?`” and “`why?`” questions. We start with a gentle
introduction to Spring Statemachine. We then build our
first Spring Statemachine application and discuss some
core principles as we go.

== System Requirement

Spring Statemachine {revnumber} is built and tested with
JDK 8 (all artifacts have JDK 7 compatibility) and Spring
Framework {spring-version}. It does not require any other
dependencies outside of Spring Framework within its core system.

Other optional parts (such as <<sm-distributed>>) have dependencies on
Zookeeper, while <<statemachine-examples>> has dependencies
on `spring-shell` and `spring-boot`, which pull other dependencies
beyond the framework itself. Also, the optional security and data access features have
dependencies to on Spring Security and Spring Data modules.

== Modules

The following table describes the modules that are available for Spring Statemachine.

|===
|Module |Description

|`spring-statemachine-core`
|The core system of Spring Statemachine.

|`spring-statemachine-recipes-common`
|Common recipes that do not require dependencies outside of the core
framework.

|`spring-statemachine-kryo`
|`Kryo` serializers for Spring Statemachine.

|`spring-statemachine-data-common`
|Common support module for `Spring Data`.

|`spring-statemachine-data-jpa`
|Support module for `Spring Data JPA`.

|`spring-statemachine-data-redis`
|Support module for `Spring Data Redis`.

|`spring-statemachine-data-mongodb`
|Support module for `Spring Data MongoDB`.

|`spring-statemachine-zookeeper`
|Zookeeper integration for a distributed state machine.

|`spring-statemachine-test`
|Support module for state machine testing.

|`spring-statemachine-cluster`
|Support module for Spring Cloud Cluster.
Note that Spring Cloud Cluster has been superseded by Spring Integration.

|`spring-statemachine-uml`
|Support module for UI UML modeling with Eclipse Papyrus.

|`spring-statemachine-autoconfigure`
|Support module for Spring Boot.

|`spring-statemachine-bom`
|Bill of Materials pom.

|`spring-statemachine-starter`
|Spring Boot starter.
|===

== Using Gradle

The following listing shows a typical `build.gradle` file created by choosing various settings at https://start.spring.io:

====
[source,groovy,indent=0,subs="attributes+"]
----
buildscript {
	ext {
		springBootVersion = '{spring-boot-version}'
	}
	repositories {
		mavenCentral()
		maven { url "https://repo.spring.io/snapshot" }
		maven { url "https://repo.spring.io/milestone" }
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
	mavenCentral()
	maven { url "https://repo.spring.io/snapshot" }
	maven { url "https://repo.spring.io/milestone" }
}


ext {
	springStatemachineVersion = '{revnumber}'
}

dependencies {
	compile('org.springframework.statemachine:spring-statemachine-starter')
	testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.statemachine:spring-statemachine-bom:${springStatemachineVersion}"
	}
}
----
====

IMPORTANT: Replace `0.0.1-SNAPSHOT` with a version you want to use.

With a normal project structure, you can build this project with the following command:

====
[source,text,indent=0]
----
# ./gradlew clean build
----
====

The expected Spring Boot-packaged fat jar would be `build/libs/demo-0.0.1-SNAPSHOT.jar`.

NOTE: You do not need the`libs-milestone` and `libs-snapshot` repositories for
production development.

== Using Maven

The following example shows a typical `pom.xml` file, which was created by choosing various options at https://start.spring.io:

====
[source,xml,indent=0,subs="attributes+"]
----
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>gs-statemachine</name>
	<description>Demo project for Spring Statemachine</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>{spring-boot-version}</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-statemachine.version>{revnumber}</spring-statemachine.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.statemachine</groupId>
			<artifactId>spring-statemachine-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.statemachine</groupId>
				<artifactId>spring-statemachine-bom</artifactId>
				<version>${spring-statemachine.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>


</project>
----
====

IMPORTANT: Replace `0.0.1-SNAPSHOT` with a version you want to use.

With a normal project structure, you can build this project with the following command:

====
[source,text,indent=0]
----
# mvn clean package
----
====

The expected Spring Boot-packaged fat-jar would be `target/demo-0.0.1-SNAPSHOT.jar`.

NOTE: You do not need the `libs-milestone` and `libs-snapshot` repositories for
production development.

==  Developing Your First Spring Statemachine Application

You can start by creating a simple Spring Boot `Application` class
that implements `CommandLineRunner`. The following example shows how to do so:

====
[source,java,indent=0]
----
@SpringBootApplication
public class Application implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
----
====

Then you need to add states and events, as the following example shows:

====
[source,java,indent=0]
----
public enum States {
    SI, S1, S2
}

public enum Events {
    E1, E2
}
----
====

Then you need to add state machine configuration, as the following example shows:

====
[source,java,indent=0]
----
@Configuration
@EnableStateMachine
public class StateMachineConfig
        extends EnumStateMachineConfigurerAdapter<States, Events> {

    @Override
    public void configure(StateMachineConfigurationConfigurer<States, Events> config)
            throws Exception {
        config
            .withConfiguration()
                .autoStartup(true)
                .listener(listener());
    }

    @Override
    public void configure(StateMachineStateConfigurer<States, Events> states)
            throws Exception {
        states
            .withStates()
                .initial(States.SI)
                    .states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
            throws Exception {
        transitions
            .withExternal()
                .source(States.SI).target(States.S1).event(Events.E1)
                .and()
            .withExternal()
                .source(States.S1).target(States.S2).event(Events.E2);
    }

    @Bean
    public StateMachineListener<States, Events> listener() {
        return new StateMachineListenerAdapter<States, Events>() {
            @Override
            public void stateChanged(State<States, Events> from, State<States, Events> to) {
                System.out.println("State change to " + to.getId());
            }
        };
    }
}
----
====

Then you need to implement `CommandLineRunner` and autowire `StateMachine`.
The following example shows how to do so:

====
[source,java,indent=0]
----
@Autowired
private StateMachine<States, Events> stateMachine;

@Override
public void run(String... args) throws Exception {
    stateMachine.sendEvent(Events.E1);
    stateMachine.sendEvent(Events.E2);
}
----
====

Depending on whether you build your application with `Gradle` or `Maven`,
you can run it by using `java -jar build/libs/gs-statemachine-0.1.0.jar` or
`java -jar target/gs-statemachine-0.1.0.jar`, respectively.

The result of this command should be normal Spring Boot output.
However, you should also find the following lines:

====
[source,text,indent=0]
----
State change to SI
State change to S1
State change to S2
----
====

These lines indicate that the machine you constructed
is moving from one state to another, as it should.
