by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Jun 04 06:33
    Yogesn opened #23
  • Apr 23 14:00
    jaredculp opened #22
  • Apr 14 06:54
    arunav-b opened #21
  • Apr 14 06:39
    arunav-b opened #20
  • Apr 14 05:00
    arunav-b commented #16
  • Apr 12 20:37
    benas edited #19
  • Apr 12 20:37
    benas labeled #19
  • Apr 12 20:37
    benas milestoned #19
  • Apr 12 20:37
    benas commented #19
  • Apr 12 07:08
    mcflythekid commented #19
  • Apr 12 07:08
    mcflythekid commented #19
  • Apr 12 07:00
    mcflythekid opened #19
  • Apr 11 20:53
    benas labeled #15
  • Apr 11 20:52
    benas commented #15
  • Apr 11 20:19
    benas commented #16
  • Apr 11 20:14
    benas milestoned #18
  • Apr 11 20:14
    benas labeled #18
  • Apr 11 20:14
    benas opened #18
  • Apr 11 20:03
    benas labeled #16
  • Apr 11 20:03
    benas milestoned #16
Mahmoud Ben Hassine
@benas
This project shares the same philosophy as jeasy projects: being simple, easy to use, down to the bare minimum, no feature creep.
I hope this project would be helpful to the community.
Looking forward for your feedback!
Will Gilbert
@will-gilbert
Thanks for releasing this. Will use our (only) long US holiday to play with it. Am on iPad now, will send OSWf GitHub link when on desktop.
yanquanyu
@yanquanyu
can the easy-flows work together with easy-rules? how? Would you please show me the example code. thx
Mahmoud Ben Hassine
@benas
@yanquanyu It depends on which one you would like to run inside the other :-) a flow of rules engine or a rules engine running flows according to some rules
Both are possible
Arka Bandyopadhyay
@arkabandyopadhyay
Hi Ben
I was going through the easy-flow found no error management handlers or code any specific reason apart from being simple .
Mahmoud Ben Hassine
@benas
@arkabandyopadhyay Hello
for simplicity reason
ajson
@ajsondev

Hi Ben,

I came across JEasy and am wondering if there is a way to Serialise/Deserialise a workflow .... any thoughts?

@benas

Hi Ben,

I came across JEasy and am wondering if there is a way to Serialise/Deserialise a workflow .... any thoughts?

ALlen

Mahmoud Ben Hassine
@benas

Hi @ajsondev

if there is a way to Serialise/Deserialise a workflow

no, not out of the box.

ajson
@ajsondev
Thanks @benas .... is it as simple as marking relevant classes as serializable, or thats wishful thinking?!
Julien Bras
@bobman38
hello I am testing this nice piece of code to replace a BPMN process that run only Service Task boxes... a typical use case for easy-flow it seems ! Do you know a simple way to handle some signals throw/catch ? Goal is to start a task only after 2 specific task ended...
Mahmoud Ben Hassine
@benas
@bobman38 yes, a simple way is to have an object acting as a "rendez-vous" point. This object can hold two booleans (task1done, task2done), and is passed to both tasks. Each task updates its boolean when done. Then you can use a conditional flow that monitors the rendez-vous point and start the 3rd task when appropriate. Hope this helps.
Mahmoud Ben Hassine
@benas

@ajsondev Sorry for the late reply.

is it as simple as marking relevant classes as serializable, or thats wishful thinking?!

Well it depends on what you want to serialize actually, the flow definition or the flow execution? The flow definition can be put in a text file and then be loaded in a Workflow object. For the flow execution, I think we need more than marking classes as Serializable, we need to persist the execution context of the flow in a persistent store (and that's another story..)

Julien Bras
@bobman38
@benas thanks good idea I will try to implement this little thing. BTW thanks for all j-easy projects ! that's good job !
darshan92kumar
@darshan92kumar

Hello Ben,
Nice piece of work.
I am trying to use your project for my workflow impl.. But one of the testcase error out (WorkFlowEngineImplTest.java) with error

java.lang.NoClassDefFoundError: Could not initialize class org.jeasy.flows.engine.WorkFlowEngineBuilder
at org.jeasy.flows.engine.WorkFlowEngineImplTest.composeWorkFlowFromSeparateFlowsAndExecuteIt(WorkFlowEngineImplTest.java:92)

any idea on the fix ? I see the class files in bin folder tho...

Mahmoud Ben Hassine
@benas
@darshan92kumar Thanks. Looks like you have a class path issue. Make sure to clean/refresh your project.
darshan92kumar
@darshan92kumar
Thanks !
bhuvangu
@bhuvangu
Hi.. @benas which tool is used to generate the workflow png in README? :)
Mahmoud Ben Hassine
@benas
@bhuvangu I use http://diagrammix.com to draw these diagrams.
ameya
@ameya
Hi Ben, Thank you for your source code. Came across easy-flow, simple and nice to use. One question, you have work context to pass data between work. How do I use it a sample would help me. Wanted to try a simple case, imagine that PrintMessageWork has a counter variable, each time call is called, it used the previous PrintMessageWork value and increments and stores as it own to be discovered by the next work. Can you help me out?
Mahmoud Ben Hassine
@benas

Hi @ameya , thank you! Here is an example:

import org.jeasy.flows.work.DefaultWorkReport;
import org.jeasy.flows.work.Work;
import org.jeasy.flows.work.WorkContext;
import org.jeasy.flows.work.WorkReport;
import org.jeasy.flows.work.WorkStatus;

class PrintMessageWork implements Work {

    private int count;
    private String message;

    public PrintMessageWork(String message) {
        this.message = message;
    }

    public String getName() {
        return "print message work";
    }

    @Override
    public WorkReport call(WorkContext workContext) {
        System.out.println(message);
        count++;
        workContext.put("count", count);
        return new DefaultWorkReport(WorkStatus.COMPLETED);
    }
}

Is this what you are looking for?

ameya
@ameya

Super nice !!! Thank you very much, you're code explains me the data flow.
Two things added

    public WorkReport call(WorkContext workContext) {
        System.out.println(message);
        Integer count = (Integer) workContext.get("count");
        if(null == count){
            count =0;
        }
        workContext.put("count",++count);
        System.out.println(count);
        return new DefaultWorkReport(WorkStatus.COMPLETED);
    }

in the PrintMessageWork and

public Object get(String key) { if(context.containsKey(key)) { return context.get(key); } return null; }

in WorkContext class

Mahmoud Ben Hassine
@benas
yes sure, I just put the strict minimum in the example, you need to check if the value is in the context and all the sanity checks etc. With that, are you able now to implement your use case?
ameya
@ameya
Yes thank you very much for the help.
Arunav Bhattacharya
@arunavb_gitlab
@benas - When will you be releasing the changes done for WorkContext ? The code available on github or the dependency from mvnrepository does not have this change.
Mahmoud Ben Hassine
@benas
I was waiting for feedback on this j-easy/easy-flows#7. But I will release v0.2 with the new WorkContext API in the next couple of days. I will add a message here when it is released. Keep tuned :wink:
Arunav Bhattacharya
@arunavb_gitlab
Thanks @benas !!
One other thing that I wanted to bring up here is in the conditional flow currently the condition is based on the worker execution. In case I want to have a condition based on the input data in the WorkContext, then the condition flow will not support as in the when() condition it is checking for the WorkReportPredicate only. If we can have a boolean check or add a boolean check along with the WorkReportPredicate check in the when() condition then that will add additional flexibility on executing conditional workers based on the workContext.
Thanks again for the awesome work that you are doing !
Arunav Bhattacharya
@arunavb_gitlab
I made a change in my local to add a boolean context on the ConditionalFlow and put an additional check while calling nextOnPredicateSuccess. Can you provide a similar functionality or have a single boolean value that can be used either to track WorkflowExecution or the boolean context or have two separate entities -
    ConditionalFlow(String name, Work toExecute, Work nextOnPredicateSuccess, Work nextOnPredicateFailure,
                    WorkReportPredicate predicate, boolean context) {
        super(name);
        this.toExecute = toExecute;
        this.nextOnPredicateSuccess = nextOnPredicateSuccess;
        this.nextOnPredicateFailure = nextOnPredicateFailure;
        this.predicate = predicate;
        this.context = context;
    }

    /**
     * {@inheritDoc}
     */
    public WorkReport call(WorkContext workContext) {
        WorkReport jobReport = toExecute.call(workContext);
        if (predicate.apply(jobReport) && context) {
            jobReport = nextOnPredicateSuccess.call(workContext);
        } else {
            if (nextOnPredicateFailure != null && !(nextOnPredicateFailure instanceof NoOpWork)) { // else is optional
                jobReport = nextOnPredicateFailure.call(workContext);
            }
        }
        return jobReport;
    }
Mahmoud Ben Hassine
@benas
@arunavb_gitlab makes sense. I think adding the work context to the work report is more appropriate since we would not need a boolean next to the predicate.
The condition could still based on a Predicate<WorkReport> but it gives access to the work context:
public class MyPredicate implements WorkReportPredicate {
    @Override
    public boolean apply(WorkReport workReport) {
        WorkContext workContext = workReport.getWorkContext();
        // use context for condition
        return false;
    }
}
wdyt?
Mahmoud Ben Hassine
@benas
I pushed a change to implement this option (See j-easy/easy-flows@67999df) and deployed it in v0.2-SNAPSHOT. You should be able now to get access to the context in the predicate from work report and implement your condition accordingly. Let me know about your thoughts.
Arunav Bhattacharya
@arunavb_gitlab

@benas - WorkContext is already getting passed between Work. I m not sure adding it to the WorkReport will have any added advantage, as we can implement the WorkReportPredicate and the WorkContext can be accessed from anywhere within the WorkFlow.
Let me know if you have other thoughts on this.

On a side note constructor of AbstractWorkFlow and all its implementations (ConditionalFlow, ParallelFlow, RepeatFlow, SequentialFlow) are not public. Is there any specific reason for that ?
Had it been made public the client will have the flexibility for using either the fluent-api or use the constructor. Using the constructor will give the flexibility of building a wrapper on the client side to pass the list of workers at runtime (specifically for ParallelFlow and SequentialFlow). With the fluent api somehow there is a constraint of passing the list of workers at runtime if the client wants to use the easy-flow provided flows. Do let me know your thoughts on this as well.

Mahmoud Ben Hassine
@benas

WorkContext is already getting passed between Work. I m not sure adding it to the WorkReport will have any added advantage, as we can implement the WorkReportPredicate and the WorkContext can be accessed from anywhere within the WorkFlow.
Let me know if you have other thoughts on this.

The context is passed everywhere except in predicates. The only thing available in the scope of a predicate is the WorkReport. If you don't have access to the context from within the predicate, you cannot base the condition on some data from the context. The only option would be to pass the initial context to the each predicate via a constructor/setter, which is not convenient. Here is a quick example: Let's suppose we have a conditional flow that executes a first unit of work which will put the decision to continue or not in the work context. The predicate should be able to access the context and make the decision:

@Test
public void testPredicateWithoutContext() {
    ConditionalFlow conditionalFlow = ConditionalFlow.Builder.aNewConditionalFlow()
            .execute(new Work() {
                @Override
                public String getName() {
                    return "step 1";
                }

                @Override
                public WorkReport call(WorkContext workContext) {
                    System.out.println("step 1");
                    workContext.put("shouldContinue", false);
                    return new DefaultWorkReport(WorkStatus.COMPLETED, workContext);
                }
            })
            .when(new WorkReportPredicate() {
                @Override
                public boolean apply(WorkReport workReport) {
                    // without access to the context via the report here, 
                    // we have to pass the initial context in a constructor to the predicate..
                    return (boolean) workReport.getWorkContext().get("shouldContinue");
                }
            })
            .then(new Work() {
                @Override
                public String getName() {
                    return "step 2";
                }

                @Override
                public WorkReport call(WorkContext workContext) {
                    System.out.println("step 2");
                    return new DefaultWorkReport(WorkStatus.COMPLETED, workContext);
                }
            })
            .build();

    ExecutorService executorService = Executors.newFixedThreadPool(2);
    WorkFlowEngine workFlowEngine = aNewWorkFlowEngine().build();
    WorkReport workReport = workFlowEngine.run(conditionalFlow, new WorkContext());
    System.out.println("workReport = " + workReport);
    executorService.shutdown();

   // prints
   // step 1
   // workReport = DefaultWorkReport {status=COMPLETED, context=org.jeasy.flows.work.WorkContext@5ef60048, error=''}
}

Do you see?

Mahmoud Ben Hassine
@benas

On a side note constructor of AbstractWorkFlow and all its implementations (ConditionalFlow, ParallelFlow, RepeatFlow, SequentialFlow) are not public. Is there any specific reason for that ?

Yes, there are a couple of reasons for that:

Using the constructor will give the flexibility of building a wrapper on the client side to pass the list of workers at runtime

What prevents you from passing the workers at runtime using the builder? I see no added value in terms of flexibility of using a constructor over the builder. I'm probably not understanding your point, so if you could provide a code example it can help.

Thank you for your feedback!

Mahmoud Ben Hassine
@benas

Hi @/all ,

Easy Flows 0.2 has been released with the following new features:

  • Issue #7: Add WorkContext concept: this makes it possible to pass initial parameters to the workflow and share data between work units
  • Issue #12: Make the executor service of ParallelFlow configurable: This is not only more flexible in terms of configuration but also more efficient in terms of resource management since it makes it possible to re-use the same executor service for multiple parallel flows

Many thanks to @aaur0, @zhhaojie, @arunavb_gitlab and @ameya for their feedback on the first release of Easy Flows and their help to improve it in v0.2! It is surprising how many use cases can be implemented with this little workflow engine already with only its second release!

Arunav Bhattacharya
@arunavb_gitlab

@benas

WorkContext is already getting passed between Work. I m not sure adding it to the WorkReport will have any added advantage, as we can implement the WorkReportPredicate and the WorkContext can be accessed from anywhere within the WorkFlow.
Let me know if you have other thoughts on this.

The context is passed everywhere except in predicates. The only thing available in the scope of a predicate is the WorkReport. If you don't have access to the context from within the predicate, you cannot base the condition on some data from the context. The only option would be to pass the initial context to the each predicate via a constructor/setter, which is not convenient. Here is a quick example: Let's suppose we have a conditional flow that executes a first unit of work which will put the decision to continue or not in the work context. The predicate should be able to access the context and make the decision:

Do you see?

Yes - I get your point. What I really wanted to mean here is that the user using the API can determine if he/she needs the context to be passed to the predicates. Accordingly the WorkContext can be injected inside the predicate, as you said using a constructor/setter injection. But I agree by providing the option to pass the WorkContext in the WorkReport makes me write more fluid code. Thanks for your explanation !

Arunav Bhattacharya
@arunavb_gitlab

@benas

Using the constructor will give the flexibility of building a wrapper on the client side to pass the list of workers at runtime

What prevents you from passing the workers at runtime using the builder? I see no added value in terms of flexibility of using a constructor over the builder. I'm probably not understanding your point, so if you could provide a code example it can help.

Suppose I have multiple implementations of Work that needs to be executed sequentially for different set of api calls. For example, I have 50 implementations of Work and I have 4 different APIs. And lets say those 4 APIs use 10, 20, 15 and 5 implementations of the Work respectively. Point here is with the current Builder pattern implementation of the SequentialFlow, it enforces me to define and build the workflows at 4 different places for each of the APIs. With an increase in number of API or modification to an existing workflow (addition or deletion of a particular WorkImpl) I have to write a new workflow build step or make a change everytime in the workflow build step respectively. But had there been an option of passing a list of Work to the WorkFlow, I can simply maintain a configuration of list of Work for each of the APIs and pass that list via constructor at runtime which then builds the workflow.
Currently as the constructors in the provided WorkFlows are not made public it is not possible for me to pass a list of Work at runtime. With the current configuration I have to write separate sequential WorkFlows using the builder pattern for each API call. The same is true for ParallelFlow as well. Moreover for implementing a custom workflow I have to implement WorkFlow interface and cannot reuse the AbstractWorkFlow class.

Let me know your thoughts on this.

Arunav Bhattacharya
@arunavb_gitlab

Hi @/all ,

Easy Flows 0.2 has been released with the following new features:

  • Issue #7: Add WorkContext concept: this makes it possible to pass initial parameters to the workflow and share data between work units
  • Issue #12: Make the executor service of ParallelFlow configurable: This is not only more flexible in terms of configuration but also more efficient in terms of resource management since it makes it possible to re-use the same executor service for multiple parallel flows

Many thanks to @aaur0, @zhhaojie, @arunavb_gitlab and @ameya for their feedback on the first release of Easy Flows and their help to improve it in v0.2! It is surprising how many use cases can be implemented with this little workflow engine already with only its second release!

Thanks for the great work that you are doing !

Mahmoud Ben Hassine
@benas

@arunavb_gitlab Thank you for your feedback!

Let me know your thoughts on this.

In regards to constructor vs builder, please open an issue with a code example. I still can't see the difference because in the end, using a constructor or a builder you would need to create the same number of instances. Probably you mean create the flow once and (re)set the work dynamically at runtime? So we would need setter and not a constructor? Another argument in addition to the list I gave above is immutability of workflow definition and all the benefits of that in multi-threaded environment. Anyway, please open an issue with what you have in mind with a code example and we can discuss it there. Thank you upfront!

Arunav Bhattacharya
@arunavb_gitlab
@benas - Have opened an issue for this -
j-easy/easy-flows#16
Mahmoud Ben Hassine
@benas
Thanks! Will take a look.
Arunav Bhattacharya
@arunavb_gitlab
@benas - Sorry to bug you again, but I have forked my proposed changes here -
arunav-b/easy-flows@bf47f38
Please take a look and let me know for your comments.
Mahmoud Ben Hassine
@benas
ok thanks no worries, I will take a look when I have some time (in these difficult times..)