75
MASARYKOVA UNIVERZITA FAKULTA INFORMATIKY Moderní rámce pro webové aplikace DIPLOMOVÁ PRÁCE Petr Matulík Brno, jaro 2006

m Asarykova Univerzita Fakulta Informatiky

  • Upload
    vlcik

  • View
    81

  • Download
    1

Embed Size (px)

Citation preview

Page 1: m Asarykova Univerzita Fakulta Informatiky

MASARYKOVA UNIVERZITA

FAKULTA INFORMATIKY

}w���������� ������������� !"#$%&'()+,-./012345<yA|Moderní rámce pro webové aplikace

DIPLOMOVÁ PRÁCE

Petr Matulík

Brno, jaro 2006

Page 2: m Asarykova Univerzita Fakulta Informatiky

Prohlášení

Prohlašuji, že tato diplomová práce je mým puvodním autorským dílem, které jsem vypra-coval samostatne. Všechny zdroje, prameny a literaturu, které jsem pri vypracování použí-val nebo z nich cerpal, v práci rádne cituji s uvedením úplného odkazu na príslušný zdroj.

Vedoucí práce: RNDr. Tomáš Pitner, Ph.D.

ii

Page 3: m Asarykova Univerzita Fakulta Informatiky

Podekování

Chci podekovat zejména své prítelkyni Šárce, která akceptovala mé absolutní casové vytí-žení behem prípravy a psaní práce, dále doktoru Pitnerovi, který mi poskytl nadstandardnívolnost pri výberu a realizaci práce a v neposlední rade mým rodicum, kterí mi umožnili,abych se k psaní diplomové práce vubec dostal.

iii

Page 4: m Asarykova Univerzita Fakulta Informatiky

Shrnutí

V komunite vývojáru a architektu rozsáhlých aplikací, které jsou vytváreny v programova-cím jazyce Java, se stále casteji a ostreji ozývají hlasy, které volají po zmene typické archi-tektury Java EE aplikací, po revizi klasických postupu pri jejich návrhu a celkove po zmenesmeru celého tohoto odvetví.

Vývoj složitých aplikací podle stávajících dogmat a s bezmyšlenkovitým využitím typic-kých nástroju je podle nich výrazne nákladnejší a trvá delší dobu, než je možno dosáhnoutjinými, moderními postupy. Výsledné aplikace jsou nárocnejší na údržbu a rozširování, jsouneprenositelné, jejich kód je složitejší, hure citelný a špatne testovatelný.

Ale co je vlastne myšleno tím klasickým a zavrhovaným zpusobem vývoje JEE a jakou al-ternativu navrhují jeho odpurci? Odpovedet na tyto otázky bude jedním z úkolu této práce.

Je jasné, že vývoj netriviální JEE aplikace se v dnešní dobe již prakticky neobejde bezvyužití nekterého z aplikacních rámcu. V této práci srovnáme dva klícové aplikacní rámce- EJB a Spring framework - a použití druhého z nich predvedeme na netriviální webovéaplikaci. Aplikaci zároven zabezpecíme pomocí autentizacního a autorizacního rámce AcegiSecurity. Oba aplikacní rámce, Spring framework i Acegi Security, pak názorne popíšemetutoriálovou formou.

iv

Page 5: m Asarykova Univerzita Fakulta Informatiky

Klícová slova

Java, Java EE, webové aplikace, aplikacní rámce, Spring framework, Acegi Security fra-mework

v

Page 6: m Asarykova Univerzita Fakulta Informatiky

Obsah

1 Úvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Aplikacní rámec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Analýza požadavku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

2 Enterprise JavaBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.1 EJB v kontextu JEE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.2 Urcení EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32.3 Další nedostatky EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.4 Diskutabilní pohnutky pro použití EJB . . . . . . . . . . . . . . . . . . . . . . . 52.5 Kdy je vhodné EJB použít? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.6 EJB 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Aplikacní rámec Spring - teorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.1 Úcel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2 Podpora aplikacní vrstvy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.3 Návrhové vzory Obrácení rízení a Injektáž závislostí . . . . . . . . . . . . . . 93.4 Typická architektura Spring aplikací . . . . . . . . . . . . . . . . . . . . . . . . 10

4 Rámec Spring - jádro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134.1 Továrna tríd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134.2 Aplikacní kontext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.3 Jedinácek a prototyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.4 Atributy bežných typu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.5 Externí datové zdroje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174.6 Odesílání e-mailu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204.7 Zdroje zpráv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

5 Spring MVC - webová vrstva aplikace . . . . . . . . . . . . . . . . . . . . . . . . . 235.1 MVC - bleskový kurz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235.2 Startujeme aplikaci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235.3 Cesta požadavku útrobami Spring MVC . . . . . . . . . . . . . . . . . . . . . . 255.4 Výber kontroleru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.5 Interceptory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295.6 Detekce národního prostredí . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295.7 Výber pohledu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305.8 Zpracování výjimek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315.9 Kontrolery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

6 Rámec Spring - AOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446.1 Pokyny (advices) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446.2 Definice cílu (pointcuts) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456.3 Aspekty (advisors) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466.4 AOP proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476.5 Deklarativní transakcní management . . . . . . . . . . . . . . . . . . . . . . . . 48

7 Zabezpecení aplikací v rámci Spring . . . . . . . . . . . . . . . . . . . . . . . . . . 50

vi

Page 7: m Asarykova Univerzita Fakulta Informatiky

7.1 Prípady užití . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507.2 Architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507.3 Ochrana prístupu k webovým zdrojum . . . . . . . . . . . . . . . . . . . . . . 527.4 Ochrana prístupu k metodám servisních objektu . . . . . . . . . . . . . . . . . 587.5 Hodnocení . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

8 Záver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60A Vzorová webová aplikace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62B Podpurné webové stránky vzorové aplikace . . . . . . . . . . . . . . . . . . . . . . 63C Návrh prusvitek k tématu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64D Elektronický formát této práce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65Literatura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66Rejstrík . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

vii

Page 8: m Asarykova Univerzita Fakulta Informatiky

Kapitola 1

Úvod

Problematika moderních odlehcených JEE aplikacních rámcu je pomerne nová. Z toho du-vodu si na úvod nejdríve objasníme samotný pojem aplikacního rámce a umístíme proble-matiku do kontextu analýzy požadavku na (webovou) aplikaci. Ve 2. kapitole se budemevenovat komponentnímu aplikacnímu rámci EJB, jenž byl donedávna jedinou volbou privývoji složitých JEE aplikací a ve 3. kapitole se už seznámíme s teoretickými základy al-ternativy k EJB, tedy aplikacního rámce Spring. 4. kapitola bude úvodní tutoriálovou cástídemonstrující základy rámce Spring, v 5. kapitole pak popíšeme podporu rámce pro webo-vou vrstvu aplikace. 6. kapitola bude pojednávat o AOP funkcionalite rámce Spring a v 7.kapitole se již seznámíme s autentizacním a autorizacním aplikacním rámcem Acegi Secu-rity. V 8. kapitole již celé téma jen strucne uzavreme.

1.1 Aplikacní rámec

Tvorba jakékoliv netriviální aplikace obnáší nutnost zabývat se nekterými bežne se vysky-tujícími aspekty návrhu, jakými jsou napríklad autentizace a autorizace, správa transakcí,trvalé uložení dat v databázi apod. Dalším problémem mnoha bežných aplikací je slabástrukturovanost kódu. Tu lze rešit použitím nekterých návrhových vzoru, ovšem i takovátovlastní dekompozice kódu predstavuje zbytecné stále se opakující kódování.

Aplikacní rámec (nekdy oznacován jako framework ci kontejner) tyto problémy reší tak,že bežne se opakující funkce aplikací implementuje ve svých vlastních trídách a poskytujetak hotovou infrastrukturu, kterou aplikace pouze využívají. Aplikacní programátor se takmuže soustredit jen na vlastní problém a zbytek nechat na aplikacním rámci, jenž jeho apli-kaci spravuje.

Typickou vlastností aplikacních rámcu je fakt, že životní cyklus aplikace má na starostipráve rámec a kód aplikace je jím pouze v ruzných chvílích volán. Tím je zajišten presunkontroly nad behem aplikace z aplikace do aplikacního rámce a vynucena vhodne dekom-ponovaná struktura dodržující rozšírené návrhové vzory.

1.2 Analýza požadavku

Získá-li softwarová firma zakázku, jejímž obsahem je vývoj komplexní webové aplikace,provede analýzu požadavku zákazníka a související analýzu obchodních požadavku na

1

Page 9: m Asarykova Univerzita Fakulta Informatiky

1.2. ANALÝZA POŽADAVKU

aplikaci (business requirements). Obchodními požadavky v této souvislosti myslíme na-príklad:

• požadovanou míru bezpecnosti aplikace,

• nutnost asynchronní komunikace s jinými aplikacemi (messaging),

• poskytnutí rozhraní pro synchronní vzdálenou komunikaci s jinými aplikacemi na-psanými v Jave, prípadne v jiných jazycích,

• požadavky na odezvu aplikace, s címž souvisí vyrovnávání záteže a clustering,

• požadavek podpory více typu grafických rozhraní, napríklad webového i grafickéhodesktopového,

• nutnost správy transakcí nad více datovými zdroji apod.

Chce-li však zmínená firma být úspešná, musí minimálne stejnou pozornost venovat i po-žadavkum na obecný charakter dané aplikace a jejího vývoje, které mají na úspech projektuprimární vliv. Temito požadavky jsou:

• jednoduchost a s tím související spravovatelnost a rozširitelnost aplikace,

• vysoká míra produktivity vývoje,

• zajištení objektové orientace návrhu aplikace v maximální možné míre,

• použití léty zkušeností proverených doporucení pro návrh objektove orientovanýchaplikací - best practices, návrhové vzory,

• testovatelnost implikující spolehlivost aplikace.

2

Page 10: m Asarykova Univerzita Fakulta Informatiky

Kapitola 2

Enterprise JavaBeans

Po zhodnocení požadavku na aplikaci prichází na radu výber vhodné technologie, kteráby tyto požadavky mela být schopna uspokojit. Padne-li volba na Javu, potažmo JEE, býváprvní po ruce technologie Enterprise JavaBeans (dále jen EJB), která podporu pro uspoko-jení v podstate všech zmínených obchodních požadavku poskytuje na jednom míste v naprvní pohled líbivém balení. Jak si ovšem ukážeme, s uspokojením obecných požadavku nacharakter aplikace a její vývojový proces je na tom EJB výrazne hure.

2.1 EJB v kontextu JEE

Rozšíreným omylem mezi programátory a architekty JEE je domnenka, že JEE a EJB jev podstate totéž. To je samozrejme daleko od pravdy. JEE je sadou standardizovaných apli-kacních rozhraní (API), ke kterým patrí napríklad JMS ci JTA. A práve standardizace v ob-lasti nízkoúrovnových technologií pro tvorbu pokrocilých aplikací je obrovským a hlav-ním prínosem JEE celému výrobnímu prumyslu komplexních aplikací v Jave. EJB jsou na-proti tomu standardizovaným komponentovým modelem (rámcem) se silnou podporou provzdálenou komunikaci (remoting), který tato nízkoúrovnová rozhraní využívá, a predsta-vuje tak v podstate jen jeden ze zpusobu, kterými lze JEE aplikace vytváret.

2.2 Urcení EJB

EJB verze 1.0 vznikly v roce 1998 jako technologie, která mela usnadnit a zjednodušit vý-voj nárocných podnikových aplikací v jazyce Java. Dala si za cíl umožnit vývojárum, abynemuseli pracovat prímo s aplikacními rozhraními jednotlivých služeb JEE a nemuseli jimtedy rozumet, aby se nemuseli starat o pooling zdroju, management transakcí a stavu, bez-pecnost z hlediska vláken, perzistenci objektu apod. Duležitým zámerem tvurcu specifikaceEJB byla také prenositelnost výsledných aplikací. Takovéto aplikace melo být možné prená-šet mezi EJB kompatibilními aplikacními servery ruzných výrobcu bez jakýchkoliv zmen.Jak již bylo receno, technologie EJB definuje komponentový model JEE aplikací. Zamýšle-ným dusledkem rozšírení technologie mel být vznik a rust trhu s hotovými a prenositelnýmikomponentami.

Po pocátecním nadšení se ale bohužel zacalo ukazovat, že tato ocekávání zrejme nebu-dou naplnena. Vývoj EJB komponent byl stále výrazne složitejší než vývoj obycejných javo-vých objektu (zvaných také POJO - Plain Old Java Object) ci komponent provozovaných ve

3

Page 11: m Asarykova Univerzita Fakulta Informatiky

2.3. DALŠÍ NEDOSTATKY EJB

webových kontejnerech. Reakcí na stále casteji se ozývající stížnosti uživatelu technologieEJB bylo další rozširování specifikace a vydání verzí 2.0 a 2.1. Samotný text specifikace jetak rozsáhlý, že existuje jen málo tech, kterí jej znají dokonale. Vývoj EJB komponent kom-plikuje také nutnost vytváret nekolik tríd a popisovac rozmístení pro každou z nich. Tentonedostatek lze do jisté míry zmírnit použitím nástroju pro generování kódu, jakým je naprí-klad Xdoclet, v takovém prípade jde ale pouze o lécení symptomu nikoliv nemoci samotné.I generování kódu má totiž své citelné nedostatky.

Zámer umožnit návrh JEE aplikací bez znalosti podkladových API se ukáže být nerea-listický v prípade jakéhokoliv netriviálního problému pri nasazení ci behu aplikace, protožepro rešení takového problému jsou ve vetšine prípadu tyto znalosti nezbytné.

Pokud jde o prenositelnost aplikace, ta je komplikována snahou jednotlivých výrobcuaplikacních serveru rešit zjevné a dlouho známé nedostatky EJB implementací vlastníchdoplnkových funkcí a služeb. Pro uživatele aplikacních serveru jsou tyto funkce vetšinouvýrazným ulehcením práce, takže je hojne využívají, címž ovšem vážou svuj software k da-nému prostredí. Portovat takovou aplikaci na jiný aplikacní server se pak muže stát skutec-ným problémem. Zrejme i z techto duvodu také nikdy nedošlo ke vzniku zmíneného trhus EJB komponentami.

2.3 Další nedostatky EJB

Už jsme se také zmínili, že EJB trpí vážnými nedostatky souvisejícími s obecnými poža-davky na vlastnosti výsledné aplikace a jejího vývoje.

Velmi vážný dopad má použití technologie EJB na objektovou orientaci aplikace. Je cas-tým jevem, že i v prípadech, kdy obchodní požadavky na aplikaci nevyžadují její distribu-ovaný charakter, používají vývojári vzdálená rozhraní. At’ už v dusledku snahy o zvýšenívýkonu budoucí prípadnou dekompozicí aplikace po více serverech ci z jiných duvodu.Vzhledem k nutnosti zefektivnit vzdálený prenos objektu dochází nutne k porušení základuobjektových principu. Rozhraní takových objektu jsou v zájmu maximalizace objemu datprenesených behem jednoho vzdáleného volání príliš úzká, transferové objekty jsou nao-pak príliš velké, protože zapouzdrují více logických objektu. Sám koncept transferovýchobjektu (DTO - Data Transfer Object), které slouží jen jako datové struktury bez jakýchkolivmetod, jde proti základum objektové orientace aplikace.

Ukázalo se, že nekteré obecne snadno realizovatelné návrhové vzory v klasických (non-EJB) aplikacích, lze prekvapive nesnadno realizovat v prostredí EJB. Nejznámejším príkla-dem je zrejme vzor Jedinácek (Singleton).

Stále populárnejší, a dnes už v mnoha prípadech nezbytnou dovedností kvalitního pro-gramátora, je vývoj s využitím a podporou automatizovaných testu. I v tomto prípade námvšak EJB háže klacky pod nohy. Závislost aplikacního kódu na kontejneru EJB (napríkladv dusledku použití JNDI) znemožnuje testování jednotek (unit tests) v izolaci a nechceme-lise výhod testy rízených metod vývoje vzdát, je nutno testovat kód umístený v EJB kontej-neru s využitím nekterého z dostupných podpurných nástroju. To je ale samozrejme velminárocné casove, cili pravidelné spouštení stovek ci tisícu testu v krátkých casových interva-

4

Page 12: m Asarykova Univerzita Fakulta Informatiky

2.4. DISKUTABILNÍ POHNUTKY PRO POUŽITÍ EJB

lech není možné.Produktivita programátoru na EJB založených aplikací v porovnání s dobou pred EJB

príliš nevzrostla. Na vine jsou všechny problémy, o kterých se zde zminujeme. EJB apli-kace jsou velmi nesnadno udržovatelné a debugovatelné. Pri vývoji je též nárocné zabývatse úpravou nekolika souboru (zdrojových souboru i deskriptoru) pri implementaci každénove vytvorené funkce. Dalším omezením produktivityEJB programátora je nesnadná apli-kovatelnost zásad již zmínených produktivních testy rízených metod vývoje. Typický cykluskódování-testování ci (pro tvrdé jádro stoupencu TDD {Test Driven Development}) testování-kódování je nutné upravit na kódování-nasazení-testování (a tvrdé jádro TDD má smuluúplne). Složitost celé problematiky EJB se projevuje napríklad i v bežných problémech sezavadeci tríd. Hierarchie zavadecu i problémy s nimi související jsou v EJB kontejnerechvýrazne složitejší než v kontejnerech webových.

2.4 Diskutabilní pohnutky pro použití EJB

K castým argumentum pracovníku zodpovedných za výber použitých nástroju pri tvorbeJEE aplikací patrí následující:

• Jednodušším aplikacím poskytnou EJB dostatecný prostor pro pozdejší rozšírení ob-chodních požadavku na aplikaci.

• EJB je prece standard. Když jej zvolím, nemohu nic pokazit.

• Na EJB jsme postavili již nekolik aplikací, takže bude dobré využít zkušenosti našichvývojáru i v tomto projektu.

Ani jeden z techto duvodu není samozrejme z hlediska úspechu projektu obhajitelný. Ale-spon k prvnímu z nich dodejme, že zkušenost a propagátori agilních metodik vývoje soft-ware ríkají: „Delejte veci tím nejjednodušším možným zpusobem.“ Navrhovat architekturuaplikace tak, aby odrážela možnost všech možných budoucích zmen požadavku, je obvyklecestou do pekel, i když dláždenou dobrým úmyslem. Pravdepodobnost, že nastane situace,na kterou architekturu naší aplikace pripravujeme, je casto tak malá, že nemuže ospravedl-nit nárust zpoždení zacátku implementace a složitosti aplikace v dusledku komplexnejšíhonávrhu. Proto je nutné si ujasnit soucasné a prípadne velmi pravdepodobné budoucí ob-chodní požadavky na aplikaci a na jejich základe architekturu aplikace navrhnout. Durazna objektove orientovaný návrh a dodržování osvedcených návrhových doporucení je do-statecnou zárukou, že adaptace už hotové aplikace na neocekávané nové požadavky budemožná.

Ješte donedávna platnými argumenty pro použití EJB byli napríklad tyto:

• Požadavek podpory pro správu strední vrstvy (middle tier, business layer) aplikace -v EJB realizováno prostrednictvím Session Beans.

• Využití výhod deklarativní správy transakcí.

5

Page 13: m Asarykova Univerzita Fakulta Informatiky

2.5. KDY JE VHODNÉ EJB POUŽÍT?

• Požadavek podpory pro vrstvu prístupu k datum (Data Access Layer) - v EJB Contai-ner Managed Persistence a Bean Managed Persistence.

• Potreba komplexní deklarativní správy autentizace a autorizace aplikací.

• Podpora jasného oddelení jednotlivých vrstev aplikace.

Dnes už však lze všechny tyto požadavky uspokojit jinak než s použitím EJB a casto lépe asnadneji. Dokážeme to v dalších kapitolách této práce.

2.5 Kdy je vhodné EJB použít?

Nechteli bychom tady ale na EJB stále jen dštít síru. EJB má mnoho nedostatku a je castonevhodne používána v prípadech, kdy by bylo výrazne efektivnejší použití jiných techno-logií a prístupu. Na druhou stranu ale poskytuje excelentní podporu pro nekteré obchodnípožadavky, které jiným zpusobem jednoduše realizovat nelze. Patrí k nim následující:

• Skutecne oduvodnený požadavek na vnitrní distribuovanost aplikace. Je nutné, abyvýkonnostní režie, která vznikne realizací vzdálených volání, byla více než vyváženaprínosem distribuovaných výpoctu. Z toho plyne, že aplikace musí provádet skutecnenárocné výpocty.

• Požadavek vzdálené komunikace s okolím prostrednictvím RMI/IIOP.

• Požadavek podpory více typu grafických rozhraní - typicky jde o rozhraní založenéna technologii Swing a zároven o webové rozhraní. Vzhledem k nárocnosti instalace aúdržby Swing grafických rozhraní jde o nepríliš castý požadavek.

• Požadavek aplikace, která je silne závislá na predávání zpráv. Message Driven Beansjsou jednou z nejužitecnejších cástí specifikace EJB.

Je treba ale ješte jednou zopakovat, že na vetšinu JEE aplikací nejsou tyto požadavkykladeny a že v takových prípadech je vetšinou efektivnejší, lacinejší a po všech stránkáchvýhodnejší využít alternativy k EJB, které už dnes existují a které si v této práci predstavíme.

2.6 EJB 3.0

Nesmíme samozrejme zapomenout alespon strucne zhodnotit perspektivy EJB. V dobe psanítéto práce byl v rámci procesu tvorby specifikace EJB verze 3.0 publikován dokument s ná-zvem Proposed final draft. Po schválení tohoto dokumentu expertní skupinou by melavzniknout referencní implementace a tzv. Technology Compatibility Kit. Teprve poté mužedojít k publikaci finální verze specifikace.

Expertní skupina, která má prípravu tretí verze EJB specifikace na starosti, má na štítu(na svých webových stránkách) napsáno, že úcelem EJB 3.0 je zjednodušit architekturu EJBa usnadnit tak práci s EJB z pohledu vývojáre. Zatím všechno nasvedcuje tomu, že EJB 3.0

6

Page 14: m Asarykova Univerzita Fakulta Informatiky

2.6. EJB 3.0

reší nekteré nedostatky predchozích verzí aplikací tech principu, které stojí v pozadí od-lehcených kontejneru, kterým se v této a navazujících pracích budeme venovat. Propojeníobjektu spravovaných kontejnerem EJB bude založeno na vzoru Dependency Injection aneúspešný model Container Managed Persistence bude nahrazen rešením postaveným naperzistenci POJO objektu. Tento nový model perzistence je silne inspirován úspešnými ob-jektovými relacne-mapovacími nástroji a zejména technologie Hibernate se ukazuje být jehonejvernejší predlohou. Je jasné, že úspešná a rozšírená kombinace Spring+Hibernate stálamodelem nové verzi specifikace EJB, címž je v podstate dokázána kvalita a užitecnost tétokombinace v oblasti návrhu JEE aplikací.

Budoucnost EJB 3.0 je zatím stále mlhavá. Pravdepodobné se zdá být její nasazení v pro-dukcních podmínkách kolem poloviny roku 2006, znalci problematiky se však shodují, ževzhledem ke zpoždení implementace konecné specifikace výrobci aplikacních serveru azpoždení upgradu produkcních prostredí v softwarových firmách se výraznejšího rozšíreníaplikací založených na EJB 3.0 dockáme mnohem pozdeji.

Množství dríve zmínených nevýhod EJB však zustává i v této verzi. Pro využití vet-šiny vlastností EJB 3.0 napríklad vždycky bude potreba aplikacní server se všemi problémy,které z toho plynou. Také nutnost do jisté míry zajistit zpetnou kompatibilitu s predchozímiverzemi EJB je na prekážku odstranení všech problému.

V tomto oddíle bylo použito nekterých pojmu (Dependency Injection, ...), které budouvysvetleny v dalších kapitolách.

7

Page 15: m Asarykova Univerzita Fakulta Informatiky

Kapitola 3

Aplikacní rámec Spring - teorie

Hitem v oblasti vývoje pokrocilých JEE aplikací se v prubehu posledních trí let stal rámecpro jejich správu s názvem Spring framework. V základech tohoto open-source projektuse nachází kód, který byl publikován v roce 2003 Rodem Johnsonem, v [1] a který odrážíJohnsonovu nekolikaletou analytickou, konzultantskou, ale i programátorskou zkušenosts tvorbou rozsáhlých JEE aplikací. Tento kód byl autorem navržen pro zefektivnení rešenínekterých bežných problému, se kterými se každý vývojár pokrocilých Java aplikací se-tkává témer denne, a celkove pro zjednodušení a snížení ceny návrhu a vývoje techto pro-gramu. Jeho intenzivním rozširováním vznikl aplikacní rámec, jehož celkový pocet staženíse k dnešnímu dni rychle blíží k pul miliónu.

3.1 Úcel

Rámec Spring je modulární Java/JEE aplikacní rámec, jenž také bývá nekdy oznacován jakoodlehcený (lightweight) kontejner. Využíván je stejne jako Enterprise JavaBeans zejména protvorbu webových aplikací, ale lze jej použít v podstate pro jakýkoliv typ aplikace, vcetneklasických (desktop GUI) aplikací.

Cílem tohoto projektu je samozrejme zejména usnadnení vývoje Java (a zejména JEE)aplikací. Prostredku k dosažení tohoto primárního cíle poskytuje Spring nekolik.

Prvním z nich je podpora pro aplikacní vrstvu programu, což je vlastnost, která je natrhu unikátní a která predstavuje hlavní lákadlo pro vývojáre JEE aplikací.

Dalším duležitým cílem použití rámce Spring je snadná testovatelnost výsledné aplikace.Spring, jak pozdeji uvidíme, umožnuje cistým a pohodlným zpusobem vzájemne oddelit(z hlediska vzájemné závislosti) nejen jednotlivé vrstvy, ale dokonce i jednotlivé objekty, cožje klícovou podmínkou pro možnost využití klasického jednotkového testování. Vzhledemk tomu, že agilní metodiky vývoje software, které jsou na testování ve vetšine prípadu po-staveny, jsou stále populárnejší a rozšírenejší, je rovnež tato vlastnost rámce považována zaklícovou.

Drtivá vetšina existujících aplikacních rámcu se soustred’uje na podporu jen urcité archi-tekturální vrstvy aplikace. Príkladem budiž oblíbený MVC rámec Struts, který usnadnujevývoj webové prezentacní vrstvy aplikací, nebo neméne populární objektove-relacne ma-povací nástroj Hibernate, orientující se na perzistencní vrstvu. Rámec Spring naproti tomukonzistentním zpusobem podporuje všechny vrstvy aplikací, prezentacní vrstvou pocínaje,

8

Page 16: m Asarykova Univerzita Fakulta Informatiky

3.2. PODPORA APLIKACNÍ VRSTVY

pres již zmínenou aplikacní vrstvu až k datové a perzistencní vrstve. Výjimkou ovšem neníani napríklad vrstva webových služeb.

Dalším a rozhodne nikoliv posledním cílem aplikacního rámce Spring je nevynalézatkolo. Integrace velkého množství rozšírených softwarových nástroju poskytuje uživatelimožnost využít specializovaných a casem proverených rešení na danou problémovou ob-last, a to konzistentním zpusobem. K temto podporovaným nástrojum patrí samozrejme jižzmínené Struts a Hibernate, jejich celkové množství však jde rádove do desítek.

3.2 Podpora aplikacní vrstvy

Jádro aplikacního rámce Spring tvorí zejména funkcionalita, která poskytuje podporu apli-kacní vrstve spravovaných programu.

Objekty, které tvorí aplikaci, jsou behem svého životního cyklu spravovány kontejne-rem rámce Spring a výjimku netvorí ani objekty aplikacní vrstvy. Spring opravdu spravujeaplikaci již na úrovni objektu, nikoliv velkých komponent, jako je tomu u EJB.

Ke službám, které muže návrhár aplikace pro správu objektu využít, patrí zejména jed-notný procedurální i deklarativní transakcní management, jednotný zpusob konfiguraceaplikace v dobe nasazení, pokrocilá procedurální i deklarativní správa zabezpecení, správaprovázání a závislostí objektu, pooling objektu a další.

Stežejními návrhovými technikami, které poskytování techto služeb umožnují, jsou pro-gramování orientované na aspekty (AOP - Aspect Oriented Programming) a návrhový vzorObrácení rízení. Pokud jde o AOP, tak lze použít jednak vlastního rešení rámce Spring, kteréje postaveno na standardních dynamických proxy jazyka Java, je ale také možno využít inte-grace s populárním open-source nástrojem AspectJ. Implementace Obrácení rízení, respek-tive návrhového vzoru Injektáž závislostí (viz další kapitola), v rámci Spring predstavujenejkomplexnejší rešení této problémové oblasti vubec.

3.3 Návrhové vzory Obrácení rízení a Injektáž závislostí

Obrácení rízení (IoC - Inversion of Control) je již dlouho používaný návrhový vzor, jenžv puvodním významu predstavoval techniku, s jejíž pomocí dochází k prenesení rízení behuprogramu z kódu, který navrhuje programátor, na podpurný aplikacní rámec. Lze tedy ríci,že jakýkoliv aplikacní rámec vcetne rámce Spring a EJB je aplikací návrhového vzoru IoC.S nástupem aplikacních rámcu, jakými jsou Spring ci PicoContainer, se však význam IoCzacal posunovat a ješte v nedávné dobe byl bežne používán pro popis zpusobu, jakým jev techto aplikacních rámcích zajišteno provázání spravovaných objektu.

Na toto zmatení pojmu reagovali prední teoretici i praktici v této oblasti (vcetne RodaJohnsona) a rozhodli se pro zavedení nového pojmu Injektáž závislostí (DI - DependencyInjection). Zjednodušene receno lze postup pri použití DI popsat takto: Mejme závislostobjektu A na objektu B, jinými slovy objekt A obsahuje odkaz na objekt B. Pri použití DIbudou pri startu kontejneru, který je spravuje, oba objekty vytvoreny a v objektu A bude

9

Page 17: m Asarykova Univerzita Fakulta Informatiky

3.4. TYPICKÁ ARCHITEKTURA SPRING APLIKACÍ

inicializován odkaz na objekt B. Existuje nekolik zpusobu, jak toho lze dosáhnout, my všakzmíníme jen dva zdaleka nejrozšírenejší.

První zpusob umožnuje uspokojení závislostí prostrednictvím nastavovacích (set) metodobjektu a proto bývá oznacován jako Setter Injection. Druhou možností je pak varianta s ná-zvem Constructor Injection, která využívá standardních konstruktoru jazyka Java. A právetyto dve varianty jsou podporovány rámcem Spring.

V minulosti se provázání objektu dosahovalo bud’ prímou instanciací v kódu trídy, prí-padne ruznými vyhledávacími (lookup) mechanismy (napr. JNDI) ci aplikací návrhovéhovzoru Lokátor služby (Service Locator). Prestože v tomto poradí šlo vždy o krok kupredu,všechna tato rešení jsou horší než IoC, a to zejména z hlediska príliš úzkého vzájemnéhosvázání objektu a zhoršené citelnosti a udržovatelnosti kódu.

3.4 Typická architektura Spring aplikací

Jednou z charakteristických vlastností aplikacního rámce Spring je snaha neomezovat žád-ným zpusobem architekta aplikace, nevnucovat jeho trídám závislost na trídách rámce,snaha o maximální transparentnost rámce. Trídy aplikace by pri správném návrhu nemelyo rámci vubec „vedet“ a mela by tak existovat možnost zamenit rámec Spring za jiné pro-stredí, rekneme EJB, dojde-li napríklad ke zmene obchodních požadavku na aplikaci. Z to-hoto duvodu neexistuje striktní návod návrhu Spring aplikací, ovšem Rod Johnson již v [1]v kapitole 3 definoval aplikacní architekturu s názvem Architektura odlehcených kontej-neru (Lightweight Container Architecture), které se komunita vývojáru pri návrhu Springaplikací více méne drží.

Ve [2] je uvedená architektura zachycena pomocí schématu 3.1.Na obrázku mužeme videt, že základem architektury je typická trívrstvá architektura,

skládající se z prezentacní, aplikacní a perzistencní vrstvy. Infrastrukturní trídy rámce pakposkytují podporu všem temto vrstvám. Vzhledem k integraci mnoha existujících speciali-zovaných aplikacních rámcu, které se koncentrují na konkrétní cásti tohoto modelu, lze prowebovou prezentacní vrstvu použít jak Spring MVC modul, tak i rámce Struts ci Webwork.Pro perzistenci objektu do databáze pak lze použít jak abstrakcní JDBC vrstvy rámce Spring,tak populární ORM nástroje, jakými jsou Hibernate ci JDO.

Na základe ohlasu odborníku, bloggeru i vývojáru, kterí sdelují své názory na komunit-ních fórech a v mailových konferencích, lze usuzovat, že nejrozšírenejší kombinací techno-logií pri vytvárení webových aplikací spravovaných rámcem Spring je kombinace SpringMVC (pro správu prezentacní webové vrstvy) a Hibernate (perzistencní vrstva). Z tohotaké vycházíme pri návrhu vzorové aplikace, která je obsahem prílohy A této práce. Takováwebová aplikace tedy sleduje následující približný tok zpracování požadavku uživatele.

Spring MVC modul predává požadavek urcitému kontroleru z vrstvy kontroleru. Tytokontrolery by mely být navrženy tak, aby neobsahovaly žádnou aplikacní logiku a abypouze zpracovávaly vstupy z webového uživatelského rozhraní a pripravovaly výstupy prototo rozhraní. Samotnou aplikacní logiku by mely vykonávat jednotlivé objekty z aplikacnívrstvy (také oznacované jako manažeri {managers} ci servisní objekty {services}). Jednotlivé

10

Page 18: m Asarykova Univerzita Fakulta Informatiky

3.4. TYPICKÁ ARCHITEKTURA SPRING APLIKACÍ

kontrolery tedy volají metody manažeru a predávají jim (ci od nich získávají) doménové ob-jekty. Získané objekty pak predávají šablonám prezentacní technologie, typicky JSP, kterámá na starost vygenerování príslušného HTML kódu.

Prípadná vrstva webových služeb pak volá tytéž metody aplikacní vrstvy jako webovávrstva. Tak dochází k rychlému a prehlednému znovupoužití kódu aplikacní vrstvy.

Samy objekty aplikacní vrstvy mají na starost veškeré výpocty nezávislé na prezentacnívrstve. Predávají doménové objekty perzistencní vrstve a tyto objekty od ní také získávají.Pro každou konkrétní trídu servisního objektu pak existuje odpovídající rozhraní, címž jevyhoveno základnímu požadavku OO návrhu, tedy požadavku tzv. programování do roz-hraní.

Perzistencní vrstva v typické Spring aplikaci využívá návrhového vzoru Objekt pristu-pující k datum (DAO - Data Access Object), tzn. že v aplikacní vrstve se nenachází žádnýkód pro manipulaci s konkrétním úložištem dat (databází, LDAP atd.), takový kód je sou-cástí perzistencní vrstvy. Ke každé tríde perzistencní vrstvy také existuje odpovídající roz-hraní.

Jednotlivé vrstvy používají pouze rozhraní vrstvy nižší, tedy prezentacní trídy využí-vají metod rozhraní aplikacní vrstvy, servisní objekty pak volají metody perzistencních tríd.Programování do rozhraní pak umožnuje snadné nahrazení konkrétních tríd, které danározhraní implementují. To se týká všech vrstev. Lze vymenit konkrétní implementace kon-troleru, servisních objektu i DAO objektu. Zejména v prípade DAO objektu je pak snadnévymenit napríklad klasickou JDBC implementaci za rekneme Hibernate implementaci, cožmuže být pomerne castý požadavek pri soucasném rychlém rustu popularity ORM nástroju.

V bežné webové aplikaci jsou objekty všech trí vrstev vetšinou bezstavové, cili je vhodnéimplementovat kontrolery, servisní objekty i DAO objekty pomocí návrhového vzoru Jedi-nácek (Singleton). K inicializaci všech techto jedinácku a jejich vzájemnému provázání jepak rámcem Spring využit DI.

Jakou konkrétní podporu všem vrstvám popsané architektury Spring poskytuje si uká-žeme v následujících kapitolách této práce.

11

Page 19: m Asarykova Univerzita Fakulta Informatiky

3.4. TYPICKÁ ARCHITEKTURA SPRING APLIKACÍ

Obrázek 3.1: Schéma typické architektury Spring aplikace

12

Page 20: m Asarykova Univerzita Fakulta Informatiky

Kapitola 4

Rámec Spring - jádro

4.1 Továrna tríd

Klícovým pojmem celé architektury aplikacního rámce Spring je tzv. továrna tríd (bean fac-tory), reprezentovaná rozhraním org.springframework.beans.factory.BeanFactory.Továrna tríd má na starosti základní funkcionalitu rámce Spring, tedy zejména provázáníobjektu pomocí DI a transparentní aplikaci služeb rámce (transakce atd.) pomocí AOP.

Továrna tríd po svém startu drží objekty spravované aplikace, vzájemne je prováže aposkytuje odkazy na tyto objekty svým klientum. To, které objekty jsou továrnou tríd spra-vovány, je definováno vetšinou prostrednictvím konfiguracního souboru ve formátu XML.Lze použít i jiné formáty, napríklad tzv. properties soubor, ale zdaleka nejrozšírenejší jsouXML soubory, takže i my se tohoto formátu v dalším textu pridržíme.

Príkladem deklarace spravovaných objektu muže být následující ukázka kódu:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans><bean id="nejakeDao" class="cz.neco.NejakeDaoImpl" />

<bean id="nejakyManazer" class="cz.neco.NejakyManazer" ><property name="konf" value="nejakaHodnota" /><property name="dao" ref="nejakeDao" />

</bean></beans>

Predpokládejme tedy existenci následujících tríd a rozhraní:

interface NejakeDao {}class NejakeDaoImpl implements NejakeDao {}interface NejakyManazer {}class NejakyManazerImpl implements NejakyManazer {private NejakeDao dao;private String konf;public void setDao(NejakeDao dao) {this.dao = dao;

}

13

Page 21: m Asarykova Univerzita Fakulta Informatiky

4.1. TOVÁRNA TRÍD

public void setKonf(String konf) {this.konf = konf;

}}

V tomto prípade by kontejner použil DI variantu Setter Injection a prostrednictvím reflexeprovedl kód ekvivalentní tomuto:

NejakeDao nejakeDao = new NejakeDaoImpl();NejakyManazer nejakyManazer = new NejakyManazerImpl();nejakyManazer.setKonf("nejakaHodnota");nejakyManazer.setDao(nejakeDao);

Chceme-li tedy používat Setter Injection, pak všechny spravované trídy naší aplikacemusí dodržovat formát JavaBeans, predevším tedy musí obsahovat verejný bezparamet-rický konstruktor a verejnou set metodu k nastavovanému atributu.

Pokud bychom chteli využít DI variantu Constructor Injection, pak bychom museli náškonfiguracní soubor poupravit takto:

<beans><bean id="nejakeDao" class="cz.neco.NejakeDaoImpl" />

<bean id="nejakyManazer" class="cz.neco.NejakyManazer" ><constructor-arg value="nejakaHodnota" /><constructor-arg ref="nejakeDao" />

</bean></beans>

Také spravovaná trída cz.neco.NejakyManazerImpl by musela vypadat trochu ji-nak:

class NejakyManazerImpl implements NejakyManazer {private NejakeDao dao;private String konf;public NejakyManazerImpl(NejakeDao dao, String konf) {this.dao = dao;this.konf = konf;

}}

Je videt, že v prípade Constructor Injection muže továrna tríd rámce Spring spravovatv podstate jakékoliv trídy, což muže být užitecné napríklad v prípade, že chceme pomocírámce Spring spravovat trídy tretích stran, které formát JavaBeans nedodržují. Obe variantyDI lze v rámci celého kontejneru, ale i téže trídy libovolne kombinovat.

Nastavení atributu konf retezcového typu trídy NejakyManazerImpl ilustruje konfi-guracní možnosti rámce Spring. Stejným zpusobem lze inicializovat hodnoty atributu ostat-ních primitivních typu. Nastavení atributu dao trídy NejakaSluzbaImpl naopak ukazuje,jakým zpusobem lze využít Spring pro provázání objektu.

14

Page 22: m Asarykova Univerzita Fakulta Informatiky

4.2. APLIKACNÍ KONTEXT

Pojmenujeme-li vytvorený definicní soubor kontext.xml a umístíme-li jej do classpath,pak továrnu tríd (kontejner) nastartujeme napríklad takto:

BeanFactory beanFactory = new XmlBeanFactory("/kontext.xml");

Vytvorený objekt s id nejakyManazer získáme z kontejneru voláním:

NejakyManazer manazer = (NejakyManazer)beanFactory.getBean("nejakyManazer");

4.2 Aplikacní kontext

Aplikacní kontext (Application context) je v podstate rozšírením pojmu továrna tríd. Apli-kacní kontext je reprezentován rozhranímorg.springframework.context.ApplicationContext, které rozširuje rozhraníBeanFactory. Není-li aplikace extrémne omezena svým prostredím a není nutno šetrits každým bajtem pameti, pak je doporuceno vždy použít aplikacní kontext, protože jehomožnosti jsou výrazne vetší než možnosti továrny tríd. Obecne lze ríci, že továrnu tríd jevždy nutno inicializovat programove, kdežto pro aplikacní kontext existuje množství zpu-sobu, jak kontejner spustit deklarativne.

Dalšími vlastnostmi aplikacního kontextu, kterými se liší od továrny tríd, jsou podporazdroje zpráv (MessageSource support), podpora práce s externími zdroji dat (ResourceLoadersupport), podpora událostního modelu (framework events) a mnoho dalších.

Aplikacní kontext lze samozrejme také inicializovat programove, a to napríklad násle-dujícím zpusobem:

ApplicationContext context = new ClassPathXmlApplicationContext("/kontext.xml");

4.3 Jedinácek a prototyp

Z pohledu životního cyklu kontejnerem spravovaných tríd existují dva základní zpusobyposkytování objektu techto tríd. Deklarujeme-li element bean s atributem singleton="true"a nebo tento atribut vynecháme (jde totiž o defaultní hodnotu), tak kontejner vytvorí jedinýobjekt dané trídy a odkaz na nej pak vrací pri každém volání metodyBeanFactory.getBean("id_naseho_objektu"). Tento objekt je tedy jedináckem v rámcicelé aplikace a i všechny ostatní spravované trídy, které mají ve svém elementu bean dekla-rovánu závislost na tomto jedinácku, získají odkaz na tentýž objekt.

Naopak nastavíme-li naší tríde v elementu bean atribut singleton na hodnotu false,pak každé volání metody BeanFactory.getBean("id_naseho_objektu") vrátí novýobjekt dané trídy. Takto definovaná trída je pak oznacována jako prototyp (prototype). Jenutné ovšem zduraznit, že jakmile je nová instance trídy predána klientovi, tak kontejner sijiž odkaz na ni nedrží, cili na prototyp nelze aplikovat žádné bežné rámcem Spring posky-tované deklarativní služby, o kterých si povíme v dalším textu.

Konfiguracní kód

15

Page 23: m Asarykova Univerzita Fakulta Informatiky

4.4. ATRIBUTY BEŽNÝCH TYPU

<bean id="a" class="com.ClassA" singleton="false"><property name="propB" ref="b" />

</bean><bean id="b" class="com.ClassB" singleton="false"><property name="propA" ref="a" />

</bean>

tedy ríká, že pri každém volání BeanFactory.getBean("a") dostaneme úplne novouinstanci trídy ClassA, která bude mít odkaz na úplne novou instanci trídy ClassB. Stejnetomu je pri volání BeanFactory.getBean("b").

4.4 Atributy bežných typu

Pri definici našich spravovaných tríd v aplikacním kontextu mužeme díky pokrocilým kon-verzním schopnostem rámce Spring snadno nastavit hodnoty atributu všech primitivníchtypu, typu kolekce a casto používaných tríd. Následuje komplexní príklad:

<bean id="objekt_s_mnoha_atributy" class="com.TridaSMnohaAtributy"><property name="booleanAttr" value="true" /><property name="intAttr" value="3" /><property name="doubleAttr" value="3.45" /><!-- atribut typu java.lang.Class --><property name="classAttr" value="neco.NejakaTrida" /><!-- lze použít speciální element null pro vyjádrení prázdného odkazu

v atributu objektového typu --><property name="stringAttr"><value><null/></value></property><!-- atribut typu String[] --><property name="stringArrayAttr" value="retezec1,retezec2,retezec3"><property name="listAttr"><list><value>neco</value><value>cokoliv</value><value>kdokoliv</value>

</list></property><property name="setAttr"><set><value>1</value><value>2</value><value>3</value>

</set></property><property name="mapAttr"><map><entry key="auto"><value>moto</value>

</entry><entry key="jedna">

16

Page 24: m Asarykova Univerzita Fakulta Informatiky

4.5. EXTERNÍ DATOVÉ ZDROJE

<value>dva</value></entry><entry key="cokoliv"><value>kdokoliv</value>

</entry></map>

</property><property name="propertiesAttr"><props><prop key="auto">moto</prop><prop key="jedna">dva</prop>

</props></property>

</bean>

Príklad je natolik sebedokumentující, že není nutno jej príliš vysvetlovat. Snad jen dodejme,že princip konverze textových hodnot z konfiguracního souboru na konkrétní datové typyspocívá v kombinaci použití klasické javové reflexe a konceptu tzv. editoru vlastností (pro-perty editors), který je soucástí standardu JavaBeans. Spring pomocí reflexe zjistí typ danéhoatributu, na základe tohoto typu zvolí z registru editoru vhodný editor a použije jej pro kon-verzi z retezce na cílový typ.

Implicitne je pro všechny objekty aplikacního kontextu registrováno množství bežnýchexistujících editoru pro primitivní typy z balíku sun.bean.editors a také editoru rámceSpring z balíku org.springframework.beans.propertyeditors. Nic ovšem nebránívytvorení vlastního editoru pro konverzi retezcu na vlastní objektový typ (viz trídy v balíkucz.morosystems.sportportal.editors z priložené vzorové aplikace) a registraci to-hoto editoru v aplikacním kontextu rámce Spring.

Lze pristupovat také k vnoreným vlastnostem JavaBeans a to následujícím zpusobem:

<bean id="cokoliv" class="kdekoliv.Cokoliv"><property name="skupina.vedouci.vek" value="23"/>

</bean>

Je však nutné, aby vlastnost skupina objektu cokoliv nemela hodnotu null a aby obsaho-vala vlastnost vedouci, pro niž platí totéž. Vlastnost vek pak musí být skalárního typu.

4.5 Externí datové zdroje

Charakteristickou vlastností rámce Spring je snaha o zjednodušení práce s nekterými bežnepoužívanými, ale pomerne složitými rozhraními nižší úrovne. Spring pro taková rozhraníposkytuje abstrakcní vrstvu, která tvorí rozhraní vyšší úrovne a která usnadnuje progra-mátorum práci v dané oblasti. Klasickým príkladem je abstrakcní vrstva nad JDBC, kterábude popsána v jedné z navazujících diplomových prací, ale stejný princip Spring uplatnujei pro externí datové zdroje (resources). Externími datovými zdroji jsou myšleny všechnymožné vstupní zdroje objektu trídy java.io.InputStream, tzn. soubory, zdroje dosaži-telné prostrednictvím URL apod. Centrálním prvkem celé této abstrakcní vrstvy je rozhraní

17

Page 25: m Asarykova Univerzita Fakulta Informatiky

4.5. EXTERNÍ DATOVÉ ZDROJE

org.springframework.core.io.Resource, které umožnuje jednotnou práci s exter-ními zdroji všech typu. Výcet klícových implementujících tríd ze stejného balíku muže na-pomoci v pochopení, oc jde:

• org.springframework.core.io.ClassPathResource - reprezentuje externí zdrojumístený v classpath,

• org.springframework.core.io.FileSystemResource - reprezentuje soubornebo adresár v souborovém systému,

• org.springframework.web.context.support.ServletContextResource -reprezentuje externí zdroj dostupný príslušné webové aplikaci,

• org.springframework.core.io.UrlResource - reprezentuje externí zdroj do-stupný prostrednictvím URL.

Prostrednictvím rozhraní Resource lze jednotným zpusobem pristupovat k objektu typujava.io.InputStream prostrednictvím metody getInputStream() tohoto rozhraní.Velmi silnou vlastností této abstrakcní vrstvy je také možnost získání príslušného externíhozdroje pouze na základe zadané cesty (path). Tato cesta obsahuje volitelný prefix a samotnoucestu k externímu zdroji. Cesta muže mít jeden z následujících tvaru:

• zacíná jedním z klasických URL prefixu podporovaných danou JVM (napr. http:, ftp:,file:) - v takovém prípade je interními mechanismy rámce vytvoren objekt typu UrlResource.

• zacíná specifickým prefixem rámce Spring classpath: - vytvoren je objekt typuClassPathResource.

• není-li uveden žádný prefix, cesta je relativní a je interpretována na základe typu apli-kacního kontextu, který je pro prístup ke zdroji použit.

– Jde-li o kontext typuorg.springframework.context.support.ClasspathXmlApplicationContext,pak relativní cesta je interpretována vzhledem k aktuální classpath a je vytvorenClassPathResource.

– Jde-li o kontext typuorg.springframework.context.support.FileSystemXmlApplicationContext,pak relativní cesta je interpretována vzhledem k aktuálnímu pracovnímu adresária je vytvoren FileSystemResource.

– Jde-li o kontext typuorg.springframework.web.context.support.XmlWebApplicationContext,pak relativní cesta je interpretována vzhledem ke koreni webové aplikace a je vy-tvoren ServletContextResource.

Máme-li tedy objekt context typu ApplicationContext, pak mužeme k externím zdro-jum pristupovat pomocí cesty takto:

18

Page 26: m Asarykova Univerzita Fakulta Informatiky

4.5. EXTERNÍ DATOVÉ ZDROJE

Resource classPathResource = context.getResource("classpath:conf.xml");Resource urlResource = context.getResource("file:/cesta/k/souboru/conf.xml");Resource contextSpecificResource = context.getResource("relativni/cesta/conf.xml");

Tentýž mechanismus lze díky v aplikacním kontextu implicitne registrovanému editoruvlastností org.springframework.core.io.ResourceEditor s výhodou využít v XMLkonfiguracním souboru kontextu. Máme-li napríklad trídu

class NejakyBean {private Resource configFile;public void setResource(Resource configFile) {this.configFile = configFile;

}}

pak inicializaci vlastnosti configFile mužeme provést následující XML konfigurací:

<bean id="nejakyBean" class="....NejakyBean"><property name="configFile" value="WEB-INF/config.xml" />

</bean>

Jde-li o webový aplikacní kontext, pak atribut configFile bude reprezentovat souborconfig.xml umístený v podsložce WEB-INF webové aplikace.

Abstrakce externích datových zdroju je samozrejme intenzivne využívána v celém rámciSpring a popsané principy lze využít i pri vytvárení aplikacního kontextu. Následujícíhopríkladu využijeme i k predvedení, jakým zpusobem lze vytvorit aplikacní kontext nacte-ním více konfiguracních souboru najednou:

ApplicationContext context =new org.springframework.context.support.FileSystemXmlApplicationContext(new String[]{"file:/d:/ctx1.xml","classpath:ctx2.xml"});

Užitecnou specialitou pri vytvárení aplikacního kontextu (ale nikde jinde) je možnost spe-cifikace množiny externích datových zdroju pomocí jediné cesty. Lze k tomu využít jednakspeciální prefix claspath*: a jednak zástupný znak * použitý v druhé cásti cesty. Jejichvýznam si vysvetlíme na následujícím príkladu:

ApplicationContext context =new FileSystemXmlApplicationContext("classpath*:ctx-*.xml"});

V tomto prípade bude aplikacní kontext vytvoren na základe všech XML souboru, které senacházejí v classpath a jejichž název zacíná retezcem ctx- .

Aplikaci této abstrakcní vrstvy lze nalézt v souboru WEB-INF/web.xml priložené vzo-rové aplikace, kde je použita pro deklaraci množiny konfiguracních XML souboru vytváre-ného aplikacního kontextu.

19

Page 27: m Asarykova Univerzita Fakulta Informatiky

4.6. ODESÍLÁNÍ E-MAILU

4.6 Odesílání e-mailu

Jedním z klasických JEE rozhraní, jejichž návrh je pomerne složitý a jejichž použití pusobíprogramátorum nemalé problémy, je JavaMail API, tedy JEE rozhraní pro odesílání e-mailu.Rámec Spring proto nabízí vlastní abstrakcní vrstvu, která je postavena na JavaMail API akterá práci s e-maily usnadnuje. Tato vrstva se nachází v balíku org.springframework.mail.

Klícovými cleny celého balíku jsou rozhraní MailSender, které má na starosti odesíláníe-mailu, trída SimpleMailMessage, která je jakýmsi hodnotovým objektem (value object),zapouzdrujícím bežné atributy e-mailových zpráv (from, to, subject, text, ...) a konecne hie-rarchie behových výjimek, které slouží jako náhrada výjimek JavaMail API.

Vzhledem k tomu, že pri odesílání obycejných e-mailu prostrednictvím trídySimpleMailMessage nelze specifikovat kódování textu odesílané zprávy, tak v našich ná-rodních podmínkách je tato možnost témer nepoužitelná. Pro bohatší možnosti funkciona-lity (nastavení kódování, prílohy, ...) musíme zvolit použití MIME zpráv a k jejich odesílánípak rozhraní org.springframework.mail.javamail.JavaMailSender.

Podívejme se rovnou na príklad odeslání jednoduchého e-mailu. V priložené vzorovéaplikaci používáme pro odesílání e-mailu speciální servisní rozhranícz.morosystems.sportportal.managers.MailManager. Definice jeho implementacecz.morosystems.sportportal.managers.MailManagerImpl v konfiguracním sou-boru vypadá približne takto:

<bean id="mailManager"class="cz.morosystems.sportportal.managers.MailManagerImpl">

<property name="mailSender" ref="mailSender"/><property name="commonFrom" value="[email protected]" />

</bean>

<bean id="mailSender"class="org.springframework.mail.javamail.JavaMailSenderImpl">

<property name="host" value="adresa.smtp.serveru" /></bean>

Ukažme si i úryvek ze trídy MailManagerImpl:

import javax.mail.MessagingException;import javax.mail.internet.MimeMessage;import org.springframework.context.support.ApplicationObjectSupport;import org.springframework.mail.MailException;import org.springframework.mail.MailSendException;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessageHelper;import org.springframework.core.io.ByteArrayResource;

public class MailManagerImpl extends ApplicationObjectSupportimplements MailManager {

private JavaMailSender mailSender;

20

Page 28: m Asarykova Univerzita Fakulta Informatiky

4.7. ZDROJE ZPRÁV

private String commonFrom;

public void sendActivationEmail(String to, String url, Locale locale)throws MailException {

try {MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,"UTF-8");

helper.setTo(to);helper.setFrom(getCommonFrom());helper.setSubject(getMessageSourceAccessor().getMessage("mail.registration.subject",locale));

helper.setText(getMessageSourceAccessor().getMessage("mail.registration.text.prefix",

new Object[]{url}, locale));/*priložit soubor lze pomocí metody helper.addAttachmentbyte[] obsah_souboru_jako_byte_array = ...;helper.addAttachment("nazev_souboru.dat",new ByteArrayResource(obsah_souboru_jako_byte_array));

*/mailSender.send(mimeMessage);

} catch(MessagingException expc) {... zaloguj výjimku ...throw new MailSendException("Mail message couldn’t be sent",expc);

}}

... mailSender, commonFrom get/set metody ...}

Je videt, že programátor v podstate vubec neprijde do styku s JavaMail API a odeslání e-mailu je pomerne jednoduché. Metodu sendActivationEmail lze volat jak z kontroleru,tak z ostatních manažerských objektu. Príklad jsme využili také k ilustraci zpusobu prístupuk lokalizovaným a parametrizovaným textum z aplikacní vrstvy na základe národního pro-stredí (viz 4.7).

4.7 Zdroje zpráv

Pri návrhu témer každé reálné aplikace je nutno rešit problém její internacionalizace. A pro-tože rámec Spring je zde od toho, aby usnadnoval implementaci casto se opakujících funkcí,tak i v této oblasti poskytuje programátorovi pomocnou ruku v podobe zdroje zpráv (ob-jekty s rozhraním org.springframework.context.MessageSource). Jeho úkolem jeposkytovat parametrizované zprávy pro dané národní prostredí (Locale).

Definice zdroje zpráv, která musí mít nutne název messageSource, muže vypadattakto:

21

Page 29: m Asarykova Univerzita Fakulta Informatiky

4.7. ZDROJE ZPRÁV

<bean name="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource">

<property name="basename" value="WEB-INF/spring/messages" /><property name="cacheSeconds" value="20" /><property name="fileEncodings"><props><prop key="WEB-INF/spring/messages_cs">UTF-8</prop><prop key="WEB-INF/spring/messages_en">UTF-8</prop><prop key="WEB-INF/spring/messages">UTF-8</prop>

</props></property>

</bean>

Tento príklad je opet prevzat z priložené vzorové aplikace, konkrétne ze souboru WEB-INF\spring\applicationContext-business.xml. Trídou, kterou používáme, jeReloadableResourceBundleMessageSource, jenž je schopen oproti standardní imple-mentaci org.springframework.context.support.ResourceBundleMessageSourceve stanoveném casovém intervalu pravidelne znovu nacítat zprávy z definovaných sou-boru.

Obe zmínené implementace interne využívají klasické internacionalizacní architekturyjazyka Java, tzn. trídy java.util.ResourceBundle ve spolupráci se standardním zpra-cováním zpráv poskytovaným trídou java.text.MessageFormat. V naší definici jsmepomocí atributu basename specifikovali umístení našich properties souboru se zprávami,nastavili dvacetisekundový interval znovunacítání zpráv a definovali kódování zmínenýchproperties souboru.

Ke zprávám z definovaného zdroje zpráv pak lze snadno pristupovat ze všech vrstevaplikace. V JSP/JSTL šablonách lze využít standardní JSTL znacku fmt:message ci znackuspring:message z obecné knihovny znacek rámce Spring (viz 5.9.4). V kontrolerech (viz5.9), které rozširují jakoukoliv trídu z hierarchie základních rámcem poskytnutých kontro-leru, lze použít následující volání:

getMessageSourceAccessor().getMessage(...)

Objekt manažerské (servisní, aplikacní) vrstvy pak musí pro možnost stejného volání rozší-rit trídu org.springframework.context.support.ApplicationObjectSupport.

22

Page 30: m Asarykova Univerzita Fakulta Informatiky

Kapitola 5

Spring MVC - webová vrstva aplikace

Prestože „na trhu“ už dávno existuje nekolik kvalitních specializovaných aplikacních rámcu,které poskytují podporu pro implementaci návrhového vzoru model-pohled-kontroler (MVC- model-view-controller) v prostredí webových aplikací, s vlastním rešením prichází i rámecSpring. Nejen, že každé z existujících rešení má své nedostatky, které se Spring snaží rešit,Spring MVC navíc poskytuje pridanou hodnotu v tom, že integruje modul MVC do jednot-ného prostredí tohoto svým pokrytím všeobjímajícího aplikacního rámce. Jednotlivé cástimodulu MVC tak mohou transparentne využívat výhod a služeb, které poskytují ostatnímoduly Springu.

5.1 MVC - bleskový kurz

Návrhový vzor MVC predepisuje ve webových aplikacích, jež jsou založeny na principupožadavek/odpoved’, rozdelení webové vrstvy aplikace do 3 základních cástí:

• model - je tvoren objekty nesoucími data, která budou zobrazena prostrednictvím po-hledu,

• pohled (view) - prijímá od kontroleru model a zobrazuje (formátuje) jej - zasílá odpo-ved’,

• kontroler (controller) - zpracovává požadavek a na základe údaju z nej získaných vy-tvárí model, který následne posílá pohledu.

5.2 Startujeme aplikaci

Trídou, kolem které se, pokud jde o MVC, v rámci Spring všechno tocí, jeorg.springframework.web.servlet.DispatcherServlet. Tato trída predstavuje apli-kaci návrhového vzoru prední kontroler (Front Controller), kdy všechny požadavky urcenédané aplikaci prijímá jediný objekt. Ten je dále predává kontrolerum, které se o vyrízenípožadavku postarají.

Tento servlet je vstupním bodem každé na Springu založené webové aplikace a musí býtzaregistrován v souboru web.xml:

23

Page 31: m Asarykova Univerzita Fakulta Informatiky

5.2. STARTUJEME APLIKACI

<servlet><servlet-name>sportoviny</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet

</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/webApplicationContext1.xml/WEB-INF/spring/webApplicationContext2.xml

</param-value></init-param>

</servlet>

<servlet-mapping><servlet-name>sportoviny</servlet-name><url-pattern>*.html</url-pattern>

</servlet-mapping>

Definovali jsme zde, že servlet s názvem sportoviny je instancí trídy DispatcherServleta je mapován na všechny požadavky s koncovkou html. Navíc jsme pomocí parametrucontextConfigLocation urcili, které soubory definují webový aplikacní kontext tohotokonkrétního servletu. Je-li souboru více, pak jsou jejich umístení oddelena cárkou ci bílýmmístem, pricemž platí, že definice uvedené v pozdeji specifikovaném souboru prekrývajíidentické definice z predešlých souboru. Tento parametr je nepovinný a jeho implicitníhodnotou je /WEB-INF/[název servletu]-servlet.xml, v našem prípade by to tedybylo /WEB-INF/sportoviny-servlet.xml. Aplikacní kontext servletu je inicializovánpri startu servletu. Jednotlivých servletu trídy DispatcherServlet muže být v naší apli-kaci registrováno nekolik a každému z nich bude prirazen jeho vlastní aplikacní kontext.Tyto kontexty jsou nezávislé a vzájemne neviditelné. Prínosem takového rešení je možnostdosáhnout vyšší míry modularizace webového rozhraní.

V aplikacním kontextu servletu by mely být uchovávány pouze definice objektu webovévrstvy aplikace. Objekty, které jsou soucástí aplikacní a datové vrstvy, bývají spolecné provšechny servlety, a proto by jejich definice mely být umísteny v jediném aplikacním kon-textu, který je rodicem všech aplikacních kontextu servletu. Za tímto úcelem se ve Springupoužívá trída org.springframework.web.context.ContextLoaderListener - stan-dardní posluchac (listener) specifikace JavaServlet API - která ocekává tzv. korenový apli-kacní kontext v souboru s cestou /WEB-INF/applicationContext.xml. Tuto cestu lzev souboru web.xml opet prepsat pomocí konfiguracního parametru aplikace s názvemcontextConfigLocation:

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/applicationContext*.xml</param-value>

</context-param>

24

Page 32: m Asarykova Univerzita Fakulta Informatiky

5.3. CESTA POŽADAVKU ÚTROBAMI SPRING MVC

<listener><listener-class>org.springframework.web.context.ContextLoaderListener

</listener-class></listener>

Korenový kontext je následne uložen jako atribut objektu javax.servlet.ServletContexts klícem WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE.V prípade, že ContextLoaderListener v souboru web.xml nezaregistrujeme, korenovýaplikacní kontext nebude vytvoren.

V nekterých prípadech však nebude ContextLoaderListener fungovat. Potrebujeme,aby korenový aplikacní kontext byl vytvoren pred inicializací servletu a vytvorením je-jich aplikacních kontextu, protože tyto jako dcerinné kontexty se potrebují na korenový -rodicovský - kontext pri své inicializaci odkázat. Povinnost inicializovat posluchace predservlety však byla stanovena až ve verzi 2.4 specifikace servletu, cili ve webových kon-tejnerech implementujících starší specifikace (napr. Tomcat 4.0, Resin 2.0, BEA Weblogic8.1, Oracle OC4J 9.0.3, IBM Websphere 5.x a starší verze techto kontejneru) je nutno použítservlet org.springframework.web.context.ContextLoaderServlet, jehož funkc-nost je ekvivalentní zmínenému posluchaci.

5.3 Cesta požadavku útrobami Spring MVC

Každý požadavek (objekt typu javax.servlet.http.HttpServletRequest), kterýDispatcherServlet prijme, je zpracován následujícím zpusobem:

1. Do atributu s názvem DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTEpožadavku je umísten aplikacní kontext daného servletu. Zde je k dispozici pro po-treby dalších objektu podílejících se na životním cyklu požadavku.

2. Jako atribut je do objektu požadavku pridán detektor národního prostredí, tedy objektimplementující rozhraní org.springframework.web.servlet.LocaleResolver,který je dalšími prvky procesu využíván k rozpoznání národního prostredí (Locale)klienta. V aplikacním kontextu musí být definován pod názvem localeResolver.Nevyskytuje-li se tam taková definice, je vytvorena a použita instance trídyorg.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver.

3. Jako atribut je do objektu požadavku pridán detektor motivu, tedy objekt implemen-tující rozhraní org.springframework.web.servlet.ThemeResolver, který jedalšími prvky procesu využíván k identifikaci individuálních uživatelských nasta-vení, tzv. motivu (themes). V aplikacním kontextu musí být definován pod názvemthemeResolver. Defaultní implementací jeorg.springframework.web.servlet.theme.FixedThemeResolver.

4. Je-li v aplikacním kontextu definován objekt s názvem multipartResolver (imple-mentující rozhraní org.springframework.web.multipart.MultipartResolver),

25

Page 33: m Asarykova Univerzita Fakulta Informatiky

5.3. CESTA POŽADAVKU ÚTROBAMI SPRING MVC

objekt požadavku je zabalen do objektu implementujícího rozhraníorg.springframework.web.multipart.MultipartHttpSertvletRequest. Taktoje dalším clánkum retezu usnadnena práce se soubory, které uživatel posílá serveru.Nevyskytuje-li se v kontextu objekt s daným názvem, požadavek není nijak ovlivnen.

5. V aplikacním kontextu jsou vyhledány všechny objekty typuorg.springframework.web.servlet.HandlerMapping a v definovaném poradíjsou dotázány na kontroler, který požadavek zpracuje, a na interceptor, který provedepredzpracování a postzpracování požadavku. Neobsahuje-li aplikacní kontext žádnýobjekt trídy HandlerMapping, vytvorí se instance trídyorg.springframework.web.servlet.handler.BeanNameUrlHandlerMapping.

6. Požadavek je predán interceptoru, byl-li nejaký vybrán, a ten provede predzpracovánípožadavku pred tím, než je predán kontroleru. Interceptor muže provést libovolnouakci, napr. presmerovat požadavek jinam, a zabránit tak normálnímu pokracování re-tezu zpracování.

7. V aplikacním kontextu je vyhledán objekt typuorg.springframework.web.servlet.HandlerAdapter a požadavek je mu pre-dán. Konkrétní implementace tohoto rozhraní urcuje, jaký typ kontroleru bude použitv dalším kroku. Úcelem existence tohoto rozhraní v procesu zpracování požadavkuje možnost snadné výmeny Spring MVC rámce za jiný s jiným typem kontroleru, prí-pade libovolné úpravy zpusobu dorucení požadavku kontrolerum. HandlerAdapterje v aplikacním kontextu vyhledán na základe svého typu, název jeho definice mužebýt libovolný. Defaultní implementací jeorg.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,který podporuje kontrolery Spring MVC implementující rozhraníorg.springframework.web.servlet.mvc.Controller.

8. Požadavek je predán vybranému kontroleru ke zpracování. Kontroler vytvorí modelve spolupráci s aplikacní vrstvou aplikace a urcí logický název pohledu, který budepro zobrazení modelu použit. Obojí predá dál.

9. Požadavek je znovu zpracován interceptorem, byl-li nejaký vybrán.

10. V aplikacním kontextu jsou vyhledány všechny detektory pohledu, tedy objekty typuorg.springframework.web.servlet.ViewResolver, které mají za úkol pre-vést logický název pohledu na konkrétní cestu k souboru, který zobrazení provede.Logický název pohledu vrácený kontrolerem je jim predložen v definovaném poradí,pri cemž první úspešný detektor pohledu preruší retez a vrátí výsledek. V typickémprípade, kdy se pro zobrazení používá nejaká šablonovací technologie (JSP, FreeMar-ker, Velocity), je konkrétním pohledem práve šablona daného jazyka.

11. Získaný model je zobrazen prostrednictvím konkrétního pohledu.

26

Page 34: m Asarykova Univerzita Fakulta Informatiky

5.4. VÝBER KONTROLERU

Na popsaném procesu je videt dusledné dodržování pravidla o používání rozhraní místokonkrétních implementací (programování do rozhraní). Spring tak poskytuje v podstate ne-konecné možnosti rozširitelnosti, protože libovolný krok procesu lze upravit implementacípríslušného rozhraní, které se procesu úcastní, a registrací dané konkrétní trídy v aplikacnímkontextu. Mnohé užitecné implementace zmínených rozhraní už však obsahuje samotnýSpring, takže úpravu chování procesu zpracování požadavku lze provést pouhou konfigu-rací v aplikacním kontextu daného servletu.

Nelekejte se množství rozhraní a tríd, které zde byly zmíneny, podrobnejší vysvetleníje teprve pred námi. Pojd’me se ted’ strucne na nekteré konkrétní trídy jednotlivých typupodívat.

5.4 Výber kontroleru

V souboru web.xml jsou požadavky známým zpusobem mapovány na základe svých URLna jednotlivé servlety. Každý servlet tak zpracovává urcitou množinu požadavku, kterávšak muže být velmi rozsáhlá, casto muže jít i o všechny požadavky. V prípade, že by sekód pro zpracování všech techto požadavku nacházel v samotném servletu, byl by nepre-hledný a nespravovatelný. Z toho duvodu je obsluha požadavku rozdelena mezi kontrolery,které mají na starosti vždy konkrétní typ požadavku, címž prispívají k citelnosti aplikace.Rozhodnutí, který kontroler daný požadavek obslouží, má na starosti objekt s rozhranímHandlerMapping. Jak již bylo receno, implicitne je použit objekt trídyBeanNameUrlHandlerMapping, který zjistí URL požadavku a rízení predá tomu kont-roleru, který je v aplikacním kontextu definován pod názvem shodným s tímto URL. Zde jepríklad:

<bean id="urlMapping1"class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">

<property name="order" value="1" /><property name="interceptors"><list><ref bean="logInterceptor" /></list>

</property><property name="alwaysUseFullPath" value="true" />

</bean>

<bean name="/login.html" class="....LoginController" /><bean name="/logout.html" class="....LogoutController" />

<bean id="logInterceptor" class="....LogInterceptor" />

Podívejme se nejdrív na vlastnosti, kterými v tomto príkladu objektBeanNameUrlHandlerMapping konfigurujeme a které lze aplikovat i na objektSimpleUrlHandlerMapping, jemuž se budeme venovat vzápetí.

Vlastnost order udává prioritu tohoto objektu mezi ostatními registrovanými objektys rozhraním HandlerMapping. Objekt s nejnižší hodnotou této vlastnosti má pri výberu

27

Page 35: m Asarykova Univerzita Fakulta Informatiky

5.4. VÝBER KONTROLERU

kontroleru prednost pred objekty s vyšší hodnotou a pred temi, které tuto vlastnost nemají.Následuje objekt s druhou nejnižší hodnotou atd. Vlastnost order lze pridelit objektumvšech tríd, které implementují rozhraní org.springframework.core.Ordered. Patrík nim napríklad i všechny implementace rozhraní ViewResolver.

Pomocí vlastnosti interceptors definujeme seznam interceptoru, které pro všechnykontrolery mapované v daném HandlerMapping objektu provedou jednotné pred- ci po-stzpracování požadavku. Interceptorum se budeme venovat níže.

Hodnota true vlastnosti allwaysUseFullPath ríká, že pro vyhledání kontroleru bude po-užita plná URL. Konkrétne, bude-li požadavek tvaru http://www.abc.cz/neco/cokoliv.html,bude pro srovnání za všech okolností použit retezec /neco/cokoliv.html. V prípade de-faultní hodnoty false bude použita URL relativní k mapování daného objektuDispatcherServlet v souboru web.xml. Bude-li DispatcherServlet mapován na-príklad na cestu /neco/*.html, pak objekt HandlerMapping bude brát pri výberu kontro-leru v úvahu pouze retezec /cokoliv.html.

Po príchodu požadavku s URL http://www.abc.cz/login.html bude objektemHandlerMapping z našeho príkladu vybrán kontroler definovaný v aplikacním kontextupod názvem /login. Všimnete si, že název objektu je zadán prostrednictvím atributu name,nikoliv id. Je to z toho duvodu, že specifikace XML nepovoluje znak zpetného lomítka v ele-mentu id.

BeanNameUrlHandlerMapping dostacuje pro použití v jednoduchých aplikacích. Vý-razne mocnejší implementací rozhraní HandlerMapping je však trídaSimpleUrlHandlerMapping, která umožnuje mapování URL ve stylu souboru proper-ties. Toto mapování lze definovat v externím souboru properties nebo prímo v XML souboruaplikacního kontextu, jak je videt v následujícím príkladu.

<bean id="urlMapping2"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

<property name="order"><value>2</value></property><property name="interceptors"><list><ref bean="logInterceptor" />

</list></property><property name="mappings"><props><prop key="/index.html">homeController</prop><prop key="/exception.html">exceptionController</prop>

</props></property><property name="defaultHandler" value="defaultController" />

</bean>

Jednou z výhod této implementace je fakt, že máte mapování URL na kontrolery potenciálnecelé aplikace na jednom míste, což výrazne usnadnuje orientaci programátora v aktuálním

28

Page 36: m Asarykova Univerzita Fakulta Informatiky

5.5. INTERCEPTORY

stavu aplikace.Pribyla nám také nová vlastnost - defaultHandler. S jejím využitím lze urcit kontroler,

jenž bude daným objektem HandlerMapping vybrán v prípade, že žádný jiný kontrolervyhovovat nebude. Máme-li v aplikacním kontextu nekolik objektu HandlerMapping, paktuto vlastnost by mel mít až ten úplne poslední, protože její použití fakticky znemožní pre-dání rízení objektum HandlerMapping s nižší prioritou.

V obou uvedených implementacích rozhraní HandlerMapping lze v definici mapovánípoužívat zástupné znaky s významem zástupných znaku nástroje Ant. Následující príkladcerpáme ze vzorové aplikace, která je prílohou této práce, konkrétne ze souboru WEB-INF/spring/webApplicationContext-public.xml.

<bean id="urlMappingForPublic"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="mappings"><props><prop key="/user/*/index.html">userProfileController</prop><prop key="/*/events/*/**/index.html">portalSportEventController</prop>

</props></property>

</bean>

5.5 Interceptory

O základním principu interceptoru už jsme se zmínili. Spring pro ne definuje rozhraníorg.springframework.web.servlet.handler.HandlerInterceptor s následují-cími metodami:

• preHandle - zavolána pred predáním požadavku kontroleru,

• postHandle - zavolána po zpracování požadavku kontrolerem, ale pred zobrazenímmodelu pohledem,

• afterCompletion - zavolána po zobrazení modelu pohledem, umožnuje úklid pro-stredku na konci životního cyklu požadavku.

Spring obsahuje nekolik obecne použitelných implementací tohoto rozhraní, které lze oka-mžite použít. Pokud budete chtít napsat vlastní implementaci, rozširte tríduorg.springframework.web.servlet.handler.HandlerInterceptorAdapter, kteráobsahuje triviální implementace všech uvedených metod.

V priložené vzorové aplikaci lze interceptory nalézt v balíkucz.morosystems.sportportal.interceptors.

5.6 Detekce národního prostredí

V modulu MVC rámce Spring je problém internacionalizace usnadnen existencí takzvanýchdetektoru národního prostredí (objektu typu LocaleResolver). Jakým zpusobem je objekt

29

Page 37: m Asarykova Univerzita Fakulta Informatiky

5.7. VÝBER POHLEDU

typu LocaleResolver zarazen do retezu zpracování HTTP požadavku, jsme si ukázalidríve (viz 5.3). Jeho úcel je jednoduchý. V libovolném kroku zpracování požadavku má zaúkol vrátit objekt typu Locale príslušný tomuto požadavku. Existuje nekolik implementacítohoto rozhraní, které reprezentují ruzné strategie detekce národního prostredí.

5.6.1 Na základe HTTP hlavicek požadavku

Není-li v aplikacním kontextu daného servletu uvedena žádná deklarace s názvemlocaleResolver, je použita defaultní implementace, tedy AcceptHeaderLocaleResolver.Tato implementace urcí Locale požadavku na základe HTTP hlavicek tohoto požadavku.

5.6.2 Na základe preddefinovaného parametru

Vrací vždy stejný Locale. XML definice muže vypadat napríklad takto:

<bean id="localeResolver"class="org.springframework.web.servlet.i18n.FixedLocaleResolver">

<property name="defaultLocale" value="en_US" /></bean>

5.6.3 Na základe uživatelského sezení

Vrací objekt typu Locale uložený v uživatelském sezení (v objektu HttpSession prísluš-ném aktuálnímu uživateli). V prípade, že v uživatelském sezení není uložena žádná instanceLocale, tak je aplikován AcceptHeaderLocaleResolver. Definice:

<bean id="localeResolver"class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

5.6.4 Na základe cookies

Obdoba SessionLocaleResolver s tím rozdílem, že Locale je pro daného uživateleuchováván prostrednictvím klasických cookies. To je užitecné v prípade bezstavových webo-vých aplikací, kde se uživatelská sezení nepoužívají. Definice:

<bean id="localeResolver"class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />

5.7 Výber pohledu

Rekli jsme si, že detektory pohledu (instance rozhraní ViewResolver) mají za úkol trans-formaci logického názvu pohledu na konkrétní šablonu. Ukážeme si príklad definice dvojiceobjektu typu ViewResolver:

30

Page 38: m Asarykova Univerzita Fakulta Informatiky

5.8. ZPRACOVÁNÍ VÝJIMEK

<bean id="resourceBundleViewResolver"class="org.springframework.web.servlet.view.ResourceBundleViewResolver"><property name="order" value="1"/><property name="basename" value="views"/>

</bean>

<bean id="jstlViewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="order" value="2"/><property name="viewClass"

value="org.springframework.web.servlet.view.JstlView"/><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/>

</bean>

Logický název pohledu vrácený kontrolerem bude v takovémto prípade nejdríve zkontro-lován, zda náhodou nezacíná prefixem „redirect:“. Pokud ano, pak dojde ke klasickémuHTTP presmerování na adresu uvedenou za prefixem „redirect:“. V opacném prípade budelogický název predán objektu typu ResourceBundleViewResolver a pokud ten neurcíkonkrétní šablonu pohledu, tak na radu prijde objekt typu InternalResourceViewResolver.

V prípade definice s id resourceBundleViewResolver definujeme vlastnost basename,která urcuje základní název pro príslušný ResourceBundle. Konkrétní properties sou-bor je vybrán na základe instance Locale získané prostrednictvím definovaného detek-toru národního prostredí (objektu typu LocaleResolver). Napríklad bude-li mít aktuálníLocale vnitrní hodnotu en_US, tak nejdríve bude v classpath vyhledán soubor views_en_US.properties. Nenachází-li se tam, pak bude vyhledán soubor views_en.propertiesa posledním clánkem retezu je defaultní views.properties. Nalezený soubor pak obsa-huje definice tríd a URL pro logické názvy. Napríklad properties soubor s obsahem:

login.class=org.springframework.web.servlet.view.JstlViewlogin.url=/WEB-INF/jsp/login.jsplogout.class=org.springframework.web.servlet.view.InternalResourceViewlogout.url=/WEB-INF/jsp/logout.jsp

V tomto prípade je logický název login transformován na JSTL šablonu ve specifikovanémumístení a logický název logout pak na klasickou JSP šablonu (bez JSTL podpory).

Definovaný objekt s id jstlViewResolver se naproti tomu pokusí transformovat lo-gický název tak, že k nemu predradí zadaný prefix a priradí zadaný sufix. Existuje-li šablonave výsledné ceste, je použita.

V priložené vzorové aplikaci lze definici detektoru pohledu nalézt v souboruWEB-INF\spring\webApplicationContext-common.xml.

5.8 Zpracování výjimek

Velmi užitecnou vlastností MVC modulu rámce Spring je možnost definovat jednotné zpra-cování výjimek vyprodukovaných behem zpracování požadavku. Následující XML frag-

31

Page 39: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

ment ukazuje, jak toho lze v aplikacním kontextu dosáhnout.

<bean id="exceptionResolver"class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<property name="exceptionMappings"><props><prop key="java.lang.NullPointerException">exception/nullError</prop><prop key="cz.morosystems.sportportal.exceptions.NoSuchAddressException">exception/badURL

</prop></props>

</property><property name="defaultErrorView" value="exception/common" />

</bean>

Takto jsme definovali mapování konkrétních typu zachycených výjimek na logické názvypohledu a zároven logický název pohledu, který bude použit pri zachycení výjimky, kteránebyla explicitne uvedena v mapování. Typicky lze takto produkovat výjimky v kontrole-rech a nechat jejich konzistentní zpracování na instanci SimpleMappingExceptionResolver.

5.9 Kontrolery

Všechny dosud zmínené komponenty MVC modulu rámce Spring hrají infrastrukturní cipomocnou roli. Místem, kde se aplikace skutecne vytvárí, je kontroler. Ve správne navrženéaplikaci kontroler v podstate jen zpracovává parametry požadavku, pristupuje k objektumservisní vrstvy a cerpá z nich model. Pak zvolí správný pohled a model do nej pošle.

Webová vrstva aplikace by mela být co nejužší a nemela by se v ní vyskytovat žádnáaplikacní logika. Ta je obsažena pouze v aplikacní vrstve, aby webová vrstva mohla býtsnadno nahrazena napríklad grafickým rozhraním Swing ci aby funkce aplikace mohly býtsnadno zprístupneny pomocí webové služby.

Rámec Spring kontrolerum, jako klícové cásti návrhového vzoru MVC, samozrejme ve-nuje adekvátní pozornost a poskytuje komplexní hierarchii rozhraní a tríd, které programá-torum pri tvorbe webové aplikace výrazne ulehcují práci. Stežejní komponenty této hierar-chie jsou zachyceny diagramem 5.1, který byl publikován v [3].

Hlavní roli hraje rozhraní org.springframework.web.servlet.mvc.Controller,se kterým pracuje DispatcherServlet ci presneji SimpleControllerHandlerAdapter(viz 5.3). Jedinou metodou tohoto rozhraní je

ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res)

Metode je predán odkaz na objekty požadavku i odpovedi, címž se stává stejne mocnou jakometody doGet a doPost trídy javax.servlet.http.HttpServlet. Pokud je návrato-vou hodnotou null, pak tím autor kontroleru signalizuje, že požadavek byl v kontroleruvyrízen a odpoved’ byla sestavena. Další kroky životního cyklu požadavku (viz 5.3) jsouvypušteny a odpoved’ je odeslána klientovi.

32

Page 40: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

Obrázek 5.1: Hierarchie kontroleru

5.9.1 Reprezentace modelu a pohledu

Trídou návratové hodnoty metody handleRequest jeorg.springframework.web.servlet.ModelAndView. Tato trída predstavuje dvou-složkovou komponentu, obsahující jak model, tak pohled. Zatímco model je vždy ucho-váván v podobe instance trídy java.util.Map, pohled muže mít dve ruzné formy. Prvníz nich je konkrétní objekt implementující rozhraní org.springframework.web.servlet.View,jenž se prímo postará o zobrazení modelu. Druhou je symbolický název pohledu, který budeteprve zpracován detektorem pohledu (objektem typu ViewResolver) a preveden na kon-krétní objekt typu View.

Ukažme si ted’ implementaci triviálního kontroleru na príkladu:

public class HomeController implements Controller {public ModelAndView handleRequest(HttpServletRequest req,

HttpServletResponse res) throws Exception {

33

Page 41: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

Map model = new HashMap();model.put("now",new Date());return new ModelAndView("home",model);

}}

Vidíme, že metoda vrací ModelAndView, obsahující symbolický název pohledu home amodel s jedinou položkou.

Prestože tvorba kontroleru prímo implementujících rozhraní Controller je možná,vhodnejší je využití nekteré z existujících tríd z uvedené hierarchie. Ruzné trídy reší ruznébežné situace, takže abychom se mohli pro nejakou rozhodnout, musíme alespon o technejpoužívanejších neco málo vedet. Existují tri základní scénáre použití kontroleru rámceSpring.

5.9.2 Kontrolery zobrazení

Nejbežnejším scénárem je ten, kdy chceme na uživatelský HTTP GET požadavek reagovatprostým zobrazením urcitých dat. V takovém prípade je vhodné pri tvorbe vlastního kontro-leru rozšírit abstraktní tríduorg.springframework.web.servlet.mvc.AbstractController a implementovatabstraktní metodu ModelAndView handleRequestInternal(HttpServletRequest,HttpServletResponse). Tato abstraktní trída obsahuje casto používanou funkcionalitua pomocí konfigurace lze snadno nastavit zejména seznam povolených metod HTTP po-žadavku (GET, POST, ...) a pravidla kešování zobrazené stránky. Užitecným rozšírením tétoabstraktní trídy je konkrétní trídaorg.springframework.web.servlet.mvc.ParameterizableViewController, kteránavíc obsahuje vlastnost viewName pro možnost konfigurace logického názvu pohleduv konfiguracním souboru. Príklad implementace prevezmeme z priložené vzorové aplikace,konkrétne z trídycz.morosystems.sportportal.controllers.ClientUsersListController.

public class ClientUsersListController extends ParameterizableViewController{

private UserManager userManager;

public ModelAndView handleRequestInternal(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {

Map model = new HashMap();List<User> users = getUserManager().getAllClientUsers();Collections.sort(users);model.put("users", users);return new ModelAndView(getViewName(), "model", model);

}

34

Page 42: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

... get/set metody pro atribut userManager ...}

A príslušné deklarace v konfiguracním souboru aplikacního kontextu (v priložené aplikaciv souboru WEB-INF\spring\webApplicationContext-admin.xml) vypadají takto:

<bean id="urlMappingForAdmin"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="order" value="3" /><property name="mappings"><props>...<prop key="/secure/admin/client-users/list.html">clientUsersListController

</prop>...

</props></property><property name="alwaysUseFullPath" value="false" />

</bean>

<bean id="clientUsersListController"class="cz.morosystems.sportportal.controllers.ClientUsersListController" ><property name="supportedMethods" value="GET,HEAD" /><!-- hodnota 0 predstavuje zákaz cachování, pri -1 nebudou nastaveny

žádné cachovací HTTP hlavicky --><property name="cacheSeconds" value="60" /><property name="userManager" ref="userManager" /><property name="viewName" value="secure/admin/users/client/list"/>

</bean>

V príkladu v zájmu zestrucnení neuvádíme deklaraci servisního objektu s id userManager.V naší tríde ClientUsersListController jsme definovali JavaBeans vlastnost userManager,vlastnost viewName je soucástí rozširované trídy ParameterizableViewController,ostatní vlastnosti jsou soucástí trídy AbstractController. Vlastnost supportedMethodsdefinuje cárkou oddelený seznam povolených typu HTTP požadavku, atribut cacheSecondspak ríká, jak dlouhé kešování dané stránky bude nastaveno v hlavickách HTTP odpovedi.

Pristupovat k modelu v JSP/JSTL šablone lze pomocí standardních znacek JSTL:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

<html><head><title>Seznam uživatelu</title></head><body><h1>Seznam uživatelu</h1><ul><c:forEach items="${model.users}" var="user"><li><c:out value="${user.name}"/></li>

35

Page 43: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

</c:forEach></ul>

</body></html>

5.9.3 Jednoduchý formulárový kontroler

Ponekud složitejší situace nastává pri dalším bežném scénári, pri manipulaci s HTML for-mulári. Pro tyto prípady existuje v uvedené hierarchii kontroleru abstraktní trídaorg.springframework.web.servlet.mvc.AbstractFormController, kterou ovšemrozširují dve užitecnejší trídy. První z nich je trídaorg.springframework.web.servlet.mvc.SimpleFormController, která je vhodnápro práci s jednoduchými jednostránkovými formulári. Použijeme-li jako predka pro náškontroler tuto trídu, mužeme využít kustomizace kteréhokoliv z kroku následujícího tokurízení:

1. Na URL, kterou obsluhuje daný kontroler, prichází HTTP GET požadavek. Kontrolertedy ví, že jde o požadavek na první zobrazení formulárového pohledu.

2. Je zavolána metoda kontroleru formBackingObject, která vrací tzv. príkazový (com-mand) neboli formulárový (form) objekt, jehož úkolem bude zapouzdrit data manipu-lovaná ve formulári. Tento objekt opet musí vyhovovat standardu JavaBeans. Vrácenýobjekt muže být bud’ prázdný (prázdný formulár) nebo získaný napríklad z databáze(formulár s predvyplnenými daty již pri prvním zobrazení).

3. Je zavolána metoda kontroleru initBinder, jejímž prekrytím lze upravit mechanis-mus konverze retezcových dat formuláre na atributy (casto neretezcového typu) for-mulárového objektu a obrácene. Pri konverzi jsou opet použity editory vlastností (viz4.4) a v metode initBinder je v podstate možno registrovat konkrétní editor vlast-ností na konkrétní vlastnost formulárového objektu.

4. Je zavolána metoda kontroleru referenceData, vracející model, jenž je potreba prozobrazení formulárového pohledu.

5. Je zobrazen formulárový pohled definovaný v konfiguracním souboru pomocí vlast-nosti formView. V tomto pohledu jsou svázány vlastnosti formulárového objektus príslušnými formulárovými poli (toto svázání je rešeno prostrednictvím speciálníknihovny znacek, kterou predstavíme v následujícím oddíle) a hodnoty vlastností jsouzobrazeny jako implicitní hodnoty v príslušných formulárových polích.

6. Uživatel edituje formulárová data a odesílá formulár, pri cemž POST požadavek jeodeslán na tutéž URL.

7. Kontroler detekuje HTTP POST požadavek, což je pro nej príkaz, aby spustil druhoucást toku rízení.

8. Formulárový objekt je pozmenen na základe uživatelem zadaných dat ve formulári.

36

Page 44: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

9. Je zavolána metoda kontroleru onBind.

10. Pokud je nastavena vlastnost kontroleru validateOnBinding na true, tak jsou naformulárový objekt aplikovány všechny registrované validátory, které mají za úkoloverit prípustnost uživatelem zadaných dat. Validátory musí implementovat rozhraníorg.springframework.validation.Validator a zejména metoduvalidate(Object, Errors). Všechny chyby pri validaci jsou zaznamenány v ob-jektu s rozhraním org.springframework.validation.Errors.

11. Je zavolána metoda kontroleru onBindAndValidate, kde lze provést další validacea registrovat další chyby do objektu typu Errors.

12. Jsou-li v objektu Errors registrovány nejaké chyby, pak je znovu zobrazen formulá-rový pohled spolu s uživatelem zadanými daty v príslušných formulárových polích.Na základe informací v objektu Errors lze vypsat hlášení o chybách vstupu. Násle-dují kroky od bodu 4.

13. Nejsou-li v objektu Errors registrovány žádné chyby, pak je zavolána metoda onSubmit,která provede prostrednictvím aplikacní vrstvy finální zpracování formulárového ob-jektu, napríklad jeho uložení v databázi.

Výhodou tohoto rešení je možnost použití objektu z doménového modelu jako formuláro-vého objektu, aniž bychom vždy museli využívat speciálních transferových objektu a pre-vádet data z doménového objektu do transferového a naopak.

Napríklad chceme-li editovat údaje systémového uživatele reprezentovaného trídou User,tak prekryjeme metodu formBackingObject tak, aby získala príslušnou instanci trídyUser z databáze a vrátila ji. Aktuální údaje systémového uživatele budou zobrazeny veformulári. Po jejich editaci uživatelem aplikace bude instance trídy User poslána zpet kon-troleru, který ji pouze predá servisní vrstve k aktualizaci v databázi. Odpadá tak nutnostmanuálního zpracování parametru HTTP požadavku ci použití DTO (Data Transfer Object).

Práve popsaný príklad ilustrujeme na skutecném, jen mírne zjednodušeném formuláro-vém kontroleru (cz.morosystems.sportportal.controllers.UserEditFormController)z priložené vzorové aplikace.

public class UserEditFormController extends SimpleFormController {

private UserManager userManager;

protected Object formBackingObject(HttpServletRequest request)throws Exception {

String userID = request.getParameter("id");//na základe parametru požadavku id získáme uživatele z databáze k editaciUser user = userManager.getUserByID(userID);/* není-li uživatel pro dané id v databázi, vyhodíme výjimku, která bude

zpracována prostrednictvím objektu typu ExceptionResolver definovaného

37

Page 45: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

v konfiguracním souboru (viz oddíl 4.8)

*/if (user==null) throw new NoSuchAddressException();return user;

}

public ModelAndView onSubmit(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)throws ServletException {

/* formulárový objekt je typu uživatel a je následne prostrednictvímmanažera uložen do databáze */

User user = (User)command;getUserManager().setUser(user);/* následne zobrazený pohled je definován prostrednictvím vlastnosti

’successView’ v konfiguracním souboru */return new ModelAndView(getSuccessView());

}

... get/set metody pro atribut userManager ...}

Trída User ve své nejjednodušší podobe by vypadala takto:

public class User {private String id;private String firstname;private String surname;private String email;

... get/set metody všech atributu ...}

Skutecná trída cz.morosystems.sportportal.pojo.User ovšem obsahuje množstvídalších vlastností a metod. Príslušné definice v konfiguracním XML souboru jsou následu-jící:

<bean id="urlMappingForAdmin"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="order" value="3" /><property name="mappings"><props>...<prop key="/secure/admin/admin-users/edit.html">adminUserEditFormController

</prop>...

</props></property>

38

Page 46: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

<property name="alwaysUseFullPath" value="false" /></bean>

<bean id="adminUserEditFormController"class="cz.morosystems.sportportal.controllers.UserEditFormController">

<property name="sessionForm" value="true"/><property name="commandName" value="user"/><property name="commandClass" value="cz.morosystems.sportportal.pojo.User"/><property name="validator" ref="userEditFormValidator"/><property name="formView" value="secure/admin/users/admin/edit"/><property name="successView" value="redirect:list.html"/><property name="userManager" value="userManager" />

</bean>

<bean id="userEditFormValidator"class="cz.morosystems.sportportal.validators.UserEditFormValidator" >

<property name="userManager" value="userManager" /></bean>

Atribut sessionForm indikuje, zda má být pro uložení formulárového objektu mezi GETa POST požadavkem uživatele použito uživatelské sezení. V prípade, že bychom tuto mož-nost zakázali, nebylo by možno editovat již existující údaje získané z databáze, tak jako jsmeto predvedli v predcházejícím príklade, a bylo by možno pracovat v podstate pouze s ini-ciálne prázdnými formulári. Atribut commandName urcuje název, pod kterým bude formu-lárový objekt prístupný ve formulárovém pohledu jako atribut HTTP požadavku. AtributcommandClass definuje trídu formulárového objektu, atributy formView a successViewpak specifikují formulárový a finální pohled. No a konecne atribut validator se odkazujena instanci s rozhraním Validator, která vypadá takto:

import org.apache.commons.validator.EmailValidator;import org.springframework.validation.ValidationUtils;import cz.morosystems.sportportal.pojo.User;

public class UserEditFormValidator implements Validator {

private UserManager userManager;

public boolean supports(Class clazz) {return clazz.equals(User.class);

}

public void validate(Object object, Errors errors) {User user = (User) object;ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email","error.user.email.required", "Value can’t be empty");

if (!EmailValidator.getInstance().isValid(user.getEmail())) {errors.rejectValue("email", "error.user.email.not-valid", null,

39

Page 47: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

"Value must be in correct format.");}

}

... get/set metody pro atribut userManager ...}

Metoda supports urcuje, pro které trídy formulárových objektu je tento validátor urcen.V metode validate pak kontrolujeme pouze vlastnost email formulárového objektu.Nejdríve overíme, zda uživatel nezadal prázdnou hodnotu a pokud ano, tak v objektuerrors zaregistrujeme chybu pro vlastnost email s chybovým hlášením specifikovanýmpomocí i18n klíce error.user.email.required. Pri zobrazení chyby ve formulárovém pohledupak bude tento klíc využit pro internacionalizaci hlášení. Pri registraci chyby jsme navícješte uvedli defaultní zprávu, která bude zobrazena v prípade, že pro daný klíc nebude na-lezena žádná hodnota ve zdroji zpráv (viz 4.7). Následne pak podobne overíme i správnostformátu zadaného e-mailu.

Zdokumentovaný príklad komplexního formulárového kontroleru predstavuje trídacz.morosystems.sportportal.controllers.ExistingCompetitorsFormControllerv priložené vzorové aplikaci.

5.9.4 Knihovny znacek JSP

Vzhledem k blízké príbuznosti s tématem formulárového kontroleru, který jsme si predsta-vili v predchozím oddíle, si ted’ ukážeme práci s knihovnou znacek JSP rámce Spring promanipulaci s formulári. Tato knihovna je novinkou rámce Spring od verze 2.0 a je identifi-kována pomocí URI http://www.springframework.org/tags/form. Jde v podstate o saduznacek, které zjednodušují provázání atributu (vlastností) formulárových objektu s formu-lárovými poli.

V predchozích verzích rámce Spring se k tomuto úcelu používala obecná knihovna zna-cek JSP identifikovaná prostrednictvím URI http://www.springframework.org/tags, kteráje i ve verzi 2.0 stále prítomna. K provázání vlastností formulárového objektu s formuláro-vými poli sloužily znacky bind, nestedPath a hasBindErrors.

Náš príklad s editací údaju uživatele tedy uzavreme jednoduchým poupraveným príkla-dem JSP/JSTL šablony (/WEB-INF/jsp/secure/admin/users/admin/edit.jsp v pri-ložené vzorové aplikaci), kde bude demonstrováno použití znacek bind a hasBindErrorsz obecné knihovny znacek JSP.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %><%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %><%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html><head><title>Uživatel - editace údaju</title></head><body>

40

Page 48: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

<div class="chyby"><%-- název formulárového objektu ’user’ byl definován vlastností’commandName’ v definici kontroleru v konfiguracním souboru (viz výše) --%>

<spring:hasBindErrors name="user"><p class="chyba"><fmt:message key="error.email.index.chyby"/></p><ul><c:forEach items="${errors.allErrors}" var="error" ><li style="color:red"><fmt:message key="${error.code}" /></li>

</c:forEach></ul>

</spring:hasBindErrors></div>

<form method="post" action="" ><spring:bind path="user.firstname"><input type="text" name="<c:out value="${status.expression}" />"

value="<c:out value="${status.value}"/>" /><c:out value="${status.errorMessage}"/>

</spring:bind><spring:bind path="user.surname">príjmení: <input type="text"

name="<c:out value="${status.expression}"/>"value="<c:out value="${status.value}"/>" />

<c:out value="${status.errorMessage}"/></spring:bind><spring:bind path="user.email">e-mail: <input type="text"

name="<c:out value="${status.expression}" />"value="<c:out value="${status.value}"/>" />

<c:out value="${status.errorMessage}"/></spring:bind>

<input type="submit" value="Upravit" /></form>

</body></html>

V první cásti jsme souhrnne zobrazili všechny chybové zprávy, pokud nejaké existují, v sa-motném formulári jsme pak definovali jednotlivá formulárová pole a pomocí znacky binda jejího atributu path jsme je namapovali na vlastnosti firstname, surname a email formulá-rového objektu s názvem user. Promenná s názvem status, dostupná uvnitr znacky bind,predstavuje danou mapovanou vlastnost formulárového objektu. Promenná status.valueobsahuje aktuální hodnotu vlastnosti, status.expression plnou cestu daného atributu(napr. user.email) a status.errorMessage pak prípadné chybové hlášení registrovanék dané vlastnosti.

Pomocí nové knihovny znacek pro manipulaci s formulári bychom tutéž šablonu navrhlitakto:

41

Page 49: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %><%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %><%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html><head><title>Uživatel - editace údaju</title></head><body>

<form:form commandName="user" action="" method="post"><div class="chyby"><form:errors path="*" cssStyle="color:red" />

</div>jméno: <form:input path="firstname" />

<form:errors path="firstname" />príjmení: <form:input path="surname" />

<form:errors path="surname" />email: <form:input path="email" />

<form:errors path="email" /><input type="submit" value="Upravit" />

</form:form>

</body></html>

Je videt výrazné zjednodušení a odstranení opakujícího se kódu. Bohužel v této práci ne-máme prostor pro demonstraci složitejších možností obou knihoven znacek ve spoluprácis formulárovými kontrolery, které jsou ovšem k videní v priložené vzorové aplikaci. Jdezejména o demonstraci dalších formulárových prvku (select boxy, tlacítka typu radio atd.,viz napr. WEB-INF\jsp\secure\client\competitors\create-team.jsp), mapováníparametru požadavku na kolekce objektu(cz.morosystems.sportportal.controllers.CompetitorCreateTeamFormController.initBinder() ve spolupráci s editorem vlastnostícz.morosystems.sportportal.editors.SportBranchEditor) apod.

5.9.5 Kontroler pro vícekrokové formuláre

Chceme-li v aplikaci použít vícestránkové ci vícekrokové formuláre, pak je vhodné sáhnoutpo druhém z formulárových kontroleru a rozšírit tríduorg.springframework.web.servlet.mvc.AbstractWizardFormController. Tokrízení tohoto kontroleru je ješte výrazne složitejší než je tomu u trídy SimpleFormControllera je možné jej nastudovat v javadoc dokumentaci trídy. Shrnme jen strucne jeho funkciona-litu.

Návrhár kontroleru definuje sadu pohledu, které predstavují jednotlivé kroky vícekro-kového formuláre. Tyto kroky jsou identifikovány svým poradím, a to nulou pocínaje. Prvníkrok má tedy index 0, druhý index 1 atd. Prechod mezi jednotlivými kroky je realizovánpomocí detekce parametru požadavku s názvem _targetX, kde X je index následujícího

42

Page 50: m Asarykova Univerzita Fakulta Informatiky

5.9. KONTROLERY

kroku. Toho je typicky dosahováno pomocí odesílacího prvku dané cásti formuláre, tedynapríklad takto:

<input type="submit" name="_target2" value="Další krok" /><input type="submit" name="_target0" value="Krok zpet" />

Podobne je tomu s parametrem požadavku s názvem _finish, který indikuje definitivníukoncení a zpracování všech dosud sebraných dat v jednotlivých krocích (v kontroleru jezavolána metoda onFinish), a s parametrem požadavku s názvem _cancel, jehož prí-tomnost kontroler interpretuje jako požadavek ukoncení práce s formulárem bez zpracovánísebraných dat (je zavolána metoda onCancel).

Na pozadí práce s formulárem je rovnež v akci formulárový objekt, na nejž jsou jednot-livá formulárová pole jednotlivých kroku mapována. Tento objekt je prubežne validován ave finále zpracován a napríklad uložen do databáze.

Príkladem komplexního a zdokumentovaného vícekrokového kontroleru je trídacz.morosystems.sportportal.controllers.SportEventCreateFormControllerz priložené vzorové aplikace.

5.9.6 Kontroler pro více podobných akcí

Tento kontroler, tedy kontroler trídyorg.springframework.web.servlet.mvc.multiaction.MultiActionController,je jako predek využíván v prípadech, kdy by jinak bylo nutno nasadit nekolik jednoduchýchkontroleru, jejichž funkcnost by byla do znacné míry podobná. Príkladem takového kontro-leru je trídacz.morosystems...controllers.PublicSportEventRenderStructureControllerz priložené vzorové aplikace (viz také príslušné definice v konfiguracním XML souboru).

5.9.7 Jednorázový kontroler

Rozhraní org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerje vydeleno z celé dosud popisované hierarchie kontroleru. Dosud jsme predpokládali, ženaše implementace kontroleru jsou bezstavové a že tedy budou v rámci naší aplikace de-finovány jako jedinácci (Singleton). Tyto kontrolery (a to se týká zejména formulárovýchkontroleru) pak pracují s docasnými formulárovými objekty, které drží stav.

Návrh rozhraní ThrowawayController byl inspirován zejména pojetím kontroleru(neboli akcí) v aplikacních rámcích WebWork ci Maverick. ThrowawayController je vy-tváren vždy znovu pro každý požadavek a obsahuje docasná data stejne jako aplikacní lo-giku, která s nimi pracuje. Jde tedy o jakýsi kontroler a príkazový ci formulárový objektv jednom.

Tento typ kontroleru je však soucástí rámce Spring prece jen spíše pro ty uživatele, kteríjsou na podobnou filozofii zvyklí, než pro zacátecníky, kterí se s webovým MVC návrhovýmvzorem teprve seznamují.

43

Page 51: m Asarykova Univerzita Fakulta Informatiky

Kapitola 6

Rámec Spring - AOP

Aspektove orientované programování (Aspect-oriented programming, AOP) se v poslednídobe stalo velmi populární technikou napomáhající zprehlednení vývoje složitých systému.

Nemáme prostor na detailní rozbor této programovací techniky, takže se spokojíme s vý-stižnou charakteristikou Tomáše Pitnera v [4]: „AOP v zásade smeruje k tomu, jak ele-gantne dosáhnout vyclenení opakovaných „nezáživných“, „režijních“ cástí kódu, rešícíchobvykle nejaké (mimofunkcní, napríc jdoucí) zámery (cross-cutting concerns, napr. auten-tizaci/autorizaci klienta pri vstupu do urcité metody, protokolování volání metod, prístupk perzistentnímu úložišti dat . . . ) do tzv. pokynu (advices). Tyto jsou vycleneny do speciál-ních tríd a deklarativním zpusobem je receno, kam všude se príslušný pokyn má aplikovat(ta místa se oznacují jako point-cuts). Behové prostredí príslušného nástroje pro AOP pakzajistí aplikaci príslušného pokynu a vzniká tak tzv. aspekt (aspect).“

AOP funkcionalita tvorí klícovou složku rámce Spring, který je casto oznacován jako IoCa AOP kontejner. Možnosti AOP jsou zde opravdu pomerne široké a základní principy si napríkladech predvedeme i my.

V rámci Spring je AOP využíváno jako prostredek pro:

1. poskytnutí deklarativních služeb, zejména tedy deklarativního transakcního manage-mentu,

2. tvorbu uživatelských aspektu.

Jednotlivé stavební prvky AOP modulu si ted’ strucne predstavíme.

6.1 Pokyny (advices)

Rámec Spring podporuje vetšinu v AOP komunite používaných typu pokynu. Temi základ-ními jsou pokyny-interceptory (Interception Around Advice), které jsou aplikovány pred ipo vyvolání metody, dále predsazené pokyny (Before Advice), které se volají pouze pred vy-voláním metody, pokyny výjimek (Throws Advice), které jsou volány pouze pokud cílovámetoda vyhodí výjimku konkrétního typu, a konecne pokyny po návratu (After ReturningAdvice), které se provedou pouze po úspešném návratu rízení z cílové metody.

Ukážeme si príklad nejpoužívanejšího typu pokynu, tedy pokynu-interceptoru. Príkladje prevzat z [5] a mírne upraven.

44

Page 52: m Asarykova Univerzita Fakulta Informatiky

6.2. DEFINICE CÍLU (POINTCUTS)

import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;

public class MessageDecorator implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) throws Throwable {System.out.print("Startuji dekoraci ...");Object retVal = invocation.proceed();System.out.println("Koncím dekoraci ...");return retVal;

}}

Vidíme, že pokyn implementuje rozhraní MethodInterceptor a jeho metodu invoke,v jejímž prubehu zavolá cílovou metodu a její návratovou hodnotu nakonec také vrátí. Jsoupoužita standardní rozhraní AOP Alliance API, takže knihovnu s tímto API je nutno umístitdo classpath každé aplikace využívající AOP funkcionality rámce Spring.

6.2 Definice cílu (pointcuts)

Na které metody mají být pokyny aplikovány, je urceno prostrednictvím definic cílu, kterémají za úkol identifikovat objekty a jejich konkrétní metody, na než bude daný pokyn apliko-ván. Definice cílu jsou v rámci Spring reprezentovány rozhranímorg.springframework.aop.Pointcut, jehož implementace muže vypadat napríkladtakto:

import org.springframework.aop.ClassFilter;import org.springframework.aop.MethodMatcher;import java.lang.reflect.Method;

public class SamplePointcut implements Pointcut {

public ClassFilter getClassFilter() {return new ClassFilter() {public boolean matches(Class clazz) {... vrátí true pro trídy, které vyhovují ...

}};

}

public MethodMatcher getMethodMatcher() {return new MethodMatcher() {public boolean isRuntime() {... vrátí true pro dynamické definice cílu nebo false

pro statické definice cílu ...}public boolean matches(Method m, Class targetClass) {

45

Page 53: m Asarykova Univerzita Fakulta Informatiky

6.3. ASPEKTY (ADVISORS)

... vrátí true pro metody a trídy, které vyhovují ...}public boolean matches(Method m, Class targetClass, Object[] args) {... vrátí true pro metody, její parametry a trídy, které vyhovují ...

}};

}}

Tato definice cílu je schopna pro jakoukoliv trojici {trída, metoda, parametry metody} urcit,zda vyhovuje definici cílu ci nikoliv. Nejdríve bude overena prípustnost trídy prostrednic-tvím volání getClassFilter().matches(Class) a v prípade kladné návratové hod-noty se prejde k overení metody. Vrací-li metoda getMethodMatcher().isRuntime()hodnotu true, pak mluvíme o takzvané dynamické definici cílu (dynamic pointcuts), vrací-li hodnotu false, pak jde o statickou definici cílu (static pointcut). Statické definice cílu jsouuprednostnovány vzhledem k možnosti prekladu a vyhodnocení již pri tvorbe tzv. AOPproxy objektu (viz 6.4).

6.3 Aspekty (advisors)

Aspekty jsou v rámci Spring reprezentovány instancemi rozhraníorg.springframework.aop.Advisor. Jde v podstate o kombinaci pokynu a definicecílu v jediném objektu. Nejpoužívanejší trídou pro implementaci aspektu jeorg.springframework.aop.support.DefaultPointcutAdvisor. Tato trída podpo-ruje všechny hlavní typy pokynu. Ukažme si deklaraci dosud uvedených tríd v konfigurac-ním souboru aplikacního kontextu.

<bean id="messageDecorator" class="....MessageDecorator" />

<bean id="samplePointcut" class="....SamplePointcut" />

<bean id="sampleAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor" >

<property name="advice" ref="messageDecorator" /><property name="pointcut" ref="samplePointcut" />

</bean>

Užitecnou základní trídou rámce Spring pro implementaci aspektu je iorg.springframework.aop.support.RegexpMethodPointcutAdvisor, který vy-užívá standardních JSE regulárních výrazu pro specifikaci definice cílu. Vývojári pak zbýváimplementovat již jen vlastní pokyn:

<bean id="setAndSendAdvisor"class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

<property name="advice" ref="messageDecorator"/><property name="patterns">

46

Page 54: m Asarykova Univerzita Fakulta Informatiky

6.4. AOP PROXY

<list><value>.*set.*</value><value>.*send.*</value>

</list></property>

</bean>

Posledním prvkem procesu použití AOP modulu rámce Spring jsou AOP proxy objekty.

6.4 AOP proxy

Chceme-li vytvorený aspekt použít v naší aplikaci, musíme nejdrív zajistit, aby se aspektzaradil do retezce zpracování metod našich tríd. Toho se v rámci Spring dosahuje tak, žepro cílový objekt (target object), tedy instanci naší trídy, je vytvoren tzv. AOP proxy objekt,jenž obaluje cílový objekt a „vydává“ se za objekt puvodní, pri tom však už jde o kombinacicílového objektu a aplikovaného pokynu. AOP proxy objekty lze vytváret dvema zpusoby:

1. použitím dynamických proxy, standardní to funkcionality JSE (Java Standard Edition),

2. zapojením knihovny CGLIB, tedy knihovny pro behovou úpravu Java kódu.

První rešení je uprednostnováno, nebot’ nevnáší do výsledné aplikace další závislost naexterní knihovne. Lze tak ovšem pracovat pouze s cílovými objekty implementujícími nejakérozhraní. Vytvorený AOP proxy objekt pak lze pretypovat pouze na toto rozhraní, nikoli nakonkrétní trídu cílového objektu.

Použití CGLIB umožnuje aplikaci aspektu i na konkrétní trídy.Tvorba AOP proxy objektu za použití standardních dynamických proxy je v rámci Spring

realizována prostrednictvím trídyorg.springframework.aop.framework.ProxyFactoryBean. Uved’me si príklad vy-užívající výše definovaný aspekt a dríve uvedenou (oddíl 4.6) servisní trídu MailManagerImpl:

<bean id="mailManagerTarget"class="cz.morosystems.sportportal.managers.MailManagerImpl">

<property name="mailSender" ref="mailSender"/><property name="commonFrom" value="[email protected]" />

</bean>

<bean id="mailManager"class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="proxyInterfaces"value="cz.morosystems.sportportal.managers.MailManager" />

<property name="target" ref="mailManagerTarget"/><property name="interceptorNames"><list><value>sampleAdvisor</value>

</list></property>

47

Page 55: m Asarykova Univerzita Fakulta Informatiky

6.5. DEKLARATIVNÍ TRANSAKCNÍ MANAGEMENT

</bean>

<bean id="nejakyKontroler" class="....NejakyKontroler"><property name="mailManager" ref="mailManager" />

</bean>

Puvodní deklarace manažerského objektu trídy MailManagerImpl je zde zminovanýmcílovým objektem. Tretí definicí v príkladu je kontroler, který má vlastnost (JavaBeans pro-perty) trídy MailManager. Dosud byl propojen prímo s cílovým objektem trídy MailManagerImpl,ovšem chceme-li aplikovat na toto rozhraní náš aspekt, musíme prinutit kontroler používatobalující AOP proxy objekt. A to je práve úkolem instance trídy ProxyFactoryBean, kterápredstavuje tovární objekt (factory object) aplikující seznam zadaných pokynu (v našemprípade pouze jeden) v definovaném poradí na specifikovaný cílový objekt. Tento továrníobjekt vytvorí novou dynamickou proxy, která implementuje seznam rozhraní specifikova-ných atributem proxyInterfaces. Výsledkem tedy je, že kontroler získá odkaz na novevytvorený AOP proxy objekt a pracuje s ním prostrednictvím rozhraní MailManager.

Zavoláme-li v kontroleru metodu sendActivationEmail, tak bude nejdríve dotázánadefinice cílu, zda se má aplikovat specifikovaný pokyn. V prípade kladné odpovedi budevyvolána metoda invoke pokynu a skrze ni pak i cílová metoda sendActivationEmailcílového objektu trídy MailManagerImpl.

6.5 Deklarativní transakcní management

Typickým príkladem využití AOP funkcionality rámce Spring pro poskytnutí deklarativ-ních služeb spravovaným objektum je deklarativní transakcní management.

Zabíháme ted’ sice trochu do tématu podpory datové vrstvy rámcem Spring, kterému sebudou venovat navazující diplomové práce, ale predvedeme si jen strucný príklad s dura-zem na jeho význam pro pochopení AOP funkcionality rámce Spring.

Vzhledem k tomu, že v priložené vzorové aplikaci používáme na datové vrstve ORMnástroj Hibernate, tak i následující príklad využívá definice tríd specifických pro podporurámce Spring práve tomuto nástroji.

<bean id="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/>

</bean>

<bean id="transactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor"><property name="transactionManager" ref="transactionManager"/><property name="transactionAttributeSource"><value>

cz...MailManager.sendActivation*=PROPAGATION_REQUIREDcz...MailManager.sendEmailWithNewCompetitorLoginData=PROPAGATION_MANDATORYcz...MailManager.sendEmail=PROPAGATION_REQUIRED,-MyCheckedException</prop>

48

Page 56: m Asarykova Univerzita Fakulta Informatiky

6.5. DEKLARATIVNÍ TRANSAKCNÍ MANAGEMENT

</value></property>

</bean>

<bean id="mailManager"class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="proxyInterfaces"value="cz.morosystems.sportportal.managers.MailManager" />

<property name="target" ref="mailManagerTarget"/><property name="interceptorNames"><list><value>transactionInterceptor</value><value>sampleAdvisor</value>

</list></property>

</bean>

V zájmu využít predchozích príkladu, kde figuruje servisní trída MailManager, jsme apli-kovali deklarativní transakcní management na tuto trídu. To má ovšem smysl pouze v prí-pade, že tato servisní trída pracuje s Hibernate DAO vrstvou, což tedy tiše predpokládáme.

Na príkladu také vidíme, že Hibernate transakcní manažer se odvolává na objektsessionFactory, který nás ovšem momentálne nezajímá. Jinak princip aplikace transakc-ního pokynu reprezentovaného trídou TransactionInterceptor je stejný jako u všechostatních pokynu. Pro aplikaci definovaných transakcních pravidel na konkrétní servisnítrídu je opet nutno pokyn zaradit mezi pokyny AOP proxy objektu této servisní trídy.

Jak už bylo receno, konkrétní význam definovaných pravidel bude vysvetlen v navazují-cích diplomových pracích. Popsané i nekteré pokrocilé možnosti AOP modulu rámce Springlze vcetne dalšího popisu nalézt v priložené vzorové aplikaci, konkrétne v konfiguracnímsouboru WEB-INF\spring\applicationContext-business.xml.

49

Page 57: m Asarykova Univerzita Fakulta Informatiky

Kapitola 7

Zabezpecení aplikací v rámci Spring

Soucástí v podstate každé reálné netriviální JEE aplikace musí být komplexní rízení auten-tizace a autorizace uživatelu systému. Vzhledem k tomu, že jde o tzv. napríc jdoucí (cross-cutting) koncept, je logické, že pro rešení této problematiky vznikají specializované apli-kacní rámce. Jedním z nejpopulárnejších a nejmocnejších bezpecnostních rámcu tohoto typuje v soucasné dobe rámec Acegi Security. Acegi Security od zacátku svého vývoje využívákonfiguracních, IoC a AOP možností rámce Spring a jeho použití bez spolupráce s rám-cem Spring je možné, ale nefrekventované. Svého casu se dokonce mluvilo o sloucení techtodvou projektu, k cemuž však zatím nedošlo. My se podíváme na nejaktuálnejší vývojovouverzi, verzi 1.0 RC2.

7.1 Prípady užití

Bezpecnostní rámec Acegi Security lze v podstate použít v rámci trí odlišných scénáru. Jsoujimi rízení prístupu k metodám objektu servisní vrstvy, rízení prístupu k jednotlivým cás-tem webové aplikace a konecne rízení prístupu k doménovým objektum ve stylu ACL (Ac-cess Control List). Vzhledem k našemu zamerení na webové aplikace se budeme podrobnevenovat zejména druhému uvedenému scénári.

7.2 Architektura

Architektura rámce Acegi Security je pomerne komplexní a poskytuje tak mnoho možností,jak prizpusobit chování rámce vývojárovým potrebám. Predstavíme si její základní prvky.

7.2.1 Chránený cíl

Klícovým bodem celé architektury je samozrejme reprezentace cíle, ke kterému chceme ríditprístup. Pro reprezentaci vyvolání metody objektu servisní vrstvy je použita trídaorg.aopalliance.intercept.MethodInvocation standardu AOP Alliance (viz 6.1)a pro rízení prístupu je využito standardní AOP funkcionality rámce Spring, popsané v pred-chozí kapitole.

Druhým typem chráneného cíle je webový zdroj reprezentovaný trídouorg.acegisecurity.intercept.web.FilterInvocation. Objekt této trídy obsahujeaktuální HTTP požadavek, tedy objekt trídy javax.servlet.http.HttpServletRequest,

50

Page 58: m Asarykova Univerzita Fakulta Informatiky

7.2. ARCHITEKTURA

aktuální HTTP odpoved’, tedy objekt trídy javax.servlet.http.HttpServletResponsea seznam aplikovaných filtru rámcem Acegi Security (viz 7.3).

Pro rízení prístupu k doménovým objektum se využívá služeb sofistikovaného AOPrámce AspectJ a reprezentací chráneného doménového objektu je AspectJ trídaorg.aspectj.lang.JoinPoint.

7.2.2 Prístupové atributy

Každý chránený cíl je definován prostrednictvím konfiguracního XML souboru aplikacníhokontextu rámce Spring a pro každý chránený cíl jsou definovány prístupové atributy. Každýprístupový atribut je reprezentován instancí rozhraní org.acegisecurity.ConfigAttribute,pri cemž pri defaultní implementaci jde v podstate o pouhé zapouzdrení retezce. K danémuchránenému cíli má tedy prístup pouze uživatel (reprezentovaný objektem Authentication- viz následující oddíl), jemuž byly prirazeny prístupové atributy definované pro tento cíl.

7.2.3 Stav autentizace

Stav autentizace aktuálního uživatele je reprezentován rozhranímorg.acegisecurity.Authentication. Jde v podstate o objekt, jenž obsahuje predlo-ženou identitu uživatele (vetšinou uživatelské jméno), která je oznacována jako principál(principal), predložený dukaz identity (vetšinou heslo) a informaci, zda tyto údaje již bylyovereny. Byly-li již overeny, pak objekt Authentication obsahuje i této identite systémempridelená oprávnení (prístupové atributy).

7.2.4 Zpusob uchování stavu autentizace

Objekt Authentication musí být k dispozici pri každém prístupu k chráneným zdrojum,ale zároven není vhodné, aby tento objekt byl predáván z metody do metody prostred-nictvím parametru metody. Z toho duvodu je objekt Authentication pri svém vytvo-rení uložen do objektu s rozhraním org.acegisecurity.context.SecurityContexta ten pak do objektu org.acegisecurity.context.SecurityContextHolder, kterýzajistí svázání objektu Authentication s aktuálním vláknem programu prostrednictvímfunkcionality trídy java.lang.ThreadLocal. Odtud je pak kdykoliv dostupný pomocístatického volání SecurityContextHolder.getContext().getAuthentication().

7.2.5 Rozhodnutí o autentizaci

Pri každém prístupu k chránenému cíli je vyvolána metoda authenticate objektu roz-hraní org.acegisecurity.AuthenticationManager. Této metode je predán objektAuthentication, získaný z objektu SecurityContextHolder. Metoda v databázi, LDAPzáznamech ci jinde overí správnost identity a dukazu a jsou-li správné, získá z databáze,LDAP záznamu ci odjinud všechna bezpecnostní oprávnení (prístupové atributy) pro danouidentitu. Tato oprávnení pak vloží do predloženého objektu Authentication. Staronový

51

Page 59: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

objekt Authentication je opet uložen do objektu SecurityContextHolder. Nejsou-liidentita a dukaz správné, je vyhozena výjimka.

7.2.6 Rozhodnutí o autorizaci

Je-li uživatel úspešne autentizován, dochází k overení jeho oprávnení pristoupit k danémuchránenému cíli. To má na starost objekt rozhraníorg.acegisecurity.AccessDecisionManager, jehož metode decide je predloženareprezentace chráneného cíle, ke kterému chceme pristoupit, objekt Authentication, jenžobsahuje prístupové atributy aktuálního uživatele, a seznam prístupových atributu defino-vaných pro daný chránený cíl. Rozhodne-li AccessDecisionManager kladne, chránenýcíl je uživateli zprístupnen.

V priložené vzorové aplikaci predstavuje trídacz.morosystems.sportportal.spring.FirstWinAccessDecisionManager okomen-tovaný príklad implementace rozhraní AccessDecistionManager.

7.3 Ochrana prístupu k webovým zdrojum

S práve nabytými znalostmi o architekture rámce Acegi Security si ted’ ukážeme popsanétrídy v akci, a to opet s využitím mírne upravené priložené vzorové webové aplikace, kde jeAcegi Security použito pro rízení prístupu uživatelu k jednotlivým webovým zdrojum iden-tifikovaným pomocí URL (viz souborWEB-INF\spring\applicationContext-acegi.xml).

7.3.1 Filtry

Integrace služeb Acegi Security do webové aplikace je realizována pomocí klasických filtruspecifikace servletu. V souboru web.xml webové aplikace tedy musíme uvést následujícídeklaraci:<filter><filter-name>Acegi Filter Chain Proxy</filter-name><filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class><init-param><param-name>targetClass</param-name><param-value>org.acegisecurity.util.FilterChainProxy</param-value>

</init-param></filter>

<filter-mapping><filter-name>Acegi Filter Chain Proxy</filter-name><url-pattern>/*</url-pattern>

</filter-mapping>

Trída FilterToBeanProxy ve skutecnosti pouze deleguje zpracování na objekt (filtr),který je definován v aplikacním kontextu a jehož trída je definována pomocí atributu targetClass.

52

Page 60: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

Tak lze pro klasické filtry naší aplikace využít všech služeb, které aplikacní kontext rámceSpring nabízí. Trída se stejnými delegujícími vlastnostmi existuje i v samotném rámci Spring(org.springframework.web.filter.DelegatingFilterProxy), Acegi Security všakz historických duvodu poskytuje vlastní implementaci.

Situace je však ješte zdánlive komplikována (ve skutecnosti však zjednodušena) tím, žeobjekt trídy org.acegisecurity.util.FilterChainProxy, na který zpracování dele-gujeme, je pouhým zapouzdrením retezce vlastních funkcionalitu poskytujících filtru, rov-než definovaných v aplikacním kontextu. Definice filtru v konfiguracním XML souboru(WEB-INF\spring\applicationContext-acegi.xml), který je nacítán stejným zpu-sobem jako ostatní konfiguracní soubory korenového aplikacního kontextu Spring webovéaplikace (viz 5.2), jsou následující:

<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"><property name="filterInvocationDefinitionSource"><value>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISONPATTERN_TYPE_APACHE_ANT/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,

exceptionTranslationFilter,filterInvocationInterceptor</value>

</property></bean>

<bean id="httpSessionContextIntegrationFilter"class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">

<property name="context"value="org.acegisecurity.context.SecurityContextImpl" />

</bean>

<bean id="authenticationProcessingFilter"class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">

<property name="authenticationManager" ref="authenticationManager"/><property name="authenticationFailureUrl"

value="/login.html?login_error=1" /><property name="defaultTargetUrl" value="/index.html" /><property name="filterProcessesUrl" value="/j_acegi_security_check" />

</bean>

<bean id="exceptionTranslationFilter"class="org.acegisecurity.ui.ExceptionTranslationFilter">

<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/></bean>

<bean id="authenticationEntryPoint"class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"><property name="loginFormUrl" value="/login.html" /><property name="forceHttps" value="false" />

53

Page 61: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

</bean>

<bean id="filterInvocationInterceptor"class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">

<property name="authenticationManager" ref="authenticationManager"/><property name="accessDecisionManager" ref="accessDecisionManager"/><property name="objectDefinitionSource"><value>CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISONPATTERN_TYPE_APACHE_ANT/secure/client/**/*=ROLE_CLIENT,ROLE_ADMIN/secure/**/*=ROLE_ADMIN

</value></property>

</bean>

První deklarace predstavuje zminovaný retezec filtru, pri cemž lze pomocí klasické Ant-likesyntaxe specifikovat, který retez filtru bude aplikován na které požadované URL. V našemprípade bude uvedený retez aplikován na všechna požadovaná URL. Poradí filtru v retezuje velmi duležité a nedodržení uvedeného poradí je castým zdrojem chyb.

Prvním filtrem v poradí je filtr trídy HttpSessionContextIntegrationFilter. Tenmá za úkol vyjmutí objektu SecurityContext (a v nem obsaženého objektu Authenticationpríslušného aktuálnímu uživateli) z uživatelského sezení (z objektujavax.servlet.http.HttpSession) a jeho vložení do aktuálního vlákna pomocí ob-jektu SecurityContextHolder. Neexistuje-li dosud uživatelské sezení ci neobsahuje-litoto sezení objekt SecurityContext a tedy ani Authentication, pak je vytvoren novýprázdný objekt SecurityContext (konkrétne defaultní implementaceorg.acegisecurity.context.SecurityContextImpl) a tento je pak vložen do vláknapožadavku. Po ukoncení zpracování požadavku je filtrem opet z objektu SecurityContextHoldervyzvednut potenciálne zmenený objekt SecurityContext a je uložen zpet do uživatel-ského sezení, kde je uchován do príštího požadavku daného uživatele.

Filtr trídy AuthenticationProcessingFilter má na starosti integraci prezentacnívrstvy naší aplikace (prihlašovacího formuláre) s rámcem Acegi Security za úcelem poskyt-nutí uživatelovy identity (uživatelského jména) a jejího dukazu (hesla). V prípade poža-davku na chránený webový zdroj a soucasné absence objektu Authentication v objektuSecurityContextHolder je uživatel presmerován na prihlašovací formulár, jehož URL jedefinováno prostrednictvím následujícího deklarovaného filtru trídyExceptionTranslationFilter a asociovaného objektu trídyAuthenticationProcessingFilterEntryPoint.

Samotný prihlašovací formulár muže vypadat napríklad takto (v priložené aplikaci jeobsáhlejší kód v JSP šablone WEB-INF/jsp/login.jsp potažmo ve vložené šablone WEB-INF/jsp/include/login.jsp):

<form action="j_acegi_security_check" method="post"><input type="text" name="j_username"/>

54

Page 62: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

<input type="password" name="j_password" /><input class="button" type="submit" value="Odeslat" />

</form>

Klícová je hodnota atributu action, která se musí shodovat s hodnotou vlastnosti filter-ProcessesUrl filtru authenticationProcessingFilter. Názvy parametru obsahujícíchuživatelské jméno a heslo musí dále být j_username a j_password. Filtr tyto údaje vložído objektu Authentication a nechá je proverit asociovaným objektem AuthenticationManager.V prípade úspešného overení je uživatel presmerován na puvodne požadovanou URL. Pristoupil-li k prihlašovacímu formulári prímo, pak je po úspešné autentizaci presmerován na adresudefinovanou atributem defaultTargetUrl filtru authenticationProcessingFilter.V prípade neúspešné autentizace je uživatel presmerován na adresu definovanou atributemauthenticationFailureUrl.

No a konecne filtr filterInvocationInterceptor definuje pomocí Ant-like vzoruprístupové atributy pro jednotlivé webové zdroje, tedy pro jednotlivá URL. V našem prí-pade jde o prístupové atributy - retezce - s prefixem ROLE_, címž imitujeme klasické rízeníprístupu na základe uživatelských rolí. Jednotlivé vzory jsou vyhodnocovány jeden po dru-hém, což je nutno pri jejich návrhu brát v úvahu. Pro rozhodnutí o prístupu k chránené URLje použit asociovaný objekt accessDecisionManager.

7.3.2 Rozhodnutí o autentizaci

Pro overení predložených prihlašovacích údaju potrebuje objekt authenticationManagersamozrejme definovat datový zdroj, který mu pro toto rozhodnutí poskytne podklady. Exis-tuje nekolik typu techto zdroju, jejichž podpora je v rámci Acegi Security standardne k dis-pozici. Lze použít specifikaci všech uživatelských jmen a hesel systému v konfiguracnímXML souboru (vhodné pouze pro testovací úcely), preddefinovaný prístup do databáze po-mocí JDBC spojení, uložení prihlašovacích údaju v LDAP serveru, ale je samozrejme možnévytvorit si i vlastní strategii prístupu k datovému skladu s prihlašovacími údaji systému.V našem prípade pujde o vlastní implementaci prístupu k temto údajum prostrednictvímnaší vlastní servisní vrstvy.

Následují související definice v konfiguracním souboru:<bean id="authenticationManager"

class="org.acegisecurity.providers.ProviderManager"><property name="providers"><list><ref local="daoAuthenticationProvider"/>

</list></property>

</bean>

Objekt authenticationManager, konkrétne použitá implementace ProviderManager,definuje seznam tzv. poskytovatelu autentizace (authentication providers), kterým je jed-nomu po druhém predáván objekt trídy Authentication, dokud jej nekterý z nich úspešneneautentizuje ci nezamítne.

55

Page 63: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

<bean id="daoAuthenticationProvider"class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">

<property name="userDetailsService" ref="hibernateAuthenticationDaoImpl"/><property name="userCache" ref="userCache"/><property name="saltSource" ref="saltSource"/><property name="passwordEncoder" ref="passwordEncoder"/>

</bean>

<bean id="hibernateAuthenticationDaoImpl"class="cz.morosystems.sportportal.spring.HibernateAuthenticationDaoImpl"><property name="userManager" ref="userManager"/>

</bean>

Námi použitý poskytovatel autentizace pak pro získání reprezentace uživatele z databázena základe predloženého uživatelského jména uloženého v objektu authentication pou-žije naši implementaci strategie pro prístup k databázi, tedy tríducz.morosystems.sportportal.spring.HibernateAuthenticationDaoImpl. Ná-zev trídy je odvozen od faktu, že pro prístup k databázi používá priložená vzorová aplikacetechnologii Hibernate. Kód trídy je jednoduchý:import org.acegisecurity.userdetails.UserDetailsService;import org.acegisecurity.userdetails.UsernameNotFoundException;import org.springframework.dao.DataAccessException;

public class HibernateAuthenticationDaoImpl implements UserDetailsService{

private UserManager userManager;

public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException {

User userFromDB = userManager.getUserByUsername(username);if (userFromDB==null) {throw new UsernameNotFoundException("User not found");

}if (userFromDB.getAuthorities().length==0) {throw new UsernameNotFoundException("User has no GrantedAuthority");

}return userFromDB;

}

... userManager set/get metody ...}

Z databáze je takto získán doménový objekt trídycz.morosystems.sportportal.pojo.User, reprezentující uživatele systému, imple-mentující rozhraní UserDetails a obsahující uživatelské jméno i heslo. Heslo ze získanéhoobjektu je porovnáno s heslem v objektu Authentication a v prípade kladné odpovedi jeuživatel autentizován.

56

Page 64: m Asarykova Univerzita Fakulta Informatiky

7.3. OCHRANA PRÍSTUPU K WEBOVÝM ZDROJUM

V reálné aplikaci je vetšinou neprípustné ukládat hesla v databázi v citelném tvaru a vevetšine prípadu je pro zašifrování hesla v databázi použit nejaký hashovací algoritmus:

<bean id="passwordEncoder"class="org.acegisecurity.providers.encoding.ShaPasswordEncoder"/>

<bean id="saltSource"class="org.acegisecurity.providers.dao.salt.SystemWideSaltSource">

<property name="systemWideSalt" value="abcba" /></bean>

V našem prípade tedy v poskytovateli autentizace registrujeme speciální objekt rozhraníorg.acegisecurity.providers.encoding.PasswordEncoder, konkrétne implemen-taci ShaPasswordEncoder. Predpokládáme pak, že v databázi není uloženo heslo, alepouze jeho SHA1 hash, navíc ješte prisolený pomocí námi definovaného retezce. Pri srov-nání predloženého hesla s heslem z databáze tedy náš poskytovatel autentizace nejprvevytvorí prisolený SHA1 hash predloženého hesla a teprve ten srovná s hodnotou získanouz databáze.

Je samozrejme nutné, aby bylo heslo každého nového uživatele systému pred ulože-ním do databáze zakódováno pomocí téhož objektu passwordEncoder a téhož objektusaltSource.

Vzhledem k tomu, že není žádoucí, abychom pri každém požadavku autentizovanéhouživatele pristupovali do databáze, registrujeme v našem autentizacním poskytovateli ijednu z rámcem Spring poskytovaných implementací kešovací strategie a sice strategii po-stavenou na populárním projektu EHCache.

<bean id="userCacheBackend"class="org.springframework.cache.ehcache.EhCacheFactoryBean">

<property name="cacheManager" ref="cacheManager"/><property name="cacheName" value="userCache" />

</bean>

<bean id="cacheManager"class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>

<bean id="userCache"class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">

<property name="cache" ref="userCacheBackend"/></bean>

7.3.3 Rozhodnutí o autorizaci

Poslední cástí príkladu jsou definice související s trídou AccessDecisionManager:

<bean id="accessDecisionManager"class="org.acegisecurity.vote.AffirmativeBased">

57

Page 65: m Asarykova Univerzita Fakulta Informatiky

7.4. OCHRANA PRÍSTUPU K METODÁM SERVISNÍCH OBJEKTU

<property name="allowIfAllAbstainDecisions" value="false" /><property name="decisionVoters"><list><ref local="adminAccessToClientSectionVoter" /><ref local="roleVoter"/>

</list></property>

</bean>

<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"/><bean id="adminAccessToClientSectionVoter"class="cz.morosystems.sportportal.spring.AdminAccessToClientSectionVoter"/>

Objekt rozhraní AccessDecisionManager obsahuje seznam objektu rozhraníorg.acegisecurity.vote.AccessDecisionVoter a jednomu po druhém (konkrétnejejich metode vote) predává objektovou reprezentaci chráneného cíle, jeho prístupové atri-buty a objekt Authentication. Objekt AccessDecisionVoter má tri alternativy návra-tové hodnoty. Muže zamítnout prístup objektu Authentication k chránenému cíli, mužejej povolit, ale muže se také zdržet rozhodnutí. Na základe návratových hodnot metodyvote všech techto objektu rozhodne i accessDecisionManager. ImplementaceAffirmativeBased, kterou jsme použili my, rozhodne kladne v prípade, že alespon je-den z objektu AccessDecisionVoter rozhodne kladne.

Rámec Acegi Security poskytuje trídu RoleVoter, velmi casto to používanou imple-mentaci rozhraní AccessDecisionVoter, která pracuje s výše uvedenými prístupovýmiatributy s prefixem ROLE_ a interpretuje je jako uživatelské role, které mají k danému chrá-nenému cíli prístup. Prístup k temto cílum pak udelí tem uživatelum, kterí mají mezi svýmiprístupovými atributy práve požadovanou roli.

Druhá deklarovaná implementace rozhraní AccessDecisionVoter je soucástí prilo-žené vzorové aplikace a demonstruje další možnost, jak prizpusobit chování rámce AcegiSecurity specifickým potrebám aplikace, která jej používá.

7.4 Ochrana prístupu k metodám servisních objektu

Budeme-li chtít naši aplikaci rozšírit napríklad i o rozhraní webových služeb, nebude námochrana prístupu k webovým zdrojum príliš platná. Pri zabezpecení aplikace budeme musetsestoupit ponekud hloubeji do jejích útrob a to až na úroven servisní vrstvy a metod jejíchobjektu. Navíc je-li bezpecnost aplikace a dat, která má k dispozici, duležitým obchodnímpožadavkem, pak je vhodné kombinovat rízení prístupu k webovým zdrojum s rízenímprístupu k servisní vrstve i u aplikací pouze s webovým rozhraním.

Pro rízení prístupu k metodám servisních objektu je rámcem Acegi Security využito AOPslužeb rámce Spring, cili následující príklad je opakováním k tématu kapitoly 6 (viz 6) azároven rozšírením príkladu z predchozího oddílu.

<bean id="methodInvocationInterceptor"class="

58

Page 66: m Asarykova Univerzita Fakulta Informatiky

7.5. HODNOCENÍ

org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"><property name="authenticationManager" ref="authenticationManager"/><property name="accessDecisionManager" ref="accessDecisionManager"/><property name="objectDefinitionSource"><value>

cz...MailManager.sendActivationEmail=ROLE_ADMINcz...MailManager.sendEmailWithNewCompetitorLoginData=ROLE_ADMIN,ROLE_CLIENT

</value></property>

</bean>

<bean id="mailManager"class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="proxyInterfaces"value="cz.morosystems.sportportal.managers.MailManager" />

<property name="target" ref="mailManagerTarget"/><property name="interceptorNames"><list><value>methodInvocationInterceptor</value><value>sampleAdvisor</value>

</list></property>

</bean>

Druhá deklarace se liší od té uvedené v príkladu v oddíle 6.4 (viz 6.4) pouze tím, že AOPproxy objekt mailManager obsahuje navíc odkaz na AOP pokynmethodInvocationInterceptor. Tento pokyn definuje prístupové atributy pro jednot-livé chránené cíle, v tomto prípade pro jednotlivé metody servisního objektu MailManager.Pri každém volání techto metod tedy pokyn ve spolupráci s asociovanými objektyauthenticationManager a accessDecisionManager rozhodne o povolení ci zamít-nutí prístupu k nim.

7.5 Hodnocení

Na první pohled se muže zdát, že množství XML konfiguracních prvku je až príliš vysokéa práce se systémem Acegi Security je tudíž složitá, ovšem po prostudování uvedenýchpríkladu se lze presvedcit, že témer neomezená možnost rozšírení a prizpusobení rámcenašim potrebám na mnoha místech promyšlené architektury za to jiste stojí. Navíc behemnaší demonstrace základních funkcí jsme vytvorili v podstate jen dve trídy, vše ostatní bylootázkou konfigurace. A práve možnost deklarativní definice zabezpecení (webových) apli-kací patrí spolu s maximální prenositelností k nejsilnejším argumentum pro použití rámceAcegi Security.

59

Page 67: m Asarykova Univerzita Fakulta Informatiky

Kapitola 8

Záver

V této práci jsme se pokusili ceskému ctenári priblížit v dnešní dobe stále populárnejší témaodlehcených kontejneru, o kterém se v tuzemských podmínkách zatím príliš nepíše. Jde jisteo dosud nejobsáhlejší ceský text na toto téma, jehož snahou je zvýšit zájem a povedomí o mo-derní metody vývoje JEE aplikací a jejich zabezpecení. K tomu by melo pomoci i predloženésrovnání s dosud hojne využívaným rešením na bázi EJB.

Zamerení na rámce Spring a Acegi Security je dáno jejich vedoucím postavením v tétooblasti a podrobný popis jejich schopností a jejich možného použití, který jsme poskytli, jenezbytným východiskem pro další práce, které se tématu odlehcených aplikacních rámcubudou venovat.

Duležitým prínosem práce je také priložená vzorová netriviální webová aplikace, na nížjsou všechny popsané principy jednotlivých technologií a jejich integrace demonstroványv jejich úplnosti.

V aplikaci se nachází i množství rozšírení funkcionality rámcu Spring a Acegi v podobetríd v balíku cz.morosystems.sportportal.spring. Príkladem muže být vlastní in-frastrukturní trída cz.morosystems.sportportal.spring.MultipleFormController,která reší jednu z dlouho známých bolestí rámce Spring, tedy neexistenci podpory pro víceHTML formuláru na jedné stránce. V aplikaci je také vyrešena a popsána dosud vubec ni-kde nedokumentovaná integrace rámcu Spring a Acegi s nástrojem JCaptcha, jehož úcelemje zamezení automatizovaných útoku na formuláre webových aplikací.

Na druhou stranu nebylo možné na tomto relativne malém prostoru zachytit všechnyvlastnosti popisovaných technologií pri zachování alespon minimální názornosti a užitec-nosti takového výkladu. Napríklad pokud jde o rámec Spring, tak velmi rozsáhlá podpora,kterou rámec poskytuje datové (DAO) vrstve spravovaných aplikací ci jejich testování, bylavynechána, nebot’ tématu rámcu na datové vrstve a jejich integraci s rámcem Spring, tématutestování v rámci odlehcené architektury a dalším souvisejícím oblastem, se budou venovatplánované navazující diplomové práce kolegu Tomáše Párala a Stanislava Hybáška.

Nedostalo se také na další témata související s rámcem Spring. Patrí k nim napríkladpoužití metadat ve zdrojovém kódu pro konfiguraci rámce, rozsáhlá podpora rámce SpringAOP rámci AspectJ, integrace s mnoha populárními projekty specializujícími se na prezen-tacní vrstvu (Struts, Tiles, Sitemesh, Velocity, Freemarker, JavaServer Faces, WebWork, atd.),podpora vzdálených volání a webových služeb, integrace s EJB, podpora JMS a JMX ci veverzi 2 rámce nove pridaná oblast podpory skriptování (Groovy, BeanShell a další) ci speci-fikace portletu.

60

Page 68: m Asarykova Univerzita Fakulta Informatiky

8. ZÁVER

Podobne je tomu i u komplexního rámce Acegi Security, který by si také zasloužil víceprostoru.

Ve všech uvedených prípadech jde ovšem o spíše pokrocilá témata a veríme, že samotnýzáklad obou rámcu se nám podarilo postihnout a demonstrovat úplne. Demonstrace nekte-rých ze zmínených témat lze i s popisem nalézt alespon v priložené vzorové aplikaci, jejížpokrytí tématu je výrazne vyšší než tomu muže být u tutoriálového textu této práce.

61

Page 69: m Asarykova Univerzita Fakulta Informatiky

Príloha A

Vzorová webová aplikace

Pri vývoji této aplikace bylo cerpáno z pomerne dlouhodobé zkušenosti autora s vývojemmoderních webových aplikací založených na technologiích odlehcené JEE architektury, ja-kými jsou Spring framework, Acegi Security ci Hibernate. Pro vzorovou aplikaci byl zvo-len projekt sportovního portálu, jenž je urcen pro ostré nasazení približne v polovine roku2006, což znamená, že jde o aplikaci netriviální, reálnou a pomerne složitou. Tuto aplikacijsme spolu se zmínenými kolegy, kterí pracují na navazujících diplomových pracích, vybraliproto, že její složitost umožnuje demonstraci velkého množství funkcí všech technologií,které jsou predmetem našich prací, a to i funkcí pokrocilých.

Aplikace predstavuje hodnotný doplnující zdroj k informacím uvedeným v této práci,nebot’ pokrývá, demonstruje a vysvetluje radu témat, na která se v této práci nedostalo.

Ve vzorové aplikaci jsou použity nejnovejší vývojové verze obou popisovaných rámcu,tedy Spring framework verze 2.0M3 a Acegi Security verze 1.0RC2.

Kompletní zdrojový kód vzorové aplikace je dostupný na priloženém CD.

62

Page 70: m Asarykova Univerzita Fakulta Informatiky

Príloha B

Podpurné webové stránky vzorové aplikace

Jedním z cílu této práce je i priblížení tématiky moderních JEE aplikacních rámcu studen-tum Fakulty informatiky Masarykovy univerzity v Brne. Z toho duvodu je soucástí tétopráce i vytvorení základu podpurného informacního webu, jehož úkolem bude zprístup-nit studentum doplnující informace o vzorové aplikaci, její popis, postup instalace, Javadocdokumentaci, zdrojové kódy v HTML podobe a další údaje, které se do této práce nevešly.

HTML zdrojový kód celého webu je umísten na priloženém CD.

63

Page 71: m Asarykova Univerzita Fakulta Informatiky

Príloha C

Návrh prusvitek k tématu

Soucástí práce je i návrh prusvitek, kterých by mohlo být využito pro prezentaci tématustudentum v rámci predmetu PA165 - Vývoj systému v jazyce Java.

Návrh prusvitek je umísten na priloženém CD.

64

Page 72: m Asarykova Univerzita Fakulta Informatiky

Príloha D

Elektronický formát této práce

Na priloženém CD je také k dispozici elektronická forma této práce, konkrétne:

• zdrojový kód této práce ve formátu XML,

• zdrojový kód této práce ve formátu LaTex,

• tato práce ve formátu PDF.

65

Page 73: m Asarykova Univerzita Fakulta Informatiky

Literatura

[1] Johnson, R. a Hoeller, J.: Expert One-on-one: J2EE Development Without EJB, WilleyPublishing, Inc., 2004, 0-7645-5831-5. 3, 3.4

[2] Johnson, R. a Hoeller, J. a Arendsen, A. a Risberg, T. a Sampaleanu, C.: ProfessionalJava Development with the Spring Framework, Willey Publishing, Inc., 2005, 0-7645-7483-3. 3.4

[3] Walls, C. a Breidenbach, R.: Spring in Action, Manning Publications Co., 2005, 1-932394-35-4. 5.9

[4] Pitner, T. a Matulík, P.: Podpora aplikacní logiky v J2EE aplikacních rámcích, Fakultaelektrotechniky a informatiky, VŠB - Technická univerzita Ostrava, 2005, 80-248-0595-2, elektronická podoba <http://www.cs.vsb.cz/objekty/2005/download/objekty2005paper24.pdf>. 6

[5] Harrop, R. a Machacek, J.: Pro Spring, Apress, 2005, 1-59059-461-4. 6.1

66

Page 74: m Asarykova Univerzita Fakulta Informatiky

Rejstrík

rízení prístupuk doménovým objektum, 50k metodám servisních objektu, 58k webovým zdrojum, 52

Acegi Security, 50architektura, 50

agilní metodiky, 5analýza požadavku, 1Ant, 29AOP Alliance, 45AOP Aspekt, 46AOP Definice cílu, 45

dynamická, 46statická, 46

AOP pokyn, 44predsazený pokyn, 44pokyn po návratu, 44pokyn výjimky, 44pokyn-interceptor, 44

AOP Proxy objekt, 47aplikacní kontext, 15aplikacní rámec, 1Architektura odlehcených kontejneru, 10AspectJ, 51aspektove orientované programování, 44autentizace, 50automatizované testování, 4autorizace, 50

CGLIB, 47chránený cíl, 50

dukaz identity, 51deklarativní transakcní management, 48doménové objekty, 11dynamická proxy, 47

editory vlastností, 17EHCache, 57Enterprise JavaBeans

Bean Managed Persistence, 6Container Managed Persistence, 6, 7

Message Driven Beans, 6Session Beans, 5verze 3.0, 6

filtry, 52formulárové prvky, 42formulárový objekt, 36framework, 1FreeMarker, 26

HandlerMapping, 27Hibernate, 7, 8

identita uživatele, 51interceptory, 29

Java Enterprise Edition, 3JavaBeans, 14JavaMail, 20JDO, 10JNDI, 4, 10JSP, 11JSTL, 31

kešovací strategie, 57knihovna znacek JSP

obecná, 40pro manipulaci s formulári, 40

korenový aplikacní kontext, 24kontejner, 1kontrolery, 10, 32

LDAP, 11LocaleResolver, 29

manažeri, 10Maverick, 43MessageSource, 21

návrhový vzorConstructor Injection, 10Data Transfer Object, 4, 37Dependency Injection, 9Front Controller, 23

67

Page 75: m Asarykova Univerzita Fakulta Informatiky

Inversion of Control, 9Model-View-Controller, 23Service Locator, 10Setter Injection, 10singleton, 4Value Object, 20

obchodní požadavky, 1odesílání e-mailu, 20odlehcený kontejner, 8

príkazový objekt, 36prístupové atributy, 51PicoContainer, 9podpora externích datových zdroju, 17POJO objekty, 3poskytovatel autentizace, 55principál, 51programování do rozhraní, 11prototyp, 15

reflexe, 17RMI/IIOP, 6Rod Johnson, 8

servisní objekty, 10SHA1, 57Spring framework, 8Struts, 8Swing, 6

trívrstvá architektura, 10Test Driven Development, 5továrna tríd, 13

uživatelské role, 58událostní model rámce Spring, 15

validátory, 39Velocity, 26ViewResolver, 30vrstvy aplikace

aplikacní vrstva, 9DAO, 49

perzistencní vrstva, 11vrstva webových služeb, 9, 11webová vrstva, 10

WebWork, 10

XDoclet, 4

zpracování výjimek, 31

68