OpenEmcee Microflow Engine for Java

Home | Downloads | Project Information | Discussion Groups | Tutorials | Team

Documentation

Tutorials
JavaDoc
News

FAQ's

Lists/Support
Strange name?

Downloads

Downloads
CVS Access

 

 

 

 

 

 

 

 

 

 

 

 

 

An Intro Tutorial to OpenEmcee Microflows

By Scott Schenkein, Edited By Michael Roane

Welcome to the OpenEmcee Microflow quick-start tutorial.  After reading this tutorial you should have a good feeling for how the system works (and perhaps what it does).  The engine has a slight learning curve, but the following example shows you all you need to get started.

This simple example can not demonstrate the complexity reducing, reuse encouraging properties of this development methodology. We will explore more complex examples in a future tutorial.

Business Case

Let's consider a simple business process for creating new credit card accounts.  This system
  1. Take the user's information as input.
  2. Call to a credit service to determine suitability for awarding an account.
    1. If the service is unavailable, return an appropriate error to the caller.
    2. If the service is unable to determine a score, put the account into the manual work queue.
    3. If the score is good, continue to step 3.
  3. Call to a service that verifies addresses.
    1. If the address is unverifiable, put the account into the manual work queue (2.2).
    2. If the address is verifiable, continue to step 4.
  4. Create the account.
    1. If the creation succeeds, you're done.  Congrats!
    2. If the creation fails, return an appropriate error to the caller.

Digest the Requirements

Now, lets deconstruct the requirements into business functions (model), and business contexts (control).

Model: First lets look at business functions. (The non-business function information is stricken through.)  This part of the spec will be implemented in "Task" classes.  More on that later.
  1. Take the users information as input.
  2. Call to a credit service to determine suitability for awarding an account.
    1. If the service is unavailable, return an appropriate error to the caller.
    2. If the service is unable to determine a score, put the account into the manual work queue.
    3. If the score is good, continue to step 3.
  3. Call to a service that verifies addresses.
    1. If the address is unverifiable, put the account into the manual work queue (2.2).
    2. If the address is verifiable, continue to step 4.
  4. Create the account.
    1. If the creation succeeds, you're done.  Congrats!
    2. If the creation fails, return an appropriate error to the caller.

Control: Next we look at the business contexts, or decisions. (The business function information is stricken though).  These decisions will be implemented in the XML Microflow jobs descriptor.
  1. Take the users information as input.
  2. Call to a credit service to determine suitability for awarding an account.
    1. If the service is unavailable, return an appropriate error to the caller.
    2. If the service is unable to determine a score, put the account into the manual work queue.
    3. If the score is good, continue to step 3.
  3. Call to a service that verifies addresses
    1. If the address is unverifiable, put the account. into the manual work queue (2.2).
    2. If the address is verifiable, continue to step 4.
  4. Create the account.
    1. If the creation succeeds, you're done.  Congrats!
    2. If the creation fails, return an appropriate error to the caller.

Define the Core Functions for which we must code

For each piece of core function, we create a "MicroflowTask".
  1. Source of user data is irrelevant for this example...

  2. A module which takes user information, and calls to a credit service. (com.mybank.CreditServiceTask)
  3. A module which takes user information, and calls an address verification service. (com.mybank.AddressVerificationTask)
  4. A module which creates an account in our core banking system. (com.mybank.CreateAccountTask)
  5. A module which puts the account into the manual service queue. (com.mybank.AddToManualServiceQueue)

Implement the modules

package com.mybank;

import com.mybank.services.CreditService;
import org.openemcee.microflow.usercore.*; // Don't really import *

public class CreditServceTask implements MicroflowTask {

private int acceptableScore;

public MicroflowTaskOutcome executeTask(MicroflowData data)
{
CustomerAddress addr = (CustomerAddress)data.getAttribute("address");
// Delegate to the model
try {
long score = CreditService.getScore(addr);
data.setAttribute("credit_score", new Long(score));
return new MicroflowTaskOutcome("success");
} catch(CreditCalculationExcepton e) {
return new MicroflowTaskOutcome("calc_failed");
} catch(ServiceNotAvailableException e) {
return new MicroflowTaskOutcome("service_unavailable");
}
}


public boolean init(MicroflowParameters params) {
return true;
}

public CreditServceTask () {}
}
Sample Implementation of the CreditServiceTask

In the above implementation, notice that the code knows nothing about manual work queues, what happens next, etc...


Write the XML to configure the flow of control between the tasks

Next we create an XML file containing the schema and flow for our microflow.  It will be saved in c:\temp\myflows\create_acct.xml

Please read the XML comments IN ALL CAPS to help understand the file.

<?xml version="1.0"?>

<!DOCTYPE openemcee-microflow-descriptor PUBLIC "-//OpenEmcee Software//Microflow Job Definition 1.0//EN" "http://openemcee.org/microflow/dtds/openemcee-mfjd-1_0.dtd">
<job-definition identifier="SampleJob">
   <setup>
<!-- MAKE AN ENTRY HERE FOR EACH PIECE OF DATA THAT YOU WANT TO FLOW THOUGH THE SYSTEM -->
       <permissions>
          <data>
         <name>address</name>
         <type>com.mybank.data.CustomerAddress</type>
      </data>
      <data>
<name>credit_score</name>
<type>java.lang.Long</type>
      </data>
<data>
<name>account_id</name>
<type>java.lang.String</type>
      </data>
<data>
<name>added_to_manual_queue</name>
<type>java.lang.Boolean</type>
      </data>
       </permissions>
   </setup>
   <!-- THE JOB STARTS AT WHATEVER TASK IS DEFINED HERE -->
<first-task>CallCreditService</first-task>
   <task name="CallCreditService"
         jclass="com.mybank.CreditServiceTask">
         <outcome name="success"
                  nextTask="VerifyAddress"/>
         <outcome name="calc_failed"
                  nextTask="AddToManualWorkQueue"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="service_unavailable"
                  terminal="true"
                  failure="true"/>
   </task>

<task name="VerifyAddress"
         jclass="com.mybank.VerifyAddressTask">
         <outcome name="success"
                  nextTask="CreateAccount"/>
         <outcome name="calc_failed"
                  nextTask="AddToManualWorkQueue"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="default"
                  terminal="true"
                  failure="true"/>
   </task>

<task name="CreateAccount"
         jclass="com.mybank.CreateAccountTask"> <!-- We're done. Notice that we didn't fail. -->
         <outcome name="success"
                  terminal="true"/>
         <outcome name="calc_failed"
                  nextTask="AddToManualWorkQueue"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="default"
                  terminal="true"
                  failure="true"/>
   </task>


<task name="AddToManualWorkQueue"
         jclass="com.mybank.AddToManualServiceQueueTask">
         <outcome name="success"
                  terminal="true"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="default"
                  terminal="true"
                  failure="true"/>
   </task>
</job-definition>

Sample Implementation of the CreditServiceTask

Add the microflow hooks to your client software

Finally, we need to incorporate a call to the Microflow engine into our application (maybe its a web application, or a desktop application, or batch process taking input from a database or a JMS queue, etc...).

import org.openemcee.microflow.frontend.*;  // Never import *...
class .... {
public MicroflowFactory factory =  new MicroflowFactory("file:c:\\temp\\myflows\\");

public String createAccount(CustomerAddress addr)
     throws AddedToManualQueueException, XYZFailure
{
      MicroflowRunner runner = null;



            
      try {
//
// Get the Microflow runner (thing that executes
// the Microflow). The name here is your XML file
// minus the base URL used to create the MicroflowFactory
// and without the XML file extention. Here:
//
// c:\\temp\\myflows\\create_account.xml
//
// Note that this could easily have been from a web server
// an ftp server, a jar file, a jar file on a web server,
// "file t3", or anything which you can get a URL connection to.
// 
           runner = factory.getRunner("create_account");
//
// Expose the customer address to the user.
//      
runner.addAttribute("customer_address", addr);
//
// AND.... GO!!!
//
runner.run();
//
// Now we have finished. Return the resulting account ID to the caller.
//
return (String)runner.getAttribute("account_id");
      } catch(MicroflowRunner.BusinessException e) {
//
// The microflow terminated on a failure="true" outcome
//
// Lets find out if the account was added to the manual
// work queue... Data would have been set by the
// "AddToManualWorkQueueTask"
//
Boolean manualQueue = runner.getAttribute("added_to_manual_queue");
if(manualQueue != null && manualQueue == Boolean.TRUE) {
throw new AddedToManualQueueException();
} else {
throw new XYZFailure(e);
}
 } catch(MicroflowRunner.MicroflowException e) {
//
// Something failed in execution maybe an unchecked exception was thrown?
// Maybe an unknown class was in your XML?
//
throw new XYZFailure(e);
} catch(MicroflowFactory.MicroflowNotFoundException e) {
          // Your XML file wasn't found...  You need to handle this error.
throw new XYZFailure(e);
     
}


And now... The Business Emergency that we're all way too familiar with....

Time:  4:45 on a Friday afternoon, software is due on Monday.
Emergency:  Your business support folks finally understood what you were explaining to them about the system not making a decision based on credit score.  Sound familiar?  You need to add logic to add the account to the "manual processing queue" if the score is less than 400 while the business people go home at 5:00 mad at you because THEY didn't listen to the developers in requirements analysis.  Arghh...

So, what do we do?
  1. Go to your team and complain about the *@** users.  Regain composure.
  2. Create a task which returns a status of "ok" if it is 400+, or "score_too_low" if it is less.
  3. Update the XML to call the new task after the credit check, and before the address check.  Set the outcome for "score_too_low" to "AddToManualWorkQueue".
  4. Check it into version control.  Update the status in the defect tracker so it goes with the next build (You do have a defect tracker and automated build, right?)
  5. You're done.  AND, you didn't have to deal with the complexities of MODIFYING code.
Time: 7:30 you're home.  Not optimal, but much better than what it could have been had this been "traditional" code.

The changes we made are in bold.  I'll leave the code ("Task") part to your imagination.
   <task name="CallCreditService"
         jclass="com.mybank.CreditServiceTask">
         <outcome name="success"
                  nextTask="VerifyCreditScore"/>
         <outcome name="calc_failed"
                  nextTask="AddToManualWorkQueue"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="service_unavailable"
                  terminal="true"
                  failure="true"/>
   </task>

<task name="VerifyCreditScore"
         jclass="com.mybank.VerifyCreditScoreTask">
         <outcome name="ok"
                  nextTask="VerifyAddress"/>
         <outcome name="score_too_low"
                  nextTask="AddToManualWorkQueue"/>
<!-- USE A CONFIG PARAMETER RATHER THAN HARD-CODING THE VALUE
YOU KNOW THAT THE USERS WILL CHANGE THEIR MINDS -->
<params>
<param>
<name>score_threshold</name>
<value>400</value>
</param>
</params>
   </task>

<task name="CallCreditService"
jclass="com.mybank.CreditServiceTask">
<outcome name="success"
nextTask="VerifyAddress"/>
<outcome name="calc_failed"
nextTask="AddToManualWorkQueue"/>
<outcome name="service_unavailable"
terminal="true"
failure="true"/>
</task>


<task name="VerifyAddress"
         jclass="com.mybank.VerifyAddressTask">
         <outcome name="success"
                  nextTask="CreateAccount"/>
         <outcome name="calc_failed"
                  nextTask="AddToManualWorkQueue"/> <!-- Stop processing, call error handling hooks (none in this example) -->
<outcome name="default"
                  terminal="true"
                  failure="true"/>
   </task>

Many other features

The framework has many other features including:
  • Data Validation (Validation of Microflow data attributes on initial set and after each modification or access)
  • Error Handling (Rollbacks, or tasks which are called when a microflow fails, prior to returning to the caller)
  • Subflows (The ability to call a Microflow Job from another Microflow Job)

What we've learned

In this lesson, we've learned the basics of creating a Microflow:

  1. Deconstructing the requirements.
  2. Defining the components.
  3. Implementing the task modules.
  4. Writing the XML job definition.
  5. Invoking the Microflow from a client application.

You should now be prepared to create your first OpenEmcee Microflow.

Download a distribution and try it for yourself.

Click here for our download page.

Please let us know what you think about the product and this tutorial.

Click here to give your feedback. PLEASE don't forget to leave me your email so I can respond!

Thanks,

Scott Schenkein and the whole OpenEmcee Team


Any software or content on this or affiliated project site are released for your use under MPL 1.1 License.  Please see source code modules for complete license.


 
 

SourceForge.net Logo