Piping the Machine: PureMVC Multicore with Pipes and the Finite State Machine (FSM)

This is going to be a walkthrough of making use of PureMVC Multicore (AS3). To help in building a PureMVC Multicore application, we are going to make use of the StateMachine utility for initial setup and configuration as well as the Pipes utility for communication between cores.

Here’s the Source.

Get Adobe Flash player


Overview

PureMVC can seem a bit overwhelming at first, but once the core concepts are understood the simplicity of the framework is possibly its biggest strength. Developing multicore applications in PureMVC, that is applications that create multiple instances of the PureMVC core actors (Facade, Model, View, Controller), adds another level of complexity that can be daunting. Luckily, there are utilities to help us out here, and abstract some of the problems with communication between modules loaded into an application.

This tutorial is based on the Sea of Arrows player put together by Cliff Hall. My goal was to remove some of the complexity involved with a real application and deconstruct the blocks used to build the player in a form that is easier to digest. It is highly recommended that you check out the player for a further example of this particular application structure.

For the purposes of demonstration this application is kept as simple as possible. For a trivial project, this level of complexity in regards to multiple cores, dynamically loaded modules, a finite state machine, and a plumbed framework for components to communicate is over-engineered (at best). It seems like a lot of work, and it is, but the payoff is when you have a complex project with multiple developers and designers working across many modules. This structure is made to scale, and scale well providing many options for efficient development. It allows for finely grained control of all of the actors in your system.

The Tools

Here is a brief overview of the tools we are going to use, and then we will get into some code and look at a simple example of everything put together.

PureMVC

From the website:

PureMVC is a lightweight framework for creating applications based upon the classic Model, View and Controller concept.

Based upon proven design patterns, this free, open source framework which was originally implemented in the ActionScript 3 language for use with Adobe Flex, Flash and AIR, is now being ported to all major development platforms.

If you want a tutorial for basic PureMVC usage, click here.

Pipes

Pipes is a drop in utility for multicore PureMVC applications that utilizes a plumbing metaphor to facilitate communication between cores. If you’ve ever walked down the PVC aisle of your local mega-hardware store, you are familiar with the objects represented in the utility.

pipes-diagram

The above diagram comes from the excellent Pipes overview written by Joshua Ostrom.

StateMachine

The StateMachine utility is a finite state machine for controlling application state in PureMVC. Configured by XML it seems daunting at first, but is relatively simple with only a handful of classes in the utility to make things happen. It makes for an elegant way to step through logical progressions and control available actions inside your application. This example is utilizing the StateMachine for the initial configuration, but it is possible to create complex workflows with the utility.

Check out the FSMVisualizer for a really cool way to look at the PureMVC StateMachine.

The Code

The Shell

The shell of the application is the root container that will be in charge of creating instances of the modules and displaying their visual components. In this Flex example of the shell, the main MXML file serves as the viewComponent of the first PureMVC core that will be created.

The application is started like a typical PureMVC application. The difference here is with the naming convention being used. Normally in an application that will make use of a single PureMVC core will will create an ApplicationFacade. In this multicore application we are going to instead name the facade ShellFacade.

The ShellFacade class contains our notification constants. In this case it also cotains constants related to the StateMachine utility that define the states, their actions, and the notifications associated with those actions.

The StateMachine is initialized in our StartupCommand, which is a typical MacroCommand found in most PureMVC applications. The first Command run is the InjectFSMCommand which initializes the StateMachine and provides it with the XML configuration defining the various states. The next Command issued is the StartShellCommand, which is our initial state as configured by the StateMachine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var fsm:XML =
<fsm initial={ShellFacade.STARTING}>
 
	<!-- STARTUP THE SHELL -->
	<state name={ShellFacade.STARTING}>
 
       <transition action={ShellFacade.STARTED}
       			   target={ShellFacade.PLUMBING}/>
 
       <transition action={ShellFacade.STARTUP_FAILED}
       			   target={ShellFacade.FAILING}/>
	</state>
 
	<!-- PLUMB THE CORES -->
	<state name={ShellFacade.PLUMBING} changed={ShellFacade.PLUMB}>
 
       <transition action={ShellFacade.PLUMBED}
       			   target={ShellFacade.ASSEMBLING}/>
 
       <transition action={ShellFacade.PLUMB_FAILED}
       			   target={ShellFacade.FAILING}/>
 
	</state>
 
	<!-- ASSEMBLE THE VIEW -->
	<state name={ShellFacade.ASSEMBLING} changed={ShellFacade.ASSEMBLE}>
 
       <transition action={ShellFacade.ASSEMBLED}
       			   target={ShellFacade.NAVIGATING}/>
 
       <transition action={ShellFacade.ASSEMBLY_FAILED}
       			   target={ShellFacade.FAILING}/>
 
	</state>
 
	<!-- READY TO ACCEPT BROWSER OR USER NAVIGATION -->
	<state name={ShellFacade.NAVIGATING} changed={ShellFacade.NAVIGATE}/>
 
	<!-- REPORT FAILURE FROM ANY STATE -->
	<state name={ShellFacade.FAILING} changed={ShellFacade.FAIL}/>
 
</fsm>;

StateMachine states are defined by their name, a changed property, and transitions. Transitions define an action that triggers the move to a new state, and a target which defines the name of the state in which to move when the action notification is received by the StateMachine. The changed property of a state corresponds directly to the Command defined in the ShellFacade. When the StateMachine receives a notification to move to a new state, the Command defined in the changed property is executed.

pipingthemachinestatediagram

We start off in the Starting state which simply mediates the shell application. From there we move to the Plumbing state which creates some of the initial Pipes, instantiates the LoggerModule and connects it to the shell. From there we move to the Assembling state which requests a LoggerWindow viewComponent to add to the stage. From here we move to the Navigating state which has no transitions associated with it. It is our final state and means that our application is configured and ready for the user to interact with. There is also a Failing state, which like the Navigating state has no transitions. If some portion of the configuration process were to fail, this state would be called to notify the user that something horrible has happened.

Communications between modules happens in a module’s JunctionMediator. This mediator’s viewComponent is a Junction. One issue to consider when utilizing dynamic modules is memory management. The connections made between modules with the Pipes utility need to be managed so that when they are disconnect there are no artifacts left to get in the way of Garbage Collection. This application utilizes an extended JunctionMediator defined by the ManagedJunctionMediator class which defines a connection pool for the various pipes. When a module is instantiated, the connections are stored in a HashMap. Later, when we want to remove the module, the ManagedJunctionMediator references its pool of connections and removes any connections related to that particular module. This is an area that needs more refinement, and while it works well for this demonstration, it could be further abstracted to make a more complete solution for this issue. The ShellJunctionMediator is the only class extending ManagedJunctionMediator in this application.

It is important to note that a JunctionMediator’s handleNotifications method needs to have the default in the switch set to super.handleNotification( note ). There are standard notifications that a JunctionMediator listens for that need to be handled. In addition, the listNotificationInterests method is handled a bit differently to accommodate this functionality.

Adding a Doodad

When you click on the Add Doodad button, the AddNewDoodadCommand is called. This command instantiates a new DoodadModule and registers a DoodadModuleMediator with the shell’s core. The command then connects the module’s pipes to the shell’s STDIN and STDOUT pipes. After the module is wired to the shell for communication a notification is sent to get the UI associated with the DoodadModule

The DoodadModule is not a visual component. It extends PipeAwareModule, which is itself an extension of ModuleBase. This is as opposed to Module, which is a visual class, but in this demonstration the visual components are created by the module and then sent to the shell for display. All of the visual components are mediated by their module, but control of their placement rests with the shell.

A PipeAwareModule is instantiated with a unique identifier. This provides a way to differentiate it from other instances of the module easily. The DoodadModule itself also creates a sequential integer identifier, but this is used here for the purposes of labeling and not marking the module for unique identification.

When the shell asks for a new Doodad, the UIQueryMessage of type GET is sent from the ShellJunctionMediator and is received by the DoodadJunctionMediator. The DoodadModule then creates the visual component in the CreateDoodadCommand and sends it back to the shell via a UIQueryMessage of type SET. The shell then adds the component to the stage. When the Doodad’s ‘kill’ button is pressed, the DoodadJunctionMediator is ordered to send out another UIQueryMessage, this time of type DESTROY which lets the shell know that it needs to unplug the module.

Conclusion

There is a lot going on here, and it is complex on many levels. There are huge advantages to engineering a complex application this way. It provides flexibility in terms of development allowing for growth and expansion of the system. This particular example is kept relatively simple, and in a real world application I would strive to make the modules more generic, abstracting out the Doodad-specific bits in the shell so that any module added would share common functionality in terms of memory management and basic mechanisms related to the visual components. Hopefully it provides a helpful overview for people looking to understand multicore applications with PureMVC and make use of some of the handy utilities the community has made available.

Let me know if you have any questions and I will do my best to answer them. If you have anything to add, or find any errors, please don’t hesitate to let me know.

  • https://www.newtriks.com Simon Bailey

    Nice Joel, very clearly detailed, good job!

    Simon

  • https://puremvc.org Cliff Hall

    Good one, Joel!

  • https://blog.jeffyamada.com Jeff Yamada

    Awesome Joel, thanks so much. Really awesome material and very well written to boot.

  • https://craigkaminsky.blogspot.com Craig Kaminsky

    Great post across the board, Joel! Thanks!

  • https://revisual.co.uk neil manuell

    this is cool. someday soon I’m going to have to use piping, and this is where I’m going to start :)

  • Alex

    Thanks a lot! It realy helps.
    Does anybody know anything about PureMVC multicore port for Objective-C?

  • anton mejorada

    Thanks for the clear description. Im a little less scared now after reading your post.

    Would this be a realistic framework to use if I have a bunch of individual modules or widgets (8-9 displayed at one time), which must synchronize with each other based on event(s)? For example, clicking on one frame displayed on the shell, would affect another window(s) display. Would I be using the pipes in this instance?

    It seems that pipes send messages from a module to the shell, and then vice versa, but would they be used to send messages from one module to many other modules? If so, would you have a spaghetti path of pipes between the modules being displayed? Thanks again.

  • Greg

    First of all, thank you for the example.
    I have a question though. In AddNewDoodadCommand doesn't DoodadModuleMediator take the Doodad as its parameter?
    I am unable to work past this. Thank you.

    var doodadModuleMediator:DoodadModuleMediator = new DoodadModuleMediator( doodadModule );

    public function DoodadMediator(viewComponent:Doodad)?

  • joelhooks

    DoodadMediator mediates an individual Doodad (viewComponent) where-as DoodadModuleMediator is mediating the whole module (non-visual). DoodadModuleMediator is instantiated in the Shell and is owned by the ShellFacade.

  • Greg

    First of all, thank you for the example.
    I have a question though. In AddNewDoodadCommand doesn't DoodadModuleMediator take the Doodad as its parameter?
    I am unable to work past this. Thank you.

    var doodadModuleMediator:DoodadModuleMediator = new DoodadModuleMediator( doodadModule );

    public function DoodadMediator(viewComponent:Doodad)?

  • https://joelhooks.com Joel Hooks

    DoodadMediator mediates an individual Doodad (viewComponent) where-as DoodadModuleMediator is mediating the whole module (non-visual). DoodadModuleMediator is instantiated in the Shell and is owned by the ShellFacade.

  • grantdavies

    Nice example, have you tried the fabrication utility ? It makes pipes considerably easier and removes a lot of the, excuse the pun “plumbing.

    Cheers
    grant

  • https://joelhooks.com Joel Hooks

    I don't find Pipes difficult ( any more ;) ) and while I have huge respect for Darshan's efforts with Fabrication, it extends virtually every single PureMVC framework creating an entirely new framework. I really like working from the reference PureMVC.

  • grantdavies

    Nice example, have you tried the fabrication utility ? It makes pipes considerably easier and removes a lot of the, excuse the pun “plumbing.

    Cheers
    grant

  • https://joelhooks.com Joel Hooks

    I don't find Pipes difficult ( any more ;) ) and while I have huge respect for Darshan's efforts with Fabrication, it extends virtually every single PureMVC framework creating an entirely new framework. I really like working from the reference PureMVC.

  • Greg

    Hi Joel, thank you. I used your example and the one at https://www.tekool.net/ as the basis for an Actionscript only project. I implemented IEventDispatcher in a module data proxy so it can dispatch an event when it is updated. I passed the module facade to each module on instantiation so each module retrieves its proxy and then attaches a listener to it. This also allows the module to directly notify its junction mediator. Each module is assigned to an index in the proxy array. This acts as a data binding to the proxy, something not readily available in Actionscript only projects. Module to module communication is then bounced off the shell back to the intended module index in the proxy. I also modified the module message header to contain a to-module and a from-module parameter. When the module proxy gets an update it uses the to-module parameter as the index in it's array. As mentioned prior each module listens to its index in the proxy. As a side benefit each module can also access another modules data if needed and I suppose an index in the proxy could be assigned to the shell so that data would be readily available too. The next step is to use the state machine for layout control perhaps implementing a three phase process like the Flex Layout Manager does. Thank you again for your great example.

  • https://joelhooks.com Joel Hooks

    That's awesome Greg. Pipes is really a fantastic tool once you get your head around it. You'll dig the State Machine too. Cheers

  • Greg

    Hi Joel, thank you. I used your example and the one at https://www.tekool.net/ as the basis for an Actionscript only project. I implemented IEventDispatcher in a module data proxy so it can dispatch an event when it is updated. I passed the module facade to each module on instantiation so each module retrieves its proxy and then attaches a listener to it. This also allows the module to directly notify its junction mediator. Each module is assigned to an index in the proxy array. This acts as a data binding to the proxy, something not readily available in Actionscript only projects. Module to module communication is then bounced off the shell back to the intended module index in the proxy. I also modified the module message header to contain a to-module and a from-module parameter. When the module proxy gets an update it uses the to-module parameter as the index in it's array. As mentioned prior each module listens to its index in the proxy. As a side benefit each module can also access another modules data if needed and I suppose an index in the proxy could be assigned to the shell so that data would be readily available too. The next step is to use the state machine for layout control perhaps implementing a three phase process like the Flex Layout Manager does. Thank you again for your great example.

  • https://joelhooks.com Joel Hooks

    That's awesome Greg. Pipes is really a fantastic tool once you get your head around it. You'll dig the State Machine too. Cheers

  • https://www.fellowesbindingmachine.net/ fellowes_binding_machine

    This is so interested! Where can I find more like this?

  • Pingback: Modular Robotlegs | Building Blocks