Internetove technologie na platforme Java

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    1 Favorite

    Internetove technologie na platforme Java - Presentation Transcript

    1. ˇe Cesk´ vysok´ uˇen´ technick´ v Praze ecı e Fakulta elektrotechnick´ a ˇ CVUT FEL katedra poc´tac ˚ ˇı ˇu Bakal´ˇsk´ pr´ce ar a a Internetov´ technologie na platformˇ JAVA e e Miroslav Hr´z u Vedouc´ pr´ce: Ing. Andrej Zachar ıa Studijn´ program: Elektrotechnika a informatika strukturovan´ bakal´ˇsk´ ı e ar e Obor: Informatika a v´poˇetn´ technika ycı srpen 2007
    2. ii
    3. Podˇkov´n´ e aı Chtˇl bych podˇkovat firmˇ SimpleWay s.r.o., kde jsem mˇl tu moˇnost sezn´mit se s techno- e e e e z a logiemi, kter´ popisuji ve svoj´ bakal´ˇsk´ pr´ci. e ı ar e a iii
    4. iv
    5. Prohl´ˇen´ as ı Prohlaˇuji, ˇe jsem svou bakal´ˇskou pr´ci vypracoval samostatnˇ a pouˇil jsem pouze podklady s z ar a e z uveden´ v pˇiloˇen´m seznamu. e rze Nem´m z´vaˇn´ d˚vod proti uˇit´ tohoto ˇkoln´ d´ ve smyslu §60 Z´kona ˇ. 121/2000 Sb., a a zy u zı s ıho ıla a c o pr´vu autorsk´m, o pr´vech souvisej´ ıch s pr´vem autorsk´m a o zmˇnˇ nˇkter´ch z´kon˚ a e a ıc´ a y ee e y a u (autorsk´ z´kon). ya V Praze dne 21.8. 2007 ............................................................. v
    6. vi
    7. Abstract The goal of this bachalor thesis is to show possibilities of the Java platform for building web applications. Content of this paper is from on piece a recherche and from other piece an im- plementation. In the beginning I make the reader acquainted with technologies like Java EE, Spring framework and Hibernate ORM. In the next section I show the principles of Extreme programming and Test-Driven development. In the last section I apply knowledges of foregoing parts to the practical project, to the portal for seeking roommates. I show illuminating apply of Extreme programming in the real life. Abstrakt C´ılem t´to bakal´ˇsk´ pr´ce je uk´zat moˇnosti JAVA platformy pro tvorbu webov´ch aplikac´ e ar e a a z y ı. Obsah d´ je z ˇ´sti reˇerˇn´ z ˇ´sti implementaˇn´ V uvodu seznamuji ˇten´ˇe s techno- ıla ca s s ı, ca c ı. ´ c ar logiemi jako Java EE, Spring framework a Hibernate ORM. V dalˇ´ ˇ´sti ukazuji principy sı ca Extr´mn´ programov´n´ a Testem ˇ´ eho v´voje. V posledn´ ˇ´sti aplikuji teoretick´ zna- e ıho aı rızen´ y ı ca e losti z pˇedchoz´ textu na praktick´m projektu, port´lu pro hled´n´ spolubydl´ ıch. Ukazuji r ıho e a aı ıc´ n´zornou aplikaci Extr´mn´ programov´n´ v kaˇdodenn´ ˇivotˇ. a e ıho aı z ım z e vii
    8. viii
    9. Obsah Seznam obr´zk˚ au xiii ´ 1 Uvod 1 1.1 N´roky a technick´ obt´znost textu . . . . . . . . . . . . . . . . . . . . . . . . . a a ıˇ 1 1.2 Typografick´ uprava a ˇlenˇn´ textu . . . . . . . . . . . . . . . . . . . . . . . . a´ c eı 1 2 Reˇerˇe ss 3 2.1 Java EE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.1 V´ ıcevrstv´ architektura (N-tier architecture) a . . . . . . . . . . . . . . . 3 2.1.2 Koncept kontejneru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.1.3 Aplikaˇn´ servery . . . . . . . . . . . . . . . . cı . . . . . . . . . . . . . . . 3 2.1.3.1 Servletov´ kontejnery . . . . . . . . e . . . . . . . . . . . . . . . 4 2.1.3.2 Java EE aplikaˇn´ servery . . . . . . cı . . . . . . . . . . . . . . . 4 2.1.4 V´bˇr technologi´ . . . . . . . . . . . . . . . . ye ı . . . . . . . . . . . . . . . 4 2.2 Spring framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2.1 Aplikaˇn´ framework . . . . . . . . . . . . . . cı . . . . . . . . . . . . . . . 4 2.2.2 Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2.3 Hlavn´ pˇednosti . . . . . . . . . . . . . . . . ır . . . . . . . . . . . . . . . 5 2.2.4 Moduly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2.5 Inversion of Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.2.5.1 Dependecy lookup . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.5.2 Dependecy injection . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2.6 Rozhran´ BeanFactory a ApplicationContext ı . . . . . . . . . . . . . . . 8 2.2.6.1 BeanFactory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 ˇ 2.2.6.2 Zivotnost beany . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.6.3 Typov´ konverze . . . . . . . . . . . e . . . . . . . . . . . . . . . 11 2.2.6.4 Autowiring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2.6.5 ApplicationContext . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.7 Spring Web MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.7.1 N´vrhov´ vzor MVC . . . . . . . . a y . . . . . . . . . . . . . . . 12 2.2.7.2 Rozdˇlen´ webov´ch framework˚ . . eı y u . . . . . . . . . . . . . . . 14 2.2.7.3 Architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2.7.4 Dispacher servlet . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.2.7.5 Webov´ scopy . . . . . . . . . . . . e . . . . . . . . . . . . . . . 17 2.2.8 Spring Web Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.8.1 Struktura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.8.2 Stavy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.8.3 V´hody . . . . . . . . . . . . . . . . y . . . . . . . . . . . . . . . 19 2.2.8.4 Nev´hody . . . . . . . . . . . . . . . y . . . . . . . . . . . . . . . 19 2.2.8.5 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2.8.6 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.2.8.7 Konfigurace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3 Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3.1 ORM framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3.2 JPA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.3.3 Mapov´n´ . . . . . . . . . . . . . . . . . . . . aı . . . . . . . . . . . . . . . 23 2.3.4 Anotace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.3.5 Pr´ce s objekty . . . . . . . . . . . . . . . . . a . . . . . . . . . . . . . . . 24 ix
    10. 2.3.6 Dotazy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3.6.1 HQL API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3.6.2 Criteria API . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3.7 Lazy, Eager loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.3.8 Integrace se Springem . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.3.8.1 Deklarativn´ transakce . . . . . . . . . . . . . . . ı . . . . . . . . 26 2.4 Extr´mn´ programov´n´ . . . . . . . . . . . . . . . . . . . . . . . eı aı . . . . . . . . 27 2.4.1 Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.4.2 Role . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.4.3 Z´kladn´ postupy . . . . . . . . . . . . . . . . . . . . . . . a ı . . . . . . . . 28 2.4.3.1 Pl´novac´ hra . . . . . . . . . . . . . . . . . . . . a ı . . . . . . . . 28 2.4.3.2 Mal´ iterace . . . . . . . . . . . . . . . . . . . . e . . . . . . . . 28 2.4.3.3 Metafora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4.3.4 Testov´n´ . . . . . . . . . . . . . . . . . . . . . . aı . . . . . . . . 29 2.4.3.5 Jednoduch´ n´vrh . . . . . . . . . . . . . . . . . ya . . . . . . . . 29 2.4.3.6 Refaktorov´n´ . . . . . . . . . . . . . . . . . . . aı . . . . . . . . 29 2.4.3.7 P´rov´ programov´n´ . . . . . . . . . . . . . . . ae aı . . . . . . . . 29 2.4.3.8 Kolektivn´ vlastnictv´ k´du . . . . . . . . . . . . ı ıo . . . . . . . . 29 2.4.3.9 40-ti hodinov´ pracovn´ t´den . . . . . . . . . . y ıy . . . . . . . . 29 2.4.3.10 Z´kazn´ na spr´vn´m m´ e (On-site customer) a ık ae ıstˇ . . . . . . . . 30 2.4.3.11 Standardn´ podoba k´du . . . . . . . . . . . . . ı o . . . . . . . . 30 2.4.3.12 Sjednocen´ pracovn´ prostˇed´ . . . . . . . . . . e ı rı . . . . . . . . 30 2.5 Testovac´ framework JUnit . . . . . . . . . . . . . . . . . . . . . ı . . . . . . . . 30 2.5.1 JUnit 3.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.5.1.1 Architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.5.1.2 Assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.5.1.3 Integrace s IDE . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.5.2 JUnit 4.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.5.2.1 Hlavn´ zmˇny oproti 3.x . . . . . . . . . . . . . . ı e . . . . . . . . 31 2.5.2.2 assertThat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.5.2.3 Pˇedpoklady a teorie . . . . . . . . . . . . . . . r . . . . . . . . 32 2.6 Testovac´ framework JMock . . . . . . . . . . . . . . . . . . . . . ı . . . . . . . . 32 2.6.1 Mockov´n´ Mock objekty . . . . . . . . . . . . . . . . . . a ı, . . . . . . . . 32 2.6.2 Architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.6.3 Pˇ´rıklad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3 Implementace 35 3.1 Zad´n´ . . . . . . . . . . . . . . . . . aı . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2 Anal´za . . . . . . . . . . . . . . . . y . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.1 Co uˇ je naimplementov´no? z a . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.2 Pr˚zkum trhu . . . . . . . . . u . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.3 Prototypy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.3 Implementace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.3.1 Iterace 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.3.2 Iterace 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.3.3 Iterace 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.3.4 Iterace 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4 Zhodnocen´ ı 59 4.1 Shrnut´ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ı 59 4.2 Moˇnosti rozˇ´ren´ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . z sıˇ ı 59 x
    11. 4.3 Osobn´ zkuˇenosti ı s .................................. 59 5 Obsah pˇiloˇen´ho CD rze 61 6 Literatura 63 7 Seznam pouˇit´ch zkratek zy 65 xi
    12. xii
    13. Seznam obr´zk˚ au 2.1 Kontext Java EE technologi´ zdroj: [6] . . . . . . . ı, .......... ... . . . 3 2.2 Spring framework, zdroj: [13] . . . . . . . . . . . . .......... ... . . . 5 2.3 Princip IoC, zdroj: [13] . . . . . . . . . . . . . . . . .......... ... . . . 6 2.4 N´vrhov´ vzor MVC, zdroj: [15] . . . . . . . . . . a y .......... ... . . . 13 2.5 MVC implementace JSP Model 2, zdroj: [1] . . . . .......... ... . . . 13 2.6 Architektura aplikace ve Spring MVC, zdroj: [20] . .......... ... . . . 14 2.7 Stavy objektu a pˇechody pomoc´ metod persistent r ı manageru, zdroj: [17] . . . 25 2.8 Pr˚bˇh v´voje v XP . . . . . . . . . . . . . . . . . ue y .......... ... . . . 28 3.1 Z´kladn´ UI prototyp . . . a ı . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.2 Vkl´d´n´ nov´ho inzer´tu aaı e a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.3 Validace poloˇky . . . . . z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.4 Nov´ inzer´t v´ lokalit . y a ıce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.5 Hled´n´ v popt´vk´ch . . aı aa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 xiii
    14. xiv
    15. ´ KAPITOLA 1. UVOD 1 ´ 1 Uvod Internet se v dneˇn´ dobˇ stal nepostradatelnou z´leˇitost´ m˚ˇeme ho vyuˇ´ sı e az ı, uz zıvat r˚zn´mi zp˚soby a vydolovan´ch informac´ je v´ neˇ dost. Je to v dneˇn´ dobˇ, st´le, nejper- uy u y ı ıce z sı ea spektivnˇjˇ´ reklamn´ m´dium na svˇtˇ. Z´kazn´ u, pˇej´ ıch si m´ na internetu svoji prezentaci e sı ıe ee a ık˚ r ıc´ ıt st´le pˇib´v´. Nen´ proto od vˇci se sezn´mit s technologiemi pouˇit´mi pro v´voj internetov´ch a r ya ı e a zy y y aplikac´ı. Tato pr´ce pojedn´v´ o tvorbˇ rozs´hlejˇ´ aplikac´ na platformˇ JAVA. JAVA spolu a aa e a sıch ı e s technologiemi .NET a Ruby on rails vede ˇebˇ´cky statistik pouˇit´ch platforem [12] a z rıˇ zy pokud si zvol´ ıme vhodnou metodiku v´voje, aplikace n´m roste pod rukama velice rychle. y a 1.1 N´roky a technick´ obt´znost textu a a ıˇ Pr´ce by mˇla slouˇit jako studijn´ materi´l vˇem, kteˇ´ maj´ z´jem vyv´ internetov´ a e z ı as rı ıa ıjet e aplikace na platformˇ Java. Pˇedpokl´d´m proto patˇiˇnou znalost tohoto jazyka. Je dobˇe, po- e r aa rc r kud m´ ˇten´ˇ znalost v´voje webov´ch aplikac´ na jin´ platformˇ (napˇ´ a c ar y y ı e e rıklad s PHP). Na ˇkodu s nejsou zkuˇenosti s v´vojem pro Java EE, znalost tvorby JSP str´nek nebo JPA, plnˇ postaˇuje s y a e c absolvov´n´ pˇedmˇtu X36TJV - Technologie programov´n´ v jazyku Java, vyuˇovan´ho u n´s aı r e aı c e a na fakultˇ. e 1.2 Typografick´ uprava a ˇlenˇn´ textu a´ c eı Veˇker´ text je ps´n pomoc´ n´stroje L TEX, s pouˇit´ pˇipraven´ ˇablony, kterou sy a ıa z ım r es A poskytuje katedra. Text je ˇlenˇn hierarchicky do kapitol, sekc´ a podsekc´ Pro lepˇ´ ˇitelnost ce ı ı. sı c a orientaci v textu budu veˇker´ k´d ps´t takto, pokud budu v textu hovoˇit o rozhran´ s yo a r ıch, zapisuji je takto. Tˇ´ a metody zapisuji takto. Pr´ce je ps´na v ˇeˇtinˇ, aˇkoliv je IT odvˇtv´ rıdy a a cs e c eı charakteristick´ vˇemoˇn´mi anglick´mi term´ tam, kde to lze, jsem pouˇil jejich ˇesk´ ekvi- es zy y ıny, z cy valent s origin´ln´ znˇn´ v z´vork´ch. a ım e ım a a
    16. ´ 2 KAPITOLA 1. UVOD
    17. ˇˇ KAPITOLA 2. RESERSE 3 2 Reˇerˇe ss 2.1 Java EE Java Enterprice Edition je platforma urˇen´ pro v´voj byznys aplikac´ St´vaj´ ı spe- ca y ı. a ıc´ cifikace Java EE 5 definuje ˇadu technologi´ a API potˇebn´ch pro v´voj rozs´hl´ch syst´m˚. r ı r y y ay eu Dˇ´ se platforma oznaˇovala zkratkou J2EE, z marketingov´ch d˚vod˚, stejnˇ jako JRE 1.5 rıve c y u u e se oznaˇuje Java 5, se pˇejmenovala na Java EE. Aktu´lnˇ je cel´ JVM opensourceov´no a Javˇ c r ae e a e nic nestoj´ v cestˇ st´t se nejpouˇ´ ejˇ´ platformou od mobiln´ telefon˚ po velk´ byznys apli- ı ea zıvanˇ sı ıch u e kace. Kromˇ standardn´ API jsou k dispozici stovky opensourceov´ch knihoven, framework˚ e ıch y u a projekt˚. Nejdˇ´ se sezn´m´ se z´klady. u rıve a ıme a Obr´zek 2.1: Kontext Java EE technologi´ zdroj: [6] a ı, 2.1.1 V´ ıcevrstv´ architektura (N-tier architecture) a Architektura byznys aplikace v Java EE se typicky skl´d´ z ˇ´st´ jeˇ ukazuje obr´zek a a ca ı, z a 2.1. Vˇdy se jedn´ o model klient/server [2], resp. request/response, serverov´ ˇ´st se skl´d´ z a a ca aa z webov´ho kontejneru, doplnˇn´ho v pˇ´ e pouˇit´ specifikace EJB o EJB kontejner, d´le e ee rıpadˇ zı a jak´hokoliv perzistentn´ datov´ho uloˇiˇtˇ (pˇedstavme si napˇ´ e ıho e ´ zs e r rıklad klasickou relaˇn´ da- cı tab´zi) a nˇjak´ho klienta, v naˇem pˇ´ e webov´ho prohl´zeˇe. a ee s rıpadˇ e ıˇ c 2.1.2 Koncept kontejneru Kontejner je takov´ softwarov´ objekt, kter´ je alokov´n uvnitˇ dan´ho aplikaˇn´ y y y a r e c ıho serveru nebo aplikaˇn´ frameworku. c ıho • Zodpov´ a za pˇidˇlov´n´ zdroj˚ dan´m komponent´m. ıd´ r e aı u y a ˇ ıd´ z • R´ ı ˇivotn´ cyklus objekt˚. ı u Nˇkdy se pojmy jako framework a kontejner z´mˇrnˇ zamˇnuj´ napˇ. EJB a EJB kon- e aee eˇ ı, r tejner, Spring a Spring kontejner. 2.1.3 Aplikaˇn´ servery cı Aplikaˇn´ server je j´drem Java EE technologie. Podle poˇtu implementovan´ch speci- cı a c y fikac´ je m˚ˇeme rozdˇlit do dvou skupin ı uz e
    18. ˇˇ 4 KAPITOLA 2. RESERSE 2.1.3.1 Servletov´ kontejnery e Implementuj´ pouze webov´ kontejner a technologie v nˇm dostupn´, viz 2.1. Jeho ı y e e nasazen´ je vhodn´ obecnˇ pro menˇ´ a stˇedn´ aplikace. C´ ı e e sı r ı ılem moj´ pr´ce je uk´zat, ˇe i bez ıa a z tˇˇkoton´ˇn´ kan´nu jm´nem EJB jsme schopni vytvoˇit robustn´ aplikaci pˇesnˇ podle naˇich ez az ıho o e r ı re s pˇedstav. Mezi z´stupce t´to kategorie patˇ´ r a e rı • Apache Tomcat - opensource projekt vydan´ pod Apache licenc´ nejbˇˇnˇjˇ´ a nejdo- y ı, ez e sı stupnˇjˇ´ v˚bec. e sı u • Jetty - opensource, webov´ server pro statick´ a dynamick´ obsah, optimalizace pro y y y v´kon. y 2.1.3.2 Java EE aplikaˇn´ servery cı Narozd´ od pˇedchoz´ kategorie, Java EE AS implementuj´ plnou specifikaci z Java EE ıl r ı ı stacku. • GlassFish (Sun Java AS) - opensource ˇeˇen´ od Sun Microsystems, referenˇn´ implemen- rs ı cı tace • JBoss AS - opensource • IBM Websphere AS - komerˇn´ vlastn´ implementace JVM c ı, ı • BEA WebLogic AS - komerˇn´ cı 2.1.4 V´bˇr technologi´ ye ı Pˇi v´bˇru aplikaˇn´ serveru mus´ r ye c ıho ıme zv´ˇit, co dan´ projekt opravdu potˇebuje, az y r jak´ pouˇ´ ame aplikaˇn´ framework, jak´ ORM n´stroj nebo jak´ dalˇ´ API jsou nezbytn´, y zıv´ cı y a e sı e popˇ´ e jak´ pouˇ´ ame IDE. V z´sadˇ bychom mˇli zvolit jednoho dodavatele ˇeˇen´ a ne- rıpadˇ e zıv´ ae e rs ı kombinovat dodavatele JVM, AS, frameworku a dalˇ´ API a knihoven nebo zvolit takovou sıch kombinaci, kter´ je odzkouˇen´ a bezprobl´mov´; napˇ´ a sa e a rıklad Sun JVM, Glassfish AS, JSF, EJB, Toplink Essentials a NetBeans IDE, vˇe pˇknˇ standardn´ nebo v naˇem pˇ´ e Sun JVM, s ee ı s rıpadˇ Apache Tomcat, Spring framework, Spring Web MVC a WebFlow, Hibernate a Eclipse IDE. 2.2 Spring framework 2.2.1 Aplikaˇn´ framework cı Aplikace se dnes netvoˇ´ cel´ od z´klad˚, sp´se se podobaj´ stavebnic´ Proˇ bychom rı e a u ıˇ ı ım. c mˇli znovu objevovat kolo, kdyˇ uˇ ho nˇkdo pˇed n´mi vymyslel a funguje dobˇe. Framework e zz e r a r je softwarov´ syst´m, kter´ n´m pom´h´ ˇeˇit nejˇastˇjˇ´ probl´my pˇi implementaci, mˇl y e ya aars c e sı e r e by zrychlovat a usnadˇovat vlastn´ v´voj, ˇ´ n ıy rıdit ˇivotn´ cyklus aplikace a pom´hat oddˇlit z ı a e n´ urovˇov´ k´d od vlastn´ aplikaˇn´ logiky dan´ho zad´n´ ızko´ n y o ı cı e a ı. 2.2.2 Historie Odlehˇen´ J2EE framework Spring vznikl p˚vodnˇ jako demo aplikace knihy Roda cy u e Johnsonna: Expert One-to-One: J2EE design and development z roku 2002, kter´ uk´zala v a a dobˇ EJB 2.0, ˇe lze v J2EE programovat tak´ jednoduˇe. Pozdˇji, roku 2003 byl zaloˇen e z e s e z opensource projekt na port´lu sourceforge.net, kter´ vych´zel z p˚vodn´ k´du a postupnˇ se a y a u ıho o e stal mezi v´voj´ˇi velmi popul´rn´ Dnes je Spring ve stabiln´ verzi 2.0, ˇas uk´zal, ˇe koncept, y ar a ı. ı c a z
    19. ˇˇ KAPITOLA 2. RESERSE 5 kter´ Rod Johnsonn navrhl je spr´vn´. St´vaj´ ı Java EE 5 se velmi Springem inspirovala a y ay a ıc´ budouc´ specifikace Java EE 6 se pl´nuje Springu pˇibl´zit jeˇtˇ v´ ı a r ıˇ s e ıce. 2.2.3 Hlavn´ pˇednosti ır • Snazˇ´ a pˇ´ sı rıjemnˇjˇ´ pr´ce s J2EE, e sı a • neinvazivnost, nenut´ pouˇ´ ı zıvat nic, co pr´vˇ nepotˇebujeme, ae r • objektovˇ orientovan´ n´vrh je d˚leˇitˇjˇ´ neˇ jak´koliv implementaˇn´ technologie, e ya u z e sı z a cı • JavaBeans jako siln´ konfiguraˇn´ n´stroj, y cı a • testov´n´ je z´sadn´ Spring dˇl´ k´d snadnˇji testovateln´, aı a ı, ea o e y • modularita, umoˇnuje zvolit jen ty komponenty, kter´ potˇebujeme, zˇ e r • usnadˇuje pr´ci s dalˇ´ frameworky, pouˇit´ jako sjednocuj´ ı pˇ´ n a sımi zı ıc´ rıstup rozliˇn´ch tech- cy nologi´ a implementac´ ı ı, • usnadˇuje pr´ci s vyj´ n a ımkami, kdy je zaobaluje do l´pe ˇiteln´ch. ec y Obr´zek 2.2: Spring framework, zdroj: [13] a 2.2.4 Moduly D˚leˇit´m aspektem je, ˇe Spring lze pouˇ´ i na servletov´m kontejneru, dokonce i uzy z zıt e ˇistˇ s Java SE. Modul´rnost je tak´ doc´ ce a e ılena t´ ˇe se skl´d´ z v´ .jar archiv˚, z´kladn´ ım, z aa ıce ua ıch i voliteln´ch, pˇiˇemˇ si vybereme, co z nˇj chceme pouˇ´ y rc z e zıt. • Spring Core - hlavn´ j´dro frameworku, jedin´ povinn´ modul, metodika Inversion of ıa y y Control, rozhran´ BeanFactory ı • Spring DAO - abstrakce a zjednoduˇen´ pr´ce s JDBC sıa
    20. ˇˇ 6 KAPITOLA 2. RESERSE • Spring ORM - integrace s ORM frameworky, vlastn´ implementace perzistentn´ API i ı ıho na JRE 1.4 bez JPA • Spring Web - integrace s webov´mi frameworky jako JSF, Struts2, Velocity, vlastn´ im- y ı plementace webov´ho frameworku Spring Web MVC e • Spring AOP - podpora aspektovˇ orientovan´ho programov´n´ e e aı • Spring J2EE - podpora pro EJB, JMX, JMS, web service 2.2.5 Inversion of Control N´vrhov´ vzor IoC je zaloˇen na tzv. Hollywoodsk´m principu: “Nevolejte mi, j´ za- a y z e a vol´m v´m.”[5]. Aplikace se vzd´v´ odpovˇdnosti za vytv´ˇen´ a nastaven´ instanc´ ve prospˇch a a aa e ar ı ı ı e frameworku, resp. jeho IoC kontejneru. Jedn´ se o stˇˇejn´ vlastnost, proto ji uk´ˇu na pˇ´ a ez ı az rıkladu. Obr´zek 2.3: Princip IoC, zdroj: [13] a Zap´seme tˇ´ SampleContoller dvˇma zp˚soby, jednak klasicky a jednak s vyuˇit´ ıˇ rıdu e u z ım techniky IoC. SampleContollerWithoutIoC.java public class SampleContollerWithoutIoC implements org.springframework.web.servlet.mvc.Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { Connection connection = DatabaseUtils.getConnection(); UserDAO dao = DAOFactory.createUserDAO(\"hibernate\", connection); List users = dao.getUsers(); DatabaseUtils.closeConnection(conn); return new ModelAndView(\"userList\", \"users\", users); } } SampleContollerWithIoC.java public class SampleContollerWithIoC implements org.springframework.web.servlet.mvc.Controller { private UserDAO dao = null; public void setUserDAO(UserDAO userDAO) { this.dao = userDAO; } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { List users = dao.getUsers();
    21. ˇˇ KAPITOLA 2. RESERSE 7 return new ModelAndView(\"userList\", \"users\", users); } } action-servlet.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <beans> <bean id=\"sampleController\" class=\"SampleContollerWithIoC\"> <property name=\"userDAO\" ref=\"userDAO\"/> </bean> </beans> Ve tˇ´ e SampleContollerWithoutIoC jsme ruˇnˇ nastavovali komponentu dao. Ve tˇ´ e rıdˇ ce rıdˇ SampleContollerWithIoC jsme ji dostali jiˇ nakonfigurovanou. Pˇedstavme si pˇ´ z r rıklad v kon- textu re´ln´ho ˇivota, komponentu dao vyuˇ´ ı des´ ae z zıvaj´ ıtky tˇ´ Co by se stalo, kdybychom byli rıd. nuceni zmˇnit rozhran´ tov´rn´ metody createUserDAO? Museli bychom zmˇnit i implementaci e ı aı e v des´ ach tˇ´ kter´ dao pouˇ´ ı, coˇ odporuje prvn´ z´sadˇ OOP: “Pˇid´vej vlastnost jen ıtk´ rıd, e zıvaj´ z ıa e ra na jednom m´ e!”.1 ıstˇ ´c Uˇel souboru action-servlet.xml a vlastn´ konfigurace bean, jak vid´ ı ıme v pˇ´ rıkladu, ˇeˇ´ v kapitole 2.2.6. r sım 2.2.5.1 Dependecy lookup N´vrhov´ vzor IoC m˚ˇeme rozdˇlit do dvou technik, prvn´ z nich je dependecy lookup. a y uz e ı Je to n´vrhov´ vzor, pomoc´ nˇhoˇ objekt ˇ´d´ kontejner o beanu. Toto ˇeˇen´ bylo pouˇito a y ıez za a rs ı z v EJB 2.X v technologii JNDI, kdy poˇadovan´ beana musela implementovat API specifick´ z a e pro dan´ kontejner. Evoluce uk´zala, ˇe toto nen´ nejvhodnˇjˇ´ ˇeˇen´ Spring jej pouˇ´ a pro y a z ı e sı r s ı. zıv´ z´ an´ beany “z ruky”. ısk´ ı XmlBeanFactory beanFactory = new XmlBeanFactory(\"action-servlet.xml\"); DemandService demandService = (DemandService) beanFactory.getBean(\"demandService\"); 2.2.5.2 Dependecy injection Druhou technikou IoC je n´vrhov´ vzor dependecy injection, kter´ ˇeˇ´ samotn´ na- a y y r sı e staven´ a nainjektov´n´ potˇebn´ch objekt˚ a jejich z´vislost´ Vlastn´ vloˇen´ z´vislost´ m˚ˇe ı aı r y u a ı. ı zıa ı uz prob´ıhat pomoc´ı Setter injection - Nainjektov´n´ z´vislost´ pomoc´ JavaBeans setter˚. Pˇ´ aı a ı ı u rıklad je uveden v kapitole 2.2.5. Constructor injection - Nainjektov´n´ z´vislost´ pomoc´ konstruktoru. aı a ı ı public class SampleContollerWithIoC implements Controller { private UserDAO dao = null; public SampleContollerWithIoC(UserDAO userDAO) { this.dao = userDAO; } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { 1 V takto jednoduch´m pˇ´ e rıkladu to samozˇejmˇ lze obej´ v´voj´ˇ komponenty dao by byl nucen prov´st r e ıt, y ar e zmˇnu s ohledem na kompatibilitu, kaˇdop´dnˇ je to probl´m, do kter´ho se nemus´ e z ae e e ıme dostat.
    22. ˇˇ 8 KAPITOLA 2. RESERSE List users = dao.getUsers(); return new ModelAndView(\"userList\", \"users\", users); } } action-servlet.xml <bean id=\"sampleController\" class=\"SampleContollerWithIoC\"> <constructor-arg ref=\"userDAO\"/> </bean> rıklad.2 Method injection - Pouˇit´ pro speci´ln´ pˇ´ zı a ı rıpady, pro netrivi´lnost zde neuvedu pˇ´ a Mezi dalˇ´ typ dependecy injection, kter´ Spring avˇak nepodporuje, patˇ´ Interface sı y s rı injection, na kter´m je zaloˇen kontejner frameworku Avalon. Nejpouˇ´ ejˇ´ je metoda Setter e z zıvanˇ sı injection, nejˇastˇji z tˇchto d˚vod˚: ce e u u • je jednoduˇˇ´ pracovat s defaultn´ hodnotami properties dan´ho objektu, ssı ımi e • pomoc´ getteru z´ ame okamˇitˇ hodnotu dan´ property, ı ısk´ ze e • settery mohou b´t v pˇ´ e potˇeby vol´ny v´ y rıpadˇ r a ıcekr´t, coˇ konstruktor b´t nem˚ˇe, a z y uz • gettery/settery jsou zdˇdˇny na rozd´ od konstruktor˚ jako klasick´ metody. ee ıl u e 2.2.6 Rozhran´ BeanFactory a ApplicationContext ı 2.2.6.1 BeanFactory BeanFactory je ustˇedn´ ˇ´st´ cel´ho frameworku, ˇ´ ı ˇivotn´ cyklus, konfiguraci a ´r ı ca ı e rıd´ z ı vlastn´ injektaci vˇech bean. Kontejner startuje podle nasazen´ aplikace, napˇ. na aplikaˇn´ ı s ı r c ım serveru pomoc´ definice v deployment deskriptoru web.xml nebo v JUnit testovac´ sadˇ pomoc´ ı ı e ı pˇipraven´ch startovac´ API, m˚ˇeme ho tak´ samozˇejmˇ nainicializovat ruˇnˇ, jak jsme r y ıch uz e r e ce vidˇli v kapitole 2.2.5.1. e Manageovan´ objekt m˚ˇe b´t jak´koliv, v t´to souvislosti se mluv´ o tzv. POJO, coˇ v y uz y y e ı z pˇekladu znamen´ star´ dobr´ java objekt. Je to jak´koliv objekt, kter´ nen´ nucen dodrˇovat r a y y y y ı z implementaˇn´ omezen´ dan´ platformy. Vlastn´ konfigurace vz´jemn´ch vztah˚ bean m˚ˇeme cı ı e ı a y u uz definovat pomoc´ ı: • XML - nejˇastˇjˇ´ a nejpˇehlednˇjˇ´ z´pis, oddˇluje konfiguraci od vlastn´ pouˇit´ c e sı r e sı a e ıho z ı, • anotac´ - podobnˇ jako EJB, je konfigurace zaps´na v POJO, ı e a • JavaBean properties, • Jakarta Commons atributes. Pokud vyuˇ´ ame XML konfigurace, m´me minim´lnˇ jeden koˇenov´ konfiguraˇn´ sou- zıv´ a ae r y cı bor, nazveme jej napˇ. applicationContext.xml, na kter´ ve webov´m nasazen´ ukazuje de- r y e ı poloyment descriptor. web.xml 2 Pokud chceme nainjektovat beany s r˚zn´mi scopy (viz. 2.2.6.2), nevystaˇ´ uy cıme si s setter injection, ˇten´ˇe c ar odkazuji na referenˇn´ dokumentaci Springu [13]. cı
    23. ˇˇ KAPITOLA 2. RESERSE 9 <?xml version=\"1.0\" encoding=\"UTF-8\"?> <web-app> <!-- Context Configuration locations for Spring XML files --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <!-- Base dispacher’s servlet mapping --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/action-servlet*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app> Kromˇ koˇenov´ho aplikaˇn´ kontextu3 nadefinujeme servlet, kter´ se vˇdy star´ o je- er e c ıho y z a den typ poˇadavk˚ (v naˇem pˇ´ e o vˇe s pˇ´ z u s rıpadˇ s rıponou .html), kter´ zpracov´v´ DispacherServlet. e aa Podrobnˇji je tomu vˇnov´na kapitola 2.2.7.4. Jelikoˇ budu mluvit o jedn´ jedin´ konfiguraci, e e a z e e budu ApplicationContext.xml a action-servlet.xml vˇdomˇ zamˇnovat. e e eˇ ıme, kam zapsat XML konfigurace, aby se automaticky naˇetly, pod´ ame se ted’ na V´ c ıv´ z´kladn´ syntaxi. Hodnotu do property dan´ beany m˚ˇeme zapsat bud’to rovnou, a ı e uz <?xml version=\"1.0\" encoding=\"UTF-8\"?> <beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\"> <bean id=\"bean\" class=\"SampleBean\"> <property name=\"property\" value=\"value\"/> </bean> </beans> nebo odkazem na jinou beanu. <bean id=\"bean\" class=\"SampleBean\"> <property name=\"property\" ref=\"bean2\"/> </bean> <bean id=\"bean2\" class=\"SampleBean2\" /> ˇ 2.2.6.2 Zivotnost beany 3 Nic n´m nebr´n´ m´ koˇenov´ch konfiguraˇn´ soubor˚ v´ v tagu <param-value/> m˚ˇe b´t regul´rn´ a a ı ıt r y c ıch u ıce, uz y aı v´raz. y
    24. ˇˇ 10 KAPITOLA 2. RESERSE Kaˇd´ beana m´ z hlediska konverze IoC kontejneru s klientem svoji ˇivotnost (obor za a z viditelnosti), kter´ ˇ´ ame scope. Kromˇ definice z´vislost´ beany, Spring jednoduˇe dovoluje si e rık´ e a ı s zvolit i jej´ scope. Od verze 2.0 m˚ˇeme vytv´ˇet beany 5ti (resp. 6ti) scop˚. ı uz ar u • sigleton - existuje jen jedna instance, kter´ se vytvoˇ´ pˇi prvn´ injekci, u dalˇ´ se a rı r ı sıch injektuje tato, • prototype - pro kaˇdou injektaci se vytvoˇ´ nov´ instance, z rı a • request - beana plat´ po dobu HTTP requestu,4 ı • session - beana plat´ po dobu HTTP session, ı • global session - beana plat´ po dobu HTTP global session, ı • custom - pokud potˇebujeme nestandardn´ ˇeˇen´ Spring dovoluje definovat vlastn´ r ı r s ı, ı. prototype Defaultnˇ se vˇdy pouˇije singleton. Je to logick´, ve vˇtˇinˇ pˇ´ u n´s stav beany ne- e z z e e s e rıpad˚ a zaj´ a a plnˇ si s t´ vystaˇ´ ım´ e ım cıme, pokud ale potˇebujeme vytv´ˇet pokaˇd´ nov´ instance, r ar ze e tj. chceme zohledˇovat stav beany, mus´ n ıme uv´st explicitnˇ parametr scope. e e <bean id=\"bean3\" class=\"SampleBean3\" scope=\"prototype\" /> web scopes Jako pˇ´ rıklad uvedu n´sleduj´ ı beany: a ıc´ <bean id=\"loginAction\" class=\"LoginAction\" scope=\"request\"/> <bean id=\"userPreferences\" class=\"UserPreferences\" scope=\"session\"/> <bean id=\"userPreferences\" class=\"UserPreferences\" scope=\"globalSession\"/> Po kaˇd´m HTTP requestu se vytvoˇ´ nov´ instance beany loginAction, zat´ ze rı a ımco intuitivnˇ e beana userPreferences m´ m´ platnost po celou dobu relace. a ıt Ve vˇtˇ´ e pˇ´ u ve webov´ aplikaci si pˇi definici bean vystaˇ´ e sınˇ rıpad˚ e r cıme pouze s prvn´ımi dvˇma standardn´ scopy. Pokud potˇebujeme nainjektovat beanu s webov´m (nebo e ımi r y custom) scopem do beany se standardn´ scopem, mus´ k tomu poˇ´ chytr´ho objektu ım ıme zıt e - proxy, kter´ nainjektovanou beanu zastupuje. Pˇi vol´n´ metody beany proxy z´ a y r aı ısk´ objekt ze scopu a deleguje jej´ vol´n´ ı a ı. <bean id=\"userPreferences\" class=\"com.foo.UserPreferences\" scope=\"session\"> <aop:scoped-proxy/> </bean> <bean id=\"userService\" class=\"com.foo.SimpleUserService\"> <property name=\"userPreferences\" ref=\"userPreferences\"/> </bean> K webov´m scop˚m vˇak nejˇastˇji pˇistupujeme jako ke kontejner˚m, do kter´ch y u s ce r u y ukl´d´me objekty (a z nich vyb´ ame) programovˇ. Tuto praktiku ukazuji spolu s SWF aa ır´ e scopy v kapitol´ch 2.2.7.5, 2.2.8.5. a 4 Tyto 3 tzv. webov´ scopy maj´ smysl pouze ve webov´ aplikaci s pouˇit´ webov´ implementace e ı e z ım e ApplicationContextu - s XmlWebApplicationContext.
    25. ˇˇ KAPITOLA 2. RESERSE 11 2.2.6.3 Typov´ konverze e Typov´ konverze je zp˚sob pˇevodu hodnot zapsan´ch v XML deskriptoru na re´ln´ a u r y ae objekty. Chtˇli bychom napˇ´ e rıklad zapsat nˇco jako e <bean id=\"bean\" class=\"SampleBean\"> <property name=\"measure\" value=\"90/60/90\"/> </bean> public Class SampleBean { private Measure measure; public Measure getMeasure() { return measure;} public void setMeasure(Measure measure) {this.measure=measure;} } Spring n´m nab´ ı moˇnost implementace Property editoru, na kter´ se kontejner obr´t´ a ız´ z y aı v pˇ´ e, ˇe v setteru naraz´ na dan´ typ, kter´ neum´ automaticky dosadit. rıpadˇ z ı y y ı public class MeasurePropertyEditor extends java.beans.PropertyEditorSupport { @Override public void setAsText(String measure) throws IllegalArgumentException { try { //set data from String to value setValue(value); } catch (NumberFormatException e) { throw new IllegalArgumentException(); } } } Implementaci metody setAsText nech´v´m na ˇten´ˇi. Kromˇ n´ existuje inverzn´ metoda aa c ar eı ı getAsText(). Property editor zaregistrujeme napˇ´ rıklad do beany customEditorConfigurer, kte- rou um´ ıme na viditeln´ m´ ıst´ e ısto z ApplicationContextu. <bean id=\"customEditorConfigurer\" class=\"org.springframework.beans.factory.config.CustomEditorConfigurer\"> <property name=\"customEditors\"> <map> <entry key=\"Measure\"> <bean class=\"MeasurePropertyEditor\" /> </entry> </map> </property> </bean> 2.2.6.4 Autowiring Ohlednˇ Spring IoC kontejneru stoj´ za to zm´ e ı ınit jeˇtˇ jednu zaj´ se ımavou vlastnost - autowiring. <bean id=\"bean\" class=\"SampleBean\" autowire=\"byName\" /> T´ ˇekneme Sprigu, aby se pokusil automaticky vyˇeˇit z´vislosti injektovan´ch bean ım r rs a y s´m, bez nutnosti je explicitnˇ uv´dˇt. Hodnota v parametru autowire m˚ˇe nab´vat nejˇastˇji a e ae uz y ce n´sleduj´ ıch hodnot: a ıc´ • no - ˇ´dn´ autowiring, za y
    26. ˇˇ 12 KAPITOLA 2. RESERSE • byName - Spring hled´ ve sv´m ApplicationContextu beanu, podle jm´na JavaBean pro- a e e perty, • byType - Spring hled´ beanu podle typu JavaBean property, funguje pouze pokud se v a beanˇ nevyskytuje v´ properties stejn´ho druhu. e ıce e 2.2.6.5 ApplicationContext Rozhran´ ApplicationContext obaluje BeanFactory a rozˇiˇuje jej´ funkcionalitu o inter- ı sr ı nacionalizaci, publikov´n´ ud´lost´ nebo nahr´v´n´ zdroj˚. Uk´ˇi na pˇ´ aı a ı aaı u az rıkladu internacionalizaci, resp. pouˇit´ souboru .properties, kde uv´d´ zı a ıme k dan´mu kl´ci jeho hodnotu. e ıˇ Sample.properties bean.value = someOtherValue Kdekoliv v naˇ´ XML konfiguraci tyto promˇnn´ m˚ˇeme pomoc´ Expression Language5 sı e e uz ı pouˇ´ napˇ´ zıt, rıklad takto. <bean id=\"bean\" class=\"SampleBean\"> <property name=\"property\" value=\"${bean.value}\"/> </bean> Vˇe bude fungovat, pokud dopln´ s ıme ApplicationContext o beanu <bean id=\"propertyConfigurer\" class=\"org.springframework.beans.factory.config. PropertyPlaceholderConfigurer\"> <property name=\"locations\"> <list> <value>classpath:Sample.properties</value> </list> </property> </bean> 2.2.7 Spring Web MVC V t´to kapitole pˇedstav´ webov´ frameworky, vysvˇtl´ cestu, jak jsme se k nim e r ım e e ım dostali a uk´ˇu Springovskou implementaci webov´ho frameworku Spring Web MVC. Jako az e vˇechny modern´ webov´ frameworky je Spring MVC, jak uˇ jeho n´zev napov´ a, zaloˇen na s ı e z a ıd´ z n´vrhov´m vzoru Model View Controller. a e 2.2.7.1 N´vrhov´ vzor MVC a y Z historick´ho pohledu se pˇed n´stupem JSP pouˇ´ e r a zıvaly pouze servlety, s nimiˇ se z v´sledn´ HTML v´stup generoval obt´znˇ. Z dneˇn´ pohledu je tento zp˚sob vhodn´ jen pro y y y ıˇ e s ıho u y nejjednoduˇˇ´ pˇ´ ssı rıpady. Pˇi pˇ´ r rıchodu specifikace JSP 1.0 byla zvolena architektura tzv. Model 1, kdy je HTTP poˇadavek delegov´n pˇ´ z a rımo na pˇ´ snou JSP str´nku, v n´z je um´ en veˇker´ k´d pomoc´ rısluˇ a ıˇ ıstˇ s yo ı 6 Tento pˇıstup se dnes oznaˇuje term´ skriptlet˚. u r´ c ınem “ˇpagetov´ k´d”, postr´d´me tu jak´koliv s yo aa e 5 EL se prim´rnˇ pouˇ´ a pro vyhodnocov´n´ v´raz˚ na JSP str´nk´ch, pro pˇ´ ae zıv´ aı y u aa rıstup k hodnot´m z Command a Objekt˚ a podobnˇ. u e 6 Skriptlet je speci´ln´ tag, v nˇmˇ je um´ en pˇ´ aı ez ıstˇ rımo Java k´d <% /*java code here*/ %>. o
    27. ˇˇ KAPITOLA 2. RESERSE 13 Obr´zek 2.4: N´vrhov´ vzor MVC, zdroj: [15] a a y rozdˇlen´ do vrstev, centralizovan´ pˇ´ eı y rıstup HTTP poˇadavk˚ a potenci´ln´ moˇnost rozˇ´ren´ z u aı z sıˇ ı aplikace. Specifikace JSP 1.1, kromˇ pˇ´ e rıstupu psan´ logiky aplikace pomoc´ tag˚, pˇinesla imple- ı ı ur mentaci n´vrhov´ho vzoru MVC (obr´zek 2.4), kterou oznaˇujeme jako Model 2. a e a c Obr´zek 2.5: MVC implementace JSP Model 2, zdroj: [1] a MVC slouˇ´ k oddˇlen´ aplikaˇn´ prezentaˇn´ logiky a datov´ho modelu. Nen´ pouˇit zı eı c ı, cı e ı z jen ve webov´ch frameworc´ y ıch, ale napˇ´rıklad v desktopov´ch aplikac´ u knihoven Swing a y ıch SWT. Jeho koˇeny m˚ˇeme naj´ ve Smalltalku, kdy byl p˚vodnˇ pouˇit pro zakomponov´n´ r uz ıt u e z aı klasick´ho postupu vstup-zpracov´n´ ystup do GUI program˚. e a ı-v´ u • Controller - prostˇedn´ mezi vrstvami Model a View r ık • Model - pˇedstavuje vlastn´ data zobrazovan´ ve View r ı a • View - transformuje Model do prezentaˇn´ podoby cı Jelikoˇ HTTP protokol je typu request/response, tedy bezestavov´, nem˚ˇeme zde apli- z y uz kovat typick´ MVC pattern. Model 2 je upraven´ MVC sch´ma, kdy jedin´ rozd´ spoˇ´ a v y e e y ıl cıv´ tom, ˇe View nem˚ˇe volat Controller. z uz Vz´jemnou interakci si m˚ˇeme pˇedstavit tak, ˇe klient poˇle poˇadavek na webov´ a uz r z s z y server, ten je vz´pˇt´ delegov´n na servlet, kter´ pˇedstavuje controller, servlet zavol´ aplikaˇn´ a eı a yr a cı
    28. ˇˇ 14 KAPITOLA 2. RESERSE logiku, ze kter´ z´ a model, ten zaregistruje do scopu viditeln´ho z JSP, kter´ pˇedstavuje view. e ısk´ e er N´slednˇ provede pˇesmˇrov´n´ na View a posl´ze na JSP, kter´ si vyzvedne data ze scopu a a e r e aı e e ten v´sledek zobraz´ y ı.[15] V prostˇed´ webu existuje nˇkolik implementac´ kter´ odpov´ ı n´vrhov´m vzor˚m: rı e ı, e ıdaj´ a y u • Front Controller (Service to worker) - frameworky Struts, Spring Web MVC, WebWork, Ruby on Rails • Dispacher view (View helper) - frameworky JSF-based (JSF, Seam, Shale), Tapestry, Wicket, Echo2 Rozd´ mezi pˇ´ ıl rıstupy je v okamˇiku vol´n´ aplikaˇn´ logiky. V pˇ´ e Front Controller u z aı cı rıpadˇ se aplikaˇn´ logika vol´ pˇed pˇed´n´ zpracov´n´ do View, zat´ cı ar r a ım aı ımco u Dispacher View se vol´ a aplikaˇn´ logika uvnitˇ View pomoc´ View Helper u[3]. cı r ı 2.2.7.2 Rozdˇlen´ webov´ch framework˚ eı y u ˇ ım e sı ´ Nejd˚leˇitˇjˇ´ roli hraje uroveˇ abstrakce nad protokolem HTTP. C´ vˇtˇ´ je uroveˇ abstrakce, u z e sı ´ n n t´ m´nˇ se mus´ jednotliv´ vrstvy starat o samotn´ protokol. Takto dˇl´ webov´ frameworky ım e e ı e y e ıme e do dvou skupin • Poˇadavkovˇ orientovan´ (Request based) - zaloˇen´ na Front Controlleru, z e e ze • Komponentovˇ orientovan´ (UI component based, event-based) - zaloˇen´ na View Hel- e e ze peru. Budoucnost patˇ´ komponentovˇ orientovan´m webov´m framework˚m. Ty umoˇnuj´ rı e y y u zˇ ı v´voj´ˇi pracovat na urovni UI komponent a ud´lost´ tedy stejnˇ jednoduˇe jako s dalˇ´ y ar ´ a ı, e s sımi napˇ. s desktopov´mi frameworky (Swing, SWT). Snadno napˇ´ r y rıklad vytvoˇ´ rıme RAD n´stroj a pro WYSIWYG editaci webov´ aplikace. e 2.2.7.3 Architektura Obr´zek 2.6: Architektura aplikace ve Spring MVC, zdroj: [20] a Architektura aplikace ve Spring Web MVC je rozdˇlena do navz´jem nez´visl´ch vrstev, e a ay jak vid´ ıme na obr´zku 2.6. a User Interface - prezentaˇn´ vrstva, star´ se o generov´n´ v´stupu k uˇivateli (nejˇastˇji cı a aı y z ce XHTML), vˇtˇinou pouˇito JSP, ke kter´mu existuj´ implementace rozhran´ View es z e ı ı
    29. ˇˇ KAPITOLA 2. RESERSE 15 org.springframework.web.servlet.view.JstlView. Jako pˇ´ rıklad si uvedeme jednodu- chou JSP str´nku.7 a SampleJSP.jsp <?xml version=\"1.0\" encoding=\"UTF-8\" ?> <html><body> <c:choose> <c:when test=\"${1 + 1 == 10}\"> we count in binary base </c:when> <c:otherwise> we don’t count in binary base </c:otherwise> <c:choose> </body></html> Web - webov´ vrstva ˇeˇ´ pˇechody mezi str´nkami a odstiˇuje servisn´ vrstvu od vlastn´ a r sı r a n ı ı implementace. Hlavn´ rozhran´ v t´to vrstvˇ je Controller. ı ı e e • AbstractController - z´kladn´ abstraktn´ kontrol´r a ı ı e • BaseCommandController,AbstractCommandController - pracuje s Command Ob- jectem, tj. objekt, do kter´ho namapujeme vstupn´ hodnoty z hodnot z´ e ı ıskan´ch y HTTP requestu, ten pak pouˇ´ ame ve View vrstvˇ zıv´ e • SimpleFormController,AbstractSearchController - umoˇnuje napˇ´ zˇ rıklad pˇechody r mezi jednotliv´mi obrazovkami pomoc´ reakce na v´sledek metody y ı y • MultiActionController - umoˇnuje reagovat na v´ typ˚ HTTP request˚, de- zˇ ıce u u faultnˇ se metoda obsluhuj´ ı danou namapovanou str´nku jmenuje stejnˇ jak tato e ıc´ a e str´nka a • UrlFilenameViewController - pomoc´ namapovan´ str´nky zobraz´ JSP se stejn´m ı ea ı y jm´nem e Pˇ´ rıklad na takov´ jednoduch´ kontrol´r je uveden hned v kapitole 2.2.5. y y e Service - V t´to vrstvˇ se nach´z´ implementace dan´ch byznys requirement˚, jako jedin´ m´ e e aı y u aa pˇ´ rıstup k perzistentn´ vrstvˇ. Jelikoˇ je tato vrstva obvykle v kaˇd´ aplikaci jin´, Spring ı e z ze a k n´ nenab´ ı ˇ´dn´ rozhran´ Jako pˇ´ ı ız´ za e ı. rıklad uk´ˇu rozhran´ IUserService az ı public interface IUserService { public boolean isLoginFree(String login); public AbstractUser getUser(String login); public boolean saveUser(AbstractUser user); } Persistence - Perzistentn´ vrstva implementuje ukl´d´n´ ı a a ı,nahr´v´n´ a pr´ci s objekty datov´ho aaı a e modelu z datab´ze. Nejˇastˇji implementuje metody CRUD (create,retrieve,update,delete). a ce Spring ve sv´m ORM modulu dovoluje pouˇ´ deklarativn´ transakce imlementovan´ po- e zıt ı e moc´ AOP nebo zaobaluje m´nˇ ˇiteln´ vyj´ ı e ec e ımky do ˇitelnˇjˇ´ Spolupr´ce s Hibernate c e sıch. a frameworkem je hlavnˇ doc´ e ılena pomoc´ tˇ´ ı rıd • HibernateTemplate 7 Uveden´ tagy zaˇ´ ıc´ XML namespacem c nebo ftm jsou souˇast´ knihovny JSTL, kde jsou definov´ny e cınaj´ ı c´ ı a z´kladn´ tagy pro vˇtven´ k´du, zobrazov´n´ iterace, definov´n´ a pouˇ´ an´ promˇnn´ch nebo pro form´tov´n´ a ı e ıo a ı, aı zıv´ ı ey a aı textu.
    30. ˇˇ 16 KAPITOLA 2. RESERSE • HibernateDaoSupport Jako pˇ´ rıklad zde uvedu rozhran´ IOfferDao. Uk´zku deklarativn´ transakc´ spolu s im- ı a ıch ı plementaci rozhran´ IOfferDao si uk´ˇeme v kapitole 2.3.8.1. ı az public interface IOfferDao { void saveOrUpdate(Offer offer); Offer getOfferById(long id); Offer getOfferBy(Inzerent inzerent); void delete(Offer offer); List<Offer> getTopItems(int maxResult); } Domain model - datov´ model, soubor tzv. Bussiness Object˚, kter´ abstrahuj´ entity z y u e ı dom´nov´ho modelu do programu, kter´ s nimi pracuje, nakonfigurovan´ch pro O/R e e y y mapov´n´ dan´m perzistentn´ n´strojem. Uk´ˇeme si jednoduch´ pˇ´ aı y ım a az y rıklad se z´pisem a pomoc´ JPA anotac´ ı ı. Inzerent.java @Entity public class Inzerent extends AbstractUser { private String firstName = \"\"; private String lastName = \"\"; private String telephone = \"\"; //appropriate getters and setters } Podrobnˇji se t´to problematice vˇnuji v kapitole 2.3. e e e 2.2.7.4 Dispacher servlet DispacherServlet je Front Controller, jak jsem uk´zal v kapitole 2.2.7.1. Nyn´ si uk´ˇeme a ı az jednoduch´ pˇ´ y rıklad, kde d´me vˇe probran´ v t´to kapitole do kontextu. V deployment deskrip- a s ee toru jsme nadefinovali pro vˇechny pˇ´ s rıstupy na *.html obsluhu servletu action, jemuˇ odpov´ a z ıd´ konfigurace action-servlet.xml8 , jak je uvedeno v kapitole 2.2.6. V action-servlet.xml nadefinu- jeme: <bean id=\"urlMapping\" class=\"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\"> <property name=\"alwaysUseFullPath\" value=\"true\" /> <property name=\"mappings\"> <props> <prop key=\"/sample-web-page.html\">sampleController2</prop> </props> </property> </bean> <bean id=\"sampleController2\" class=\"SampleController2\"> <property name=\"view\" value=\"sampleJSP\" /> </bean> 8 Podle jm´na servletu “action” se pˇid´ automaticky “-servlet”. e ra
    31. ˇˇ KAPITOLA 2. RESERSE 17 Tedy kaˇd´ poˇadavek o sample-web-page.html bude zpracov´vat beana sampleCont- zy z a roller2, ta m´ “nasetov´nu” JSP str´nku9 , kter´ se ve v´sledku zobraz´ Implementace tˇ´ a a a a y ı. rıdy SampleController2 bude velmi podobn´ pˇ´ a rıkladu SampleContollerWithIoC u kapitoly 2.2.5. public class SampleContoller2 implements org.springframework.web.servlet.mvc.Controller { private String view; public void setView(String view) { this.view = view; } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { return new ModelAndView(view); } } Pokud potˇebujeme jen jednoduˇe podle poˇadavku na sample-web-page.html zobrazit r s z jen JSP str´nku a nic jin´ho, tak k tomu pouˇijeme kontrol´r UrlFilenameViewController, a e z e kter´ hled´ View a posl´ze JSP se stejn´m jm´nem jako je html poˇadavek. V pˇ´ y a e y e z rıkladu by staˇilo zmˇnit odpov´ ıc´ kontrol´r. c e ıdaj´ ı e <bean id=\"urlFilenameViewController\" class=\"org.springframework.web.servlet.mvc.UrlFilenameViewController\" /> 2.2.7.5 Webov´ scopy e Jak jsem zm´ v kapitole 2.2.6.2, ˇastˇji k webov´m scop˚m pˇistupujeme jako ke ınil ce y u r kontejner˚m pro data, jeˇ maj´ b´t viditeln´ v r´mci n´mi definovan´ho scopu. u z ıy a a a e • Request - platnost jen v pr˚bˇhu jednoho HTTP poˇadavku, v requestu pˇich´z´ para- ue z r aı metry z HTTP GETu, • Session - platnost v pr˚bˇhu HTTP session, ue • Global session - platnost v pr˚bˇhu HTTP global session, ue Z´kladn´ moˇnosti pr´ce uv´d´ v n´sleduj´ ım pˇ´ a ı z a a ım a ıc´ rıkladu, napˇ´ rıklad s pomoc´ tˇ´ ı rıdy org.springframework.web.context.request.DispatcherServletWebRequest. HttpServletRequest request; //gets as method parameter HttpSession session = request.getSession(); DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request); Integer id = (Integer) request.getParameter(\"id\"); id = (Integer) webRequest.getAttribute(\"id\", RequestAttributes.SCOPE_REQUEST); session.setAttribute(\"string\", id.toString()); webRequest.setAttribute(\"string\", id.toString(), RequestAttributes.SCOPE_SESSION); webRequest.setAttribute(\"string\", id.toString(), RequestAttributes.SCOPE_GLOBAL_SESSION); 9 V tomto jednoduch´m kontextu si m˚ˇeme pˇedstavit napˇ´ e uz r rıklad tu, jeˇ jsem uk´zal v kapitole 2.2.7.3. z a
    32. ˇˇ 18 KAPITOLA 2. RESERSE 2.2.8 Spring Web Flow Spring Web Flow je mocn´ n´stroj pro definici toku obrazovek ve webov´ aplikaci. ya e Snadno ˇeˇ´ probl´my, kter´ konvenˇn´ pˇ´ r sı e e c ı rıstup ˇeˇ´ velmi neefektivnˇ. Hlavn´ idea spoˇ´ a v r sı e ı cıv´ kl´cov´ abstrakci konverzace mezi uˇivatelem a serverem - ve flow. Flow je nˇco v´ neˇ jed- ıˇ e z e ıce z notliv´ poˇadavek, nˇco m´nˇ neˇ cel´ session. Jedin´ flow ˇ´ ı celou konverzaci, kdyˇ se od yz e ee z a e rıd´ z uˇivatele oˇek´v´ nˇjak´ vstup, flow je pozastaveno a ˇek´ na uˇivatel˚v vstup. Klient ovl´d´ z c aa e y ca z u aa konverzaci pomoc´ ud´lost´ podle kter´ch se flow rozhoduje, co udˇlat d´l. ıa ı, y e a 2.2.8.1 Struktura Definice flow nen´ nic jin´ho neˇ koneˇn´ automat s poˇ´teˇn´ stavem, mnoˇinou ı e z cy ca c ım z stav˚, ve kter´ch se m˚ˇe nach´zet, mnoˇinou koneˇn´ch stav˚ a vlastn´ pˇechody do dalˇ´ u y uz a z cy u ımi r sıch ’to do XML souboru nebo do java k´du (pouˇit´ pˇi stav˚. Takov´to z´pis prov´d´ u y a a ıme bud o zı r speci´ln´ pˇ´ a ıch rıpadech a napojen´ na dalˇ´ syst´my). Pro zaˇ´tek zde uvedu uvodn´ z´pis, ıch sı e ca ´ ıa ve kter´m flow konfigurujeme. e sample-flow.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <flow xmlns=\"http://www.springframework.org/schema/webflow\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd\"> </flow> 2.2.8.2 Stavy Start State - Poˇ´teˇn´ stav, jako u kaˇd´ho automatu je pr´vˇ jeden. Parametr idref odkazuje ca c ı ze ae na jin´ stav. y <start-state idref=\"actionState\" /> End State - Koncov´ stav, pokud je ve flow na nejvyˇˇ´ urovni, ukonˇ´ se jeho prov´dˇn´ y ssı ´ cı a e ı, zresetuj´ se vˇechny promˇnn´. ı s ee <end-state id=\"endState\" /> View State - Stav, ve kter´m doch´z´ k vykreslen´ zadan´ho obsahu. e aı ı e <view-state id=\"viewState\"> <transition to=\"actionState\" /> </view-state> Action State - Stav, ve kter´m doch´z´ k nˇjak´ akci, tj. zavol´n´ metody dan´ beany a e aı ee aı e obvykle podle n´vratu n´sleduje pˇechod do jin´ho stavu. a a r e <action-state id=\"actionState\" > <action bean=\"actionBean\" method=\"sampleAction\" /> <transition to=\"decisionState\" /> </action-state> Decision State - Stav, urˇen´ k vˇtven´ logiky, testuje podm´ cy e ı ınku, podle kter´ pˇejde do er dalˇ´ stavu. sıho
    33. ˇˇ KAPITOLA 2. RESERSE 19 <decision-state id=\"decisionState\" > <if test=\"requestParameters.id gt 0\" then=\"viewState\" else=\"subflowState\"/> </decision-state> SubFlow State - Do flow m˚ˇeme vnoˇit jin´ flow jako subflow a pomoc´ koncov´ho stavu uz r e ı e dan´ho subflow reagovat a pˇej´ dle logiky aplikace. Vhodn´ pokud se urˇit´ sekvence e r ıt e ce stav˚ a pˇechod˚ opakuj´ ve v´ flow. u r u ı ıce <subflow-state id=\"subflowState\" flow=\"inner-flow\"> <transition on=\"ok\" to=\"viewState\" /> <transition on=\"cancel\" to=\"endState\"/> </subflow-state> 2.2.8.3 V´hody y Za hlavn´ v´hody SWF m˚ˇeme bezpochyby oznaˇit: ıy uz c • oddˇlen´ navigace od vlastn´ k´du, eı ıho o • automatick´ ˇ´ ı stavu aplikace, e rızen´ • vyˇˇ´ uroveˇ abstrakce, ssı ´ n • dovoluje volat metody stˇedn´ vrstvy bez nutnosti pouˇit´ kontrol´ru. r ı zı e 2.2.8.4 Nev´hody y Spring Web Flow nem˚ˇeme pouˇ´ tam, kde kv˚li velk´ pr´ci, neˇ se zprovozn´ jen uz zıt u ea z ı jedna jedin´ obrazovka, se n´m jej nevyplat´ nasadit. Avˇak po urˇit´ velikosti a sloˇitosti a a ı s ce z pˇechod˚ mezi str´nkami v aplikaci tato nev´hoda pad´. r u a y a 2.2.8.5 Scope Stejnˇ jako webov´ scopy ve Spring Web MVC, existuj´ ve SWF podobn´ kontejnery pro e e ı e uloˇen´ jak´chkoliv kr´tkodob´ch dat. Pˇistupuje se k nim pˇes rozhran´ Map pomoc´ instance zı y a y r r ı ı tˇ´ RequestContext. rıdy SampleFormAction.java public org.springframework.webflow.Event sampleAction(org.springframework.web.servlet.support. RequestContext context) { Object object = context.getXXXScope().get(\"key\"); context.getXXXScope().put(\"key\",object); context.getExternalContext().getRequestMap().put(\"key\",object); Object object = context.getExternalContext().getSessionMap().get(\"key\"); context.getExternalContext().getGlobalSessionMap().put(\"key\",object); context.getExternalContext().getApplicationMap().put(\"key\",object); return success(); } kde za XXX dosad´ jeden za 4 scop˚ pouˇiteln´ch ve SWF: ıme u z y • request - maj´ platnost pouze pro dan´ poˇadavek, ı yz
    34. ˇˇ 20 KAPITOLA 2. RESERSE • flash - plat´ dokud uˇivatel neopust´ aktu´ln´ stav, ı z ı aı • flow - plat´ pro cel´ flow, ı e • conversation - plat´ po dobu ˇivotnosti mateˇsk´ho flow. ı z re Na pˇ´ rıkladu vid´ ıme, ˇe pomoc´ tˇ´ RequestContext m´me pˇ´ z ı rıdy a rıstup i ke klasick´m y webov´m scop˚m pˇedstaven´m v kapitole 2.2.7.5, s t´ rozd´ y u r y ım ılem, ˇe k nim pˇistupujeme jako z r k mapˇ. Posledn´ dosud nepˇedstaven´ je application scope, kter´ plat´ v pr˚bˇhu chodu cel´ e ı r y y ı ue e aplikace. Nutno poznamenat, ˇe data v jak´mkoliv scopu, kromˇ singletonu, nejsou viditeln´ z e e a vˇemi uˇivateli dohromady. s z 2.2.8.6 Syntax <transition/> - Definujeme pˇechod do jin´ho stavu, parametry on, to, on-exception. Pa- r e rametr to je povinn´. Pokud prov´d´ y a ıme ve stavu v´ akc´ pˇechod m˚ˇeme podm´ ıce ı, r uz ınit v´sledkem pr´vˇ jedn´ metody. y ae e <action-state id=\"actionState\" > <action bean=\"actionBean\" method=\"sampleAction\" /> <action bean=\"actionBean\" method=\"sampleAction2\" /> <transition on=\"sampleAction.success\" to=\"decisionState\" /> <transition on-exception=\"exception\" to=\"endState\" /> </action-state> <global-transitions/> - Vhodn´ pouˇ´ pokud se pˇechody v kaˇd´m stavu z flow opakuj´ e zıt, r ze ı. <global-transitions> <transition on=\"globalEvent1\" to=\"state1\"/> <transition on=\"globalEvent2\" to=\"state2\"/> </global-transitions> <xxx-actions/> - Pomoc´ tagu definujeme akce: ı • <start-actions/> - akce se provedou po vstupu do flow, • <end-actions/> - akce se provedou pˇed koncem flow, r • <entry-actions/> - akce se provedou po vstupu do stavu, • <exit-actions/> - akce se provedou pˇed koncem stavu, r • <renderer-actions/> - akce se provedou pˇed vlastn´ renderov´n´ ve viewState. r ım a ım <xxx-mapper/> - Tagem definujeme mapov´n´ do flow/z subflow: aı • <input-mapper/> - vstupn´ mapov´n´ ı a ı, • <output-mapper/> - v´stupn´ mapov´n´ y ı a ı. Pokud chceme v subflow pˇistoupit k dat˚m z nadˇazen´ho flow, m´me 2 r u r e a moˇnosti, jak toho dos´hnout. Prvn´ moˇnost´ je pouˇ´ conversation scope, kter´ je v z a ı z ı zıt y subflow viditeln´. T´ y ımto ale dan´ data zviditeln´ a ıme i pro vˇechny ostatn´ subflow. Dalˇ´ s ı sı moˇnost´ je pouˇ´ mapov´n´ atribut˚. V hlavn´ flow definujeme: z ı zıt aı u ım
    35. ˇˇ KAPITOLA 2. RESERSE 21 <subflow-state id=\"sampleSubflow\" flow=\"sample-flow\"> <attribute-mapper> <input-mapper> <mapping source=\"flowScope.number\" target=\"number\"/> </input-mapper> <output-mapper> <output-attribute name=\"result\"/> </output-mapper> </attribute-mapper> <transition to=\"decisionState\"/> </subflow-state> Z´pisem <mapping source=\"flowScope.number\" target=\"number\"/> vezmeme a promˇnnou number uloˇenou ve flow scope a pˇed´me j´ pomoc´ input-mapperu do e z ra ı ı subflow. Z´pisem <output-attribute name=\"result\"/> z´ ame promˇnnou result z a ısk´ e output-mapperu a pˇed´me j´ do flow scope tohoto flow. V subflow je ovˇem mus´ ra ı s ıme definovat tak´: e <flow> <input-mapper> <input-attribute name=\"number\"/> </input-mapper> ... some logic <end-state id=\"finish\"> <output-mapper> <output-attribute name=\"result\"/> </output-mapper> </end-state> </flow> Z´pisem <input-attribute name=\"number\"/> z´ ame pomoc´ input-mapperu a ısk´ ı promˇnnou number do flow scope, z´pisem <output-attribute name=\"result\"/> um´ en´m e a ıstˇ y v koncov´m stavu mapujeme promˇnnou result z flow scope do output-mapperu. e e redirect - Pokud chceme n´silnˇ pˇesmˇrovat tok aplikace, pˇev´ˇnˇ pouˇijeme jeden ze dvou a er e r az e z hlavn´ pˇ´ ıch rıstup˚: u • flowRedirect - pˇesmˇrov´n´ na jin´ flow, r e aı e • externalRedirect - pˇesmˇrov´n´ na kompletnˇ jin´ HTTP poˇadavek10 . r e aı ey z <end-state id=\"finish\" view=\"flowRedirect:sampleFlow\"/> <end-state id=\"finish\" view=\"externalRedirect:sample-web-page.html\"/> Pro kompletn´ pˇehled syntaxe a pr´ci s SWF odkazuji ˇten´ˇe na referenˇn´ dokumen- ır a c ar cı taci SWF. [14] 10 Rozd´ je samozˇejmˇ ten, ˇe ve druh´m pˇ´ ıl r e z e rıpadˇ se postupuje k dispacher servletu, zat´ e ımco prvn´ pˇ´ ı rıpad z˚st´v´ ve flowControlleru. u aa
    36. ˇˇ 22 KAPITOLA 2. RESERSE 2.2.8.7 Konfigurace SWF zapoj´ do naˇ´ aplikace t´ ˇe pˇid´me n´sleduj´ ı beany do XML deskriptoru ıme sı ım, z r a a ıc´ Springu. action-servlet.java <?xml version=\"1.0\" encoding=\"UTF-8\"?> <beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:flow=\"http://www.springframework.org/schema/webflow-config\" xsi:schemaLocation=\" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd\"> <bean name=\"/sample-web-page.html\" class=\"org.springframework.webflow.executor.mvc.FlowController\"> <property name=\"flowExecutor\" ref=\"flowExecutor\" /> </bean> <!-- Launches new flow executions and resumes existing executions. --> <flow:executor id=\"flowExecutor\" registry-ref=\"flowRegistry\"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id=\"flowRegistry\"> <flow:location path=\"/WEB-INF/sample-flow.xml\"/> </flow:registry> <!-- All formActions in application --> <bean id=\"formAction\" class=\"SampleFormAction\" /> </beans> Uveden´ konfigurace je opravdu z´kladn´ pro vˇtˇ´ poˇet str´nek a flow se nehod´ Lepˇ´ a a ı, e sı c a ı. sı konfigurace je uvedena v implementaci port´lu www.chcispolubydlici.cz, jehoˇ zdrojov´ k´dy a z eo jsou obsaˇeny na pˇiloˇen´m CD. z rze 2.3 Hibernate Tato ˇ´st by mˇla ˇten´ˇi osvˇtlit pouˇit´ perzistentn´ vrstvy ve webov´ aplikaci. ca e c ar e zı ı e Vysvˇtl´ cestu k ORM framework˚m, jejich dneˇn´ standard JPA a bl´ze se pod´ ame na e ım u sı ıˇ ıv´ jednu z implementac´ - JBoss Hibernate framework. ı 2.3.1 ORM framework Z historick´ho pohledu existuje v´ pˇ´ e ıce rıstup˚, jak ˇeˇit probl´m s napojen´ perzis- u rs e ım tentn´ vrtsvy k javovsk´ aplikaci. Probl´m spoˇ´ a v tom, ˇe zde m´me dva pˇ´ ı e e cıv´ z a rıstupy - OOP pˇ´ rıstup pouˇ´ y v Javˇ a relaˇn´ pˇ´ zıvan´ e c ı rıstup pouˇ´ y v relaˇn´ datab´z´ zıvan´ c ıch a ıch. JDBC - vlastn´ implementace perzistentn´ vrstvy pomoc´ JDBC API, z´pis SQL dotaz˚ a ı ı ı a u metod DAO tˇ´ kter´ volaj´ pˇ´ sn´ procedury - pˇ´ s zdlouhav´, dnes v podstatˇ rıd, e ı rısluˇ e rıliˇ e e pˇeˇitek rz Serializace - ukl´d´n´ c´ an´ serializovateln´ch hierarchi´ objekt˚ do/z souboru nebo DB - a a ı/naˇıt´ ı y ı u pracuje se jen s cel´mi hierarchiemi - nevhodn´ y e
    37. ˇˇ KAPITOLA 2. RESERSE 23 Objektov´ DB - pouˇit´ objektov´ho datab´zov´ho stroje, neexistuje jedin´ ˇeˇen´ kter´ by a zı e ae e r s ı, e se prosadilo v praxi EJB 2.x Entity Beany - starˇ´ EJB 2.x specifikace, v 3.0 se pˇeˇlo k ORM-based JPA sı rs Dlouhou dobu pˇevl´dal prvn´ zp˚sob, aˇ do doby, kdy se objevily prvn´ ORM n´stroje ra ıu z ı a jako Castor a Hibernate. ORM je mapovac´ n´stroj mezi OOP svˇtem, kde z´kladn´ nosiˇ dat je ıa e a ı c tˇ´ rıda, vztah mezi objekty je definov´n kompozic´ a je vˇdy jednosmˇrn´, a mezi svˇtem relaˇn´ a ı z ey e cı DB, kde nosiˇem dat je tabulka, sloupce definuj´ atributy entity a vztah (relace) mezi tabulkami c ı je definov´n pomoc´ ciz´ kl´c˚ nebo vztahov´ tabulky a je vˇdy obousmˇrn´. ORM mapuje a ı ıch ıˇu e z ey tˇ´ na tabulky, jejich instance na jednotliv´ ˇ´dky tabulky. rıdy e ra 2.3.2 JPA Java Persistence API je dneˇn´ standard, kter´ popisuje z´kladn´ myˇlenky ORM. Hibernate sı y a ı s 3.x byl pˇeps´n, aby odpov´ ra ıdal tomuto standardu. Jako referenˇn´ implementace byl zvolen cı framework Oracle Toplink Essentinals. Mezi dalˇ´ hlavn´ ORM ˇeˇen´ patˇ´ sı ı rs ı rı: • Oracle Toplink - komerˇn´ cı • Apache iBATIS - opensource • JBoss Hibernate - opensource ˇs ı Reˇen´ pouˇ´ ıc´ JPA jej berou jako z´kladn´ bal´ vlastnost´ budeme-li pouˇ´ ˇistˇ zıvaj´ ı a ı ık ı, zıvat c e samotn´ JPA, teoreticky bychom mohli vymˇnit jeden ORM framework za jin´ bez zmˇny jedin´ e e y e e ˇ´dky k´du. JPA bohuˇel neˇeˇ´ vˇechny probl´my a tak jsou frameworky nuceny pouˇ´ ra o z r sı s e zıvat vlastn´ nestandardn´ ˇeˇen´ ı ı r s ı. 2.3.3 Mapov´n´ aı Vlastn´ mapov´n´ z objekt˚ na tabulky zapisujeme v JPA pomoc´ anotac´ U Hiber- ı aı u ı ı. nate se tak´ m˚ˇeme setkat se z´pisem metadat pomoc´ XML nebo XDoclet. Zde uk´ˇu jen e uz a ı az prvn´ zp˚sob, protoˇe se jedn´ o standard. Anotace zapisujeme bud’to pˇed atribut tˇ´ ıu z a r rıdy, kde framework pouˇ´ a pro pˇ´ zıv´ rıstup k poloˇk´m Java Reflection API (gettery/settery nemus´ b´t za ıy v˚bec implementov´ny), nebo do getteru JavaBean tˇ´ u a rıdy, kdy framework pro pˇ´ rıstup pouˇ´ a zıv´ vol´n´ getter˚/setter˚[19]. JavaBeans pˇ´ aı u u rıstup je nejˇastˇjˇ´ c e sı. @Entity public class Customer implements Serializable { private Integer id; private String name; @Id @GeneratedValue public Integer getId() {return id;} public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name;} } @Entity public class Producer implements Serializable { private Integer id; private String name; private List<Customer> customers;
    38. ˇˇ 24 KAPITOLA 2. RESERSE @OneToMany public List<Customer> getCustomers() {return customers;} public void setCustomers(List<Customer> customers) {this.customers = customers;} @Id @GeneratedValue public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} } 2.3.4 Anotace Mezi z´kladn´ anotace patˇ´ a ı rı: @Entity - vlastn´ oznaˇen´ objektu, kter´ chceme mapovat ı cı y @Id - oznaˇen´ prim´rn´ kl´ce cı a ıho ıˇ @OneToOne - vyj´dˇen´ vztahu 1 : 1 ar ı @OneToMany - vztah 1 : N @ManyToOne - vztah N : 1 @ManyToMany - vztah M : N @Transient - metoda (poloˇka) nebude nijak mapov´na z a @MappedSuperClass - pro danou tˇ´ se nevytv´ˇ´ tabulka, pouˇito u abstraktn´ tˇ´ rıdu arı z ıch rıd, kdy potomci dˇd´ jejich anotace eı @Emeddable - pro danou tˇ´ se nevytv´ˇ´ tabulka, poloˇky tˇ´ se pˇipoj´ k jin´ jiˇ exisuj´ ı rıdu arı z rıdy r ı ez ıc´ tabulce, pomoc´ n´sleduj´ ı anotace ve vztahu 1 : 1 nebo N : 1 (JPA neum´ vztah 1 : N s ıa ıc´ ı @Emeddable entitou)11 @Embedded - poloˇky tˇ´ @Emeddable se pˇipoj´ ke tˇ´ e z rıdy r ı rıdˇ @Version - oznaˇen´ atributu k optimistick´mu zamyk´n´ cı e aı Uveden´ anotace maj´ mnoho nepovinn´ch parametr˚, kter´ zde v uvodu nebudu popi- e ı y u e ´ sovat. Vedle JPA anotac´ podporuje Hibernate sv´ vlastn´ @org.hibernate.anotations.xxx, ı e ı kter´ pouˇijeme, pokud potˇebujeme cokoliv nestandardn´ e z r ıho. 2.3.5 Pr´ce s objekty a JPA pro pr´ci s objekty pouˇ´ a rozhran´ EntityManager, s Hibernatem vˇak ˇastˇji a zıv´ ı sce pracujeme starˇ´ zp˚sobem a to pomoc´ Session. sım u ı Jak vid´ıme na obr´zku 2.7, novˇ vytvoˇen´ objekt, kter´ jeˇtˇ nem´ v datab´zi repre- a e ry y se a a zentaci, je tranzientn´ jeho ID je zat´ = 0. Pokud ho uloˇ´ ı, ım zıme, pˇejde objekt do perzistentn´ r ıho stavu, tj. stavu, kde se reprezentace objektu v pamˇti a v datab´zi shoduj´ Pokud objekt e a ı. mˇn´ v t´ sam´ session, zmˇny se okamˇitˇ prom´ e ıme e e e ze ıtnou do datab´ze. Pokud se sessiona zavˇe, a r pˇejdeme do Detached stavu, kdy se obˇ reprezentace neshoduj´ Po uloˇen´ Detached objektu r e ı. zı se stane znovu perzistentn´ Perzistentn´ objekt m˚ˇeme vymazat, Detached objekt po uzavˇen´ ı. ı uz rı session putuje do garbage collectoru. 11 Hibernate m´ pro tento speci´ln´ pˇ´ a a ı rıpad anotaci @org.hibernate.annotations.CollectionOfElements
    39. ˇˇ KAPITOLA 2. RESERSE 25 Obr´zek 2.7: Stavy objektu a pˇechody pomoc´ metod persistent manageru, zdroj: [17] a r ı 2.3.6 Dotazy Velkou v´hodou ORM je, ˇe dotazy p´seme v˚ˇi objektov´ reprezentaci. Vlastn´ relaˇn´ y z ıˇ uc e ı cı reprezentace n´s ve vˇtˇinˇ pˇ´ u dokonce nezaj´ a. a e s e rıpad˚ ım´ 2.3.6.1 HQL API HQL je podobnˇ jako standardn´ JPQL urˇen k dotaz˚m do datab´ze. Syntax je po- e ı c u a dobn´ klasick´mu SQL s t´ rozd´ a e ım ılem, ˇe se pt´me na objekty. Jako pˇ´ z a rıklad uvedu jednoduch´ y HQL dotaz. SessionFactory factory = new Configuration().buildSessionFactory(); Session session = factory.openSession(); Transaction tx = session.beginTransaction(); Query cutomerQuery = session.find(\"SELECT Customer FROM Producer AS p \" + \"WHERE p.customer.name = :customerName\", \"mira\"); List<Customer> customers = customerQuery.list(); tx.commit(); session.close(); Takov´to kus k´du m˚ˇeme pouˇ´ kdekoliv v aplikaci s nakonfigurovan´m Hibernatem. y o uz zıt y Vˇimnˇme si, jak z´ av´me instanci Session a ˇe sami ˇ´ ıme transakce. Takov´to zp˚sob s e ısk´ a z rıd´ y u se oznaˇuje jako programov´ (programmatic). Existuje jeˇtˇ jin´ pˇ´ c y s e y rıstup k transakc´ - dekla- ım rativn´ kter´ ukazuji v kapitole 2.3.8.1. ı, y 2.3.6.2 Criteria API Hibernate vˇak podporuje jeˇtˇ jin´ zp˚sob dotazov´n´ - Criteria API. Dotazy zapi- s se yu aı sujeme pomoc´ pˇid´v´n´ restrikc´ k dan´mu krit´riu. N´sleduj´ ı pˇ´ ı r aaı ı e e a ıc´ rıklad vrac´ stejn´ v´sledek ı yy jako HQL dotaz v minul´m odstavci. e SessionFactory factory = new Configuration().buildSessionFactory();
    40. ˇˇ 26 KAPITOLA 2. RESERSE Session session = factory.openSession(); Transaction tx = session.beginTransaction(); Criteria customerCriteria = session.createCriteria(Producer.class); customerCriteria.add(Restrictions.eq(\"customer.name\",\"mira\"); List<Customer> customers = customerCriteria.list(); tx.commit(); session.close(); Criteria se rozhodnˇ nehod´ na vˇechno. Jsou dotazy, kter´ pomoc´ krit´ri´ zapsat e ı s e ı eı nejdou. Na druhou stranu jsou dotazy, kter´ pomoc´ HQL vypadaj´ sloˇitˇ, a s krit´rii jdou e ı ı ze e zapsat velmi snadno. 2.3.7 Lazy, Eager loading Hibernate podporuje v´znamnou optimalizaci datab´zov´ch dotaz˚ t´ ˇe m´ y ay u ım, z ısto ob- jektu samotn´ho vrac´ tzv. dynamickou proxy. Proxy je potomek dan´ tˇ´ kter´ je vytv´ˇen e ı e rıdy, y ar v runtimu pomoc´ frameworku CGLIB. Proxy realizuje odloˇen´ nahr´v´n´ poloˇek v objektu, ı ze aaı z od kter´ho dˇd´ Pokud objekt obsahuje reference na dalˇ´ objekt, tato data se v dotazu nevr´t´ e e ı. sı a ı. Defaultnˇ je vybr´na pr´vˇ tato strategie. Probl´m nast´v´ v pˇ´ e, ˇe chceme pˇistoupit k e a ae e aa rıpadˇ z r properties proxy v dobˇ, kdy se uˇ zavˇela hibernate Session. e z r Opakem t´to strategie je Eager loading, pomoc´ kter´ se nahraj´ property do objektu e ı e ı vr´cen´ho v dotazu. Strategii zapisujeme pro kaˇdou property v entitˇ pomoc´ parametru k ae z e ı anotaci fetch = FetchType.EAGER. Tohoto se d´ vyuˇ´ jen v jednoduch´ch dotazech, kter´ a zıt y e vrac´ pˇ´ ı rımo datab´zov´ sloupce (poloˇky). ae z Dalˇ´ moˇnost´ jak “doloadovat” property k proxy je znovu tento objekt nahr´t do sı z ı a aktu´ln´ session pomoc´ metody refresh(). Existuje jeˇtˇ jin´ zp˚sob, kter´ by se nemˇl aı ı se yu y e zneuˇ´ zıvat, pouˇ´ statickou metodu Hibernate.initialize(proxy), kterou zavol´me bez- zıt a prostˇednˇ po dotazu v t´ sam´ session. r e e e 2.3.8 Integrace se Springem Spring nab´ ı velk´ kvalitativn´ zlepˇen´ pr´ce s Hibernatem. Jak jsem psal v kapitole ız´ e ı sıa 2.2.7.3, jsou k tomu urˇeny tˇ´ c rıdy • org.springframework.orm.hibernate.HibernateTemplate • org.springframework.orm.hibernate.support.HibernateDaoSupport 2.3.8.1 Deklarativn´ transakce ı V programov´m pˇ´ e rıstupu k transakc´ se st´le opakuje ten sam´ k´d - z´ ım a yo ıskat session, otevˇ´ session, vytvoˇit transakci...potvrdit transakci, uzavˇ´ session. Vˇe ˇeˇ´ tˇ´ rıt r rıt s r sı rıda HibernateDaoSupport, kdy tento k´d prov´d´ pˇed a po jak´koliv metodˇ v jeho potomkovi o aı r e e pomoc´ Spring AOP. ı Pˇi pouˇit´ Hibernatu v prostˇed´ webu se nejˇastˇji vyuˇ´ a n´vrhov´ho vzoru Open r zı rı ce zıv´ a e Session in View, kdy je Hibernate session otevˇena po celou dobu HTTP requestu. Jin´ pˇ´ r y rıstup je, ˇe v kaˇd´ volan´ metodˇ se otev´ a session nov´. Open Session in View ˇeˇ´ probl´m s lazy z ze e e ır´ a r sı e loadingem, kdy se tyto properties do proxy doloadov´vaj´ pˇi renderov´n´ view.12 a ır aı Nyn´ m´me dostateˇn´ znalosti k tomu napsat implementaci rozhran´ IOfferDao. ıa ce ı 12 Avˇak pokud naˇteme objekt z datab´ze pˇi jednom requestu, ale pouˇijeme ho v jin´m requestu (v jin´ s c a r z e e session), mus´ ıme ho ruˇnˇ do aktu´ln´ session nahr´t. ce aı a
    41. ˇˇ KAPITOLA 2. RESERSE 27 public class OfferHibernateDao extends HibernateDaoSupport implements IOfferDao { public void saveOrUpdate(Offer offer) { getHibernateTemplate().saveOrUpdate(offer); } public Offer getOfferById(long id) { return (Offer) getHibernateTemplate().get(Offer.class, id); } public Offer getOfferBy(Inzerent inzerent) { List offers = getHibernateTemplate().findByNamedParam(\"from Offer as offer\" + \"where offer.inzerent.id = :userId \", \"userId\", inzerent.getId()); return (Offer) offers.get(0); } public void delete(Offer offer) { getHibernateTemplate().delete(offer); } public List<Offer> getTopItems(int maxResults) { DetachedCriteria criteria = DetachedCriteria.forClass(Offer.class); criteria.addOrder(Order.desc(\"updated\")); return getHibernateTemplate().findByCriteria(criteria, 0, maxResults); } } 2.4 Extr´mn´ programov´n´ e ı aı Extr´mn´ programov´n´ nebo zkr´cenˇ XP je odlehˇen´ discipl´ v´voje softwaru. eı aı ae ca ına y Jako jednu z agiln´ metodik j´ m˚ˇeme popsat manifestem agiln´ pˇ´ ıch ı uz ıho rıstupu[10], kdy • m´ funguj´ ı software pˇednost pˇed obs´hlou dokumentac´ a ıc´ r r a ı, • maj´ individuality a interakce pˇednost pˇed procesy a n´stroji, ı r r a • m´ spolupr´ce se z´kazn´ a a a ıkem pˇednost pˇed sjedn´v´n´ smluv, r r a a ım • a nakonec reakce na zmˇnu m´ pˇednost pˇed plnˇn´ pl´nu. e ar r e ım a XP se navrhuje nasadit na projektov´ t´my o menˇ´ aˇ stˇedn´ velikosti, kter´ potˇebuj´ ey sı z r ı e r ı vyv´ software rychle a v prostˇed´ kde se ˇasto mˇn´ z´kazn´ ıjet r ı, c eı a ıkovy poˇadavky. z 2.4.1 Historie XP vytvoˇil roku 1996 v pr˚bˇhu sv´ho projektu pro firmu Chrysler Kent Beck, poprv´ r ue e e vˇc publikoval spolu s Erichem Gammou v knize Extreme Programming Explained roku 1999. e Kv˚li tomu, ˇe obracelo sw v´voj “naruby”, se XP stalo velmi popul´rn´ u z y a ı. 2.4.2 Role Z hlediska XP rozdˇlujeme vˇechny zainteresovan´ osoby do jedn´ ze tˇ´ z´kladn´ e s e e rı a ıch rol´ ı[9], kter´ mohou (ale nemus´ odpov´ e ı) ıdat pˇ´ rımo jednotliv´m osob´m. y a • Z´kazn´ - P´se, vykl´d´ uˇivatelsk´ pˇ´ ehy, m˚ˇe a nemus´ b´t koncov´ uˇivatel. a ık ıˇ aa z e rıbˇ uz ıy yz • Program´tor - Odhaduje ˇas potˇebn´ na implementaci uˇivatelsk´ch pˇ´ eh˚, implemen- a c r y z y rıbˇ u tuje testy a produkˇn´ k´d. cı o • Tester - Implementuje funkˇn´ integraˇn´ testy. c ı, cı
    42. ˇˇ 28 KAPITOLA 2. RESERSE Obr´zek 2.8: Pr˚bˇh v´voje v XP a ue y 2.4.3 Z´kladn´ postupy a ı Celou metodiku m˚ˇeme v podstatˇ shrnout do n´sleduj´ ıch 12ti extr´mn´ pˇ´ uz e a ıc´ e ıch rıstup˚. u ˇa C´st z nich klidnˇ uplatn´ e ıme i pokud pracujeme sami. 2.4.3.1 Pl´novac´ hra a ı Z´kazn´ pˇipravuje uˇivatelsk´ pˇ´ ehy (user stories, podobn´ v´znam jako use case), a ık r z e rıbˇ yy kter´ z´kazn´ po konzultaci s program´tory rozdˇl´ do jednotliv´ch iterac´ (release planning). ea ık a eı y ı ˇ Casto jsou k tomu pouˇity CRC karty (CRC cards, story cards), coˇ jsou pap´ e kartiˇky z z ırov´ c tak o velikosti vizitky, kde na kaˇd´ z nich je jeden uˇivatelsk´ pˇ´ eh. Program´toˇi pomoc´ ze z y rıbˇ ar ı nich odhadnou pˇibliˇnou ˇasovou sloˇitost implementace uˇivatelsk´ho pˇ´ ehu (lepˇ´ pouˇ´ rz c z z e rıbˇ sı zıvat sofistikovanˇjˇ´ sw n´stroj - tracker). Z´kazn´ rozhodne, kter´ pˇ´ ehy maj´ pro nˇj vˇtˇ´ byznys e sı a a ık e rıbˇ ı e e sı hodnotu a tak rozhoduje, ve kter´ iteraci je chce vidˇt. e e 2.4.3.2 Mal´ iterace e Jak vid´ na obr´zku 2.8, z´kladn´ motto m˚ˇe zn´ “Uvolˇuj rychle, uvolˇuj ˇasto!”. ıme a a ı uz ıt: n nc Bˇhem jedn´ iterace, kter´ trv´ nejˇastˇji 1-4 t´dny, se uvolˇuje produkˇn´ verze sw s nov´mi e e a a ce y n cı y vlastnostmi, kter´ m˚ˇe b´t okamˇitˇ nasazena. Z´kazn´ tak nejrychleji dostane poˇadovanou a uz y ze a ık z funkcionalitu. Pokud z´kazn´ nevid´ pokrok, m˚ˇe projekt zruˇit. Takov´to v´voj se nˇkdy a ık ı uz s y y e oznaˇuje jako Rapid Application Development nebo zkr´cenˇ RAD. c ae 2.4.3.3 Metafora V´voj´ˇi ˇasto definuj´ v´stiˇn´ pˇirovn´n´ metaforu, jak dan´ syst´m m´ vypadat y ar c ı y ze r a ı, y e a a co m´ dˇlat. Soubor tˇ´ a n´vrhov´ch vzor˚, kter´ odpov´ ı dan´mu byznys probl´mu a ae rıd a y u e ıdaj´ e e ˇeˇen´ pom´h´ ˇlen˚m t´mu v komunikaci o syst´mu. r s ı, a ac u y e
    43. ˇˇ KAPITOLA 2. RESERSE 29 2.4.3.4 Testov´n´ aı V XP se ˇ´ ıme metodikou programov´n´ ˇ´ e testy (test-driven development, test- rıd´ a ı rızen´ first design). Za prv´, test p´seme pˇed vlastn´ implementac´ Metodikou TDD pˇid´v´me novou e ıˇ r ı ı. r aa vlastnost do syst´mu v 5ti f´z´ e a ıch: 1. Rychle pˇidat test. r ’ 2. Spustit vˇechny testy, at vid´ s ıme, jestli test dopadne ˇpatnˇ. Pokud nov´ test nepad´, je s e y a naps´n ˇpatnˇ. as e 3. Prov´st drobnou zmˇnu. e e ’ 4. Spustit vˇechny testy, at vid´ s ıme, jak vˇe dopadne dobˇe. s r 5. Refaktorov´n´ odstranit duplicitu. a ım Jednotkov´ a funkˇn´ testy nahrazuj´ specifikaci. Pokud testy neproch´z´ mus´ se k´d e cı ı a ı, ı o opravit. V´ o programov´n´ ˇ´ em testy nalezneme v [16]. ıce a ı rızen´ 2.4.3.5 Jednoduch´ n´vrh ya Software vyv´ y pomoc´ XP m´ zpravidla nejjednoduˇˇ´ design, kter´ z´roveˇ splˇuje ıjen´ ı a ssı ya nn z´kazn´ a ıkovy poˇadavky. Pomoc´ TDD a refaktorov´n´ je implementov´no opravdu jen to, co z ı aı a je tˇeba. Jednoduch´ design je z´kladem snadn´ho pˇid´v´n´ dalˇ´ vlastnost´ a udrˇov´n´ r y a e r aaı sıch ı z aı syst´mu. e 2.4.3.6 Refaktorov´n´ aı Pomoc´ refaktorov´n´ dok´ˇeme udrˇet architekturu syst´mu ˇistou, jednoduchou, kter´ ı aı az z e c a vˇdy odpov´ a poˇadavk˚m na syst´m. Refaktorov´n´ restrukturalizujeme k´d bez zmˇny z ıd´ z u e a ım o e jeho funkcionality, dˇl´me k´d ˇitelnˇjˇ´ pˇehlednˇjˇ´ l´pe odpov´ ıc´ aktu´ln´ ea oc e sı, r e sı, e ıdaj´ ı a ımu ch´p´n´ aaı syst´mu. Pˇed refaktorov´n´ spust´ vˇechny testy, refaktorujeme po mal´ch kroc´ a pot´ e r a ım ıme s y ıch e znovu spust´ vˇechny testy, abychom se ujistili, ˇe syst´m funguje stejnˇ. V´ o refaktorov´n´ ıme s z e e ıce aı nalezneme v [18]. 2.4.3.7 P´rov´ programov´n´ a e aı P´rov´ programov´n´ znamen´ dva lidi u jednoho poˇ´ ce s jednou kl´vesnic´ Pro- ae aı a cıtaˇ a ı. as ’ dukuje lepˇ´ k´d neˇ pˇi pr´ci kaˇd´ho zvl´ˇt , prob´ a neust´l´ revize k´du, lepˇ´ porozumˇn´ sı o zr a ze ıh´ aa o sı eı co syst´m dˇl´. Vˇechen produkˇn´ k´d by mˇl b´t tvoˇen p´rov´n´ Tak´ vhodn´ pro v´uku e ea s cı o ey r a a ım. e e y (slabˇ´ se uˇ´ od zdatnˇjˇ´ a naopak). sı cı e sıch 2.4.3.8 Kolektivn´ vlastnictv´ k´du ı ıo Veˇker´ k´d patˇ´ vˇem v´voj´ˇ˚m. Minimalizuje prodlevy v ˇeˇen´ probl´m˚. Kdokoliv s yo rı s y aru rs ı eu potˇebuje zmˇnu v k´du, kter´ nenapsal, tak jednoduˇe udˇl´. r e o y s ea 2.4.3.9 40-ti hodinov´ pracovn´ t´den y ıy Unaven´ v´voj´ˇ p´se mizern´ k´d, dˇl´ v´ chyb. y y ar ıˇ yo e a ıce
    44. ˇˇ 30 KAPITOLA 2. RESERSE 2.4.3.10 Z´kazn´ na spr´vn´m m´ e (On-site customer) a ık ae ıstˇ Z´kazn´ by mˇl aktivnˇ pˇisp´ a ık e e r ıvat do v´voje. Vykl´d´ uˇivatelsk´ pˇ´ ehy, ovlivˇuje y aa z e rıbˇ n poˇadavky, nastavuje priority, zodpov´ a ot´zky od v´voj´ˇ˚. Dokumentace je m´nˇ v tiˇtˇn´ z ıd´ a y aru ee se e podobˇ, je sp´se sd´ e ıˇ ılena vˇemi ˇleny t´mu. s c y 2.4.3.11 Standardn´ podoba k´du ı o Jelikoˇ kaˇd´ m˚ˇe editovat cokoliv, je d˚leˇit´ zav´st urˇit´ standard v psan´ k´du, z z y uz uze e cy ıo kter´ by mˇli vˇichni ˇlenov´ t´mu dodrˇovat. y es c ey z 2.4.3.12 Sjednocen´ pracovn´ prostˇed´ e ı rı Usnadˇuje pr´ci v p´ru. Jsou nastaveny jednotn´ kl´vesov´ zkratky a podobnˇ. n a a ea e e 2.5 Testovac´ framework JUnit ı JUnit je n´stroj pro jednotkov´ (unit) testov´n´ v Javˇ, jehoˇ autoˇi jsou Kent Beck a a e aı e z r Erich Gamma. Vych´z´ z p˚vodn´ n´stroje SUnit pro Smalltalk napsan´ho Kentem Beckem aı u ıho a e pˇi pˇedstaven´ XP. Je stˇˇejn´ prostˇedkem TDD. rr ı ez ım r Dnes pˇevl´daj´ 2 major verze tohoto testovac´ frameworku, a to JUnit 3.x (3.8.1) a raı ıho JUnit 4.x (4.4). Pˇedstav´ zde obˇ verze, zaˇneme st´le nejrozˇ´renˇjˇ´ verz´ 3.x. r ım e c a sıˇ e sı ı 2.5.1 JUnit 3.x 2.5.1.1 Architektura • Soubor test˚ se skl´d´ ze soubor˚ testovac´ sad, tj. tˇ´ kter´ dˇd´ od pˇedka u aa u ıch rıd, e eı r junit.framework.TestCase. Testovac´ sada obsahuje testy. ı • Testy jsou metody public void, pokud je oznaˇ´ poˇ´teˇn´ slovem test, napˇ. public cıme ca c ım r void testAddTwoIntegers(), test se spust´ automaticky. ı Kaˇd´ test by mˇl b´t spuˇtˇn nez´visle na ostatn´ a neovlivˇovat je. zy ey se a ıch n • private void setUp() - provede se pˇed kaˇd´m testem, nastavuje stejn´ prostˇed´ r zy e r ı, stejn´ testovac´ data a ı • private void tearDown() - provede se bezprostˇednˇ po skonˇen´ kaˇd´ho testu, ukl´ ı r e c ı ze ız´ pro dalˇ´ test, zav´ a datov´ proudy sı ır´ e 2.5.1.2 Assert Pomoc´ pˇedpoklad˚ (assert˚) definujeme jak se m´ testovan´ k´d zachovat. Jsou ır u u a yo implementov´ny ve tˇ´ e junit.framework.Assert, vˇechny maj´ hlaviˇku public static a rıdˇ s ı c void. V testovac´ sadˇ je p´seme bez pˇ´ ı e ıˇ rıstupu ke tˇ´ e. rıdˇ • assertTrue(boolean expression) - oˇek´v´ true, c aa • assertFalse(boolean expression) - oˇek´v´ false, c aa • assertNotNull(Object object) - oˇek´v´ nenullovou instanci, c aa • assertNull(Object object) - oˇek´v´ nullovou instanci, c aa • assertEquals(Object x, Object y) - oˇek´v´ rovnost na equals() objekt˚ x a y, c aa u
    45. ˇˇ KAPITOLA 2. RESERSE 31 e’ • assertSame(Object x, Object y) - oˇek´v´, ˇe maj´ objekty x a y stejnou pamˇt ovou c aaz ı referenci, • assertNotSame(Object x, Object y) - oˇek´v´, ˇe nemaj´ objekty x a y stejnou c aa z ı ’ovou referenci, pamˇt e • fail() - skonˇ´ test chybou, cı Vˇechny uveden´ metody maj´ jeˇtˇ jeden nepovinn´ parametr String message, kter´ s e ı se y y se zobraz´ pokud assert selˇe. ı z 2.5.1.3 Integrace s IDE Pˇi TDD je d˚leˇit´, abychom testy pouˇtˇli ˇasto a byly co nejv´ automatizovan´. r uze se c ıce e K tomu n´m pom´h´ integrace s v´vojov´m prostˇed´ JUnit je standardn´ souˇ´st´ bˇˇn´ch a aa y y r ım. ı ca ı ez y n´stroj˚ jako Eclipse, Netbeans a IntelliJ Idea. a u 2.5.2 JUnit 4.x Tento odstavec by mˇl pokr´t poˇ´teˇn´ uvod do nov´ ˇady JUnitu, kter´ se hodnˇ e y ca c ı ´ er y e inspiroval frameworky TestNG, Popper a JMock. Pˇedstav´ hlavn´ zmˇny oproti minul´ verzi r ım ı e e a vˇe uk´ˇu na pˇ´ s az rıkladu, kter´ jsem pˇevzal z [8]. y r 2.5.2.1 Hlavn´ zmˇny oproti 3.x ı e • Testovac´ sada nemus´ dˇdit od junit.framework.TestCase. ı ıe • Testy nemus´ zaˇ´ ı cınat prefixem “test”, m´ ısto toho jsou oznaˇeny anotac´ @Test. c ı • M´ısto metod setUp() a tearDown() se pˇed a po kaˇd´m testu volaj´ metody oznaˇen´ r ze ı ce anotacemi @Before a @After. • Pokud potˇebujeme spustit metodu v r´mci testovac´ sady jen jednou pˇi startu a jednou r a ı r na konci, oznaˇ´ metodu anotacemi @BeforeClass a @AfterClass. cıme • Anotace @Test m˚ˇe m´ parameter Timeout, kter´ kdyˇ prov´dˇn´ testu pˇekroˇ´ test uz ıt y z aeı r cı, selˇe. z • Spustiteln´ jen od JDK 5. e 2.5.2.2 assertThat Konstrukt assertThat() doplˇuje a rozˇiˇuje pouˇit´ pˇedpoklad˚ z 3.x, doslovn´ n sr zı r u y pˇeklad m˚ˇe zn´ “oˇek´v´m ˇe” a takto se i zapisuje. Prvn´ argument je objekt, na kter´ r uz ıt c a a z ı y prov´d´ a ıme pˇedpoklad. Druh´ parametr je tzv. matcher, tj. funkce, kterou formulujeme r y pˇedpoklad. r assertThat(something, eq(\"Hello\")); assertThat(something, isA(Color.class)); assertThat(something, contains(\"World\")); assertThat(myList, hasItem(\"3\")); assertThat(something, not(contains(\"Cheese\"))); assertThat(responseString, either(containsString(\"color\")) .or(containsString(\"colour\")))
    46. ˇˇ 32 KAPITOLA 2. RESERSE 2.5.2.3 Pˇedpoklady a teorie r Pˇedpoklady slouˇ´ k explicitn´ r zı ımu vyj´dˇen´ podm´ ar ı ınek, za kter´ch mus´ test proj´ y ı ıt, obecnˇ z´vislost´ mimo rozsah vlastn´ testu. Teorie se skl´d´ ze vstupn´ testovac´ dat, ea ı ıho aa ıch ı pˇedpokladu, za kter´ho je schopen test s daty pracovat a vlastn´ testu. D´ teorii m˚ˇeme r e ıho ıky uz ˇ´ za pˇedpokladu tˇchto vstupn´ podm´ rıci, r e ıch ınek se testovan´ k´d mus´ chovat takto. D´ yo ı ıky podm´ am je moˇn´ nechat JUnit generovat vstupn´ data, kter´ jdou za rozsah toho, jak ınk´ ze ı a bychom oˇek´vali nebo toho, co by bylo pracn´ vyj´dˇit klasicky. ca e ar @RunWith(Theories.class) public class UserTest { @DataPoint public static String GOOD_USERNAME = \"optimus\"; @DataPoint public static String USERNAME_WITH_SLASH = \"optimus/prime\"; @Theory public void filenameIncludesUsername(String username) { assumeThat(username, not(containsString(\"/\"))); assertThat(new User(username).configFileName(), containsString(username)); } } Anotace @DataPoint definuje vstupn´ data teorie. Spouˇtˇˇ testu s´m zad´v´ do tes- ı s ec a aa tovac´ metody filenameIncludesUsername vˇechny kompatibiln´ veˇejn´ promˇnn´ oznaˇen´ ı s ıre ee ce anotac´ @DataPoint (shoduj´ se typy promˇnn´ a parametru). Pˇedpoklad je vyj´dˇen´ pomoc´ ı ı ee r ar y ı assertThat. Tato teorie by se dala pˇeloˇit do ˇeˇtiny asi takto:“Za pˇedpokladu, ˇe username rz cs r z neobsahuje /, mus´ platit, ˇe jm´no konfiguraˇn´ souboru uˇivatele obsahuje jeho jm´no.” ı z e c ıho z e 2.6 Testovac´ framework JMock ı V t´to kapitole pˇedstav´ d˚vody, kdy je vhodn´ pouˇ´ mockov´n´ pˇedstav´ jeden z e r ım u e zıt a ı, r ım nejpouˇ´ ejˇ´ mockovac´ framework˚ v Javˇ. Z´kladn´ syntaxi a pouˇit´ uk´ˇu na kr´tk´m zıvanˇ sıch ıch u ea ı z ı az ae pˇ´ rıkladu. 2.6.1 Mockov´n´ Mock objekty a ı, Vˇude tam, kde je klasick´ unit testov´n´ obt´zn´, pomal´ nebo se jedn´ o takˇka s e aı ıˇ e e a r neˇeˇiteln´ probl´m, si mus´ rs y e ıme poradit, jak danou vˇc otestovat. Napˇ´ e rıklad, pokud testovan´ a tˇ´ rıda: • produkuje nedeterministick´ v´sledky (aktu´ln´ ˇas, aktu´ln´ teplota), ey a ıc aı • m´ stavy, kter´ je tˇˇk´ vytvoˇit nebo reprodukovat (s´ ’ov´ chyba), a e ez e r ıt a • m´ pomalou odezvu (inicializace kompletn´ DB spojen´ a ıho ı), • jeˇtˇ neexistuje nebo bude mˇnit chov´n´ se e a ı, • bude muset obsahovat rozd´ e informace pro testy a pro re´ln´ nasazen´ [11] ıln´ ae ı. Mock objekt je faleˇn´ z´stupce, kter´ se navenek chov´ stejnˇ jako jeho re´ln´ sy a y a e ay protˇjˇek. M´me na v´bˇr z v´ implementac´ es a ye ıce ı: • implementovat faleˇnou tˇ´ (fake) s oˇek´vanou vlastn´ implementac´ s rıdu ca ı ı, • pouˇ´ pˇedem vytvoˇen´ faleˇn´ implementace zn´m´ch tˇ´ (napˇ. HttpServletRequest), zıt r re se ay rıd r Spring nab´ ı mock implementace k bˇˇn´m komponent´m syst´mu (MockHttpServletRequest), ız´ ez y a e
    47. ˇˇ KAPITOLA 2. RESERSE 33 • pouˇ´ statick´ mock creator, napˇ. MockCreator, kter´ vytvoˇ´ kostru tˇ´ implemen- zıt y r y rı rıdy tuj´ ı testovan´ rozhran´ ıc´ e ı, • pouˇ´ dynamick´ mock creator, kter´ za bˇhu v testu vytvoˇ´ objekt implementuj´ ı zıt y y e rı ıc´ testovan´ rozhran´ Pˇedstaviteli t´to kategorie jsou JMock, EasyMock a rMock. e ı. r e 2.6.2 Architektura Spouˇtˇn´ JMock testu se liˇ´ podle pouˇit´ verze JUnitu. Pˇedstav´ pouˇ´ ejˇ´ verzi se ı sı ze r ım zıvanˇ sı s JUnit 3.x. • Testovac´ sada dˇd´ od abstraktn´ tˇ´ MockTestCase. ı eı ı rıdy • Mock objekt vytv´ˇ´ pomoc´ metody mock(Clazz.class). arıme ı • K objektu, kter´ napodobuje, se dostaneme vˇdy pomoc´ mockObject.proxy(). y z ı 2.6.3 Pˇ´ rıklad Pˇedstavme si jednoduch´ pˇ´ r y rıklad [7]. Vyv´ ıme software pro nˇjakou bankovn´ spoleˇnost. ıj´ e ı c Budeme potˇebovat rozhran´ Account, kter´ zaobaluje informace o uˇtech a rozhran´ Ac- r ı e ´c ı countManager, kter´ nad nimi prov´d´ operace. e aı public interface AccountManager { void transfer(Account from, Account to, double amount) throws OutOfMoneyException; } public interface Account { void charge(double amount) throws OutOfMoneyException; void deposit(double amount); } Banka je chamtiv´. Kdykoliv je proveden pˇevod z jednoho uˇtu na druh´, strhne se a r ´c y 10 Kˇ + 10% z pˇev´dˇn´ ˇ´stky. Tyto pen´ se pˇip´sou na bonusov´ uˇet banky, kde si c r a e e ca ıze r ıˇ y ´c je bankovn´ manaˇeˇi rozdˇl´ Naimplementujeme chov´n´ tˇ´ AccountManagerImpl pomoc´ ı zr e ı. a ı rıdy ı TDD, tedy nap´seme test pˇed vlastn´ implementac´ ıˇ r ı ı. public class AccountManagerImplTest extends MockObjectTestCase { public void testTransferWithoutOvercharging() throws Exception { Mock mockAccount1 = mock(Account.class); mockAccount1 .expects(once()) .method(\"charge\") .with(eq(100.0 * 1.10 + 10.0)); Vytvoˇili jsme nov´ Mock objekt pomoc´ JMock, kter´ bude pozdˇji pouˇit jako zdrojov´ uˇet. r y ı y e z y ´c Ve drud´ ˇ´dce jsme pˇidali pˇedpoklad. Oˇek´v´me, ˇe AccountManagerImpl zavol´ jednou e ra r r c aa z a metodu charge() s argumentem rovn´m 100*1.1 + 10 = 120. y Mock mockAccount2 = mock(Account.class); mockAccount2 .expects(once()) .method(\"deposit\") .with(eq(100.0)) .after(mockAccount1, \"charge\");
    48. ˇˇ 34 KAPITOLA 2. RESERSE Vytvoˇili jsme Mock objekt, kter´ bude pouˇit jako c´ y uˇet. Pˇedpokl´d´me, ˇe AccountMa- r y z ılov´ ´c r aa z nagerImpl bude volat metodu deposit() s argumentem rovn´m 100. Posledn´ ˇ´dka ˇ´ a, ˇe bude y ı ra rık´ z metoda vol´na aˇ po vol´n´ metody mockAccount1.charge(), ˇ´ z nepˇip´seme pen´ na c´ y a z aı cımˇ r ıˇ ıze ılov´ uˇet, pokud se pˇedt´ neodepsaly ze zdrojov´ho. ´c r ım e Mock mockBonusAccount = mock(Account.class); mockBonusAccount .expects(once()) .method(\"deposit\") .with(eq(100.0 * 0.10 + 10.0)) .after(mockAccount1, \"charge\"); Nakonec jsme vytvoˇili Mock objekt pro bonusov´ uˇet. AccountManagerImpl na nˇj pˇip´se r y ´c e r ıˇ pen´ pomoc´ metody deposit() s argumentem 100*0.1+10=20, kterou zavol´ aˇ po uspˇˇn´m ıze ı az ´ es e odeps´n´ penˇz ze zdrojov´ho uˇtu. aı e e ´c Account account1 = (Account) mockAccount1.proxy(); Account account2 = (Account) mockAccount2.proxy(); Account bonusAccount = (Account) mockBonusAccount.proxy(); Nyn´ jednoduˇe z Mock objekt˚ vytvoˇ´ uˇty ı s u rıme ´c AccountManagerImpl man = new AccountManagerImpl(bonusAccount); man.transfer(account1, account2, 100.0); } } a nakonec vytvoˇ´ AccountManagerImpl, na kter´m zavol´me metodu transfer(). rıme e a V pr˚bˇhu cel´ho testu jsme nikde nevolali JUnit asserty. Aˇ test skonˇ´ pˇedek ue e z cı, r MockObjectTestCase pˇekontruje zda vˇechny zadan´ pˇedpoklady na Mock objekty byly r s er splnˇny. Pokud nebyly, pokud byla metoda vol´na v jin´m poˇad´ nebo s jin´m typem ar- e a e rı y gumentu, test skonˇ´ s fail() vyj´ cı ımkou AssertionFailedError.
    49. KAPITOLA 3. IMPLEMENTACE 35 3 Implementace V reˇerˇn´ ˇ´sti jsem uk´zal z´klady technologi´ tvorby webov´ch aplikac´ na platformˇ s s ı ca a a ı y ı e Java, v t´to ˇ´sti uk´ˇu praktick´ aplikov´n´ nabyt´ch znalost´ implementac´ ˇ´sti port´lu pro e ca az e aı y ı ı ca a hled´n´ spolubydl´ ıch. aı ıc´ 3.1 Zad´n´ aı Vylepˇit st´vaj´ ı port´l pro hled´n´ spolubydlen´ www.chcispolubydlici.cz o moˇnost s a ıc´ a aı ı z inzerovat popt´vku. Pomoc´ t´to nov´ funkcionality p˚jde v inzer´tech vyhled´vat. Pˇidat au- a ıe e u a a r tomatick´ maz´n´ inzer´t˚ a umoˇnit export inzer´t˚ do form´tu RSS. e aı au z au a 3.2 Anal´za y Uˇivatel´ v anketˇ vyplnili nejpalˇivˇjˇ´ vˇci, kter´ na port´lu postr´daj´ Kromˇ z e e c e sı e e a a ı. e pˇeloˇen´ do angliˇtiny je inzerov´n´ popt´vky na druh´m m´ e. Neimplementov´n´ inzerov´n´ rzı c aı a e ıstˇ aı aı popt´vky mˇlo sv˚j d˚vod, lid´ nab´ ıc´ spolubydlen´ vˇtˇinou tuto kategorii inzer´t˚ pˇ´ s a e uu e ızej´ ı ı es a u rıliˇ nevyhled´vaj´ Avˇak niˇemu neuˇkod´ pokud tato funkcionalita bude a sami si statisticky a ı. s c s ı, ovˇˇ´ erıme, jestli se n´ˇ n´zor potvrdil ˇi vyvr´til. as a c a 3.2.1 Co uˇ je naimplementov´no? z a Port´l se neust´le vyv´ ı. Kdyˇ jsem poprv´ pˇiˇel do firmy Simple Way s.r.o., port´l uˇ a a ıj´ z e rs az byl nasazen a mˇli jsme p˚lroˇn´ zkuˇenosti s provozem. V´voj aplikace do t´to doby a nesazen´ e u cı s y e ı je v podstatˇ obsahem bakal´ˇsk´ pr´ce studenta Luboˇe Raˇansk´ho. e ar e a s c e 3.2.2 Pr˚ uzkum trhu Na internetov´m trhu se spolubydlen´ vynikaj´ pˇev´ˇnˇ servery www.spolubydlici.cz, e ım ı r az e www.espolubydleni.cz a www.bydlim.com. Server www.spolubydlici.cz umoˇnuje inzerovat nab´ zˇ ıdku i popt´vku, inzer´t nen´ de- a a ı tailn´ a tak i vyhled´v´n´ inzer´t˚ postr´d´ v´bˇr parametr˚, kter´ uˇivatele zaj´ ı aaı au aa ye u ez ımaj´ Hled´n´ ı. a ım ˇ jde omezit v´bˇr jen v Praze, Moravˇ a Slezsku a ve zbytku CR, pomoc´ maxim´ln´ ceny a ye e ı aı pohlav´ Evidentnˇ je to nedostaˇuj´ ı a uˇivatel mus´ hled´n´ str´vit zbyteˇnˇ v´ ˇasu. ı. e c ıc´ z ı a ım a c e ıce c Navzdory tomu je nejpouˇ´ ejˇ´ port´lem v t´to kategorii. zıvanˇ sım a e Server www.espolubydleni.cz je na tom o pozn´n´ l´pe. Umoˇnuje inzerovat nab´ aıe zˇ ıdku i popt´vku, inzer´t je v´ detailn´ jelikoˇ se server objevil po uspˇˇn´m nasazen´ spolubydl´ ıch, a a ıce ı, z ´ es e ı ıc´ m˚ˇeme pˇedpokl´dat, ˇe se j´ nechal inspirovat. Adresa se zad´v´ pomoc´ okresu z html uz r a z ım aa ı selectu a mˇsta, kter´ je v html text area. Hled´n´ lze omezit v´bˇr podle druhu domu, okresu e e a ım ye a ceny n´jemn´ho od/do, coˇ opˇt nedostaˇuje. Zaj´ a e ze c ımav´ funkce je napˇ´ a rıklad sumarizovan´ a ˇ pr˚mˇrn´ cena n´jmu v Praze, v Brnˇ a ve zbytku CR. uea a e Posledn´ zm´ en´ server www.bydlim.com je sponzorov´n medi´ln´ ı ınˇ y a a ımy partnery jako ˇıp, atlas.cz, S´ Annonce, 24hodin. Nasadil dokonce let´kovou kampaˇ po Praze. Adresa je jen a n mˇsto a nav´ jen velk´ (cca. 11 krajsk´ch mˇst). Vybrat lze druh bytu, jestli je dotyˇn´ student e ıc e y e cy nebo kuˇ´k. Inzer´t je m´nˇ detailn´ neˇ v pˇedchoz´ pˇ´ e. ra a ee ız r ım rıpadˇ Co jsem nikde nenaˇel? Ohlednˇ lokalit, kde by chtˇl uˇivatel bydlet, jsem nikde nevidˇl s e ez e ˇ moˇnost zapsat lokalit v´ z ıce. Student CVUT napˇ´ rıklad hled´ bydlen´ pobl´z Dejvic, ˇili P6 a ı ıˇ c Dejvice, P6 Bubeneˇ nebo P6 Stˇeˇovice. M˚ˇe nastat dalˇ´ pˇ´ c rs uz sı rıpad, uˇivatel chce bydlet na z
    50. 36 KAPITOLA 3. IMPLEMENTACE Praze 1, ale je mu jedno v jak´ ˇtvrti a mimo to by se spokojil s Prahou 2 Nov´ Mˇsto. Nen´ ec ee ı nic jednoduˇˇ´ neˇ zadat nˇco jako P1 nez´leˇ´ a P2 Nov´ Mˇsto. S podobnou logikou vˇci ssıho z e a zı ee e bychom mohli zadat celou Prahu, popˇ. cel´ kraj. r y 3.2.3 Prototypy Po prozkoum´n´ trhu jsem vytvoˇil z´kladn´ prototyp vkl´d´n´ nov´ho inzer´tu. UI aı ra ı aaı e a prototypy pom´haj´ s ujasnˇn´ poˇadavk˚ a k vyjasnˇn´ pojm˚ se z´kazn´ aı e ım z u eı u a ıkem. Obr´zek 3.1: Z´kladn´ UI prototyp a a ı V nov´m inzer´tu n´s zaj´ a kromˇ poˇadovan´ch lokalit tak´ maxim´ln´ pˇijateln´ e a a ım´ e z y e aır a cena za bydlen´ datum odkdy se chce uˇivatel nastˇhovat a na jak dlouho, jestli je student, ı, z e kuˇ´k, jestli m´ zv´re, velikost bytu, druh domu. D´le pak poˇadovan´ vybaven´ bytu (internet, ra a ıˇ a z e ı kabelov´ televize, telefon,..) a dalˇ´ vybaven´ v domu (ledniˇka, praˇka, mikrovlnka,...). a sı ı c c 3.3 Implementace V r´mci release pl´nov´n´ jsem rozdˇlil vˇechny uˇivatelsk´ pˇ´ ehy do requirement˚ a ty a a aı e s z e rıbˇ u do jednotliv´ch iterac´ Jak jsem psal v kapitole 2.4.3.2, iterace by mˇla prob´ y ı. e ıhat 1-4 t´dny. Tam, y kde to bylo moˇn´, jsem psal testy nejdˇ´ Nˇkter´ ˇ´sti vznikaly p´rov´m programov´n´ ze rıve. e e ca ay a ım s vedouc´ moj´ pr´ce Ing. Andrejem Zacharem a studentem Luboˇem Raˇansk´m. Veˇker´ ım ıa s c y sy k´d, kter´ uv´d´ v t´to ˇ´sti, je v rozsahu nutn´m pro pochopen´ dan´ho ˇeˇen´ Cel´ projekt o y a ım e ca e ı e r s ı. y je k dispozici na pˇiloˇen´m CD. rze 3.3.1 Iterace 1 Do prvn´ iterace jsem napl´noval implementaci z´kladn´ requirementu. ı a a ıho • Umoˇnit vkl´d´n´ nov´ho inzer´tu. z aaı e a
    51. KAPITOLA 3. IMPLEMENTACE 37 Podle zvyklosti pouˇ´ e u vkl´d´n´ nab´ zıvan´ aaı ıdky jsem ukon rozdˇlil na nˇkolik obrazovek. ´ e e Na prvn´ obrazovce se zad´vaj´ stˇˇejn´ parametry inzer´tu, na druh´ obrazovce poˇadovan´ ı a ı ez ı a e z e lokality, na tˇet´ obrazovce potom pˇihlaˇovac´ udaje na server. Posledn´ obrazovka slouˇ´ pro rı r s ı´ ı zı rekapitulaci zadan´ch udaj˚. y´u Obr´zek 3.2: Vkl´d´n´ nov´ho inzer´tu a aaı e a V prvn´ ˇadˇ potˇebujeme datab´zovou reprezentaci inzer´tu a uˇivatele, definujeme ır e r a a z proto BO Demand a HomelessInzerent. Demand.java @Entity public class Demand extends AbstractBo { private static final long serialVersionUID = -4713748752937686298L; private HomelessInzerent homelessInzerent; private Set<FlatSizeType> flatSizeTypeSet = new HashSet<FlatSizeType>(); //and many other properties with appropriate getters and setters @OneToOne(cascade = { CascadeType.ALL }) public HomelessInzerent getHomelessInzerent() { return homelessInzerent; } public void setHomelessInzerent(HomelessInzerent homelessInzerent) { this.homelessInzerent = homelessInzerent; } @ManyToMany(fetch = FetchType.EAGER) public Set<FlatSizeType> getFlatSizeTypeSet() { return flatSizeTypeSet; } public void setFlatSizeTypeSet(Set<FlatSizeType> flatSizeType) { this.flatSizeTypeSet = flatSizeType;
    52. 38 KAPITOLA 3. IMPLEMENTACE } } HomelessInzerent.java @Entity public class HomelessInzerent extends Inzerent { private static final long serialVersionUID = -546574687654564L; } Vytvoˇili jsme BO, d´le mus´ r a ıme d´t Hibernatu vˇdˇt, aby s nimi mohl pracovat. a ee Pˇid´me je beanˇ sessionFactory do property annotatedClasses. ra e core-service.java <bean id=\"sessionFactory\" class=\"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean\"> ... <property name=\"annotatedClasses\"> <list> <value>cz.sw.getroommate.bo.HomelessInzerent</value> <value>cz.sw.getroommate.bo.Demand</value> </list> </property> ... </bean> Podle obr´zku 2.6 se webov´ aplikace skl´d´ z v´ vrstev. Datov´ model jiˇ m´me a a aa ıce y za naimplementovan´, kde budeme pokraˇovat? Zaˇneme od servisn´ vrstvy, respektive od jej´ y c c ı ıho testu. DemandServiceTest.java public class DemandServiceTest extends TestCase { DemandService demandService; Demand demand; // OVERRIDE protected void setUp() throws Exception { super.setUp(); demandService = SpringTestUtil.getDemandService(); setupDemand(); } public void setupDemand() { demand = new Demand(); //and set all properties } public void testSaveAndLoad() { try { demandService.saveOrUpdate(demand); } catch (Exception e) { fail(e.getMessage()); } long id = demand.getId(); Demand loadeDemand = demandService.getDemand(id); assertEquals(demand, loadeDemand); assertNotNull(loadeDemand.getHomelessInzerent()); } }
    53. KAPITOLA 3. IMPLEMENTACE 39 Test n´m spadne kv˚li tomu, ˇe jsme zat´ nenapsali metody demandService.getDemand() a u z ım a demandService.saveOrUpdate(Demand demand). Ted’ je ta spr´vn´ chv´ je doplnit. aa ıle DemandService.java public class DemandService extends AbstractService<IDemandDao> { public void saveOrUpdate(Demand demand) throws Exception { assert (demand != null); Date now = new Date(); if (demand.getCreated() == null) { demand.setCreated(now); } demand.setUpdated(now); demand.setCancellingEmailSent(null); dao.saveOrUpdate(demand); } public Demand getDemand(long id) { return dao.getDemandById(id); } } D´le zb´v´ zapsat beanu, kterou bude Spring IoC kontejner injektovat. a ya core-service.xml <bean id=\"demandService\" class=\"cz.sw.getroommate.service.DemandService\"> <property name=\"dao\" ref=\"demandHibernateDao\" /> </bean> Test poˇ´d neproch´z´ jeˇtˇ je tˇeba napsat DAO vrstvu, rozhran´ IDemandDao re- ra a ı, s e r ı spektive jeho implementaci DemandHibernateDao. DemandHibernateDao.java public class DemandHibernateDao extends SimpleCrudHibernateDao<Demand> implements IDemandDao { public Demand getDemandById(long id) { return get(Demand.class, id); } public void saveOrUpdate(Demand demand) { getHibernateTemplate().saveOrUpdate(demand); } } Opˇt je potˇeba zapsat beanu do xml deskriptoru Springu, za zm´ e r ınku stoj´ beana abs- ı tractHibernateDao, pomoc´ kter´ dos´hneme deklarativn´ transakc´ viz. kapilota 2.3.8.1. ı e a ıch ı, core-service.xml <bean id=\"demandHibernateDao\" parent=\"abstractHibernateDao\"> <property name=\"target\"> <bean class=\"cz.sw.getroommate.dao.hibernate.DemandHibernateDao\" parent=\"abstracDao\" /> </property> </bean> <bean id=\"abstractHibernateDao\"
    54. 40 KAPITOLA 3. IMPLEMENTACE class=\"org.springframework.transaction.interceptor.TransactionProxyFactoryBean\" abstract=\"true\"> <property name=\"transactionManager\" ref=\"hibernateTransactionManager\" /> <property name=\"transactionAttributes\"> <props> <prop key=\"load*\">PROPAGATION_REQUIRED,readOnly</prop> <prop key=\"save*\">PROPAGATION_REQUIRED</prop> <prop key=\"delete*\">PROPAGATION_REQUIRED</prop> <prop key=\"get*\">PROPAGATION_REQUIRED,readOnly</prop> </props> </property> </bean> Naimplementovali jsme tedy servisn´ perzistentn´ vrstvu a datov´ model. Vˇechnu ı, ı y s funkˇnost m´me ovˇˇenou testy. Vrhneme se tedy na UI a webovou vrstvu. Jelikoˇ budeme c a er z m´ v´ obrazovek, kter´mi budeme navigovat na dalˇ´ a na pˇedchoz´ obrazovku, v uvahu ıt ıce y sı r ı ´ pˇipad´ implementace pomoc´ Spring Web MVC a jeho kontrol´ru r a ı e AbstractWizardFormController nebo pomoc´ SWF. Kv˚li uˇivatelsk´ pˇ´ etivosti SWF a ı u z e rıvˇ tak´ proto, ˇe nab´ e z ıdkov´ ˇ´st je dˇlan´ pr´vˇ takto, jsem zvolil SWF. a ca e a ae Zaˇneme od definice URL mapov´n´ pomoc´ kter´ho budeme pˇistupovat k vkl´d´n´ c a ı, ı e r aaı inzer´t˚, pˇid´me property do beany urlMapping do property mappings, au r a action-servlet.java <bean id=\"urlMapping\" class=\"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\"> <property name=\"alwaysUseFullPath\" value=\"true\" /> <property name=\"mappings\"> <props> <prop key=\"/hledam-bydleni.html\">flowController</prop> </props> </property> </bean> kter´ ukazuje na beanu flowController. SWF je nakonfigurov´no podobnˇ jako jsem a a e ukazoval v kapitole 2.2.8.7, s t´ rozd´ ım ılem, ˇe pro vazbu URL na flow je pouˇita tˇ´ z z rıda FlowIdExtractor, kter´ n´m dovoluje zapsat do mapy property mappings kl´c celou URL aa ıˇ (bez koncov´ho .html) a jako hodnotu flow, kter´ se spust´ e e ı. action-servlet.java <!-- Launches new flow executions and resumes existing executions. --> <flow:executor id=\"flowExecutor\" registry-ref=\"flowRegistry\" /> <!-- Creates the registry of flow definitions for this application --> <flow:registry id=\"flowRegistry\"> <flow:location path=\"/WEB-INF/flows/**/*-flow.xml\" /> </flow:registry> <bean id=\"flowController\" class=\"org.springframework.webflow.executor.mvc.FlowController\"> <property name=\"flowExecutor\" ref=\"flowExecutor\" /> <property name=\"argumentHandler\"> <bean class=\"cz.sw.getroommate.web.custom.FlowIdExtractor\"> <property name=\"mappings\"> <map> <entry key=\"hledam-bydleni\" value=\"new-demand-flow\" /> </map>
    55. KAPITOLA 3. IMPLEMENTACE 41 </property> </bean> </property> </bean> Na Dispacher servlet (viz. kapitola 2.2.7.4) pˇijde URL, kterou se odk´ˇeme do flow- r az Controlleru, kter´ spust´ new-demand-flow.xml. y ı new-demand-flow.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <flow> <start-state idref=\"newDemand\" /> <action-state id=\"newDemand\"> <action bean=\"demandAction\" method=\"createDemand\" /> <action bean=\"userAction\" method=\"createHomelessInzerent\" name=\"userAction\" /> <transition on=\"userAction.success\" to=\"demandDetails\" /> </action-state> <subflow-state id=\"demandDetails\" flow=\"demand-details-flow\"> <transition to=\"demandLocation\" /> </subflow-state> ... <action-state id=\"saveDemand\"> <action bean=\"demandAction\" method=\"saveDemand\" /> <action bean=\"loginAction\" method=\"setCurrentUser\" name=\"setCurrentUser\" /> <transition on=\"setCurrentUser.homelessInzerent\" to=\"finish\" /> <transition on=\"error\" to=\"summary\" /> </action-state> <end-state id=\"finish\" view=\"externalRedirect:spolubydlici.html\" /> </flow> Ve stavu newDemand vytvoˇ´ nov´ BO Demand a HomelessInzerent, kter´ uloˇ´ do rım e e zım SWF Conversation scope (viz. kapitola 2.2.8.5) a pˇi uspˇchu pˇech´z´ do subflow demandDe- r´ e r a ım tails, kde bude definovan´ prvn´ obrazovka. Po skonˇen´ subflow (pokud uˇivatel klikne na but- a ı cı z ton dalˇ´ pˇech´z´ na druhou, tˇet´ obrazovku... Na konci ve stavu saveDemand vol´m metodu sı) r a ım rı a demandAction.saveDemand(), kter´ inzer´t uloˇ´ do DB a loginAction.setCurrentUser(), a a zı kter´ zaloguje uˇivatele t´ ˇe jej uloˇ´ do HTTP session (viz. kapitola 2.2.7.5). a z ım, z zı Pod´ıvejme se napˇ´ rıklad na metodu demandAction.createDemand() action-servlet.java <bean id=\"demandAction\" class=\"cz.sw.getroommate.web.action.flow.DemandFormAction\" parent=\"abstractFormAction\"> <property name=\"validator\" ref=\"demandValidator\" /> <property name=\"propertyEditorRegistrar\" ref=\"enumsEditorRegistrar\" /> </bean> DemandFormAction.java public class DemandFormAction extends FormAction implements ApplicationContextAware { public DemandFormAction() { super(DemandFormObject.class); } public Event createDemand(RequestContext context) {
    56. 42 KAPITOLA 3. IMPLEMENTACE Demand newDemand = new Demand(); newDemand.setWantToMoveIn(new Date()); context.getConversationScope().put(Constants.DEMAND, newDemand); return success(); } Vˇimnˇme si konstruktoru DemandFormAction(), j´ z vol´me kostruktor FormAction s e ımˇ a 1 . Vytvoˇili jsme nov´ inzer´t a uˇivatele, ted’ s parametrem, kter´ je typ tˇ´ Form Objectu y rıdy r y a z n´s zaj´ a subflow demandDetails. a ım´ demand-details-flow.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <flow> <start-state idref=\"demandDetails\" /> <view-state id=\"demandDetails\" view=\"newDemandForm\"> <render-actions> <action bean=\"demandAction\" method=\"setupForm\" /> </render-actions> <transition on=\"submit\" to=\"saveDemandDetails\"> <action bean=\"demandAction\" method=\"bindAndValidate\" /> </transition> </view-state> <action-state id=\"saveDemandDetails\"> <action bean=\"demandAction\" method=\"saveToScope\" /> <transition to=\"finish\" /> </action-state> <end-state id=\"finish\" /> </flow> Startovn´ stav je view state demandDetails, pˇed vlastn´ “vyrenderov´n´ ı r ım a ım” JSP str´nky vol´me metodu demandAction.setupForm(), kter´ je zdˇdˇna od Springovsk´ tˇ´ a a a ee e rıdy FormAction. Pokud nenajde ve sv´m flow scopu Form Object, tak zavol´ metodu createFormObject() e a a ta jej uloˇ´ do scope. zı DemandFormAction.java @Override protected Object createFormObject(RequestContext context) throws Exception { DemandFormObject demandFormObject = (DemandFormObject) super.createFormObject(context); BeanUtils.copyProperties(getDemand(context), demandFormObject); demandFormObject.setAllFlatEquipments(enumService.getAllEquipments()); return demandFormObject; } V createFormObject() resp. v setupForm() je ta spr´vn´ chv´ naplnit vˇechny poloˇky aa ıle s z z Form Objectu urˇen´ pro data html prvk˚, ze kter´ch si m˚ˇeme vyb´ z nˇjak´ch hodnot ce u y uz ırat ey z datab´ze (html select, checkbox, radio,...). Vˇechny takov´ poloˇky jsem pro lepˇ´ orientaci a s e z sı oznaˇoval slovy allXXXs. c Dobr´, nap´seme tedy koneˇnˇ JSP str´nku. Tolik vˇc´ co jsme museli kv˚li jedn´ a ıˇ ce a e ı, u e str´nce udˇlat, se n´m zaplat´ v pˇ´ e rozˇiˇov´n´ nebo uprav´ch syst´mu. a e a ı rıpadˇ sr a ı ´ a e NewDemandForm.jsp <form:form commandName=\"demandFormObject\"> <div><label><spring:message code=\"equipment\" />: </label> <div class=\"facilities\"><c:forEach items=\"${demandFormObject.allFlatEquipments}\" 1 Form Object je jin´ n´zev pro Command Object, tj. objekt do/z kter´ho “bindujeme” data v html formu. ya e
    57. KAPITOLA 3. IMPLEMENTACE 43 var=\"equipment\"> <form:checkbox path=\"flatEquipmentSet\" value=\"${equipment}\" />&nbsp;${equipment.name} </c:forEach> </div></div> <%-- and other attributes--%> <input type=\"hidden\" name=\"_flowExecutionKey\" value=\"${flowExecutionKey}\" /> <input type=\"submit\" name=\"_eventId_submit\" value=\"<spring:message code=\"next\" />\" /> </form:form> Vˇimnˇme si prvn´ tagu <form:form/>, ten definuje do/z kter´ho Form Objectu s e ıho e se maj´ data bindovat. Z kolekce allFlatEquipments vybereme pomoc´ html checkboxu data ı ı do flatEquipmentSet. Dalˇ´ vˇc, kterou je tˇeba zm´ sı e r ınit, jsou pole <input/>, kde v prvn´ım pˇed´v´me flowExecutionKey, kter´ urˇuje uˇivatelovu session s flow a button dalˇ´ kter´ vol´ r aa yc z sı, y a event submit, na kter´ reagujeme v demand-details-flow.xml. y DemandFormObject.jsp public class DemandFormObject implements Serializable{ private Set<FlatEquipment> flatEquipmentSet = new HashSet<FlatEquipment>(); private List<FlatEquipment> allFlatEquipments; //and appropriate getters and setters } ’ Vrat me se do demand-details-flow, jdeme vyˇetˇit, co se stane po kliknut´ na button sr ı dalˇ´ resp. po odp´len´ eventu “submit”, vol´me metodu demandAction.bindAndValidate(), sı, aı a kter´ je zdˇdˇn´ od Springovsk´ tˇ´ FormAction. Data z html formul´ˇe se nabinduj´ do a eea e rıdy ar ı Form Objectu a provede se nad nimi validace. Valid´tor pro demandFormAction uˇ jsme beanˇ a z e nainjektovali, uk´ˇeme si jeho implementaci pomoc´ Valangu. az ı Obr´zek 3.3: Validace poloˇky a z Pomoc´ Valangu snadno nap´seme krit´ria pro spr´vn´ hodnoty zad´van´ch poloˇek. ı ıˇ e ae a y z Valang m´ dost vestavˇn´ch funkc´ a oper´tor˚, nic n´m ale v pˇ´ e potˇeby nebr´n´ si nov´ a ey ı au a rıpadˇ r aı e funkce napsat sami. Pro kompletn´ specifikaci jazyka odkazuji ˇten´ˇe na referenˇn´ dokumen- ı c ar cı taci. web-core-validators.xml <bean id=\"demandValidator\" class=\"org.springmodules.validation.valang.ValangValidator\"> <property name=\"valang\"> <value> <![CDATA[ { age : ? BETWEEN 15 AND 99 : ’Size must be between <15,99>’ : ’errors.betweenvalues’ : 15, 99 } ]]> </value> </property> Pokud validovac´ funkce BETWEEN vrac´ true, vˇe probˇhne uspˇˇnˇ. Pokud vr´t´ ı ı s e ´ es e aı false, metoda bindAndValidate() vr´t´ error(), nepˇejde se na dalˇ´ stranu, zobraz´ se chybov´ aı r sı ı a hl´ˇka a ˇek´ se na opravu, jak vid´ as ca ıme na obr´zku 3.3. Chybov´ hl´ˇen´ se hled´ pomoc´ kl´ce a e as ı a ı ıˇ ’errors.betweenvalues’, ten se hled´ ve vˇech souborech .properties, kter´ definujeme v beanˇ a s e e messageSource. core-resources.xml
    58. 44 KAPITOLA 3. IMPLEMENTACE <bean id=\"messageSource\" class=\"org.springframework.context.support. ResourceBundleMessageSource\"> <property name=\"basenames\"> <list> <value>ApplicationResources</value> <value>ErrorMessages</value> </list> </property> <property name=\"useCodeAsDefaultMessage\" value=\"true\" /> </bean> Hl´ˇka m˚ˇe b´t, jak je vidˇt v t´to uk´zce, parametrizov´na, hodnoty se dosazuj´ as uz y e e a a ı pomoc´ sloˇen´ch z´vorek ˇ´ ı zy a cıslovan´ch od nuly. y ErrorMessages.properties errors.betweenvalues = Mus´ b´t od {0} do {1} ıy 3.3.2 Iterace 2 Do druh´ iterace jsem napl´noval zprovoznit n´sleduj´ ı funkcionalitu: e a a ıc´ • Umoˇnit mazat inzer´ty. z a • Umoˇnit editovat inzer´ty. z a Syst´m rozezn´v´ v tuto chv´ 3 typy uˇivatel˚, vˇichni dˇd´ od abstraktn´ tˇ´ e aa ıli z us eı ı rıdy AbstractUser: • Inzerent - uˇivatel inzeruj´ ı nab´ z ıc´ ıdku po bydlen´ ı, • WatchDog - uˇivatel, kter´ si zˇ´ SMS notifikaci, z y rıdil • HomelessInzerent - uˇivatel inzeruj´ ı popt´vku. z ıc´ a Pro jakoukoliv zmˇnu inzer´tu je nutn´ pˇihl´ˇen´ Pro editaci se pos´ a URL /edit.html, e a e r as ı. ıl´ kter´ DispacherServlet deleguje na flowController, jeˇ vol´ edit-flow.xml. V edit-flow.xml se y z a podle typu uˇivatele rozhodne, co chce vlastnˇ editovat. Vytvoˇil jsem tedy subflow edit- z e r demand-flow.xml, kter´ zhruba odpov´ a new-demand-flow.xml, jeˇ jsem podrobnˇ rozebral e ıd´ z e minulou iteraci. Jedin´ rozd´ je, ˇe se nevytv´ˇ´ nov´ Byznys Objecty, ale podle aktu´ln´ y ıl z arı e a ıho uˇivatele se z datab´ze nahraje jeho inzer´t a data se pˇeklop´ do Form Objectu, z kter´ho se z a a r ı e naloaduj´ do JSP str´nky. ı a Pro smaz´n´ inzer´tu je situace jeˇtˇ jasnˇjˇ´ Poˇleme request na /delete.html, Dispa- aı a se e sı. s cherServlet jej deleguje na flowController, kter´ spust´ delete-flow.xml, kde podle typu uˇivatele y ı z spust´ delete-demand-flow.xml. Zobraz´ potvrzovac´ dialog a po potvrzen´ nejdˇ´ podle ıme ıme ı ı rıve uˇivatele naˇteme inzer´t z datab´ze a vz´pˇt´ pomoc´ t´to reference jej smaˇeme. z c a a a eı ıe z 3.3.3 Iterace 3 Zprovoznil jsem vkl´d´n´ editaci a maz´n´ nov´ho inzer´tu. Ohlednˇ popt´vky zb´v´ a a ı, aı e a e a ya naimplementovat uˇ jen tyto uˇivatelsk´ poˇadavky: z z ez • Umoˇnit vkl´dat do inzer´tu v´ lokalit. z a a ıce
    59. KAPITOLA 3. IMPLEMENTACE 45 Obr´zek 3.4: Nov´ inzer´t v´ lokalit a y a ıce • Umoˇnit vyhled´vat v inzer´tech. z a a Zprovozn´ nejdˇ´ prvn´ requirement - vkl´d´n´ v´ lokalit, kde ˇ´st´ lokality m˚ˇe ıme rıve ı a a ı ıce ca ı uz b´t poloˇka “nez´leˇ´ jak vid´ y z a zı”, ıme na obr´zku 3.4. a Jak´ bude reprezentace lokalit? V syst´mu je BO Location oznaˇen´ anotac´ @Em- a e cy ı ˇ sen´ kter´ vyp´l´ “od boku” je, ˇe BO Demand bude obsahovat mnoˇinu lokalit beddable. Reˇ ı, e a ıme z z ˇs ı ve vztahu 1:N. Reˇen´ se zd´ v poˇ´dku, jedin´ fakt, kter´ n´m vad´ je, ˇe Location ne- a ra y ya ı, z vytv´ˇ´ tabulku (viz. kapitola 2.3.4), ˇili pom˚ˇeme si tak, ˇe vytvoˇ´ arı c uz z rıme nov´ BO, ˇeknˇme y r e DemandLocation, kter´ bude oznaˇen anotac´ @Entity. Obˇ reprezentace lokalit spoj´ y c ı e ıme ex- trahov´n´ metod do rozhran´ ILocation. Bohuˇel ˇeˇen´ vyp´len´ “od boku” jsem kv˚li a ım ı z rs ı ae u pozdˇjˇ´ e sımu probl´mu pˇedˇlal a tak si potom uk´ˇeme refaktorov´n´ v praxi. e re az aı Naimplementujeme BO DemandLocation a pˇid´me jej do BO Demand. ra DemandLocation.java @Entity public class DemandLocation extends AbstractBo implements ILocation { private static final long serialVersionUID = -358439507774392169L; private Region region; private District district; private City city; private String street; @ManyToOne public City getCity() {return city;} public void setCity(City city) {this.city = city;} @ManyToOne public District getDistrict() {return district;} public void setDistrict(District district) {this.district = district;} @ManyToOne public Region getRegion() {return region;} public void setRegion(Region region) {this.region = region;} public String getStreet() {return street;} public void setStreet(String street) {this.street = street;} } Property street nen´ pˇ´ ı rımo potˇeba, pokud nechceme spojit obˇ implementace lokalit r e rozhran´ coˇ je pr´vˇ n´ˇ pˇ´ ım, z a e as rıpad. ILocation.java
    60. 46 KAPITOLA 3. IMPLEMENTACE public interface ILocation extends IBO{ public City getCity(); public void setCity(City city); public District getDistrict(); public void setDistrict(District district); public Region getRegion(); public void setRegion(Region region); public String getStreet(); public void setStreet(String street); } Zmˇn´ e ıme tak´ hlaviˇku BO Location, aby implementoval ILocation, d´le pˇid´me do e c a ra BO Demand mnoˇinu DemandLocation. z Demand.java @Entity public class Demand extends AbstractBo { private static final long serialVersionUID = -4713748752937686298L; //... private Set<DemandLocation> locations = new HashSet<DemandLocation>(); @OneToMany(cascade={CascadeType.ALL}) public Set<DemandLocation> getLocations() { return locations;} public void setLocations(Set<DemandLocation> locations) { this.locations=locations;} //... } Servisn´ ani DAO vrstvu zat´ mˇnit nemus´ ı ım e ıme. Pˇid´me dalˇ´ obrazovku do wizardu ra sı vkl´d´n´ nov´ho inzer´tu, jin´mi slovy pˇid´me dalˇ´ subflow do new-demand-flow.xml. aaı e a y ra sı new-demand-flow.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <flow> ... <subflow-state id=\"demandDetails\" flow=\"demand-details-flow\"> <transition to=\"demandLocation\" /> </subflow-state> <subflow-state id=\"demandLocation\" flow=\"demand-location-flow\"> <transition on=\"back\" to=\"demandDetails\" /> <transition on=\"next\" to=\"createUser\" /> </subflow-state> ... </flow> Vytvoˇ´ rıme nov´ flow, kter´ bude zodpov´ e e ıdat za volbu lokalit. Dobr´m zvykem je y vytv´ˇet pro r˚zn´ akce r˚zn´ Form Objecty a Form Action. Proto vytvoˇ´ tak´ DemandLo- ar ue ue rıme e cationFormObject a DemandLocationFormAction, kter´ bude s t´ y ımto form objectem pracovat. demand-location-flow.xml <?xml version=\"1.0\" encoding=\"UTF-8\"?> <flow> <start-state idref=\"demandLocation\" /> <view-state id=\"demandLocation\" view=\"demandLocationForm\"> <render-actions> <action bean=\"demandLocationAction\" method=\"setupForm\" /> </render-actions>
    61. KAPITOLA 3. IMPLEMENTACE 47 <transition on=\"back\" to=\"back\" /> <transition on=\"addLocation\" to=\"addLocation\" /> <transition on=\"submit\" to=\"saveDemandLocation\" /> <transition on=\"delete\" to=\"deleteDemandLocation\"/> </view-state> <action-state id=\"addLocation\"> <action bean=\"demandLocationAction\" method=\"bindAndAdd\" /> <transition to=\"demandLocation\" /> </action-state> <action-state id=\"saveDemandLocation\"> <action bean=\"demandLocationAction\" method=\"storeLocation\" /> <transition to=\"next\" /> </action-state> <action-state id=\"deleteDemandLocation\"> <action bean=\"demandLocationAction\" method=\"deleteLocation\" /> <transition to=\"demandLocation\" /> </action-state> <end-state id=\"back\" /> <end-state id=\"next\" /> </flow> Metoda demandLocationAction.setupForm() respektive metoda createFormObject(), kterou zavol´, “naset´ hodnoty ve form objectu bud’ nov´ nebo uloˇen´ v datab´zi, jelikoˇ toto a ı” e ze a z flow bude vol´no i pˇi editaci. Jak vid´ a r ıme na obr´zku 3.4 m´me zde 4 r˚zn´ akce: a a ue • Akce addLocation pˇid´ zvolenou lokalitu do locationListu. ra • Akce delete odebere lokalitu z locationListu a pˇid´ j´ do deletedLocationListu, protoˇe raı z vˇechny akce se ukl´daj´ aˇ v posledn´ f´zi wizardu vkl´d´n´ s a ız ıa a a ı/editace inzer´tu. List m´ a ısto mnoˇiny je zvolen proto, ˇe v HTTP RequestParameters pˇi akci deleteDemandLocation z z r se pˇed´v´ id poloˇky, kterou chceme smazat, toto id je poˇad´ v listu, nikoliv id z da- r aa z rı tab´ze. a • Akce submit uloˇ´ form object do SWF conversation scope a pˇejde do koncov´ho stavu zı r e “next”. • Akce back jednoduˇe pˇejde do koncov´ho stavu “back”. sr e Spolu s form objectem, form action potˇebujeme JSP str´nku demandLocationForm.jsp. r a Jejich implementace je na pˇiloˇen´m CD. rze Je ˇas zprovoznit druh´ requirement - hled´n´ v inzer´tech, jak vid´ na obr´zku 3.5. c y aı a ıme a Na tuto str´nku se dostaneme po zad´n´ requestu o /hledani-poptavek.html. Jelikoˇ jedin´ a aı z e o co n´m jde, je pomoc´ buttonu “search” zavolat vlastn´ v´bˇr inzer´t˚ a pomoc´ odkazu a ı ı ye au ı “detail” pˇej´ na detail inzer´tu, web flow bude v tomto pˇ´ e kan´nem na vrabce. Zvolil r ıt a rıpadˇ o jsem tedy technologii Spring Web MVC, resp. implementaci pomoc´ BaseCommandController, ı resp. jeho potomka AbstractSearchController, kter´ je v projektu kv˚li hled´n´ v nab´ ach y u aı ıdk´ a MultiActionController pro zobrazen´ detailu inzer´tu. Zaˇneme opˇt od zaˇ´tku, pˇid´me ı a c e ca ra URL mapov´n´ do beany urlMapping. aı action-servlet.xml <bean id=\"urlMapping\" class=\"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\"> <property name=\"alwaysUseFullPath\" value=\"true\" /> <property name=\"mappings\"> <props> <prop key=\"/hledani-poptavek.html\">searchDemandFormController</prop>
    62. 48 KAPITOLA 3. IMPLEMENTACE Obr´zek 3.5: Hled´n´ v popt´vk´ch a aı aa <prop key=\"/spolubydleni-detail-zajemce.html\">demandMultiActionController</prop> </props> </property> </bean> Vytvoˇ´ rıme nejdˇ´ kontrol´r searchDemandFormController. rıve e action-servlet.xml <bean id=\"abstractSearchFormController\" abstract=\"true\"> <property name=\"valueListHandler\" ref=\"valueListHelper\" /> <property name=\"enumService\" ref=\"enumService\" /> <property name=\"propertyEditorRegistrar\" ref=\"enumsEditorRegistrar\" /> </bean> <bean id=\"searchDemandFormController\" class=\"cz.sw.getroommate.web.action.SearchDemandController\" parent=\"abstractSearchFormController\"> <property name=\"formView\" value=\"searchDemand\" /> </bean> Jelikoˇ se na str´nce vyskytuje formul´ˇ, budeme potˇebovat form object, resp. v ter- z a ar r 2 minologii Spring MVC command object. 2 Nen´ to uplnˇ pravda, mapovat data z formul´ˇe m˚ˇeme pˇ´ ı ´e ar uz rımo do BO, avˇak toto ˇeˇen´ se hod´ jen v s rs ı ı
    63. KAPITOLA 3. IMPLEMENTACE 49 SearchDemandCommand.java public class SearchDemandCommand implements Serializable { private static final long serialVersionUID = 3659969246628625452L; private Region region;AbstractSearchController //and appropriate getters/setters } Pro pochopen´ SearchDemandController, resp. jeho pˇedka AbstractSearchController ı r potˇebujeme zn´t jeˇtˇ jeden framework, kter´ jsem v reˇerˇn´ ˇ´sti nepˇedstavil - ValueList. r a se y s s ı ca r Opensource projekt ValueList je implementac´ core j2ee paternu ValueList handler. Slouˇ´ pro ı zı postupn´ naˇ´ an´ hodnot a str´nkuje v´sledky. Nejˇastˇji je pouˇit s datab´z´ a tabulkou, e cıt´ ı a y ce z aı pˇedan´ data mohou b´t ale jak´koliv, napˇ. DynaBean, DynaClass, cokoliv z file syst´mu r a y a r e apod., pokud pro nˇ existuje adapt´r. Kromˇ toho se firma Simple Way s.r.o. pod´ e e e ılela na v´voji, tak nen´ divu, ˇe je pouˇit pr´vˇ ValueList. V´ nalezneme na ofici´ln´ webov´ch y ı z z ae ıce a ıch y str´nk´ch projektu [4]. aa Nap´seme vlastn´ kontrol´r a pod´ ame se na pouˇit´ s ValueListem. ıˇ ı e ıv´ zı SearchDemandController.java public class SearchDemandController extends AbstractSearchController { private static final Logger logger = Logger.getLogger(SearchDemandController.class); public SearchDemandController() { setCommandClass(SearchDemandCommand.class); setCommandName(SearchDemandCommand.COMMAND_NAME); } @Override protected Object formBackingObject(@SuppressWarnings(\"unused\") HttpServletRequest request) throws Exception { SearchDemandCommand searchDemandCommand = (SearchDemandCommand) createCommand(); searchDemandCommand.setAllRegions(EnumService.addEmptyRegion( enumService.getAllRegions())); searchDemandCommand.setRegion(EnumHelper. getDefaultEnum(searchDemandCommand.getAllRegions())); //... return searchDemandCommand; } protected ModelAndView showGrid(HttpServletRequest request, @SuppressWarnings(\"unused\") HttpServletResponse response, Object command, BindException errors) throws Exception { SearchDemandCommand searchCommand = (SearchDemandCommand) command; fillGrid(request, searchCommand); // Trigger rendering of the specified view, using the final model. return new ModelAndView(getFormView(), errors.getModel()); } @SuppressWarnings(\"unchecked\") private void fillGrid(HttpServletRequest request, SearchDemandCommand searchDemandCommand) { ValueListInfo info = valueListHandler.getValueListInfo(request); info.getFilters().put(SearchDemandCommand.COMMAND_NAME, searchDemandCommand); ValueList valueList = valueListHandler. getValueList(\"searchDemandContentProvider\", info); valueListHandler.backupAndSet(request, valueList, \"list\", \"t1\"); } } z´kladn´ pˇ´ a ıch rıpadech.
    64. 50 KAPITOLA 3. IMPLEMENTACE Jelikoˇ je AbstractSearchController potomkem BaseCommandController, hlavn´ me- z ı toda, kter´ se zavol´ po delegov´n´ poˇadavku na kontrol´r, je handleRequestInternal(). Ta a a aı z e ze session vezme command object. • Pokud je null, tak zavol´ metodu showNewForm(), kter´ pˇes getErrorsForNewForm() a ar vol´ metodu formBackingObject(), kterou jsem pˇekryl v SearchDemandController, a r je to obdoba createFormObject() ze SWF, getErrorsForNewForm() pak d´l zavol´ a a bindAndValidate(), zjist´ objekt BindException a uloˇ´ command object do session ı zı scopu a vol´ metodu showGrid(). a • Pokud byl command object nenullov´, tak opˇt zavol´ bindAndValidate(), zjist´ objekt y e a ı BindException a vol´ metodu showGrid(). a N´sleduj´ ı 2 metody jsou nutn´ ke spolupr´ci s ValueListem. Metodu showGrid() jsem a ıc´ e a opˇt pˇekryl, ta vezme command object, zavol´ metodu fillGrid() a vr´t´ novou instanci er a aı ModelAndView, kterou zobraz´ ıme poˇadovan´ v´sledek. Metoda fillGrid() vytvoˇ´ instanci z yy rı ValueListInfo, kter´ drˇ´ vˇechny informace potˇebn´ k vytvoˇen´ vlastn´ ValueListu a vloˇ´ a zı s r e rı ıho zı do mapy Filters command object, vytvoˇ´ ValueList a zavol´ metodu backupAndSet(), kter´ rı a a uloˇ´ ValueList do session, odkud ho na JSP str´nce zobraz´ zı a ıme. Command object do mapy Filters v instanci ValueListInfo vkl´d´me kv˚li dat˚m, kter´ aa u u e m´ ValueList zobrazit. Pokud chceme napˇ´ a rıklad zobrazit jen inzer´ty, kter´ maj´ jako jednu z a e ı poloˇek region, jeˇ hled´me. z z a Vytvoˇ´ content provider, kter´ se kromˇ tohoto bude starat o defaultn´ tˇ´ ıc´ slou- rıme y e ı rıd´ ı pec, defaultn´ smˇr ˇazen´ nebo o poˇet v´sledk˚ na str´nce. ı er ı c y u a action-servlet-valuelist.xml <bean id=\"valueListHelper\" class=\"net.mlw.vlh.web.mvc.ValueListHandlerHelper\"> <property name=\"valueListHandler\" ref=\"valueListHandler\" /> </bean> <bean id=\"valueListHandler\" class=\"net.mlw.vlh.DefaultValueListHandlerImpl\"> <property name=\"config.adapters\"> <map> <entry key=\"searchDemandContentProvider\"> <bean class=\"cz.sw.getroommate.web.action.SearchDemandContentProvider\"> <property name=\"sessionFactory\" ref=\"sessionFactory\"/> <property name=\"defaultNumberPerPage\" value=\"20\" /> <property name=\"defaultSortColumn\" value=\"updated\" /> <property name=\"defaultSortDirection\" value=\"desc\" /> </bean> </entry> </map> </property> </bean> Naimplementujeme jeho z´kladn´ kostru, kter´ bude vracet vˇechny poloˇky BO De- a ı a s z mand z datab´ze pomoc´ Hibernate Criteria API. a ı SearchDemandContentProvider.java public class SearchDemandContentProvider extends AbstractCriteriaContentProvider { protected Criteria getCriteria(ValueListInfo info, Session session) { return session.createCriteria(Demand.class); } }
    65. KAPITOLA 3. IMPLEMENTACE 51 Pˇedek AbstractCriteriaContentProvider zat´ nen´ v ValueList API, oˇividnˇ tam r ım ı c e ale patˇ´ Je zodpovˇdn´ za celou maˇin´rii nahr´n´ dat a nastaven´ ValueListu, vzhledem k rı. ey se aı ı rozsahu t´to pr´ce ho nebudu popisovat. e a Zb´v´ napsat JSP str´nku searchDemand.jsp, kterou jsme nainjektovali do beany sear- ya a chDemandFormController. searchDemand.jsp <title>Seznam popt´vek</title> a <form:form commandName=\"searchDemandCommand\"> <div><label>Region:</label> <form:select path=\"region\" multiple=\"false\" id=\"region\" items=\"${searchDemandCommand.allRegions}\" itemValue=\"id\" itemLabel=\"name\" /> <form:errors path=\"region\" /> </div> <input type=\"submit\" value=\"search\" /> </form:form> <vlh:root value=\"list\" configName=\"microsoftLook\" url=\"?\" includeParameters=\"#\"> <table class=\"microsoftLook\" cellspacing=\"0\" cellpadding=\"0\"> <vlh:row bean=\"demand\"> <vlh:column titleKey=\"Popis\" property=\"description\"> <vlh:attribute name=\"class\" value=\"description\" /> <tags:shorterDescription value=\"${demand.description}\" maxLength=\"100\" /> </vlh:column> <vlh:controls title=\"\"> <vlh:action url=\"spolubydleni-detail-zajemce.html?\"> <vlh:attribute name=\"class\" value=\"action\" /> <vlh:addParam property=\"id\" /> <spring:message code=\"searchForm.detail\" arguments=\"${demand.id}\" /> </vlh:action> </vlh:controls> </vlh:row> </table> <vlh:paging showSummary=\"true\" pages=\"3\" /> </vlh:root> V tagu <form:form/> nen´ nic nov´ho, mapujeme hodnotu z HTML selectu do property ı e region, jak jsem uk´zal v kapitole 3.3.1. a • Tag <vlh:root/> zobrazuje ValueList. • Pomoc´ <vlh:row/> iterujeme jednotliv´ ˇ´dky tabulky z Listu. ı e ra • S <vlh:column/> nadefinujeme sloupce, jejich n´zev a hodnotu z iter´toru, kterou v tomto a a pˇ´ e reprezentuje tag <tags:shorterDescription/>, kter´ jsem pro tento pˇ´ rıpadˇ y rıpad 3 M´ napsal. ısto toho bychom jednoduˇe mohli napsat ${demand.description}. s • Tag <vlh:atribute/> um´ en´ v <vlh:row/> pˇ´ av´ atributy do html <tr/> tagu, ıstˇ y rıd´ a um´ en´ v <vlh:column/> pˇid´v´ atributy do html <td/>. ıstˇ y r aa • Tag <vlh:controls/> je urˇen pro akce nad daty, speci´lnˇ s <vlh:actions/> vytvoˇ´ c ae rıme html odkaz. Ve <vlh:root/> jsme napsali atribut includeParameters=\"#\", t´ jsmeım ˇekli, ˇe vˇechno, co pˇiˇlo ValueListu z requestu, tak´ dosad´ do <vlh:actions/>, ˇili r zs rs e ı c 3 Argumentem pˇed´m property ${demand.description}, kterou podle d´lky bud’ vr´t´ celou, nebo zkr´cenou ra e a ım a na 100 znak˚ plus ..., implementace je v definiˇn´ souboru /WEB-INF/tags/shorterDescription.tag a ve tˇ´ e u c ım rıdˇ TagHelper v metodˇ getShorterDescription(). e
    66. 52 KAPITOLA 3. IMPLEMENTACE m˚ˇeme napsat poˇadavek rovnou uz z <vlh:action url=\"spolubydleni-detail-zajemce.html?\">. • Pomoc´ <vlh:addParam/> pˇid´me id inzer´tu do HTTP ReqestParameters. ı ra a • S <vlh:paging/> nastavujeme poˇet str´nek ze str´nkovac´ menu. c a a ıho Napsali jsme z´kladn´ kostru str´nky, vyhled´v´n´ nen´ zat´ zapojen´, stejnˇ jako a ı a aaı ı ım e e odkaz na detail inzer´tu. Zprovozn´ nejdˇ´ vyhled´v´n´ pˇid´n´ krit´ri´ do tˇ´ a ıme rıve a a ı r a ım eı rıdy SearchDemandContentProvider. Tady je vhodn´ chv´ napsat test, jedn´ se o uˇebnicov´ a ıle a c y pˇ´ rıpad, kdy zad´me nˇjak´ hodnoty do datab´ze a v simulaci hled´n´ chceme vr´tit patˇiˇn´ a ee a aı a rc y poˇet v´sledk˚. Tento test je ve tˇ´ e SearchDemandContentProviderTest na pˇiloˇen´m CD. c y u rıdˇ rze Chtˇli bychom podle zadan´ch lokalit k vyhled´v´n´ vybrat inzer´ty, kter´ je obsahuj´ e y aaı a e ı. Jenomˇe v tomto pˇ´ e s Criteria API to tak jednoduch´ nen´ V´znamnˇ bychom si pomohli, z rıpadˇ e ı. y e kdyby v BO DemandLocation byla reference na Demand a tak obr´tili vlastn´ vztahu. Je to a ıka d˚vod k refaktorov´n´ tak se do nˇho pust´ u a ı, e ıme. Odebereme mnoˇinu lokalit v BO Demand a pˇid´me referenci do BO DemandLocation. z ra DemandLocation.java @Entity public class DemandLocation extends AbstractBo implements ILocation { //... private Demand demand; @ManyToOne(optional = false) public Demand getDemand() {return demand;} public void setDemand(Demand demand) {this.demand = demand;} //... } Vˇude tam, kde jsme pracovali s jedn´ BO, nyn´ mus´ s ım ı ıme pracovat s dvˇma, coˇ nen´ e z ı aˇ tak velk´ reˇie. Horˇ´ je, pokud chceme na JSP k dan´ instanci Demand zobrazit lokality. z az sı e JSP nem´ prostˇedek k vol´n´ metod s parametrem, pouˇ´ ame pro to TagHelper. Napsal a r aı zıv´ jsem tag <tags:location/>, kter´mu pˇed´m instanci Demand, helper zavol´ metodu servisn´ e ra a ı vrstvy, kter´ vr´t´ vˇechny lokality k dan´mu inzer´tu a helper vr´t´ HTML v´stup, kter´ a aı s e a aı y y oˇek´v´me. Tento pˇ´ c aa rıstup je na hranici unosnosti, bohuˇel mˇ nenapadlo lepˇ´ ˇeˇen´ Potom ´ z e sı r s ı. m˚ˇeme pomoc´ Criteria API zapsat uz ı SearchDemandContentProvider.java public class SearchDemandContentProvider extends AbstractCriteriaContentProvider { protected Criteria getCriteria(ValueListInfo info, Session session) { Criteria locationCriteria = session.createCriteria(DemandLocation.class); addId(locationCriteria, \"region.id\", searchDemandCommand.getRegion()); addId(locationCriteria, \"district.id\", searchDemandCommand.getDistrict()); addId(locationCriteria, \"city.id\", searchDemandCommand.getCity()); Criteria criteria = locationCriteria.createCriteria(\"demand\", CriteriaSpecification.INNER_JOIN); if (searchDemandCommand.getMaxPrice() != null) criteria.add(Restrictions.le(\"maxPrice\", searchDemandCommand.getMaxPrice())); locationCriteria.setProjection(Projections.distinct( Projections.property(\"demand\"))); return criteria;
    67. KAPITOLA 3. IMPLEMENTACE 53 } private void addId(Criteria c, String field, IBO bo) { if (bo != null && bo.getId() != EnumService.EMPTY_ID) { if (bo instanceof District || bo instanceof City) { Disjunction disjunction = Restrictions.disjunction(); disjunction.add(Restrictions.eq(field, bo.getId())); disjunction.add(Restrictions.isNull(field)); c.add(disjunction); return; } c.add(Restrictions.eq(field, bo.getId())); } } } Ze vˇech lokalit pomoc´ metody addId() se vyberou jen ty, kter´ uˇivatel hledal; (m˚ˇou s ı ez uz a r rıpady, bud’ je v inzer´tu nˇjak´ ˇ´st lokality a hled´me pr´vˇ tuto ˇ´st, nebo je se st´t tˇi pˇ´ a e a ca a ae ca v inzer´tu nˇjak´ ˇ´st lokality, ale hled´me “nez´leˇ´ ˇili nekladame podm´ a e a ca a a zı”, c ınku, nebo je v inzer´tu “nez´leˇ´ a my hled´me nˇjakou ˇ´st, ˇili bychom mˇli vr´tit i “nez´leˇ´ Z krit´ri´ a a zı” a e ca c e a a zı”). eı pro lokality vytvoˇ´ pˇes referenci na BO Demand krit´ria, kter´ v z´vˇru vrac´ rıme r e a ae ıme. Nastav´ıme krit´ria pro dalˇ´ hledan´ poloˇky, v tomto pˇ´ e sı e z rıkladu je to maxim´ln´ cena. Nakonec vybereme aı z kart´zsk´ho souˇinu projekc´ distinct ze sloupce demand a vrac´ ee c ı ıme popt´vkov´ krit´ria. a a e Zb´v´ uˇ jen naimplementovat detail vyhled´van´ho inzer´tu. Budeme potˇebovat jed- ya z a e a r noduch´ kontrol´r, kter´ vezme id z HTTP RequestParameters, naloaduje z datab´ze inzer´t y e y a a podle tohoto id a nastav´ ho do scopu viditeln´ho z JSP a pˇejde na nˇj. Toho jsem doc´ ı e r e ılil pomoc´ potomka od Springovsk´ho kontrol´ru MultiActionController. Jedn´ se o trivi´ln´ ı e e a aı pˇ´ rıklad a na konec kapitoly se nehod´ ı. 3.3.4 Iterace 4 V minul´ iteraci jsem dokonˇil vˇe k inzerov´n´ popt´vky, do posledn´ iterace jsem e c s aı a ı napl´noval zprovoznit posledn´ uˇivatelsk´ pˇ´ ehy: a ız e rıbˇ • Pˇidat automatick´ maz´n´ inzer´t˚. r e aı au • Pˇidat export do RSS. r s’ a Export do RSS je zajiˇt ov´n pomoc´ opensource knihovny Informa dostupn´ na serveru ı e sourceforge.net. Vlastn´ integrace do syst´mu je zaˇ´ ı e rızena pomoc´ helperu RSSUtil, kter´ abs- ı y trahuje z´kladn´ metody tvorby RSS a umoˇnuje zmˇnit pouˇitou RSS knihovnu (napˇ. ROME) a ı zˇ e z r pomoc´ zmˇny jen tohoto helperu. ı e Cel´ aplikaˇn´ logika je um´ ena v servisn´ tˇ´ e RSSService, kde se vytvoˇ´ nov´ RSS a cı ıstˇ ı rıdˇ rı y feed, do kter´ho z datab´ze pˇijdou inzer´ty, kter´ maj´ b´t v RSS, tento feed se exportuje do e a r a e ıy souboru, dostupn´ho z port´lu. Pro vytv´ˇen´ popisu poloˇky RSS feedu je pouˇit templatovac´ e a ar ı z z ı framework Apache Velocity. Feed se updatuje kaˇd´ 3 hodiny pomoc´ tasku BuildRSSTask na- ze ı pojen´ho na Quarz scheduler. Pˇedstaven´ Apache Velocity se neveˇlo do z´bˇru t´to bakal´ˇsk´ e r ı s ae e ar e pr´ce, z´jemce odkazuji na referenˇn´ dokumentaci nebo patˇiˇnou literaturu. Pr´ci s Quarz a a cı rc a schedulerem uk´ˇu na n´sleduj´ ım pˇ´ az a ıc´ rıkladu. Inzer´ty se po nˇjak´ dobˇ stanou neaktu´ln´ a v syst´mu se zaˇnou postupnˇ hromadit. a ee e aı e c e C´ılem je naimplementovat ˇeˇen´ kter´ po x dnech poˇle uˇivateli email, ˇe by si mˇl inzer´t r s ı, e s z z e a zaktualizovat, nebo bude po y dnech vymaz´n. Pokud ho zaktualizuje, cel´ vˇc se za x dn´ a ae ı opakuje. Kaˇd´ den se spust´ job, kter´ poˇle uˇivatel˚m potvrzovac´ emaily a smaˇe star´ zy ı y s z u ı z e inzer´ty. a
    68. 54 KAPITOLA 3. IMPLEMENTACE s’ Potˇebujeme tedy nˇjak zjiˇt ovat, jestli se m´ poslat email, jestli m´ b´t inzer´t smaz´n r e a ay a a nebo nedˇlat nic. Dobr´m zvykem v OOP je um´ ’ovat metody nejbl´ze k jejich dat˚m, ˇili e y ıst ıˇ u c ide´lnˇ je um´ ae ıstit do BO Offer a Demand reprezentuj´ ı nab´ ıc´ ıdkov´ a popt´vkov´ inzer´t. Tento y a y a ukol je jako stvoˇen´ pro TDD, nejprve tedy nap´seme test. ´ ry ıˇ OfferEraseTest.java public class OfferEraseTest extends TestCase { Calendar calendar; Offer offer; protected void setUp() { calendar = Calendar.getInstance(); } public void testDontDeleteOffersWithoutCancellingMailSent() { offer = new Offer(); offer.setCancellingEmailSent(null); assertFalse(offer.willBeDeletedToday()); } Pro tuto funkci n´m staˇ´ pˇidat jednu promˇnnou typu Date cancellingEmailSent, a cı r e pokud je null, jeˇtˇ nebyl potvrzovac´ email posl´n, pokud nen´ null a ˇasov´ rozd´ mezi touto se ı a ı c y ıl promˇnnou a dneˇkem je vˇtˇ´ neˇ y, bude oznaˇen pro smaz´n´ Tedy pˇid´me dalˇ´ poloˇku e s e sı z c a ı. ra sı z do BO Offer. Prvn´ metoda ˇ´ a, ˇe inzer´t nebude smaz´n, pokud nebyl posl´n email. Metoda ı rık´ z a a a willBeDeletedToday() je um´ ena v BO, ale nechceme aby pro n´ Hibernate hledal property ıstˇ ı k mapov´n´ do datab´ze, oznaˇ´ aı a cıme j´ anotac´ @Transient. ı ı public void testDeleteOffersWithCancellingMailSent() { offer = new Offer(); calendar.roll(Calendar.DAY_OF_YEAR, -CancelAdvertisementTask. MAX_DAYS_OFFER_TO_CANCEL); offer.setCancellingEmailSent(calendar.getTime()); assertTrue(offer.willBeDeletedToday()); } Pokud u inzer´tu byl posl´n potvrzuj´ ı email pr´vˇ pˇed x dny, bude smaz´n. a a ıc´ ae r a public void testNotCancelWithCancellingMailSent() { offer = new Offer(); calendar.roll(Calendar.DAY_OF_YEAR, -CancelAdvertisementTask. MAX_DAYS_OFFER_TO_CANCEL + 1); offer.setCancellingEmailSent(calendar.getTime()); assertFalse(offer.willBeDeletedToday()); } A obr´cenˇ, pokud byl posl´n mail, ale jeˇtˇ neuplynula doba x dn´ smaz´n nebude. ae a se ı, a public void testSentCancellingMailWithCancellingMailNull() { offer = new Offer(); calendar.roll(Calendar.DAY_OF_YEAR, -CancelAdvertisementTask. MAX_DAYS_OFFER_TO_SEND_CANCEL); offer.setUpdated(calendar.getTime()); offer.setCancellingEmailSent(null); assertFalse(offer.willBeDeletedToday()); assertTrue(offer.willBeSentCancellingMail()); } Zaˇali jsme testovat odes´ an´ email˚, pokud byl inzer´t naposledy editov´n pˇed y dny c ıl´ ı u a ar a potvrzovac´ email jeˇtˇ nebyl posl´n, tak se poˇle. ı se a s
    69. KAPITOLA 3. IMPLEMENTACE 55 public void testSentCancellingMailWithCancellingMailNotNull() { offer = new Offer(); offer.setUpdated(new Date()); //or something else offer.setCancellingEmailSent(new Date()); assertFalse(offer.willBeSentCancellingMail()); } Tady ˇ´ ame, ˇe pokud byl uˇ email posl´n, znovu se nepoˇle, podm´ rık´ z z a s ınka v metodˇ e willBeSentCancellingMail() testuje cancellingMailSent na r˚znost od null, jej´ hodnota u ı m˚ˇe b´t jak´koliv datum, v tomto pˇ´ e n´s to nezaj´ a. uz y e rıpadˇ a ım´ public void testDontSendCancelingMail(){ offer = new Offer(); calendar.roll(Calendar.DAY_OF_YEAR, -CancelAdvertisementTask. MAX_DAYS_OFFER_TO_SEND_CANCEL+1); offer.setCancellingEmailSent(null); offer.setUpdated(calendar.getTime()); assertFalse(offer.willBeSentCancellingMail()); } } Posledn´ test ˇ´ a, ˇe pokud email nebyl posl´n a inzer´t byl editov´n pˇed (y - 1) ı rık´ z a a a r dny, email posl´n nebude. Postupn´m zprovozˇov´n´ test˚ jsme naimplementovali tranzientn´ a y n a ım u ı metody v BO Offer. Offer.java @Entity public class Offer extends AbstractBo { //... @Transient public boolean willBeDeletedToday() { if (cancellingEmailSent != null && MultifunctionHelper. getTimeDifferenceBetweenTodayAnd(cancellingEmailSent) >= CancelAdvertisementTask.MAX_DAYS_OFFER_TO_CANCEL) { return true; } return false; } @Transient public boolean willBeSentCancellingMail() { Assert.notNull(updated); if (cancellingEmailSent != null) { return false; } else if (cancellingEmailSent == null && MultifunctionHelper.getTimeDifferenceBetweenTodayAnd(updated) >= CancelAdvertisementTask.MAX_DAYS_OFFER_TO_SEND_CANCEL) { return true; } return false; } } Metoda MultifunctionHelper.getTimeDifferenceBetweenTodayAnd() vrac´ rozd´ ı ıl dn˚ mezi dneˇkem a pˇedan´m argumentem. Instance inzer´tu v´ co se m´ s n´ udˇlat. Zb´v´ u s r y a ı, a ıe ya naimplementovat vlastn´ job, kter´ prov´d´ tyto ukony. Nejprve jej nakonfigurujeme. Pro tyto ı y aı ´ uˇely slouˇ´ Quartz scheduler, st´hl jsem knihovnu, pˇidal do classpath a nakonfiguroval Spring. ´c zı a r core-service.xml
    70. 56 KAPITOLA 3. IMPLEMENTACE <bean name=\"cancelAdJobDetail\" class=\"org.springframework.scheduling.quartz.JobDetailBean\"> <property name=\"jobClass\" value=\"cz.sw.getroommate.tasks.CancelAdvertisementTask\" /> <property name=\"jobDataAsMap\"> <map> <entry key=\"offerService\" value-ref=\"offerService\" /> <entry key=\"demandService\" value-ref=\"demandService\" /> <entry key=\"mimeMailService\" value-ref=\"mailService\" /> <entry key=\"mailTemplate\" value-ref=\"cancellingMail\" /> </map> </property> </bean> Vytvoˇ´ job a nainjektujeme mu properties offer a demandService pro pr´ci s inzer´ty, rıme a a mimeMailService pro odes´ an´ mail˚ a mailTemplate Velocity ˇablonu mailu. ıl´ ı u s <bean id=\"cancelAdCronTrigger\" class=\"org.springframework.scheduling.quartz.CronTriggerBean\"> <property name=\"jobDetail\" ref=\"cancelAdJobDetail\" /> <!-- run every 02:00 --> <property name=\"cronExpression\" value=\"0 0 2 * * ?\" /> </bean> <bean class=\"org.springframework.scheduling.quartz.SchedulerFactoryBean\"> <property name=\"triggers\"> <list> <ref bean=\"cancelAdCronTrigger\" /> </list> </property> </bean> Nastav´ spouˇtˇn´ jobu na kaˇd´ den ve 2:00 a pˇid´me trigger do SchedulerFactory- ıme se ı zy ra Bean. Nakonfigurovali jsme job, zb´v´ ho naimplementovat, zaˇneme opˇt od test˚, uk´ˇeme ya c e u az si zde jen jeho kostru. CancelAdvertisementTask.java public class CancelAdvertisementTask extends QuartzJobBean implements ICronTask { public static final int MAX_DAYS_OFFER_TO_SEND_CANCEL = 3; public static final int MAX_DAYS_OFFER_TO_CANCEL = 3; public static final int MAX_DAYS_DEMAND_TO_SEND_CANCEL = 7; public static final int MAX_DAYS_DEMAND_TO_CANCEL = 3; private OfferService offerService; private DemandService demandService; private MimeMailService mimeMailService; private CancellingEmailTemplate mailTemplate; //apropriate getters and setters @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { doTask(); } public void doTask() { //no code (yet) } } Metoda executeInternal() je protected a tud´z nejde volat jinde neˇ ve tˇ´ e (nebo ıˇ z rıdˇ v potomkovi), potˇebujeme nˇjak simulovat spuˇtˇn´ jobu, proto nedˇl´ nic jin´ho, neˇ ˇe vol´ r e se ı ea e zz a public metodu doTask(), kterou v testu bez obav volat m˚ˇeme. uz
    71. KAPITOLA 3. IMPLEMENTACE 57 Test na tento job nalezneme spolu s jeho implementac´ v pˇiloˇen´m CD ve tˇ´ e ı rze rıdˇ CancellAdTaskOfferTest a CancelAdvertisementTask. Nedˇl´ nic jin´ho, neˇ ˇe nastav´ tes- ea e zz ı tovac´ data a testuje smaz´n´ z datab´ze nebo odes´ an´ email˚. Pro testov´n´ odes´ an´ email˚ ı aı a ıl´ ı u aı ıl´ ı u je pouˇit embedded SMTP server subethamail a emailov´ klient wiser. z y
    72. 58 KAPITOLA 3. IMPLEMENTACE
    73. KAPITOLA 4. ZHODNOCEN´ I 59 4 Zhodnocen´ ı 4.1 Shrnut´ ı V reˇerˇn´ ˇ´sti jsem pˇedstavil stˇˇejn´ technologie pouˇ´ e pˇi v´voji webov´ch apli- s s ı ca r ez ı zıvan´ r y y kac´ na platformˇ Java. Popsat jsem pr´ci se Springem, Hibernatem a webov´mi frameworky ı e a y Spring Web MVC a Spring WebFlow. Popsal jsem metodiku Extr´mn´ programov´n´ a Pro- e ıho aı gramov´n´ ˇ´ e testy. a ı rızen´ V implementaˇn´ ˇ´sti jsem popsal v´voj ˇ´sti aplikace pˇi zprovozˇov´n´ uˇivatelsk´ch c ı ca y ca r n aı z y pˇ´ eh˚. Uk´zal jsem z´kladn´ pracovn´ postupy a n´vyky. Pr´ce sv´m rozsahem nesupluje rıbˇ u a a ı ı a a y patˇiˇn´ dokumentace ani referenˇn´ manu´ly, d˚raz jsem kladl na pouˇit´ vˇech technologi´ v rc e cı a u zı s ı kontextu a vˇe popsal na re´ln´m netrivi´ln´ pˇ´ s ae a ım rıkladu. 4.2 Moˇnosti rozˇ´ren´ z sıˇ ı s’ Naimplementovala se celkem velk´ ˇ´st nov´ funkcionality. Nˇjak´ ˇas se budou zjiˇt ovat a ca e e yc zpˇtn´ ohlasy od uˇivatel˚. Z dalˇ´ funkc´ kter´ na port´lu postr´d´m, je ukl´d´n´ fotek bytu ee z u sıch ı, e a aa aaı a potenci´ln´ z´jemc˚ o bydlen´ jeˇ dokonˇuji v dobˇ psan´ tohoto odstavce. a ıch a u ı, z c e ı 4.3 Osobn´ zkuˇenosti ı s Pevnˇ vˇˇ´ ˇe moje pr´ce m˚ˇe pomoci zaˇ´ ıc´ v´voj´ˇ˚m webov´ch aplikac´ v e erım, z a uz cınaj´ ım y aru y ı Javˇ, ˇe m˚ˇe poslouˇit jako z´kladn´ sezn´men´ a uvod do problematiky. ez uz z a ı a ı´ Spolu s touto prac´ jsem z´ ı ıskal zkuˇenosti i na dalˇ´ projektu ve firmˇ Simple Way s sım e s.r.o., a tak m˚ˇu ˇ´ ˇe tento zp˚sob v´voje aplikac´ je zaj´ uz rıci, z u y ı ımav´, snadno se osvojuje a nebr´n´ y aı pˇem´ˇlet. Pokud jste po pˇeˇten´ moj´ pr´ce z´ r ys rc ı ıa ıskali podobn´ n´zor na vˇc, jsem potˇˇen. ya e es
    74. KAPITOLA 4. ZHODNOCEN´ 60 I
    75. ˇ ˇ ´ KAPITOLA 5. OBSAH PRILOZENEHO CD 61 5 Obsah pˇiloˇen´ho CD rze /getroommate/src - ve sloˇce jsou zdrojov´ k´dy, adres´ˇ je projektem v Eclipse IDE. z eo ar /getroommate/war - webov´ archiv spustiteln´ na AS. y y /getroommate/readme.txt - podrobn´ n´vod pro spuˇtˇn´ aplikace. ya se ı /getroommate/bak.zip - zdrojov´ text t´to pr´ce. y e a
    76. ˇ ˇ ´ 62 KAPITOLA 5. OBSAH PRILOZENEHO CD
    77. KAPITOLA 6. LITERATURA 63 6 Literatura [1] Servlets and jsp pages best practices, 2003. http://java.sun.com/developer/technicalArticles/javaserverpages/. [2] Client-server - wikipedia, the free encyclopedia, 2007. http://en.wikipedia.org/wiki/Client-server. [3] Core j2ee patterns, 2007. http://java.sun.com/blueprints/corej2eepatterns/Patterns. [4] Core j2ee patterns - value list handler, 2007. http://valuelist.sourceforge.net/. [5] Hollywood principle, 2007. http://en.wikipedia.org/wiki/Hollywood_Principle. [6] Java ee 5 apis, 2007. http://java.sun.com/javaee/5/docs/tutorial/doc/Overview9.html. [7] jmock-solid testing, 2007. http://therning.org/niklas/node/3&title=jMock-solid+Testing. [8] Junit 4.4 kladivo na testy, 2007. http://www.sweb.cz/pichlik/archive/2007_08_05_archive.html. [9] Key xp roles, 2007. www.softwarereality.com/lifecycle/xp/extreme_programming_roles.jsp. [10] Manifesto for agile software development, 2007. http://agilemanifesto.org/. [11] Mock object, 2007. http://en.wikipedia.org/wiki/Mock_object. [12] Programming language wars, part one, 2007. http://radar.oreilly.com/archives/2007/03/programming_lan.html. [13] The spring framework - reference documentation, 2007. http://static.springframework.org/spring/docs/2.0.x/reference. [14] Spring web flow reference documentation, 2007. static.springframework.org/spring-webflow/docs/current/reference. [15] Web frameworky v jave, 2007. http://www.sweb.cz/pichlik/archive/2006_12_10_archive.html. [16] K. Beck. Programov´n´ ˇ´ e testy. Grada, 1 edition, 2004. a ı rızen´ [17] G. K. Christian Bauer. Java Persistence with Hibernate (Hibernate in action revised). Apress, 2 edition, 2007. [18] M. Fowler. Refactoring: Improving the Design of Existing Code. 1 edition, 2002. [19] M. S. Mike Keith. Pro EJB3 Java Persistence API. Apress, 1 edition, 2006. [20] D. D. Seth Ladd. Expert Spring MVC and Web Flow. Apress, 1 edition, 2006.
    78. 64 KAPITOLA 6. LITERATURA
    79. ˇ´ KAPITOLA 7. SEZNAM POUZITYCH ZKRATEK 65 7 Seznam pouˇit´ch zkratek zy AOP Aspect Oriented Programming API Application Programming Interface AS Application Server BO Bussiness Object DAO Data Access Object EJB Enterprise Java Beans EL Expression language HQL Hibernate Query Langugage HTML HyperText Markup Language HTTP HyperText Transfer Protocol IoC Inversion of Control JDBC Java Database Connection JMS Java Message Service JMX Java Management Extensions JNDI Java Naming and Directory Interface JSF Java Sever Faces JSTL Java Standard Tag Library JPA Java Persistence API JPQL Java Persistence Query Language ORM Object to relation mapping OOP Object oriented programming POJO Plain Old Java Object RAD Rapid application development SWF Spring WebFlow SWT Standard Widget Toolkit TDD Test-driven development UI User Interface WYSIWYG What You See Is What You Get XML Extensible Markup Language XP eXtream Programming

    + jinxxjinxx, 10 months ago

    custom

    1521 views, 1 favs, 1 embeds more stats

    Bakalarska prace pro FEL CVUT na tema Internetove t more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 1521
      • 1501 on SlideShare
      • 20 from embeds
    • Comments 0
    • Favorites 1
    • Downloads 21
    Most viewed embeds
    • 20 views on http://blog.javaee.cz

    more

    All embeds
    • 20 views on http://blog.javaee.cz

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories