/java/

Fines State Machine. Statefulj-fsm & JEE

2016-08-15 12:17:25

Here we have simple implementation of FSM for post object. We can distinguish 5 main parts.

1. State definition

    private State

<post> DRAFT = new StateImpl&lt;&gt;(PostState.DRAFT.toString());
    private State<post> PUBLISHED = new StateImpl&lt;&gt;(PostState.PUBLISHED.toString());
    private State<post> UNPUBLISHED = new StateImpl&lt;&gt;(PostState.UNPUBLISHED.toString());
    private State<post> DELETED = new StateImpl&lt;&gt;(PostState.DELETED.toString());

2. State grouping

    private List<state<post>&gt; states = new LinkedList<state<post>&gt;() {{
        add(DRAFT);
        add(PUBLISHED);
        add(UNPUBLISHED);
        add(DELETED);
    }};

3. State transition description

    DRAFT.addTransition("EventPublish", PUBLISHED);

    PUBLISHED.addTransition("EventUnPublish", UNPUBLISHED);

    DRAFT.addTransition("EventDelete", DELETED);
    UNPUBLISHED.addTransition("EventDelete", DELETED);

4. Init FSM

    postFiniteStateMachine = new FSM&lt;&gt;(new MemoryPersisterImpl&lt;&gt;(states, DRAFT));

5. Use FSM

    postFiniteStateMachine.onEvent(post, event);

Full example with test controller & post mock storage

check this out GIT PostFiniteStateMachine.java

package pl.btbw.module;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.statefulj.fsm.FSM;
import org.statefulj.fsm.model.State;
import org.statefulj.fsm.model.impl.StateImpl;
import org.statefulj.persistence.memory.MemoryPersisterImpl;

import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.LinkedList;
import java.util.List;

@Stateless
public class PostFiniteStateMachine {

    private static final Logger LOGGER = LoggerFactory.getLogger(PostFiniteStateMachine.class);

    private FSM<post> postFiniteStateMachine;

    @Inject
    private PostMockStorage postMockStorage;

    private State<post> DRAFT = new StateImpl&lt;&gt;(PostState.DRAFT.toString());
    private State<post> PUBLISHED = new StateImpl&lt;&gt;(PostState.PUBLISHED.toString());
    private State<post> UNPUBLISHED = new StateImpl&lt;&gt;(PostState.UNPUBLISHED.toString());
    private State<post> DELETED = new StateImpl&lt;&gt;(PostState.DELETED.toString());

    private List<state<post>&gt; states = new LinkedList<state<post>&gt;() {{
        add(DRAFT);
        add(PUBLISHED);
        add(UNPUBLISHED);
        add(DELETED);
    }};

    @PostConstruct
    public void init() {

        DRAFT.addTransition("EventPublish", PUBLISHED);

        PUBLISHED.addTransition("EventUnPublish", UNPUBLISHED);

        DRAFT.addTransition("EventDelete", DELETED);
        UNPUBLISHED.addTransition("EventDelete", DELETED);

        postFiniteStateMachine = new FSM&lt;&gt;(new MemoryPersisterImpl&lt;&gt;(states, DRAFT));
    }

    public void process(Integer postId, String event) {
        LOGGER.info("Process post with id:{}, on event:{}", new Object[]{postId, event});

        Post post = postMockStorage.getPost(postId);

        LOGGER.info("Current state of post:{}", post.getState());

        try {
            postFiniteStateMachine.onEvent(post, event);

            LOGGER.info("New state of post:{}", post.getState());
        } catch (Exception e) {
            LOGGER.error("Something went wrong, message:{}", e.getMessage());
        }
    }

}

FSMController

package pl.btbw.web;

import pl.btbw.module.Post;
import pl.btbw.module.PostFiniteStateMachine;
import pl.btbw.module.PostMockStorage;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/")
public class FSMController {

    @Inject
    private PostFiniteStateMachine postFiniteStateMachine;

    @Inject
    private PostMockStorage postMockStorage;

    @GET
    @Path("/post/{postId}/event/{event}")
    public String process(@PathParam("postId") Integer postId, @PathParam("event") String event) {
        postFiniteStateMachine.process(postId, event);
        return "process...";
    }

    @GET
    @Path("/post/{postId}")
    @Produces("application/json")
    public Post test(@PathParam("postId") Integer postId) {
        return postMockStorage.getPost(postId);
    }
}

Post.java

package pl.btbw.module;

import org.statefulj.persistence.annotations.State;

public class Post {

    @State
    private String state;

    public Post(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

PostState.java

package pl.btbw.module;

public enum PostState {

    DRAFT,
    PUBLISHED,
    UNPUBLISHED,
    DELETED
}

App Configuration

package pl.btbw.core;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("rest")
public class MyApp extends Application {
}

PostMockStorage

package pl.btbw.module;

import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import java.util.HashMap;
import java.util.Map;

@Stateless
public class PostMockStorage {

    private Map<integer, post=""> posts = new HashMap&lt;&gt;();

    @PostConstruct
    private void init() {
        posts.put(1, new Post(PostState.DRAFT.toString()));
        posts.put(2, new Post(PostState.PUBLISHED.toString()));
        posts.put(3, new Post(PostState.PUBLISHED.toString()));
        posts.put(4, new Post(PostState.UNPUBLISHED.toString()));
        posts.put(5, new Post(PostState.UNPUBLISHED.toString()));
        posts.put(6, new Post(PostState.DELETED.toString()));
    }

    public Post getPost(Integer id) {
        return posts.get(id);
    }
}

pom.xml

<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?>
<project http:="" maven-4.0.0.xsd&quot;="" maven.apache.org="" xmlns='"http://maven.apache.org/POM/4.0.0"' xmlns:xsi='"http://www.w3.org/2001/XMLSchema-instance"' xsd="" xsi:schemalocation='"http://maven.apache.org/POM/4.0.0'>
<modelversion>4.0.0</modelversion>
<groupid>btbw.pl</groupid>
<artifactid>fsm</artifactid>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupid>javax</groupid>
<artifactid>javaee-api</artifactid>
<version>7.0</version>
</dependency>
<dependency>
<groupid>org.statefulj</groupid>
<artifactid>statefulj-fsm</artifactid>
<version>2.1</version>
</dependency>
<dependency>
<groupid>org.slf4j</groupid>
<artifactid>slf4j-api</artifactid>
<version>1.7.21</version>
</dependency>
</dependencies>
<build>
<finalname>fsm</finalname>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<configuration>
<source/>1.8
                    <target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactid>maven-war-plugin</artifactid>
<configuration>
<failonmissingwebxml>false</failonmissingwebxml>
</configuration>
</plugin>
</plugins>
</build>
</project>