č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:

  • 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.prijmeni


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í.

  • ř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

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í):

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.

čtvrtek 13. prosince 2007

DAO pattern + generika pro EJB3

Když jsem se dostal do fáze většího poznávání J2EE, došlo mi, že vrstvení logiky je nedílnou součástí úspěchu při návrhu.
Jistě by nebylo moudré nechat "dolování dat" rozházené po aplikační logice tak, jak ho zrovna potřebuji. Zde nastupuje DAO (Data Access Object), což je návrhový vzor sloužící ke komunikaci mezi persistentní vrstvou a zbytkem světa.

Asi nejvíce mě zaujala možnost, kde DAO vrstvu definuji pomocí generiky a abstraktní třídy. Více než tisíc slov je lepší přímá ukázka kódu.

První věcí je interface, který obsahuje všechny důležité metody, které je třeba pro DAO jednotlivých entit třeba vystavit:
public interface GenericDAO {
public T find(ID id);
public List findAll();
public T create(T entity);
public T save(T entity);
public void remove(T entity);
}

Jednotlivé metody jsou vcelku jasné. Jedná se o základní operace jako: získej data, vytvoř, ulož, smaž. Co se týče generických typů, jedná se o entitu, které přísluší dané DAO a o typ primárního klíče. Typem primárního klíče může být jak základní datový typ jako String, Integer, tak i složený primární klíč, např: ZakazkaPK (obsahuje číslo zakázky a datum vystavení).

Nyní mohu přejít k vlastní implementaci lokálního interface:
@Local
public interface ZamestnanecDAOLocal extends GenericDAO {
}

Samotná EJB beana může vypadat následovně:
@Stateless
public class ZamestnanecDAOBean implements ZamestnanecDAOLocal {
@PersistenceContext(unitName = "myDB")
private EntityManager em;
//... implementace metod
}


Velkou nevýhodou je ovšem fakt, že pokaždé musím implementovat metody, které jsou potřeba ke splnění požadavku z GenericDAO.

Zde nastupuje abstraktní třída, která za nás vyřeší onu potřebnou implementaci.
public abstract class AbstractDAO implements GenericDAO {
private Class entityBeanType;
private EntityManager em;
public AbstractDAO() {
// vytvari, ziskava typ entity pro dane DAO
this.entityBeanType = (Class) ((ParameterizedType)
getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}

public Class getEntityBeanType() {
return entityBeanType;
}

@PersistenceContext(unitName = "default")
public void setEntityManager(EntityManager em) {
this.em = em;
}

protected EntityManager getEntityManager() {
if (em == null) {
throw new IllegalStateException("...");
}
return em;
}

public T find(ID id) {
T entity;
entity = getEntityManager().find(getEntityBeanType(), id);
return entity;
}

// ... implementace zbylych metod
}


Nyní stačí danou abstraktní třídu nechat dědit samotnou implementací EJB beany:
@Stateless
public class ZamestnanecDAOBean extends AbstractDAO implements ZamestnanecDAOLocal {
}


Takto definovaná beana je schopna podědit abstraktní třídu a například anotovaný setter pro entitymanager převzít na svá bedra. Podle specifikace je totiž možné EJB beany nechat dědit mezi sebou, s vyjímkou, že dependency injections a další služby jsou poté managovány potomkem, v našem případě "ZamestnanecDAOBean".

Aby samotné DAO nebylo tak chudé, přidal jsem si několik dalších možností. Některé jsou již specifikovány na danou implementace ORM frameworku. Osobně jsem díky tomu přešel na Hibernate Session a Criteria API.
Tady jsou některé další metody:

/**
*

Vraci vsechny zaznamy z dane tabulky, ktera prislusi entite.
* Navic umoznuje definovat razeni podle Hibernate Criteria API: Order


*

Zpusob pouziti: {@code findAll(Order.asc("jmeno"), Order.desc("prijmeni"))}


*
* @param orderBy definice razeni zaznamu
* @return vsechny zaznamy, pokud zadne neexistuji, vraci null
*/
public List findAll(Order... orderBy);

/**
*

Vraci vsechny zaznamy z dane tabulky, ktera prislusi entite.
* Navic umoznuje definovat omezeny vyber dat, pouzivane napr. pro strankovani.


*

Zpusob pouziti: {@code findAll(0, 20); } Prvni radek je roven 0


*
* @param first prvni radek, od ktereho se vybiraji zaznamy
* @param offset maximalni pocet vybiranych zaznamu
* @return vsechny zaznamy dle definovanych omezenich
*/
public List findAll(int first, int offset);

/**
*

Vraci vsechny zaznamy z dane tabulky, ktera prislusi entite.
* Navic umoznuje definovat omezeny vyber dat, pouzivane napr. pro strankovani
* a definovat razeni podle Hibernate Criteria API: Order


*

Zpusob pouziti: {@code findAll(0, 20, Order.asc("jmeno")); } Prvni radek je roven 0


*
* @param first prvni radek, od ktereho se vybiraji zaznamy
* @param offset maximalni pocet vybiranych zaznamu
* @param orderBy definice razeni zaznamu
*
* @return vsechny zaznamy dle definovanych omezenich
*/
public List findAll(int first, int offset, Order... orderBy);

/**
*

Dohledava zaznamy na zaklade Hibernate Example.


*

Spousti: {@code findByExample(exampleInstance, false, false, false, excludeProperty)}


*

Zpusob pouziti: 15.6. Example queries
*
* @param exampleInstance s naplnenymi vlastnostmi pro vyber
* @param excludeProperty pradavajici vlastnosti pro vyber pres example
* @return dohledane zaznamy
*/
public List findByExample(T exampleInstance, String... excludeProperty);

/**
*

Ziskava pocet vsech radku z tabulky prislusici dane entite


*
* @return pocet vsech radku
*/
public int countAll();

pondělí 3. prosince 2007

Seam & Hibernate Validator

Když jsem přemýšlel nad tím jak validovat (kontrolovat) data od uzivatele, napadlo mě spoustu možností jak to řešit. Jeden je ovšem velice sexy, který stojí za to zmínit.

Mám klasickou webovou aplikaci postavenou pomocí MVC (model-view-controller). Do modelu můžu nacpat entity, do kontroleru jednotlivé akce nad modelem a komunikace mezi view a business logikou. Do view klasické webové stránky.
Jelikož je Java striktně typový jazyk, nutí mě již na základě modelu definovat typ tak, aby odpovídal skutečnosti. Při vystavení modelu do persistentní vrstvy, ode mně navíc očekává popis jednotlivých atributů, podle kterého komunikuje s databází.

Ale zpět k view.
Začnu tvořit klasický formulář, který například obsahuje vlastnosti uživatele. Musím ošetřit, že login nesmí být prázdný, že musí splňovat podmínku, která říká, že použitelné znaky jsou pouze daného výčtu. Podobné vlastnosti budu kontrolovat například u hesla či jména a příjmení. Dané kritéria, které musí uživatel, při vyplňování formuláře, splnit, jsou identická s kritériemi, které musí splnit model (entita), aby byla schopna se persistovat (uložit) do databáze.
Abych se vyhnul duplicitní validaci, existuje možnost tyto věci propojit. Zde právě nastupuje Seam framework a Hibernate Validator. Nejlépe bude, pokud vše vysvětlím na malé ukázce:

Mám entitu uživatele, která může vypadat následovně:
@Entity
@Name("uzivatel")
public class Uzivatel implements Serializable {
@Id
@NotNull
@Length(min=5, max=30)
@Pattern("^\w*$", message="#{message.errorValidateLogin}")
private String login;
@NotNull
@Length(min=6)
private String heslo;
private String jmeno;
private String prijmeni;
// ... settry, gettry
}


Danými anotacemi jsem dal najevo, že model musí splnit tyto požadavky. Pokud se tak nestane, nepodaří se mi mi danou hodnotu uložit do databáze.

Tak a teď k view.
Mám v podstatě 2 možnosti, buď budu znovu psát pravidla pro validaci, nebo nechám vše na frameworku.







Při pokusu odeslání formuláře, dostanu hlášení, že jsem nesplnil daná kritéria. K tomu ale dojde ještě dříve, než se model dostane k persistentní vrstvě. To znamená, že validace se provádí hned při spuštění akce "registrace".
Jednotlivé hlášky, které posílá Hibernate Validator jsou přeložitelné, takže není problém si udělat vlastní definici, která mi jasně řekne: "Nene, máš chybu, chyby jsou... zde a zde...".

Je to jedna z vlastností Seamu, která mě pomalu nutí více používat Hibernate :)

Příjemných vlastností v Seam frameworku mohu nalézt více. Ve zkratce uvedu, pro mne, ty nejzásadnější:

  • Hot deploy - mohu provádět změny nad aplikací, bez nutnosti restartovat server či provádět redeploy aplikace

  • @In, @Out - Jednotlivé komponenty mohu zasazovat do logiky či je dále vystavovat; v ukázce je vidět, že uživatele jako komponentu přímo použiji v xhtml stránce, aniž bych musel používat nějaký controller nad modelem

  • @Scope - výčet životností komponent je mnohem bohatší, než je tomu v JSF

  • @Logger - přímá podpora logování pomocí log4j

  • pages.xml - pageflow, které umožňuje mnohem větší možnosti než je tomu u navigation-rules v JSF

  • No POST - díky Seamu, který umožňuje používat konverzaci, nemusí být view posílán přes POST

  • security - Seam obsahuje docela pěknou práci s rolemi, uživateli a vůbec s restrikcemi; navíc umožňuje ručně definovat práva a jejich chování


  • components.xml, exceptions.xml - konfigurovat se dá snad vše, ať už jsou to komponenty či zachytávání výjimek



Výčet by byl jistě mnohem bohatší. I když s daným frameworkem nemám tolik zkušenností, ihned jsem si ho oblíbil. Důvodem je možná i samotná referenční příručka, která mi odpoví téměř na všechny dotazy.

čtvrtek 29. listopadu 2007

Novinky 2

Jsem tu s další sadou novinek či událostí, které mě v poslední době asi nejvíce zaujaly. Bez dalších řečí, rovnou začnu:

NetBeans:
Jelikož jsem velkým fandou tohoto IDE a i samotné RCP platformy, jsem rád, že se blíží konečné vydání. Nyní je k dispozici NetBeans RC2, která navíc byla uživateli označena za použitelnou a připravenou k releasnutí.

Facelets:
Byl to pro mě trn v oku. Sice již několik měsíců používám NB6, ale bohužel bez podpory, která mi dost usnadňuje život. Nicméně, podpora facelets je na světě! Sice se jedná o testovací verzi, ale i tak jsou již některé věci použitelné.

Seam 2.0:
Jak již psal Petr Ferschmann, Seam 2.0 se konečně dostal do fáze GA. Po přechodu na tuto verzi jsem si zejména liboval v konfiguraci. Pokud projdete referenční příručku (mimochodem takhle si představuji studijní materiál, výborně napsaná), zjistíte, že nyní již není třeba konfigurovat tolik vlastností. Tímto ovšem apeluji na Petra Pišla (všemohoucí díky za podporu facelets), aby oprášil NetBeans plugin pro Seam 2.0. :)

MySQL:
V poslední době mi přijde, že nějak strnul vývoj tohoto produktu. Nyní je k dispozici MySQL 5.0.x, která jako jediná je GA. Zbytek je stále v aplha či RC verzi. Osobně jsem dost zvědavý na Falcon engine, který by měl nastoupit na místo InnoDB a míti rychlost srovnatelnou s MyISAM. Jenže, kde nic tu nic.
Ale proč přidávám toto do novinek? Před nedávnem mě dost vypekl konektor pro Javu. Při použití MySQL Connector/J 5.1.5 společně s Hibernate 3.2.x jsem se dostal do stavu, kdy hibernate nebyl schopen vytvářet "auto_increment", který má nastarosti právě MySQL. Při přechodu zpět na MySQL Connector/J 5.0.x je vše v pořádku. Možná se MySQL rozhodlo MySQL Connector/J verzovat stejně jako své verze Database Serveru.

Video Netbeans RCP vs. Eclipse RCP:
Konečně vyšlo toužebně očekávané video, kterého jsem se osobně zůčastnil. Každému vřele doporučuji. I když zrovna člověk neplánuje využít dané platformy, pro všeobecný přehled nad modulárními systémy, by si měl video prohlédnout.
Malý tip: No a když se dostanete na konec videa, tak mě můžete zahlédnout. Jsem ten poslední a jediný tazající :)

Exceptions for Action:
Poslední novinkou je článek na javaworld.com s názvem Exceptions for Action. Jedná se o hezký popis, jak správně navhovat výjimky uvnitř J2EE. Abych byl upřímný. I když rozumím způsobu práce výjimek, je pro mě velká magie je správně navrhovat a pracovat s nimi.

Tak to by bylo vše. Ještě jedna malá věc pro pobavení: Přáníčka od kofoly.

pátek 23. listopadu 2007

JBoss Seam 2.0 & Hibernate 3.2.5 & Glassfish V2

Když jsem přemýšlel jaký zvolím framework pro webovou aplikaci, naskytly se mi v podstatě dvě možnosti. Buď JSF či Struts. Osobně jsem raději hned sáhnul po JSF. Komponentový framework, navíc tolik dotovaný samotným Sunem.

Po vyzkoušení pár příkladů jsem ihned přešel do vývoje. Zde ovšem nastaly potíže. Psaní stejného kódu stále dokola. Stálá definice přes XML, neexistence použitelného scope pro komponenty. Navíc jsem na vývoj sám a přijde mi příliš těžkopádné vše striktně oddělovat.

Našel jsem Seam. Jedná se o webový framework od JBosse, který tvoří ono lepidlo mezi JSF a EJB3. Samotná definice komponent je plně řízena anotacemi, čímž odpadají mnohé XML definice. Seam navíc obsahuje dosah komponenty „conversation“. Taková komponenta poté existuje od doby vzniku konverzace až do jejího zániku. Odpadá tak potřeba definovat komponenty jako „session“. Další věcí, která mě zaujala byla podpora Hibernate Validatoru. Pokud použiji Hibernate, mám možnost nechat Seam, ať mi jednotlivé hodnoty entit validuje podle definice z Hibernate.

Já se zde nechci zabývat podrobnostmi, jak daný Seam funguje, k tomu slouži referenční příručka. Mimochodem skutečně výtečně napsaná. Spíše se zaměřím na to, jak nainstalovat Seam 2.0 na Glassfish V2.

Seam je primárně určen pro aplikační server Jboss. Pravdou ovšem je, že ho lze použít i na jiném serveru či přímo bez EJB kontejneru (viz. Tomcat). Samotná konfigurace se liší jen nepatrných věcech. Mě ovšem dali pekelně zabrat :)

Takže, nejprve je nutné stáhnout samotný Seam 2.0 a Hibernate. Již přímo v examplech existuje ukázková aplikace s názvem booking (v adresáři jee5). Jedná se o web. aplikaci pro hotel, kde si můžete zamluvit vlastní aparmá. Na aplikaci je vysvětlené jak funguje komunikace mezi Seamem, JSF a JPA.

Při tvorbě vlastní aplikace si musím nejprve zaregistovat dané knihovny.
Pro webovou vstvu to je:

  • el-api.jar

  • el-ri.jar

  • jsf-facelets.jar

  • jboss-seam-ui.jar


Pro EJB modul:

  • jboss-seam.jar

  • jboss-seam-debug.jar

  • hibernate-all.jar

  • thirdparty-all.jar



EJB Modul dále musí obsahovat conf/seam.properties, který může být klidně prázdný. Definice persistence.xml, kde je třeba, aby byla správně nastavena transakce:




org.hibernate.ejb.HibernatePersistence
my/db



value="org.hibernate.cache.HashtableCacheProvider"/>








Webový modul:
Jednak obsahuje web.xml, kde je třeba dodefinovat Seam:



org.jboss.seam.servlet.SeamListener



Seam Filter
org.jboss.seam.servlet.SeamFilter



Seam Filter
/*



Seam Resource Servlet
org.jboss.seam.servlet.SeamResourceServlet



Seam Resource Servlet
/seam/resource/*




A jednak reference na EJB Beany.

For Transaction from EJB3 Container
Irminsul/EjbSynchronizations/local
Session
org.jboss.seam.transaction.LocalEjbSynchronizations
EjbSynchronizations



MyEARName/MyBean/local
Session
ejb.seam.app.MyLocal
MyBean


Je to trochu nevýhoda oproti použití samotného Jbosse, kde reference jsou dohledany automaticky.

Dalšími konfiguračními soubory jsou:

  • components.xml

  • exceptions.xml

  • pages.xml

  • security.drl



V components.xml se definují jednotlivé vlastnosti Seamu jako jndi-pattern, security, atd.






concurrent-request-timeout="10000"
conversation-id-parameter="cid"/>















/WEB-INF/security.drl



Zaregistrování JNDI patternu je potřebné pro referencování EJB Bean. Dále nastavím, že transakce je řízena přes EJB kontejner, kde si Seam dokáže odchytit i exeptions při rollbacku.
Dále se jedná o nastavení časové platnosti konverzace.
Poslední věcí je authentifikace a nadefinovaní pravidel pro práci s rolemi a celkovými pravami nad aplikací.

V exceptions.xml se definuje ruční správa nad výjimkami. Jinymi slovy, pokud dojde k výjimce, mohu nechat aplikaci automaticky někam přesměrovat, odeslat příslušnou správu, atd. Samotná konfigurace může vypadat následovně:

V pages.xml se definuje page-flow. Zde je možnost využít klasický přistup přes navigation-rule v JSF nebo Seam přístup. Osobně se mi více zalíbíl tento přístup, jelikož povyšuje definici mnohem dál než v případě JSF.

V security.drl mám možnost definovat, nastavovat, odebírat, upravovat práva jednotlivých rolí. Jedná se o jakýsi konfigurační skript nad správou rolí. Pokud například chci, aby role admin mohla to samé co role obchodnik, žádný problém takovou věc nastavit.

Pro samotný běh enterprise aplikace je ještě zapotřebí nadefinovat jboss-seam.jar jako ejb modul. Definice se provádí v application.xml.

Pro práci s hibernate je zapotřebí nahrát všechny požadované knihovny do $GLASSFISH_HOME/lib a restartovat server.

Každý, kdo to s webovými aplikacemi v Jave myslí vážně, měl by se na Seam podívat. Už jen z prostého důvodu, že Gavin King se stal hlavním vývojářem pro novou specifikaci WebBeans, která by měla být v Java EE 6. Jak je patrné, tak WebBeans vycházejí právě z konceptu Seamu.

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...