Univerzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod
Využití EJB 3.0 při tvorbě podnikových aplikací Diplomová práce
Autor: Studijní obor:
Bc. Tomáš Olívka Aplikovaná informatika
Vedoucí práce:
Prof. PhDr. RNDr. Antonín Slabý, CSc.
Hradec Králové
listopad 2009
Prohlášení: Prohlašuji, že jsem diplomovou práci zpracoval samostatně a s použitím uvedené literatury. V Hradci Králové dne 15.11. 2009
Tomáš Olívka
Poděkování: Děkuji vedoucímu diplomové práce Prof. PhDr. RNDr. Antonínu Slabému, CSc. za veškerou pomoc a podnětné připomínky při zpracování diplomové práce. Dále děkuji majitelům firmy Datex spol. s.r.o., Ing. Aloisi Nevrtalovi a Ing. Václavu Vyniklářovi za možnost vyvíjet prototyp aplikace SOB v rámci této diplomové práce.
Anotace: Diplomová práce se zabývá možnostmi uplatnění frameworku EJB 3.0 při tvorbě podnikových aplikací a na základě získaných teoretických poznatků vytvoření prototypu reálné podnikové aplikace. Annotation: Title: Making use of EJB 3.0 in development enterprise applications. The thesis focuses on the usage framework EJB 3.0 in development enterprise applications. The thesis aims to set up real world application prototype that would be built on theoretical knowledge of EJB 3.0.
Obsah 1
2
3
4
5
Úvod ..........................................................................................................................3 1.1 Terminologie ......................................................................................................3 1.2 Odkazy...............................................................................................................4 1.3 Schémata a zdrojové kódy ..................................................................................4 Java Enterprise Edition 5, základní pojmy ..................................................................5 2.1 Java EE specifikace ............................................................................................5 2.2 Java EE platforma...............................................................................................6 2.2.1 Java EE kontejner .......................................................................................6 2.2.2 JEE aplikační server....................................................................................6 2.2.3 JEE webový kontejner ................................................................................8 2.2.4 EJB kontejner .............................................................................................8 2.3 Java EE BluePrints ...........................................................................................10 2.3.1 JEE BluePrints Katalog.............................................................................11 2.3.2 JEE Patterns..............................................................................................11 2.3.3 Návrhové vzory ........................................................................................12 2.4 Architektura JEE aplikací .................................................................................14 2.4.1 Vícevrstevná architektura..........................................................................14 2.4.2 Komponentová architektura ......................................................................20 EJB 3.0 ....................................................................................................................24 3.1 Enterprise java beany........................................................................................24 3.2 Metadata - anotace nebo XML? ........................................................................25 3.3 Session beany ...................................................................................................26 3.3.1 Specifikace obchodního rozhraní session beanů ........................................26 3.3.2 Specifikace třídy session beanů.................................................................28 3.3.3 Bezstavové session beany .........................................................................29 3.3.4 Stavové session beany...............................................................................31 3.4 Message-driven beany ......................................................................................35 3.4.1 Kdy použít MDB?.....................................................................................36 3.4.2 Specifikace MDB......................................................................................37 3.5 Dependency inejction .......................................................................................37 3.5.1 Field nebo setter injection? .......................................................................38 3.5.2 Kdy lze použít dependency injection? .......................................................39 3.6 Light-weight AOP koncept – Intereceptory.......................................................41 3.6.1 Kde a jak lze použít interceptory? .............................................................42 3.6.2 Rozdíl mezi interceptory a AOP................................................................43 JPA ..........................................................................................................................44 4.1 Nezávislost JPA na EJB kontejneru ..................................................................44 4.2 Entity................................................................................................................45 4.2.1 Entitní třída...............................................................................................45 4.2.2 Objektově relační mapování......................................................................46 4.2.3 Vazby mezi entitami .................................................................................47 4.3 Práce s třídou EntityManager...........................................................................48 4.4 Nastavení JPA – persistence.xml ......................................................................49 Prototyp aplikace SOB .............................................................................................51 5.1 Popis prototypu SOB ........................................................................................51 5.2 Motivace tvorby prototypu ...............................................................................51 5.3 Architektura prototypu SOB .............................................................................52
1
6 7 8 9
5.3.1 Prezentační vrstva .....................................................................................52 5.3.2 Střední vrstva............................................................................................53 5.3.3 EIS vrstva .................................................................................................53 5.3.4 Pohled nasazení ........................................................................................53 5.4 Řešené problémy ..............................................................................................54 5.4.1 Vývojový nástroj a JEE platforma.............................................................54 5.4.2 ORM reverse engineering .........................................................................54 5.4.3 Integrace EJB a Stripes .............................................................................55 5.4.4 Nasazení protypu do různých prostředí .....................................................59 5.4.5 Efektivní vývoj a testování........................................................................61 5.5 Zhodnocení prototypu SOB ..............................................................................63 5.6 Poučení o manipulaci s prototypem SOB..........................................................64 Závěr........................................................................................................................65 Slovník pojmů..........................................................................................................66 Seznam použité literatury .........................................................................................69 Přílohy .....................................................................................................................72
2
1 Úvod V dnešním světě dynamicky se rozvíjejících informačních systémů a tvrdém konkurenčním boji na trhu výrobců aplikací lze jen těžko obstát bez dobře propracované metodiky, podle které lze efektivně a úspěšně vyvíjet a nasazovat systémy do produkčního prostředí. Požadavky na kvalitu, funkčnost a bezpečnost softwaru se stále zvyšují, a proto je nezbytné dodržovat určité standardy při vývoji aplikace. Pro úspěšné splnění všech požadavků je zcela nezbytné zvolit společně s metodiku nějaký rámec (framework), který nám pomůže zpřehlednit a zefektivnit používání těchto standardů v rámci vývojářských týmu podílejících se na produkci aplikací. Jedním z takovýchto frameworků pro podporu tvorby distribuovaných vícevrstevných aplikací v programovacím jazyce Java je EJB 3.0. Tento framework je součástí specifikace Java Enteprise Edition 5 od společnosti Sun Microsystems a poskytuje standardní prostředky pro budování obchodní logiky vícevrstevných škálovatelných aplikací pomocí tzv. EJB komponent, které jsou nasazovány na aplikační server. Aplikační sever poskytuje EJB komponentám bezpečné prostředí pro jejich provoz. Cílem této diplomové práce je seznámit čtenáře s možnostmi frameworku EJB 3.0. Za tímto účelem byly vytvořeny jednoduché ukázky zdrojových kódů a schémata, které by měly sloužit k lepší orientaci v dané problematice. Ukázky zdrojových kódů v dokumentaci, v tutoriálech nebo v odborné literatuře obvykle ne zcela korespondují s požadavky z praxe, proto bude vytvořen prototyp aplikace na základě reálných požadavků zákazníka. Smyslem tvorby prototypu je snaha ověřit, zdali pomocí popsaných technologií a postupů lze úspěšně vytvořit a nasadit celý systém. Text práce by měl být srozumitelný čtenáři se základními znalostmi objektově orientovaného programování a syntaxe jazyka Java a měl by mu poskytnout nejen přehled o frameworku EJB 3.0, ale především pomoci získat dostatek informací o vhodnosti frameworku pro využití při implementaci konkrétních požadavků na část či celou aplikační logiku.
1.1 Terminologie S přihlédnutím k tomu, že naprostá většina termínů týkajících se oblasti zájmu této práce pochází z angličtiny a je v ustálené podobě používána mezi vývojáři a analytiky na celém světě, není vhodné tyto termíny vždy překládat. Jednak by
3
překlad působil kostrbatě, a také by mohl zcela srozumitelný anglický termín překladem ztratit svoji jednoznačnost. Příkladem může být hned klíčové slovo této práce „bean“, které by překladem do českého ekvivalentu (fazole, bob, zrno, ...) zcela znepřehlednilo text práce a především zmátlo čtenáře, kterému již prošla pod rukama některá odborná publikace o tomto tématu. Ovšem některá slova překladem do češtiny neztrácejí svůj původní význam a působí přirozeněji (např. web container – webový kontejner). Z těchto důvodů se snažíme o nalezení jednoznačného termínu v češtině a při jeho prvním výskytu v textu se uvádí s anglickým ekvivalentem v kulatých závorkách. Český termín pak bude důsledně používán v celé práci, ovšem pokud by se jevil jako zavádějící, bude zvolen anglický originál, který bude skloňován (např. za pomocí beanů).
1.2 Odkazy Veškeré zdroje, ze kterých tato práce čerpá, jsou uvedeny v kapitole 8 (Seznam použité literatury). Pokud je někde v textu uvedena přímá citace, pak je označena hranatými závorkami, v nichž je uvedeno číslo odkazu se zkratkou cit. a za ní číslo stránky1. Například citace z odkazu 6 stránka 17 bude vypadat takto: „[16 cit. 17]“. Většina odkazů použitých v této práci je v anglickém jazyce, a proto se citace uvádějí v českém jazyce a jsou autorským překladem. Uvedení hranatých závorek s číslem odkazu za nějakým pojmem nebo větou neznamená citaci, ale pouze uvádí zdroj, kde je pojem vysvětlen, nebo kde se můžeme zevrubněji s danou problematikou seznámit. Příkladem může být: „... Hibernate [18] ...“, kde odkaz 18 za názvem Hibernate odkazuje na oficiální stránky s dokumentací a referenčním manuálem k produktu Hibernate.
1.3 Schémata a zdrojové kódy V této práci se vyskytuje celá řada schémat a kusů zdrojových kódů, které by měly čtenáři usnadnit pochopení dané problematiky. Ke správné interpretaci některých schémat (především diagramů tříd) se předpokládá alespoň základní znalost UML notace 2.0. Jak v diagramech tříd, tak ve zdrojových kódech se pro zpřehlednění názvu definuje každé rozhraní počátečním písmenem „I“ a až za ním následuje logický název. Například rozhraní Operace se bude definováno jako
IOperace. Podobně je tomu i u abstraktních tříd, které jsou uvozeny
písmenem
„A“. Veškeré názvy tříd, rozhraní, atributů či metod uvedené v běžném textu budou označeny kurzívou stejně tak jako výše zmíněné rozhraní IOperace.
1
V případě odkazu na internetové stránky se číslo stránky neuvádí.
4
2 Java Enterprise Edition 5, základní pojmy Tato kapitola přináší obecný vhled do Java Enterprise Edition 5 společnosti Sun Microsystems (dále jen Java EE, nebo JEE) a vysvětluje základní pojmy, které jsou nezbytné pro další studium EJB 3.0 technologie. V literatuře se také můžeme setkat s pojmem Java 2 Enterprise Edition (J2EE), označuje starší zápis platformy, kde číslo verze bylo určeno třemi znaky (např. J2EE 1.4). Tvůrci specifikace se rozhodli vynechat z názvu specifikace číslo „2“ stejně jako číslo „1“ z čísla verze (revision numer) [7]. Proto dostal nástupce verze 1.4 označení Java EE 5 (JEE 5), což je podle staršího zápisu ekvivalentní s označením Java 2 EE 1.5 (J2EE 1.5). Zdůrazněme, že v textu celé práce budeme obecné označení Java EE (JEE) vztahovat pouze k verzi Java EE 5.
2.1 Java EE specifikace Java EE je specifikace, nikoli konkrétní produkt. Definuje vstupy a výstupy JEE kontejneru bez ohledu na konkrétního výrobce. Je to standard, díky kterému je zajištěna snadná přenositelnost aplikací mezi jednotlivými platformami. Specifikace je tvořena rozsáhlou sadou API. Obrázek 2-1 zakresluje dostupnost API v jednotlivých JEE kontejnerech. Pro stručné seznámení s funkcionalitou jednotlivých API postačí JEE tutorial [1]. Některými API, např. Java persistence (JPA), Java transaction (JTA), Web Services, se budeme podrobněji zabývat v následujících kapitolách této práce.
Schéma 2-1 JEE API, převzato z [1]
5
2.2 Java EE platforma Java EE platforma je široce rozšířená provozní (runtime) platforma pro vývoj, nasazení a běh enterprise aplikací v jazyce Java. Cílem JEE platformy je poskytnout vývojářům rozsáhlou sadu specifikací (APIs), pomocí níž je možné snižovat časovou náročnost vývoje, redukovat složitost a zvyšovat výkon enterprise aplikací [1 cit.1]. Java EE platforma je určena Java EE specifikací a poskytuje vícevrstevný distribuovaný aplikační model. To znamená, že jednotlivé části aplikace mohou běžet na různých zařízeních [10 cit. 6]. Jinými slovy řečeno: JEE platforma implementuje JEE specifikaci. JEE platforem existuje celá řada a jsou uzpůsobeny konkrétním hardwarovým prostředím, či aplikačním nárokům. Příkladem mohou být platformy pro různé operační systémy (Windows, Unix), platformy pro provoz aplikací v clusterech, či platformy pro efektivní vývoj aplikací s nižšími hardwarovými nároky.
2.2.1 Java EE kontejner Pokud tvoříme jednoduché Java třídy v Java SE, potřebujeme na jejich spuštění JVM, což je fakticky kontejner pro běh Java SE aplikací. Pro běh enterprise aplikace také potřebujeme kontejner, který je obecně nazýván Java EE kontejner. V rámci Java EE kontejneru existují 4 kontejnery: EJB, Web, aplikační klient a aplet.
2.2.2 JEE aplikační server JEE aplikační server, nebo také jen JEE server, je konkrétní JEE kontejner, který implementuje JEE specifikaci a je poskytovatelem JEE platformy. To, že je JEE platforma definována JEE specifikací znamená, že může existovat více implementací konkrétní verze JEE specifikace. Pokud má být nějaký aplikační server nazván JEE aplikačním serverem, tak musí bezpodmínečně implementovat celou JEE specifikaci. Díky komponentové architektuře JEE specifikace si může každý výrobce serveru (vendor) implementovat jednotlivá API JEE specifikace buď sám, nebo využít některých existujících řešení. Pokud se budeme striktně držet vývoje aplikace dle JEE specifikace, bude nám prakticky jedno, na jakém JEE aplikačním serveru naši aplikaci nasadíme. Mělo by bez výjimky platit „Write Once, Run anywhere“2 [7].
2
Write Once, Run anywhere (WORA) – jedná se o dlouhodobý strategický záměr firmy Sun Microsystems, jehož cílem je bezproblémová přenositelnost aplikací, popř. aplikačních modulů, mezi jednotlivými poskytovateli JEE platformy[7]
6
Pozorný čtenář by si mohl položit otázku, proč tedy existuje celá řada jak komerčních tak i open-source řešení aplikačních serverů, když, jak bylo psáno v předchozím odstavci, by mělo být možné nasadit naší aplikaci na jakémkoli JEE serveru. Je třeba zdůraznit, že právě díky jednotné specifikaci, jednoznačnému určení co vše má aplikační server poskytovat, musí logicky existovat i určitá omezení. Omezení v tom smyslu, že JEE specifikace nedefinuje ani nemůže definovat veškerou funkcionalitu, která by se mohla hodit při vývoji konkrétní aplikace. Jednotliví výrobci se často liší: 1. Jakým způsobem implementují jednotlivá API JEE specifikace. Například JEE aplikační server JBoss [19] implementuje JPA API (viz. kapitola 4) pomocí ORM frameworku Hibernate [18], narozdíl od JEE aplikačního serveru GlassFish, který implementuje JPA API pomocí TopLink Essentials [17]. Rozdíly v implementaci obvykle mívají za následek různé požadavky serveru na hardware a v konečném důsledku přímo ovlivňují rychlost nasazování a provozu aplikací. 2. Jaké nestandardní služby mimo JEE specifikaci poskytují. Např. aplikační server JBoss využívá pro logování knihovnu Log4j, kterou můžeme
využít
i
při
logování
v naší
aplikaci.
Při
využívání
nadstandardních služeb fakticky snižujeme možnost přenositelnosti aplikace mezi aplikačními servery3 a především zvyšujeme závislost na konkrétní platformě. Pro zvolení vhodného aplikačního serveru musíme zvážit, zdali si při tvorbě aplikace vystačíme s JEE specifikací, nebo bychom rádi využili některých speciálních služeb, které konkrétní server může poskytovat. Dále bychom si měli promyslet, zda některý z open-source produktů vyhovuje našim požadavkům, nebo budeme preferovat komerční řešení. Některé servery jsou vhodnější pro vývoj aplikací na localhostu, jiné jsou uzpůsobeny pro provoz aplikací v rozsáhlém produkčním prostředí (řeší např. clustering a mirroring).
Není zde
prostor pro popisování a porovnávání jednotlivých aplikačních serverů. Pro veškeré ukázky zdrojových kódů v této diplomové práci platí, že jsou platformě nezávislé a mohou být nasazeny a spuštěny na jakémkoli aplikačním serveru implementujícím specifikaci JEE 5.
3
Příklad s knihovnou Log4j je pouze ilustrativní a ve většině případů není problém ji distribuovat s aplikací i do ostatních aplikačních serverů.
7
2.2.3 JEE webový kontejner Nejprve si připomeňme, že JEE webový kontejner (někdy též servlet kontejner) je součástí JEE kontejneru a je tedy dostupný v kterémkoli JEE serveru. Webový kontejner implementuje část JEE specifikace (JSP API a Servlet API), která poskytuje běh webových komponent a úložiště html stránek. Webový kontejner může existovat samostatně, ne jen v rámci JEE kontejneru, a může zajišťovat běh statických, či dynamických webových aplikací. JEE servery jsou velice robustní, poskytují řadu služeb a jsou navrženy pro efektivní běh aplikací, což klade nemalé požadavky na výkon stanice, na které jsou provozovány. Při vývoji a testování GUI webových aplikací dochází k častým úpravám kódu a následně k jejich opětovnému nasazování a spouštění. Neustálé opakování takovéhoto postupu je velice neproduktivní. To bývá jedním z důvodů, proč vývojáři nezřídka volí hardwarově méně náročný samostatný webový kontejner. Jedním z nejrozšířenějších samostatných webových kontejnerů je Apache Tomcat od společnosti The Apache Software Foundation. Apache Tomcat poskytuje i některé služby nad rámec JEE webového kontejneru (např. definice datasource ve verzi 5 a 6) [9] a je třeba zdůraznit, že samostatné webové kontejnery nemusí sloužit pouze k vývoji aplikací. Ve webovém kontejneru lze s dalšími knihovnami (komponentami) aplikace nasadit tzv. light-weight kontejner (např. Spring 2.5), který fakticky existuje jako alternativa (nadstavba) ke službám JEE, včetně IOC kontejneru, což může být dalším důvodem upřednostnění samostatného webového kontejneru před aplikačním serverem. Na samostatném webovém kontejneru ovšem nelze nasadit a spustit EJB komponenty4, a proto pro potřeby vývoje a nasazování EJB komponent budeme muset zvolit nějaký JEE server.
2.2.4 EJB kontejner EJB kontejner slouží k běhu EJB beanů. Nejdůležitější zodpovědností kontejneru je zajistit bezpečné transakční distribuované prostředí pro provoz beanů [5 cit. 59]. Kontejner implementuje vlastní verzi IOC, díky němuž je možné volat naše beany z klientů, nebo jiných beanů bez přímého použití kontejnerového API prostřednictvím anotací nebo xml descriptoru. Takto můžeme kontejneru definovat, jaké EJB beany chceme v kódu použít a kontejner nám za běhu zajistí 4
Existují i určité možnosti, jak nasadit EJB komponenty mimo JEE server (např. na webovém kontejneru). Může se jednat např. o plug-in OpenEJB pro Tomcat, nebo prostřednictvím Springu.
8
jejich instance. Kontejner se v podstatě chová jako neviditelný prostředník mezi klientem a beanem a zajišťuje životní cyklus beanů. V následujícím výčtu je definováno několik základních služeb, které jsou díky kontejneru k dispozici.
Řízení transakcí: Transakcí rozumíme jednotku práce (např. sled volaných metod), která musí být vykonána v kuse bez přerušení a ve zvoleném pořadí. EJB framework poskytuje dva typy transakcí: Kontejnerem řízené transakce, kdy kontejner zajistí start a commit (popř. rollback, když se vyskytne chyba) transakce. Beanem řízené transakce umožňující vykonávat persistentní operace definované pomocí atributů (anotací) spjatých s našimi EJB beany, kdy má vývojář stoprocentní kontrolu nad průběhem transakce. Této kontroly může být dosaženo z klienta nebo přímo z EJB beanu.
Bezpečnost: je jedna z nejdůležitějších výhod EJB frameworku a hlavním důvodem pro využití vícevrstevného aplikačního modelu. Správa rolí a oprávnění pro volání metod je možné řešit již v Java SE programováním oproti Java security API. EJB framework značně zjednodušuje
definici
oprávnění
pro metody
EJB
beanů
prostřednictvím nastavení atributů (anotace nebo xml).
Životní cyklus a zdroje: EJB kontejner řídí zdroje (např. vlákna či databázová spojení), které využívají EJB beany a řídí životní cyklus beanů. Vytváří a likviduje instance beanů, pasivuje je prostřednictvím serializace do sekundární paměti a aktivuje je zpět. Kontejner má schopnost znovu použít instance beanů tak, jak uzná za vhodné, a dokáže automaticky zvyšovat či snižovat počet dostupných instancí beanů, díky čemuž se značně zefektivňuje práce s pamětí a snižují hardwarové nároky na PC, na kterém běží JEE server.
Vzdálený přístup: EJB framework poskytuje vývojářům velice snadné definování EJB beanů, jejichž metody se mají volat z klientů běžících ve vzdálených JVM. Kontejner zajistí transformaci lokálních EJB beanů do tzv. distribuovaných síťových objektů sloužících pro přenos dat mezi JEE serverem a vzdáleným klientem. Metody EJB beanů je možné volat i z jiných programovacích jazyků.
Podpora pro souběžné požadavky (requesty): Kontejner se postará o obsluhu souběžně volaných EJB beanů z klienta, aniž bychom museli psát více vláknový obslužný kód a zajišťuje podporu řízení vláken. 9
Pokud více klientů volá metody instance beanu, kontejner zeserializuje requesty a díky tomu dovoluje pouze jednomu klientovi zavolat instaci beanu v jednom okamžiku. Pokud k souběhu volání dojde, ostatní klienti jsou nejprve přesměrováni na jiné instance beanu, nebo jsou přinuceni počkat, dokud se původní instance stane opět dostupnou. Vývojáři tak ušetří spoustu času, který by museli věnovat psaní vláknově bezpečného kódu.
Clustering a vyrovnávání zátěže (load-balancing): Clusterem máme na mysli paralelní práci více výpočetních jednotek pracujících společně za určitým cílem. V JEE je clustering obecně práce nad clusterem a poskytuje dvě zásadní služby: škálovatelnost (scalability) - možnost pružně přidávat resp. odebírat výpočetní stanice do resp. z clusteru a tak zásadním způsobem ovlivňovat celkovou výkonnost clusteru a vysokou dostupnost (high availability), zachovat dostupnost clusteru při výpadku některé ze stanic clusteru a stávající požadavky rovnoměrné rozdělit mezi zbylé funkční stanice. Z definice specifikace není EJB kontejner povinen podporovat clustering a vyrovnávání zátěže, ovšem téměř všichni výrobci kontejnerů více méně tyto služby podporují 5. Je potřeba vzít v úvahu, že se jedná o tzv. nestandardní (viz. 2.2.2) služby a jejich konfigurace může být kontejner od kontejneru odlišná. Na druhé straně je třeba si uvědomit, že výhody, které přinášejí tyto služby při řešení obsluhy mnoha souběžných requestů či při provádění náročných algoritmů, jsou dosti vysoké a změna EJB kontejneru resp. aplikačního serveru nebývá tak častá.
2.3 Java EE BluePrints Společně se JEE specifikací jsou vyvíjeny a dokumentovány tzv. BluePrints, které vývojářům poskytují pravidla, vzory a zdrojové kódy pro vývoj, ladění a nasazování JEE aplikací [8]. Motivací vzniku BluePrints byly především neustále se opakující otázky vývojářů a analytiků, jakým způsobem řešit řadu problémů při vývoji aplikací. Ukázalo se, že samotný tutoriál[1] s jednoduchými ukázkami kódu je jako teoretický základ pro produkci složitějších aplikací nedostačující. BluePrints
obsahují
BluePrints
Solution
Katalog[11],
J2EE
Patterns
Catalog[12] i řadu dalších kompletů. Jedním z nich je Enterprise definující 5
některé JEE servery podporují clustering pouze nad celou JEE aplikací (ear), nikoli nad samostatným EJB modulem
10
modelové řešení, které je postaveno nad Java SE a Java EE platformou. Seskupuje souhrn pravidel (pokynů), návrhových vzorů a příkladů řešení reálných problémů ve zdrojových kódech, které umožňují tvorbu robustních škálovatelných přenosných aplikací. Dalším kompletem BluePrints je Performance zabývající se problematikou výkonnosti a škálovatelnosti aplikací. Nabízí celou řadu strategií, jak upravit a vylepšit výkonnost buď nově vznikající či již existující aplikace. [8] Kromě výše zmíněných kompletů existuje mnoho dalších sestav, avšak cílem této práce není kompletně vyjmenovat všechny kategorie BluePrints. Měli bychom mít na paměti, že při analytické fázi je vhodné pokusit se vyhledat již existující řešení. Nemělo by být cílem analytika vymyslet vždy originální řešení daného problému. Není nutné objevovat již objevené. Literatura [8] je právě tím místem, kde by mělo začínat hledání, jak řešit konkrétní problémy JEE.
2.3.1 JEE BluePrints Katalog BluePrints katalog se dělí do čtyř logických kategorií6. Každá kategorie katalogu je rozdělena do klíčových otázek (problémů) a nabízí různé strategie, vzory a pokyny pro jejich řešení. V následujícím výčtu jsou stručně komentovány jednotlivé kategorie. [11]
AJAX – podporuje způsob návrhu a tvorby JEE aplikací využívajících technologii AJAX.
Web tear – prezentuje řadu řešení pro vývoj webové vrstvy JEE aplikací s použitím JEE technologií JSF, Servlets, JSPa JSTL.
Business logic – soustřeďuje se na vrstvu JEE aplikace, která obsahuje obchodní a integrační logiku. Technologie použité v této vrstvě jsou především EJB, JSM, XML, JDBC, JEE Conectors.
Web services – řeší problematiku webových služeb a servisně orientované architektury (SOA) aplikací.
2.3.2 JEE Patterns Součástí BluePrints je katalog návrhových vzorů [12] a nově „Core JEE paterns“[13] řešící řadu typických programátorských problémů. Je potřeba zmínit, že návrhové vzory existují v obecné podobě [14] a je možné je implementovat prakticky do všech objektově orientovaných jazyků.
6
V době sepisování této práce existuje kompletní katalog pouze pro starší verzi J2EE 1.4 a pro verzi JEE 5 zatím není kompletní.
11
2.3.3 Návrhové vzory Návrhové vzory obsažené v Java EE Bluepritns jsou už konkrétní ukázkou implementace některých návrhových vzorů za pomocí Java 5 a JEE 5, využívajících nových vlastností jazyka Java verze 5 jakou jsou například generika a anotace. V následujícím výčtu si vyjmenujeme a stručně popíšeme několik obecných návrhových vzorů, jejichž konkrétní aplikace se vyskytuje v prototypu aplikace SOB (kap. 5) a které by měl bezpodmínečně znát každý vývojář či softwarový architekt pracující s objektově orientovaným programovacím jazykem. Všechny níže uvedené vzory můžeme nalézt v [14]. 2.3.3.1 Singleton (jedináček) Jedná se o konstrukci, která zajistí, aby v celé aplikaci existovala pouze jediná instance dané třídy. K této instanci se přistupuje pomocí statické metody a samotný konstruktor jedináčka je privátní (tzn. nelze vytvořit instanci jedináčka voláním konstruktoru z jiných tříd).
Schéma 2-2 Model View Controler
2.3.3.2 Model View Controler MVC Dnes už standardní přístup (princip) oddělení datové vrstvy a aplikační logiky (model) od vrstvy, která zpracovává požadavky klienta na aplikaci (controler) a od vrstvy starající se o prezentaci (zobrazení) dat na klientovi (view). V Javě existuje celá řada MVC frameworků, které podporují a usnadňují vývoj JEE aplikací podle principů vzoru MVC. Součástí JEE je komponentově orientovaný MVC framework JSF. Z ostatních můžeme uvést např. Struts, Struts2, Webwork, Spring MVC, Stripes, Tapestry... . Na schématu 2-2 je naznačen způsob komunikace mezi jednotlivými vrstvami a ostatními systémy. U větších systémů může být více než 12
jenom tyto tři vrstvy popsané vzorem MVC (viz. kapitola 2.4 Architektura JEE aplikací). 2.3.3.3 Data Access Object DAO Smyslem tohoto vzoru je oddělení vrstvy přístupu k datům od vrstvy aplikační logiky. DAO se programuje striktně proti rozhraní a implementace DAO rozhraní zajišťuje přístup k datům konkrétním způsobem (schéma. 2-3). V aplikaci můžeme využít více implementací konkrétního DAO rozhraní podle toho, jaký přístup nám bude pro konkrétní získávání více vyhovovat. Např. JPA implementace se bude hodit pro snadné získání a manipulaci s namapovaným objektem. Oproti tomu může být v některých případech efektivnější využít implementaci např. pomocí JDBC (složité dotazy přes více tabulek – nutnost optimalizace a efektivnosti dotazů). Snadné přepínání jednotlivých implementací tohoto vzoru můžeme zajistit pomocí AbstractFactory.
Schéma 2-3 implementace DAO tříd, jejichž účelem je perzistence třídy faktura
2.3.3.4 Facade Pokud se některá část systému či komponenta stává příliš „složitou“ a komunikace s ostatními subsystémy či komponentami se s přibývající složitostí čím dál více znepřehledňuje, pak přichází prostor pro využití návrhového vzoru Facade, který doporučuje nahrazení početné sady veřejných metod rozmístěných v mnoha třídách subsystému jedním rozhraním, které bude vystaveno jako „fasáda“ pro komunikaci s ostatními subsystémy. Celá problematika je velice dobře zřetelná ze schématu 2-4.
13
Schéma 2-4 Naznačení způsobu komunikace Subsystému 1 s ostatními subsystémy prostřednictvím návrhového vzoru Facade
2.4 Architektura JEE aplikací V této podkapitole se budeme věnovat dvou základním konceptům JEE aplikační architektury. Nejprve si popíšeme
vícevrstevnou architekturu a její
nejčastěji definované vrstvy a v druhé časti nastíníme problematiku komponentové architektury. Je třeba zdůraznit, že oba koncepty architektur nejdou proti sobě, ale vzájemně se doplňují. Uplatňování obou konceptů patří k tzv. „best practice” při vývoji JEE aplikací.
2.4.1 Vícevrstevná architektura JEE platforma zajišťuje podporu vývoje serverové (server-side) a klientské (client-side)
části distribuovaných vícevrstevných aplikací. Takovéto aplikace
bývají zpravidla sestaveny ze tří základních vrstev (tier7) definovaných JEE (schéma 2-5): 1. klientská vrstva (client-tier): poskytuje uživatelské rozhraní
a
podporuje mnoho typů klientů (mobilní zařízení, síťová zařízení, PC...) 2. střední vrstva (middle-tier): střední vrstva je tvořena jedním nebo více moduly (může obsahovat více pod-vrstev – sub-tier) a zajišťuje klientské služby (client services) prostřednictvím webového kontejneru (prezentační vrstva) a služby obchodní logiky aplikace díky EJB kontejneru (vrstva obchodní logiky). 7
v literatuře se můžeme setkat i s pojmem layer
14
3. EIS vrstva (EIS tear popř. back-end tear): bývá tvořena zpravidla databázovým systémem a je přístupná standardními JEE API.
Schéma 2-5 tří základní vrstvy v JEE aplikacích - převzato z [10]
JEE vrstvy aplikace zobrazené na schématu 2-5 se většinou ještě dělí na pod-vrstvy, které mohou prostupovat napříč třemi základními vrstvami. Smyslem rozdělení aplikace do vrstev je především při high-level pohledu logické oddělení jednotlivých funkcionalit od sebe a udržení si představy o tom, co má jaká vrstva resp. pod-vrstva v aplikaci na starosti. Počet vrstev aplikace vždy záleží na její velikosti a členitosti - jaké služby poskytuje a pomocí jakých prostředků dosahuje požadovanou funkcionalitu. Schéma 2-6 znázorňuje nejčastěji používané vrstvy u středně velkých JEE systémů. Nižší vrstva (na schématu zprava) by měla být vždy nezávislá na vyšší vrstvě, která se nalézá vlevo od nižší vrstvy. Například vrstva obchodní logiky, v tomto případě nižší vrstva, by nikdy neměla být závislá na vyšší prezentační vrstvě. To znamená, že jakákoliv změna prezentační logiky neovlivní funkčnost vrstvy obchodní logiky. Opačně tento princip samozřejmě neplatí ani platit nemůže, protože vyšší vrstvy jsou závislé na vrstvách nižších. Pokud nastane v nižší vrstvě chyba, pak se tato chyba promítne do všech vyšších vrstev. Nižší vrstva by nikdy neměla volat metody vyšší vrstvy, jinak by došlo k porušení závislosti mezi nižší a vyšší vrstvou. Na schématu 2-6 je naznačeno volání jednotlivých vrstev resp. závislost. Šipka vychází vždy ze závislé vrstvy (pozn. přerušovaná šipka značí pouze odpověď, nikoli závislost).
15
Schéma 2-6 naznačuje závislost jednotlivých vrstev. Zprava plně nezávislá EIS vrstva.
2.4.1.1 Prezentační vrstva (view tear) Prezentační vrstva se stará o zpřehlednění a usnadnění komunikace klienta se systémem prostřednictvím graficky uživatelského rozhraní (GUI). Presentační vrstva se obvykle skládá z webové a klientské vrstvy. To odpovídá fyzickému rozdělení do dvou částí. Serverový front-end controler (dále jen kontroler), který je umístěn na serveru a aplikační klient (internetový prohlížeč, dále jen klient), který je umístěn u uživatele systému. Prezentační vrstva prostupuje klientskou a střední JEE vrstvou.
Kontroler přijímá a validuje požadavky od klienta. Na základě těchto požadavků získává data z vrstvy obchodní logiky, generuje je do formátu (x)html nebo xml a posílá je prostřednictvím http protokolu klientovi. Kontroler bývá zpravidla řešen nějakým MVC frameworkem (viz. kap. 2.3.3 MVC)
Klient posílá požadavky na server prostřednictvím http nebo https protokolu metodou get, nebo post. Klient je z principu tzv. tenký klient, protože na něm neběží žádná obchodní logika, stará se pouze o zobrazení (x)html nebo xml dat a může validovat požadavky prostřednictvím skriptu (Java script, VB scrtipt).
2.4.1.2 Vrstva Obchodní logiky (business tear) Vrstva obchodní logiky může být tvořena za pomoci různých frameworků či knihoven, nebo jen s Java SE, ale my se v této práci budeme zabývat výhradně
16
tvorbou obchodních služeb pomocí EJB frameworku a JPA API. Takto definovaná vrstva obchodní logiky se v JEE aplikacích dělí do dvou částí: doménový model (domain model) a služby obchodní logiky (services). Doménový model je reprezentován entitami8, které nesou data o svém stavu. Jinými slovy doménový model reprezentuje stav systému, který je ukládán resp. načítán vrstvou přístupu k datům do EIS vrstvy. Entity odpovídají specifikaci POJO a jsou definovány JPA API – obsahují anotace nad atributy resp. metodami definující jejich názvy ve zdroji dat a jejich omezení (podrobně popsáno v kapitole 4 JPA). Služby obchodní logiky jsou JavaBean komponenty (EJB beany9) zajišťující požadovanou funkcionalitu. Vývojáři se mohou při využití EJB beanů skutečně soustředit na implementaci obchodní logiky, zatímco EJB kontejner se postará o veškerou podporu (viz. kap. 2.4.4) nutnou pro běh kódu. EJB beany musí z definice obsahovat rozhraní a implementaci (viz kapitola 3 EJB 3.0). Striktním přístupem programování proti rozhraní jsou nejprve vydefinovány funkce, které má jednotlivá služba poskytovat, a až následně vytvořena implementace dané služby (implementací může být více). Výhodou takového postupu je snadná změna implementace obchodní logiky, aniž by se muselo zasahovat do jiných tříd náležících do různých vrstev. Při návrhu vrstvy obchodní logiky je možno držet se objektového paradigmatu rich domain model velice dobře popsaného v [16], nebo využít tzv. „anti-vzor“ (antipatern) anemic domain model. Každý z přístupů nese určité výhody a nechá se použít při tvorbě vrstvy obchodní logiky s využitím frameworku EJB [4]. S přihlédnutím k tomu, že hlavním cílem EJB frameworku je podpora produkce rozsáhlých podnikových aplikací vyvíjených desítkami programátorů, pokusíme se v následujícím textu nastínit některé možné komplikace při využití domain modelu a proč je stále, nejen v JEE ale obecně, při tvorbě rozsáhlých systémů v OOP je jednou z „best practice“ využívání anemic domain modelu. Anemic domain model je nazýván anti-vzorem [15], protože jde proti standardnímu objektově orientovanému pohledu (domain model), kdy každá doménová třída nese nejen data ale i zodpovědnost za určité činnosti aplikace poskytuje služby (obchodní logiku v metodách). V rozsáhlých aplikacích se domain model problematicky uplatňuje především z těchto důvodů: 8
Ve starší verzi EJB existovaly entity beany, které lze sice v EJB 3.0 použít, ale již nejsou dál vyvíjeny. Místo toho existují samostatné entity definované JPA, které lze využít i bez použití EJB frameworku. 9 Podrobněji o různých typech EJB beanů v kapitole 3.
17
Pokud je v doménové třídě příliš mnoho metod zajišťujících obchodní logiku, nebo jsou tyto metody rozsáhlé, stává se doménová třída nepřehlednou a obtížně udržovanou. Při použití anemic domain modelu se tato problematika řeší tvorbou více služeb.
V některých případech bývá velice obtížné určit, která z doménových tříd má zajišťovat konkrétní obchodní službu. Jedná se zejména o případy, kde existuje požadavek na zpracování vícedoménových objektů. Tento problém se týká obecně delegování zodpovědnosti za transakce, které probíhají napříč doménovým modelem.10
Jednotlivé služby doménové třídy mohou obsahovat celou řadu metod, které k dané doméně logicky patří, ale každá metoda může využívat zcela jiných prostředků (knihoven). Z tohoto důvodu je u rozsáhlejších systémů za jejich tvorbu zodpovědný právě vývojář specializující se na konkrétní typ prostředků. Sestavování a udržování třídy, jejíž metody programuje více vývojářů, se může značně komplikovat. Jako příklad takového problému můžeme uvést skupinu metod využívajících složitějšího matematického aparátu a jednoduché validace. Obě skupiny metod do doménové třídy logicky patří, ale není efektivní zatěžovat zkušeného vývojáře (senior developer) tvorbou jednoduchých validací.
Při využívání EJB entitních tříd pro datové objekty nelze bezezbytku uplatnit koncepci domain modelu, a to především z důvodu, že entitní třídy musejí splňovat specifikaci POJO. V některých třídách rich domain modelu nemusí být žádoucí uveřejňovat např. nějakou set metodu, či doménová třída s veřejným prázdným konstruktorem nemusí splňovat existenční podmínky instance objektu. V takových případech je o poznání těžší „namapovat“ entity na databázovou strukturu.
Tvorba testů jednotlivých metod obchodní logiky pro složitější domain model
oproti
testování
jednotlivých
metod
služeb
může
být
komplikovanější. Nutno podotknout, že i anemic domain model nese určité nevýhody, a to především skutečnost, že i když služby obchodní logiky pracují s poměrně složitým objektovým datovým modelem, jejich tvorba se značně přibližuje procedurálnímu 10
Domain model samozřejmě může obsahovat i služby obchodní logiky definující jednotlivé transakce, rozdíl oproti anemic domain modelu je v tom, že služby domain modelu by měly poskytovat pouze fasádu (návrhový vzor Facade viz. kap. 2.3.3) tj. volat pouze metody doménových tříd, tedy nezpracovávat žádná data.
18
programování a může tak docházet k částečné duplicitě kódu jednotlivých služeb. Je vždy na analytikovi, jakou strategii zvolí, a je třeba zdůraznit, že finální rozhodnutí by mělo vždy přinášet více výhod. Výhodou anemic domain modelu je především snadnější udržovatelnost projektu při práci ve větším počtu vývojářů a snadná změna implementace služeb. Oproti tomu výhoda domain modelu je ve stoprocentním využití OOP, a tím minimalizace duplicitního kódu. 2.4.1.3 Vrstva přístupu k datům (data acess tear11) Vrstva přístupu k datům se stará o persistenci datového modelu vrstvy obchodní logiky. Je tvořena DAO objekty (kap 2.3.3) a měla by být jediným místem v aplikaci, odkud je možný přístup do EIS vrstvy. Při použití EJB frameworku jsou DAO objekty EJB beany stejně jako služby obchodní logiky. Každý DAO objekt má na starosti persistenci jedné konkrétní doménové třídy. Většinou zajišťuje základní CRUD operace, a pokud je třeba, tak mohou být vyspecifikovány další metody zohledňující speciální potřeby aplikace na persistenci dané entity. Tato vrstva bývá implementována s využitím některého z ORM frameworků odstiňujícího DAO objekty od implementace konkrétního datového zdroje. ORM frameworky mají vestavěný dotazovací jazyk (obecně OQL), který umožňuje pracovat s datovým zdrojem tak, jako by se jednalo o objektovou databázi a způsob dotazování je ovlivněn pouze doménovým modelem, nikoli datovým zdrojem. OQL dotazy jsou ORM frameworkem převedeny na dotazy odpovídající způsobu dotazování dle využitého datového zdroje. Například pokud budeme využívat jako datový zdroj relační databázi Oracle, tak námi psané objektové dotazy v OQL budou transformovány do podoby SQL s příslušným dialektem databázové verze. V dnešní době existuje celá řada ORM frameworků, které se od sebe liší jak způsobem dotazování, tak způsobem mapování doménových tříd. V JEE existuje pro ORM standard JPA API, což je specifikace funkcí, které každý JEE ORM framework poskytuje. JPA taktéž definuje dotazovací jazyk JPQL a způsob mapování EJB entit na datový zdroj. JPA nás tedy odstiňuje nejen od konkrétního datového zdroje, ale také od ORM frameworku, a z toho důvodu nám bude při tvorbě datové vrstvy prostřednictvím JPA API jedno, na jakém JEE serveru bude naše aplikace nasazena a jaký typ datového zdroje bude pro produkční verzi vyvíjené aplikace zvolen.
11
nebo také persistence tear viz. [4]
19
2.4.1.4 EIS vrstva EIS vrstva slouží jako zdroj dat pro enterprise aplikace. EIS vrstva obsahuje jeden nebo více systémů jako jsou například relační databáze, mainframe systémy, objektové databáze, FTP file systémy aj. pro trvalé ukládání dat. Vrstva obchodní logiky zpravidla pracuje v jednom okamžiku jenom se zlomkem dat a jejich stav se průběžně načítá resp. ukládá do EIS vrstvy. JEE platforma poskytuje přístupy do různých typů EIS technologií:
JEE Konektorová architektura (Connector architecture) je normou pro integraci JEE aplikací s existujícími EIS systémy využívající adaptéry (resource adapters12), které mohou být umístěny do JEE serverů .
Zprávové služby (JMS API) definují způsob asynchronního zasílání zpráv mezi JEE zprávovými systémy.
JDBC API slouží pro spojení JEE aplikací s relačními databázemi. Umožňuje otevírání a uzavírání nových spojení s databází, volání SQL dotazů, spouštění databázových funkcí, startování transakcí, zapsání (commit) popř. vyrolování (rollback) transakcí.
JEE platforma umožňuje jak synchronní tak asynchronní integraci pro přístup JEE aplikace k EIS vrstvě. Asynchronní
systémy jsou vhodnější pro kvalitní a
spolehlivé vyměňování zpráv mezi systémy a zpravidla jsou i velice výkonným řešením. Oproti tomu synchronní systémy jsou jen těžko nahraditelné všude tam, kde je nutný přístup aplikace do více datových zdrojů a požadované zpracování musí proběhnout v přesném pořadí v rámci jedné transakce.
2.4.2 Komponentová architektura EJB framework je výhradně komponentovým frameworkem, což znamená, že jeho základními stavebními prvky jsou komponenty. Návrh a implementace systému za pomocí komponent je v pořadí třetí „best practice“ dle metodiky RUP [21]. Zásadním přínosem komponentového systému je možnost snadnějšího a tedy i levnějšího rozšíření stávajícího systému, či nahrazení implementace stávajících komponent. Komponentová architektura se dělí do tzv. zásuvných modulů (plugin), které sdružují komponenty vykonávající určitou specifickou činnost. Klíčovými pojmy komponentové architektury jsou komponenta a rozhraní (API), jejichž význam a funkce si popíšeme v následujících podkapitolách.
12
Jedná se o technologii zásuvných modulů umožňující JEE serveru připojit se na různé druhy EIS systémů.
20
2.4.2.1 Rozhraní Rozhraní odděluje specifikaci od implementace. V jazyce Java se definuje klíčovými slovy public interface a jeho tělo je tvořeno pouze veřejnými (public) hlavičkami metod13. Hlavičky metod specifikují název metody a popřípadě její vstupní parametry a návratovou hodnou. Rozhraní nemůže obsahovat výkonná těla metod a ani žádné atributy. Veškerá logika (prostředky, jimiž se dosáhne požadovaného úkolu) je ponechána třídě, která implementuje dané rozhraní. Rozhraní naopak může obsahovat libovolný počet konstant a může dědit (vazba extend) z jiného rozhraní. Při tvorbě rozhraní se snažíme v systému nalézt metody, které se vyskytují nebo by se mohli vyskytovat napříč různými třídami a následně tyto metody rozdělit podle funkce k příslušným rozhraním. Jednotlivá rozhraní pak můžou obsahovat i sémantiku metod, neboli popis funkcí, vstupních parametrů, návratových hodnot a „vyhazovaných“ (throws) výjimek prostřednictvím komentované dokumentace. V dokumentaci se taktéž můžou nalézat jistá omezení metod, popřípadě příklady jejich volání. Jedno rozhraní pak může být implementováno více třídami a jedna třída může implementovat více rozhraní narozdíl od abstraktní třídy, kdy každá třída může mít pouze jednoho předka. Pokud někde v kódu voláme metody specifikované rozhraním, pak je nikdy nevoláme přímo na proměnné třídy implementující toto rozhraní, ale vždy na proměnné popř. atributu typu rozhraní. Důsledným používáním atributů typu rozhraní místo samotných tříd získáváme značnou flexibilitu kódu. Kdykoli můžeme nahradit třídu implementující rozhraní, nebo přidat další typy tříd implementujících dané rozhraní, aniž bychom museli upravovat klientský kód14. Pro snadnou inicializaci rozhraní se využívají návrhové vzory např. Factory method, Factory, Facade, nebo můžeme využít služeb EJB kontejneru15, který realizuje inicializaci atributů prostřednictvím dependency injection (viz. kapitola 3.5), nebo jiného IOC kontejneru. Na schématu (2-7) je naznačena část bankovní aplikace, kde jsou definovány tři typy bankovních účtů pomocí tříd (BeznyUcet, Silver, Gold) implementujících rozhraní IBankovniOperace. Klienti jsou v tomto příkladu komponenty, které nevolají přímo jednotlivé instance tříd bankovních účtů, ale veškeré operace
13
V literatuře se můžeme setkat s pojmem signatura metody. Klientský kód v tomto kontextu znamená jakýkoliv kus kódu užitý v aplikaci, který používá proměnnou nebo atribut typu rozhraní 15 V Javě můžeme použít jakýkoliv IOC kontejner (např. Spring) 14
21
provádějí prostřednictvím rozhraní IBankovniOperace. Klient v tomto případě provede požadovanou bankovní operaci např. pošle peníze na jiný účet (odesliPenize) a nemusí vědět s jakým typem účtu pracuje. Každá třída účtu pak obsahuje vlastní implementaci operace. Třída BeznyUcet nejprve provede kontrolu, zdali je na účtu dostatečný zůstatek a pouze pokud ano, pak odešle peníze. Oproti tomu třída Silver může odesílat peníze do debetu -250000 a třída Gold do -1000000.
Schéma 2-7 Ukázka nahrazení přímé vazby mezi jednotlivými implementacemi bankovního účtu a klientskými komponentami prostřednictvím rozhraní IBankovniOperace
Výhodou takového návrhu je, že jednotliví klienti nejsou závislí na konkrétní třídě. Tím jsou eliminovány přímé vazby a je umožněna vysoká flexibilita a modularita
celého modelu.
Jednotlivé implementace mohou
být
snadno
zaměňovány, rušeny a přidávány. Pokud bychom se například rozhodli ve výše zmíněném příkladu rozšířit systém o další typ účtu Platium, kde by neexistoval žádný limit při posílání peněz, pak jednoduše přidáme novou třídu, které bude implementovat rozhraní IBankovniOperace. 2.4.2.2 Komponenta Smyslem vytváření komponent je především lokalizovat znovupoužitelné části systému tak, aby každá komponenta systému poskytovala svému okolí určité funkce (služby). Tento princip zcela koresponduje s OOP. Jak komponenta, tak objekt zapouzdřují své chování a poskytují svému okolí komunikační rozhraní. „Komponenta je fyzickou nenahraditelnou částí systému, která obsahuje implementaci a poskytuje realizaci množiny specifikovaných rozhraní“ [22 cit. 350].
22
Jinými slovy každá komponenta obsahuje specifikaci (rozhraní) a zapouzdřuje implementaci specifikace. Na úrovni jazyka Java je komponentou každý kus zdrojového kódu zkompilovaného do souboru s příponou class. Takto vytvořené soubory nazýváme elementárními komponentami, které se zpravidla seskupují do archívů jar a přidávají se k aplikaci jako knihovny (library). Elementární komponenta může být třída (Class), rozhraní (Interface) nebo seznam (Enum). Je třeba zdůraznit, že rozhraní elementární komponenty není reprezentováno klasickým rozhraním, které jsme si popsali v kapitole 2.4.2.1, nýbrž ho specifikují všechny deklarace16 komponenty označené klíčovým slovem public. Při návrhu aplikace se ovšem nespokojíme pouze s elementárními komponentami na úrovni jazyka, nýbrž máme potřebu vytvářet vyšší celky, které si pro
přehlednost
pojmenujeme
jako
logické
komponenty.
Každá
logická
komponenta je tvořena několika elementárními komponentami, z nichž je definováno klasické Java rozhraní (interface) určené pro komunikaci s ostatními komponentami17 a jedna či více implementačních tříd. Další prvky jako např. abstraktní třídy, seznamy, soubory obsahující metadata atp. jsou volitelné. Příkladem takovýchto komponent z JEE prostředí jsou například servlet, JSP stránka, nebo EJB session beany (kap. 3.3). Z vyššího návrhového pohledu může být komponenta složena z více logických komponent. Logické komponenty pak můžeme seskupovat do tzv. zásuvných modulů, které lze snadno přidávat do aplikace. V případě EJB to jsou ejb-moduly komprimované do archivu *.jar, které kromě vlastních zkompilovaných zdrojových kódů obsahují deployment descriptor popisující způsob nasazení do JEE serveru. Jednotlivé komponenty EJB modulů mohou být přístupné ostatním aplikacím (jak vzdáleným tak lokálním – detailněji popsané v kapitlole 3).
16
Deklaracemi elementární komponenty máme namysli, hlavičky metod, atributy, konstanty, názvy vnořených tříd(inner class) nebo samotnou deklaraci komponenty (název třídy, rozhraní nebo seznamu). 17 Rozhrání komponenty může být více, nebo mohou být nahrazeny návrhovým vzorem Facade
23
3 EJB 3.0 V této kapitole se detailněji seznámíme s EJB 3.0 frameworkem od společnosti Sun Microsystems. Popíšeme si jeho základní rysy, seznámíme se specifikací jednotlivých EJB 3.0 komponent a uvedeme si možnosti jejich využití při tvorbě střední vrstvy serverově orientované aplikace. V následujícím textu budeme mít zkratkou EJB vždy na mysli EJB verzi 3.0, pokud nebude výslovně uvedeno jinak. Jak již bylo naznačeno (kap. 2.4), EJB framework je navržen pro usnadnění vývoje střední vrstvy rozsáhlých škálovatelných podnikových aplikací (vrstva obchodní logiky a vrstva přístupu k datům viz. kap. 2.4.2 a 2.4.3). Využívá k tomu služby EJB kontejneru (kap. 2.2.4) starajícího se o řadu procesů, které běží na pozadí. Programátor se tak může soustředit na vývoj obchodní logiky a nemusí se zabývat psaním neustále se opakujícího servisního kódu18, který do značné míry činí nepřehledným zdrojový kód aplikace.
3.1 Enterprise java beany Enterprise Java beany (EJB) jsou serverové komponenty běžící v EJB kontejneru a slouží k zapouzdření obchodní logiky aplikace. Obchodní logikou máme na mysli zdrojový kód vykonávající funkční požadavky, které má vyvíjený systém splňovat. Například v systému elektronického bankovnictví budou EJB implementovat
obchodní
logiku
v metodách
provedPrikazKUhrade
a
odesliVypisUctuNaEmail. Vyvoláním těchto metod provede klient příkaz k úhradě a obdrží zprávu o stavu účtu na jeho emailovou adresu. Klient v tomto případě může být buď lokální nebo vzdálený. Lokálním klientem je např. komponenta webové vrstvy (JSP nebo Servlet) nebo jiná EJB komponenta v rámci jedné JVM. Vzdáleným klientem může být obecně Java komponenta běžící v jiné JVM, nebo vzdálená webová služba. EJB beany dělíme podle způsobu komunikace s klientem na session beany a na message-driven beany. Session beany slouží k implementaci synchronní komunikace s klientem a Message-driven beany komunikují s klientem asynchronně. Podrobněji si celou problematiku implementace obou skupin EJB beanů popíšeme v kapitolách 3.2 a 3.3. Mezi EJB beany můžeme taktéž zařadit
18
V tomto případě myslíme obslužný kód, který nemá nic společného s obchodní problematikou (business service). Příkladem může být otevření databázového spojení před spuštěním transakce.
24
Entity19, kterým se budeme věnovat v kapitole 4 (JPA) a to z toho důvodu, že je lze nasadit i mimo EJB kontejner20.
3.2 Metadata - anotace nebo XML? Ještě než se pustíme do popisování jednotlivých EJB komponent, vysvětlíme si jakým způsobem je možné vytvářet metadata k EJB komponentám. Metadata, nebo-li popisná data EJB komponent, slouží k nastavení specifických vlastností. Určují jakým způsobem bude kontejner nakládat s EJB komponenty, popř. s jejich metodami či atributy. Metadata můžou ovlivňovat např. životní cyklus EJB komponent, ověřovat práva klienta pro přístup k obchodní metodě, nastavovat JNDI názvy EJB komponent, zajišťovat dependency injection komponentovým atributům nebo určovat typ EJB komponent21. Metadata můžeme vytvářet dvěma způsoby: buď pomocí anotací přímo v kódu EJB komponent, nebo pomocí XML souboru (deployment descriptor). Při interpretaci metadat platí princip konfigurace výjimkou (configuration by exception), kdy kontejner nastavuje základní provozní hodnoty (default), pokud nebyly zadány žádná nebo minimum metadat. Díky tomuto principu nemusíme ztrácet čas s detailním popisováním veškerých EJB komponent jen proto, že si je chceme spustit. Dalším pravidlem při čtení metadat je skutečnost, že EJB kontejner čte současně jak anotace tak XML a pouze pokud je pomocí XML i anotace nastavena stejná vlastnost, pak platí ta hodnota, která je nastavena v XML. O tom, zda-li použít anotace nebo XML nebo jejich kombinaci záleží vždy na preferenci vývojáře popř. na firemní metodice jakým způsobem tvořit a dokumentovat metadata. Podívejme se nyní na výhody jednotlivých konceptů. Anotace nám nepochybně přinášejí přehlednější kód. Je z nich jasně patrné chování třídy, popř. kusu kódu, a mohou tak částečně posloužit i jako zdroj dokumentace. Oproti tomu díky XML můžeme ponechat zdrojový kód EJB komponent bez závislostí na anotacích z balíčku javax.ejb.*. Navíc při jakékoli změně nastavení EJB komponenty nemusíme zasahovat do zdrojového kódu a provádět následnou kompilaci. Kombinování obou přístupů dohromady může přinést snadnější vývoj, kdy programátor používá výhradně anotace pro zpřehlednění kódu a při ladění a nasazování aplikace se může využít XML, které automaticky přepíše všechny anotace nastavující vlastnosti pro vývojové prostředí. 19
Pozor, neplést s Entity beany z EJB 2.1. Podrobněji o této problematice v kapitole 4. 21 Výčet možností není úplný a s jednotlivými typy metadat se budeme seznamovat v následujícím textu této práce. 20
25
Kombinace obou principů se nejčastěji využívá všude tam, kde je vývojové prostředí odlišné od provozního prostředí. V této práci budeme pro veškerá metadata využívat výhradně anotací a to především z těchto důvodů: ukázkové příklady jsou podstatně čitelnější, k ukázkovým příkladů není třeba kromě zdrojového kódu přikládat ještě XML a samozřejmě platí, že veškerá metadata nastavená anotacemi je možná nastavit i pomocí XML.
3.3 Session beany Session beany (beany „sezení“) jsou využívané především k tvorbě úkolů (task) nebo k vytváření případů užití (use case), které mají být zpracovány synchronně. Požadovaný úkol může být např. zobrazení detailní informace o stavu našeho bankovního účtu, nebo výpis osobních údajů. Případem užití by mohl být např. výběr zboží a potvrzení objednávky v internetovém obchodě. Session beany jsou EJB komponenty tvořené obchodním rozhraním (business interface) a třídou beanu. Obchodní rozhraní EJB komponenty specifikuje všechny služby (metody), které má komponenta vykonávat a třída beanu implementuje obchodní rozhraní, tzn. realizuje všechny služby definované obchodním rozhraním. Session beany se dělí buď dle typu jejich rozhraní, nebo zda si uchovávají nějaká data o klientovi mezi jeho jednotlivými požadavky. V podkapitole 3.2.1 si popíšeme dva typy obchodního rozhraní session beanů. V podkapitole 3.2.2 se budeme věnovat bezstavovým session beanům, rozebereme si jejich životní cyklus a zvážíme možnosti jejich aplikace. Obdobným způsobem budeme postupovat při seznamování se stavovými session beany v podkapitole 3.2.3.
3.3.1 Specifikace obchodního rozhraní session beanů Obchodní rozhraní může být označeno anotací @Local nebo @Remote. Tato anotace určuje, bude-li EJB komponenta volána vzdáleně (z jiných JVM) nebo lokálně v rámci JEE serveru. Pokud anotace nebude uvedena, tak EJB kontejner bude s komponentou pracovat jako s lokální. Na schématu (3-1) je naznačen způsob volání lokálních a vzdálených EJB komponent nasazených na aplikačním
serveru1. Volba typu obchodního rozhraní je velice důležitá a při větší zátěži může mít zásadní vliv na výkonnost aplikace.
26
Schéma 3-1 Způsob volání vzdálených a lokálních EJB komponent
Lokální rozhraní nemá žádné speciální požadavky. Klient, který volá lokální rozhraní, musí být ve stejné JVM a klient i lokální EJB komponenta pracují se stejným
parametrickým
objektem,
tj.
oba
mohou
takový
objekt
měnit.
Parametrickým objektem máme namysli objekt, který klient předává EJB komponentě prostřednictvím obchodní metody jako parametr. Ukázku takového případu demonstruje kód (3-1), ve kterém klient předává objekt typu Produkt obchodní metodě pridejProdukt a následně klient modifikuje takto předaný objekt. Pokud EJB komponenta Kosik vlastní atribut Produkt a metodou pridejZbozi se provede
nastavení
tohoto
atributu,
pak
provedením
metody
v klientovi
p1.setVyprodany(true) nastavíme nejen proměnou p1 typu Produkt, ale i atribut Prodkut v EJB komponentě. Obě proměnné se totiž odkazují na stejný objekt v paměti. Na tuto vlastnost bychom se neměli spoléhat, a to především s přihlédnutím na skutečnost, že bychom někdy v budoucnu mohli potřebovat změnit lokální rozhraní na vzdálené, nebo využívat oba dva přístupy. @Local //lokální rozhraní nákupního košíku public interface IKosikLocal { ... public void pridejZbozi(Produkt produkt, int pocet); ... } //Kód klienta @EJB IKosikLocal kosik = null; Produkt p1 = new Produkt(); //provede se přidání produktu do nákupního košíku kosik.pridejZbozi(p1, 10); //klient označí produkt jako vyprodaný p1.setVyprodany(true); ...
Kód 3-1 Příklad volání lokálního EJB objektu
Vzdálené obchodní rozhraní @Remote slouží pro vzdálené klienty. Je sice možné volat vzdálené rozhraní i lokálně, tj. ze stejné JVM, ale je to značně neekonomické, protože celá komunikace pobíhá prostřednictvím RMI API (schéma 27
3-2). Jakýkoliv parametrický objekt předávaný metodě vzdáleného rozhraní (1a, 1b) je nejprve serializován (2a, 2b), poté RMI zajistí přenos parametru do cílové destinace. V dalším kroku (3) provede deserializaci parametru a na závěr RMI vyvolá (invoke) metodu a předá jí parametr. Ať voláme metody vzdáleného rozhrání ze stejné JVM (1b), nebo z jiné JVM (1a), vždy dochází k serializaci, což při lokálním volání není efektivní.
Schéma 3-2 Rozdíl mezi použitím vzdáleného, nebo lokálního rozhraní EJB komponenty.
Stejný princip platí i pro návratové hodnoty metod vzdáleného rozhraní. Proto musíme zajistit, aby jak parametry, tak návratové hodnoty metod vzdáleného rozhraní, byly serializovatelné. Oproti lokálnímu rozhraní klient, i když by byl ve stejné JVM, a EJB komponenta nepracují se stejným parametrickým objektem, tzn. nelze provést modifikaci atributu EJB komponenty jinak, než prostřednictvím metody vzdáleného obchodního rozhraní.
3.3.2 Specifikace třídy session beanů Ve třídě session beanu je obsažen výkonný kód obchodních metod EJB komponenty a musí splňovat tři podmínky: 1. Musí být označena anotací @Stateless nebo @Statefull – podle toho zda-li chceme, aby se komponenta chovala jako stavová nebo bezstavová. 2. Musí splňovat notaci POJO - obsahovat bezparametrický konstruktor a všechny atributy třídy beanu musejí mít veřejné get a set metody a samotná
třída
musí
být
serializovatelná
(musí
implementovat
22
java.io.Serializable ). 3. Musí implementovat obchodní rozhraní session beanu. 22
implementace java.io.serializable nemusí být vždy nutná (především u bezstavových session beanů), přesto ji doporučuji důsledně používat.
28
3.3.3 Bezstavové session beany Někdy též bezkontextové session beany jsou určeny pro implementaci jednorequestových požadavků, kdy není potřeba udržovat žádné informace o stavu klienta. Typickou úlohou bezstavového session beanu může být např. metoda, která nám zobrazí zůstatek na našem účtu. Metoda obsahuje jeden parametr
idUctu a vrací datový typ Double s příslušnou hodnotou zůstatku. V tomto případě si není nutné pamatovat jakýkoliv stav. Kód 3-2 znázorňuje realizaci skládající se z Obchodního rozhraní IUcetLocal a samotné třídy beanu UcetBean. @Local public interface IUcetLocal { public Double ukazZustatekNaUctu(Integer idUcet); } @Stateless public class UcetBean implements IUcetLocal, Serializable { public UcetBean() {} public Double ukazZustatekNaUctu(Integer idUcet) { Double zustatek = null; //provedeni obchodni logiky zjisteni zustatku z DB return zustatek; } }
Kód 3-2 implementace bezstavového session beanu, který podává informaci o hodnotě zůstatku na účtu.
Bezstavové session beany jsou velice efektivním a bezpečným způsobem, jak implementovat obchodní logiku, která nevyžaduje znalost stavu klienta. Právě proto, že bezstavové session beany neudržují stav klienta, tak všechny instance stejné třídy bezstavového session beanu jsou pro klienta od sebe nerozeznatelné. Prakticky vůbec nezáleží na tom, kdo a odkud volá bezstavový session bean. To znamená, že jakýkoliv bezstavový session bean může vyřídit klientův požadavek, protože všechny instance dané třídy jsou identické. Díky této vlastnosti EJB kontejner udržuje pool bezstavových session bean instancí a rozhoduje, která instance na daný požadavek odpoví. Při volání bezstavového session beanu klientem nikdy nedochází k tzv. přímému volání. Celý proces je ilustrován na schématu (3-3). EJB kontejner nejprve vytvoří tzv. EJB objekt (EJB object) na základě obchodního rozhraní požadovaného EJB beanu (krok 1.). EJB objekt implementuje obchodní rozhraní a skrze něj má klient přístup k metodám obchodní logiky EJB beanu. V kroku 2 vybere EJB kontejner libovolnou instanci beanu z poolu a označí ji jako používanou. Do používané instance nikdy nemůže vstoupit více vláken naráz tak, jako to je běžné např. u servletu (javax.servlet.http.HttpServlet). Proto se při
29
implementaci EJB beanu nemusíme starat o bezpečnost vláken. V kroku 3 EJB objekt zavolá instanci vybranou z poolu, a ta vykoná požadovanou funkci. Po skončení požadavku se provede krok 4, kdy EJB kontejner uvolní instanci beanu a vrátí ji zpět do poolu, kde je opět připravena pro další volání.
Schéma 3-3 Volání bezstavového session beanu klientem.
Takový způsob poolování (pooling), nebo-li sdílení instancí prostřednictvím EJB kontejneru, je velice efektivní při využívání fyzické paměti. Šetří čas, který by byl za potřebí, kdyby se po každém volání musela vytvářet nová instance. Vyzvednutí existující instance z poolu je podstatně efektivnější23. A druhou výhodou, ač se to na první pohled nemusí tak jevit, je definovaní maximální velikosti poolu, což nám zajistí, že v aplikaci nikdy nebude existovat více instancí dané bezstavové session bean třídy, a tím nedojde k přetečení serverové paměti. U naprosté většiny EJB kontejnerů lze nastavit minimální a maximální velikost poolu a EJB kontejner si sám řídí počet instancí v poolu mezi horní a dolní hranicí. Při malém počtu volání daného beanu bude mít EJB kontejner tendenci snižovat velikost poolu a naopak při větším počtu volání se bude pool blížit maximální velikosti. Instance session beanu má pouze dva stavy. Buď neexistuje, nebo existuje a je připravena v poolu pro použití24. O tom, kdy bude instance vytvořena a zrušena, rozhoduje vždy EJB kontejner. Nyní se podrobněji podívejme na životní cyklus bezstavového session beanu, který je znázorněn na schématu (3-4). Při vytváření instance beanu (1.) EJB kontejner nejprve zavolá bezparametrický konstruktor třídy příslušného beanu, poté se provede dependency injection (2.) všech označených atributů metadaty (XML nebo anotace). Dependency injection se provádí až po vytvoření instance, a 23
Obdobným způsobem jsou poolována spojení s databází, kdy se při každém volání databáze nemusí vytvářet nové spojení. 24 v této souvislosti můžeme hovořit ještě o třetím stavu, kdy je bean vyjmut EJB kontejnerem z poolu a využit pro výkon požadavku. Tento stav se obecně označuje jako existence beanu.
30
proto veškerý kód provedený v konstruktoru ještě nemá k dispozici žádné závislosti. Po provedení dependeny injection nastane fáze (3.), pokud v beanu existuje metoda označená anotací @PostConstruct. Tato metoda je vhodným místem pro inicializaci beanu, protože již má k dispozici veškeré závislosti nastavené kontejnerem. Následně je bean vložen do poolu a připraven k výkonu klientských požadavků.
Schéma 3-4 Cyklus bezstavového session beanu
Jak již bylo popsáno dříve, vždy při vyvolání metod na beanu kontejner vyzvedne instanci beanu z poolu, poté
volá klient metody na instanci
prostřednictvím EJB objektu a po dokončení požadavku je bean opět navrácen zpět do poolu (schéma 3-4). Ještě existuje druhá možnost, kdy je v beanu nastaven tzv. časovač např. anotací @Timeout. Metoda označena anotací
@Timeout může vykonávat periodické nebo jednorázové akce, např. úklid databáze či pravidelné zálohování dat. Časovač je kontejnerem zaregistrován na určitou dobu, a když tato doba nastane, EJB kontejner vyjme instanci z poolu, zavolá metodu @Timeout a vrátí instanci zpět do poolu. Na konci životního cyklu instance beanu (4) je zavolána metoda označena anotací @PreDestroy , kde je možné provést například „závěrečný úklid“. Po provedení této metody je instance beanu předána Garbage Collectoru a dále již neexistuje.
3.3.4 Stavové session beany Stavové session beany (Statefull session bean) se používají v těch případech, kdy je potřeba udržovat stav klienta. Typickým příkladem uplatnění stavové session beany je realizace nákupního košíku internetového obchodu. Klient vybírá zboží z katalogu a průběžně se rozhoduje, zda nějaké zboží přidá či
31
odebere. Stav košíku je nutné uchovávat v paměti, abychom měli k dispozici produkty v požadovaném množství, když se klient rozhodne objednávku provést. Narozdíl od bezstavových session beanů se stavové session beany nedají sdílet s ostatními klienty a pro každého klienta vzniká nová instance. Ta je v paměti tak dlouho, dokud klient drží aktivní spojení se serverem, nebo dokud její platnost nevyprší. Pokud bychom použili na tvorbu obchodní logiky pouze stavové session beany, pak by naše aplikace s rostoucím počtem souběžně aktivních klientů začala mít potíže s pamětí. Proto bychom měli volit session beany pouze v případě, kdy se daná logika nedá sdílet. Ukázka realizace nákupního košíku pomocí stavového session beanu. Třída beanu KosikBean má jeden privátní atribut zbozi typu Map, který uchovává klientem vybrané produkty a jejich aktuální počty. Z příkladu je vidět, že implementace stavového session beanu se oproti bezstavovému session beanu liší pouze v anotaci @Statefull nad třídou beanu. Opět musí být splněny všechny podmínky pro tvorbu beanu stejně jako v případě bezstavového session beanu (kap. 3.2.1). Na první pohled je rozdíl mezi oběma typy beanů jen nepatrný, ovšem v jejich životním cyklu a volaní instance beanů je rozdíl zásadní. @Local public interface IKosikLocal { public void pridejZbozi(Produkt produkt, int pocet); public void odeberZbozi(Produkt produkt); public void objednej(); } @Statefull public class KosikBean implements IKosikLocal, Serializable { private Map
zbozi = new HashMap(); public KosikBean() {} public void pridejZbozi(Produkt produkt, int pocet) { zbozi.put(produkt,pocet); } public void odeberZbozi(Produkt produkt) { zbozi.remove(produkt); } public void objednej() { //metoda ulozi objednavku do DB } }
Kód 3-3 implementace nákupního košíku e-shopu prostřednisctvím stavového session beanu
32
Nejprve se podívejme, jak probíhá volání stavového session beanu klientem. Na schématu (3-5) jsou znázorněni tři klienti a každý z nich volá stejnou obchodní logiku implementovanou ve stavovém session beanu. EJB kontejner vytvoří pro každého klienta EJB objekt dle obchodního rozhraní beanu. Klient pak volá metody obchodní logiky přes EJB objekt. Důležité je, že každý klient přistupuje pouze ke svojí vlastní instanci beanu a ty nemohou být nikdy mezi klienty zaměněny. Kontejner může instance pasivovat (passivate), tzv. odložit na disk. Když klient znovu zavolá instanci, EJB kontejner ji opět načte (activate) z disku do paměti. Kontejner tímto způsobem reguluje počet aktivních instancí v paměti. Vzhledem k tomu, že stavové session beany mohou být pasivovány, musíme důsledně dbát na to, aby byly serializovatelné. Pokud budou obsahovat nějaký neserializovatelný atribut, pak o hodnotu takového atributu po provedení pasivace definitivně přijdeme. Jak eliminovat tento problém si popíšeme v následujícím textu, který se věnuje životnímu cyklu stavových session beanů.
Schéma 3-5 způsob volání stavového session beanu klientem
Jak již bylo naznačeno v předchozím textu, životní cyklus stavového session beanu (schéma 3-6) je odlišný od bezstavového session beanu. První tři kroky jsou shodné a jsou popsány v kapitole 3.2.3. Po vytvoření instance může klient volat obchodní metody. Instance může přejít z aktivního stavu (method ready) v paměti do dvou stavů. Buďto do pasivního stavu, nebo ji kontejner zruší. Ke zrušení instance z aktivního stavu (6.) může dojít buď vypršením časového limitu existence instance (timeoutu), nebo zavoláním metody obchodní logiky označené anotací
@Remove. V obou případech dojde k následnému volání metody označené metodou @PreDestroy (7.), stejně jako u bezstavového session beanu. K timeoutu dojde, pokud doba uplynulá od posledního požadavku na instanci je větší než délka životnosti bezstavového session beanu. Nastavení délky životnosti instance
33
záleží na konkrétním typu EJB kontejneru a většinou se provádí přímo v popisovači aplikace, nebo přímo v aplikačním serveru25.
Schéma 3-6 životní cyklus stavového session beanu
Pokud se kontejner rozhodne pasivovat instanci beanu, je nejprve zavolána metoda označena anotací @PrePassivate, v níž můžeme provést akce, které se mají stát těsně před pasivací, tj. ještě před tím, než je instance beanu zeserializována a uložena na disk. Instance je pasivována tak dlouho, dokud ji klient opět nezavolá, nebo dokud nevyprší její platnost (timeout). Po vypršení platnosti dojde k přímému smazaní zeserializované instance beanu z disku. V tomto případě se již neprovede žádná metoda, jak tomu bylo při odstraňování aktivní instance z paměti. Instance beanu je nenávratně ztracena, a když se ji klient pokusí zavolat po vypršení timeoutu, tak je vytvořena nová instance, která provede požadované akce. V druhém případě stihne klient zavolat pasivovanou instanci ještě před tím, než vyprší její platnost. EJB kontejner ji opět natáhne do paměti a zavolá se anotovaná metoda @PostActivate. Poté je instance stavové session beany opět aktivně v paměti připravena pro použití. Jak již bylo naznačeno, tím, že se stavové session beany dají odkládat na disk prostřednictvím serializace, vzniká riziko, že veškeré atributy beanu, které nejsou serializovatelné, se nenávratně ztratí. Právě proto jsou v EJB k dispozici metody @PrePassivate, resp. @PostActivate, kde je možno provést přípravu pro serializaci, resp. uvedení instance stavového session beanu do původního stavu, ve kterém se nacházela před pasivací - tj. restaurovat všechny atributy, které
25
Např. v implementaci JEE serveru Glassfish (SJS AS verze 8.x,nebo 9.x) od společnosti Sun Microsystem se délka timeoutu nastavuje v souboru sun-ejb-jar.xml atributem removal-timeout-in-seconds.
34
nemohly být serializovány tak, aby klient vůbec nepoznal, že byla instance beanu pasivována.
3.4 Message-driven beany Hlavní motivací pro použití message-driven beanů („zprávami řízených“ beanů – dále jen MDB) je potřeba asynchronní komunikace mezi aplikací a vzdáleným klientem. Zdůrazněme, že kdykoliv budeme hovořit o asynchronní komunikaci v souvislosti s MDB, vždy budeme mít namysli vzdálené volání metod. Implementace synchronního vzdáleného volání metod pomocí session beanů bylo popsáno v kapitole 3.2.1. Na schématu (3-7) je naznačen rozdíl mezi synchronním a asynchronním způsobem komunikace.
Schéma 3-7 Rozdíl mezi synchoronní a asynchronní komunikací
V případě synchronní komunikace pošle klient požadavek na aplikaci a čeká dokud od aplikace neobdrží odpověď. Během čekání klient nemůže provádět žádné akce – je zaneprázdněn. Tato skutečnost může být velice nepříjemná, především pokud má aplikace provést více náročnějších operací pro splnění požadavku, nebo když je v jednu dobu velké množství požadavků na aplikaci. Klient nejprve musí počkat, než aplikace vyřídí ostatní požadavky, a až následně může vyřídit jeho požadavek a zaslat mu odpověď. Čím je prodleva mezi vytvořením požadavku a obdrženou odpovědí větší, tím více bychom se měli zabývat, zda-li je v takovém případě opravdu nutná synchronní komunikace a jak co nejvíce zkrátit dobu, po kterou klient čeká na odpověď. Představme si, že bychom chtěli v aplikaci e-bankovnictví zaslat výpis účtu v pdf formátu na náš mail stisknutím tlačítka odeslat. Vzdálená aplikace musí udělat několik kroků, aby splnila náš požadavek: vybere informace o našem účtu 35
z databáze, vygeneruje na základě těchto informací pdf dokument, vytvoří tělo emailu, přiloží dokument jako přílohu a na závěr email odešle. Pokud bude tato operace probíhat synchronně, pak po stisknutí tlačítka odeslat nebudeme moci pracovat s aplikací tak dlouho, dokud nám vzdálená aplikace nezašle odpověď nebo dokud nevyprší timeout. Právě pro takové případy je velice vhodné využít asynchronní komunikace, např. MDB. Ještě jednou se podívejme na spodní část schématu (3-7) asynchronní komunikace, kde jsou naznačeni dva klienti volající souběžně vzdálenou aplikaci. Mezi nimi a aplikací je prostředník (message oriented middleware - MOM), který slouží jako fronta požadavků. Klient vytvoří požadavek a pošle ho aplikaci. Prostředník požadavek zachytí a vloží ho do fronty. Prostředník posílá postupně požadavky aplikaci tak, jak mu přišly od nejstaršího. Aplikace přijme požadavek, zpracuje ho a na závěr zašle zprávu klientovi. Rozdíl mezi synchronní komunikací je v tom, že klient po odeslání požadavku nemusí čekat, než mu aplikace odpoví. Kdyby byl uvedený příklad e-bankovnictví řešen asynchronně, např. pomocí MDB, pak by klient po stisku tlačítka odeslat mohl neprodleně pokračovat v činnosti.
3.4.1 Kdy použít MDB? V úvodu kapitoly 3.2 jsme si naznačili jednu z možností, kdy je vhodnější použít asynchronní MDB než vzdálené synchronní session beany. V následujícím výčtu se seznámíme se čtyřmi základními problémy synchronní komunikace, na jejichž řešení jsou MDB vhodnými kandidáty. Zrušení aktivního čekání na odpověď. Tento případ byl popsán v úvodu kapitoly 3.3 (druhý a třetí odstavec) na příkladu aplikace e-bankovnictví. Při asynchronní komunikaci nemusí klient čekat než obdrží odpověď od vzdálené aplikace a hned po odeslání požadavku na server může pokračovat v práci s aplikací narozdíl od synchronního zpracování, kdy klient nemůže provádět žádné akce, dokud nedostane odpověď nebo dokud nevyprší timeout – doba, do které má klient obdržet odpověď, jinak se operace automaticky zruší. Zrušení závislosti mezi klientem a serverem. Při synchronní komunikaci musí každý klient vědět, na jaký server posílá požadavek, tj. klient přímo adresuje pomocí RMI vzdálené objekty. Mezi klientem a serverem existuje přímá vazba, a tak nemůžeme jednoduše vyjmout server ze systému a nahradit ho nějakým jiným aniž bychom nemuseli upravit klientův kód. Oproti tomu při asynchronním zpracování klient nemusí znát příjemce zprávy, protože veškeré zprávy posílá
36
prostředníkovi (schéma 3-7). Při výměně serveru není nutné jakkoliv upravovat klienta. Spolehlivost doručení požadavku. Pokud klient při synchronní komunikaci odešle požadavek na server a server je vytížen, nebo je momentálně nedostupný, pak se klientův požadavek nikdy nevyřídí a klient po vypršení timeoutu vrátí výjimku. Jediná možnost jak požadavek doručit je opakovat celou akci tak dlouho, dokud klient neobdrží odpověď. Opakované vytváření a zasílání požadavku není příliš vhodným způsobem zajištění doručitelnosti, a to především z důvodu, že klient je během této aktivity zcela zaneprázdněn (viz. odstavec Zrušení aktivního čekání na odpověď). Narozdíl od asynchronní komunikace, kdy klient odešle zprávu prostředníkovi a ten ji drží tak dlouho, dokud není server opět aktivní. Poté prostředník doručí zprávu na server. Klienta nemusí vůbec zajímat, zda-li je server momentálně dostupný, protože server požadavek automaticky obdrží až bude znovu dostupný. Podpora zasílaní hromadných požadavků pro více příjemců.
Pomocí
synchronní komunikace nelze řešit podporu vysílání hromadných požadavků, protože je vždy aktivní jeden odesílatel (např. klient) a jeden příjemce (např. EJB komponenta). Neexistuje zde žádná možnost jak může více odesílatelů posílat zprávy na různé příjemce. Většina implementací asynchronních prostředníků MOM umožňuje přijímat zprávy od mnoha odesílatelů a dále je distribuovat více příjemcům.
3.4.2 Specifikace MDB MDB komponenty se skládají pouze z POJO třídy a oproti session beanům neobsahují obchodní rozhraní. Třída MDB je označena anotací @MessageDriven a musí implementovat rozhraní MessageListener. Pro zpracování zprávy se nejčastěji používá JMS, ale MDB byly navrženy tak, aby umožňovaly implementovat i jiná API pro asynchronní komunikaci. Problematika MDB a asynchronní komunikace mezi JEE komponentami je velice rozsáhlé téma a není zde prostor pro podrobnější popis. S konkrétními ukázkami implementace MDB s využitím JMS se můžeme seznámit např. v knize [5].
3.5 Dependency inejction Při popisu životního cyklu EJB komponent jsme narazili na pojem dependency injection (někdy též inversion of control IOC). Jedná se o kontejnerovou službu, která zajistí instance všech atributů označených příslušnými
37
metadaty nebo jejich veřejných set metod. Kód XYZ demonstruje, jakým způsobem se dají získat reference v EJB komponentě na ostatní typy beanů: (1) získání reference na bezstavový session bean UcetBean (3), nebo na datové zdroje: (2) získá referenci k DataSource definovaném v JEE serveru. @Stateful(name = "kosik" ) public class KosikBean implements IKosikLocal, Serializable { (1)
@EJB(beanName="ucet") private IUcetLocal ucet;
(2)
@Resource(name="jdbc/fsolivt1") private javax.sql.DataSource dataSource; ...
} (3) @Stateless(name = "ucet") public class UcetBean implements IUcetLocal, Serializable { ... }
Kód 3-4 Ukázka dependency injection pomocí field injection
3.5.1 Field nebo setter injection? Už nadpis této podkapitoly signalizuje, že máme k dispozici dva způsoby, kterými můžeme docílit dependency injection. Začněme nejprve popisem prvního způsobu. Field injection zajistí přímé nastavení atributu, a to i v případě, kdy se jedná o atribut privátní. EJB kontejner přímo pomocí reflexe nastaví hodnotu atributu (kód 3-4 – (1), (2)). Výhodou tohoto způsobu je, že nemusíme vytvářet k atributu veřejnou set metodu. Ovšem v případě injekce do privátního atributu je tak trochu obcházeno pravidlo zapouzdření komponenty, ve které chceme danou referenci injectovat. Pro setter injection musíme zajistit k atributu, jehož referenci chceme pomocí dependency injection získat, veřejnou set metodu. Anotací pak neoznačujeme atribut, ale jeho set metodu. Tento postup se nám může zdát poněkud zdlouhavý, ale v jistých případech přináší určité výhody. V kódu 3-5 je naznačen způsob inicializace atributu connection, který se získá metodou getConnection() z atributu
dataSource injektovaného pomocí set metody. Díky setter injection nemusíme při prvním použití atributu connection získávat jeho instanci.
38
private javax.sql.DataSource dataSource; private java.sql.Connection connection; @Resource(name="jdbc/fsolivt1") public void setDataSource(DataSource dataSource) throws SQLException { this.dataSource = dataSource; this.connection = dataSource.getConnection(); }
Kód 3-5 ukázka implementace setter injection
3.5.2 Kdy lze použít dependency injection? Vzhledem k tomu, že dependency injection je kontejnerová služba, která je k dispozici v JEE serveru, můžeme jí využívat napříč vrstvami podnikové aplikace, ale pouze v JEE komponentách, jejichž životní cyklus řídí JEE kontejer26. Příkladem takových komponent může být např. Servlet, Filter, Listener, JSF backing bean nebo EJB komponenty.
Existují však případy, kdy dependency
injection nemusí splňovat naše požadavky, nebo ji nelze provést vůbec a místo dependecy injection budeme muset získat referenci pomocí JNDI. V prvním případě se jedná o injectování stavového session beanu pomocí anotace @javax.ejb.EJB, do servletů, JSF backing beanů nebo bezstavových session beanů. Problém spočívá v tom, že reference injektovaného stavového session beanu může být sdílena více klienty, protože výše uvedené JEE komponenty mohou být sdíleny více klienty. Pokud bychom např. v třídě servletu injektovali stavový session bean do atributu servletu analogicky jako v kódu 3-4 (1), pak by všichni klienti servletu sdíleli stejný stavový session bean, což neodpovídá účelu stavového session beanu (kap Stavový session bean). Obdobně by tomu bylo v případě injektování stavového session beanu např. do bezstavového session beanu. Vždy musíme mít na paměti, kdo je klient. Pokud je klient komponenta sdílená více uživateli, pak všichni její uživatelé budou sdílet stejnou referenci stavového session beanu. Kód 3-6 naznačuje správný způsob injektování a následnou manipulaci se stavovým session beanem KosikBean (1)27 v servletu ServletKlient (2). Injekce se provádí na úrovni deklarace třídy servletu (2). Nejprve se pokusíme získat objekt
KosikBeanu pod názvem kosik ze session (HttpSession) (3). Pokud se objekt v session nenalézá, provedeme inicializace proměnné kosik pomocí JNDI (4). V případě neexistence názvu KosikBean v JNDI vyhodíme výjimku (5). V (6) kroku můžeme provádět volání metod nad takto získaným objektem. Po provedení všech 26
Některé JEE servery podporují dependency injection i do objektů, jejichž životní cyklus není řízen JEE kontejnerem. Jedná se však o nadstandardní služby a nemůžeme s nimi počítat ve všech JEE serverech. 27 Kompletní implementace komponenty KosikBean se nalézá v kódu 3-3
39
požadavků uložíme KosikBean do session pod názvem kosik. V ukázkovém kódu si můžeme všimnout dvou věcí. Při manipulaci se session beany v klientovi bychom měli vždy důsledně používat jejich rozhraní nikoli implementační třídu. V našem případě je klient servlet a proměnná kosik (3) je typu rozhraní
IKosikLocal. Druhou věcí je fakt, že JNDI lookup (4) vrací vždy novou instanci typu Object. (1) @Statefull(name = "kosik") public class KosikBean implements IKosikLocal, Serializable { ... } (2) @EJB(name="KosikBean", beanInterface=IKosikLocal.class, beanName="kosik") public class ServletKlient extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { IKosikLocal kosik = null; (3) kosik = (IKosikLocal) request.getSession().getAttribute("kosik"); if(kosik==null){ try { Context ctx = new InitialContext(); (4) kosik = (IKosikLocal) ctx.lookup("java:comp/env/KosikBean"); } catch (NamingException e) { e.printStackTrace(); (5) throw new RuntimeException(e); } } (6) kosik.pridejZbozi(...);//pridame zbozi do kosiku request.getSession().setAttribute("kosik", kosik); }//konec metody }//konec tridy
Kód 3-6 Volání stavového session beanu ze servletu
Druhý případ, kdy vůbec nemůžeme použít dependency injection, je injektování v objektech, jejichž životní cyklus neřídí JEE kontejner. Typicky se jedná o POJO třídy, které nejsou EJB komponenty. Nejen že v takových třídách nelze použít dependecy inection a místo toho musíme využít služeb JNDI, ale také je nemůžeme injektovat do EJB komponent. Možnost injektovat obyčejné POJO objekty do jiných Java SE či Java EE komponent by měla umožnit nová verze Javy EE 6. JNDI „look up“ je jediná možnost, jak zavést EJB komponenty do tříd, jejichž životní cyklus neřídí JEE kontejner. Jednotlivé servery se liší způsobem mapování EJB komponent do JNDI. Používání JNDI „look up“ (kód 3-6 4) je tedy závislé na konkrétním aplikačním serveru, což způsobuje řadu problému při migraci aplikace. Abychom těmto problémům co nejvíce předešli, je nutné si důkladně prostudovat způsob mapování EJB komponent do JNDI příslušného serveru, zavést způsob pojmenovávání jednotlivých komponent a provádět „look up“ pokud možno 40
z jednoho místa. Jednou z možností, jak provádět „look up“ unifikovaně, je použít JEE návrhový vzor Service Locator. Konkrétní řešení tohoto problému je popsáno v kapitole 5.
3.6 Light-weight AOP koncept – Intereceptory Pomocí aspektově orientovaného programování (dále jen AOP) můžeme řešit tzv. horizontální prostupování komponent. Aspekty nám umožní definovat výkonný kód, který se automaticky spustí při zavolání metody, ve které chceme výkonný kód uplatnit, aniž by byl v takové metodě uveden jakýkoliv odkaz na aspekt. Díky aspektům můžeme z metod vykonávajících obchodní logiku odsunout obslužný kód, který přímo nesouvisí s uplatněním obchodní logiky. Příkladem aspekty nejčastěji řešených problémů bývá logování, oprávnění pro volání metod (security) nebo transakce. EJB kontejner poskytuje pouze tzv. odlehčenou (lightl-weight) verzi AOP pomocí interceptorů. Kód 3-7 naznačuje způsob tvorby interceptoru a jeho následné definování nad obchodní metodou pridejZbozi z předchozích ukázek již dobře známého stavového session beanu KosikBean. public class LoggingInterceptor { (1)
(2)
@AroundInvoke public Object logMethodEntry(InvocationContext ic) throws Exception { System.out.println("-Logovani vstupnich parametru-"); System.out.println("Nazev tridy: "+ic.getTarget().getClass().getName()); System.out.println("Nazev metody: "+ic.getMethod().getName()); if(ic.getParameters()!=null){ for (int i = 0; i < ic.getParameters().length; i++){ Object o = ic.getParameters()[i]; System.out.println("parametr typu "+o.getClass().getName() + " = "+o.toString()); } } return ic.proceed(); }
} //----Definice interceptoru nad metododou EJB beanu (3) @Interceptors(LoggingInterceptor.class) public void pridejZbozi(Produkt produkt, int pocet) { zbozi.put(produkt,pocet); }
Kód 3-7 Ukázka interceptoru a jeho definice nad metodou EJB beanu.
Pojďme se podrobněji podívat na třídu interceptoru LoggingInterceptor. Aby tato třída byla EJB kontejnerem zaregistrována jako interceptor, musí obsahovat metodu, která obsahuje tyto vlastnosti: její návratová hodnota je typu Object, má jeden
vstupní
parametr
typu
InvocationContext a je označena anotací
41
@AroundInvoke. V ukázkovém příkladu (kód 3-7) je tato metoda pojmenována logMethodEntry (1) a demonstruje princip, jak zaznamenávat volané metody a jejich parametry28. V bodě (2) se vrací návratová hodnota pomocí metody
ic.proceed(). Pokud se metoda nezavolá, pak by nenásledovalo volání dalších událostí (následujících interceptorů a samotné metody EJB beanu), a proto pro správnou funkčnost metody interceptoru je třeba dodržovat návratovou hodnotu bodu (2). Pro tvorbu interceptoru nemusí být vytvořena samostatná třída. Anotaci
@AroundInvoke lze použít v jakékoliv třídě, třeba i v implementační třídě session beanu. Důležité, je aby metoda interceptoru splňovala výše zmíněné vlastnosti.
3.6.1 Kde a jak lze použít interceptory? Už ze samotného nadpisu této podkapitoly můžeme tušit, že interceptory nelze použít všude. V kódu 3-7 bodě (3) je naznačena definice interceptoru nad obchodní metodou session beanu pomocí anotace @Interceptors, jejíž parametr tvoří třída obsahující metodu interceptoru. Takováto anotace je funkční pouze session a message-driven beanech. V ostatních komponentách nasazených v rámci JEE kontejneru či v jiných třídách je ignorována. Nad každou metodou EJB beanů lze definovat jeden či více interceptorů. Pořadí jejich spouštění závisí vždy na pořadí parametrů v anotaci @Interceptors. První parametr anotace definuje první interceptor, druhý parametr definuje druhý interceptor atd. Anotaci lze použít i nad třídou EJB beanu, čímž jednotně definujeme iterceptor(y) nad všemi metodami tohoto beanu. Oba způsoby definice můžeme kombinovat. Například logovací interceptor budeme chtít definovat nad všemi metodami a jiný interceptor pouze nad jednou metodou, pak použijeme anotaci s logovacím interceptorem nad celou třídou beanu a jiný interceptor definujeme pouze nad požadovanou metodou. Mezi interceptory patří i metody, které volá kontejner v rámci životního cyklu EJB komponent, jako jsou např.: @PreDestroy, @PostConstruct, @Prepasivate aj., které byly popsán v rámci životního cyklu session beanů (3.3.3 a 3.3.4) Podrobněji se interceptory zabývají např. knihy [4] a [5].
28
Logování pomocí System.out.print(...) je pouze ilustrativní a v praxi bychom se ho měli vyvarovat a místo toho použít nějakou standardní knihovnu, např. Log4j.
42
3.6.2 Rozdíl mezi interceptory a AOP Interceptory definované v EJB sice řeší obdobným způsobem aspektovou problematiku, také oddělují obchodní logiku od AOP kódu, ale nejedná se o AOP v pravém slova smyslu. Zásadní rozdíl je v definici aspektu, budeme-li uvažovat v případě interceptoru jako o aspektu, protože pomocí interceptorů nejdou definovat tzv. point-cut. To znamená že interceptory můžeme nadefinovat pouze nad metodami (popř. celou třídou). Pomocí interceptorů nelze definovat např. aspekt, který se provede nad všemi metodami začínajícími slovem get v balíčku cz.datex, jak je to běžné v případě AOP. Dalším zásadním rozdílem je, že interceptory lze použít pouze v metodách EJB beanů. AOP je proti interceptorům podstatně propracovanější a nabízí celou škálu možností. Pro použití nějaké implementace AOP (např. AspectJ [33]) je nutné se důkladněji seznámit s terminologií a novými jazykovými prvky nad rámec standardního programovacího jazyka Java. V řadě případů jsou interceptory dostačující a jejich definice je snadnější a pro některé programátory přehlednější, protože veškeré point-cuty jsou definovány pomocí anotací nad místem volání, z čehož je patrné, co se před danou metodou zavolá.
43
4 JPA V této kapitole se budeme věnovat základnímu konceptu objektově relačního mapování pomocí JPA. Samotná problematika JPA je velice rozsáhlá a není smyslem této práce vytvořit kompletní přehled všech možností JPA. Proto si pouze stručně popíšeme vybrané pojmy a seznámíme se s několika principy, které byly uplatněny v prototypu SOB (kap. 5). Při tvorbě JEE aplikace můžeme využít JPA ve vrstvě obchodní logiky (kap. 2.4.2) pro objektově relační mapování (kap. 4.2) doménového modelu a ve vrstvě přístupu k datům (kap. 2.4.3) pro získávání persistentních objektů z relační databáze pomocí JPQL (kap.4.4 a 4.5). Nutno podotknout, že JPA není zdaleka jedinou možností jak mapovat doménový model na relační databázi a jak z databáze získávat persistentní objekty. Vedle JPA existuje celá řada ORM frameworků např. TopLink [17]) nebo Hibernate [18], které je možné využít pro realizaci ORM. Zásadním rozdílem mezi JPA a ostatními ORM frameworky je v tom, že JPA je součástí JEE specifikace, díky čemuž je dostupná v každém JEE aplikačním serveru. JPA je skutečně jenom API, tedy implementace je ponechána na výrobci JEE serveru, tzn. při využití JPA jsme odstíněni nejen od typu relační databáze, ale i od ORM frameworku na námi zvoleném JEE serveru. Pokud budeme například vyvíjet a testovat aplikaci na open-source serveru JBoss [19], tak bude veškerý JPA kód vykonávat
Hibernate. Následně nasadíme aplikaci např. na IBM
WebSpheare [20], kde bude stejný JPA kód vykonávat TopLink.
4.1 Nezávislost JPA na EJB kontejneru JPA je sice součástí JEE specifikace, ale je napsána tak, aby nebyla závislá na ostatních JEE API. Pro běh JPA postačí samotná JVM. Ve starší verzi EJB 2.1 existovaly Entity Beany, které byly závislé na EJB kontejneru, což v případě JPA entit neplatí. JPA entity jsou mezi vývojáři stále populárnější a je možné je kombinovat přímo s konkrétní implementací ORM frameworku. V praxi se například může provádět objektově relační mapování pomocí JPA anotací a samotná implementace DAO může využívat konkrétní OQL ORM poskytovatele. Tím bude doménový
model
nezávislý
na
konkrétní
databázi,
ovšem
například
z výkonnostních důvodů bude použit místo JPQL třeba HQL a veškeré DAO budou závislé přímo na implementaci Hibernate.
44
4.2 Entity Entity jsou objekty, které zapouzdřují jak data tak chování a je možné je trvale ukládat do databáze. „Entita reprezentuje v zásadě podstatné jméno, nebo stav více podstatných jmen sdružených do jednoho celku. Entita může mít vazby na mnoho dalších entit. V objektovém paradigmatu (kap. 2.4.2 viz. domain model) můžeme entitám kromě atributů přidávat i chování (obchodní logiku) a nazývat je jako doménové objekty“ [6 s. 17]. Doménové objekty jsou určeny doménovou třídou. Příkladem doménové třídy z reálného systému může být např. uživatel, který má atributy uživatelské jméno, heslo a obchodní metody přihlásit resp. odhlásit se z aplikace. Doménový objekt je konkrétní instance doménové třídy. V tomto případě např. uživatel s uživatelským jménem František a heslem 12345.
4.2.1 Entitní třída @Entity public class Uzivatel implements Serializable{ @Id private String jmeno; private String heslo; public Uzivatel(){} public String getJmeno() { return jmeno; } public void setJmeno(String jmeno) { this.jmeno = jmeno; } public String getHeslo() { return heslo; } public void setHeslo(String heslo) { this.heslo = heslo; } public void prihlasit(){ // provede přihlášení do systému } public void odhlasit(){ // provede odhlášení ze systému } } Kód 4-1 Ukázka implementace entitní třídy uživatel.
Co musí doménová třída splňovat, abychom ji mohli používat jako JPA entitní třídu? Entity jsou ve své podstatě opět jednoduché Java komponenty stejně jako session beany či MDB a jak již jsme si zvykli, všechny třídy EJB komponent musí splňovat notaci POJO (viz. kap. 3.2.2), tak ani Entitní třídy nejsou žádnou výjimkou. Pro připomenutí: POJO musí obsahovat - veřejný bezparametrický konstruktor, veřejné get a set metody pro všechny atributy a implementovat rozhraní java.io.Serializable. Kromě notace POJO musí být entitní třída označená
45
anotací @javax.persistence.Entity a jeden její atribut musí být označen anotací
@javax.persistence.Id určující jednoznačný identifikátor entity29. Kód (4-1) demonstruje implementaci systémového uživatele jako entitní třídu. Zásadní rozdíl mezi entitou a objektem je v tom, že každá entita obsahuje jeden atribut30 jako jednoznačný identifikátor - id (primární klíč), který nemá vůbec nic společného s její objektovou referencí. V našem ukázkovém případě bude každou entitu typu Uzivatel určovat jednoznačně Id jmeno. Entitní třída může být popsána ještě celou řadou metadat, které slouží ke specifikování objektově relačního mapování (viz. následující podkapitola).
4.2.2 Objektově relační mapování Objektově relační mapování (dále jen ORM) je proces, při němž definujeme vztah mezi doménovým modelem a jeho trvalým úložištěm dat v podobě relační databáze. Pokud bychom využívali objektové databáze, tak bychom se mohli tomuto procesu zcela vyhnout, ale v dnešní době jsou relační databáze stále jednoznačně nejvyužívanějším systémem pro trvalé ukládání dat podnikových aplikací. Proto je ve většině případů proces ORM nedílnou součástí vývoje a úprav podnikových
aplikací
implementovaných
pomocí
libovolného
objektově
orientovaného programovacího jazyka.
Schéma 4-1
Cílem ORM je definovat mezi doménovým modelem a tabulkami relační databáze takové vztahy, abychom mohli doménové objekty ukládat a načítat z relační databáze tak, jako bychom pracovali s objektovou databází. Na schématu (4-1) je naznačen jednoduchý způsob mapování třídy s primitivními atributy na databázovou tabulku, kde primitivní atributy {1,2,...,n} třídy jsou namapovány na sloupce tabulky {1,2,...,n}. Jednotlivé řádky tabulky {1,2,...,n} reprezentují instance 29
Opět platí, že jako alternativu k anotacím můžeme využít XML popisovač. O přednostech obou forem metadat pojednává kapitola 3.6. 30 ID může být jak primitivní typ, tak jiný objekt. V případě objektu se používá anotace @javax.persistence.EmbeddedId .
46
{1,2,...,n} dané třídy. V tomto případě je vztah mezi sloupci tabulky a atributy entitní třídy 1:1 stejně tak jako mezi instancemi třídy a řádky tabulky. Každá tabulka v relační databázi musí obsahovat alespoň jeden sloupec určený jako primární klíč, který slouží jako jednoznačný identifikátor relace. Instance Java tříd jsou určeny objektovou referencí. Abychom mohli docílit transparentního ukládání objektů do databázové tabulky, musíme vytvořit atribut, který bude sloužit jako primární klíč. Proto je v entitních třídách povinný atribut s anotací buď@Id nebo @EmbeddedId (složený primární klíč).
4.2.3 Vazby mezi entitami Případ, kdy entitní třída obsahuje pouze atributy primitivních typů, není v doménovém modelu příliš častý. Entity mezi sebou mohou mít vazby. Pro zjednodušení si budeme popisovat pouze vazby mezi dvěmi entitami, které si pro přehlednost pojmenujeme A a B, a ty mohou být buď jednostranné, nebo oboustranné. V případě jednostranného vztahu zná vlastník vztahu A druhou stranu – je možné volat A.getB(). Jinými slovy entita A obsahuje atribut entity B. V opačném případě to neplatí. Entita B vůbec „netuší“, že je v nějakém vztahu s entitou
A.
U
oboustranného
vztahu
platí,
že
A.equals(B.getA())
a
B.equals(A.getB()), což znamená, že A vlastní instanci B a B vlastní instanci A. Jednostranné a oboustranné vazby můžeme dle počtu vlastníků rozdělit na 1:1, 1:N, M:N a M:1. Pro mapování těchto vztahů existují následující anotace. Tabulka 1 obsahuje vztahy mezi entitami z pohledu entity A, atributy vztahu s příslušnými anotacemi. Tabulka 1 Vztahy mezi entitami
Vztah entit A:B
Anotace nad atributem v A
Reprezentace atributu v A
1:1
@OneToOne
private B a;
1:N
@OneToMany
private Collection bs;
M:N
@ManyToMany
private Collection bs;
M:1
@ManyToOne
private B a;
V tabulce 1 jsou vícenásobné vztahy reprezentovány atributem typu
Collection, což je nejobecnější zápis. V případě 1:N či M:N, kdy jsme si jisti, že A může vlastnit pouze jednu instanci B, pak je vhodnější použít místo Collection rozhraní Set. Například A je auto a B je cestující. Je zcela logické, že v každém autě může sedět každý cestující právě jednou. Rozhraní set nám zajistí, že se v kolekci nebude vyskytovat vícekrát jeden objekt. V opačném případě můžeme 47
použít rozhraní List. Příkladem může být např. entita slovo jako A a písmeno jako
B. Jedno slovo může obsahovat více stejných písmen.
4.3 Práce s třídou EntityManager EnitityManager (dále jen EM) je třída, která nám umožňuje načítat, aktualizovat a ukládat data. Pomocí entity manažeru můžeme vytvářet JPQL dotazy a získávat tak přímo objekty uložené v relační databázi. Dále můžeme např. vytvářet transakce, provádět commit, nebo rollback. Všechny funkce EM můžeme dohledat v dokumentaci. Celá problematika je velice dobře popsaná v knize [6]. Práce s EM se do značné míry odvíjí od toho, zdali necháváme řízení transakcí a životní cyklus EM na EJB kontejneru či nikoliv. Pokud je naše aplikace nasazena v EJB kontejneru, tak nám pro základní práci s EM bude stačit pouze injektovat EM do EJB komponent (kód 4-2). O všechny ostatní náležitosti se postará EJB kontejner. @Stateless public class JpaZavodDaoImpl implements IZavodDao { (1) @PersistenceContext(unitName="sob") private EntityManager em; public void delete(String id){ em.remove(t); } public void save(Typ e){ em.persist(e); } public ZavodT01 find(String id){ return em.find(ZavodT01.class, id); } @SuppressWarnings("unchecked") public List findAll(){ List zavody = null; String sql = "SELECT zavod FROM ZavodT01 as zavod "; Query q = em.createQuery(sql); zavody = q.getResultList(); return zavody; } }
Kód 4-2 Práce s EntityManagerem – základní CRUD operace nad objektem ZavodT01 v bezstavovém session beanu (příklad DAO objektu z prototypu aplikace SOB viz. kap. 5)
Pomocí anotace @PersistenceContext v kódu 4-2 bod (1) kontejner provede přijmou field inejction EM. Pokud nemáme žádné speciální požadavky na transakce, pak je tento kód vším, co musíme udělat, abychom mohli provádět základní CRUD operace s objektem ZavodT01. Aby tento kód úspěšně fungoval, musíme mít nadefinovanou perzistentní jednotku sob ( viz. kap 4.5 – kód 4-3). Pokud pracujeme s EM mimo EJB kontejner, pak musíme zajistit jeho vytvoření, práci s transakcí a popř. jeho uzavření. Tato manipulace je analogická
48
s prácí s JDBC. Analogie je především ve vynuceném vytváření obslužného kódu, který musí být obalen do bloku try-catch, který by se musel opakovat v každé metodě DAO (kód 4-2), abychom dosáhli stejnou funkcionalitu. Odsunutí obslužného kódu mimo obchodní metody můžeme mimo EJB kontejner řešit pomocí AOP, nebo použít Spring [32] a jeho podpůrnou třídu JpaDaoSupport. Problematika řízení transakcí, distribuované transakce, vícedatabázových zdrojů (perzistentních jednotek) je příliš rozsáhlá a není zde prostor pro podrobnější rozbor. Hlubší znalosti můžeme získat např. z knih [4], [5] a [6].
4.4 Nastavení JPA – persistence.xml (1) (2) <provider> oracle.toplink.essentials.PersistenceProvider (3) <jta-data-source>jdbc/sobtest (4) cz.datexhk.sob.entity.VolbaUlohyT00 cz.datexhk.sob.entity.ZavodT01 ... <properties > <property name="toplink.logging.level" value="FINE"/> (5) <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/sobtest"/> <property name="toplink.jdbc.user" value="root"/> <property name="toplink.jdbc.password" value=""/> <property name="toplink.target-database" value="MySQL4"/> -->
Kód 4-3 Ukázka nastavení souboru persistence.xml pro ORM framework TopLink
Jak již bylo několikrát zmíněno, JPA API je pouze standard a pro jeho chod musíme zajistit nějakou implementaci. Abychom si nezanášeli do naší aplikace žádné
závislosti
na
konkrétním
poskytovateli,
můžeme
vytvořit
soubor
persistence.xml (kód 4-4), který slouží pro konfiguraci JPA persistentní jednotky (persistence unit), a uložit ho do adresáře META-INF v class-path naší aplikace. Kód 4-3 demonstruje nastavení persistence.xml. Bod (1) definuje název perzistentní jednotky a typ transakce. V bodě (2) je definována třída poskytovatele implementace ORM frameworku (v našem případě TopLink). Bod (3) nastavuje
49
název datasource, který musí být definovaný na aplikačním serveru. Dále bodem (4) jsou definovány entitní třídy. V kódu 4-2 je ještě zakomentované nastavení přímého spojení z databází bez využití datasource (5). Pokud nám nevyhovuje tento zápis v xml, nebo bychom chtěli provést nastavení perzistentní jednotky např. podle nějaké systémové proměnné či vstupního parametru zadaného uživatelem, potom máme možnost provést definici persistentní jednotky přímo z námi vytvořené třídy (kód 4-4). public static void main(String[] args) { Map<String, String> props = new HashMap<String, String>(); props.put("toplink.jdbc.user", args[0]); props.put("toplink.jdbc.password", args[1]); EntityManagerFactory emf = Persistence.createEntityManagerFactory( "sobj", props); emf.createEntityManager(); } Kód 4-4 Nastavení perzistentní jednotky přímo ze aplikace.
50
5 Prototyp aplikace SOB Tato práce si klade za cíl nejen seznámit čtenáře s prostředky JEE, s jejichž využitím se dá vytvořit robustní, škálovatelná a bezpečná vrstva obchodní logiky podnikového informačního systému, ale taktéž vytvořit funkční prototyp aplikace, který by využíval teoreticky nabyté poznatky z kapitol (2, 3, 4) a sloužil tak jako příklad využití EJB v praxi. V následujících podkapitolách se seznámíme s prototypem aplikace SOB a popíšeme si některé technické problémy, které bylo nutné vyřešit při jeho vývoji a nasazování. Dále zvážíme, zdali je navržené řešení prototypu vhodné a efektivní pro vývoj celého systému.
5.1 Popis prototypu SOB Aplikace SOB (zkr. sklady a obaly), je vyvíjena firmou Datex spol. s.r.o. pro produkční nasazení v rámci závodu Škoda Mladá Boleslav provozované společností Škoda auto a.s.. Jejím účelem je sledování počtu a pohybu obalů na jednotlivých skladech (útvarech a dílnách) a efektivní řízení jejich skladování a odvozu. Prototyp SOB bude obsahovat pouze obslužné funkce systému SOB, jedná se o správy číselníků a nastavení aplikace. Prototyp SOB obsahuje část obchodní logiky reálné podnikové aplikace. Kompletní popis implementovaných funkcí prototypu včetně zpracovaných obrazovek vychází z výstupu druhé části analytické fáze projektu dle metodiky SEP, který se nalézá v příloze V.
5.2 Motivace tvorby prototypu Jedním z nejdůležitějších rozhodnutí při návrhu podnikových aplikací je volba vhodného frameworku(ů), která do značné míry ovlivní techniky a postupy při samotném vývoji systému. Aby mohl analytik rozhodnout, jaký framework má zvolit, je nutné se nejprve seznámit s možnostmi (výhodami, nevýhodami) jednotlivých frameworků. Požadavky z praxe obvykle ne zcela korespondují s ukázkovými příklady v jednotlivých tutoriálech, dokumentacích či odborné literatuře. Až při započetí vývoje se zpravidla začnou objevovat konkrétní problémy, které je nutné řešit a se kterými by původní návrh nemusel počítat. Proto bývá obvyklou praxí nejprve vytvořit tzv. prototyp úlohy, několik plně funkčních a otestovaných scénářů (use case), a rozhodnout se až na základě funkčního prototypu, zdali je možno pomocí navrhovaného řešení úspěšně dokončit celý systém. Aplikace SOB již běží v produkčním prostředí několik let v technologii ASP. V rámci podniku Škoda Mladá Boleslav dochází k restrukturalizaci aplikačních
51
serverů a definitivně se opouští platforma Microsoft, která má být kompletně nahrazena technologiemi postavenými na platformě Java. U aplikací běžících na platformě Micrososft ASP a ASP .NET musí dojít k migraci na platformu JEE, což prakticky znamená přepsat aplikace do Javy. Účelem tvorby prototypu SOB je především
seznámení
se
s doposud
nevyužívanými
Java
technologiemi
(frameworky) ve společnosti Datex a zvážit možnosti jejich využití při tvorbě nových systémů.
5.3 Architektura prototypu SOB Prototyp SOB uplatňuje principy jak vícevrstevné
(kap. 2.4.1) tak
komponentové architektury (kap. 2.4.2). Nejprve se seznámíme s technologiemi jednotlivých vrstev prototypu a následně si popíšeme komponentový pohled nasazení.
5.3.1 Prezentační vrstva Prezentační vrstvu můžeme ještě pro přehlednost rozdělit na klientskou vrstvu a webovou vrstvu. Klientská vrstva obsahuje webový prohlížeč (klient), který musí splňovat požadavky podnikového intranetu - specifikaci vizuálního stylu podnikových aplikací Škoda Mladá Boleslav. Proto jsou design a skript aplikace je optimalizovány pouze pro webový prohlížeč Internet Explorer 6 a 7 od společnosti Microsoft. Aplikace taktéž využívá asynchronní komunikaci klienta se serverem pomocí technologie AJAX, pro níž je využit javascriptový framework Prototype [23], který značně usnadňuje tvorbu požadavků získaných z formulářových polí na server. Webová vrstva je tvořena MVC frameworkem Stripes 1.5 [24], pomocí něhož jsou vyřešeny tyto požadavky:
Validace formulářových polí, převod formulářových polí na standardní Java datové typy (Boolean, Integer, Double, Date...).
Prezentace dat obchodní logiky do HTML. Tvorba šablon v JSP pro efektivnější tvorbu obrazovek (pohledů a formulářů).
Více jazyčnost – veškeré texty jsou uloženy v property souborech a pro přidání dalšího jazyka se nemusí zasahovat do žádné vrstvy aplikace.
Podpora asynchronní komunikace.
52
5.3.2 Střední vrstva Střední vrstva aplikace je tvořena službami obchodní logiky, doménovým modelem a perzistentní vrstvou. Služby obchodní logiky a perzistentní vrstva (DAO komponenty)
jsou implementovány
pomocí bezstavových
session beanů.
Doménový model tvoří JPA entity, které jsou namapovány na relační databázi. ORM framework poskytující služby JPA je Toplink [17].
5.3.3 EIS vrstva EIS vrstvu tvoří relační databáze MySQL 5.0 a databázové schéma bylo vygenerováno pomocí ORM frameworku.
5.3.4 Pohled nasazení
Schéma 5-1 Pohled nasazení prototypu SOB
Prototyp je nastaven a otestován pro Sun GlassFish (TM) Enterprise Server v2.1.131, který poskytuje kompletní JEE platformu. Schéma 5-1 znázorňuje pohled nasazení prototypu SOB. Aplikace se skládá ze tří zásuvných modulů, které jsou nasazeny v rámci aplikačního EAR: 1. SOB – web: Zásuvný modul starající se o přijímání požadavků od klienta a odesílaní dat klientovi prostřednictvím síťového protokolu HTTP. Tento modul je tvořen MVC frameworkem Stripes. 2. SOB business: Implementuje veškerou obchodní logiku aplikace a stará se o perzistenci dat prostřednictvím ORM do relační databáze. 31
Sever je k dispozici na http://java.sun.com/javaee/downloads/ (ověřeno k datu 24.10.2009)
53
Tento
zásuvný
modul
využívá
služeb
EJB
kontejneru
a
je
implementován pomocí EJB komponent a JPA API. 3. Datex Utils: Podpůrné knihovny usnadňující řešení některých neustále se opakujících postupů jako jsou např. práce s řetězci, třídění kolekcí, práce s reflexí atd.
5.4 Řešené problémy Tato podkapitola se věnuje vybraným problémům, které se vyskytly při implementaci prototypu SOB a popisuje jejich konkrétní řešení.
5.4.1 Vývojový nástroj a JEE platforma Před započetím samotné implementace bylo nutno zvolit prostředky (software), pomocí nichž by se dalo efektivně vyvíjet aplikaci. Jako vývojový nástroj byl zvolen open-source Eclipse WTP [26] a JEE platforma pro vývoj a nasazení prototypu byla vybrána Java EE 5 SDK32 [7] od společnosti Sun Microsystems. Důvodem této volby byla především předchozí dobrá zkušenost při vývoji aplikací s těmito prostředky, nulové pořizovací náklady, dostatek zdrojů (informací) a rozsáhlá komunita.
5.4.2 ORM reverse engineering Vzhledem k tomu, že aplikace SOB musí manipulovat s daty v již existujícím databázovém schématu, kterého používají i jiné aplikace, nebylo možné navrhnout doménový model a podle něj vygenerovat nové schéma, tak jak bývá běžnou praxí při tvorbě nových systémů. V úvahu přicházelo buď vytvořit nový doménový model a pokusit se ho namapovat na stávající databázové tabulky a v případě nutnosti přidat nějaké nové sloupce do tabulek existujícího databázového schématu kvůli ORM mapování, nebo ze stávajícího databázového schématu vygenerovat doménový model. Nakonec byla zvolena druhá varianta - ORM reverse engineering, tedy vygenerovat doménový model z již existujícího databázového schématu. Pro tento účel byl použit nástroj Hibernate Tools [25], který byl nainstalován jako plug-in do vývojového prostředí Eclipse WTP. Hibernate Tools umožňuje vygenerovat doménový model tvořený JPA entitami, kde každá entita odpovídá databázové tabulce. Takto vygenerovaný model reprezentuje pouze holá data bez jakékoli logiky, kterou je nutno doplnit. Velkou výhodou je nesporně vysoká úspora času, 32
JEE server Sun GlassFish Enterprise Server v2.1.1
54
která se při vývoji ušetří, protože není nutné veškeré vztahy (M:N, 1:N, M:1) mapovat ručně, a navíc jsme oproštěni od chyb plynoucích z překlepů, které se mohou vyskytnou při ručním mapování. Doménový model vytvořený pomocí reverse engineering trpí jednou zásadní slabinou. Vůbec nevyužívá možnosti dědičnosti mezi entitními třídami. Pokud bychom
chtěli
dědičnosti
doplnit,
znamenalo
by
to
nezbytnou
změnu
databázového schématu, která by se odvíjela od strategie, kterou bychom pro mapování dědičnosti zvolili (viz. kap. 4.2.3). V případě prototypu SOB byl entitní model vygenerován z existujícího databázového
schématu
běžícího
v ostrém
provozu
na
relační
databázi
Oracle 10g, a to především z důvodu, že provozovatelem databáze není povolena žádná změna či doplňování stávajícího databázového schématu o další tabulky či sloupce tabulek. Prototyp SOB obsahuje pouze 14 vygenerovaných entitních tříd nutných k implementaci části obslužných funkcí z celého počtu 92 databázových tabulek. Společně s entitními třídami byly vygenerovány i třídy reprezentující složené primární klíče. Schéma 9-1 (přílohy) ilustruje část databázového schématu a k němu vygenerovaný doménový model včetně tříd reprezentujících složené primární klíče. ORM reverse engineering rozhodně nepatří k postupům jak vytvořit plnohodnotný doménový model a je třeba mít na paměti, že takto vytvořený model zdaleka nemusí splňovat naše požadavky, jako jsou například nutné existenční podmínky, omezení vstupních parametrů set metodám, žádná vnitřní logika entity atd. Na druhou stranu je to velice rychlý způsob, jak vytvořit ke stávajícím databázovým tabulkám JPA entitní třídy, a tak prakticky ihned můžeme pracovat s jednotlivými řádky tabulek jako s objekty.
5.4.3 Integrace EJB a Stripes Jedním z klíčových úkolů bylo vyřešit integraci EJB s MVC frameworkem Stripes 1.5, který je využíván ve společnosti Datex pro tvorbu prezentační vrstvy podnikových aplikací. Konkrétně jde o problém inicializace EJB komponent do Stripes action beanů. V tomto případě není dependency injection možná, protože se nejedná o komponenty řízené přímo prostřednictvím JEE kontejneru. V úvahu přicházelo řešení přímo od tvůrců Stripes, tzv. EJB interceptor, který se nalézá v rozšíření Stripes EJB 3. Pomocí speciálních anotací je možné injektovat EJB beany přímo do Stripes action beanů.
55
Toto řešení má jednu dost podstatnou slabinu, a to že je závislé na konkrétním aplikační serveru. Hlavním důvodem je nejednotnost výrobců aplikačních serverů při definování JNDI názvů EJB beanů. To znamená, že pokud bychom chtěli přenést aplikaci na aplikační server od jiného výrobce, pak bychom museli veškerý kód prezentační vrstvy, kde se nacházejí anotace pro definici dependency inejction, upravit dle konkrétní JNDI implementace. Vnášet do každého Stripes action beanu, který využívá EJB komponent, závislost na konkrétním
aplikačním
serveru
není
příliš
vhodné
řešení
s přihlédnutím
k možnostem nasadit aplikaci do různých JEE prostředí. Nakonec bylo zvoleno obecnější řešení, jehož jádrem je vzor z JEE BluePrints (kap. 2.3) service locator a není nutné využívat rozšíření Stripes EJB 3. Řešení je obecné a je použitelné v jakékoliv komponentě uvnitř webového modulu, ne jen ve stribes action beanech. Základní myšlenkou bylo nakonfigurovat JNDI názvy na jednom místě tak, aby při změně JNDI názvů jak z důvodů migrace aplikace na jinou platformu, tak z důvodu změny vnitřních standardů pro názvy komponent, se nemuselo zasahovat do komponent rozmístěných napříč webovým modulem. @EJBs({ //Base EJBs @EJB(name=ILogin.NAME, beanInterface=ILogin.class), @EJB(name=ISeznamy.NAME, beanInterface=ISeznamy.class), //OBSL EJBs @EJB(name=IDodavatele.NAME, beanInterface=IDodavatele.class), ... @EJB(name=IZavody.NAME, beanInterface=IZavody.class), //OBSL DAOs @EJB(name=IZavodDao.NAME, beanInterface=IZavodDao.class), ... @EJB(name=IUtvarDao.NAME, beanInterface=IUtvarDao.class), }) public class EJBMappingServlet extends HttpServlet { ... } //-----Servlet mapping (web.xml) <servlet> <description> EJBMappingServlet <servlet-name>EJBMappingServlet <servlet-class> cz.datexhk.sob.utils.EJBMappingServlet
Kód 5-1 Ukázka servletu, který se postará o registraci EJB komponent do JNDI
Za tímto účelem byl vytvořen servlet, na který se zaregistrují veškeré EJB komponenty používané webovou vrstvou pomocí standardních anotací popsaných v kapitole 3. Kód 5-1 nahoře naznačuje způsob registrace EJB komponent na servlet a ve spodní části je naznačeno mapování servletu do souboru web.xml.
56
Všimněme si, že jednotlivá obchodní rozhraní EJB komponent obsahují konstantu
NAME sloužící jako definice názvu komponenty, který bude zaregistrován do JNDI aplikačního serveru. Takto zaregistrované EJB komponenty lze volat pomocí JNDI Iookup (viz. kapitola 3.5.2). Abychom nemuseli provádět JNDI lookup v každém stripes action beanu, kde je potřeba použít některou z EJB komponent, tak byla vytvořena třída
ServiceLocator (kód 5-2) podle stejnojmenného návrhového vzoru. public class ServiceLocator { private static ServiceLocator me = null; private Context context; (1) private Map<String, Object> cache; (2)
private ServiceLocator(){ try { this.context = new InitialContext(); cache = new HashMap<String, Object>(); } catch (NamingException e) { e.printStackTrace(); throw new IllegalStateException(e); } this.cache = new HashMap<String, Object>(); }
(3)
public static ServiceLocator getInstance(){ if(me==null) me = new ServiceLocator(); return me; }
(4)
public T getLocal(Class type,String name){ try { T local; if(cache.containsKey(type.getSimpleName())){ local = (T) cache.get(type.getSimpleName()); }else{ //Only for glassfish local = (T) context.lookup("java:comp/env/"+name); cache.put(type.getSimpleName(), local); } return local; }catch (NamingException e) { e.printStackTrace(); throw new IllegalStateException(e); } }
} //---------Ziskani reference na EJB komponentu IZavody.NAME (5) IZavody zavody = ServiceLocator.getInstance(). getLocal(IZavody.class, IZavody.NAME);
Kód 5-2 Implementace návrhového vzoru service locator pro JEE server Sun GlassFish Enterprise Server v2.1.1 a ukázka získání reference na EJB komponentu
Pojďme se podrobněji zamyslet nad zdrojovým kódem třídy ServiceLocator (kód 5-2). V první řadě je třeba zmínit, že třída ServiceLocator má pouze privátní konstruktor (2) a její instanci lze získat pouze statickou metodou (3). Tato konstrukce odpovídá návrhovému vzoru singleton (viz. kap 2.3.3). Atribut třídy 57
cache (1) typu Map slouží k uložení reference na EJB komponenty. Instanci rozhraní EJB komponenty, v našem případě IZavody, získáme metodou getLocal (4). Kompletní volání metody getLocal je uvedeno bodem (5). Jednoznačné výhody používání ServiceLocatoru k zajištění instancí rozhraní EJB komponent v komponentách, které nespravuje EJB kontejner, jsou: 1. Nemusíme v každé třídě, ve které potřebujeme inicializovat EJB komponentu, vytvářet Context a volat metodu lookup, která si vynutí obalení kódu do bloku try – catch tak jako v metodě (4), což by zbytečně znepřehledňovalo a natahovalo zdrojový kód. 2. Pokud potřebujeme změnit úplné JNDI jméno, které je závislé na konkrétním aplikačním serveru, tak to provedeme na jednom místě (4). 3. Volání JNDI lookup z každé komponenty zvlášť není příliš efektivní a opakované volání může značně zatěžovat JEE server. Metoda
getLocal (4) volá JNDI lookup pro každou EJB komponentu pouze jednou a okamžitě její referenci uloží do mapy cache (1). Pokud reference v mapě existuje JNDI lookup se už neprovádí a návratová hodnota je získána přímo z cache. Webový modul prototypu SOB používá pouze lokálních EJB komponent, proto je ve třídě ServiceLocator pouze metoda getLocal. V případě nutnosti nasadit některé EJB komponenty na jiné zařízení by stačilo např. přidělat metodu vracející vzdálené EJB instance. public final class ObslServiceFactory { private ObslServiceFactory(){} public static IZavody getZavody(){ return ServiceLocator. getInstance().getLocal(IZavody.class, IZavody.NAME); } public static IUtvary getUtvary(){ return ServiceLocator. getInstance().getLocal(IUtvary.class, IUtvary.NAME); } ... } //---------Ziskani reference na EJB komponentu IZavody.NAME (1) private IZavody zavody = ObslServiceFactory.getZavody();
Kód 5-3 Tovární třída poskytující EJB komponenty obslužných funkcí
Stripes action beany nevolají přímo ServiceLocator, jak je to uvedeno v kódu 2-5 (5). Pro tento účel byly vytvořeny tzv. tovární třídy, které obsahují pouze
58
statické metody vracející příslušné instance EJB komponent. Kód 5-3 obsahuje část tovární třídy pro obslužné funkce. Třída ObslServiceFactory je tzv. knihovní třídou33. Její instance se nedá vytvořit a slouží pouze k volání statických metod. Bod (1) naznačuje jakým způsobem
se
v Stripes
action
beanech
získává instance rozhraní
EJB
komponenty. Tvorbou takovýchto továrních tříd odstiňujeme Stripes action beany od způsobu získání instance rozhraní. Pokud bychom se někdy v budoucnu rozhodovali, že pro některou službu nebudeme používat jinou implementaci rozhraní (nikoli EJB), nebo do ServiceLocatoru přidáme další metodu pro zisk vzdálené EJB komponenty, pak jednoduše stačí změnit tělo příslušné statické metody v tovární třídě. Závěrem si pro přehlednost shrneme řešení integrace Stripes MVC a EJB. Nejprve byl vytvořen servlet pro registraci EJB komponent do JNDI. Následně byl použit návrhový vzor service locator pro získání instance rozhraní EJB komponenty a na závěr byly vytvořeny tovární třídy odstiňující Stripes action beany od způsobu získání instance. Toto řešení je obecné a mělo by fungovat ve všech komponentách běžících v JEE serveru. To znamená, že tímto způsobem lze integrovat EJB i s jiným webovým frameworkem (např. Struts 1, Struts 2, Webwork...) nebo pouze se servlety.
5.4.4 Nasazení protypu do různých prostředí Jedním z požadavků bylo zjistit, zdali jsou technologie použité při implementaci prototypu nezávislé na cílové platformě, tzn. zda je výsledný prototyp snadno přenositelný mezi různými JEE servery. Pro testování byly použity dva JEE servery GlassFish Enterprise Server v2.1.1 (dále jen GlassFish) a JBoss 5.0.0.GA (dále jen JBoss) běžící v rámci JVM Java HotSpot(TM) Client VM 1.6.0_01b06,Sun Microsystems Inc. na operačním systému Windows XP 5.1,x86 Pro přehlednost si celou problematiku rozdělíme dle standardních tří vrstev JEE aplikací. 5.4.4.1 Klientská vrstva Klinská vrstva zasahuje na aplikační server, kde je reprezentována zásuvným modulem SOB-WEB a je tvořena pomocí frameworku Stripes MVC. Pro využití frameworku Stripes je nezbytné přidat k webovému modulu příslušné knihovny34, které nikterak nekolidují se serverovou platformou a webová vrstva je plně funkční 33 34
knihovní třída obsahuje v deklaraci klíčové slovo final a pouze privátní konstruktor. knihovny jsou volně dostupné na [24]
59
na obou
JEE serverech.
Problém
nastává s registrací
EJB komponent
prostřednictvím servletu (kód 5-1) a následném volání ServiceLocatoru (kód 5-2). Implementace JNDI služeb JEE serverů není jednotná. Kódy 5-1 a 5-2 jsou upraveny pro GlassFish a pro úspěšné nasazení webového modulu na JBoss je nutné upravit třídy EJBMappingServlet a ServiceLocator dle jmenné konvence JNDI serveru JBoss. Obdobný problém by nastal i při přenosu prototypu na další JEE servery. 5.4.4.2 Střední vrstva Střední vrstva je tvořena EJB komponenty zapouzdřující služby obchodní logiky a JPA entitními třídami reprezentujícími doménový model. Dependeny inejction v rámci jednotlivých EJB komponent funguje velice dobře a dá se říci, že je platformě nezávislé. Ovšem co se týče samotné JPA tak přenositelnost není stoprocentní. Hlavním důvodem je to, že jednotlivé servery nemají stejnou implementaci JPA – JBoss používá Hibernate [18] a GlassFish Toplink [17]. V tom by dle JEE specifikace neměl být zásadní problém a je možné, že ve většině případů je přenositelnost bezproblémová. Jako konkrétní problém můžeme například zmínit automatickém generování primárních klíčů, jejichž generování není závislé na předem vytvořené databázové sekvenci35. Aplikace fungovala pouze s JPA implementací Hibernate a pro úspěšné zprovoznění aplikace s JPA implementací Toplink bylo třeba provést doplnění anotace atributu v anotaci @GeneratedValue na primárním klíčem na
@GeneratedValue(stratehgy = GenerationType.IDENTITY). Při snaze vytvořit nový objekt s takto generovaným primárním klíčem docházelo k potížím s jeho opětovným čtením. Tento problém se odstranil pomocí zavolání metody flush na instanci EntityManageru, kterou je třeba zavolat před prvním čtením takto uloženého objektu. Celá problematika je podrobněji popsaná na stánkách [27]. 5.4.4.3 EIS Vrstva Posledním testem byla snaha ověřit, jestli lze provést změnu databáze, aniž by bylo nutné jakkoliv měnit střední vrstvu aplikace. Jako pokusné databáze byly zvoleny Oracle 10g a MySQL 5.0. Testování se provádělo tak, že se z entitního modelu vygenerovalo nové databázové schéma a poté se naplnilo zkušebními daty. Bylo třeba dodat konkrétní JDBC ovladače a vytvořit pro každou databázi
35
V případě MySQL se primárnímu klíči přiřadí vlastnost auto-increment označovaná v ORM jako sequence identity. Tuto vlastnost nepodporují všechny relační databáze.
60
nový datasource. Zmíněný postup byl vyzkoušen pouze na aplikačním serveru GlassFish. Potíže se vyskytly při generování primárních klíčů, kdy databáze Oracle využívá
jinou
strategii
GenerationType.SEQUENCE,
zatímco
MySQL
GenerationType.IDENTITY. Po nutných změnách v anotacích nad automaticky generovanými primárními klíči nebyly zaznamenány žádné další potíže. Střední vrstvu prototypu SOB se díky JPA podařilo do značné míry odstínit od databázového úložiště. 5.4.4.4 Shrnutí Všechny prostředky, které byly použity pro vybudování střední vrstvy aplikace, jsou součástí specifikace JEE. Žádná jiná API nad rámec JEE specifikace nebyla použita, a přesto se nedá říci, že je aplikace bezproblémově přenositelná mezi jednotlivými JEE platformami bez nutných zásahů do zdrojového kódu tak, jak bychom mohli dle JEE specifikace předpokládat. Za největší problém v tomto smyslu spatřuji nejednotnost jmenných konvencí JNDI jednotlivých výrobců JEE serverů. Částečným problémem může být i rozdílná implementace JPA, ale narozdíl od JNDI můžeme s aplikací použít jiný námi zvolený ORM framework. Úplné odstínění střední vrstvy aplikace od relační databáze nezávisí pouze na vlastní implementaci JPA, ale také na tom, jak kvalitně je zpracován JDBC driver a zdali chceme využít některých databázových služeb nad rámec standardního SQL. Z těchto důvodů se nepodařilo stoprocentně vytvořit platformě nezávislou aplikaci.
5.4.5 Efektivní vývoj a testování Jedním z hlavních důvodů, proč mnoho JEE programátorů nepreferuje používání JEE serverů při vývoji aplikace, jsou jejich hardwarové nároky. Při každé změně zdrojového kódu je nezbytné provést nové nasazení aplikace na server. V praxi to znamená vybudovat nový EAR ze zdrojových kódů a zkopírovat ho do složky aplikačního serveru. Díky vývojovému prostředí se tento postup zajistí automaticky36. Při rozsáhlejší aplikaci a slabší pracovní stanici muže tento proces trvat i několik desítek vteřin. Aplikace je pak znovu spuštěna, čímž se kompletně ztratí session uživatele. Pokud budeme chtít provést drobnou změnu ve zdrojovém kódu a následně ji ve webovém prohlížeči otestovat, nezbude nám nic jiného, než se k zmíněné obrazovce znovu „proklikat“. Pokud je v aplikaci už řešena 36
Většina vývojových prostředí používá ANT nebo MAVEN k vybudování aplikace. Tyto nástroje si můžeme sami nakonfigurovat tak, aby splňovali naše požadavky.
61
autorizace uživatele, tak je nutno se ještě přihlásit. Je zcela pochopitelné, že tento proces, obzvláště když je prováděn několikrát za hodinu, muže přivádět vývojáře téměř k šílenství. Místo aby snadno a rychle ověřili, zdali požadovaná funkce dělá skutečně pouze to co má, a následně se zabývali dalším úkolem, tráví desítky minut v rámci pracovní doby neefektivním „proklikáváním“ aplikace. V následujících dvou podkapitolách si uvedeme jakými způsoby a prostředky je možné celý proces vývoje značně zefektivnit. 5.4.5.1 Java agent JRebel Jedním ze způsobů, jak značně omezit nutnost neustálého nasazování aplikace na JEE server po každé změně zdrojového kódu, je využít tzv. Java agenta v rámci JVM. Při tvorbě prototypu byl vyzkoušen JRebel [28], který velice dobře spolupracoval jak s vývojovým prostředím Eclipse, tak s aplikačním serverem GlassFish. Tento produkt je komerční a zdarma pro jakékoliv účely je možné ho využívat pouze po dobu třiceti dnů, což je dostatečná doba na zvážení, zdali je tento produkt přínosem pro vývoj firemních aplikací. Pokud se na cílové stanici dennodenně spouští JEE server za účelem vývoje a testování aplikací, pak může používání Java agenta JRebel přinést značné úspory. Na oficiálních webových stránkách produktu [28] se tvrdí, že ušetřený čas se pohybuje mezi osmi až osmnácti procenty. Na trhu samozřejmě existuje i celá řada open-source produktů. Nutno podotknout, že se jedná vždy pouze o částečná řešení jako jsou např. (Hot deploy, HotSwap aj.) a s komplexností JRebelu se nemohou srovnávat. JRebel umožňuje například přidávat do tříd nové metody, atributy, měnit rozhraní, přidávat a ubírat parametry metod a to tak, aniž by se musel provést restart aplikace. JRebel funguje velice dobře i s EJB komponentami nasazenými v rámci EJB kontejneru. 5.4.5.2 Testování V dnešní době je už nezbytnou součástí vývoje jakýchkoli aplikací jejich řádné otestování. V různých metodikách se doporučuje testovat software již při prvním započetí jeho vývoje a každá další iterace by měla obsahovat testovací fázi. Testovat aplikaci, až když je kompletní, se může značně prodražit, a to především, pokud se nalezne nějaká chyba, kvůli které se bude muset změnit vnitřní infrastruktura. Test-driven development (TDD) zachází ještě dále, protože se nejprve vytvoří testy a až následně se programuje vlastní implementace. Díky
62
automatizovanému testování nemusíme pokaždé vyplňovat formulářové pole a odesílat data na server, abychom zjistili, zda náš program správně funguje. Samotné testování můžeme rozdělit na jednotkové testy spouštěné bez JEE serveru a na testy běžící uvnitř JEE serveru. Na testování základní funkcionality obchodních logiky EJB komponent či doménových tříd můžeme použít jednotkové testy pomocí knihovny JUnit[29], která je k dispozici ve vývojovém prostředí Eclipse včetně GUI k vyhodnocování jednotlivých testů. Pokud potřebujeme simulovat některé objekty jako například HttpSession, které jsou nutné ke správnému otestování komponent, a přesto nechceme spouštět testy na serveru, můžeme využít např. knihovnu EasyMock[30], která nám umožní simulovat objekty z webového kontejneru. Pro testy spouštěné přímo z JEE kontejneru existuje knihovna Cactus[31], která rozšiřuje JUnit a slouží k testování JEE komponent, jako jsou např. servlety, filtry, EJB komponenty aj. přímo v jejich běhovém prostředí.
5.5 Zhodnocení prototypu SOB Cílem tvorby prototypu SOB bylo vytvořit funkční část aplikace vycházející z reálných požadavků zákazníka a zjistit, zdali se v technologiích popsaných v kapitole 5.3 dá vytvořit celý systém SOB s důrazem na vhodnost využití EJB a JPA. První část cíle byla splněna a prototyp je plně funkční. Můžeme konstatovat, že za pomocí technologie EJB a JPA lze vybudovat obchodní logiku aplikace, kterou je možno integrovat s MVC Stripes. Nyní je třeba zvážit, zdali je navrhované řešení vhodné pro implementaci celého systému. V kapitole 5.4 byly popsány zásadní problémy (úkoly), které se vyskytly při tvorbě prototypu a nutno podotknout, že se je všechny podařilo více či méně úspěšně vyřešit. Otázkou zůstává, zadli není využití technologie EJB v tomto případě oním pověstným „kanónem na vrabce“ s přihlédnutím k faktu, že aplikace obsahuje pouze bezstavové session beany a neřeší žádné složité non-funkční požadavky. Dle mého názoru ano. Pro podporu služeb obchodní logiky tohoto typu aplikace bych preferoval tzv. light-weight řešení pomocí Springu[32], a to především z důvodu větší nezávislosti na konkrétní JEE platformě, snadnou přenositelnost aplikace, možnost vývoje na samostatném webovém kontejneru jako je např. Tomcat (viz. kap. 2.2.3) a do značné míry i snadnější testování. Na druhou stranu je nutno dodat, že řešení pomocí EJB je značně modulární a
63
jednotlivé komponenty by šlo velice snadno volat i z jiných aplikací, které by tak mohly využívat některých služeb SOB. Při použití JPA jako prostředku ORM jsme se nesetkali s žádnými omezujícím faktory, které by nás nutily použít nějakou konkrétní ORM implementaci nebo přímo JDBC. JPA lze použít samostatně mimo JEE kontejner a s největší pravděpodobností bude využita pro manipulaci s persistentními daty v celém systému SOB. Díky poznatkům získaných při studiu a osobním zkušenostem bych doporučoval volit EJB pokud naše požadavky znějí:
Tvorba rozsáhlejšího distribuovaného systému, jehož jednotlivé komponenty by bylo nutné rozmístit na různá zařízení.
Tvorba systému, který by musel pracovat s vícedatabázovými zdroji součastně (řešení distribuovaných transakcí).
Asynchronní komunikace, implementace zprávových systémů
Tvorba obchodních služeb pro různé typy klientů (tenký klient, tlustý klient, jiná aplikace aj, webová služba).
5.6 Poučení o manipulaci s prototypem SOB Prototyp aplikace SOB používá design, který je ve výhradním vlastnictví společnosti Škoda Auto a.s. Jakékoliv další používání tohoto designu nad rámec této diplomové práce je bez výslovného souhlasu majitele zakázáno. Názvy, obrázky a další informace použité v prototypu SOB mohou být obchodními značkami firem, ochrannými známkami, patentovanými označeními, komerčními produkty, nebo majetkem ve vlastnictví komerčních firem a jsou chráněny podle zákona.
64
6 Závěr Cílem této práce bylo seznámit se s možnostmi frameworku EJB 3.0 a díky teoretickým poznatkům vytvořit prototyp aplikace na základě reálných požadavků zákazníka a zvážit, zdali pomocí kombinace EJB lze úspěšně a efektivně vybudovat celou aplikaci. Pomocí EJB 3.0 byly implementovány veškeré požadavky aplikační logiky kladené na prototyp. Avšak dospěli jsme k názoru, že pro tento typ aplikace, která nevyžaduje žádné speciální non-funkční požadavky, by bylo vhodnější využít nějakého ligh-weight řešení např. s využitím Springu, a to především z důvodu přenositelnosti aplikace a snadnějšímu testování v průběhu vývoje. Můžeme konstatovat, že všechny vytyčené cíle práce byly splněny. Pravdou zůstává, že pro bližší seznámení s některými pokročilejšími technikami v rámci EJB a JPA by bylo nutné pracovat na prototypu s odlišnými non-funčními požadavky. Toto však již nebylo předmětem zkoumání.
65
7 Slovník pojmů AJAX
zkr. Asynchronous JavaScript and XML - umožňuje asynchronní komunikaci mezi klientem a serverem
AOP
zkr. Aspect-Oriented Programming
API
složení slov application programming interface je množina funkcí, procedur, metod, tříd nebo protokolů, kterým operační systém, knihovna nebo služba zajišťuje podporu požadavků vytvořených počítačovým programem.
ASP
zkr. Active Server Pages – technologie od společnosti Microsoft pro GUI internetových aplikací (obdoba JSP).
cluster
Shluk výpočetních jednotek pracujících paralelně a společně za určitým cílem.
clustering
Obecně práce s clusterem.
CRUD
Create Read Update Delete
DAO
Data acces object
dependency inejction
viz. IOC
EIS
Zkratka slov Enterprise information systém - obecně datový zdroj poskytující data aplikaci (např. relační databáze, či jiný informační systém)
EJB
zkr. Enterprise Java Beans – komponentový framework pro tvorbu střední vrstvy aplikace v rámci JEE.
Enterprise aplikace
vícevrstevná škálovatelná distribuovaná podniková aplikace, tzv. tenký klient, kdy veškerá aplikační logika běží na jednom či více aplikačních serverech
GUI
zkratka z Angl. graphical user interface - grafické uživatelské rozhraní, které zprostředkovává komunikaci mezi klientem a systémem
HTTP
zkr. Hypertext Transfer Protocol – protokol určený ke komunikaci mezi serverem a klientem (webovým prohlížečem).
IOC
zkr. Inversion of Control – Návrhový vzor umožňující svazovat komponenty neinvazivním způsobem. Nejčastěji je tento vzor zabudován v kontejneru a jeho využití je k dispozici jako tzv. kontejnerová služba.
66
Java SE
Java Standard Edition - základní verze jazyka Java pro vývoj a spouštění desktopových aplikací
JDBC
zkr. Java Database Connectivity – API podporující práci s relační databází.
JMS
zkr. Java Message Service asynchronní komunikaci.
JNDI
zkr. Java Naming Directory Interface – API zpřístupňující jmenné služby serveru pro získávaní zaregistrovaných objektů v jmenném adresáři JEE serveru.
JPA
zkr. Java Persistence API – specifikace pro manipulaci s objekty, které se trvale ukládají do relační databáze.
JPQL
zkr. Java Persistence Query Language – platformě nezávislý dotazovací jazyk specifikovaný JPA a sloužící pro práci s perzistentními objekty.
JSF
zkr. Java Server Faces – komponentový framework, který je součástí JEE specifikace.
JSP
zkr. Java Server Pages – technologie umožňující tvorbu GUI prostřednictvím satických (X)HTML prvků a dynamických prvků.
JSTL
zkr. Java Standard Tag Library – knihovna pro podporu a usnadnění tvorby JSP.
JVM
zkr. Java Virtual Machine –běhové prostředí pro Java aplikací.
load-balancing
Postupy pro vyrovávání zátěže mezi jednotlivými servery v rámci clusteru.
localhost
Označení místní pracovní stanice, na které se jak provádí vývoj, tak běží aplikační server.
MDB
zkr. Message-driven Bean – serverové EJB komponenty určené pro asynchronní komunikaci.
MOM
zkr. Message oriented middleware - prostředník při asynchronní komunikaci.
OOP
zkr. Object Oriented Programming orientované programování.
open-source
Je software, který má zveřejněny zdrojové kódy a je možné ho bezplatně využívat a upravovat dle vlastních potřeb.
OQL
zkr. Object Query Language – obecné označení
67
–
API
podporující
–
MVC
objektově
jakéhokoliv objektově orientovaného jazyka jako je např. JPQL
dotazovacího
POJO
zkr. Plain Old Java Object – je definován třídou, která musí obsahovat bezparametrický konstruktor a všechny atributy třídy musejí mít veřejné get a set metody a samotná třída musí být serializovatelná.
RUP
zkr. Rational Unified Process – metodika firmy Rational Software Corporation, která pokrývá všechny etapy životního cyklu při vývoji software. Etapy jsou např. sběr požadavků, analýza, návrh, implementace, nasazení.
SFSB
zkr. Stetefull session bean – serverové EJB komponenty udržující stav klienta.
SLSB
zkr. Stateless session bean – EJB komponenty.
TCP/IP
zkr. Transmission Control Protocol/Internet Protocol – protokol určený pro komunikaci jednotlivých stanic po síti.
UML
zkr. Unified Modeling Language – vizuální jazyk pro zachycení softwarových procesů, požadavků a architektury.
XML
zkr. eXtensible Markup Language – obecný standardizovaný značkovací jazyk, určený pro výměnu a ukládání informací. Výhodou je, že je na rozdíl od streamů může číst a upravovat člověk. Nevýhodou je nesporně větší množství takto přenášených dat.
Zprávový systém
Systém postavený na asynchronní komunikaci dvou či více jednotek, které jsou si v komunikaci rovni – tzv. peer-to-peer komunikace.
68
bezstavové serverové
8 Seznam použité literatury [1]
JENDROCK, Eric, et al. The Java™ EE 5 Tutorial : Third Edition. Massachusetts : [s.n.], 2007. 1344 s. ISBN 0-321-49029-0.
[2]
KODALI, Raghu R. , WETHERBEE, Jonathan, ZADROZNY, Peter. Beginning EJB™ 3 Application Development : From Novice to Professional. New York : [s.n.], 2006. 510 s. ISBN 1-59059-671-4.
[3]
BURKE, Bill, MONSON-HAEFEL, Richard. Enterprise JavaBeans, 3.0. [s.l.] : O'Reilly, 2006. 760 s. ISBN 0-596-00978-X.
[4]
PANDA, Debu, RAHMAN, Reza, LANE, Darek. EJB3 in Action. [s.l.] : Manning Publications Co., 2007. 705 s. ISBN 1-933988-34-7.
[5]
SRIGANESH, Rima Patel, BROSE , Gerald , SILVERMAN, Micah. Mastering Enterprise JavaBeans™ 3.0. 4th edition. Indianapolis : Wiley Publishing, Inc, 2006. 720 s. ISBN 0-471-78541-5.
[6]
MERRICK, Mike , SCHINCARIOL, Keith . Pro EJB 3 : Java Persistence API. New York : Apress, 2006. 475 s. ISBN 1-59059645-5.
[7]
Java EE [online]. Sun Microsystems, Inc., 1994-2009 [cit. 2009-0226]. Dostupný z WWW: .
[8]
Java BluePrints : Guidelines, patterns, and code for end-to-end applications [online]. Sun Microsystems, Inc., 1999-2009 [cit. 200903-01]. Dostupný z WWW: .
[9]
Apache Tomcat [online]. The Apache Software Foundation, 19942009 [cit. 2009-03-11]. Dostupný z WWW: .
[10]
SINGH, Inderjeet, STEARNS, Beth, JOHNSON, Mark. Design Enterprise Applications: with the J2EETM Platform, Second Edition. [s.l.] : Sun Microsystems, Inc., 2002. 440 s. ISBN 0-201-78790-3.
[11]
Java BluePrints Solutions Catalog [online]. Sun Microsystems, Inc., 2004 [cit. 2009-03-13]. Dostupný z WWW: .
[12]
J2EE Patterns Catalog [online]. Sun Microsystems, Inc., 2002 [cit. 2009-03-15]. Dostupný z WWW: .
[13]
J2EE Patterns [online]. Sun Microsystems, Inc., 1994 - 2009 [cit. 2009-03-15]. Dostupný z WWW: .
[14]
GAMA, Erich, HELM, Richard, JOHNSON, Ralph, VLISSIDES, John. Design Patterns. Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. 396 s. ISBN 0-201-30998-X.
69
[15]
FOWLER, Martin. AnemicDomainModel [online]. 25.11. 2003 [cit. 2009-10-11]. Dostupný z WWW: .
[16]
EVANS, Eric. Domain-Driven Design : Tackling Complexity in the Heart of Software. [s.l.] : Addison-Wesley, 2003. 529 s. ISBN 0321-12521-5.
[17]
Oracle TopLink [online]. 2003-2009 [cit. 2009-10-25]. Dostupný z WWW: .
[18]
Hibernate [online]. c2009 [cit. 2009-10-25]. Dostupný z WWW: .
[19]
JBoss Enterprise Web Server [online]. c2009 [cit. 2009-10-25]. Dostupný z WWW: .
[20]
WebSphere software [online]. c2009 [cit. 2009-10-25]. Dostupný z WWW: .
[21]
Rational Unified Process : Best Practices for Software Development Teams. [s.l.] : [s.n.], c1998. 21 s. Dostupný z WWW:
[22]
ARLOW, Jim, ILA, Neustadt. UML a unifikovaný proces vývoje aplikací : Průvodce analýzou a návrhem objektově orientovaného softwaru. Brno : Computer Press, 2003. 387 s. ISBN 80-7226-947-X.
[23]
Prototype JavaScript framework : Easy Ajax and DOM manipulation for dynamic web applications [online]. 2006-2007 [cit. 2009-09-28]. Dostupný z WWW: .
[24]
Stripes Framework [online]. 2007 [cit. 2009-09-29]. Dostupný z WWW:
[25]
Hibernate Tools [online] 2009 [cit. 2009-09-29]. Dostupný z WWW: https://www.hibernate.org/hib_docs/tools/reference/en/html/
[26]
Eclipse org [online] c2009 [cit. 2009-09-27]. Dostupný z WWW: http://www.eclipse.org/
[27]
Retrive autoincrement value with MySQL [online] c2007 - 2009 [cit. 2009-09-30]. Dostupný z WWW: http://markmail.org/message/sreshw6etqfavk54
[28]
JRebel [online] c2009 [cit. 2009-10-20]. Dostupný z WWW: http://www.zeroturnaround.com/jrebel/
[29]
JUnit.org : Resources for Test Driven Development [online] c2009 [cit. 2009-10-20]. Dostupný z WWW: http://www.junit.org/
[30]
EasyMock [online] c2009 [cit. 2009-10-20]. Dostupný z WWW:
70
http://easymock.org/ [31]
Jacarta Cactus [online] c2009 [cit. 2009-10-20]. Dostupný z WWW: http://jakarta.apache.org/cactus/
[32]
SpringSource.org [online] c2009 [cit. 2009-10-20]. Dostupný z WWW: http://www.springsource.org/
[33]
Aspect J : Crosscutting objects for better modularity [online] c2009 [cit. 2009-11-05]. Dostupný z WWW: http://www.eclipse.org/aspectj/
71
9 Přílohy I.
Soubor source.zip - zdrojové kódy prototypu SOB.
II.
Soubor
SQL_Script_DB.txt
-
skript
pro
vytvoření
databázového schématu sobtest pro DB MySQL a testovací data. III.
Soubor SOB-EA.EAR modul pro nasazení prototypu SOB na JEE server GlassFish Enterprise Server v2.1.1
IV.
Soubor nasazeni.doc Popis nasazení prototypu
V.
SOB_Pflichtenheft..doc – popis funkcí prototypu SOB
72
Schéma 9-1 Zleva část databázového schématu alikace SOB a zprava entitní třídy odpovídající tomuto schématu. Jednotlivé třídy jsou označeny číslem odpovídajícím číslu příslušné tabulky.Např. tabulka SP03T42 odpovídá třídě GtCisloT42. Pozn. názvy tabulek vycházejí z jmenné konvence útvaru EOT závodu Škoda Mladá Boleslav.
73
Schéma 9-2 Úvodní obrazovka prototypu SOB. Při používání prototypu je uživatel označen jako Uknow User s roli SPRAVCE, a proto jsou k dispozici veškeré funkcionality systému.
74
Schéma 9-3 Prototyp obsahuje řadu obrazovek obsahující tabulky dat s vybudovaným stránkováním a možností libovolného složeného třízení, dle jednotlivých sloupců tabulky,které se dá definovat pomocí odkazů v záhlaví sloupců.
75