Git Product home page Git Product logo

easyflow's Introduction

EasyFlow

EasyFlow 1.3 is out (12 Dec 2013)

  • refactored to use Java enums for states and events
  • added new example

EasyFlow is a simple and lightweight Finite State Machine for Java

With EasyFlow you can:

  • implement complex logic but keep your code simple and clean
  • handle asynchronous calls with ease and elegance
  • avoid concurrency by using event-driven programming approach
  • avoid StackOverflow error by avoiding recursion
  • simplify design, programming and testing of complex java applications

All this in less then 30kB and no run-time overhead!

Here is a simple example illustrating how a state machine can be definded and implemented with EasyFlow

This is a State diargam fragment describing a simple ATM workflow

With EasyFlow we can define the above state machine like this

enum States implements StateEnum {
    SHOWING_WELCOME, WAITING_FOR_PIN, RETURNING_CARD, SHOWING_WELCOME, ...
}

enum Events implements EventEnum {
    cardPresent, pinProvided, cardExtracted, cancel, ...
}
...
EasyFlow<FlowContext> flow =

    from(SHOWING_WELCOME).transit(
        on(cardPresent).to(WAITING_FOR_PIN).transit(
            on(pinProvided).to(...).transit(
                ...
            ),
            on(cancel).to(RETURNING_CARD).transit(
                on(cardExtracted).to(SHOWING_WELCOME)
            )
        )
    );

then all what's left to do is to implement our state handlers like so

whenEnter(SHOWING_WELCOME, new ContextHandler<FlowContext>() {
    @Override
    public void call(final FlowContext context) throws Exception {
        ...
        btnOption1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                context.trigger(cardPresent);
            }
        });
        ...
    }
});
...

and start the flow

flow.start(new FlowContext());

See complete example ATM emulator - Android application

To start using EasyFlow on your project, define Maven dependency like so

<dependency>
    <groupId>au.com.datasymphony</groupId>
    <artifactId>EasyFlow</artifactId>
    <version>1.3.1</version>
</dependency>

See also

EasyFlow for Node.js

License Apache 2.0

easyflow's People

Contributors

bachi76 avatar beh01der avatar hank-cp avatar johnnylambada avatar maximdim avatar mpontes avatar sergeyzenchenko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easyflow's Issues

Current pattern/library status

Hi, Andrey.

The library looks promising to me, but I am a little bit confused that it was not updated for years. Did you switch off Android development, or decided that the pattern does not work well / there are better ways to structure the screen logic? Or you're just using it as a proven tool and do not have a need to make it better?

Can you please elaborate on the effectiveness of the pattern. Do you use it in every app/on every screen, or it fits only some cases?

Race condition during flow starting.

There is possible race condition during flow starting.

Example:

flow.start(true, context);
flow.trigger(SomeEvent);

Flow start calls setCurrentState which is using executor to set current state, but it's possible that initial state won't be initialised before actual trigger method call if we call trigger just after start.

Support possibility to create flow in a step by step manner

instead of "tree style" like

 flow = FlowBuilder

            .from(SHOWING_WELCOME).transit(
                onCardPresent.to(WAITING_FOR_PIN).transit(
                    onPinProvided.to(CHECKING_PIN).transit(
                        onPinValid.to(SHOWING_MAIN_MENU).transit(
                            onMenuShowBalance.to(SHOWING_BALANCE).transit(
                                onCancel.to(SHOWING_MAIN_MENU)
                            ),
                            onMenuWithdrawCash.to(SHOWING_WITHDRAW_MENU).transit(
                                onCancel.to(SHOWING_MAIN_MENU),
                                onConfirm.to(SHOWING_TAKE_CASH).transit(
                                    onCashExtracted.to(SHOWING_MAIN_MENU)
                                )
                            ),
                            onMenuExit.to(RETURNING_CARD)
                        ),
                        onPinInvalid.to(SHOWING_PIN_INVALID).transit(
                            onTryAgain.to(WAITING_FOR_PIN),
                            onNoMoreTries.to(SHOWING_CARD_LOCKED).transit(
                                onConfirm.to(SHOWING_WELCOME)
                            ),
                            onCancel.to(RETURNING_CARD)
                        )
                    ),
                    onCancel.to(RETURNING_CARD).transit(
                        onCardExtracted.to(SHOWING_WELCOME)
                    )
                )
            )
                                  ...

something like

flow = FlowBuilder.register(SHOWING_WELCOME,
                onCardPresent.to(WAITING_FOR_PIN)
);

FlowBuilder.register(WAITING_FOR_PIN,
                onPinProvided.to(CHECKING_PIN),
                onCancel.to(RETURNING_CARD)
);
                      ...
FlowBuilder.register(SHOWING_PIN_INVALID,
                 onTryAgain.to(WAITING_FOR_PIN),
                 onNoMoreTries.to(SHOWING_CARD_LOCKED),
                 onCancel.to(RETURNING_CARD)
);
                      ...                                  ...

Handlers fired multiple times for same transition

I'm trying to implement simple FSM to manage UI state in Android application. Here is a problem I found (isolated to an abstract example):

FSM definition (pseudo code):

State { A, B, C }

Event { next }

fsm = from(State.A).transit(
        on(Event.next).to(State.B).transit(
                on(Event.next).to(State.C).transit(
                        on(Event.next).to(State.A)
                        )
                  )
            );
fsm.executor(new UIThreadExecutor()); // same as in android ATM example

Handler on "whenEnter":

        mFlow.whenEnter(new StateHandler<FlowContext>() {
            @Override
            public void call(StateEnum state, FlowContext context) throws Exception {
                println("entering: " + state);
                Thread.sleep(200); // delay handler execution, so that issue become repeatable
            }
        });

Now, last but not least, following code is executed on some event (eg. button press):

mContext.trigger(Event.next);
mContext.trigger(Event.next);
mContext.trigger(Event.next);

Expected result:

entering: A
entering: B
entering: C
entering: A

Observed result:

entering: A
entering: B
entering: B
entering: B

My understanding of what causes an issue here:
After looking briefly into code I see that triggering is implemented in a strange way by splitting it to two runnables: first, handlers are called in separate executor task, but then another executor task is added to do actual state switching (setCurrentState method). So, as a matter of luck, if second task (state change) is interleaved with another trigger, we may observe handlers being called twice for same transition.

Support of forks

Hi,
I have the following use case, when a main flow reaches to some state, it should split into several branches, the branches then are worked independently. When each branch is complete, the main flow should resume.
If you want a real case, then here it is. A loan application reaches to a point, where security, legal and some other services are needed to provide their input. After all the inputs are collected, the loan application goes to the next stage.
Is this scenario supported?

non-android example

I like to use easy flow for a regular java project is it possible to do so?

StateHandler for whenEnter never invokes

Hello.

Looks like there is issue in whenEnter(StateHandler onEnter) logic.

public <C1 extends StatefulContext> EasyFlow<C1> whenEnter(StateHandler<C1> onEnter) {
        handlers.setHandler(EventType.STATE_ENTER, null, null, onEnter);
        return (EasyFlow<C1>) this;
    }

It should be

handlers.setHandler(EventType.ANY_STATE_ENTER, null, null, onEnter);

not

handlers.setHandler(EventType.STATE_ENTER, null, null, onEnter);

Current version never invoke onEnter at all because HandlerCollection#callOnStateEntered
is trying to find handler without specific state and with type ANY_STATE_ENTER

public <C extends StatefulContext> void callOnStateEntered(StateEnum state, C context) throws Exception {
        Handler h = handlers.get(new HandlerType(EventType.STATE_ENTER, null, state));
        if (h != null) {
            ContextHandler<C> contextHandler = (ContextHandler<C>) h;
            contextHandler.call(context);
        }

        h = handlers.get(new HandlerType(EventType.ANY_STATE_ENTER, null, null));
        if (h != null) {
            StateHandler<C> stateHandler = (StateHandler<C>) h;
            stateHandler.call(state, context);
        }
    }

Support for non-deterministic FSM

Hello,

Is there a way we can add support for non-deterministic Finite state machine.??
Does we currently support them by any mean or we can support them by some extensions!! any future plans for same??

Please add Event.scheduledTrigger

Thanks for this awesome and really lightweight engine!!!

But for my requirements i need to have a support of event triggering scheduling.
I suggest to add a special method for this in Event class:

public void scheduledTrigger(final C context, long delay, TimeUnit unit) {
scheduledExecutorService.schedule(new Runnable() {
trigger(context);
}, delay, unit);
}

scheduledExecutorService - should be configurable like executor in EasyFlow object.

What do you think about such feature?

Adding terminate() method?

Hi Andrey,

Thank you for your library, along with greenrobot EventBus, it helped us scale our background processing engine in our Android app!

It may be a non-issue but in certain of our state handler sub-classes, we initialize resources (ex: register broadcast receivers, holding a wakelock, etc.). However, for transitions that are not initiated within a state, it would be nice to have a mirror handler method for call(), named terminate() where we can resource clean-up.

Please let me know what you think, maybe I am misusing the APIs and there is already a facility in place for this scenario.

Thanks!
-Antoine

JavaDoc

Hello.

I realize this library is relatively easy to use and the error messages are telling (thanks) but it would still be nice if every exposed object/method had JavaDoc so users don't have to do trial-and-error when learning it.

One concrete example I had that was slightly frustrating is when I was encountering a race condition error when I didn't have an executor (it said I was in state "null" when I called 'trigger'; yet when I debugged it showed it in state "INITIAL_STATE" and it passed the trigger fine.) If I knew how this executor was used, I might have known to use it and thus avoided that race condition issue.

P.S. Not sure if the above is expected behavior.

Duplicated depending - slf4j-api

I can not build the Gradle-project using your library due to the fact that the duplicated depending on org.slf4j: slf4j-api: 1.7.2:
--- au.com.datasymphony:EasyFlow:1.3.1
+--- org.slf4j:slf4j-api:1.7.2
+--- com.google.guava:guava:15.0
--- org.slf4j:slf4j-log4j12:1.7.2
+--- org.slf4j:slf4j-api:1.7.2
--- log4j:log4j:1.2.17

Easyflow never terminates its threads

I'm using easyflow to manage state change in a file parser and so far it's come in quite handy. However one thing I can't figure out is why after I reach a final state, the context reports itself as terminated, but the flow engine keeps going.

Is there a recommended way to exit the entire FlowEngine when your processing is complete?

EDIT: I found a solution, for anyone looking; create your own Executor class, instantiate your EasyFlow object with it, and call shutdownNow() on the executorservice once your context terminates.

Would be nice to have something like that be a built in option though, or at least the ability to tell EasyFlow to terminate directly.

This is no longer working

Sad, the sample project does not even work and looked promising :( Is this project super outdated? What about the maven sources? I would like to try it out :)

The "on" method cannot be called

Hello everyone,

I am trying to use the EasyFlow framework, but I have a problem when I try to call the "on" method.

I have writen the following code:

public enum States implements StateEnum {
    NEW, SUGGESTION
}

public enum Events implements EventEnum {
    CONFIRM, CANCEL
}

EasyFlow<FlowContext> flow = 
                FlowBuilder.from(States.NEW)
                .transit(
                        on(Events.CONFIRM).to(States.SUGGESTION));

At the code above, the "on" method is not recognized.

Do you have any idea how I could resolve this problem?

Paper

Hi guys,
I am writing a Paper on Open Source Workflow Engines(WE). Can you tell me if your WE can support the Control-Flow Patterns described under http://www.workflowpatterns.com/patterns/control/ ? If you are not sure, can you provide a Getting Started Doc or an Example for a simple Sequence Workflow, so i can try to check it myself?
thank you,
Silviu

StateHandler for whenEnter never invokes

Hello.

Looks like there is issue in whenEnter(StateHandler onEnter) logic.

public <C1 extends StatefulContext> EasyFlow<C1> whenEnter(StateHandler<C1> onEnter) {
        handlers.setHandler(EventType.STATE_ENTER, null, null, onEnter);
        return (EasyFlow<C1>) this;
    }

It should be

handlers.setHandler(EventType.ANY_STATE_ENTER, null, null, onEnter);

not

handlers.setHandler(EventType.STATE_ENTER, null, null, onEnter);

Current version never invoke onEnter at all because HandlerCollection#callOnStateEntered
is trying to find handler without specific state and with type ANY_STATE_ENTER

public <C extends StatefulContext> void callOnStateEntered(StateEnum state, C context) throws Exception {
        Handler h = handlers.get(new HandlerType(EventType.STATE_ENTER, null, state));
        if (h != null) {
            ContextHandler<C> contextHandler = (ContextHandler<C>) h;
            contextHandler.call(context);
        }

        h = handlers.get(new HandlerType(EventType.ANY_STATE_ENTER, null, null));
        if (h != null) {
            StateHandler<C> stateHandler = (StateHandler<C>) h;
            stateHandler.call(state, context);
        }
    }

Some events are not triggered in a consistent manner

In my App developed in Android Studio I am using EasyFlow but the events are not triggered in a reproducible manner:

  • some events are always firing in a perfect manner,
  • other events are not firing in the first attempt(s) then they fire fine and
  • others are never firing; proper breakpoints show that the "onEvent.trigger(context)" is reached by the state is not changing as defined in the flow.

All are in the main thread; here are extracts of my code:

onSelectingMainMenu.to(MAIN_MENU).transit(
onReturnToPreviousChoice.to(READY),
onSelectingMainMenuFinished.to(READY),
onSelectingSettingsMenu.to(SETTINGS_MENU).transit(
onReturnToPreviousChoice.to(MAIN_MENU)
)
)

...

switch (direction) {
case TOP:
onReturnToPreviousChoice.trigger(flowContext);
...

Any help?
Thank you.

In which package or library can I fine the "StatefulContext" class?

I am trying to use EasyFlow for my project, but I am facing a problem, because I cannot find out from which package comes the "StatefulContext" class.

This class appears to be founmental, because all the "FlowConntext" should extend the "StatefulContext" class.

In case you know where I can find the "StatefulContext", please leave a comment below.

What the right way to "clean up" after exception during state process?

Hi, Andrey

Firstly, thank you for very good library.
So powerfull and simple.

But I can't figure out the right way to handle exception during state process.

What the right way to "clean up" after exception?
In some situation I wanna to revert to previous state. In others I prefer restart flow. How make this?

migrate to jdk enum

probably it would be easier for end user if you allowed to use jdk enum:

enum UserState implements EasyFlow.State {}

enum UserEvent implements EasyFlow.Event {}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.