`
astroxl
  • 浏览: 55069 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Implementing Finite State Machines with Physhun and Spring

阅读更多

原文: Implementing Finite State Machines with Physhun and Spring

http://www.theserverside.com/tt/articles/article.tss?l=FiniteStateMachinesPhyshunandSpring

Implementing Finite State Machines with Physhun and Spring


July 18, 2008

Discuss this Article

 

Abstract

When software is required to solve complex problems, the proper solutions are often beyond the capabilities of traditional programming techniques. A robust alternative for solving very complex problems is to use the Finite State Machine (FSM) paradigm for modeling the solution space and a FSM-based framework for the implementation. This paper presents the Physhun project, a Spring-based framework for implementing complex processes through Finite State Machine models. Physhun provides finite State Model persistence and transaction management with synchronous or asynchronous behavior. Although development with Physhun can be accomplished much more quickly and efficiently with Physhun Modeler, we will demonstrate development without Modeler to give visibility into the Physhun framework.

Finite State Machines and Process Control

Complex process logic is common in information systems. It can be found in software ranging from GUIs to Operational Support Systems. There are many concise ways of describing complex processes, the most common being flow charts and UML diagrams; however, implementation of complex processes may not always be as straightforward. Complex processes are often implemented as procedural logic which can become lengthy and difficult to manage as process rules change.

A complementary technology to procedural and object-oriented programming is the Finite State Machine paradigm. Finite State Machine (FSM) is a behavioral algorithm that can be used to model and execute complex logic. A finite state machine defines a process as a set of states (nodes) and state Transitions (edges). Implementation of a process with FSM technology involves laying out the valid states for the process, all Transitions between those states, Conditions dictating when Transitions are to be executed and Actions containing logic to be executed on Transition execution.

Finite state machine technology has been used for a number of years in a wide spectrum of industries. Successful projects using finite State Model technology include communication systems, automobiles, avionics systems, and man-machine interfaces. These problem domains share common characteristics; they are usually complex, large in size and reactive in nature. A primary challenge of these domains is the difficulty of describing reactive behavior in ways that are clear, concise, and complete while at the same time formal and rigorous.

Finite State Models provide a way to describe large, complex systems. Finite State Machines view these systems as a set of well defined states, inbound and outbound events, Conditions, and Actions. FSM technology provides a set of rules for evaluating and processing the events, Conditions, and Actions. The partitioning of the problem into the states, events, Conditions, and Actions, the structured processing environment, and the ease of expressing the processing logic are the foremost strengths of FSMs.

The fundamental components of finite State Models include:

State represents the “mode of being” for the system at any given time.

Transition describes a single pathway from a state to another state. The set of all Transitions describe all possible paths among the defined states. A Transition contains an event that it is subscribing to (the event that triggers execution of the Transition), a Condition and an Action. A Transition also contains references to the state from which the Transition is exiting (i.e. the “from” state) and the state to which the Transition is entering (i.e. the “to” state).

Event is the mechanism that the system uses to interact with external systems and with itself.

Condition represents a set of logic that evaluates to a Boolean result. A Condition is used to determine if a Transition is to be executed.

Action is the logic to be executed when a Transition is executed.

A diagram of the components of a typical FSM is shown below:

Illustration 1: State Model Core Components

Physhun Overview

Physhun is an open source framework for building and executing Finite State Machines in J2SE and J2EE environments. Although the framework is simple, it is powerful in that it allows processes to be long lived, persistent and transactional. Processes can be purely synchronous or asynchronous. Synchronous processes once started, will run to completion without any further interaction from outside systems or users. Asynchronous processes will have states that require input from external systems or users before the process will continue. Asynchronous behavior is common in workflow systems.

The Physhun framework leverages the Spring framework and exposes all of the value added services provided by Spring including Inversion of Control (IOC), Aspect-Oriented Programming (AOP) and transaction management. Physhun allows the usage of graphical editors such as Physhun Modeler for development of State Models. This allows for accelerated development, ease of maintenance and facilitated communication.

Process control with Physhun is accomplished by defining processes as State Models. A State Model is defined as a JavaBean comprised of the following components:

  • States (com.wazeegroup.physhun.framework.State)
  • Transitions (com.wazeegroup.physhun.framework.Transition)
  • Actions (com.wazeegroup.physhun.framework.Action)
  • Conditions (com.wazeegroup.physhun.framework.Condition)

A State model can be defined in one of several ways. The first way is to define the beans comprising the State Model in Java code. The second way is to define the State Model as XML that can be consumed by a bean factory (such as one of those provided by the Spring framework). The third way is to define the State Model graphically using an editor like Physhun Modeler. Defining the State Model graphically is appealing because it allows a complex process to be easily understood, communicated and maintained. In all three of these methods the elements comprising the State Model are defined and wired together as Spring beans and can be written to take advantage of Spring services.

The Physhun runtime paradigm is this: A process is defined by a process model. Multiple instances of a given process can be executed. Each process instance has a business object (ProcessObject) instance associated with it, which traverses a State Model. The data in the ProcessObject can be used to determine which paths in the State Model to take (via execution of Conditions). As the process executes, Actions in the State Model may manipulate the ProcessObject.

Physhun processes are instantiated and interacted with at runtime through a ProcessContainer. The ProcessContainer interface is shown in Table 1. It contains methods for starting processes and sending asynchronous events to existing processes. Executing Physun State Models is as simple as instantiating a ProcessObject and passing it to the Physun ProcessContainer instance.

package com.wazeegroup.physhun.engine;

import com.wazeegroup.physhun.framework.ProcessObject;
import com.wazeegroup.physhun.framework.StateModel;

public interface ProcessContainer {
    
    public void startProcess(ProcessObject processObject, StateModel stateModel);

    public ProcessObject sendTriggerEventToProcess(String processID, Object triggerEvent);
    
    public void resumeProcess(ProcessObject processObject, StateModel stateModel);

}

Table 1: ProcessContainer interface

The low-level rules for how State Models are executed are defined in the StateEngine. Generally, the developer does not need to interact with the StateEngine directly, but through the ProcessContainer which will proxy calls to the StateEngine appropriately. Physhun provides standard implementations for ProcessContainer and StateEngine, but the user may define and use different implementations as the need arises.

For long lived and mission critical processes, the Physhun library provides the ability to persist ProcessObjects, suspend and resume long lived processes and tie process execution into distributed transactions.

Example 1: Simple process control

In this example, we will build an overly simplified order process. The order process is this: Order received, check inventory, if the product is not in stock cancel the order. Otherwise, complete the order.

Illustration 2: Simple Example Process Model

The first step in the implementation is to define a ProcessObject. This is the object that will traverse the State Model during process execution. Other FSM technologies may refer to the object as a Business Object, Business Process Object (BPO) or Process Document. The ProcessObject implementation must implement the interface com.wazeegroup.Physhun.framework.IProcessObject. The ProcessObject for this example is defined by the class com.wazeegroup.physhun.examples.simple.Order, which is shown in Table 2.

package com.wazeegroup.physhun.examples.simple;

import com.wazeegroup.physhun.framework.ConcreteProcessObject;

public class Order extends ConcreteProcessObject {
    private String customerId;
    private String itemId;
    public int quantity;

    public Order(String customerId, String itemId, int quantity) {
        super(null);
        this.customerId = customerId;
        this.itemId = itemId;
        this.quantity = quantity;
        setID(toString());
    }

    public String toString() {
        return ("Order (" + customerId + "/" + itemId + "/" + quantity + " units)");
    }
}

Table 2: A simple ProcessObject implementation

Note that Order inherits from com.wazeegroup.Physhun.framework.ConcreteProcessObject. By inheriting from this concrete class, we are saved from the effort of implementing “plumbing” functionality such as tracking current state and other details allow the ProcessObject to function in the Physhun StateEngine.

The next step is to define the State Model for the order process. A visual representation of the model is shown in Illustration 2. The State Model is a spring bean made up of States and Transitions and is easily defined as an XML document. The XML defining the State Model is shown in Table 3. This XML document can be consumed by a Spring bean factory to inflate the State Model as Java objects. The State Model definition is lengthy, but it is straightforward and easy to understand, especially when viewed with a graphical editor.

Once the ProcessObject and State Model have been defined, any custom Actions and Conditions must be implemented. Conditions define the circumstances that must be met for a Transition from one state to another to occur. At runtime the StateEngine evaluates Conditions to determine what Transition (if any) to execute. Actions define the logic that is executed on a given Transition. The code for a simple Condition is shown in Table 4 and an Action in Table 5.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteState" 
			dependency-check="default" id="newOrder" lazy-init="default" singleton="true">
      <property name="initialState">
         <value>true</value>
      </property>
   </bean>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteState" 
			dependency-check="default" id="orderCancelled" lazy-init="default" singleton="true">
      <property name="terminationState">
         <value>true</value>
      </property>
   </bean>
      <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteState" 
			dependency-check="default" id="orderComplete" lazy-init="default" singleton="true">
      <property name="terminationState">
         <value>true</value>
      </property>
   </bean>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.examples.simple.CheckOrderAvailability" 
			dependency-check="default" id="CheckItemInventory" lazy-init="default" singleton="true"/>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.generic.condition.Default" 
			dependency-check="default" id="ConditionDefault" lazy-init="default" singleton="true"/>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.examples.simple.CancelOrder" 
			dependency-check="default" id="CancelOrder" lazy-init="default" singleton="true"/>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.examples.simple.CommitOrder" 
			dependency-check="default" id="CommitOrder" lazy-init="default" singleton="true"/>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteTransition" 
			dependency-check="default" id="newOrder-orderCancelled" lazy-init="default" singleton="true">
      <property name="condition">
         <ref bean="ConditionDefault"/>
      </property>
      <property name="action">
         <ref bean="CancelOrder"/>
      </property>
      <property name="fromState">
         <ref bean="newOrder"/>
      </property>
      <property name="toState">
         <ref bean="orderCancelled"/>
      </property>
   </bean>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteTransition" 
			dependency-check="default" id="newOrder-orderComplete" lazy-init="default" singleton="true">
      <property name="condition">
         <ref bean="CheckItemInventory"/>
      </property>
      <property name="action">
         <ref bean="CommitOrder"/>
      </property>
      <property name="fromState">
         <ref bean="newOrder"/>
      </property>
      <property name="toState">
         <ref bean="orderComplete"/>
      </property>
   </bean>
   <bean class="com.wazeegroup.physhun.framework.ConcreteStateModel" id="stateModel">
      <property name="states">
         <list>
            <ref bean="newOrder"/>
            <ref bean="orderCancelled"/>
            <ref bean="orderComplete"/>
         </list>
      </property>
      <property name="transitions">
         <list>
            <ref bean="newOrder-orderCancelled"/>
            <ref bean="newOrder-orderComplete"/>
         </list>
      </property>
   </bean>
</beans>

Table 3: State Model XML

package com.wazeegroup.physhun.examples.simple;

import com.wazeegroup.physhun.framework.AbstractCondition;
import com.wazeegroup.physhun.framework.ProcessObject;

import java.util.Random;

public class CheckOrderAvailability extends AbstractCondition {

    private static final Random _random = new Random();

    public boolean evaluate(ProcessObject iProcessObject) {
        boolean result = _random.nextBoolean();
        System.out.println("Order Availability for Order (" + iProcessObject + "): " + result);
        return result;
    }
}

Table 4: A simple Condition implementation

package com.wazeegroup.physhun.examples.simple;

import com.wazeegroup.physhun.framework.AbstractAction;
import com.wazeegroup.physhun.framework.ProcessObject;

public class CommitOrder extends AbstractAction {
    public void execute(ProcessObject iProcessObject) {
        System.out.println(iProcessObject + " - completed");
    }
}

Table 5: A simple Action implementation

Now that the State Model, ProcessObject, Actions and Conditions have been defined, the final step is to load and execute the process. To execute the process, two additional components are needed: A StateEngine and a ProcessContainer. The StateEngine is the component that contains the rules on how State Models executed, and the ProcessContainer is the component that keeps track of process instances and sends events to those instances. The default StateEngine is com.wazeegroup.Physhun.engine.StandardEngine, and the default ProcessContainer is com.wazeegroup.physhun.SimpleProcessContainer. The ProcessContainer and StateEngine are configured by standard Spring configuration as shown in Table 6. The code used to execute the example is shown in Table 7.

<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean id="engine" class="com.wazeegroup.physhun.engine.StandardEngine" />
    <bean id="container" class="com.wazeegroup.physhun.engine.SimpleProcessContainer">
        <property name="processEngine">
            <ref bean="engine"/>
        </property>
    </bean>
</beans>

Table 6: Sample ProcessContainer configuration

package com.wazeegroup.physhun.examples.simple;

import com.wazeegroup.physhun.engine.ProcessContainer;
import com.wazeegroup.physhun.framework.StateModel;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RunSample {
    public static void main(String[] args) {
        if (args.length < 3) {
            System.err.println("usage: RunSample custID itemID numItems");
        } else {
            Order order = new Order(args[0], args[1], Integer.parseInt(args[2]));
            //Get the Spring ApplicationContext
            String[] beanFiles = new String[]{"simple-processConfig.xml", "simple-stateModel.xml"};
            ClassPathXmlApplicationContext ctx =
                    new ClassPathXmlApplicationContext(beanFiles);
            //inflate and retrieve the ProcessContainer bean.
            ProcessContainer container = (ProcessContainer) ctx.getBean("ProcessContainer");
            //inflate and retrieve the stateModel bean.
            StateModel stateModel = (StateModel) ctx.getBean("stateModel");  
            container.startProcess(order, stateModel);
        }
    }
}

Table 7: Execution of the State Model

Example 2: Long Lived Processes and Asynchronous Events

Our first example showed a very simple order management process. In that example, the process is short lived and synchronous – as soon as the process is started, it runs to completion without dependence on incoming events. The second example is similar to the first, but with added asynchronous behavior – in this example, the process waits for and reacts to asynchronous events indicating changes to inventory. When the order is created, if stock is available to fill the order, the order completes normally. Otherwise, the process waits until it receives notification that the appropriate inventory is available at which point the process is completed. The new State Model is shown in Illustration 3.

Illustration 3: Asynchronous process State Model

The Transitions from the waitForStock state are triggered by an external event – in this case, notification that inventory changes have occurred. The Transition from waitForStock to orderComplete occurs if the inventory system notifies the process that stock is now available. The waitForStock self-Transition occurs if an inventory change occurs that does not result in appropriate stock being available. In order to define an “asynchronous” Transition, we associate a TriggeredCondition with the Transition. A TriggeredCondition implements the interface com.wazeegroup.physhun.framework.TriggeredCondition and is very similar to Condition, the difference being that the evaluate() method takes an additional Object which is the asynchronous event that drives the Transition. The TriggeredCondition implementation for this example is shown in Table 8 , and the Condition and Transition definitions from the State Model XML are shown in Table 9.

package com.wazeegroup.physhun.examples.asynch;

import com.wazeegroup.physhun.framework.AbstractTriggeredCondition;
import com.wazeegroup.physhun.framework.ProcessObject;

public class InventoryChanged_orderStockAvailable extends AbstractTriggeredCondition {
    public boolean evaluate(ProcessObject processObject, Object triggerEvent) {
        InventoryChangeEvent evt = (InventoryChangeEvent)triggerEvent;
        System.out.println("Received inventory change event (" + evt.itemID + "/" + evt.newQuantity + " units)");
        Order order = (Order)processObject;
        if (evt.itemID.equals(order.getItemId()) && evt.newQuantity >= order.getQuantity()) {
            //the item we are waiting on is now in stock.
            System.out.println("Item we are waiting for is now in stock.  Condition evaluates to true!");
            return true;
        }
        else {
            //We still don't have enough stock to fill the order
            System.out.println("Inventory we are waiting for 
            		(" + order.getQuantity() + " units of " + order.getItemId() + ") 
					is still out of stock");
            return false;
        }
    }
}

Table 8: TriggeredCondition implementation

<bean abstract="false" autowire="default" class="com.wazeegroup.physhun.generic.condition.TriggeredDefault" 
			dependency-check="default" id="ConditionDefaultTriggered" lazy-init="default" singleton="true"/>

   <bean abstract="false" autowire="default" 
			class="com.wazeegroup.physhun.examples.asynch.InventoryChanged_orderStockAvailable" 
			dependency-check="default" id="InventoryNowAvailable" lazy-init="default" singleton="true"/>

<bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteTransition" 
			dependency-check="default" id="newOrder-orderComplete" lazy-init="default" singleton="true">
      <property name="condition">
         <ref bean="CheckItemInventory"/>
      </property>
      <property name="action">
         <ref bean="CommitOrder"/>
      </property>
      <property name="fromState">
         <ref bean="newOrder"/>
      </property>
      <property name="toState">
         <ref bean="orderComplete"/>
      </property>
   </bean>
   <bean abstract="false" autowire="default" class="com.wazeegroup.physhun.framework.ConcreteTransition" 
			dependency-check="default" id="newOrder-waitForStock" lazy-init="default" singleton="true">
      <property name="condition">
         <ref bean="ConditionDefault"/>
      </property>
      <property name="fromState">
         <ref bean="newOrder"/>
      </property>
      <property name="toState">
         <ref bean="waitForStock"/>
      </property>
   </bean>

Table 9: Asynchronous Transition and Condition State Model definition

In this example, the asynchronous event that drives Transitions from the waitForStock state are notifications of changes to inventory. In an enterprise system, these may be anything from calls to a Web Service to events published on a JMS destination, event queue or other Enterprise Message Bus.

The final step is to bridge the asynchronous events into the running process. This is done via the ProcessContainer by calling the method sendTriggerEventToProcess(). In our example, we mock up inventory change events by spawning a thread that sends mocked up inventory change events to the ProcessContainer. The code used to execute this example and send asynchronous events to the process instances is shown in Table 10.

package com.wazeegroup.physhun.examples.asynch;

import com.wazeegroup.physhun.engine.ProcessContainer;
import com.wazeegroup.physhun.framework.StateModel;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Random;

public class RunSample {
    public static void main(String[] args) {
        if (args.length < 3) {
            System.err.println("usage: RunSample custID itemID numItems");
        } else {
            Order order = new Order(args[0], args[1], Integer.parseInt(args[2]));
            ClassPathXmlApplicationContext ctx = new 
            		ClassPathXmlApplicationContext(new String[]{"asynch-processConfig.xml", 
					"asynch-stateModel.xml"});
            ProcessContainer container = (ProcessContainer) ctx.getBean("ProcessContainer");  
					//inflate and retrieve the ProcessContainer bean.
            StateModel stateModel = (StateModel) ctx.getBean("stateModel");  
					//inflate and retrieve the stateModel bean.
            InventorySystemStub stub = new InventorySystemStub();
            stub.container = container;
            stub.orderID = order.getID();
            stub.start();
            container.startProcess(order, stateModel);
            while (! stateModel.isProcessComplete(order)) {
                try { Thread.sleep(50); } catch (InterruptedException ie) {}
            }
            stub.finished = true;
            stub.interrupt();
        }
    }

}

class InventorySystemStub extends Thread {
    public boolean finished = false;
    public String orderID;
    public ProcessContainer container;
    public String[] itemIDs = new String[] {"RootBeer", "CremeSoda", "Lollipops", "LicorishWhips"};

    public void run() {
        Random random = new Random();
        try { Thread.sleep(5000); } catch (InterruptedException ie) {}
        while (!finished) {
            InventoryChangeEvent event = new InventoryChangeEvent();
            event.itemID = itemIDs[random.nextInt(itemIDs.length)];
            event.newQuantity = random.nextInt(100);
            container.sendTriggerEventToProcess(orderID, event);
            try { Thread.sleep(5000); } catch (InterruptedException ie) {}
        }
    }
}

Table 10: Execution of the Asynchronous State Model

Example 3: Distributed Transactions and ProcessObject Persistence

In the final example, we add a distributed transaction and ProcessObject persistence to our process. ProcessObject persistence writes the state of the process to a datastore. This is important for two reasons: it allows data to be restored in the event of a system failure, and it allows the construction large scale systems. Systems can run with many concurrent processes without keeping all Process Instances in memory at all times.

The process in this example is identical to the process in Example 2, but we change the commitOrder Action (which is the Action associated with the Transition from waitForStock to orderComplete) to do the following: reserve inventory in the inventory system, enter the order in the billing system and enter the order in the provisioning system. All three of these steps are executed against different systems, but must be done as a single transaction. For this example, our interface to each of the target systems is through the database. To accomplish this distributed transaction, we must do three things in our implementation:

  1. Code individual Actions to work with a common transaction manager. To accomplish this, we wire an appropriately configured DataSource object in to each Action, and modify the Actions to acquire DB connectivity through the DataSource.
  2. Configure a Spring transaction manager and wire it into the StateEngine and DataSource beans.
  3. Define a composite Action that aggregates the individual Actions that make up the distributed transaction.

The XML for the composite Action is shown in Table 11 and a representative Action class implementation is shown in Table 12. Note that each of the Actions use a DataSource. The DataSources are standard Spring JDBC DataSources with a common TransactionManager wired in. That same TrasnactionManager is also wired in to the Physhun StateEngine. The code in the Actions is simple; it uses the DataSource to aquire a DB connection, through which it manipulates data.

<bean class="com.wazeegroup.physhun.generic.action.Composite" id="CommitOrder">
      <property name="actions">
         <list>
            <bean class="com.wazeegroup.physhun.examples.transactional.ReserveInventory" 
					id="reserveInventory">
               <property name="dataSource">
                  <ref bean="inventoryDataSource"/>
               </property>
            </bean>
            <bean class="com.wazeegroup.physhun.examples.transactional.BillOrder" 
					id="billOrder">
               <property name="dataSource">
                  <ref bean="billingDataSource"/>
               </property>
            </bean>
            <bean class="com.wazeegroup.physhun.examples.transactional.SendOrderToWarehouse" 
					id="sendToWarehouse">
               <property name="dataSource">
                  <ref bean="warehouseDataSource"/>
               </property>
            </bean>
         </list>
      </property>
   </bean>

Table 11: Composite Action definition for a distributed transaction

package com.wazeegroup.physhun.examples.transactional;

import com.wazeegroup.physhun.framework.ProcessObject;
import com.wazeegroup.physhun.framework.PhyshunException;
import com.wazeegroup.physhun.framework.AbstractAction;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.Types;

public class ReserveInventory extends AbstractAction {

    private DataSource dataSource;
    private JdbcTemplate _jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        _jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void execute(ProcessObject processObject) {
        try {
            Order order = (Order)processObject;
            _jdbcTemplate.update("update inventory set quantity = quantity-? where item_id = ?",
                    new Object[]{new Integer(order.getQuantity()), order.getItemId()},
                    new int[]{Types.INTEGER, Types.VARCHAR});
        } catch (Exception e) {
            throw new PhyshunException("Error during execute", e);
        }
    }
}

Table 12: Implementation of Action class that is used in a distributed transaction

Note that ProcessObject persistence does not necessarily have to be implemented from scratch. The PhyshunXML package provides a ProcessObject implementation whose data is all stored internally in an XML Document. This package also provides persistence functionality to Oracle and MySQL databases. If desired, implementation of custom persistence is straightforward. For the sake of illustration this example demonstrates implementation of custom ProcessObject and persistence functionality.

To add ProcessObject persistence, we must do two things:

  1. Define a ProcessObjectPersistence class. This is where we define how to store and retrieve the ProcessObject to and from the persistence layer.
  2. Wire the ProcessObjectPersistence class defined in step 1 into the StateEngine.

When the StateEngine has a ProcessObjectPersistence object wired in, it will persist the ProcessObject on execution of each state Transition. Furthermore, if the StateEngine has a Transaction Manager wired in, it will persist the ProcessObject as part of the state Transition transaction. This transaction includes persistence of the ProcessObject and execution of any Actions associated with the Transition The example ProcessObjectPersistence class is shown in Table 13 and the Container and StateEngine XML are shown in Table 14.

Note that the StateEngine and DataSources for Billings, Warehouse, Inventory and Orders (ProcessObject persistence store) databases all use the same TransactionManager. Because the beans are wired in this manner, when the StateEngine executes a Transition, persistence of the ProcessObject and Actions on the transition will be executed on the same transaction. So, if a single Action, or persistence of the ProcessObject fails, the entire transaction will fail as a whole and no data will be written to any of the data sources. The Transition will be rolled back and the process will roll back to the state from which the Transition occurred.

package com.wazeegroup.physhun.examples.transactional;

import com.wazeegroup.physhun.framework.ProcessObjectPersistenceSupport;
import com.wazeegroup.physhun.framework.ProcessObject;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import java.sql.Types;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

public class OrderPersistenceSupport implements ProcessObjectPersistenceSupport {

    private DataSource dataSource;
    private JdbcTemplate _jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        _jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void persistState(ProcessObject obj) {
        Order order = (Order)obj;
        Address shipTo = order.getShippingAddress();
        _jdbcTemplate.update("delete from orders where order_id = ?", new Object[] { order.getID() },
                new int[] { Types.VARCHAR });
        _jdbcTemplate.update("insert into orders values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 
				new Object[] { order.getID(),
                   order.getActiveStates()[0], order.getItemId(), new Integer(order.getQuantity()),
                   order.getCustomerId(), shipTo.getStreet1(), shipTo.getStreet2(), shipTo.getCity(), 
				       shipTo.getState(), shipTo.getZip() },
               new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.VARCHAR, 
				       Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR });
    }

    public ProcessObject getPersistedObject(final String id) {
        final ArrayList<Order> resultsList = new ArrayList<Order>();
        _jdbcTemplate.query("select item_id, process_state, quantity, customer_id, shipping_street1, 
			     shipping_street2, " + "shipping_city, shipping_state, shipping_zip from orders where 
			     order_id = ?", new Object[] {id},
                new int[] { Types.VARCHAR },
                new RowCallbackHandler() {
                    public void processRow(ResultSet resultSet) throws SQLException {
                        Order order = new Order(null);
                        order.setID(id);
                        order.setCustomerId(resultSet.getString("customer_id"));
                        order.setItemId(resultSet.getString("item_id"));
                        order.setQuantity(resultSet.getInt("quantity"));
                        Address addr = new Address();
                        addr.setStreet1(resultSet.getString("shipping_street1"));
                        addr.setStreet2(resultSet.getString("shipping_street2"));
                        addr.setCity(resultSet.getString("shipping_city"));
                        addr.setState(resultSet.getString("shipping_state"));
                        addr.setZip(resultSet.getString("shipping_zip"));
                        order.setShippingAddress(addr);
                        String activeState = resultSet.getString("process_state");
                        order.setStateActive(activeState);
                        resultsList.add(order);
                    }
                });
        if (resultsList.size() < 1) {
            System.err.println("Order (" + id + ") not found in DB");
            return null;
        }
        else if (resultsList.size() == 1) {
            return resultsList.get(0);
        }
        else {
            throw new RuntimeException("Multiple orders with id (" + id + ") found in DB");
        }
    }

    public ProcessObject[] getPersistedObjects() {
        //not used in this example, but normally this should be implemented.
        return null;
    }
}

Table 13: ProcessObjectPersistenceSupport implementation

<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="propertyConfigurator" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="jdbc.properties"/>
    </bean>
    <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
    <bean id="engineTxManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="userTransaction">
            <ref local="jotm"/>
        </property>
    </bean>
    <bean id="inventoryDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
    		destroy-method="shutdown">
        <property name="dataSource">
            <bean id="inv_innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" 
			      destroy-method="shutdown">
                <property name="transactionManager">
                    <ref local="jotm"/>
                </property>
                <property name="driverName">
                    <value>${inventory.driver}</value>
                </property>
                <property name="url">
                    <value>${inventory.url}</value>
                </property>
            </bean>
        </property>
        <property name="user">
            <value>${inventory.username}</value>
        </property>
        <property name="password">
            <value>${inventory.password}</value>
        </property>
        <property name="maxSize">
            <value>2</value>
        </property>
    </bean>
    <bean id="billingDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
				destroy-method="shutdown">
        <property name="dataSource">
            <bean id="bill_innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" 
			      destroy-method="shutdown">
                <property name="transactionManager">
                    <ref local="jotm"/>
                </property>
                <property name="driverName">
                    <value>${billing.driver}</value>
                </property>
                <property name="url">
                    <value>${billing.url}</value>
                </property>
            </bean>
        </property>
        <property name="user">
            <value>${billing.username}</value>
        </property>
        <property name="password">
            <value>${billing.password}</value>
        </property>
        <property name="maxSize">
            <value>2</value>
        </property>
    </bean>
    <bean id="warehouseDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
		      destroy-method="shutdown">
        <property name="dataSource">
            <bean id="warehouse_innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" 
			      destroy-method="shutdown">
                <property name="transactionManager">
                    <ref local="jotm"/>
                </property>
                <property name="driverName">
                    <value>${warehouse.driver}</value>
                </property>
                <property name="url">
                    <value>${warehouse.url}</value>
                </property>
            </bean>
        </property>
        <property name="user">
            <value>${warehouse.username}</value>
        </property>
        <property name="password">
            <value>${warehouse.password}</value>
        </property>
        <property name="maxSize">
            <value>2</value>
        </property>
    </bean>
    <bean id="ordersDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" 
          destroy-method="shutdown">
        <property name="dataSource">
            <bean id="orders_innerDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" 
			     destroy-method="shutdown">
                <property name="transactionManager">
                    <ref local="jotm"/>
                </property>
                <property name="driverName">
                    <value>${orders.driver}</value>
                </property>
                <property name="url">
                    <value>${orders.url}</value>
                </property>
            </bean>
        </property>
        <property name="user">
            <value>${orders.username}</value>
        </property>
        <property name="password">
            <value>${orders.password}</value>
        </property>
        <property name="maxSize">
            <value>2</value>
        </property>
    </bean>
    <bean id="engine" class="com.wazeegroup.physhun.engine.StandardEngine">
        <property name="transactionManager">
            <ref bean="engineTxManager"/>
        </property>
        <property name="persistenceSupport">
            <bean id="OrderPersistence" 
            	class="com.wazeegroup.physhun.examples.transactional.OrderPersistenceSupport">
                <property name="dataSource">
                    <ref bean="ordersDataSource"/>
                </property>
            </bean>
        </property>
    </bean>
    <bean id="ProcessContainer" class="com.wazeegroup.physhun.engine.SimpleProcessContainer">
        <property name="processEngine">
            <ref bean="engine"/>
        </property>
    </bean>
</beans>

Table 14: XML definitions for StateEngine and supporting beans

Conclusion

Finite State Machine technology offers a useful, alternative paradigm to implementing complex logic and process flow. The Physhun project is an open source framework that enables rapid development of systems using Finite State Machine technology, while leveraging and exposing the power and flexibility of the Spring Framework. More information about the Physhun project, including examples, source code and binaries can be found on the Physhun project homepage at http://physhun.sourceforge.net .

分享到:
评论

相关推荐

    基于 OpenCV 的魔兽世界钓鱼机器人

    基于 OpenCV 的魔兽世界钓鱼机器人

    供应链管理中信息共享问题的研究.docx

    供应链管理中信息共享问题的研究

    青春文学中的爱情观呈现.doc

    青春文学中的爱情观呈现

    分布式光伏储能系统的优化配置方法 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    Delphi 12.3 控件之XLSReadWriteII6.02.01.7z

    XLSReadWriteII6.02.01.7z

    图解系统-小林coding-v1.0.rar

    图解系统-小林coding-v1.0

    【光伏功率预测】基于EMD-PCA-LSTM的光伏功率预测模型 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    漫画作品与乌托邦理想追求.doc

    漫画作品与乌托邦理想追求

    江苏建筑消防设施维护保养规程.rar

    江苏建筑消防设施维护保养规程.rar

    基于交互式可视化的Transformer模型注意机制探索工具-DODRIO及其应用

    内容概要:论文介绍了一款名为DODRIO的交互式可视化工具,帮助自然语言处理(NLP)研究人员和从业者解析基于转换器架构的语言模型内部工作机理。DODRIO整合了概述图与详尽视图,支持用户比较注意力权重与其输入文本的句法结构和语义特征。具体而言,它包含了依赖关系视图(Dependency View)、语义关注图(Semantic Attention Graph)以及注意力头概览(Attention Head Overview),并利用不同的图形展示方法使复杂的多层多头转换器模型中的注意力模式更容易理解和研究。 适用人群:适用于从事深度学习、自然语言处理的研究人员和技术从业者;尤其适合对基于变换器架构的大规模预训练语言模型感兴趣的开发者们。 使用场景及目标:DODRIO用于探索转换器模型各层级之间的联系、验证已有研究成果,同时激发新假设形成。具体使用时可以选择特定数据集中的句子作为样本输入,观察不同注意力机制如何响应文本内容的变化。此外,还可以用来对比精简版本DistilBERT的表现,评估其相对全量模型BERT的优势与不足。 其他说明:DODRIO为开源项目,提供web端实施方式,使得

    基于机器学习的疾病数据集分析

    该代码使用scikit-learn的乳腺癌数据集,完成分类模型训练与评估全流程。主要功能包括:数据标准化、三类模型(逻辑回归、随机森林、SVM)的训练、模型性能评估(分类报告、混淆矩阵、ROC曲线)、随机森林特征重要性分析及学习曲线可视化。通过`train_test_split`划分数据集,`StandardScaler`标准化特征,循环遍历模型进行统一训练和评估。关键实现细节包含:利用`classification_report`输出精确度/召回率等指标,绘制混淆矩阵和ROC曲线量化模型效果,随机森林的特征重要性通过柱状图展示,学习曲线分析模型随训练样本变化的拟合趋势。最终将原始数据和预测结果保存为CSV文件,便于后续分析,并通过matplotlib进行多维度可视化比较。代码结构清晰,实现了数据处理、模型训练、评估与可视化的整合,适用于乳腺癌分类任务的多模型对比分析。

    数字化智慧园区建设实施PPT(43页).pptx

    在智慧城市建设的大潮中,智慧园区作为其中的璀璨明珠,正以其独特的魅力引领着产业园区的新一轮变革。想象一下,一个集绿色、高端、智能、创新于一体的未来园区,它不仅融合了科技研发、商业居住、办公文创等多种功能,更通过深度应用信息技术,实现了从传统到智慧的华丽转身。 智慧园区通过“四化”建设——即园区运营精细化、园区体验智能化、园区服务专业化和园区设施信息化,彻底颠覆了传统园区的管理模式。在这里,基础设施的数据收集与分析让管理变得更加主动和高效,从温湿度监控到烟雾报警,从消防水箱液位监测到消防栓防盗水装置,每一处细节都彰显着智能的力量。而远程抄表、空调和变配电的智能化管控,更是在节能降耗的同时,极大地提升了园区的运维效率。更令人兴奋的是,通过智慧监控、人流统计和自动访客系统等高科技手段,园区的安全防范能力得到了质的飞跃,让每一位入驻企业和个人都能享受到“拎包入住”般的便捷与安心。 更令人瞩目的是,智慧园区还构建了集信息服务、企业服务、物业服务于一体的综合服务体系。无论是通过园区门户进行信息查询、投诉反馈,还是享受便捷的电商服务、法律咨询和融资支持,亦或是利用云ERP和云OA系统提升企业的管理水平和运营效率,智慧园区都以其全面、专业、高效的服务,为企业的发展插上了腾飞的翅膀。而这一切的背后,是大数据、云计算、人工智能等前沿技术的深度融合与应用,它们如同智慧的大脑,让园区的管理和服务变得更加聪明、更加贴心。走进智慧园区,就像踏入了一个充满无限可能的未来世界,这里不仅有科技的魅力,更有生活的温度,让人不禁对未来充满了无限的憧憬与期待。

    Matlab实现BO贝叶斯优化-Transformer-GRU多特征分类预测的详细项目实例(含完整的程序,GUI设计和代码详解)

    内容概要:本文档介绍了基于MATLAB实现的贝叶斯优化(BO)、Transformer和GRU相结合的多特征分类预测项目实例,涵盖了详细的程序设计思路和具体代码实现。项目旨在应对数据的多样性与复杂性,提供一种更高效的多特征数据分类解决方案。文档主要内容包括:项目背景与意义,技术难点与解决方案,具体的实施流程如数据处理、模型构建与优化、超参数调优、性能评估以及精美的GUI设计;详细说明了Transformer和GRU在多特征数据分类中的应用及其与贝叶斯优化的有效结合,强调了其理论与实际应用中的价值。 适合人群:具备一定机器学习和MATLAB编程基础的研发人员,特别是从事多维数据处理与预测工作的专业人士和技术爱好者。 使用场景及目标:① 适用于金融、医疗、交通等行业,进行复杂的多维数据处理和预测任务;② 提升现有分类任务中复杂数据处理的准确度和效率,为各行业提供智能预测工具,如金融市场预测、患者病情发展跟踪、交通流量管理等。 其他说明:本文档包含了丰富的实战案例和技术细节,不仅限于模型设计本身,还涉及到数据清洗、模型优化等方面的知识,帮助使用者深入理解每一步骤背后的原理与实现方法。通过完整的代码样例和GUI界面设计指导,读者可以从头到尾跟随文档搭建起一套成熟的分类预测系统。

    Hive sql练习题,只是参考作用

    大数据的sql练习题,初级中级高级

    基于自注意力机制的序列转换模型-Transformer的提出及其应用

    内容概要:论文介绍了名为Transformer的新网络架构,它完全基于自注意力机制,在不使用递归或卷积神经网络的情况下建模输入与输出之间的全局依赖关系,尤其适用于长文本处理。通过多头自注意力层和平行化的全连接前馈网络,使得在机器翻译任务上的表现优于当时最佳模型。具体地,作者用此方法实现了对英语-德语和英语-法语翻译、句法解析等任务的高度并行化计算,并取得显著效果。在实验方面,Transformer在较短训练时间内获得了高质量的翻译结果以及新的单一模型基准。除此之外,研究人员还探索了模型变体的效果及其对于不同参数变化时性能的变化。 适用人群:从事自然语言处理领域的研究者、工程师、学生,熟悉深度学习概念尤其是编码器-解码器模型以及关注模型创新的人士。 使用场景及目标:主要适用于序列到序列(seq2seq)转换任务如机器翻译、语法分析、阅读理解和总结等任务的研究和技术开发;目标在于提高计算效率、缩短训练时间的同时确保模型性能达到或超过现有技术。 其他说明:本文不仅提出了一个新的模型思路,更重要的是展示了自注意力机制相较于传统LSTM或其他方式所拥有的优势,例如更好地捕捉远距离上下文关系的能力

    【故障诊断】一种滚动体轴承或齿轮的重复瞬态提取方法研究 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    用于平抑可再生能源功率波动的储能电站建模及评价 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    使用 KTH 数据集进行人类行为识别 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    【深度学习】基于计算机视觉的自动驾驶应用 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    自己写的远控木马,欢迎各位大佬改善

    自己写的远控木马,欢迎各位大佬改善

Global site tag (gtag.js) - Google Analytics