Když jsem poprvé začal používat plnohodnotné IDE, nejvíce jsem si zamiloval NetBeans. Důvodem bylo zejména to, že pro začátečníka byl tento nástroj asi nejsnáze pochopitelný. Postupem času si člověk vytvoří určité návyky a jen težko je schopen přejít na jiné prostředí.
NetBeans jsem používal posledních 5 let. Což už je dost dlouhá doba na to, abych věděl co od tohoto nástroje můžu očekávat. Nicméně v poslední době se mi zdá, že více než na práci se vývoj tohoto nástroje soustředí na to, jak vytvořit demo aplikaci. Ta je poté slavnostně prezentována ve formě videotutoriálu na stránkách netbeans.org. Ovšem při práci na větším projektu se často dostávám do potíží se samotnou stabilitou tohoto prostředí. Proto jsem se rozhodl vyzkoušet IntelliJ IDEu, kterou všichni její uživatelé tak slavně opěvují.
Při prvním spuštění je jasné, že i když oba nástroje použiji na tu stejnou věc (programování Java, PHP), tak přesto jsou dost rozdílné, abych hned věděl kde začít.
Projekty a moduly
První velký zádrhel je mezi porovnání projektů a modulů. v NetBeans vytvořím nový projekt a rovnou můžu pracovat. Onen projekt můžu například zabalit do EAR projektu a přidávat další a další. IDEA na to jde jinak. Projektem je zde spíše něco jako skupina, do které vlkádám dané projekty (ehm, tedy moduly). Modulem je tedy onen projekt a projekt je spíše celé okno IntelliJ IDEy. Trošku zmatek, nicméně se mi podařilo tímto prokousat a pochopit logiku tohoto nástroje. :)
PHP Projekt (nebo modul?)
Projekty a moduly pro Javu jsem vcelku pochopil. Horší je to ovšem s PHP. IntelliJ IDEA má slušnou podporu PHP a proto jsem chtěl vyzkoušet i tu. Problém ovšem je, že neexistuje nabídka jak vytvořit čistě PHP modul. Jediný způsob je vytvořit "projekt ze zdroje" a poté do něj java modul bez vytvoření src adresáře. Tento způsob je přímo popisovaný i v nápovědě tohoto nástroje. Asi nemá příliš cenu komentovat, proč tento způsob vývojáři trošku neupraví. Nicméně, chceš-li pracovat s PHP, vytvoř Java Modul :)
Přenos projektu z NetBeans do IntelliJ IDEy
Po menším prozkoumání tohoto nástroje jsem hledal způsob, jak rozumně převést projekt z NetBeans do IDEy. Jelikož jsem v NetBeans projekt sestavoval pomocí Antu (ano, vím, že už to není moderní :)) nenašel jsem jednoduchý způsob, jak projekt převést. Nezbývalo tedy nic jiného, než použít Maven. Po dvoudenním trápení se mi podařilo sestavovat celý projekt pomocí Mavenu. Kromě toho, že jsem toto měl provést již dávno, tak přenos projektu byl více než snadný. Nyní stačilo otevřít IntelliJ IDEu a pouze projekt "otevřít". Vše začalo fungovat bez problémů.
Nevýhody IDEy
Nemá smysl psát o výhodách tohoto nástroje. Je totiž vážně slušně propracován a navíc splňuje téměř vše, co ke své práci potřebuji. Ovšem i přesto existují určité věci, které stále nejsem schopen překousnout a nutí mě přemýšlet, zda mám tento nástroj skutečně koupit a začít používat.
Logování Glassfish
Při deploy (debugování) aplikace na Glassfish je problém s tím, že nefunguje zobrazování logování v okně. Toto bylo u NetBeans skutečně na mnohem lepší úrovni. Nakonec jsem zjistil, že i když je v nastavení tohoto aplikačního serveru nastavena log console, musím přidat vlastní. Poté základní logování funguje. Ovšem pouze do doby, než se provede "rotate" log, tedy provede se záloha log souboru a vytvoří se nový glassfish log soubor. IDEA ovšem není schopná pokračovat ve výpisu tohoto logu z čistého souboru.
Podpora Mavenu
NetBeans má podporu Mavenu na lepší úrovni. U knihoven je přímo vidět zda se jedná o závislost na jiné, v jaké je "scope", atd. Toto jsem u IDEy opět nenašel v rozumné formě.
Závěrem
Je zde spousta dalších drobností, které potřebuji vyřešit, ale ty považuji spíše za "porodní bolesti" (jako například rozumné formátování kódu, jelikož přes Ctrl+Alt+L se mi formátování provede jen "někdy"). Nicméně dost reálně uvažuji o koupi tohoto IDE. Přeci jen, cena za nástroj, který používám více jak 8 hodin denně je zanedbatelná vůči produktivitě, kterou mi může přinést.
Blog nejen o programování. Hlavním tématem jsou technologie, jako je Javascript, Typescript, React či GraphQL. Občas zabrousím i soft skills oblastí, díky tomu, že se dostanu do role team leadera či školitele. Dále jsem zakladatelem společnosti: https://apitree.cz
Zobrazují se příspěvky se štítkemJava. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemJava. Zobrazit všechny příspěvky
pátek 7. ledna 2011
středa 14. října 2009
Apache Wicket - verze 1.4
Apache Wicket zdárně dospěl do verze 1.4, která sebou přináší změny zejména na úrovni podpory generických typů. Tato verze je tedy určena pro javu 1.5 či vyšší.
Po zdravé úvaze jsem se rozhodl přejít na tuto verzi a provést úpravy na stávajícím projektu, který byl psán pro verzi 1.3.x.
Hlavní rozdíly oproti starší verzi
Jak jsem již zmínil, hlavní změnou je podpora generických typů. Bohužel došlo i na změnu v API. Metoda "getModel()" či "getModelObject()" byla nahrazena "getDefaultModel...". Směle jsem se tedy pustil do přeměny pomocí hromadného přepisu (cca 50 výskytů). Naštěstí byla tato změna dostatečná a aplikace je plně kompatibilní s verzí 1.4.
Využitelnost generik
Na jedné straně jsem jásal, že je konečne wicket více typově kontrolovatelný a nemusí docházet k ruzným přetypováním. Na druhou stranu je ovšem dobré poznamenat, že jsou pasáže (viz např. DropDownChoice), které jsou dost neštastně navrženy pro využití generických typů. Problém spočívá zejména v tom, že musím napsat tunu kódu navíc, která nepřináší až tak závratné změny.
Po zhruba měsíční migraci jsem omezil použití generických typů jen na místa, kde to skutečně má smysl (viz např. IModel).
Komponenty třetích stran
To, že je ve Wicketu tvorba znovupoužitelných komponent, hračka, je nepopíratelný fakt. Komponenty třetích stran se tedy spíše soustředí na složitejší věci. Bohužel, v současné době jsem nabyl dojmu, že je vývoj dost chaoticky či vůbec neřízen. Použít některou přídavnou vlastnost z WicketStuff je sázka do loterie. Pokud bych toto porovnal s JSF, je na tom Wicket naprosto žalostně.
Osobně využívám následující přídavné vlastnosti:
Zbylé "projekty" jsem po zběžném otestování raději zahodil. Mrzí mě, že například podpora pro jQuery je dost nestabilní a její použití je dost invazivní (viz. nutnost použít pro Application daného předka, což znemožňuje využít zase například Swarm).
Pokud má někdo zkušenosti z dalšími přídavnými vlastnostmi pro Wicket, nechť se o ně podělí v diskuzi.
I přes zmíněné zápory, které jsem zde popsal, je pro mne Wicket tou nejlepší volbou pro tvorbu web aplikací. Je prostě radost s ním pracovat! :)
Po zdravé úvaze jsem se rozhodl přejít na tuto verzi a provést úpravy na stávajícím projektu, který byl psán pro verzi 1.3.x.
Hlavní rozdíly oproti starší verzi
Jak jsem již zmínil, hlavní změnou je podpora generických typů. Bohužel došlo i na změnu v API. Metoda "getModel()" či "getModelObject()" byla nahrazena "getDefaultModel...". Směle jsem se tedy pustil do přeměny pomocí hromadného přepisu (cca 50 výskytů). Naštěstí byla tato změna dostatečná a aplikace je plně kompatibilní s verzí 1.4.
Využitelnost generik
Na jedné straně jsem jásal, že je konečne wicket více typově kontrolovatelný a nemusí docházet k ruzným přetypováním. Na druhou stranu je ovšem dobré poznamenat, že jsou pasáže (viz např. DropDownChoice), které jsou dost neštastně navrženy pro využití generických typů. Problém spočívá zejména v tom, že musím napsat tunu kódu navíc, která nepřináší až tak závratné změny.
Po zhruba měsíční migraci jsem omezil použití generických typů jen na místa, kde to skutečně má smysl (viz např. IModel).
Komponenty třetích stran
To, že je ve Wicketu tvorba znovupoužitelných komponent, hračka, je nepopíratelný fakt. Komponenty třetích stran se tedy spíše soustředí na složitejší věci. Bohužel, v současné době jsem nabyl dojmu, že je vývoj dost chaoticky či vůbec neřízen. Použít některou přídavnou vlastnost z WicketStuff je sázka do loterie. Pokud bych toto porovnal s JSF, je na tom Wicket naprosto žalostně.
Osobně využívám následující přídavné vlastnosti:
- wicket contrib javaee 1.1 - podpora pro injectování EJB komponent
- swarm 1.4, wasp 1.4 - pro podporu security management
- grid - jedná se o "rich" tabulky; kdysi byl projekt vystaven na inmethod.com, poté byl zrušen; pohužel jsem musel udělat vlastní úpravy, aby daná komponenta byla vůbec funkční
Zbylé "projekty" jsem po zběžném otestování raději zahodil. Mrzí mě, že například podpora pro jQuery je dost nestabilní a její použití je dost invazivní (viz. nutnost použít pro Application daného předka, což znemožňuje využít zase například Swarm).
Pokud má někdo zkušenosti z dalšími přídavnými vlastnostmi pro Wicket, nechť se o ně podělí v diskuzi.
I přes zmíněné zápory, které jsem zde popsal, je pro mne Wicket tou nejlepší volbou pro tvorbu web aplikací. Je prostě radost s ním pracovat! :)
úterý 6. ledna 2009
Apache Wicket - Hot Deploy html souborů
Při vývoji javovských webových aplikací nad aplikačním serverem (Glassfish) či jen nad samotným webovým kontejnerem (Tomcat), dochází k jedné nepříjmené situaci a tím je nahrání aplikace na server. Pokud se jedná o malý projekt, je čas strávený nad undeploy/deploy mizivý. Jak ale projekt začne růst, roste s ním i čas, který strávite při nahrávání nové verze na server.
Existuje několik možností, jak se vyhnout co nejčastějšímu nahrávání. Jednou z možností je důsledné psaní JUnit testů, které by mě měly upozorňovat na vzniklé problémy již při vývoji. Další možností je spuštění aplikačního serveru v debug módu. Ten částečně umožňuje provést tzv. hot deploy, kde se na server aktualizuje pouze část, kterou jsem právě změnil. Záměrně používám slovo částečně, protože i tento způsob je dost omezen (tvorba nových tříd, nových metod, EJBs, atd).
Projekt postavený nad Apache Wicket je možné spouštět také v debug řežimu. Tento mód umožňuje využít komplexnější analýzu (logování, ajax výstupy, atd) při vývoji. Pokud je tedy tento projekt spuštěn v debug řežimu a celý aplikační server také v debug módu, je možné za běhu měnit implementaci metod. Problém ovšem nastane s html soubory u wicket komponent. Ty se jen těžko budu snažit dostat do hot deploy řežimu.
Po menším pátrání jsem ovšem objevil možnost jak provést "autodeploy" těchto html souborů. Celý trik spočívá v tom, že si definuji vlastní tzv. "resource folder", který nastavím do místa, kde se vyskytuje můj projekt. Tato funkcionalita se zapisuje do init metody uvnitř vlastní Application Class, která je definována v web.xml jako "applicationClassName".
1. funkcionalita se spustí jen v případě, že je celý projekt spuštěn v debug modu.
2. pokud ve svém projektu měním html soubor, je tato změna promítnuta v běžícím projektu ihned (1 vteřina)
3. nastavím celou cestu k mému projektu a k jeho balíčkům
Tento "resource folder" umožňuje změnu jak html souborů, tak i dalších vlastností jako je např. lokalizace v properties souborech, css, atd. Tedy vše kromě javovských tříd.
Co se týče samotného hot deploy, doufám, že se situace o dost zlepší s příchodem Glassfish V3, který by měl obsahovat OSGI kontejner.
*Malé rýpnutí:*
Nedávno jsem poslouchal poslední podcast 29 z java.cz, kde Roman Štrobl mluvil o nové funkcionalitě "Compile on Save" v NetBeans. Přiznám se, že se mi zatím nepodařilo tuto funkcionalitu využít při vývoji J2EE aplikace. Mám pocit, že je hot deploy na glassfishi omezen tak, že je to téměř nepoužitelné. Pokud se nemýlím, tak samotná EJB3 komponenta moc dobře takto "aktualizovat" nejde.
Existuje několik možností, jak se vyhnout co nejčastějšímu nahrávání. Jednou z možností je důsledné psaní JUnit testů, které by mě měly upozorňovat na vzniklé problémy již při vývoji. Další možností je spuštění aplikačního serveru v debug módu. Ten částečně umožňuje provést tzv. hot deploy, kde se na server aktualizuje pouze část, kterou jsem právě změnil. Záměrně používám slovo částečně, protože i tento způsob je dost omezen (tvorba nových tříd, nových metod, EJBs, atd).
Projekt postavený nad Apache Wicket je možné spouštět také v debug řežimu. Tento mód umožňuje využít komplexnější analýzu (logování, ajax výstupy, atd) při vývoji. Pokud je tedy tento projekt spuštěn v debug řežimu a celý aplikační server také v debug módu, je možné za běhu měnit implementaci metod. Problém ovšem nastane s html soubory u wicket komponent. Ty se jen těžko budu snažit dostat do hot deploy řežimu.
Po menším pátrání jsem ovšem objevil možnost jak provést "autodeploy" těchto html souborů. Celý trik spočívá v tom, že si definuji vlastní tzv. "resource folder", který nastavím do místa, kde se vyskytuje můj projekt. Tato funkcionalita se zapisuje do init metody uvnitř vlastní Application Class, která je definována v web.xml jako "applicationClassName".
@Override
protected void init() {
super.init();
// 1
if (Application.DEVELOPMENT.equalsIgnoreCase(getConfigurationType())) {
// 2
getResourceSettings().setResourcePollFrequency(Duration.ONE_SECOND);
// 3
getResourceSettings().addResourceFolder("/home/ales/projekt/src/java");
}
}1. funkcionalita se spustí jen v případě, že je celý projekt spuštěn v debug modu.
2. pokud ve svém projektu měním html soubor, je tato změna promítnuta v běžícím projektu ihned (1 vteřina)
3. nastavím celou cestu k mému projektu a k jeho balíčkům
Tento "resource folder" umožňuje změnu jak html souborů, tak i dalších vlastností jako je např. lokalizace v properties souborech, css, atd. Tedy vše kromě javovských tříd.
Co se týče samotného hot deploy, doufám, že se situace o dost zlepší s příchodem Glassfish V3, který by měl obsahovat OSGI kontejner.
*Malé rýpnutí:*
Nedávno jsem poslouchal poslední podcast 29 z java.cz, kde Roman Štrobl mluvil o nové funkcionalitě "Compile on Save" v NetBeans. Přiznám se, že se mi zatím nepodařilo tuto funkcionalitu využít při vývoji J2EE aplikace. Mám pocit, že je hot deploy na glassfishi omezen tak, že je to téměř nepoužitelné. Pokud se nemýlím, tak samotná EJB3 komponenta moc dobře takto "aktualizovat" nejde.
pondělí 29. prosince 2008
Apache Wicket - Znovupoužitelné komponenty
Tvorba vlastních komponent je u komponentově orientovaného web frameworku, důležitá vlastnost. Jen težko bych hledal projekt, který by si vystačil s existujícími vlastnostmi a komponenty daného rámce a nesnažil se o vlastní tvorbu.
Pokud bych Wicket srovnával s JSF na této úrovni, musel bych více podrobněji znát tvorbu komponent v JSF. Ať jsem se snažil sebevíc, nikdy jsem úplně neodhalil výhody vlastních komponent v JSF. Ten postup mi přišel natolik složitý a nesmyslný, že v tomto případě jsem vždy sáhnul po něčem existujícím (RichFaces, Tomahawk, atd.). V JSF mám v tomto ohledu jen teoretickou znalost, která mi ovšem stačí k tomu, abych se do nečeho takového, jako je tvorba vlastních komponent, vůbec nepouštěl.
Ve Wicketu je situace naprosto rozdílná. Samotný framework mě v podstatě nutí rozsekávat danou prezentaci do několika menších celků, které se později dají znovu použít. Navíc je situace o to snažší, že není potřeba se učit či používat nové věci. Bohatě stačí znalost základních komponent a jejich využití. Vše ostatní vyplyne samo.
Pro tvorbu nové komponenty je nejčastěji používán "org.apache.wicket.markup.html.panel.Panel", od kterého má vlastní komponenta dědí. Díky tomu získám základní vlastnosti, které bych musel jinak znovu tvořit. Podobnost lze najít u Swingu, kde vlastní komponenty nejčastěji dědí od JPanelu.
Na malém příkladu ukážu, jak vytvořit komponenu, která bude obsahovat základní informace o zaměstnanci. Tuto komponentu poté budu moci využít např. při kliknutí na detail zaměstnance, při výpisu zaměstnanců, apod. Všude, kde budu chtít znát základní informace o daném zaměstnanci.
Nejprve doménový model:
Nyní vytvořím wicket komponentu:
A k danému HTML i Java kod:
Tím mám vlastní komponentu hotovou.
Nyní ji stačí využít. Například při zobrazení detailu zaměstnance:
K danému HTML i Java kod:
I když je daný příklad triviální, jasně znázorňuje to, že tvorba vlastních komponent je v Apache Wicket velice jednoduchá a hlavně dobře použitelná. Způsob jak navrhovat takové komponenty je stejný jako v případě tvorby objektově orientovaného kódu. Třídy jsou malé a obsahují jen jednu jasně specifikovanou funkcionalitu. Stejné je to i zde.
*Myslím, že tohle je ta nejzásadnější věc, která dělá z Apache Wicketu dobrý framework, ve kterém je radost něco psát. Vytvořit komponenty a pak je skládat jako lego :)*
Pokud bych Wicket srovnával s JSF na této úrovni, musel bych více podrobněji znát tvorbu komponent v JSF. Ať jsem se snažil sebevíc, nikdy jsem úplně neodhalil výhody vlastních komponent v JSF. Ten postup mi přišel natolik složitý a nesmyslný, že v tomto případě jsem vždy sáhnul po něčem existujícím (RichFaces, Tomahawk, atd.). V JSF mám v tomto ohledu jen teoretickou znalost, která mi ovšem stačí k tomu, abych se do nečeho takového, jako je tvorba vlastních komponent, vůbec nepouštěl.
Ve Wicketu je situace naprosto rozdílná. Samotný framework mě v podstatě nutí rozsekávat danou prezentaci do několika menších celků, které se později dají znovu použít. Navíc je situace o to snažší, že není potřeba se učit či používat nové věci. Bohatě stačí znalost základních komponent a jejich využití. Vše ostatní vyplyne samo.
Pro tvorbu nové komponenty je nejčastěji používán "org.apache.wicket.markup.html.panel.Panel", od kterého má vlastní komponenta dědí. Díky tomu získám základní vlastnosti, které bych musel jinak znovu tvořit. Podobnost lze najít u Swingu, kde vlastní komponenty nejčastěji dědí od JPanelu.
Na malém příkladu ukážu, jak vytvořit komponenu, která bude obsahovat základní informace o zaměstnanci. Tuto komponentu poté budu moci využít např. při kliknutí na detail zaměstnance, při výpisu zaměstnanců, apod. Všude, kde budu chtít znát základní informace o daném zaměstnanci.
Nejprve doménový model:
public class Zamestnanec {
private String idCislo;
private String prijmeni;
private String jmeno;
// set, get
}Nyní vytvořím wicket komponentu:
<wicket:panel>
<span wicket:id="prijmeni"></span> <span wicket:id="jmeno"></span> (<span wicket:id="idCislo"></span>)
</wicket:panel>A k danému HTML i Java kod:
public class ZamestnanecPanel extends Panel {
public ZamestnanecPanel(String id, Zamestnanec z) {
super(id);
add(new Label("prijmeni", z.getPrijmeni());
add(new Label("jmeno", z.getJmeno());
add(new Label("idCislo", z.getIdCislo());
}
}Tím mám vlastní komponentu hotovou.
Nyní ji stačí využít. Například při zobrazení detailu zaměstnance:
... hlavicka html stranky ....
<div class="detail">
<span wicket:id="zakladni"></span>
.... další vlasnosti
</div>
... paticka html stranky.....K danému HTML i Java kod:
public class DetailPage extends WebPage {
public DetailPage(String idCislo) {
super();
add(new ZamestnanecPanel("zakladni", service.findById(idCislo)));
// dalsi vlastnosti
}
}I když je daný příklad triviální, jasně znázorňuje to, že tvorba vlastních komponent je v Apache Wicket velice jednoduchá a hlavně dobře použitelná. Způsob jak navrhovat takové komponenty je stejný jako v případě tvorby objektově orientovaného kódu. Třídy jsou malé a obsahují jen jednu jasně specifikovanou funkcionalitu. Stejné je to i zde.
*Myslím, že tohle je ta nejzásadnější věc, která dělá z Apache Wicketu dobrý framework, ve kterém je radost něco psát. Vytvořit komponenty a pak je skládat jako lego :)*
úterý 23. prosince 2008
JPA 2.0
Nedávno zveřejněná specifikace JPA 2.0 obsahuje asi nejzásadnější posun v možnosti psaní Criteria API. Pokud někdo očekává okopírované Criteria API z Hibernate bude možná trošku zklamán (či potěšen).
Nový způsob je totiž více postaven na "objektovosti" daných entit. Dobrý zdroj a ukázku lze najít na blogu Gavina Kinga.
Osobně toto považuji za správný krok. Samotné JPA (EntityManager) umí pouze JPQL dotazy, které jsou napsané jako jeden String řetězec. To sebou přináší hromadu nevýhod a v mnoha případech nutí, aby si vývojář napsal nějaký ten vlastní "JPQL String Parser". Z tohoto důvodu jsem osobně JPA degradoval na "pouhé" ORM mapovaní, kde mi jasně specifikovaný standard umožňuje být více nezavislý na použitém "provideru pro persistenci". EntityManager jsem vyměnil za "Hibernate Session" a vesele si psal svá Criteria API.
Hibernate Criteria API ovšem nejsou všespásná. V některých případech je tento způsob nepoužitelný. Občas člověk narazí na nějaký ten bug či extrémní složitost psaní dotazu. Jakmile vznikne požadavek na "subquery", je Hibernate Criteria API velká loterie. Samotná podpora poddotazů je totiž žalostná. Podle toho, co jsem zatím viděl, si myslím, že způsob zápisu v JPA 2.0 bude v tomto více přizpůsobivější a umožní plně nahradit jakýkoli JPQL dotaz.
Další věcí je **návratová hodnota dotazu**. Asi nikdo nepředpokládá, že lze pomocí namapovaných entit vše vyřešit. V takových chvílích nastupují DTO objekty, které se časem začnou množit. Způsob, jak výsledek převádět do vlastních objektů, je v JPA 1.0 dost špatně navržen. Psát "new cela.pakaz.az.k.ZakaznikDTO(a, zde, hromada, parametru)" je otřesné. Když k tomu ještě připočítám nutnost tvorby konstruktorů, tak jak jsou tvořeny v JPQL, dostanete se do situace, kdy Vám Java jako staticky typovaný jazyk přestane pomáhat. Budete se muset spoléhat na správnost daných JPQL dotazů i v případech refactoringu (samozřejmě testy mohou pomoci :)).
Proto i zde jsem zvolil Hibernate Criteria API, jelikož způsob návratové hodnoty lze mnohem lépe zapsat pomocí "Hibernate Projections".
Bohužel i tato volba není úplně ideální a opět obsahuje "mouchy". První z nich je asi to, že nelze vytvořit DTO objekt, který bude obsahovat referenci na jiný objekt než ten, který zná Hibernate nad JDBC (String, Integer, atd.). Zde se jaksi ztrácí ono magické OOP a nastupuje hromada primitivních DTO objektů, které mezi sebou nelze propojit jako v případě mapovaných entit. Jediné propojení lze samotřejmě provést pouze na úrovni vlastního Java kódu.
I když jsem novou specifikaci nečetl řádek po řádku, nenalezl jsem v této oblasti žádný posun. Je pravdou, že i "select statement" byl z části vylepšen, ale nikoli tak, aby umožňoval tvorbu vzájemně propojených DTO objektů.
Ale i přesto musím říct, že nové Criteria Query API se mi v JPA dost zamlouvají. Snad bude tento nový způsob zápisu alespoň schopen nahradit Hibernate Criteria API.
*Specifikace pro JPA je dosti rozsáhlá a myslím, že jako čtivo na dlouhé zimní večery bude jistě dobrou volbou :)*
Nový způsob je totiž více postaven na "objektovosti" daných entit. Dobrý zdroj a ukázku lze najít na blogu Gavina Kinga.
Osobně toto považuji za správný krok. Samotné JPA (EntityManager) umí pouze JPQL dotazy, které jsou napsané jako jeden String řetězec. To sebou přináší hromadu nevýhod a v mnoha případech nutí, aby si vývojář napsal nějaký ten vlastní "JPQL String Parser". Z tohoto důvodu jsem osobně JPA degradoval na "pouhé" ORM mapovaní, kde mi jasně specifikovaný standard umožňuje být více nezavislý na použitém "provideru pro persistenci". EntityManager jsem vyměnil za "Hibernate Session" a vesele si psal svá Criteria API.
Hibernate Criteria API ovšem nejsou všespásná. V některých případech je tento způsob nepoužitelný. Občas člověk narazí na nějaký ten bug či extrémní složitost psaní dotazu. Jakmile vznikne požadavek na "subquery", je Hibernate Criteria API velká loterie. Samotná podpora poddotazů je totiž žalostná. Podle toho, co jsem zatím viděl, si myslím, že způsob zápisu v JPA 2.0 bude v tomto více přizpůsobivější a umožní plně nahradit jakýkoli JPQL dotaz.
Další věcí je **návratová hodnota dotazu**. Asi nikdo nepředpokládá, že lze pomocí namapovaných entit vše vyřešit. V takových chvílích nastupují DTO objekty, které se časem začnou množit. Způsob, jak výsledek převádět do vlastních objektů, je v JPA 1.0 dost špatně navržen. Psát "new cela.pakaz.az.k.ZakaznikDTO(a, zde, hromada, parametru)" je otřesné. Když k tomu ještě připočítám nutnost tvorby konstruktorů, tak jak jsou tvořeny v JPQL, dostanete se do situace, kdy Vám Java jako staticky typovaný jazyk přestane pomáhat. Budete se muset spoléhat na správnost daných JPQL dotazů i v případech refactoringu (samozřejmě testy mohou pomoci :)).
Proto i zde jsem zvolil Hibernate Criteria API, jelikož způsob návratové hodnoty lze mnohem lépe zapsat pomocí "Hibernate Projections".
Bohužel i tato volba není úplně ideální a opět obsahuje "mouchy". První z nich je asi to, že nelze vytvořit DTO objekt, který bude obsahovat referenci na jiný objekt než ten, který zná Hibernate nad JDBC (String, Integer, atd.). Zde se jaksi ztrácí ono magické OOP a nastupuje hromada primitivních DTO objektů, které mezi sebou nelze propojit jako v případě mapovaných entit. Jediné propojení lze samotřejmě provést pouze na úrovni vlastního Java kódu.
I když jsem novou specifikaci nečetl řádek po řádku, nenalezl jsem v této oblasti žádný posun. Je pravdou, že i "select statement" byl z části vylepšen, ale nikoli tak, aby umožňoval tvorbu vzájemně propojených DTO objektů.
Ale i přesto musím říct, že nové Criteria Query API se mi v JPA dost zamlouvají. Snad bude tento nový způsob zápisu alespoň schopen nahradit Hibernate Criteria API.
*Specifikace pro JPA je dosti rozsáhlá a myslím, že jako čtivo na dlouhé zimní večery bude jistě dobrou volbou :)*
pondělí 13. října 2008
Apache Wicket - IBehavior, Tabulky
Minule jsem psal o možnosti spojení Wicketu s EJB3 a o Wicket Security. Dnes se podívám na další vlastnosti a možnosti tohoto webového frameworku.
IBehavior
Jedná se o interface, který je označován jako druh pluginu wicket komponent. Já jsem dané řešení využil například pro statistiku návštěvnosti či pro zjistění, jak dlouho trvalo vyrendrování wicket stránky.
Příklad, pro zjištění doby trvání vyrendrování wicket stránky:
Třída je potomkem AbstractBehavior, což je adaptér pro interface IBehavior. Tuto implementaci stačí poté přiřadit v "BasePage" (základní Wicket Page, která je předkem všech Page). Samozřejmě, pro Wicket je Page komponenta stejně jako např. TextField. Proto je možné daný plugin přiřazovat i daným komponentám uvnitř Page, Panel, atd.
Možností, jak využít toto rozšíření pro komponenty je mnoho. Pomocí této vlastnosti se například dá z klasického input fieldu vytvořit zadávací políčko s kalendářem pro datum, atd. Stačí implementovat metodu "onComponentTag" a tu patřičně upravit. Tato funkcionalita s kalendářem je již přímo implementována (viz. wicket-datetime: DatePicker).
Tabulky
Každý webový intranetový systém se z velké části točí okolo poskytování dat v jasně specifikované formě. Nebo chcete-li: "hromada tabulek s vlastní filtrací". K tomuto účelu poskytuje Wicket přímo vlastní komponenty, viz: repeater example.
Do nedávné doby existoval web: inmethod.com, který danou funkcionalitu rozšiřoval o vlastní druh tabulek. Tato implementace byla zatím asi to nejlepší, co jsem v oblasti webu a webových frameworků viděl.
Tabulky mají statické sloupce a posuvný obsah (ala excel), možnost řazení sloupců, přesun sloupců, změnu velikosti sloupců, atd.
Příliš nechápu proč, byl tento projekt ukončen (zřejmě jako ostatní: nedostatek volného času). Naštěstí je ale možné si přes svn stáhnout zdrojové kódy, které lze použít. Je k dispozicii živá ukázka.
Daný projekt je psán vůči verzi 1.2. Pro verzi 1.3.x není k dispozici alternativa. Osobně danou knihovnu pro verzi 1.3.3 využívám, ovšem s tím, že jsem dané zdrojové kódy musel částečně upravit. Pokud by měl někdo zájem, mohu mu upravenou knihovnu zaslat.
Danou implementaci lze samozřejmě dále rozšiřovat. Stačí se například podívat jak je definována třída: "com.inmethod.grid.column.PropertyColumn".
Příště ukáži, jak lze snadno tvořit znovupoužitelné komponenty v Apache Wicket.
IBehavior
Jedná se o interface, který je označován jako druh pluginu wicket komponent. Já jsem dané řešení využil například pro statistiku návštěvnosti či pro zjistění, jak dlouho trvalo vyrendrování wicket stránky.
Příklad, pro zjištění doby trvání vyrendrování wicket stránky:
public class TimeBehavior extends AbstractBehavior {
private long start;
@Override
public void beforeRender(Component component) {
start = System.currentTimeMillis();
}
@Override
public void onRendered(Component component) {
super.onRendered(component);
long dobaBehu = System.currentTimeMillis() - start;
// dalsi zpracovani
}
}Třída je potomkem AbstractBehavior, což je adaptér pro interface IBehavior. Tuto implementaci stačí poté přiřadit v "BasePage" (základní Wicket Page, která je předkem všech Page). Samozřejmě, pro Wicket je Page komponenta stejně jako např. TextField. Proto je možné daný plugin přiřazovat i daným komponentám uvnitř Page, Panel, atd.
Možností, jak využít toto rozšíření pro komponenty je mnoho. Pomocí této vlastnosti se například dá z klasického input fieldu vytvořit zadávací políčko s kalendářem pro datum, atd. Stačí implementovat metodu "onComponentTag" a tu patřičně upravit. Tato funkcionalita s kalendářem je již přímo implementována (viz. wicket-datetime: DatePicker).
Tabulky
Každý webový intranetový systém se z velké části točí okolo poskytování dat v jasně specifikované formě. Nebo chcete-li: "hromada tabulek s vlastní filtrací". K tomuto účelu poskytuje Wicket přímo vlastní komponenty, viz: repeater example.
Do nedávné doby existoval web: inmethod.com, který danou funkcionalitu rozšiřoval o vlastní druh tabulek. Tato implementace byla zatím asi to nejlepší, co jsem v oblasti webu a webových frameworků viděl.
Tabulky mají statické sloupce a posuvný obsah (ala excel), možnost řazení sloupců, přesun sloupců, změnu velikosti sloupců, atd.
Příliš nechápu proč, byl tento projekt ukončen (zřejmě jako ostatní: nedostatek volného času). Naštěstí je ale možné si přes svn stáhnout zdrojové kódy, které lze použít. Je k dispozicii živá ukázka.
Daný projekt je psán vůči verzi 1.2. Pro verzi 1.3.x není k dispozici alternativa. Osobně danou knihovnu pro verzi 1.3.3 využívám, ovšem s tím, že jsem dané zdrojové kódy musel částečně upravit. Pokud by měl někdo zájem, mohu mu upravenou knihovnu zaslat.
Danou implementaci lze samozřejmě dále rozšiřovat. Stačí se například podívat jak je definována třída: "com.inmethod.grid.column.PropertyColumn".
Příště ukáži, jak lze snadno tvořit znovupoužitelné komponenty v Apache Wicket.
pátek 19. září 2008
Trocha dávky Apache Wicket
Od posledního příspěvku na tomto blogu již uplynula nějaká ta doba. Nebudu tvrdit, že jsem neměl čas. Důvodem byla spíše chuť něco nového napsat. To je ovšem pryč, takže se vrátím zpět ke svému psaní :)
Apache Wicket je zajímavý hned z několika důvodů. Některé z nich jsem prezentoval minule a proto dnes přidám snad jediný důvod: baví mě v něm psát aplikace.
Každý, kdo má alespoň obecnou znalost tohoto frameworku, musí si daný způsob ihned spojit s psaním aplikací ve swingu. Asi bych to vyjádřil slovy: piš to jako ve swingu a přidej k dané třídě html soubor.
Wicket a EJB3
Dají se tyto dvě technologie jednoduše spojit? Repektive dá se snadno zařídit závislost wicket komponenty na EJB servisní beaně? Ano dá. Stačí stáhnout wicket-contrib-javaee.
Pro schopnost provést jednoduché DI (dependency injection) přes anotaci @EJB je třeba definovat referenci ve web.xml.
Při definici ejb-ref-name používám celý název daného rozhraní. Důvod je ten, že poté nění třeba při definici DI uvádět @EJB(name="reference"). Výsledné DI vypadá následovně:
Wicket a Security
Existuje několik možností, jak definovat security. Já osobně jsem zvolil WASP. Toto řešení je postaveno na rolích (i když spíše záleží na způsobu implementace). Je zde několik důležitých kroků k uspěšnému nasazení.
1. Definovat vlastní .hive soubor
2. Base page musí být potomkem SecureWebPage
3. Wicket Session musí být potomkem WaspSession
4. Application class musí být částečně upravena (viz příklady na daném WASP webu)
5. Musí se definovat vlastní LoginContext a Principal
Nebudu zde rozepisovat jednotlivé kroky. Dané příklady lze najít na odkazovaném webu pro WASP. Uvedu jen to zajímavé:
Definice oprávnění lze použít jak na celou page, tak na danou akci, tak na daný model. Z toho lze usoudit, že daný způsob by měl být dostačující k danému použití v jakémkoli scénáři. Celá tato definice je uvedena v .hive souboru, který je poté nastaven v Application class. Samozřejmě daných .hive souborů může existovat klidně více.
U definice oprávnění v .hive souboru lze nastavit, zda se bude daná definice týkat i potomků daných page tříd. Zda je k nějaké page přístup globální, či pro danou roli, atd.
Daný způsob implementace se mi vcelku zamlouvá. Jediné, co bych danému řešení vytkl je to, že dokumentace je téměř nulová. Jediný zdroj informací jsou proto jen příklady, které si lze stáhnout a poté prozkoumat jejich kód.
Tak to by bylo asi vše. Příště ukáži jak lze využít IBehavior či jak je na tom Wicket s nenáviděnými html tabulkami.
Apache Wicket je zajímavý hned z několika důvodů. Některé z nich jsem prezentoval minule a proto dnes přidám snad jediný důvod: baví mě v něm psát aplikace.
Každý, kdo má alespoň obecnou znalost tohoto frameworku, musí si daný způsob ihned spojit s psaním aplikací ve swingu. Asi bych to vyjádřil slovy: piš to jako ve swingu a přidej k dané třídě html soubor.
Wicket a EJB3
Dají se tyto dvě technologie jednoduše spojit? Repektive dá se snadno zařídit závislost wicket komponenty na EJB servisní beaně? Ano dá. Stačí stáhnout wicket-contrib-javaee.
Pro schopnost provést jednoduché DI (dependency injection) přes anotaci @EJB je třeba definovat referenci ve web.xml.
<ejb-local-ref>
<ejb-ref-name>cz.irminsul.javaee.ejb3.ServiceLocal</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>cz.irminsul.javaee.ejb3.ServiceLocal</local>
</ejb-local-ref>Při definici ejb-ref-name používám celý název daného rozhraní. Důvod je ten, že poté nění třeba při definici DI uvádět @EJB(name="reference"). Výsledné DI vypadá následovně:
public class MyPage extends Page {
@EJB
private ServiceLocal service;
....
}Wicket a Security
Existuje několik možností, jak definovat security. Já osobně jsem zvolil WASP. Toto řešení je postaveno na rolích (i když spíše záleží na způsobu implementace). Je zde několik důležitých kroků k uspěšnému nasazení.
1. Definovat vlastní .hive soubor
2. Base page musí být potomkem SecureWebPage
3. Wicket Session musí být potomkem WaspSession
4. Application class musí být částečně upravena (viz příklady na daném WASP webu)
5. Musí se definovat vlastní LoginContext a Principal
Nebudu zde rozepisovat jednotlivé kroky. Dané příklady lze najít na odkazovaném webu pro WASP. Uvedu jen to zajímavé:
Definice oprávnění lze použít jak na celou page, tak na danou akci, tak na daný model. Z toho lze usoudit, že daný způsob by měl být dostačující k danému použití v jakémkoli scénáři. Celá tato definice je uvedena v .hive souboru, který je poté nastaven v Application class. Samozřejmě daných .hive souborů může existovat klidně více.
U definice oprávnění v .hive souboru lze nastavit, zda se bude daná definice týkat i potomků daných page tříd. Zda je k nějaké page přístup globální, či pro danou roli, atd.
Daný způsob implementace se mi vcelku zamlouvá. Jediné, co bych danému řešení vytkl je to, že dokumentace je téměř nulová. Jediný zdroj informací jsou proto jen příklady, které si lze stáhnout a poté prozkoumat jejich kód.
Tak to by bylo asi vše. Příště ukáži jak lze využít IBehavior či jak je na tom Wicket s nenáviděnými html tabulkami.
středa 16. července 2008
Serial port v Jave aneb RXTX
Jakožto spokojený uživatel linuxu jsem dost odstíněn od windows. Bohužel to sebou přináší i určité problémy, kdy potřebuji portovat aplikaci na windows.
Ano, java je multiplatformní, jenže pouze do chvíle, kdy potřebujete přistupovat k sériovému portu a číst z něj data. K tomuto účelu existuje několik možností, které ovšem v jave znamenají implementovat funkčnost pouze pro daný OS.
Nic není tak černé, a proto existuje možnost, jak donutit vaši aplikaci, aby uměla komunikovat se sériovým portem na více OS. To vše bez nutnosti měnit stávající kód.
Ta možnost je přes RXTX. Kromě klasického COMx umí i LPT či USB.
Samotná implementace je víceméně jednoduchá:
Jelikož na notebooku používám redukci mezi USB a sériovým portem, trochu jsem se bál samotného zprovoznění. Opak je pravdou. Přes identifikaci zařízení "/dev/ttyUSB0" jsem byl schopen se spojit stejně jako přes COM1 na windows.
Pro testovací účely jsem si napsal malý prográmek, který se snaží dohledat všechny porty. Podle identifikace je poté možné port otevřít a čekat, zda z portu přijdou nějaká data.
Ke stažení: Testovací program pro poslouchání portů
Ano, java je multiplatformní, jenže pouze do chvíle, kdy potřebujete přistupovat k sériovému portu a číst z něj data. K tomuto účelu existuje několik možností, které ovšem v jave znamenají implementovat funkčnost pouze pro daný OS.
Nic není tak černé, a proto existuje možnost, jak donutit vaši aplikaci, aby uměla komunikovat se sériovým portem na více OS. To vše bez nutnosti měnit stávající kód.
Ta možnost je přes RXTX. Kromě klasického COMx umí i LPT či USB.
Samotná implementace je víceméně jednoduchá:
- nakopírují se příslušné knihovny (u linuxu do usr/lib/ a u windows do jre/bin).
- přes JNI se zavolají příslušné metody z knihoven daných OS
- otevře se port, zaregistruje listener a již mohu číst či zapisovat.
Jelikož na notebooku používám redukci mezi USB a sériovým portem, trochu jsem se bál samotného zprovoznění. Opak je pravdou. Přes identifikaci zařízení "/dev/ttyUSB0" jsem byl schopen se spojit stejně jako přes COM1 na windows.
Pro testovací účely jsem si napsal malý prográmek, který se snaží dohledat všechny porty. Podle identifikace je poté možné port otevřít a čekat, zda z portu přijdou nějaká data.
Ke stažení: Testovací program pro poslouchání portů
středa 18. června 2008
Je čas na Apache Wicket?
Záznam diskuze:
L: Ahoj Franto, mám menší problém.
F: Jaký Lumíre?
L: Nevím, který webový framework pro javu je nejlepší.
F: No to je jednoduché. Vyzkoušej těch 40 nejznámenjších a pak se rozhodni.
L: Hmm... :(
Konec.
Ať již člověk navštíví jakoukoli homepage webového frameworku, dočte se, že zvolil dobře. Nejlepší možné řešení, vše jednoduché, perfektně rozšířitelné a v porovnání s ostatními je proste "Number ONE".
Sám jsem již dávno rezignoval na hledání zlaté žíly v podobě nejlepšího řešení. Volbu by mělo ovlivňovat to, jakou architekturu dané aplikace plánuji či jakým backendem aplikce disponuji. I když jsou snad všechny možné kombinace možné (Seam + Spring, Seam + EJB, Wicket + Spring, ...., ne vždy je volba zrovna šťasná. Pokud do hry vložím další možnosti jako volání část aplikace jiným způsobem (např. remote EJB), dostávám již užší skupinu možností.
Z této skupiny, která se vyznačuje volnou vazbou na business logiku, snadnou rozšířitelností a možností souběžného běhu s jiným web kontextem mi zatím nejlépe vychází Apache Wicket.
Ano já vím, stále existuje lepší .... nebo ještě lepší .... a vlastně úplně nejlepší ...... Ale já nemám čas ani náladu zkoumat další desítku možností. Stačí mi ta první desítka :)
Proč Apache Wicket?
Asi bych mohl pokračovat dalšími body. Nicméně to, co jsem zde vypsal jsou vlastnosti, které mě přesvědčují o správné volbě.
Co se týče záporů, ty si budu muset nechat na pozdější dobu. Zatím jsem jen nezávislým pozorovatelem daného web frameworku. Každopádně jsem pevně rozhodnut toto řešení použít i za cenu složitejší spolupráce s EJB modulem a částí aplikace napsané v Seamu.
Takže mám 2 favority. Seam a Apache Wicket. Každý z nich přistupuje k cíli jiným způsobem a každý z nich se rozhodně hodí pro jiný typ aplikací. Až si Apache Wicket více osahám pokusím se obě řešení více porovnat a přesněji zjistit, pro jaký typ aplikací se dané frameworky hodí.
L: Ahoj Franto, mám menší problém.
F: Jaký Lumíre?
L: Nevím, který webový framework pro javu je nejlepší.
F: No to je jednoduché. Vyzkoušej těch 40 nejznámenjších a pak se rozhodni.
L: Hmm... :(
Konec.
Ať již člověk navštíví jakoukoli homepage webového frameworku, dočte se, že zvolil dobře. Nejlepší možné řešení, vše jednoduché, perfektně rozšířitelné a v porovnání s ostatními je proste "Number ONE".
Sám jsem již dávno rezignoval na hledání zlaté žíly v podobě nejlepšího řešení. Volbu by mělo ovlivňovat to, jakou architekturu dané aplikace plánuji či jakým backendem aplikce disponuji. I když jsou snad všechny možné kombinace možné (Seam + Spring, Seam + EJB, Wicket + Spring, ...., ne vždy je volba zrovna šťasná. Pokud do hry vložím další možnosti jako volání část aplikace jiným způsobem (např. remote EJB), dostávám již užší skupinu možností.
Z této skupiny, která se vyznačuje volnou vazbou na business logiku, snadnou rozšířitelností a možností souběžného běhu s jiným web kontextem mi zatím nejlépe vychází Apache Wicket.
Ano já vím, stále existuje lepší .... nebo ještě lepší .... a vlastně úplně nejlepší ...... Ale já nemám čas ani náladu zkoumat další desítku možností. Stačí mi ta první desítka :)
Proč Apache Wicket?
- volná vazba na business logiku
- žádné xml
- žádné anotace, které z POJOs dělají komponenty použitelné pouze v daném kontextu
- téměř čisté html
- vše je psané v jave, takže refactoring je mnohem snažší záležitost
- dané komponenty si pamatují předchozí stav, takže tlačítko zpět již nemusí být noční můrou
- menší zátěž pro server oproti JSF či Seamu
- psaní aplikace je velmi blízké psaní swingových aplikací
- dostatek komponent + snadné psaní znovupoužitelných komponent
- při psaní se skutečně využívá objektově orientovaných principů
- pro běh není třeba žádného speciálního kontextu (běží stejne v Tomcatu jako v AS)
- daná aplikace je snadno přenositelná na jakýkoli jiný web kontejner
Asi bych mohl pokračovat dalšími body. Nicméně to, co jsem zde vypsal jsou vlastnosti, které mě přesvědčují o správné volbě.
Co se týče záporů, ty si budu muset nechat na pozdější dobu. Zatím jsem jen nezávislým pozorovatelem daného web frameworku. Každopádně jsem pevně rozhodnut toto řešení použít i za cenu složitejší spolupráce s EJB modulem a částí aplikace napsané v Seamu.
Takže mám 2 favority. Seam a Apache Wicket. Každý z nich přistupuje k cíli jiným způsobem a každý z nich se rozhodně hodí pro jiný typ aplikací. Až si Apache Wicket více osahám pokusím se obě řešení více porovnat a přesněji zjistit, pro jaký typ aplikací se dané frameworky hodí.
sobota 24. května 2008
Testování EJB komponent
Všeobecně známý fakt, že se EJB komponenty špatně testují, je postaven zejména na tom, že neexistuje žádný standardní způsob, jak při psaní testů postupovat. Pokusím se sepsat způsoby, jak tento palčivý problém vyřešit.
O tom, že je testování důležitá část vývoje, nemá smysl polemizovat. V poslední době se na české scéně objevilo několik článků: "proč je testování důležité". Osobně zastávám názor, že bez testů nemohu kód považovat za funkční a jeho nasazení je velký risk, který se násobí následným refaktoringem či řozšiřováním stávajícího kódu.
V následujících odstavcích se budu zabývat jen EJB 3.0.
Způsoby testování bych rozdělil na 2 typy: testování uvnitř aplikačního serveru a mimo něj.
Testování uvnitř aplikačního serveru
Tento způsob má tu výhodu, že dané testy se mohou začít psát bez jakékoli další pomůcky pro inicializaci EJB kontejneru a služeb v něm. Ohromnou nevýhodou je ovšem to, že samotné psaní a spouštění testů je přímo závislé na nastartování a nahrání celé aplikace na server. Tato činnost může při větších projektech trvat řádově i několik minut. V takové chvíli může vývojář získat naprostý odpor k testování.
Způsob, jak testovat EJB komponenty uvnitř AS, je použití vzdáleného volání EJB komponent. Nevýhodou je ovšem to, že při návrhu musíte definovat "remote" rozhraní.
Druhým způsobem je testování přímo v EJB komponentě, kde mohu pomocí DI (dependency injection) získanou EJB komponentu otestovat. Nevýhodou je zde to, že tento způsob již překračuje základní způsoby testování. JUnit či jemu podobný framework je v takovém případě nepoužitelný.
Testování mimo aplikační server
První velkou nevýhodou tohoto řešení je samotný fakt, že nějak musíte nahradit EJB kontejner. Asi nejznámějším řešením je použití Embedded JBoss.Tento způsob vyžaduje, aby vaše aplikace byla schopná běžet pod JBoss AS, a abyste použili JVM 1.5 (na novejších verzích bohužel embedded JBoss nespustíte). Pokud aplikaci vyvíjíte pod jiným aplikačním serverem, budete s tímto mít asi problémy. Například už jen z důvodu samotné nestandardizace JNDI či použitých nekompatibilních vlastností pro Java EE 5.
Druhým způsobem, jak umožnit testování EJB komponent mimo aplikační server je, že použijete framework typu Ejb3unit, který na základě vaší definice EJB komponent sám provede dependency injection a další vlastnosti typické EJB kontejneru. Nevýhoda tohoto řešení spočívá v tom, že má příliš mnoho omezení, která poté mohou zbytečně ovlivňovat návrh business vrstvy. Na stránkách je možné se dočíst, že brzy vyjde verze 2.0, která snad přinese slibované vlastnosti.
Posledním způsobem, jak vyřešit testování, je použití vlastního řešení. Jelikož používám jako aplikační server Glassfish, byl pro mne JBoss Embedded tabu. Jelikož mám i uvnitř EJB komponent občas složitejší architekturu (dělené vrstvy), tak Ejb3unit byl nepoužitelný.
Můj vlastní způsob spočívá v tom, že testuji EJB komponenty, které obsahují vazby na další EJB pomocí anotací @EJB či vstřikují pomocí @PersistenceContext EntityManager (+ občasné delegování na Hibernate Session).
Jinými slovy, řídím EJB pomocí anotací, což mi umožňilo napsat si i vlastní řešení na testování. Moje řešení totiž využívá Java reflection API, přes které provádím jak dependency injection dalších EJB komponent, tak samotné DI EntityManageru a dalších zdrojů, které zrovna potřebuji.
Test poté vypadá jednoduše: "při inicializaci pošlu testovanou EJB komponentu do metody, která mi vrátí instanci samotné EJB komponenty i s inicializací potřebných závislostí".
Zatím jsem nenalezl lepší řešení. Pravdou je, že v této oblasti jsou EJB komponenty nechány napospas vývojářům, což s sebou přináší jen nekompatibilní způsoby testování jednotlivých částí aplikace.
Doufám, že Glassfish V3 přinese slibovanou možnost být "embeddable" v plném rozsahu či někoho v SUNu napadne s tímto problémem něco udělat. Zatím mi přijde, že každý si napíše vlastní řešení, protože kromě JBoss Embeddable jsem nenašel nic, co by alespoň trošku splňovalo mé požadavky.
Pokud má někdo zkušenosti i s dalšími možnostmi testování EJB komponent, nechť se o ně podělí v komentáři, děkuji.
O tom, že je testování důležitá část vývoje, nemá smysl polemizovat. V poslední době se na české scéně objevilo několik článků: "proč je testování důležité". Osobně zastávám názor, že bez testů nemohu kód považovat za funkční a jeho nasazení je velký risk, který se násobí následným refaktoringem či řozšiřováním stávajícího kódu.
V následujících odstavcích se budu zabývat jen EJB 3.0.
Způsoby testování bych rozdělil na 2 typy: testování uvnitř aplikačního serveru a mimo něj.
Testování uvnitř aplikačního serveru
Tento způsob má tu výhodu, že dané testy se mohou začít psát bez jakékoli další pomůcky pro inicializaci EJB kontejneru a služeb v něm. Ohromnou nevýhodou je ovšem to, že samotné psaní a spouštění testů je přímo závislé na nastartování a nahrání celé aplikace na server. Tato činnost může při větších projektech trvat řádově i několik minut. V takové chvíli může vývojář získat naprostý odpor k testování.
Způsob, jak testovat EJB komponenty uvnitř AS, je použití vzdáleného volání EJB komponent. Nevýhodou je ovšem to, že při návrhu musíte definovat "remote" rozhraní.
Druhým způsobem je testování přímo v EJB komponentě, kde mohu pomocí DI (dependency injection) získanou EJB komponentu otestovat. Nevýhodou je zde to, že tento způsob již překračuje základní způsoby testování. JUnit či jemu podobný framework je v takovém případě nepoužitelný.
Testování mimo aplikační server
První velkou nevýhodou tohoto řešení je samotný fakt, že nějak musíte nahradit EJB kontejner. Asi nejznámějším řešením je použití Embedded JBoss.Tento způsob vyžaduje, aby vaše aplikace byla schopná běžet pod JBoss AS, a abyste použili JVM 1.5 (na novejších verzích bohužel embedded JBoss nespustíte). Pokud aplikaci vyvíjíte pod jiným aplikačním serverem, budete s tímto mít asi problémy. Například už jen z důvodu samotné nestandardizace JNDI či použitých nekompatibilních vlastností pro Java EE 5.
Druhým způsobem, jak umožnit testování EJB komponent mimo aplikační server je, že použijete framework typu Ejb3unit, který na základě vaší definice EJB komponent sám provede dependency injection a další vlastnosti typické EJB kontejneru. Nevýhoda tohoto řešení spočívá v tom, že má příliš mnoho omezení, která poté mohou zbytečně ovlivňovat návrh business vrstvy. Na stránkách je možné se dočíst, že brzy vyjde verze 2.0, která snad přinese slibované vlastnosti.
Posledním způsobem, jak vyřešit testování, je použití vlastního řešení. Jelikož používám jako aplikační server Glassfish, byl pro mne JBoss Embedded tabu. Jelikož mám i uvnitř EJB komponent občas složitejší architekturu (dělené vrstvy), tak Ejb3unit byl nepoužitelný.
Můj vlastní způsob spočívá v tom, že testuji EJB komponenty, které obsahují vazby na další EJB pomocí anotací @EJB či vstřikují pomocí @PersistenceContext EntityManager (+ občasné delegování na Hibernate Session).
Jinými slovy, řídím EJB pomocí anotací, což mi umožňilo napsat si i vlastní řešení na testování. Moje řešení totiž využívá Java reflection API, přes které provádím jak dependency injection dalších EJB komponent, tak samotné DI EntityManageru a dalších zdrojů, které zrovna potřebuji.
Test poté vypadá jednoduše: "při inicializaci pošlu testovanou EJB komponentu do metody, která mi vrátí instanci samotné EJB komponenty i s inicializací potřebných závislostí".
Zatím jsem nenalezl lepší řešení. Pravdou je, že v této oblasti jsou EJB komponenty nechány napospas vývojářům, což s sebou přináší jen nekompatibilní způsoby testování jednotlivých částí aplikace.
Doufám, že Glassfish V3 přinese slibovanou možnost být "embeddable" v plném rozsahu či někoho v SUNu napadne s tímto problémem něco udělat. Zatím mi přijde, že každý si napíše vlastní řešení, protože kromě JBoss Embeddable jsem nenašel nic, co by alespoň trošku splňovalo mé požadavky.
Pokud má někdo zkušenosti i s dalšími možnostmi testování EJB komponent, nechť se o ně podělí v komentáři, děkuji.
neděle 4. května 2008
NetBeans 6.1 a Facelets plugin
O tom, že vyšly Netbeans 6.1 se nemá smysl zmiňovat. Ovšem výhodou této verze je zejména její rychlost, na kterou se údajně vývojáři nejvíce zaměřili.
Jedna věc mi ovšem zde chyběla. A to opět plugin pro facelets :)
Naštěstí existuje řešení, jak daný plugin dostat do této verze.
Následující text je spíše dočasný, tzn., že až vyjde oficiální podpora, jistě bude lepší použít přímo ji.
Na stránce https://nbfaceletssupport.dev.java.net/ je k dispozici cvs repozitář. Z daného repozitáře stačí provést checkout projektu a daný projekt "nbfaceletssuite" otevřít v NetBeans 6.1. Dané pluginy poté zbuildovat a provést create nbm. Tyto pluginy jsou poté schopny pracovat ve verzi 6.1.
Pak již stačí pluginy nainstalovat a máte k dispozici starou dobrou (i když ne zrovna plnou) code completion v xhtml souborech :)
Jedna věc mi ovšem zde chyběla. A to opět plugin pro facelets :)
Naštěstí existuje řešení, jak daný plugin dostat do této verze.
Následující text je spíše dočasný, tzn., že až vyjde oficiální podpora, jistě bude lepší použít přímo ji.
Na stránce https://nbfaceletssupport.dev.java.net/ je k dispozici cvs repozitář. Z daného repozitáře stačí provést checkout projektu a daný projekt "nbfaceletssuite" otevřít v NetBeans 6.1. Dané pluginy poté zbuildovat a provést create nbm. Tyto pluginy jsou poté schopny pracovat ve verzi 6.1.
Pak již stačí pluginy nainstalovat a máte k dispozici starou dobrou (i když ne zrovna plnou) code completion v xhtml souborech :)
Glassfish V3 bude embeddable
Minulý týden mi v rss čtečce přistál článek od Kohsuke Kawaguchi, že Glassfish V3 bude "embeddable".
Osobně jsem si přesně toto pro Glassfish přál. Představte si, že budete mít EJB projekt, který budete moci spouštět např. v Tomcatu. Že budete moci testovat EJB, aniž by bylo potřeba provádět deploy na server či používat remote call EJB či používat "nepoužitelné" frameworky jako je EJBUnit (i když uvidíme jak dopadne verze 2.0). Vše co obsahuje specifikace Java EE 5 a vše co implementuje Glassfish je tedy možné provozovat aniž by někde musel běžet daný server.
V současné době se touto možností může chlubit JBoss AS či Jetty, nyní již stačí počkat pár měsíců (možná týdnů do testovací fáze) a bude možné to samé dělat s GlassFish.
Díky modularitě tohoto AS je ovšem možné startovat server v řádech několika milisekund, což narozdíl od JBoss AS je podstatný rozdíl :)
Před nějakým časem jsem chtel Glassfish opustit a jít cestou JBoss, ale asi ještě chvíli počkám, je vidět, že konkurenční prostředí je prostě ideální stav.
Teď nezbývá, než držet palce, ať se dané featury dotáhnou do úspěšného konce.
Osobně jsem si přesně toto pro Glassfish přál. Představte si, že budete mít EJB projekt, který budete moci spouštět např. v Tomcatu. Že budete moci testovat EJB, aniž by bylo potřeba provádět deploy na server či používat remote call EJB či používat "nepoužitelné" frameworky jako je EJBUnit (i když uvidíme jak dopadne verze 2.0). Vše co obsahuje specifikace Java EE 5 a vše co implementuje Glassfish je tedy možné provozovat aniž by někde musel běžet daný server.
V současné době se touto možností může chlubit JBoss AS či Jetty, nyní již stačí počkat pár měsíců (možná týdnů do testovací fáze) a bude možné to samé dělat s GlassFish.
Díky modularitě tohoto AS je ovšem možné startovat server v řádech několika milisekund, což narozdíl od JBoss AS je podstatný rozdíl :)
Před nějakým časem jsem chtel Glassfish opustit a jít cestou JBoss, ale asi ještě chvíli počkám, je vidět, že konkurenční prostředí je prostě ideální stav.
Teď nezbývá, než držet palce, ať se dané featury dotáhnou do úspěšného konce.
pondělí 21. dubna 2008
Informace o WebBeans
O víkendu jsem shlédl video, ve kterém Gavin King přednášel o WebBeans. Z daného videa mám vcelku dobrý pocit a pokusím se zjednodušeně sepsat, co v přednášce bylo a jak asi bude vypadat psaní takových Web Beans.
Web Beans jsou novou specifikací, která se objeví v Java EE 6. Mezi hlavní účel patří zjednodušení vývoje webových aplikací. Samotné Web Beans jsou inspirovány (kopie) webového frameworku Seam. Jelikož je autorem jeden a tentýž člověk (Gavin King), je vcelku jasné, že lidé, kteří znají Seam budou na Web Beans přecházet stejně jednoduše jako třeba v případě Hibernate -> JPA.
Takže, co například Web Beans poskytují a nabízejí:
Nikoho asi nepřekvapí, že samotné Web Beans jsou řízeny pomocí anotací. Samozřejmě opět existuje možnost používat xml, ale myslím, že tato volba bude zřídkakdy využívána a nejsem si jist, že všechny možnosti budou v xml obsaženy.
Jak tedy takové Web Beans vypadají? Příkladem může být jednoduchá komponenta:
Samotnou anotací @Component označujeme, že se jedná o Web Bean komponentu. Kontejner nám takovouto třídu inicializuje jako Web Beanu. Nic převratného.
Dependency Injection
Při psaní definování závislosti mezi jednotlivými komponenty můžeme použít několik možností:
Další možností, jak definovat závislost je např:
Gavin tento způsob názývá "constructor injection" :)
Aby toho nebylo málo, máme zde další možnost nazvanou "initializer injection":
A nyní jednoduché vstříknutí do JSF stránky:
Další věcí, která stojí za zmínku je definování "binding type". Jde o to, že mohu vzít komponentu, tu podědit a při stríknutí závislosti chtít podědenou komponentu. No raději to předvedu :)
Definice vlastniho "binding type":
Nyní již stači napsat vlastní komponentu dědicí moji "komponenta".
A nyní již vstříknu vlastní komponentu do Printeru.
Výsledkem bude: "Cau Ales".
Věcí, které Gavin King představil je opravdu hodně. Já zde ukážu poslední věc, která je jistě neméně zajímavá.
Nazval bych to "zástupnými anotacemi". Jak jsem na začátku psal, Web Beans jsou řízeny pomocí anotací, což ovšem může vyvolat určité rozpaky ve chvílích, kdy samotná Web Beana bude obsahovat hromadu anotací, které se často opakují.
Představme si, že máme takovouto Web Beanu:
Výčet anotací může být i delší, což mě trochu děsí :)
Je však možné použít následující:
A nyní již použít:
Alespoň že tak :)
Všem, kteří se alespoň trochu zajímají o nové specifikace v oblasti J2EE by si měli dané video prohlédnout.
Já osobně jsem si alespoň udělal lepší obrázek o tom, jak vlastně WebBeans budou vypadat.
Zatím musím uznat, že jsem nadmíru spokojen. Pokud bude vše tak, jak Gavin King ukazuje, asi pošlu jeho Seam do věčných lovisť :) Nenarazil jsem v podstatě na nic podstatného, co by Seam měl a WebBeans nikoli. Zatím to vypadá na pravý opak.
Web Beans jsou novou specifikací, která se objeví v Java EE 6. Mezi hlavní účel patří zjednodušení vývoje webových aplikací. Samotné Web Beans jsou inspirovány (kopie) webového frameworku Seam. Jelikož je autorem jeden a tentýž člověk (Gavin King), je vcelku jasné, že lidé, kteří znají Seam budou na Web Beans přecházet stejně jednoduše jako třeba v případě Hibernate -> JPA.
Takže, co například Web Beans poskytují a nabízejí:
- možnost psát komponenty jako Stateful EJB, které se anotací vystaví jako Web Beans se všemi vlastnostmi Stateful EJB
- dependency injection, jednoduché vstříknutí do JSF stránek; tak jako v Seamu
- konverzace
- ovládání persistence context, která může např. řešit lazy loading
- integrace s JSF, Servlety či JPA
- JSF backing beana může být vystavena jako Web Beana
- atd.
Nikoho asi nepřekvapí, že samotné Web Beans jsou řízeny pomocí anotací. Samozřejmě opět existuje možnost používat xml, ale myslím, že tato volba bude zřídkakdy využívána a nejsem si jist, že všechny možnosti budou v xml obsaženy.
Jak tedy takové Web Beans vypadají? Příkladem může být jednoduchá komponenta:
@Component
public class Komponenta {
public String pozdrav(String jmeno) {
return "Ahoj " + jmeno;
}
}Samotnou anotací @Component označujeme, že se jedná o Web Bean komponentu. Kontejner nám takovouto třídu inicializuje jako Web Beanu. Nic převratného.
Dependency Injection
Při psaní definování závislosti mezi jednotlivými komponenty můžeme použít několik možností:
@Component
public class Printer {
@Current Komponenta komponenta;
public void pozdrav() {
System.out.println(komponenta.pozdrav("Ales"));
}
}Další možností, jak definovat závislost je např:
@Component
public class Printer {
private Komponenta komponenta;
public Printer(Komponenta komponenta) {
this.komponenta = komponenta;
}
public void pozdrav() {
System.out.println(komponenta.pozdrav("Ales"));
}
}Gavin tento způsob názývá "constructor injection" :)
Aby toho nebylo málo, máme zde další možnost nazvanou "initializer injection":
@Component
public class Printer {
private Komponenta komponenta;
@Initializer
initKomponent(Komponenta komponenta) {
this.komponenta = komponenta;
}
}A nyní jednoduché vstříknutí do JSF stránky:
Další věcí, která stojí za zmínku je definování "binding type". Jde o to, že mohu vzít komponentu, tu podědit a při stríknutí závislosti chtít podědenou komponentu. No raději to předvedu :)
Definice vlastniho "binding type":
@BindingType
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER}}
public @interface Casual{}Nyní již stači napsat vlastní komponentu dědicí moji "komponenta".
@Casual
@Component
public class Cau extends Komponenta {
public String pozdrav(String jmeno) {
return "cau " + jmeno;
}
}A nyní již vstříknu vlastní komponentu do Printeru.
@Component
public class Printer {
@Casual Komponenta komponenta;
public void pozdrav() {
System.out.println(komponenta.pozdrav("Ales"));
}
}Výsledkem bude: "Cau Ales".
Věcí, které Gavin King představil je opravdu hodně. Já zde ukážu poslední věc, která je jistě neméně zajímavá.
Nazval bych to "zástupnými anotacemi". Jak jsem na začátku psal, Web Beans jsou řízeny pomocí anotací, což ovšem může vyvolat určité rozpaky ve chvílích, kdy samotná Web Beana bude obsahovat hromadu anotací, které se často opakují.
Představme si, že máme takovouto Web Beanu:
@Component
@Transactional
@Production
@Secure
public class Komponenta {}Výčet anotací může být i delší, což mě trochu děsí :)
Je však možné použít následující:
@Secure
@Transactional
@Named
@Production
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}A nyní již použít:
@Action
public class Komponenta {}Alespoň že tak :)
Všem, kteří se alespoň trochu zajímají o nové specifikace v oblasti J2EE by si měli dané video prohlédnout.
Já osobně jsem si alespoň udělal lepší obrázek o tom, jak vlastně WebBeans budou vypadat.
Zatím musím uznat, že jsem nadmíru spokojen. Pokud bude vše tak, jak Gavin King ukazuje, asi pošlu jeho Seam do věčných lovisť :) Nenarazil jsem v podstatě na nic podstatného, co by Seam měl a WebBeans nikoli. Zatím to vypadá na pravý opak.
neděle 20. dubna 2008
Richfaces pro JSF
Komponentový framework jakým je JSF, má několik kladů, mezi kterými také nalezneme možnost rozšíření o vlastní komponenty. I když psaní vlastních komponent pod JSF není zrovna triviální záležitost.
JSF ve verzi 1.2. nabízí základní komponenty, které mohou být i rozšířitelné, ovšem ne vždy nám vyhovují. Existence různorodých komponent za nás řeší knihovny třetích stran, které nabízejí zajímavé možnosti. Mezi nejčastějšími požadavky na komponenty jsou různé formulářové objekty, modální panely, stromy, menu, tabulky, atd. Navíc je očekávána podpora AJAXu. Jinými slovy, komponenty, které se svou vizuální a funkční podobou blíží desktop aplikacím.
Pro srovnání různých komponentových knihoven můžeme použít např. AJAX JSF Matrix. Možnost výběru je skutečně obrovský a to nepočítám další, které vznikají téměř každý den.
Samotná volba není zrovna jednoduchá. Já jsem prošel asi 3 různé knihovny z nichž jsem si vybral jasného favorita: RichFaces.
RichFaces
RichFaces má v současné době v rukou JBoss. Jedná se klasickou JSF komponentovou knihovnu, která ovšem navíc obsahuje i přímou vazbu a podporu A4J (Ajax4JSF). A4J, jak už její název vypovídá se zaměřuje čistě na AJAX a snaží se naučit JSF komponenty asynchroní práci. Jelikož je RichFaces společně s A4J dodávaná jako jedna knihovna (projekt), může nás těšit fakt, že máme jednu z nejlepších kombinaci mezi AJAX-JSF-Komponenta.
V současné době je RichFaces ve verzi 3.2.0, která vyšla vcelku nedávno. Mezi jejími novinkami můžeme najít věci jako: vlastní combo box, progress bar, file upload, atd.
I když je pravda, že s každou novou verzí můžeme nalézt v bugtraceru hromadu opravených a hromadu nový bugů, přesto je tato knihovna použitelná v produkčním nasazení.
Mě nezbývá, než tuto knihovnu doporučit všem, kteří nemají svého favorita a hledají nejakou slušnou podporu pro JSF komponenty. Psát modální okna, kontextová menu či rozbalovací stromy už nemusí být jen výsadou programátoru desktop aplikací.
Pro zběžný přehled doporučuji samotné demo, které je plně přístupné po zaregistrování na RedHat stránkách, kde si jednotlivé vlastnosti můžete ihned vyzkoušet.
JSF ve verzi 1.2. nabízí základní komponenty, které mohou být i rozšířitelné, ovšem ne vždy nám vyhovují. Existence různorodých komponent za nás řeší knihovny třetích stran, které nabízejí zajímavé možnosti. Mezi nejčastějšími požadavky na komponenty jsou různé formulářové objekty, modální panely, stromy, menu, tabulky, atd. Navíc je očekávána podpora AJAXu. Jinými slovy, komponenty, které se svou vizuální a funkční podobou blíží desktop aplikacím.
Pro srovnání různých komponentových knihoven můžeme použít např. AJAX JSF Matrix. Možnost výběru je skutečně obrovský a to nepočítám další, které vznikají téměř každý den.
Samotná volba není zrovna jednoduchá. Já jsem prošel asi 3 různé knihovny z nichž jsem si vybral jasného favorita: RichFaces.
RichFaces
RichFaces má v současné době v rukou JBoss. Jedná se klasickou JSF komponentovou knihovnu, která ovšem navíc obsahuje i přímou vazbu a podporu A4J (Ajax4JSF). A4J, jak už její název vypovídá se zaměřuje čistě na AJAX a snaží se naučit JSF komponenty asynchroní práci. Jelikož je RichFaces společně s A4J dodávaná jako jedna knihovna (projekt), může nás těšit fakt, že máme jednu z nejlepších kombinaci mezi AJAX-JSF-Komponenta.
V současné době je RichFaces ve verzi 3.2.0, která vyšla vcelku nedávno. Mezi jejími novinkami můžeme najít věci jako: vlastní combo box, progress bar, file upload, atd.
I když je pravda, že s každou novou verzí můžeme nalézt v bugtraceru hromadu opravených a hromadu nový bugů, přesto je tato knihovna použitelná v produkčním nasazení.
Mě nezbývá, než tuto knihovnu doporučit všem, kteří nemají svého favorita a hledají nejakou slušnou podporu pro JSF komponenty. Psát modální okna, kontextová menu či rozbalovací stromy už nemusí být jen výsadou programátoru desktop aplikací.
Pro zběžný přehled doporučuji samotné demo, které je plně přístupné po zaregistrování na RedHat stránkách, kde si jednotlivé vlastnosti můžete ihned vyzkoušet.
úterý 11. března 2008
Seam - tipy a triky (EJB)
Seam je dobře použitelný s EJB, kde jsou jednotlivé EJB vystaveny jako Seam komponenty a navíc obsahují všechny služby, které EJB kontejner nabízí.
Jsou ovšem chvíle, kdy bych potřeboval obyčejnou Seam komponentu a do ní nějakým způsobem vstříknout EJB beanu. Co se týče implementaci pro glassfish, není situace tak jednoduchá, jak by se mohlo zdát.
Takže, mějme příklad, kdy potřebuji vytvořit Seam komponentu, která bude míti jednu metodu, která bude vracet seznam hodnot z nějaké entity.
Klasický přístup by mohl vypadat následovně:
Použití ve facelets stránce by poté vypadalo následovně:
Pravdou je, že použití samotné anotace @Unwrap může mít hezké uplatnění.
Jenže, osobně se mi nelibí možnost, že musím uvnitř samotného "testListu" volat entity manager a navíc psát nějaké OQL. Mám již definovanou DAO vrstvu pomocí EJB a chci lookupnout samotnou EJB jako Seam komponentu.
K tomtuto účelu jsem si vytvořil jednoduchou lookup službu, která vypadá následnovně:
Takže lookup služba slouží k získaní jak EJB beany tak entity manageru popřípadě Hibernate Session. Dá se sice namítnout, že Seam umí přímo injectnout EntityManager (dokonce se to dá hackem zprovoznit i v glassfish), ale já si raději vsrtvy odděluji, proto injectuji daný "persistence provider" jen v nejnutnějších chvílích. Druhou věcí je fakt, že daný provider pro persistenci přenáší jiná EJB beana. Důvodem je fakt, že při inicializaci a vytvoření Seam komponenty ještě není znám provider pro entity manager, proto ho musím získat z jiné EJB.
Ale zpět. K tomu, aby daná lookup služba (která je jak Seam komponentou tak EJB komponentou) byla schopná lookupovat EJB, musí být navíc definovana reference na dané EJB beany v ejb.jar.xml, nebo pomocí anotací. Já jsem zde raději přistoupil k možnosti pomocí XML, jelikož se jedná o speciální požadavek, který se v průběhu života může změnit.
Díky této definici reference mám možnost uvnitř lookupBean získávat ejb dynamicky, tedy přes initialContext.
Konečné použití uvnitř Seam komponenty, která není EJB beanou, může vypadat následovně:
Samozřejmě je možné získat i Entity Manager či Hibernate Session. Použití je v podstatě jasné:
Daný návod je funkční v Seam 2.0, EJB 3 a Glassfish V2.
Jsou ovšem chvíle, kdy bych potřeboval obyčejnou Seam komponentu a do ní nějakým způsobem vstříknout EJB beanu. Co se týče implementaci pro glassfish, není situace tak jednoduchá, jak by se mohlo zdát.
Takže, mějme příklad, kdy potřebuji vytvořit Seam komponentu, která bude míti jednu metodu, která bude vracet seznam hodnot z nějaké entity.
Klasický přístup by mohl vypadat následovně:
@Name("testList")
public class TestList {
@Unwrap
public List<Entita> lookup() {
return entityManager.createQuery("from Entita e").getResultList();
}
}Použití ve facelets stránce by poté vypadalo následovně:
Pravdou je, že použití samotné anotace @Unwrap může mít hezké uplatnění.
Jenže, osobně se mi nelibí možnost, že musím uvnitř samotného "testListu" volat entity manager a navíc psát nějaké OQL. Mám již definovanou DAO vrstvu pomocí EJB a chci lookupnout samotnou EJB jako Seam komponentu.
K tomtuto účelu jsem si vytvořil jednoduchou lookup službu, která vypadá následnovně:
@Stateless
@Name("lookup")
@Scope(ScopeType.APPLICATION)
@Interceptors(value = {SeamInterceptor.class})
public class LookupBean implements LookupLocal {
@Resource
private EJBContext context;
@EJB
private SessionLookupLocal sess;
public Object ejb(String bean) {
return context.lookup(bean);
}
public EntityManager getEntityManager() {
return sess.getEntityManager();
}
public Session getSession() {
return sess.getSession();
}
}Takže lookup služba slouží k získaní jak EJB beany tak entity manageru popřípadě Hibernate Session. Dá se sice namítnout, že Seam umí přímo injectnout EntityManager (dokonce se to dá hackem zprovoznit i v glassfish), ale já si raději vsrtvy odděluji, proto injectuji daný "persistence provider" jen v nejnutnějších chvílích. Druhou věcí je fakt, že daný provider pro persistenci přenáší jiná EJB beana. Důvodem je fakt, že při inicializaci a vytvoření Seam komponenty ještě není znám provider pro entity manager, proto ho musím získat z jiné EJB.
Ale zpět. K tomu, aby daná lookup služba (která je jak Seam komponentou tak EJB komponentou) byla schopná lookupovat EJB, musí být navíc definovana reference na dané EJB beany v ejb.jar.xml, nebo pomocí anotací. Já jsem zde raději přistoupil k možnosti pomocí XML, jelikož se jedná o speciální požadavek, který se v průběhu života může změnit.
LookupBean
cz.irminsul.seam.app.LookupBean
NejakaDAOBean
cz.irminsul.NejakaDAOLocal
NejakaDAOBean
Díky této definici reference mám možnost uvnitř lookupBean získávat ejb dynamicky, tedy přes initialContext.
Konečné použití uvnitř Seam komponenty, která není EJB beanou, může vypadat následovně:
@Name("testList")
public class TestList {
@In(value = "#{lookup.ejb('NejakaBean')}")
private NejakaLocal bean;
private List<Entita> data;
@Unwrap
public List<Entita> lookup() {
if (data == null) {
data = bean.metodaLokalnihoInterface();
}
return data;
}
}Samozřejmě je možné získat i Entity Manager či Hibernate Session. Použití je v podstatě jasné:
@In("#{lookup.entityMananger}")
private EntityManager em;Daný návod je funkční v Seam 2.0, EJB 3 a Glassfish V2.
pondělí 18. února 2008
DTO a ORM
Pojem DTO jistě není třeba představovat. Jedná se o objekt reprezentující data, které je třeba přenést z jedné strany na druhou. Asi nejčastějším využitím jest výsledek dotazu z persistentní vrstvy.
Při použití ORM frameworku, jako je např. Hibernate, definuji data v DB pomocí objektů (entit). Tyto entity poté mohou být i výsledkem, tedy mohou představovat jak doménový model aplikace, tak i dané DTO. Jsou ovšem chvíle, kdy takové DTO je nepoužitelné či jeho použití může znamenat výrazný pokles výkonnosti aplikace.
Prvním příkladem, kde entita nemůže (neměla by) být reprezentována jako DTO:
"Vytvoř seznam zaměstnanců s celkovým počtem odpracovaných hodin."
V takovém případě je třeba výsledek z OQL přemapovat do vlastního objektu (DTO), který bude obsahovat číslo, jméno, příjmení, celkový počet hodin. Sice by někdo mohl namítnout, že daná hodnota (odpracované hodiny) se dá do entity, jako @Transient, přidat. Ale doménový model bych se neměl "špinit" vlastnostmi, které reprezentují pouze výsledek nějakého dotazu.
Druhým příkladem je distribuovaná Java. Jinými slovy řečeno, ve chvíli, kdy vzdáleně volám remote EJB, by výsledkem mělo být pouze to, co skutečně potřebuji. Nikoli anotovaná entita, která obsahuje mapované kolekce a dalších X vlastností, které mě v té chvíli nezajímají. Dodržení této zásady bude mít pozitivní vliv na výkon aplikace.
Důvody, proč je někdy lepší použit DTO namísto entity, jsem uvedl. Nyní ovšem přichází otázka, jak takové DTO přemapovávat z OQL dotazů a jak si co nejvíce ušetřit nudného psaní kódu.
První možností je použití klauzule new z JPA. Takové OQL by mohlo vypadat následovně:
Jak je na první pohled patrné, je třeba, aby objekt "ZamPocetHodin" obsahoval konstruktor, který obsahuje dané parametry podle daného OQL.
Tento způsob má vesměs samé nevýhody. Osobně mi asi nejvíce vadí samotná absence jakéhokoli refactoringu. Myslím, že v dnešní době neexistuje IDE, které by dokázalo poznat OQL dotazy a validovat je. Dále mi vadí, že parametrem v konstruktoru DTO může být pouze omezený výčet typů. Není možné vracet jiné Entity, či je nějak dále přemapovat. Poslední věc, která se mi nelíbí, je fakt, že píši zbytečně mnoho kódu. Někde definuji DTO a někde ho musím plnit a přitom stále hlídat, zda se mi něco nezměnilo.
Druhou možností je plnění pomoci Hibernate Criteria API:
Uvedl jsem jen malý kus kódu, který úplně nedpovídá všednímu použití, ale jde o to, že dost často je hodnota v DTO reprezentována nějakým poddotazem, či jiným způsobem, které SQL umožňuje. Je pravda, že v Hibernate Projections si vše mohu dobře nadefinovat, ale výsledkem je poté naprosto šílený kód, který je sice "dynamičtější" a "programovější" než je tomu u JPA, ale za to je složitější.
Jelikož mi nevyhovuje ani jeden z daných způsobů, snažil jsem se nalézt nějaký vhodný způsob, jak se zbavit toho nudného či složitého psaní na přemapování do DTO.
Po pečlivém rozmýšlení jsem se rozhodl, že by nebylo špatné nahlédnout na danou věc stejně jako v případě mé implementace "Irminsul Criteria". O co vlastně šlo, si můžete přečíst zde a zde.
Cílém mého snažení bylo, abych vytvořil DTO objekt, který pomocí anotací nad atributem bude obsahovat dané SQL příkazy, které naplní příslušnou hodnotu. V této chvíli se jedná stále o testování, ale výsledkem mého snažení je již celkem funkční základní část, tedy jednoduché přemapování.
Nyní uvedu malý příklad takového přemapování:
Jak je z kódu patrné, jde pouze o klasické POJO, které je anotované, aby implementace pochopila, že jde o DTO, které je spouštěne nad danou entitou (@DTO) a obsahuje ty či ony aliasy, které usnadňuj psaní ProjectionSQL.
K tomuto účelu jsem využil implementace Hibernate Projections a ProjectionsSQL, kde podle jasných pravidel převádím dané anotace na projekci, která je již obeznámena s daným typem a tím, co vlastně daný atribut reprezentuje.
Opěvná píseň
Předem musím poznamenat, že toho dané DTO umí více, jednak je to možnost spojit toto DTO i s Filtr objektem, který jsem popisoval ve výše odkazovaných člancích. Výsledkem je pote jednoduche volání:
Již se tedy nemusím zabývat nutností definování filtru a vrácených dat, oba stavy jsou řízeny anotacemi.
Dalšími možnostmi jsou řazení, získaní hodnoty přes alias, či přes vlastní definici, která může být ve formě SQL či OQL zápisu.
Díky tomu odpadá několik věcí:
Až bude daná funčnost plně funkční, nabídnu své řešení ke stažení. Zatím bych nerad někde publikoval zabugovaný nedodělek :)
Zajímal by mě Váš názor na takovouto implementaci DTO. Je dost možné, že někdo přijde s lepším nápadem či možností, která již dávno existuje a jen já o ni nevím :)
Při použití ORM frameworku, jako je např. Hibernate, definuji data v DB pomocí objektů (entit). Tyto entity poté mohou být i výsledkem, tedy mohou představovat jak doménový model aplikace, tak i dané DTO. Jsou ovšem chvíle, kdy takové DTO je nepoužitelné či jeho použití může znamenat výrazný pokles výkonnosti aplikace.
Prvním příkladem, kde entita nemůže (neměla by) být reprezentována jako DTO:
"Vytvoř seznam zaměstnanců s celkovým počtem odpracovaných hodin."
V takovém případě je třeba výsledek z OQL přemapovat do vlastního objektu (DTO), který bude obsahovat číslo, jméno, příjmení, celkový počet hodin. Sice by někdo mohl namítnout, že daná hodnota (odpracované hodiny) se dá do entity, jako @Transient, přidat. Ale doménový model bych se neměl "špinit" vlastnostmi, které reprezentují pouze výsledek nějakého dotazu.
Druhým příkladem je distribuovaná Java. Jinými slovy řečeno, ve chvíli, kdy vzdáleně volám remote EJB, by výsledkem mělo být pouze to, co skutečně potřebuji. Nikoli anotovaná entita, která obsahuje mapované kolekce a dalších X vlastností, které mě v té chvíli nezajímají. Dodržení této zásady bude mít pozitivní vliv na výkon aplikace.
Důvody, proč je někdy lepší použit DTO namísto entity, jsem uvedl. Nyní ovšem přichází otázka, jak takové DTO přemapovávat z OQL dotazů a jak si co nejvíce ušetřit nudného psaní kódu.
První možností je použití klauzule new z JPA. Takové OQL by mohlo vypadat následovně:
public List getList() {
String oql = "SELECT new ZamPocetHodin(z.cislo, z.prijmeni, z.jmeno, x.vypocetHodin) FROM Zamestnanci z .....";
return entityManager.createQuery(oql).getResultList();
} Jak je na první pohled patrné, je třeba, aby objekt "ZamPocetHodin" obsahoval konstruktor, který obsahuje dané parametry podle daného OQL.
Tento způsob má vesměs samé nevýhody. Osobně mi asi nejvíce vadí samotná absence jakéhokoli refactoringu. Myslím, že v dnešní době neexistuje IDE, které by dokázalo poznat OQL dotazy a validovat je. Dále mi vadí, že parametrem v konstruktoru DTO může být pouze omezený výčet typů. Není možné vracet jiné Entity, či je nějak dále přemapovat. Poslední věc, která se mi nelíbí, je fakt, že píši zbytečně mnoho kódu. Někde definuji DTO a někde ho musím plnit a přitom stále hlídat, zda se mi něco nezměnilo.
Druhou možností je plnění pomoci Hibernate Criteria API:
criteria.setProjection(Projections.sqlProjection(sql + " AS " + alias,
new String[]{alias},
new org.hibernate.type.Type[] {TypeWrapperImpl.getType(/* type */)}));
criteria.setResultTransformer(Transformers.aliasToBean(ZamPocetHodin.class));Uvedl jsem jen malý kus kódu, který úplně nedpovídá všednímu použití, ale jde o to, že dost často je hodnota v DTO reprezentována nějakým poddotazem, či jiným způsobem, které SQL umožňuje. Je pravda, že v Hibernate Projections si vše mohu dobře nadefinovat, ale výsledkem je poté naprosto šílený kód, který je sice "dynamičtější" a "programovější" než je tomu u JPA, ale za to je složitější.
Jelikož mi nevyhovuje ani jeden z daných způsobů, snažil jsem se nalézt nějaký vhodný způsob, jak se zbavit toho nudného či složitého psaní na přemapování do DTO.
Po pečlivém rozmýšlení jsem se rozhodl, že by nebylo špatné nahlédnout na danou věc stejně jako v případě mé implementace "Irminsul Criteria". O co vlastně šlo, si můžete přečíst zde a zde.
Cílém mého snažení bylo, abych vytvořil DTO objekt, který pomocí anotací nad atributem bude obsahovat dané SQL příkazy, které naplní příslušnou hodnotu. V této chvíli se jedná stále o testování, ale výsledkem mého snažení je již celkem funkční základní část, tedy jednoduché přemapování.
Nyní uvedu malý příklad takového přemapování:
@DTO(entity = EntitaOdkudSeDataZiskavaji.class)
@Aliases(values={
@Alias(associationPath="strediska.provoz", alias="provoz"),
@Alias(associationPath="rj.testPolozky.polozka", alias="polozka"),
.....
})
public class TestDTO implements Serializable {
@ProjectionSQL("(({polozka}.faktura * {vicePraceMena}.aktualni_kurz) * (procento / 100))")
private BigDecimal opravy;
@ProjectionSQL("(({polozkaZakazky}.cena * {mena}.aktualni_kurz) * (procento / 100))")
private BigDecimal cenaVyrobku;
@Projection("pk.cislo")
private String cislo;
// settry, gettry, atd.
}
List result = factory.findForDTO(TestDTO.class);
Jak je z kódu patrné, jde pouze o klasické POJO, které je anotované, aby implementace pochopila, že jde o DTO, které je spouštěne nad danou entitou (@DTO) a obsahuje ty či ony aliasy, které usnadňuj psaní ProjectionSQL.
K tomuto účelu jsem využil implementace Hibernate Projections a ProjectionsSQL, kde podle jasných pravidel převádím dané anotace na projekci, která je již obeznámena s daným typem a tím, co vlastně daný atribut reprezentuje.
Opěvná píseň
Předem musím poznamenat, že toho dané DTO umí více, jednak je to možnost spojit toto DTO i s Filtr objektem, který jsem popisoval ve výše odkazovaných člancích. Výsledkem je pote jednoduche volání:
List result = factory.findByCriteriaDTO(filtr, TestDTO.class); Již se tedy nemusím zabývat nutností definování filtru a vrácených dat, oba stavy jsou řízeny anotacemi.
Dalšími možnostmi jsou řazení, získaní hodnoty přes alias, či přes vlastní definici, která může být ve formě SQL či OQL zápisu.
Díky tomu odpadá několik věcí:
- není třeba definovat nějaké metody v DAO
- nemusí se psát žadné přemapování, stačí jen definice DTO
- není třeba hledat, jak se dané DTO plní, je to jasné z jeho definice
- není třeba se bát refactoringu jako v případě implementace přes JPA
- není třeba určovat vrácený typ jako v případě Hibernate, typ je již jasný z definice atributu třídy DTO
Až bude daná funčnost plně funkční, nabídnu své řešení ke stažení. Zatím bych nerad někde publikoval zabugovaný nedodělek :)
Zajímal by mě Váš názor na takovouto implementaci DTO. Je dost možné, že někdo přijde s lepším nápadem či možností, která již dávno existuje a jen já o ni nevím :)
neděle 17. února 2008
Seam - tipy a triky
Jelikož jsem zastánce tohoto frameworku, z jichž uvedených důvodů, rozhodl jsem se sepsat pár dobrých triků, které mohou pomoci při vývoji s tímto frameworkem. Dnes začnu asi tou nezajímavější a tím je Hot Deploy, nebo-li rychlá aktualizace některých částí aplikace bez nutnosti provádět celé nahrání aplikace na server.
Hot deploy - NetBeans
Netbeans 6.0 má dost slabou, respektive nulovou, podporu pro tento framework. Jednou z dobrých vlastností Seamu je tzv. hot deploy, kde změny na view vrtvě mohou být aplikovány bez nutnosti provádení undeploy-deploy aplikace. Jedná se zejména o změnu facelets stránek. Takže jak něčeho takového dosáhnout v netbeans?
Stačí vytvořit vlastní "ResourceResolver", který se poté zaregistruje v web.xml.
Příklad takového ResourceResolver:
Poté již stačí uvést registraci do web.xml:
Důležitou součástí je to, aby Seam byl spuštěn v debug módu. Toho lze docílit pomocí definice v components.xml:
Díky vlastní definici "ResourceResolver", jsou nyní jednotlivé facelets stránky načítány z cesty, která je uvedena v "PATH_TO_DEVELOPMENT". Pro změnu stačí v NetBeans editovat nějakou facelets stránku, uložit změnu a ve webovém prohlížeči provést refresh.
Tato malá pomůcka je ideální zejména ve chvíli, kdy jednotlivé facelets stránky obsahují různé komponenty (rich faces, ajax4jsf, atd.). Podle toho, co jsem se dočetl v Seam dokumentaci, tak se pracuje i na možnosti provádět hot deploy nad EJB. Otázkou je, jestli tato vlastnost není spíš více ovlivněna aplikačním serverem, než tímto frameworkem.
Takový hot deploy nad EJB se dá sice také provést (v debug modu pro AS), ale jedná se o velice omezené možnosti. Není možné vytvořit novou EJB za běhu, zmenu public metod, atd. Jde tedy čistě o vnitřní implementaci uvnitř business metod. Doufám, že se jednou dočkáme změn i v této oblasti.
Hot deploy - NetBeans
Netbeans 6.0 má dost slabou, respektive nulovou, podporu pro tento framework. Jednou z dobrých vlastností Seamu je tzv. hot deploy, kde změny na view vrtvě mohou být aplikovány bez nutnosti provádení undeploy-deploy aplikace. Jedná se zejména o změnu facelets stránek. Takže jak něčeho takového dosáhnout v netbeans?
Stačí vytvořit vlastní "ResourceResolver", který se poté zaregistruje v web.xml.
Příklad takového ResourceResolver:
public class FilesystemResourceResolver implements ResourceResolver {
private static final String PATH_TO_DEVELOPMENT = "/home/ales/dev/java/Irminsul/Irminsul-war/web/";
public URL resolveUrl(String s) {
try {
return new URL("file", "", PATH_TO_DEVELOPMENT + s);
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
}Poté již stačí uvést registraci do web.xml:
facelets.DEVELOPMENT
true
facelets.REFRESH_PERIOD
0
facelets.RESOURCE_RESOLVER
irminsulweb.FilesystemResourceResolver
Důležitou součástí je to, aby Seam byl spuštěn v debug módu. Toho lze docílit pomocí definice v components.xml:
Díky vlastní definici "ResourceResolver", jsou nyní jednotlivé facelets stránky načítány z cesty, která je uvedena v "PATH_TO_DEVELOPMENT". Pro změnu stačí v NetBeans editovat nějakou facelets stránku, uložit změnu a ve webovém prohlížeči provést refresh.
Tato malá pomůcka je ideální zejména ve chvíli, kdy jednotlivé facelets stránky obsahují různé komponenty (rich faces, ajax4jsf, atd.). Podle toho, co jsem se dočetl v Seam dokumentaci, tak se pracuje i na možnosti provádět hot deploy nad EJB. Otázkou je, jestli tato vlastnost není spíš více ovlivněna aplikačním serverem, než tímto frameworkem.
Takový hot deploy nad EJB se dá sice také provést (v debug modu pro AS), ale jedná se o velice omezené možnosti. Není možné vytvořit novou EJB za běhu, zmenu public metod, atd. Jde tedy čistě o vnitřní implementaci uvnitř business metod. Doufám, že se jednou dočkáme změn i v této oblasti.
sobota 12. ledna 2008
Linux pro javistu
Na svých oblíbených weblozích jsem našel několik příspěvků, které se věnovali linuxu, coby hlavnímu operačnímu systému pro programátora. Psal o tom dagi či ronnie. Jeden je v navážkách, druhý utekl zpět k windows.
Abych nebyl výjimkou, rozhodl jsem se, že ho teda také vyzkouším. Linux jako systém považuji za velice úspěšný na serveru, co se týče osobního desktopu, vždy jsem našel něco, co mě přinutilo vrátit se k windows. Dnes již tomu tak není a já se pokusím sepsat vše, co mě monentálně k tomuto tématu napadá.
Proč práve linux?
Není v tom nic speciálního. Na serveru nám běží bez problémů a já si chtěl vyzkoušet, zda by můj notebook nebyl s tímto systémem "rychlejší" a "efektivnější". Nehledám v tom nic převratného, a ani nechci zkoušet něco, abych jen vybočoval z "řady windowsáků".
Linux tedy zkouším čistě ze zvědavosti s nadějí, že objevím něco "užitečného".
Výběr distribuce a HW
Výběr distribucí linuxu je ohromný. Má volba padla na SuSE, jednak s touto distribucí mám trochu zkušenností a jednak ji mám instalovanou i na domácím PC, který funguje jako server.
Samozřejmě, že v "módě" je dnes Ubuntu, ale abych byl upřímný, samotná komunita, alespoň v ČR, mi tak leze na nervy, že jsem zde zaujal šovinistické stanovisko :)
Distribucí je tedy SuSE 10.3 s GNOME.
Co se týče hardwaru, instaloval jsem jej na notebook Toshiba A100-847. Jedná se o 2 jádrový Core 2 Duo, kde jsem pouze rozšířil RAM z 512MB na 2GB.
Instalace
Po rozdělení disku jsem na menší oddíl (cca asi 36GB) nainstaloval SuSE. Samotná instalace proběhla v pořádku a já si mnul ruce, jak vše dopadlo hladce. Jenže, to byl nebyl linux, aby v něm nebyl zádrhel... :)
První setkání
Při prvním pohledu se zdálo, že je vše v pořádku. Bohužel mi nefungovala WiFi a připojení druhého LCD monitoru. Po jistém vyladění ostatních věcí se mi asi po 6 hodinách povedlo rozchodit bezdrátové připojení. Bohužel u monitoru jsem nepochodil. Ani ruční konfigurace xorg.conf nepomohla a já prozatím musel monitor nahradit dalšími plochami.
Při zpětném porovnání mohu tvrdit, že kromě připojení druhého LCD je vše funkční. Samozřejmě se nevzdávám a až bude více času, pokusím se dořešit i to. Většinou vše má své řešení :)
Práce
Jelikož nepobírám mzdu za hraní si s linuxem, ale za vývoj aplikací, tak jsem se pustil do konečného nastavení. Tedy instalace JDK 1.6, Netbeans a Glassfish.
A zde to přišlo. Důvod, proč mě k windows již nikdo nedostane!
Samotný běh NetBeans je rychlejší, super. Ale co je naprosto nepochopitelné, že build&deploy projektu, který ve windows (se stejným nastavením) trvá minutu - minutu a půl, zde trvá asi 14-16 vteřin. Nevěřil jsem vlastním očím. Říkám si, asi bude někde problém v nastavení na windows. Nikoli. I samotný build projektů je prostě rychlejší a to výrazně. Sice jsem neprováděl žádné měření, ale poznal jsem na vlastní kůži, jak je to s rychlostí.
Alternativy k programům
Zde jsem nemusel nic moc řešit. Za prvé používám Javu, která je multiplatformní a za druhé programy, které jsou i pro linux. Zde jsem udělal jejich částečný výčet:
Nic víc ke své práci nepotřebuji. Jediný problém je s tím Total Commanderem. Skutečná alternativa neexistuje. Jediné, co mě čeká je, najít si nejpříjemnější alternativu, kterou zatím nemám.
Závěr
Pokud se nevyskytne skutečný problém, tak mě asi od linuxu již nic neodtáhne. Nemíním tvrdit, že windows je špatný, pouze nevyhovující pro určité typy lidí. Stejné je to i naopak. Ve chvíli, kdy jsem si nakonfiguroval klávesové zkratky, přidal přepínání ploch, zjistil jsem, že jsem shopen i efektivněji pracovat. Windows se spíše drží filozofie myš-akce, já mám raději klávesnici, protože je mnohonásobně rychlejší.
Nakonec nabízím pár screenshotů z mého nového systému:
Abych nebyl výjimkou, rozhodl jsem se, že ho teda také vyzkouším. Linux jako systém považuji za velice úspěšný na serveru, co se týče osobního desktopu, vždy jsem našel něco, co mě přinutilo vrátit se k windows. Dnes již tomu tak není a já se pokusím sepsat vše, co mě monentálně k tomuto tématu napadá.
Proč práve linux?
Není v tom nic speciálního. Na serveru nám běží bez problémů a já si chtěl vyzkoušet, zda by můj notebook nebyl s tímto systémem "rychlejší" a "efektivnější". Nehledám v tom nic převratného, a ani nechci zkoušet něco, abych jen vybočoval z "řady windowsáků".
Linux tedy zkouším čistě ze zvědavosti s nadějí, že objevím něco "užitečného".
Výběr distribuce a HW
Výběr distribucí linuxu je ohromný. Má volba padla na SuSE, jednak s touto distribucí mám trochu zkušenností a jednak ji mám instalovanou i na domácím PC, který funguje jako server.
Samozřejmě, že v "módě" je dnes Ubuntu, ale abych byl upřímný, samotná komunita, alespoň v ČR, mi tak leze na nervy, že jsem zde zaujal šovinistické stanovisko :)
Distribucí je tedy SuSE 10.3 s GNOME.
Co se týče hardwaru, instaloval jsem jej na notebook Toshiba A100-847. Jedná se o 2 jádrový Core 2 Duo, kde jsem pouze rozšířil RAM z 512MB na 2GB.
Instalace
Po rozdělení disku jsem na menší oddíl (cca asi 36GB) nainstaloval SuSE. Samotná instalace proběhla v pořádku a já si mnul ruce, jak vše dopadlo hladce. Jenže, to byl nebyl linux, aby v něm nebyl zádrhel... :)
První setkání
Při prvním pohledu se zdálo, že je vše v pořádku. Bohužel mi nefungovala WiFi a připojení druhého LCD monitoru. Po jistém vyladění ostatních věcí se mi asi po 6 hodinách povedlo rozchodit bezdrátové připojení. Bohužel u monitoru jsem nepochodil. Ani ruční konfigurace xorg.conf nepomohla a já prozatím musel monitor nahradit dalšími plochami.
Při zpětném porovnání mohu tvrdit, že kromě připojení druhého LCD je vše funkční. Samozřejmě se nevzdávám a až bude více času, pokusím se dořešit i to. Většinou vše má své řešení :)
Práce
Jelikož nepobírám mzdu za hraní si s linuxem, ale za vývoj aplikací, tak jsem se pustil do konečného nastavení. Tedy instalace JDK 1.6, Netbeans a Glassfish.
A zde to přišlo. Důvod, proč mě k windows již nikdo nedostane!
Samotný běh NetBeans je rychlejší, super. Ale co je naprosto nepochopitelné, že build&deploy projektu, který ve windows (se stejným nastavením) trvá minutu - minutu a půl, zde trvá asi 14-16 vteřin. Nevěřil jsem vlastním očím. Říkám si, asi bude někde problém v nastavení na windows. Nikoli. I samotný build projektů je prostě rychlejší a to výrazně. Sice jsem neprováděl žádné měření, ale poznal jsem na vlastní kůži, jak je to s rychlostí.
Alternativy k programům
Zde jsem nemusel nic moc řešit. Za prvé používám Javu, která je multiplatformní a za druhé programy, které jsou i pro linux. Zde jsem udělal jejich částečný výčet:
- thunderbird - thunderbird
- opera - opera
- netbeans - netbeans
- pidgin - pidgin
- total commander - midnight commander, gnome commander, muCommander
- launchy - deskbar applet
- putty - ssh
Nic víc ke své práci nepotřebuji. Jediný problém je s tím Total Commanderem. Skutečná alternativa neexistuje. Jediné, co mě čeká je, najít si nejpříjemnější alternativu, kterou zatím nemám.
Závěr
Pokud se nevyskytne skutečný problém, tak mě asi od linuxu již nic neodtáhne. Nemíním tvrdit, že windows je špatný, pouze nevyhovující pro určité typy lidí. Stejné je to i naopak. Ve chvíli, kdy jsem si nakonfiguroval klávesové zkratky, přidal přepínání ploch, zjistil jsem, že jsem shopen i efektivněji pracovat. Windows se spíše drží filozofie myš-akce, já mám raději klávesnici, protože je mnohonásobně rychlejší.
Nakonec nabízím pár screenshotů z mého nového systému:
čtvrtek 27. prosince 2007
Anotace nahrazující SQL a OQL dotazy (popis)
Bez dalších řečí se rovnou pustím do popisu.
Jak už jsem předeslal, základem dané filtrace nad daty, jsou anotace. Uvedu jejich výčet a popis.
@Criteria (class)
Jedná se o základní anotaci, která říká, že se jedná o "filtrační" objekt, který je schopen nabídnout své atributy k porovnání. Jediným parametrem je entita, která říká o jakou entitu se jedná.
@Criterion (atribut)
Popisuje atribut, který je automaticky brán jako jeden z prvků filtrace. Samotná anotace obsahuje moznosti, které označí, jak se k danému atributu bude přistupovat.
Zde je výčet některých z nich:
V budoucnu předpokládám rozšíření těchto vlastností. Ale držím se pravidla: Méně je někdy více. Přeci jen nechci tvořit kód, který bude obsahovat spoustu deprekovaných způsobů.
@Between (atribut)
Složení minimální a maximální hodnoty, která je prováděna na základě definovaného idf a property, což je výčtový typ, který může být buď MIN či MAX. Výhodou tohoto použití je také v tom, že pokud jedna z hodnot neexistuje, automaticky převádí BETWEEN na porovnání jedné hodnoty. Pokud například hodnota pro MAX bude empty a MIN bude existovat, bude generována následující podmínka: WHERE datum >= 'hodnota'.
@Conjunction (atribut)
Konjunkce funguje podobně jako v Criteria API. Na základě definovaného idf se spojí ty hodnoty, které k sobě patří. Zde ovšem existuje jedno omezení (stejně jako u disjunkce): Vnořené podmínky. Je to další krok, který budu muset vyřešit.
@Disjunction (atribut)
Opět podobný způsob jako v případě konjunkce. Zde se tedy generuje něco jako: WHERE (property [operator] value OR ....). Spojení daných atributu se opět provádí pomocí stejného názvu v idf.
@Alias (class)
Jelikož se často stává, že se potřebuji dotazovat na sloupec, který je v nějaké tabulce, která je v relaci s danou entitou, potřebuji aliasovat danou entitu, pro kterou bude platit nasledující alias.
Zde rovnou uvedu příklad:
Mám zaměstnance, který spadá do daného střediska, z kterého chci znát název, podle kterého filtruji. V OQL by mohl dotaz vypadat následovně: FROM Zamestnanec z WHERE z.stredisko.nazev = 'Vyroba'. V Criteria API se k danému sloupci dostanu přes alias, takže by to mohlo vypadat následovně: @Alias(associationPath = "stredisko", alias = "s").
Aby bylo možné definovat aliasů více, existuje anotace s nazvem Aliases, která může obsahovat pole daných aliasů.
@OrderBy (class)
Poslední popisovaná anotace slouží k definování řazení dat. Jelikož požadavek na řazení dat je častým jevem, existuje možnost nadefinovat filtračnímu objektu tuto vlastnost. Daná anotace obsahuje pole anotací s názvem @OrderValue, která má dvě vlastností a tím je název atributu pro řazení a typ řazení, který je výčtvový typ ASC nebo DESC.
Samotné použití je postaveno na DetachedCriteria. Ti, co znají Hibernate vědí, že se jedná o criteria, které neobsahují Session. Jinými slovy, takováto criteria si můžete vytvořit ještě v době, kdy o Hibernate Session nemá Váš kod ani ponětí. Jistě je to dobrý způsob jak zajistit určité odělení od zbytku vlastní implementace.
K přístupu slouží třída s názvem: "CriteriaDAOFactory". Jak už název napovídá, jedná se o factory, která vrací interface CriteriaDAO. Daný interface již obsahuje metodu vracející DetachedCriteria.
Dané použití může vypadat následovně:
Nyní uvedu několik příkladů definování daného filtru a jeho ekvivalent v podobě SQL dotazu. U všech ukázek vynechám settry a gettry, které jsou nezbytnou součástí. Pokud někdo chce tvořit neměnný objekt, tak samozřejme settry může vynechat. Takže jedem:
Tím bych asi ukončil tento popis. Samozřejmě, lze dané věci dobře kombinovat. Je jasné, že se naleznou další potřebné funkce, které nechám spíše vyplynout postupem času. Pokud by někdo měl zájem, mohu se pokusit danou funčnost implementovat.
Poslední věcí, kterou chci zmínit je pár otázek, které bych rád nějak vyřešil. Jelikož vše má svá úskalí... Pokud bude mít někdo zájem, budu vítat jakoukoli pomoc či nakopnutí.
Samotný projekt je psán v NetBeans, proto ho také jako NetBeans projekt vystavuji. Předem se omlouvám za absenci JUnit testů. Upřímně, zatím jsem neměl dostatek času vše otestovat a navíc testy mám komponovány přímo na danou specifikaci, ke které je třeba existence daného modelu a zdroje. Samotná implementace je závislá na Hibernate API a na log4j. Knihovna log4j je v adresáři lib, knihovny pro Hibernate neuvádím záměrně. Pro práci s Hibernate existuje mnohem větší závislost :)
Projekt ke stažení: IrminsulCriteria
Jak už jsem předeslal, základem dané filtrace nad daty, jsou anotace. Uvedu jejich výčet a popis.
@Criteria (class)
Jedná se o základní anotaci, která říká, že se jedná o "filtrační" objekt, který je schopen nabídnout své atributy k porovnání. Jediným parametrem je entita, která říká o jakou entitu se jedná.
@Criterion (atribut)
Popisuje atribut, který je automaticky brán jako jeden z prvků filtrace. Samotná anotace obsahuje moznosti, které označí, jak se k danému atributu bude přistupovat.
Zde je výčet některých z nich:
- property - název atributu podle dané entity, může se jednat i o vnořenou hodnotu (zakaznik.id)
- operator - definuje typ porovnávání mezi hodnotou atributu a názvem atributu, typ porovnávání je výčtový typ, který lze rozšířit
- excludeEmptyString - určuje, zda se bude brát v potaz prázdný String či se bude ignorovat
V budoucnu předpokládám rozšíření těchto vlastností. Ale držím se pravidla: Méně je někdy více. Přeci jen nechci tvořit kód, který bude obsahovat spoustu deprekovaných způsobů.
@Between (atribut)
Složení minimální a maximální hodnoty, která je prováděna na základě definovaného idf a property, což je výčtový typ, který může být buď MIN či MAX. Výhodou tohoto použití je také v tom, že pokud jedna z hodnot neexistuje, automaticky převádí BETWEEN na porovnání jedné hodnoty. Pokud například hodnota pro MAX bude empty a MIN bude existovat, bude generována následující podmínka: WHERE datum >= 'hodnota'.
@Conjunction (atribut)
Konjunkce funguje podobně jako v Criteria API. Na základě definovaného idf se spojí ty hodnoty, které k sobě patří. Zde ovšem existuje jedno omezení (stejně jako u disjunkce): Vnořené podmínky. Je to další krok, který budu muset vyřešit.
@Disjunction (atribut)
Opět podobný způsob jako v případě konjunkce. Zde se tedy generuje něco jako: WHERE (property [operator] value OR ....). Spojení daných atributu se opět provádí pomocí stejného názvu v idf.
@Alias (class)
Jelikož se často stává, že se potřebuji dotazovat na sloupec, který je v nějaké tabulce, která je v relaci s danou entitou, potřebuji aliasovat danou entitu, pro kterou bude platit nasledující alias.
Zde rovnou uvedu příklad:
Mám zaměstnance, který spadá do daného střediska, z kterého chci znát název, podle kterého filtruji. V OQL by mohl dotaz vypadat následovně: FROM Zamestnanec z WHERE z.stredisko.nazev = 'Vyroba'. V Criteria API se k danému sloupci dostanu přes alias, takže by to mohlo vypadat následovně: @Alias(associationPath = "stredisko", alias = "s").
Aby bylo možné definovat aliasů více, existuje anotace s nazvem Aliases, která může obsahovat pole daných aliasů.
@OrderBy (class)
Poslední popisovaná anotace slouží k definování řazení dat. Jelikož požadavek na řazení dat je častým jevem, existuje možnost nadefinovat filtračnímu objektu tuto vlastnost. Daná anotace obsahuje pole anotací s názvem @OrderValue, která má dvě vlastností a tím je název atributu pro řazení a typ řazení, který je výčtvový typ ASC nebo DESC.
Samotné použití je postaveno na DetachedCriteria. Ti, co znají Hibernate vědí, že se jedná o criteria, které neobsahují Session. Jinými slovy, takováto criteria si můžete vytvořit ještě v době, kdy o Hibernate Session nemá Váš kod ani ponětí. Jistě je to dobrý způsob jak zajistit určité odělení od zbytku vlastní implementace.
K přístupu slouží třída s názvem: "CriteriaDAOFactory". Jak už název napovídá, jedná se o factory, která vrací interface CriteriaDAO. Daný interface již obsahuje metodu vracející DetachedCriteria.
Dané použití může vypadat následovně:
MyFiltr filtr = new MyFiltr();
// naplneni filtru
CriteriaDAO d = CriteriaDAOFactory.getCriteria(filtr);
DetachedCriteria dc = d.getDetachedCriteria();Nyní uvedu několik příkladů definování daného filtru a jeho ekvivalent v podobě SQL dotazu. U všech ukázek vynechám settry a gettry, které jsou nezbytnou součástí. Pokud někdo chce tvořit neměnný objekt, tak samozřejme settry může vynechat. Takže jedem:
@Criteria(entity = Zamestnanec.class)
public class ZamestnanecFiltr {
@Criterion(property="cislo")
private String id;
@Criterion(operator=RestrictionDAO.LIKE)
private String prijmeni;
}SELECT * FROM zamestnanec
WHERE cislo = 'value' AND prijmeni LIKE '%value%'@Criteria(entity = Zamestnanec.class)
@OrderBy(values={@OrderValue(name = "prijmeni")})
@Alias(associationPath = "stredisko", alias = "s")
public class ZamestnanecFiltr {
@Criterion(property="s.nazev")
private String nazev;
@Criterion(property="datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MIN)
private Date datumOd;
@Criterion(property="datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MAX)
private Date datumDo;
}SELECT * FROM zamestnanec z
INNER JOIN stredisko s ON z.stredisko = s.cislo
WHERE s.nazev = 'value' AND z.datumPrijeti BETWEEN 'min' AND 'max'
ORDER BY z.prijmeniTím bych asi ukončil tento popis. Samozřejmě, lze dané věci dobře kombinovat. Je jasné, že se naleznou další potřebné funkce, které nechám spíše vyplynout postupem času. Pokud by někdo měl zájem, mohu se pokusit danou funčnost implementovat.
Poslední věcí, kterou chci zmínit je pár otázek, které bych rád nějak vyřešil. Jelikož vše má svá úskalí... Pokud bude mít někdo zájem, budu vítat jakoukoli pomoc či nakopnutí.
- řazení dat je definováno staticky, jak implementovat dynamický způsob
- specifikování získaných dat, jak omezení na určité sloupce, tak vlastní DTO či inicializace LAZY
- vnořené konjunkce a disjunkce, které závisí na předchozích podmínkách, a zda má vůbec cenu něco takového implementovat
- dynamické rozhodování zvoleného operátoru, uživatel například může mít k dispozici volbu mezi (LIKE, <, >, =)
Samotný projekt je psán v NetBeans, proto ho také jako NetBeans projekt vystavuji. Předem se omlouvám za absenci JUnit testů. Upřímně, zatím jsem neměl dostatek času vše otestovat a navíc testy mám komponovány přímo na danou specifikaci, ke které je třeba existence daného modelu a zdroje. Samotná implementace je závislá na Hibernate API a na log4j. Knihovna log4j je v adresáři lib, knihovny pro Hibernate neuvádím záměrně. Pro práci s Hibernate existuje mnohem větší závislost :)
Projekt ke stažení: IrminsulCriteria
Anotace nahrazující SQL a OQL dotazy (důvody)
V minulém příspěvku jsem psal o tom, jakým způsobem nadefinovat základní DAO vrstvu. Zvolil jsem způsob, který se stal zavislý na Hibernate Session. Tento "vendor" nabízí i jednu z věcí, kterou jsem si pomalu oblíbil, a tím jsou Criteria API.
Psaní základních SQL dotazů jsem nahrazil OQL dotazy. OQL je v podstatě velice podobný SQL s tím rozdílem, že daný dotaz se provádí nad entitami reprezentující data v DB. Díky tomu odpadá nutnost psát ruzné JOIN konstrukce, specifikovat vrácené sloupce a hlavně získávám nezávislost na použitém databázovém systému. Převod z MySQL na Oracle či MSSQL je triviální záležitost spočívající v přepsání pár řádku v persistence.xml či v přepsání JDBC zdroje v aplikačním serveru.
Ale zpět. I když je OQL velice elegantní způsob psaní, stále obsahuje velké omezení, které spočívá v tvorbě dynamických dotazů. Pokud aplikace přistupuje k datům tak, že je získává na základě uživatelského filtru, musím dotaz poskládat z daného Stringu, což je dost otravující záležitost.
K tomuto účelu se velice hodí Criteria API. Nebudu zde popisovat funkci Criteria API, k tomuto účelu slouží referenční příručka. U Criteria API se mi velice zalíbila možnost filtrovat data přes danou entitu. K tomuto účelu zde existuje objekt "Example". Při použití tohoto přístupu mohu velice snadno získat data filtrovaná podle daných sloupců (atributů) v tabulce (entitě).
Jenže....
Pokud si například řeknu: "Chci vybrat všechny záznamy s podmínkou WHERE datum BETWEEN od AND do", dostanu se opět do slepé uličky a musím přistoupit zpět k čistému Criteria API nebo dokonce k OQL.
Toto byl první důvod, proč jsem přistoupil k implementaci vlastního způsobu, který je řízen pomocí anotací. Ještě než přejdu k samotnému způsobu, musím zmínit druhou pozitivní věc.
Pokud začnu tvořit DAO vrstvu, začne se mi každý DAO objekt hemžit metodami jako: findByDatumPrijeti, findByXXX.... Jistě není nic špatného na implementaci daných metod, až na to, že metody obsahují parametry, které již jasně říkají, jaká data mají být výsledkem (návratovou hodnotou).
Uvedu malý příklad (pro pozdější srovnání):
Jak je z ukázky patrné, metoda vybíra zakázky, které spadají jistému zákazníkovi a jejich datum je v rozmezi datumOd-datumDo.
K odstranění psaní samotných dotazů použiji následující kroky:
Jediné, co mi zůstane je objekt pro filtrování. Sám se svými metadaty je schopen řici, co vlastně chci. Následující ukázka je ekvivalentní předchozímu způsobu:
Pro lidi nesnášející anotace, bude tento způsob jistě nepoužitelný, pro lidi snažící se o co největší usnadnění a zpřehlednění své práce, naopak přínosem. Věřím, že každý si již ten svůj zpusob nalezl a nehodlá ho měnit, na druhou stranu při pohledu na takovýto kod si pokládám jednu otázku: "Není přece jen efektivnější popsat kod metadaty a nechat zbytek na daném frameworku?".
Psaní základních SQL dotazů jsem nahrazil OQL dotazy. OQL je v podstatě velice podobný SQL s tím rozdílem, že daný dotaz se provádí nad entitami reprezentující data v DB. Díky tomu odpadá nutnost psát ruzné JOIN konstrukce, specifikovat vrácené sloupce a hlavně získávám nezávislost na použitém databázovém systému. Převod z MySQL na Oracle či MSSQL je triviální záležitost spočívající v přepsání pár řádku v persistence.xml či v přepsání JDBC zdroje v aplikačním serveru.
Ale zpět. I když je OQL velice elegantní způsob psaní, stále obsahuje velké omezení, které spočívá v tvorbě dynamických dotazů. Pokud aplikace přistupuje k datům tak, že je získává na základě uživatelského filtru, musím dotaz poskládat z daného Stringu, což je dost otravující záležitost.
K tomuto účelu se velice hodí Criteria API. Nebudu zde popisovat funkci Criteria API, k tomuto účelu slouží referenční příručka. U Criteria API se mi velice zalíbila možnost filtrovat data přes danou entitu. K tomuto účelu zde existuje objekt "Example". Při použití tohoto přístupu mohu velice snadno získat data filtrovaná podle daných sloupců (atributů) v tabulce (entitě).
Jenže....
Pokud si například řeknu: "Chci vybrat všechny záznamy s podmínkou WHERE datum BETWEEN od AND do", dostanu se opět do slepé uličky a musím přistoupit zpět k čistému Criteria API nebo dokonce k OQL.
Toto byl první důvod, proč jsem přistoupil k implementaci vlastního způsobu, který je řízen pomocí anotací. Ještě než přejdu k samotnému způsobu, musím zmínit druhou pozitivní věc.
Pokud začnu tvořit DAO vrstvu, začne se mi každý DAO objekt hemžit metodami jako: findByDatumPrijeti, findByXXX.... Jistě není nic špatného na implementaci daných metod, až na to, že metody obsahují parametry, které již jasně říkají, jaká data mají být výsledkem (návratovou hodnotou).
Uvedu malý příklad (pro pozdější srovnání):
public List findForExample(String zakaznik, Date datumOd, Date datumDo) {
// implementace Criteria API, OQL ci suroveho SQL
} Jak je z ukázky patrné, metoda vybíra zakázky, které spadají jistému zákazníkovi a jejich datum je v rozmezi datumOd-datumDo.
K odstranění psaní samotných dotazů použiji následující kroky:
- vytvořím objekt pro filtraci
- daný objekt anotuji příslušnými "metadaty"
- zruším metodu findForExample a v DAO vytvořím obecnou metodu: findByCriteria(Object filtr)
Jediné, co mi zůstane je objekt pro filtrování. Sám se svými metadaty je schopen řici, co vlastně chci. Následující ukázka je ekvivalentní předchozímu způsobu:
@Criteria(entity = Zakazky.class)
public class ZakazkyForExampleFiltr {
@Criterion(property="zakaznik.id")
private String zakaznik;
@Criterion(property = "datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MIN)
private Date datumPrijetiOd;
@Criterion(property = "datumPrijeti")
@Between(idf = "dp", property = BetweenDAO.MAX)
private Date datumPrijetiDo;
// settry, gettry
}
// pouziti
ZakazkyForExampleFiltr filtr = new ZakazkyForExampleFiltr();
// naplneni filtru
List result = dao.findByCriteria(filtr);
Pro lidi nesnášející anotace, bude tento způsob jistě nepoužitelný, pro lidi snažící se o co největší usnadnění a zpřehlednění své práce, naopak přínosem. Věřím, že každý si již ten svůj zpusob nalezl a nehodlá ho měnit, na druhou stranu při pohledu na takovýto kod si pokládám jednu otázku: "Není přece jen efektivnější popsat kod metadaty a nechat zbytek na daném frameworku?".
Jelikož se mi článek rozrostl, rozhodl jsem se ho rozdělit do dvou částí. V první části jsem popsal důvody a uvedl malou ukázku. V druhé části uvedu možnosti a nabídnu danou funkčnost ke stažení. Tedy ten základní "motor", který lze jednoduše "přilepit" do své DAO vrstvy. Navíc uvedu další plány a položím otázky, na které si zatím nedokážu odpovědět.
Přihlásit se k odběru:
Příspěvky (Atom)
Když programátor založí a řídí firmu
Jako malý jsem chtěl být popelářem. Ani ne tak proto, že bych měl nějaký zvláštní vztah k odpadkům, ale hrozně se mi líbilo, jak...
-
Vím, že po internetu existuje spoustu srovnání programovacích jazyků. Já se ani nechci tak moc pouštět do srovnání jako osvětlit některé věc...
-
Každý, kdo chce efektivně pracovat s objekty, musí použít nástroj, který mu umožní automaticky doplňovat potřebné informace. Jinými slovy, p...
-
Minule jsem psal o možnosti spojení Wicketu s EJB3 a o Wicket Security. Dnes se podívám na další vlastnosti a možnosti tohoto webového frame...