|
Getting started with Lexaden Web Flow
Lexaden Web Flow (LWF) is the next generation framework with the asynchronous language allowing to program an algorithm of systems behavior on a higher level. Similar to how browser interprets HTML code - LWF engine interprets business model written on Lexaden Web Flow to the complete applications. Asynchronous or dynamic business model managed by external events brings a new way of building enterprise applications. A Flow-Based Architecture Lexaden Web Flow is based on a finite state machine implementation but provides extra functionality to support inheritance, polymorphism and flow execution within the same configuration. A flow encapsulates a sequence of steps that guide a user through the execution of some business task. Applications developed with LFW are treated as one big wizard. Users can start communicating with the systems from any entry point and systems will lead them opening up the predefined steps of the particular business model.
Lexaden Web Flow includes:
Having a flow-based architecture, LWF is extremely configurable and extensible.
Lexaden Web Flow and MVC
Lexaden Web Flow is the result of lessons learned over several years of experience in Java Web-development.
Controller is responsible for creation and initialization of a View. It gets data from the Model and updates the View. Controller receives events from a View and fires application events using an event processor.
View is a composite Vaadin component. A View can be built with ease using Vaadin component model..
Model could be any data received from any service of the application. The Model is used by the Controller to populate the View. And the Model can be updated by the Controller.
Event Model. The main disadvantage of the many MVC frameworks they do not have well-designed event model. Usually they use the Publish/Subscribe pattern. It's very difficult to follow the pattern because you have to create EventListeners and Custom Events for every reusable module in the application. This increases the number of classes in the application. Also it's hard to maintain state of publishers and subscribers while changing the state of the application.
LWF uses the well designed event model based on statechart to avoid issues described above . Controllers are connected to states. State transitions are driven by events. Depending on the current state the application can receive only subset of events from the user. Changing the application state the allowed events are also changing.
This section focuses on the step-by-step creation an application with Vaadin + LWF. The example application is a simple Patient Registration application that demonstrate the following aspects of using LWF technology:
In subsequent sections, you'll improve the application over several iterations and make it more LWF savvy. Figure 2 shows an annotated view of what the final iteration of the example Patient Demo application will look like. See Download to get the application source.
The goal of the initial application is to help the user in filling the large form by splitting it into several small forms.
The application has:
To build the initial application you need to:
The application uses the following directory layout:
+---src
The Java code is under src/main/java/. The web.xml file is under the src/main/webapp/WEB-INF directory. The flow configuration file is under src/main/webapp/WEB-INF /webflow.
To use the Vaadin Application Servlet, you first need to install it in your web.xml file, as shown in Listing 1:
Listing 1. Vaadin Application Servlet
declaration in web.xml:
<servlet><servlet-name>Vaadin Application Servlet</servlet-name><servlet-class>com.vaadin.terminal.gwt.server.ApplicationServlet</servlet-class><init-param><description>Vaadin application class to start</description><param-name>application</param-name><param-value>com.lexaden.webflowpaneldemo.WebFlowPanelDemo</param-value></init-param></servlet> The init parameter 'application' defines which application class will be started by Vaadin Application Servlet. This is similar to most web.xml descriptions, except that your're giving control to the Vaadin Application Servlet to handle requests instead of specifying your own servlet.
Listing 2. Vaading Application Servlet
path mapping in web.xml
<servlet-mapping><servlet-name>Vaadin Application Servlet</servlet-name><url-pattern>/*</url-pattern></servlet-mapping>
Listing 2 tells the Vaadin Application Servlet container to send all requests to the Vaadin Application Servlet for processing.
Specify the
demo.flows.xml file
The demo.flows.xml file has
the root state 'application'. Usually this state is used to associate a
layout controller for the application. The layout controller should contain one
or several placeholders. In our case we
create the "content" placeholder. The placeholder will be used later
by Web Flow Engine to inject views based on the current application state.
<application id="application" extends="controller">
The WebFlowPanel has a bindContoller
method. It's used to bind a
controller with the specific state of the application and to bind with the
certain placeholder.
flowPanel.bindController("application", "content", new MainLayoutController());
The 'application' state contains initial
state "initView".
This is an action state which is used to initialize the main layout of the
application. Annotation
@OnEnter("initView") is used to bind the action state with the
corresponding method in the bound controller.
<application id="application" extends="controller"><action id="initView" extends="action"><on to="displayView" type="flow"/></action> public class MainLayoutController … {@OnEnterState("initView")public void initView(StateEvent event) {…}}
The MainLayoutController defines a main layout of the application and creates the 'content' placehodler.
The transition "displayView" in the "initView" action state will be invoked automatically by the Web Flow Engine. The transition has the "flow" type which indicates a new flow within the Web Flow Engine. The flow is responsible to keep the execution flow of the current user session. The flows are used to handle different entry points within the application and keep them separately from each other.
Further the application goes to the "application/displayView" state. At this stage the layout is initialized and it's ready to be displayed to the user.
The "application/displayView" state contains two controller states "welcome" and "register". The "welcome" controller state is used to create the "welcome" screen and to handle user inputs from it. The "register" controller state is used to process a new patient registration.
The WelcomeController is bound with the "welcome" state using the following code:
flowPanel.bindController("welcome", "content", new WelcomeController());
Such as the "welcome" controller extends the "controller" state it inherits initial attribute, action and view states. The Web Flow Executor goes to the "initView" action then to the "displayView" view state of the WelcomeController.
The "view" type of a state indicates that the controller has prepared view to be injected into the bound placeholder of the parent layout controller.
At the "welcome/displayView" state the application waits for input from the user.
When the user clicks the "Register a
New Patient" button
the controller fires a "register" event.
public void register(Button.ClickEvent clickEvent) {getEventProcessor().fireEvent("register");}
According to the current state and the
flow mapping the state of the
application goes to "register" state.
<controller id="welcome" extends="controller"><on event="register" to="register"/></controller><controller id="register" extends="controller">
Such as the "register" controller state also extends "controller" state it inherits the "initial" attribute and the state goes to the "initView" action state.
The "register" controller state overrides "initView" from the parent state. It defines custom transition to the "personalInfo" view state.
In order to handle the state of the
buttons the RegisterController
class defines a method with the following annotation:
@OnEnterState(VIEW)public void onView(StateEvent event) {...}
This method will be invoked each time when the application enters a state that is inherited from the "view" state. The method gets the list of available
events for the current
application state and enables or disables corresponding buttons.
<view id="personalInfo" extends="view"><on event="next" to="address"/></view>
As we see the "personalInfo" view state has only one transition with the "next" event. Only the "Next" button will be enabled then.
At the final screen of the flow there are
two final actions:
<final id="finish" extends="action"/><final id="cancel" extends="action"/>
The final action is used to finish a part of the execution flow. The context of this flow is defined by the controller bounders. The flow controller pushes in a stack all controller visited by the flow. When the controller visits the final state. A final event occurred with the name of current controller and the name of the final state. For instance:
register.finish
Which means the "register" controller just reached the final state "finish". This event is transferred to previous controller by default. Where you can map it to a transition.
In our sample we just declare that the
event is available for
handling by the controller using the "OnEvent" annotation.
<controller id="welcome" extends="controller">
@OnEvent("register.finish")public void registrationFinished(StateEvent stateEvent) {…}
Think about a flow inside a controller like a function in a programming language where you call the function by sending an event and the function returns the result using the final event. As a result the previous controller will receive the control and can handle the final event.
The "cancel" event is
accessible from any view of the
"register" controller state such as it's declared at the controller
level.
<controller id="register" extends="controller">
The "cancel" event leads to the
final "cancel"
state.
<controller id="register" extends="controller">
Which triggers the "register.cancel"
event and transfers
control to the previous controller in the flow e.g. the "welcome"
controller.
<controller id="welcome" extends="controller">
Such as we don't need any action upon
cancelation of register flow
we do not declare corresponding transition in the "welcome"
controller. Otherwise we could specify
the following transition:
<on event="register.cancel"/>
And create an annotation over a method in
the WelcomeController.java
in order to handle it:
@OnEvent("register.cancel")public void someMethod(StateEvent stateEvent) {…}
@OnEnterState(PERSONAL_INFO)public void initPersonalInfo(StateEvent event) {panel.setCaption("Enter Personal Information");patientForm.setVisibleItemProperties(new String[]{"patientNo","title","firstName","lastName","middleName","dateOfBirth","gender"}); The same works for other view states - "address", "contacts", "medicalInfo", "medicalNotes".
Buttons fire corresponding
events to the event processor. The
processor changes the state of the application and invokes the view related
methods.
public void next(Button.ClickEvent event) {...getEventProcessor().fireEvent(NEXT);}
Constructing the view
using Vaadin technology
It is fairly simple to build
user interfaces using Vaadin component
framework. Very complex forms and layouts can be build in minutes.
But using Java to construct the UI brings the following pros and cons:
Pros: You get all the benefits of using IDE such as syntax highlighting, refactoring, easy access to any utilities. It's very easy to change the behavior of components by extending them. Custom and composite components can be created without any additional mappings and configurations.
Cons: You don't see the overall structure of the user interface. Using Java it is easy to mix the UI structure logic with the UI behavior rather than using declarative XML approach. XML gives much more sophisticated picture of the UI structure. And XML encourages separation of UI structure from the UI behavior. Partially this problem can be solved using Vaadin Add-ons.
To run the application, go to the page where the WAR file is mapped (http://localhost:8080/webflow/)
Once you click on the Register a New
Patient button the first
registration screen appears:
Please notice only the Next and Cancel buttons are enabled at this stage.
The next "Enter Address" screen
appears when you click the
"Next" button.
By filling the forms and clicking the
"Next" button the
last "Enter Medical Notes" screen appears:
At this screen the "Finish" button is enabled along with "Previous" and "Cancel" buttons.
By clicking on the "Finish"
button the application
finishes the registration flow and returns to the "Welcome" screen .The last screen will show us
the First and Last names of the registered patient as a result.
Looking at the application and flow configuration a question arises: what benefits could this approach give us?
If you have well defined fine-grained loosely coupled forms/panels/pages/whatever then this approach allows you to reuse them in different flows without involving a programmer.
Or imagine a situation when you don't need anymore the last step of the flow. In our case you just remove the last step from the xml configuration and instead of next event of the "medicalInfo" add the transition to the "finish" action. That will be enough for changing the business process.
In the case when the flow logic is coded inside java class it usually looks like a bunch of enclosed if/else statements. The programmer can change the business logic only.
Also using the flow xml you can see the
whole business process at a
glance in the format that could be easily understand by non-programmers such as
functional and business analysts, managers, product owners.
Downloads Download Lexaden Web Flow Sample - Vaadin 7.0.0 - 3.5 MB - available under Apache Licence 2.0
|


