25. prosince 2007

Co (mi) přinesl letošní rok?

Je závěr roku a kdekdo se snaží shrnout, co se NEJ stalo v tomto roce. Já budu také jeden z nich, ale můj pohled bude malinkato jiný - rád bych uvedl věci, které mě jako programátora resp. architekta tento rok nejvíce pomohli v mé každodenní práci. Nebude to přehled nějakých významných objevů či událostí, které se v tomto roce ve světě Javy staly, ale spíše věci, které mi pomohly a pomáhají lépe a rychleji vytvářet aplikace, nebo které mě nějakým způsobem v mé práci ovlivnily.

Rozšiřování Spring portfólia

Já mám rád Spring, já ho rád používám a jsem plně ztotožněn s filosofií, která stála u zrodu Springu, a která platí až doposud. Proto tak oceňuji, že Spring resp. Spring portfólio se rozšiřuje o nové projekty, že čím dále více projektů třetích stran nabízí přímou a snadnou Spring-integraci, že Spring používá čím dál více lidí a tím pádem se objevuje více informací na internetu a více příspěvků v diskuzích.

Zvýšení povědomí o Springu

Tento bod velice souvisí s předchozím bodem, ale je o trochu něčem jiném. Je o tom, že Spring již dneska není považován za nějaký dočasný open-source projektík, ale za vyspělé propracované řešení, které se nebojí využívat ani velcí hráči jako jsou banky, pojišťovny, vládní instituce apod. To je v konečném důsledku super hlavně pro nás vývojáře, že můžeme jít s dobou a nemusíme pořád i dnes vyvíjet v prostředí Javy 1.3 nebo Struts.

Vyspělejší prezentační technologie

Myslím si, že Java oproti jiným jazykům (hlavně C#) nejvíce ztrácí v efektivitě vytváření "ksichtu" aplikací. Je sice hezké mít super robustní jádro aplikace, ale pokud uživatel neuvidí pěknou, moderní, uživatelsky přívětivou aplikaci, tak moc spokojený nebude. Proto jsem rád, že vznikl Seam, který ukázal v tomto ohledu zajímavý způsob řešení, že nastal posun v JSF specifikaci, že vzniká čím dál více použitelných AJAXových frameworků.

J2EE 6

Dle článků na internetu se dá tušit, že specifikace J2EE verze 6 se vyvíjí tím správným směrem. Uvidíme, jak to nakonec ale dopadne - jaké dílčí specifikace budou součástí finální J2EE specifikace, jak moc se podaří prosadit lidem okolo Springu jejich představy.

Nedostatek pracovních sil

Sice tento bod přímo nesouvisí s tím, jak každý den řeším programátorské problémy, ale nepřímo to celkem mojí práci ovlivňuje. Na jedné straně tím, že se nemusím bát o svoji práci, že si mohu vybírat a částečně určovat podmínky já sám. Na druhé straně jako nevýhodu vidím v tom, že máme problémy sehnat nové kolegy (schválně jsem vynechal slovíčko kvalitní, protože to je dneska spíše nedostižný ideál) a tedy spoustu věcí třeba ani řešit nemůžeme.

21. prosince 2007

Skriptovací jazyky v Javě - co s nimi?

Tento článek jsem nezačal psát jako jiné články s tím, že bych rád něco sdělil, ale spíše, že bych se rád něco dozvěděl. Před chvílí jsem dočetl článek o skriptovacích jazycích v Javě a pořád si nějak nemohu správně odpovědět na otázku - K čemu mi jsou v Javě ty skriptovací jazyky vlastně dobrý?

Musím se přiznat, že obecně skriptovací jazyky neovládám a spíše jen pasivně sleduji dění okolo, něco hodně málo jsem napsal v Groovy. Když si čtu články o skriptovacích jazycích v Javě, tak se často objevují tyto výhody vzájemného propojení:

  • skriptovací jazyky jsou jednodušší, lehčí, názornější, intuitivnější. To je z velké části dáno tím, že se jedná většinou o jazyky dynamické a slabě typované (obecně řečeno, protože např. Python je typově silný jazyk).

  • Java nabízí celou řadu věcí, které nejsou dostupné v samostatných skriptovacích jazycích, např. transakční management nebo vzdálená volání. Kromě toho lze pomocí Javy využít obrovské množství knihoven třetích stran, které lze ve skriptech využít.

  • výběr skriptovacích jazyků pro Javu je dneska opravdu veliký

  • díky spojení s Javou se neztrácejí hlavní přednosti Javy - možnost vytváření velkých, robustních a škálovatelných aplikací.


Kromě výhod to má samozřejmě i nějaké nevýhody:
  • často se uvádí slabší výkonnost skriptovacího enginu s JVM oproti nativnímu enginu samotnému. Je to dáno tím, že je potřeba provádět řadu kontrol a konverzí mezi oběma světy. Toto např. neplatí úplně pro Groovy, což je jen taková jiná Java.

  • skripty napsané s pomocí JVM samozřejmě pak nelze spouštět v nativním enginu

  • někdy jsou světy Javy a skriptovacího jazyka natolik rozdílné, že to nelze v některých jazycích vůbec implementovat nebo za cenu ne úplně ideálního kódu. Myslím například anotace v Javě.

  • větší nároky spojené s údržbou aplikací. Dříve stačilo umět jen Javu, ale teď k tomu potřebuji znát ještě minimálně další jazyk.

  • podpora přímo na úrovni JVM od verze 6 - JSR 223


Takže k čemu já to jen využiji? I když si dokážu představit, že určité části aplikace napíši rychleji např. pomocí Groovy, tak jsem asi moc konzervativní, ale mě to zase taková výhoda nepřijde. Já mám svoji Javu rád :), takže si všechno napíšu v ní. Když to nebudu brát jen z mého osobního pohledu, tak přeci jen otázka údržby aplikace je hodně významná a s použitím více jazyků a technologií se tento problém stává těžší a komplikovanější (a tedy nákladnější). A když už budu psát část aplikace pomocí skriptovacích jazyků, tak proč už to pak nenapíšu celé jen pomocí skriptovacího jazyka?

Často se uvádí jako vhodné využití prototypování aplikací. Zde si to dovedu celkem představit jako přínosné, ale moje zkušenost mi říká, že spousta takto vytvořených prototypů se použije pro následný vývoj celé aplikace. Raději bych to tedy od začátku psal pořádně v Javě.

Můj osobní stav je nyní takový, že pořád moc nevím, kde a jak bych mohl efektivně využít skriptovací jazyky z pohledu vývoje v Javě. Budu moc rád, když přispějete formou diskuse a vysvětlíte mi, že se v určitých ohledech pletu nebo jsem jen špatně resp. málo informovaný.

17. prosince 2007

Novinky ze Springu

Moc často nekomentuji různé události, ale teď mi to nedá. Právě se koná konference The Spring Experience, což jsou spolu s konferencí SpringOne dvě nejvýznamnější konference věnované přímo Springu.
Právě na této konferenci se objevily velice zajímavé novinky z dílny firmy SpringSource (dříve Interface21):

  • doplnění Spring portfólia o Spring Integration, o část, která by měla pomoci při vývoji servisně-orientovaných aplikací. Podle mého názoru je dneska skoro nezbytností mít něco podobného v portfóliu, v době, kdy se každý ohání slovíčkami jako SOA, webové služby, integrace apod. V brzké době by projekt měl být publikován a někdy ve druhém čtvrtletí roku 2008 by měla být verze 1.0. Více si můžete přečíst na Team blogu SpringSourcu.

  • SpringSource Application Management Suite - podle dostupných informací, které se mi podařilo najít, by to měla být taková nádstavba nad Springem, která bude zobrazovat běhové informace o aplikaci jako např. kolik proběhlo transakcí, kolik zpráv se zpracovalo, kolik aktivních beanů je v aplikaci apod. Já osobně to chápu tak, že místo abychom museli vytvářet MBeany pro přístup k velmi často potřebných informacím, tak budeme moci použít tento Application Management Suite, který nám bude automaticky nabízet standardní sestavu pro každou aplikaci.

  • SpringSource Certification Program - toto je velice zajímá informace, o které se již tak trochu mluvilo na konferenci SpringOne v letošním roce. Na certifikace jsou různé názory, ale určitě lze říci, že je to krok správným směrem, pokud chceme aby se Spring čím dál více prosazoval a používal mezi velkými hráči jako jsou například banky. Je ale otázka, kdy se to dostane až k nám do Čech - v USA to začne na konci ledna.

Více si můžete přečíst v tomto článku.

14. prosince 2007

Hudson - děkuji, rád

V poslední době se mi zdá, že se více než kdy před tím řeší, který že build server je ten nejlepší. Možná je to jen můj subjektivní pohled nebo možná je to také tím, že čím dál tím více lidí má povědomí o "postupné integraci" (continuous integration) a znají nesporné výhody tohoto přístupu.

Hned na začátku říkám, že mám rád Hudson. Už to bude přibližně rok, kdy jsem vybíral buildovací server do naší firmy a při svém výběru jsem víceméně vycházel z nástrojů uvedených v tomto článku. Hned od začátku to byla "láska na první pohled":

  • velice jednoduchá instalace. Slovíčko instalace je přehnané, protože stačí pouze nahrát soubor hudson.war na aplikační server a už vše funguje. S tím je samozřejmě spojen i bezproblémový upgrade, což se zde celkem využije, protože skoro každý den vzniká nová verze.

  • velice jednoduchá konfigurace všeho potřebného. Konfigurace je intuitivní a přímo obsahuje nápovědu, takže není potřeba žádná dokumentace.

  • Hudson nabízí přesně to, co potřebujeme a nic navíc. Používáme Subversion, nemáme (zatím) žádné složité vazby mezi projekty, v každém buildu chceme mít přístup k JavaDoc dokumentaci, výsledkům testů a k informacím z konzole. Pro sledování výsledků buildů nám stačí RSS nebo Email. Pro ty, kdo používají něco jiného než Subversion nebo mají větší nároky na integraci nástrojů třetích stran, tak budou mít s Hudsonem asi problém.

  • S tou integrací nástrojů třetích stran už to také není tak špatné jako třeba před rokem. Úspěšně jsme teď zaintegrovali nástroje FindBugs a Cobertura. Ostatní nástroje pro detekci chyb či něčeho jiného (Checkstyle, PDM, JDepend) sice používáme, ale zatím bohužel bez přímé integrace s Hudsonem. Detailní výsledky máme možnost vidět pouze z výpisu konzole.




Teď si připadám jak na střední na konci referátu o knížce: Hudson se mi velice libíl a všem bych ho jen doporučil :).

Přehled dalších buildovacích serverů:

13. prosince 2007

Apache Forrest - děkuji, nechci

V poslední době jsem musel malinko oželet programování vlastních aplikací, protože jsem více řešil nasazení a konfiguraci produktů třetích stran. Jedním z nich byl produkt se jménem Apache Forrest.

K čemu je Apache Forrest dobrý? Je dobrý k tomu, když si potřebuji vytvořit webovou prezentaci (a nechci ji vytvářet sám přímo pomocí HTML), když chci mít na webu automaticky řadu zajímavých funkcí jako fulltextové vyhledávání, tisk, export do PDF a hlavně, když mám již spoustu dokumentů (různé obrázky, HTML, PDF atd.), které bych rád znovu využil. Pokud chci vytvářet obsah webu sám, pak mám možnost použít celkem intuitivní pseudo-HTML jazyk. Úvodem je ještě vhodné uvést, že například většina projektů Apache má web vytvořený právě pomocí tohoto nástroje.

Podle toho, co jsem doposud napsal by se mohlo zdát, že je to super mocný nástroj. Bohužel jsem si to na začátku taky myslel, ale teď už moc ne. Ale postupně...

Formáty dokumentů

Apache Forrest umí pracovat na vstupu s různými formáty dokumentů a to díky rozšířením Apache Forrest (tzn. pluginy). Základní konfigurace umožňuje na vstupu použít následující formáty dokumentů (kromě dokumentů zapsaných přímo ve formátu Apache Forrest XML):
  • XML

  • HTML

  • MS Excel (tabulka z MS Excel musí být uložena jako Tabulka XML)

  • OpenOffice (Impress, Writer) verze 1 (přípona .sxw)

U těchto vstupních formátů se dokumenty přímo upraví do jednotného vzhledu a struktury webu. Samozřejmě je možné formou odkazu přímo na dokument připojit dokument v libovolném formátu – ten pak sice nebude zpracován a převeden do HTML, ale bude možné jej stáhnout.
Bohužel není možné přímo integrovat nejrozšířenější dokumenty MS Word. Jde to ale obejít přes OpenOffice – dokument z MS Word načtu v OpenOffice a uložím ve formátu OpenOffice (sxw).

Apache Forrest jsem intenzivně používal přes dva týdny a na základě této zkušenosti mohu uvést následující výhody a nevýhody:

Výhody

  • Vzhled webu lze kompletně upravit pomocí CSS (viz projekty Apache).

  • Velké množství vlastností výsledného webu je možné upravit pouze pomocí konfigurace

  • Žádný srovnatelný nástroj (se stejným zaměřením) se mi nepodařilo najít.

Nevýhody

  • Apache Forrest si moc nerozumí s češtinou - měl jsem problémy s českou diakritikou při exportu do PDF nebo při importu dokumentů z OpenOffice.

  • Nepodařilo se mi vůbec rozchodit fulltextové vyhledávání s Lucene. Varianta s Google fungovala dobře.

  • Apache Forrest je v současné verzi 0.8 a moc to nenasvědčuje tomu, že by se v brzké době objevila nějaká stabilní verze 1.0. Když jsme tento nástroj vybírali do jedné nabídky na konci roku 2006, tak byla verze 0.7.

  • Z uživatelského hlediska se nejedná o zrovna přívětivý nástroj. Vše je ovládané pouze z příkazové řádky.


Po všech uvedených nevýhodách jsme byli nuceni přejít na jiné řešení se jménem Daisy. Sice je to řešení z kategorie CMS a tedy směřované trochu jiným směrem než Apache Forrest, nicméně základ je opět postavený nad nástrojem Apache Cocoon a tedy z hlediska možností práce s dokumenty velice podobné. Všechny nevýhody Apache Forrest jsou v Daisy vyřešeny a řada výhod zůstala.

5. listopadu 2007

Přednáška: transakční strategie (nejen ve Springu)

Nechci pořád posílat odkazy na nějaké cizí prezentace, ale v poslední době jsem jich viděl celkem hodně a ta poslední mě hodně zaujala. Nedá mi to tedy, abych vás na ní neupozornil, protože fakt stojí za to.

Je to přednáška na téma "Transaction Management Strategies in Mission-Critial Enterprise Applications". Přednáška nejprve obecně popisuje problematiku transakcí (parametry transakcí, nativní vs. JTA transakční manažeři), poté jsou popisovány možnosti při využití Spring frameworku a nakonec jsou uvedeny různá doporučení s ohledem na typ aplikace nebo aplikačního serveru. Co přednáška neřeší jsou transakce ve spojení s JMS.

Jednou z hlavním myšlenek a doporučení je používání nativních transakčních manažerů (HibernateTransactionManager, DataSourceTransactionManager, ...) v porovnání s JTA transakčními manažery. Samozřejmě pokud to situace dovolí, tedy když naše aplikace nevyžaduje propagaci transakcí (např. když aplikace běží přes více JVM) nebo koordinaci více datových zdrojů najednou (např. při použití více databází). Poslední bod je ovšem možné řešit bez použití "plného" J2EE aplikačního serveru pomocí standalone řešení (Geronimo transakční manažer, JOTM, ...).

1. listopadu 2007

Přednáška: spring v produkčním nasazení

Pokud nesledujete pravidelně novinky na stránkách Spring frameworku, tak mám pro vás zajímavou informaci - firma Interace21 začala nabízet interaktivní webové prezentace o Springu.

Tento týden proběhla první z těchto přednášek na téma "Spring in production". Kromě samotné přednášky, která trvala hodinu, byla možnost stáhnutí dokumentu popisující Spring v reálném produkčním nasazení (tedy víceméně to samé, co zaznělo na samotné přednášce). Snad všechny různé typy a triky jak zvýšit výkon nebo něco optimalizovat jsem již slyšel, ale ještě jsem neviděl takto komplexní dokument, který by vše uceleně popisoval. Kromě těchto optimalizačních rad dokument obsahuje souhrnný popis toho, co se děje v průběhu inicializace Spring aplikace.

Chtěl jsem ten dokument přiložit do blogu, ale na poslední chvíli jsem si všimnul, že ten dokument není volně k šíření. Pokud o něj tedy máte zájem, tak se budete muset přihlásit k prezentacím o Springu (a snad ho bude ještě možné stáhnout) a nebo počkat, až se tento dokument objeví na stránkách Interface21 (tuto informaci jsem zachytil během chatu v průběhu přednášky).

Nejsou ještě známy témata dalších přednášek, jen doufám, že nějaké stejně zajímavé budou i nadále. Super by bylo, kdyby se ty přednášky časem objevily i na Parleys nebo podobných serverech.

25. října 2007

Testovací frameworky pro webové GUI

Rozhodli jsme se, že posuneme naše interní testování aplikací zase o kousek dále, a že vybereme nějaký testovací framework pro GUI webových aplikací.

Zatím jedinou zkušenost, co se týká testování web GUI, jsem měl s jWebUnit. Již jsem tuto knihovnu delší dobu nepoužíval, ale myslím si, že by se v dnešní době dalo najít lepší řešení. Ono je to tedy těžké napsat "lepší řešení", protože každý má jiné požadavky a cíle testování - někdo upřednostní GUI klienta na vytváření testů, někdo má rád programové vytváření testů přes API, někdo chce zaměřit na testování kompatibility mezi prohlížeči atd. Přesněji řečeno se tedy snažím "zmapovat trh" s testovacími frameworky a jeden vybrat a ten zkusit.

Já jsem věnoval zhruba půl dne tomu, abych si udělal hrubý obrázek o současných možnostech testovacích frameworků pro web GUI. V rychlosti jsem prostudoval Canoo WebTest, Selenium a Jameleon - ty jsem nominoval do užšího výběru na základě referencí od známých nebo podle toho, co jsem se dočetl na webu.
Znovu chci zdůraznit, že jsem vše procházel velice hrubě - pročetl jsem si základní dokumentaci, vyzkoušel dostupné nástroje a napsal 1-2 zkušební testíky. Nic víc. Pokud se mnou v nějakém bodě hodnocení nebudete souhlasit nebo naopak by jste něco doplnili, tak budu jedině rád.

Pro úplnost ještě uvádím odkaz na přehled všech možných testovacích knihoven a frameworků pro Javu.

Canoo WebTest

Plusy:
  • testovaní bez nutnosti použití webového prohlížeče

  • ovládání pomocí ANTu - Canoo definuje ANT tasky, kterými je možné vše ovládat (hlavně spouštět testy)

  • možnosti kontroly PDF a Excel dokumentů

  • dokumentace je rozsáhlá a přehledná

  • velice intuitivní, testy může vytvářet i neprogramátor

Mínusy:
  • vše je nutné definovat ručně přímo v XML, žádné podpůrné grafické rozhraní

  • Canoo nesimuluje reálného uživatele přes prohlížeč, pouze se víceméně zajímá o čisté HTML (to může být i výhoda pro určité typy testů)

  • může být rozdílné chování z pohledu Canoo a reálného uživatele (např. různá implementace Javascriptu v prohlížeči a v této knihovně)



Selenium

Plusy:
  • simuluje reálného uživatele

  • možnost testování kompatibility webové aplikace s prohlížeči

  • Selenium IDE (plugin do Firefox) - přes toto IDE je možné vytvářet testy (manuálně nebo nahráváním); velice intuitivní nástroj, vhodný i pro neprogramátory. Tento plugin z mého pohledu nemá chybu, parádní integrace s Firefoxem.

  • testy je možné vytvářet nahráváním pomocí Selenium IDE

  • možnost rozšiřování základní funkcionality pomocí extensions

  • ANT tásky a možnost spouštění testů z příkazové řádky

  • dokumentace, podpora

  • možnost vytváření testů přímo z jazyka Java případně z jiných jazyků (.NET, PHP, Perl, ...)

Mínusy:
  • měl jsem malé problémy se spouštěním testů z příkazové řádky, které jsem nejdříve vytvořit přes IDE nahráním

  • testovaná aplikace musí být spuštěna před vlastním testováním



Jameleon

Plusy:
  • asi nejvíce rozsáhlé a robustní řešení na testování. Je možné využívat formou pluginů jiné webové testovací frameworky - Selenium, jWebUnit, ...

  • není jen pro testovaní web GUI, ale obecně pro testování čehokoliv

Mínusy:
  • asi nejstrmější "learning-curve" z uvedených frameworků. U předchozích jsem měl hned pocit, že vím, jak s tím pracovat, tady to tak rychlé nebylo.

  • poslední release byl v prosinci 2006, od té doby nic nového (navíc poslední uvolněná verze není stabilní). Odkazy na archiv mailing listu hlásí chybu.

  • mam pocit, ze Jameleon je základní framework pro testování, který je potřeba si ještě rozšířit pro své konkrétní potřeby, aby se jeho potenciál maximálně využil (toto ale může být i výhoda)


Vítězem prvního kola se u mě stal framework Selenium. Rozhodla jednoduchost vytváření testů (která ale není vyvážena tím, že by to bylo slabé na funkcionalitu) a možnost vytváření testů neprogramátory.

20. října 2007

ImageUploader - komponenta pro upload souborů

Na posledním projektu jsme celkem dost intenzivně používali komponentu ImageUploader od firmy Aurigma. Tuto nebo jinou komponentu jsme museli použít, protože zákazník měl následující požadavky:

  • elegantní způsob výběru dokumentů (textových i obrazových)

  • možnost náhledů pro obrazové dokumenty

Také je vhodné dodat, že předchůdce námi vyvíjené aplikace byla desktopová aplikace a spoustu uživatelů nebylo možné přesvědčit, že webová aplikace se chová jinak než standardní Windows aplikace.



S komponentou jsme pracovali přibližně půl roku. Komponentu jsme nevyužívali jen pro jednoduché odesílání souborů, ale potřebovali jsme odeslat dalších cca 30 položek formuláře. Některé věci šli celkem bez problémů, jiné bylo potřeba různými způsoby obcházet a myslím, že s výsledkem je i zákazník spokojený.
Než abych to složitě popisoval, tak nyní shrnu výhody a nevýhody, které tato komponenta z našeho pohledu má.

Výhody
  • komponenta je dostupná jak pro IE (ActiveX verze), tak i pro ostatní prohlížeče (Java applet verze)

  • kvalitní dokumentace včetně kvalitního online dema

  • celkem kvalitní podpora, např. forum

  • chování komponenty lze upravit konfigurací velkého množství parametrů, vše se ovládá pomoci JavaScriptu

  • komponenta neslouží jen k výběru souborů a jejich odeslání, ale umožňuje například vytváření náhledů, získávání informací z EXIF, přidání vodoznaku atd. Přehled vlastností je možné si prohlédnout zde.

  • i cena mi přijde rozumná. Za dualní verzi (ActiveX a Applet) s vazbou na doménové jméno serveru je cena $299.


Nevýhody
  • možnosti a chování obou verzí (ActiveX, Applet) nejsou totožné. Není to jen rozdíl (není ale jinak výrazný) v chování komponenty z pohledu uživatelského ovládání, ale hlavně rozdílu z pohledu funkcionality - odlišná podpora formátů obrázků (např. TIFF obrázky), jiné možnosti získání informací z EXIF, odlišný přístup pro získání data vzniku dokumentu. Toto je zřejmě dáno rozdíly v použitých jazycích pro vývoj těchto komponent (Java vs. C#).

  • slabá integrace s komponentou Thumbnail. Tato komponenta je dodávaná spolu s ImageUploader a slouží pro zobrazování náhledů vybraných dokumentů. Tato integrace ale nefunguje úplně 100% - je k tomu potřeba celkem dost JavaScript kódu, chování se liší pro obě verze komponenty a pořád se nám to nepodařilo dotáhnout do takové podoby, jakou by jsme chtěli.

  • zatímco ActiveX komponenta resp. IE neměly s testovacími certifikáty (tj. certifikáty podepsanými naší interní autoritou) žádné problémy, u Java verze to bylo horší. Pokud jsou ale certifikáty pro HTTPS podepsané nějakou známou autoritou, tak také není žádný problém.


I přes nějaké ty nevýhody jsem byl spokojený a pokud bude potřeba, tak tuto komponentu použiji i v dalších projektech.

18. října 2007

Zajímavé články o Acegi security

Dnes jsem narazil na (z mého pohledu) velice zajímavé články o Acegi. Kdyby podobné články byly již před pár lety, kdy jsem začínal s Acegi, tak bych si určitě ušetřil spoustu času :).

Acegi Security in one hour

Securing Java applications with Acegi


From Java EE security to Acegi

15. října 2007

Vytváření JSP EL funkcí

JSP EL používám často a sem tam se mi stane, že bych potřeboval použít vlastní funkci. J2EE specifikace nabízí velmi elegantní řešení, jak si rychle takovou funkci vytvořit.
Pozn. Přijde mi, že je to v tutoriálu J2EE trochu zapadlé, tak proto jsem se rozhodl o tom napsat dnešní článek.

Originální popis se nachází zde v tutoriálu J2EE.

Pro demonstraci postupu jsem si vybral funkci, která mi řekne, zda je/není daný prvek v kolekci.

Vytvoření funkce

package cz.anect.mis.web.tags;

import java.util.Collection;

/**
* This class encapsulates handy funcions for using in JSP EL.
*
* Functions must be defined in /WEB-INF/functions.tld.
*
* @author pjuza@anect.com
*/
public class Functions {

/**
* Method returns true if specified object is contained in
* input collection.
* @param col Input collection for searching
* @param obj Searched object
* @return returns true if specified object is contained in input collection.
*/
public static boolean inCollection(Collection col, Object obj) {
if (col == null || obj == null)
return false;

return col.contains(obj);
}
}

Snad jediné omezení je, že funkce musí být statická a veřejná (public).

Definice funkce pomocí TLD

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>A tag library that defines my custom functions.</description>
<tlib-version>1.0</tlib-version>
<short-name>FunctionTagLibrary</short-name>
<uri>/FunctionLibrary</uri>

<function>
<name>inCollection</name>
<function-class>cz.anect.mis.web.tags.Functions</function-class>
<function-signature>boolean inCollection(java.util.Collection, java.lang.Object)</function-signature>
</function>
</taglib>


Použití funkce

Nejdříve musíme TLD standardně nadeklarovat před vlastním použitím na stránce:
<%@ taglib uri="/WEB-INF/tld/functions.tld" prefix="myfn" %>

A nyní můžeme bez problémů danou funkci použít:

<c:forEach var="kw" items="${keywords}">
<c:if test='${not myfn:inCollection(search.basicKeyWordIds, kw.id)}'>
<option value="<c:out value="${kw.id}"/>"><c:out value="${kw.value}"/></option>
</c:if>
</c:forEach>

5. října 2007

AOP proxy objekty - volání metody v rámci jedné třídy

Dlouho jsem přemýšlel nad nějakým výstižným názvem tohoto příspěvku, ale nic moc jsem nevymyslel. Zkusím tedy popsat o co přesně jde.
Mám třídu, která má dvě metody. Obě metody se mají pouštět v transakci, každá metoda může nebo nemusí mít svoje vlastní nastavení transakční definice. Pro účely ukázky jsem si vymyslel následující příklad:

public interface DocumentFacade {
/**
* Metoda pridava vice dokumentu.
*/
public DocumentIdsFacadeResult addDocuments(DocumentsDTO doc);

/**
* Metoda pridava pouze jeden dokument, tato metoda je volana metodou addDocuments().
*/
public DocumentIdsFacadeResult addDocument(DocumentsDTO doc);
}

<bean id="DbTransactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="dbTransactionManager" />
<property name="transactionAttributes">
<props>
<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>

<bean id="serviceProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<idref bean="documentFacade" />
</list>
</property>
<property name="interceptorNames">
<list>
<idref bean="DbTransactionInterceptor" />
</list>
</property>
</bean>

Z požadavků zákazníka vyplývá následující chování - pokud se bude vkládat více dokumentů, tak výsledek každého vložení dokumentu bude nezávislý (atomický) od ostatních vložení dokumentů. Když tedy budu přidávat dva dokumenty a během prvního se objeví nějaká chyba ve zpracování, tak vložení druhého dokumentu to nemůže ovlivnit. Jinak řečeno každé volání metody addDocument() bude ve své vlastní transakci.

Pro méně zkušené uživatele AOP (mezi ně se řadím i já) se může zdát, že předcházející požadavek je automaticky vyřešen nastavením transakcí - obě metody odpovídají nastaveným transakčním atributům (viz bean serviceProxyCreator). A zde je právě kámen úrazu. Pokud bychom volali jednotlivé metody nezávisle (např. z kontroleru), tak vše bude chodit bez problémů, ale vzhledem k tomu, že jedna metoda je volána z druhé metody stejné třídy resp. objektu, tak nedochází ke vstupu do objektu přes proxy (jsme pořád v tom samém objektu) a tudíž se neaktivuje transakční nastavení pro druhou metodu. Pro náš příklad to znamená, že vše se provede pouze v jedné transakci definované pro první metodu, tedy addDocuments().
Toto samozřejmě není problém jen transakcí, ale obecně všech AOP proxy objektů.

Jaká jsou možná řešení? Z dostupných informací jsem našel následující řešení:
  • Refaktorovat strukturu volání, tak aby nedocházelo k volání v rámci jedné třídy. Toto řešení se může nakonec ukázat jako nejvíce použitelné pro případy, které nelze vyřešit následujícím řešením.

  • Použít programové transakční vymezení pomocí TransactionTemplate. Toto je celkem elegantní řešení pokud nechceme "uměle" refaktorovat kód. Problém může ovšem být v případě, kdy potřebujeme nastavit transakci přes více zdrojů (např. databáze a JCR). Pro tento případ je pak nutné použít JtaTransactionManager.
    Možná by šla ještě vnořená implementace TransactionTemplate, ale toto jsem nezkoušel. Velká výhoda nastavení transakcí ve Springu je ta, že infrastrukturní věci jako transakce mohu definovat deklarativně a mimo vlastní kód - zde tyto výhody ztrácím.

  • Programově se dostat k proxy objektu a uměle nasimulovat volání metody z vně objektu. Přesně tomuto řešení nerozumím (více informací je možné nalézt zde), ale každopádně je to už řešení, které je pevně spojeno se Springem a vlastním kódem.


Já osobně jsem zatím vždy použil druhý způsob.

28. září 2007

Fulltextové vyhledávání (Lucene, Compass)

V dnešní době je celkem běžný požadavek na fulltextové vyhledávání v aplikaci. Uživatel aplikace si již nechce pamatovat všechny ty možné atributy a vybírat možné hodnoty - on se prostě potřebuje dostat k cíli na základě toho co zná a už bez ohledu na vlastní uložení dat.
Pokud se někdo rozhodne zahrnout fulltextové vyhledávání do své aplikace, tak první co bude muset vyřešit je, zda použít vestavěnou podporu fulltextu v databázi či vybrat nějakou knihovnu, která bude fulltext implementovat. Pro obě volby existují určité výhody a nevýhody.
Mezi výhodami řešení pomocí knihoven se často uvádí:

  • výkonnější než databázové řešení

  • plně konfigurovatelné

  • plně pod kontrolou

  • databázové řešení lze použít pouze pro omezený doménový model, kdežto knihovna v tomto ohledu žádné omezení nemá


Mezi výhody databázového řešení patří:
  • většinou automaticky součástí databázových serverů

  • snadné a lehké použití bez nějakého velkého nastavování

  • někdy i lepší vlastnosti než řešení pomocí knihovny, např. podpora češtiny


Pro vysvětlení posledního bodu bych rád dodal některá fakta: já osobně používám knihovnu Apache Lucene (verze 2.0). K hodnocení této knihovny se dostanu dále v článku, ale co už bych chtěl uvést již zde, je horší podpora češtiny. Není problém samozřejmě ukládat český text s diakritikou a i vyhledávat s diakritikou, ale již není možné počítat s ohýbáním slov. To je možné ve většině případů řešit pomocí zástupného znaku *, ale není to úplně stoprocentní. Na druhou stranu např. Microsoft nabízí český a slovenský jazykový modul do svého SQL serveru, který tuto funkcionalitu nabízí. Žádnou osobní zkušenost s tím ale nemám.

Apache Lucene a Spring modules

Jak už jsem zmínil, tak ve svém projektu používám knihovnu Apache Lucene. Nejsem odborník přes fulltextové techniky, ale myslím si, že Lucene nabízí jedno z nejlepších (možná i nejlepší) řešení pro fulltextové vyhledávání na platformě Java. Další důvod, proč jsem toto řešení kdysi vybral bylo množství dokumentace a informací na webu a také podpora ve Spring modules, což zase posouvá práci s touto knihovnou o úroveň výše. S knihovnou Lucene resp. Spring modules jsem naprosto spokojený - co se týče nastavení datového modelu, co se týče použití (ale to hlavně díky Spring modules) a také výkonnost se ukazuje jako velice dobrý. Aplikaci teď budeme nasazovat do pilotního provozu u zákazníka, takže na závěrečné zhodnocení je ještě čas.

Compass

Apache Lucene a Spring modules bude znát asi většina lidí, ale již méně je známý Compass - Java Search Engine Framework. Já osobně jsem si této knihovny všimnul na konferenci Spring One a dokonce jsem našel ke stažení vlastní prezentaci.
Nemám s tímto frameworkem zatím žádnou osobní zkušenost, ale některé klíčové vlastnosti jsou velice zajímavé:
  • Framework je postaven nad Apache Lucene. Compass lze používat nejen jako nástavbu nad Lucene, která řeší některé jeho nedostatky jako např. chybějící podpora transakcí, příliš nízkoúrovňové API, nepříliš rychlá aktualizace indexů a možnost ukládání indexů pouze na souborový systém (pozn. nízkoúrovňové API a ukládání indexů řeší již Spring modules).

  • Integrace se Springem a Hibernate

  • Compass umožňuje si stávající datový model (nebo jeho část) napamovat pro účely fulltextového vyhledávání. K tomuto je možné použít konfiguraci pomocí XML nebo anotací.

  • Změna datového modelu se může automaticky promítat v indexech.


Pokud někdo máte zkušenost s jazykovým modulem do MS SQL nebo jiných databází či přímo s Compassem, budu jedině rád, když se podělíte o vaši zkušenost.

23. září 2007

Ukázka konfigurace Acegi security

Pro tento článek jsem vybral konfigurační soubor Acegi security pro náš jeden projekt. Rád bych pár slovy popsal jednotlivé body konfigurace a částečně tím prezentoval možnosti této knihovny. Já osobně považuji Acegi security za nejlepší knihovnu pro řešení bezpečnostních problémů spojených s vývojem aplikací, tedy hlavně s autentizací a autorizací uživatelů.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<!--
- Acegi Security configuration.
-->
<beans>

Definice filtrů pro různé adresy. Zmínil bych zde jen dvě důležité věci - pořadí filtrů je velice důležité a je možné definovat různé filtry pro různé URL adresy. Zápis je pomocí ANT stylu.

<!-- Zakladni bean s definici pouzitych filtru.
Poradi pouzitych filtru je dulezite! -->
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/remoting/**=basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
/**=concurrentSessionFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>

ConcurrentSessionFilter aktualizuje informace o session a tím pádem je možné kontrolovat vypršení session pro daného uživatele.

<!-- Filter required by concurrent session handling package -->
<bean id="concurrentSessionFilter" class="org.acegisecurity.concurrent.ConcurrentSessionFilter">
<property name="expiredUrl" value="${acegi.expiredUrl}"/>
<property name="sessionRegistry" ref="sessionRegistry"/>
</bean>

HttpSessionContextIntegrationFilter se stará o přenos SecurityContext mezi jednotlivými HTTP voláními.

<!-- HttpSessionContextIntegrationFilter is responsible for storing a SecurityContext between HTTP requests -->
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>

LogoutFilter slouží k odhlášení přihlášeného uživatele (vymazání kontextu z registru). Definice obsahuje URL adresu, kam se má uživatel přesměrovat po odhlášení a seznam handlerů (implementace LogoutHandler), které se mají provést. Nezbytným handlerem je SecurityContextLogoutHandler, který vymazává informace o přihlášeném uživateli z registru. Kromě toho jsem si vytvořil handler pro účely logování.

<!-- Logs a principal out.
Use <a href="j_acegi_logout">Logout</a> on the page. -->
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="${acegi.urlAfterLogout}"/> <!-- URL redirected to after logout -->
<constructor-arg>
<list>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
<bean class="cz.anect.securitymodule.ldap.LogoutHandlerImpl"/>
</list>
</constructor-arg>
</bean>

Filter pro zpracování požadavku na přihlášení. Standardně je možné použít AuthenticationProcessingFilter. Já jsem si standardní filtr upravil, protože jsem požadoval zablokování účtu po několika špatných pokusech o přihlášení. Tuto implementaci jsem popisoval v tomto článku.

<!-- Processes an authentication form. -->
<bean id="authenticationProcessingFilter" class="cz.anect.securitymodule.DisableAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="${acegi.authenticationFailureUrl}"/>
<property name="defaultTargetUrl" value="/"/>
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
<property name="disabledAccountManager" ref="disabledAccountManager"/>
</bean>

BasicProcessingFilter zpracovává BASIC hlavičky z HTTP požadavků. Tento filtr používám pouze pro relativní adresy začínající "remoting". Na této adrese jsou totiž publikované vzdálené služby (remote services) a já pomocí Acegi zajišťuji autorizovaný přístup k těmto službám.
  
<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="authenticationEntryPoint"><ref bean="authenticationEntryPoint"/></property>
</bean>

<bean id="authenticationEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="MIS_REALM"/>
</bean>

<!-- A Filter which populates the ServletRequest with a new request wrapper. -->
<bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>

AnonymousProcessingFilter slouží k vytváření anonymních uživatelů (= uživatelů, kteří nejsou přihlášeni). To má tu velkou výhodu, že já si zde mohu nadefinovat parametry anonymního uživatele a pak v aplikaci s tím pracovat jako s jakýmkoliv dalším uživatelem. Nepřihlášení uživatel tedy není null objekt, ale normální uživatel se specifickými vlastnostmi.

<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="anonymKey"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>

ExceptionTranslationFilter zpracovává Java výjimky a vytváří odpovídající HTTP odpovědi.

<!-- Handles any AccessDeniedException and AuthenticationException thrown within the filter chain. -->
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="${acegi.loginFormUrl}"/>
<property name="forceHttps" value="true"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="cz.anect.securitymodule.ldap.CustomAccessDeniedHandlerImpl">
<property name="errorPage" value="${acegi.accessDeniedUrl}"/>
</bean>
</property>
</bean>

FilterSecurityInterceptor řídí přístup k jednotlivým URL adresám. Nastavení URL adres mám v odděleném properties souboru, který uvedu na konci.

<!-- protect web URIs -->
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>${acegi.urlMapping}</value>
</property>
</bean>

ProviderManager prochází zaregistrované providery a snaží se získat odpověď na autentikační požadavek (např. přihlášení uživatele k aplikaci). Jednotlivé providery se procházejí v uvedeném pořadí a to do té doby, než nejaký provider vrátí odpověď.
    
<!-- authority providers -->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="ldapAuthProvider"/>
<bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="anonymKey"/>
</bean>
</list>
</property>
</bean>

DaoAuthenticationProvider je provider pro přístup k datům, které jsou uloženy v databázi nebo v paměti. Tento provider toho sám o sobě moc neumí, proto je potřeba zaregistrovat userDetailsService. Já tento provider používám pouze pro účely autorizace přístupu k publikovaným službám (=remote services).

<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>

InMemoryDaoImpl je nejjednodušší implementace UserDetailsService a umožňuje mi nadefinovat si uživatele např. pomocí properties souborů. Jak jsem již zmiňoval v minulém bodě, tak tento provider resp. UserDetailService používám pouze pro oveření přístupu ke vzdáleným službám. V properties souboru si nadefinuji fiktivní uživatele pro jednotlivé klienty vzdálených služeb a mám jednoduchý způsob, jak mohu ověřovat přístup.

<!-- In-memory implementation; all information is loaded from properties file -->
<bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userProperties">
<bean class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:/config/common/users.properties"/>
</bean>
</property>
</bean>

DefaultInitialDirContextFactory slouží pro připojení k LDAP serveru. Jednotlivé hodnoty jsou uloženy v properties souboru, který uvedu na konci článku.

<!-- =========================== LDAP ========================== -->
<!-- Pripojeni k LDAP serveru -->
<bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="${ldap.providerUrl}"/>
<property name="managerDn" value="${ldap.managerDn}"/>
<property name="managerPassword" value="${ldap.password}"/>
</bean>

LdapAuthenticationProvider je základní provider pro integraci s LDAP serverem. Nastavení LDAP provideru spočívá ve dvou hlavních věcech - jak bude probíhat autentikace uživatele a jak se budou načítat role k uživatelům. Tyto věci jsou závislé na struktuře LDAP serveru a proto je nutné toto nastavení upravit dle aktuální podoby. S ohledem na specifické požadavky autentikace na základě různých atributů v LDAP serveru jsem si vytvořil vlastní implementaci LDAP provideru.

<bean id="ldapAuthProvider" class="cz.anect.securitymodule.ldap.CustomLdapAuthenticationProvider">
<constructor-arg>
<!-- autentifikace uzivatele -->
<bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userDnPatterns">
<list>
<value>${ldap.userDnPatterns1}</value>
<value>${ldap.userDnPatterns2}</value>
<value>${ldap.userDnPatterns3}</value>
<value>${ldap.userDnPatterns4}</value>
<value>${ldap.userDnPatterns5}</value>
<value>${ldap.userDnPatterns6}</value>
<value>${ldap.userDnPatterns7}</value>
<value>${ldap.userDnPatterns8}</value>
<value>${ldap.userDnPatterns9}</value>
<value>${ldap.userDnPatterns10}</value>
<value>${ldap.userDnPatterns11}</value>
<value>${ldap.userDnPatterns12}</value>
<value>${ldap.userDnPatterns13}</value>
<value>${ldap.userDnPatterns14}</value>
<value>${ldap.userDnPatterns15}</value>
<value>${ldap.userDnPatterns16}</value>
<value>${ldap.userDnPatterns17}</value>
<value>${ldap.userDnPatterns18}</value>
<value>${ldap.userDnPatterns19}</value>
<value>${ldap.userDnPatterns20}</value>
</list>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<!-- nacteni roli k uzivateli -->
<bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<constructor-arg value="${ldap.groupSearchBase}"/>
<property name="groupSearchFilter" value="${ldap.groupSearchFilter}"/>
<property name="groupRoleAttribute" value="${ldap.groupRoleAttribute}"/>
<property name="rolePrefix" value="${ldap.rolePrefix}"/>
<property name="convertToUpperCase" value="${ldap.convertToUpperCase}"/>
<property name="searchSubtree" value="true"/>
</bean>
</constructor-arg>
</bean>
<!-- =========================== LDAP ========================== -->

AffirmativeBased je jednoduchou implementací AccessDecisionManager. AccessDecisionManager rozhoduje o tom, zda přihlášený uživatel má dostatečná práva pro přístup k cílovému zdroji (to může být např. webová stránky na určité adrese, to může být metoda v Java rozhraní apod.). Pro můj konkrétní případ se kontroluje role daného uživatele (RoleVoter) a (pokud není první kontrola úspěšná) speciální atributy IS_AUTHENTICATED_FULLY nebo IS_AUTHENTICATED_REMEMBERED nebo IS_AUTHENTICATED_ANONYMOUSLY (AuthenticatedVoter) , které mohu použít v definici přístupu ke stránkám.

<!--
AffirmativeBased implementation will grant access if one or more ACCESS_GRANTED votes were
received (ie a deny vote will be ignored, provided there was at least one grant vote).
In other words principal must have corresponding ROLE and particular level of authentication.
-->
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<!-- class will vote if any ConfigAttribute begins with ROLE_. -->
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>

MethodSecurityInterceptor slouží k ověřování přístupu na úrovni Java kódu.

<!-- method authorization in FACADE LAYER -->
<bean id="facadeSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="validateConfigAttributes"><value>true</value></property>
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
cz.anect.mis.web.facade.DocumentFacade.addDocuments=ROLE_UZIVATELE, ROLE_SPRAVCIGLOBALNI, ROLE_SPRAVCILOKALNI, ROLE_ADDDOCUMENT_WITHOUTEDIT, ROLE_ADDDOCUMENT_WITHEDIT
cz.anect.mis.web.facade.DocumentFacade.addDocumentData=ROLE_ADDDOCUMENTDATA
</value>
</property>
</bean>

<!-- auto proxy for beans which we want to intercepts by security interceptor -->
<bean id="autoProxySecurityCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<idref bean="facadeSecurity"/>
</list>
</property>
<property name="beanNames">
<list>
<idref bean="DocumentFacade"/>
</list>
</property>
</bean>

Registrace listenerů pro účely logování a zachycení neúspěšných pokusů o přihlášení.
    
<!-- This bean is optional; it isn't used by any other bean as it only listens and logs -->
<bean id="loggerListener" class="cz.anect.securitymodule.CustomLoggerListener"/>

<bean id="failureListener" class="cz.anect.securitymodule.AuthentificationFailureListener">
<property name="disabledAccountManager" ref="disabledAccountManager"/>
</bean>

<bean id="disabledAccountManager" class="cz.anect.securitymodule.BasicDisabledAccountManager">
</bean>

<bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"/>

</beans>

Nyní ještě uvedu použité properties soubory.
###############################################
# property file: acegi_base.properties #
# format : key = value #
# Zakladni nastaveni pro Acegi knihovnu #
###############################################

acegi.expiredUrl =/expired.jsp
acegi.urlAfterLogout =/index.jsp
acegi.authenticationFailureUrl =/login.jsp?login_error=1
acegi.loginFormUrl =/login.jsp
acegi.accessDeniedUrl =/accessDenied.jsp



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>
Mapovani mezi URLs a rolemi.
Poradi je dulezite, konstanty jsou definovany v AuthenticatedVoter:
IS_AUTHENTICATED_FULLY or IS_AUTHENTICATED_REMEMBERED or IS_AUTHENTICATED_ANONYMOUSLY.
!!! prvni dva radky nechat beze zmeny !!!
Dalsi radky jsou ve formatu: url = jmeno role (popr. vyse uvedene promenne)
</comment>
<entry key="acegi.urlMapping">
<![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/adddocument.*=ROLE_UZIVATELE, ROLE_SPRAVCIGLOBALNI, ROLE_SPRAVCILOKALNI
/adddocumentas.*=ROLE_UZIVATELE, ROLE_SPRAVCIGLOBALNI, ROLE_SPRAVCILOKALNI
/adddocumentdataonly.*=ROLE_ADDDOCUMENTDATA
/adddocumenttodata.*=IS_AUTHENTICATED_FULLY
/analogdocstoreadmin.*=IS_AUTHENTICATED_FULLY
/newdigiuser.*=IS_AUTHENTICATED_FULLY

/remoting/*=ROLE_REMOTE_SERVICES, ROLE_BINDED_APPLICATION
]]>
</entry>
</properties>


###########################################################
# property file: users.properties #
# format : key (username) = value (password, roles #
# List of technical users for accessing remote services #
###########################################################

system1=673yuededjei3yuy3bhyu3,ROLE_REMOTE_SERVICES
system2=eddjeihyu63jeui3j7337,ROLE_REMOTE_SERVICES


###############################################
# property file: ldap.properties #
# format : key = value #
# Udaje pro pripojeni k LDAP serveru #
###############################################

#-------------------- pripojeni k LDAPu
#This should be in the form ldap://monkeymachine.co.uk:389/dc=acegisecurity,dc=org
ldap.providerUrl =ldap://
#directory user to authenticate (including base DN)
ldap.managerDn =
ldap.password =

#-------------------- autentizace uzivatele
#DN sablona pro vyhledavani uzivatele (max. 20 polozek)
ldap.userDnPatterns1 =uid={0},ou=uzivatele,ou=302
ldap.userDnPatterns2 =uid={0},ou=uzivatele,ou=311
ldap.userDnPatterns3 =uid={0},ou=uzivatele,ou=321
ldap.userDnPatterns4 =uid={0},ou=uzivatele,ou=331
ldap.userDnPatterns5 =uid={0},ou=uzivatele,ou=341
ldap.userDnPatterns6 =uid={0},ou=uzivatele,ou=342
ldap.userDnPatterns7 =uid={0},ou=uzivatele,ou=351
ldap.userDnPatterns8 =uid={0},ou=uzivatele,ou=353
ldap.userDnPatterns9 =uid={0},ou=uzivatele,ou=361
ldap.userDnPatterns10 =uid={0},ou=uzivatele,ou=362
ldap.userDnPatterns11 =uid={0},ou=uzivatele,ou=371
ldap.userDnPatterns12 =uid={0},ou=uzivatele,ou=373
ldap.userDnPatterns13 =uid={0},ou=uzivatele,ou=381
ldap.userDnPatterns14 =uid={0},ou=uzivatele,ou=391
ldap.userDnPatterns15 =uid={0},ou=uzivatele,ou=partneri
ldap.userDnPatterns16 =uid={0},ou=uzivatele,ou=verejnost
ldap.userDnPatterns17 =
ldap.userDnPatterns18 =
ldap.userDnPatterns19 =
ldap.userDnPatterns20 =

#-------------------- nacteni roli k uzivateli
#kontejner pro vyhledavani roli k teto aplikaci (DN bez zakladniho DN)
ldap.groupSearchBase =
#jmeno atributu u role, ktery obsahuje odkazy na uzivatele v dane skupine
ldap.groupSearchFilter =(roleOccupant={0})
#jmeno atributu, ktery se pouzije pro ziskani nazvu role
ldap.groupRoleAttribute =cn
#prefix ke jmenu role v LDAPu
ldap.rolePrefix =ROLE_
#maji se jmena roli prevadet na velka pismena? true|false
ldap.convertToUpperCase =true

19. září 2007

Přehled článků a knih o Springu

Kamarád se na mě obrátil s prosbou, zda bych mu mohl poslat nějaké odkazy ke Springu, že by se rád s tím seznámil. Řekl jsem si, že když už mu budu něco posílat, tak to můžu dát rovnou na blog.
V posledních dvou letech jsem si přidal do oblíbených pár článků a přečetl jsem nějaké knížky. Tyto informace bych chtěl zde tedy publikovat.

Články


Knihy

  • Agile Java Development with Spring, Hibernate and Eclipse - tuto knížku jsem četl nedávno a nedala mi nic nového. Jsem z ní spíše zklamaný, protože jsem měl pocit, že autor tam toho chtěl uvést co nejvíce, ale nic pořádně. Měl jsem také dojem, že kdybych začínal od začátku, tak nemám šanci se to podle toho naučit. Takže tuto knížku moc nedoporučuji.

  • Spring in Action - tuto knížku jsem celou nečetl, spíše jsem jí v rychlosti prošel. Pro začátečníky mi přijde dobrá, protože obsahuje celkem srozumitelný výklad o vlastnostech Springu včetně příkladů. Na druhou stranu knížka obsahuje skoro to samé jako referenční dokumentace ke Springu.

  • POJO in Action - tato knížka není jen o Springu, ale i přesto do tohoto seznamu patří. Kniha obsahuje spoustu zajímavých informací a příkladů a je z ní cítit, že autor zkušenosti má a více o čem píše. Tuto knížku vřele doporučuji.

  • J2EE Design and Development - tato knížka je taková moje programovací bible. Sice byla vydána před pár lety, ale pořád většina uvedených myšlenek je aktuální. V této knížce uvádí zakladatel Springu Rod Johnson základní myšlenky pro vývoj aplikací, podle kterých pak vlastně vznikl samotný Spring.


Pokud někdo máte jiné zajímavé odkazy na články nebo jste četly nějaké zajímavé knížky, tak budu jedině rád, když je uvedete v komentářích k tomuto článku.

7. září 2007

Více prostředí pomocí Springu, log4j

Aby byla implementace více prostředí ve Springu kompletní, tak ještě zbývá vyřešit nastavení logování (konkrétně Log4j) pro jednotlivá prostředí. Logger se spouští odděleně od vlastní inicializace Springu a samozřejmě by se měl spouštět jako první. Možná si někdo řekne, že už je to přehnané mít více nastavení loggerů, ale my jsme ty požadavky měly - produkční logger posílá chyby mailem, logger pro vývoj pouze loguje na konzoli a logger pro akceptační testy loguje do souboru. Zde je vidět, že potřeba více loggerů opravdu je.

Tento článek se odvolává na věci uvedené v předchozích článcích na toto téma - Více prostředí pomocí Springu, úvod a Více prostředí pomocí Springu, implementace.

Zjištění cesty ke konfiguraci loggeru

Nastavení loggeru bude umístěno v souboru log4j.xml v adresářích představující jednotlivá prostředí (DEVELOP, ANECT, apod.). Podle volby prostředí v konf. souboru config.properties je potřeba zjistit cestu ke správné konfiguraci loggeru. Do třídy EnvironmentUtils přidáme následující metodu:
  /**
* Method returns location of log4j configuration file.
* @return path to log4j conf. file
*/
public static String getLog4jConfigLocation() {
return "classpath:/config/env/" + getEnvironment() + "/log4j.xml";
}

Vlastní implementace Log4jConfigListener

Známe cestu ke konfiguraci a potřebujeme podle toho upravit standardní Log4jConfigListener.
/**
* Custom listener for log4j inicialization.
*
* This listener is aware of different environments.
*
* @author pjuza@anect.com
*/
public class EnvironmentAwareLog4jConfigListener extends Log4jConfigListener {

/**
* This method is overridden because we need to have environment aware initialization.
* From this reason we need to use custom Log4j configurer.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();

String location = EnvironmentUtils.getLog4jConfigLocation();

// Write log message to server log.
servletContext.log("Initializing Log4J from [" + location + "]");

//Expose the web app root system property.
WebUtils.setWebAppRootSystemProperty(servletContext);

try {
Log4jConfigurer.initLogging(location);
}
catch (FileNotFoundException ex) {
servletContext.log("Log4J config file [" + location + "] not found", ex);
}
}
}
Na závěr je ještě nutné upravit konfiguraci web.xml.
  <listener>
<listener-class>cz.anect.mis.utils.config.EnvironmentAwareLog4jConfigListener</listener-class>
</listener>