Spring web flow - framework pro management toku web aplikace
6. April 2007
Dnes existuje spousta MVC frameworků, které vám dovedou zařídit skvělé (rozuměj flexibilní) routování. Světem webu dnes vládnou nice URL`s, přístupnost atd…V některých případech však požadujete něco zcela jiného. Například taková registrace uživatelů nebo odesílání objednávky. Takovéto procesy bývají rozděleny do několika kroků a bývá zcela klíčové zajistit, aby se uživatel do každého kroku procesu mohl dostat pouze námi definovanými cestami. Při odesílání objednáky z e-shopu je jeden z možných scénářů takovýto:
- klepnutí na tlačítko objednat
- ověření zda je uživatel přihlášený
- pokud je uživatel přihlášen jdeme dále
- pokud není přihlášen nabídneme přihlašovací fomulář (a zde opět mohou nastat dva stavy – uživatel již je nebo není zaregistrován)
- potvrzení objednávky
- objednávka je odeslána
Během tohoto procesu je nutné nějak ošetřit, že jsou nastaveny všechny potřebné údaje a jednoduše řešeno – systém je v takovém stavu v jakém ho potřebujeme mít. Můžeme zvolit svoje proprietární řešení a vše si ošetřovat například pomocí session nebo použít SWF. Pomocí SWF můžeme nadefinovat UI flows, a také jednotlivé stavy systému, možné přechody z jednoho stavu do druhého a akce a podmínky, které tento přechod provází. Pomocí SWF vlastně provádíme abstrakci našich UI flows a zvyšujeme tím jejich přenositelnost. Veškerá logika pro řízení toku je soustředěna na jednom místě a ne rozeseta na x místech kódu.
Pár střípků z architektury SWF
Asi nemá cenu si detailně popisovat celý SWF. Od toho je referenční příručka. Uvede zde pár základních faktů.
SWF lze integrovat do spousty stávajících frameworků (Struts, JSF, Spring MVC, Portlet MVC). SWF se skládá z těchto knihoven:
- spring-webflow (jádro frameworku)
- spring-core (různé helper a utility třídy používané jádřem systému)
- spring-binding (Spring data binding framework)
- commons-logging (asi není třeba představovat – logovací framework z dílny Jakarta)
- OGNL (jazyk pro práci s objekty)
Flow lze definovat dvěma způsoby:
- přímo v Java API
- v XML souboru
Flow lze do sebe libovolně vnořovat. Slouží k tomu element
subflow-state
.
Lze vytvářet takzvané globální přechody, které lze použít kdykoliv.
Definují se pomocí elementu global-transitions
.
V rámci SWF si můžeme ukládat proměnné do několika různých scopů:
- request
- Mají platnost pouze pro daný požadavek.
- flash
- Platí dokud uživatel neopustí aktuální state.
- flow
- Platí pro celý flow.
- conversation
- Platí po dobu životnosti mateřského flow.
Použití základních elementů pro stavbu flow bude vysvětleno na příkladu dále v článku.
Jak SWF funguje?
Pouť UI flow se začíná ve start-state. Na další kroky procesu se můžete dostat dvěma způsoby:
- klepnutím na odkaz
- stisknutím tlačítka formuláře
V případě odkazu je nutné připojit do adresy požadavku speciální parametru s názvem _flowExecutionKey. Podle něj pak aplikace pozná o jaké flow se jedná a zajistí se tím stavovost aplikace. Dalším nutným parametrem je _eventId, což je id stavu, na který chceme přejít. Jednoduchý příklad:
<a href="<c:url value="/spring-web-flow-test.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=step2"/>">Pokračovat</a>
Pokud použijeme formulář bude kód analogický:
<form method="post" action="/spring-web-flow-test.htm">
...
<input type="hidden" name="_flowExecutionKey" value="<c:out value="${flowExecutionKey}" />" />
<input type="submit" name="_eventId_step2" value="Pokračovat" />
</form>
Za zmínku zde stojí pouze hodnota atributu name u odesílacího tlačítka formuláře – _eventId_step2. SWF si z něj „vydestiluje“ step2.
Pokud se pokusíte zmodifikovat hodnotu kteréhokoliv z těchto dvou parametrů SWF jednoduše vyhodí výjimku – buď nenajde probíhající flow s danou hodnotou parametru _flowExecutionKey a nebo nenajde přechod, který je definován pomocí zmodifikovaného parametru _eventId. SWF je také imunní pomocí pomocí kopírování URL. Po nějaké době dojde k expiraci flow s daným _flowExecutionKey a určitě tušíte, že pokud ho použijete dojde opět k vyhození výjimky.
Integrace se Spring Framework 1.2
<!-- Launches new flow executions and resumes existing executions: Spring 1.2 config version -->
<bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
<property name="definitionLocator" ref="flowRegistry" />
<property name="executionAttributes">
<map>
<entry key="alwaysRedirectOnPause">
<value type="java.lang.Boolean">false</value>
</entry>
</map>
</property>
<property name="repositoryType" value="CONTINUATION" />
</bean>
<!-- Creates the registry of flow definitions for this application: Spring 1.2 config version -->
<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations">
<list>
<value>/WEB-INF/flows/my-flow.xml</value>
</list>
</property>
</bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
<property name="mappings">
<props>
<prop key="/marketing/brands-assets/brands-assets.htm">
myFlowController
</prop>
</props>
</property>
</bean>
<bean name="myFlowController" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
<property name="defaultFlowId" value="my-flow" />
</bean>
Atribut alwaysRedirectOnPause definuje zda má při
přechodu ze z jednoho stavu do druhé dojít k redirektu. Je vhodné
jej nastavit na true
v případě, že chceme zabránit
opakovanému odesílání formulářů. Pomobí beany flowRegistry
provedeme registraci nadefinovaných flow. Beana urlMapping ukazuje
jakým na kterou URL je naše flow navázané. Beana myFlowController
slouží pro definici vlastního controlleru. Důležitá je hodnota property
defaultFlowId, která říká jaké flow se má použít.
Definice našeho flow – souboru my-flow.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">
<start-state idref="peopleOverview" />
<view-state id="peopleOverview" view="people-overview">
<render-actions>
<bean-action bean="peopleService" method="findAllPeople">
<method-result name="people" />
</bean-action>
</render-actions>
<transition on="select" to="setupFormCustom">
<action bean="peopleFormAction" method="selectPerson" />
</transition>
</view-state>
<action-state id="setupFormCustom">
<action bean="peopleFormAction" method="setupFormCustom" />
<transition on="success" to="editPerson" />
</action-state>
<view-state id="editPerson" view="person-edit">
<transition on="submit" to="savePerson">
<action bean="peopleFormAction" method="bindAndValidate" />
</transition>
<transition on="error" to="editPerson" />
</view-state>
<action-state id="savePerson">
<action bean="peopleFormAction" method="savePerson" />
<transition on="success" to="peopleOverview" />
</action-state>
</flow>
Takto vypadá naše flow ve Spring IDE Web Flow Visualizeru.
Uvedené flow by se nechalo slovy vyjádřit asi takto: Na začátku se
uživateli zobrazí stav peopleOverview
, na kterém je seznam
uživatelů. Model pro tuto stránku bude získán z třídy
peopleService
a její metody findAllPeople
.
peopleService
je klasická springovská servisní
třída. Tak vrací kolekci objektů typu Person
. Takto kolekce
bude co kontextu požadavku umístěna pod názvem people
.
Z této stránky bude možný jediný přechod a to na state
s názvem setupFormCustom
, přičemž ještě před
přechodem se zavolá metoda selectPerson
peopleFormAction
, která uloží idečko vybraného člověka do
flow kontextu. State setupFormCustom
zavolá metodu
setupFormCustom
z třídy peopleFormAction
,
která provede nastavení nastavení formbacking objektu pro editaci. Při
odesílání zeditovaných údajů se zavolá metoda
bindAndValidate
, která zavolá případný připojený validator.
Pokud třída projde validací dojde k přechodu do stavu
savePerson
, kde se náš objekt uloží. Poté dojde
k přesměrování zpět na úvodní stránce s přehledem
uživatelů.
Prosím nehledejte na příkladu chybičky. V některých ohledech je
přitažen za vlasy, avšak jako Hello World příklad myslím zcela vyhovuje.
Tento příklad jsem zvolil abych ukázal SWF při editaci objektů.
K tomu musíme využít třídu dědící
z org.springframework.webflow.action.FormAction
.
Spoustu příkladů je přímo součásti distribuce SWF.
Kód třídy peopleFormAction
vidíte zde:
package cz.vavru.spring.webflow.test.action;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;
import cz.vavru.spring.webflow.test.PeopleService;
import cz.vavru.spring.webflow.test.business.vo.Person;
public class PeopleFormAction extends FormAction {
private PeopleService peopleService;
private Person person;
public void setPeopleService(PeopleService peopleService) {
this.peopleService = peopleService;
}
public CoopCountryGroupsFormAction() {
setFormObjectName("form");
setFormObjectClass(Person.class);
setFormObjectScope(ScopeType.FLOW);
setFormErrorsScope(ScopeType.FLOW);
}
public Event selectPerson(RequestContext context) throws Exception {
int personId;
try {
personId = Integer.parseInt(context.getRequestParameters().get("personId"));
} catch (NumberFormatException ex) {
personId = 0;
}
context.getFlowScope().put("personId", new Integer(personId));
return success();
}
public Event setupFormCustom(RequestContext context) throws Exception {
int personId = Integer.parseInt(context.getFlowScope().get("personId"));
if (personId > 0) {
person = peopleService.getPersonById(personId);
} else {
person = new Person();
}
BeanUtils.copyProperties(person, (Person) getFormObject(context));
return success();
}
public Event savePerson(RequestContext context) throws Exception {
peopleService.saveOrUpdatePerson((Person) getFormObject(context));
return success();
}
}
Několik ukázek z real-life projektu
Pohled na Eclipse IDE s otevřeným projektem. Ve view Spring Beans vidíme seznam XML souborů z aplikačního kontextu Spring Frameworku a také seznam SWF souborů s definicí jednotlivých flows.
Závěr
Pokud programujete aplikaci, která obsahuje business procesy skládající se z několika kroků určitě použití SWF zvažte. Z vlastní praxe mohu říci, že se osvědčilo. A teď když Spring IDE obsahuje i Web Flow Visualizer to platí dvojnásob – graf, který produkuje je pro člověka znajícího UML intuitivní a hlavně mu bude rozumět i váš šéf (možná dokonce i zákazník). Ale abych jenom nechválil – SWF Visualizer má i své mušky. Z mých asi deseti vytvořených flow tři jednoduše neotevře. SWF má výborně zpracovanou referenční příručku.
Odkazy
- Spring Web Flow Reference Documentation
- Spring Web Flow Confluence
- Spring Web Flow na TSS
- Spring Web Flow Examined
- Spring IDE
Článek patří do kategorie: Java, Spring Framework
4 Komentářů Přidat komentář
1. Ladislav Thon | 11. April 2007 v 14.33
Asi se sluší říct, že v SWF se nedefinují business procesy, ale UI flows, i když si můžou jedna k jedné odpovídat. Jinak hezký tutoriálek, k odkazům bych doplnil pěkný web zaměřený na SWF tak říkajíc od zdroje: <a>http://www.ervacon.com/products/swf/index.html.
2. Vlasta | 11. April 2007 v 14.40
[1] Máte samozřejmě pravdu. Nedostatek opravím.
3. Zdenek | 28. August 2007 v 13.49
Zajimavy tutorial:
http://www.springframework.org/…by-step.html
4. 3asparagus&hellip | 13. January 2022 v 2.53
3questionable…
…
Přidat komentář
Povolené HTML značky:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
Odkazovat na tento článek | Přihlásit se k odběru těchto komentářů přes RSS Feed