1. října 2009

Gradle - druhý krůček

V prvním popisku použití gradlu jsem si ukázali jak na jednoduchý projekt, dneska se podíváme, jak jsme zbuildovali projekt do waru.

Vyjdeme z předcházejícího příkladu. Co musíme změnit, abychom měli jako výsledek projektu war, ve správném layoutu a ne jar? Je toho pekelně málo.


usePlugin "java"
usePlugin "maven"
usePlugin 'eclipse'
usePlugin 'war'

sourceCompatibility = 1.6
group = "cz.svt"
version = "${version}"

manifest.mainAttributes "Implementation-Title": name, ...

configurations {
deployerJars
}

dependencies {
compile fileTree(dir: "lib", includes: [ "util.jar", "JMSMailer.jar"])

compile "org.apache.activemq:activemq-core:5.2.0"
...

providedCompile "javax.servlet:servlet-api:2.4"

runtime "datedFileAppender:datedFileAppender:1.0.2"

deployerJars "org.apache.maven.wagon:wagon-http:1.0-beta-2"
}

repositories {
...
}

uploadArchives {
...
}


Tam kde jsou uvedeny 3 tečky jsem build zkrátil, buď protože obsah je totožný s buildem z minula a nebo výpis nepřináší nic zajímavého. Takže co se fakticky změnilo? Jeden řádek. Přidali jsme na úvod:


usePlugin 'war'


To je celé. Navíc jsme do struktury adresářů přidali adresáře src/main/webapp/META-INF a src/main/webapp/WEB-INF. Mě to přijde úžasně jednoduché a musím říct, že se mi gradle líbí víc a víc.

CZJUG - Hans Dockter - Gradle

Po velmi dlouhém čase jsem se dostal na CZJUG. Nelituju, spíš lituju, že mi to termínově nevychází se tam dostávat častěji. Přednáška o gradle byla hodně zajímavá. Druhou přednášku o MPS jsem nepochopil, jestli to bylo tím pivem nevím. Samozřejmě jsem rád, že jsem mohl potkat staré známé z java komunity.

Hans pojmul přednášku tak jak jsem to nečekal a bylo to hodně zajímavé. V podstatě na úvod nám ukázal jak je gradle inteligentní. Na příkladu přidání source directory do projektu nám ukázal jak na 4 řádcích je možné vyjádřit vše. Závislosti (interní, externí), vytvoření tasků na přeložení, závislostí mezi tasky. Pak přislo na pořad dne základní motivace a tím pádem trochu srovnání s ANTem a mavenem.

Dále se mluvilo o výhodách v podobě vyhodnocování tásků, které se spustí, o multiprojektech atd. Dále jsme se dozvěděli i něco o budoucnosti (především se na nás chystá spolehlivý build bez clean, tj. že gradle bude shopen zaručeně identifikovat co se změnilo a co je díky tomu nutno zbuildovat).

Nosná myšlenka, pro mě, je, že gradle se snaží být takovým vylepšeným mavenem, tj. používá intenzivně standardní layout projektu, standardní lifecycle buildu, ale navíc nabízí neuvěřitelnou flexibilitu, vše je možno změnit.

Pro mě je to cesta správným směrem, byť to vyžaduje kontrolu nad tím, jak je gradle používán, aby se nám build nevymkl z ruky. Navíc je gradle stále mladý a hodně se mění, takže použití v dnešní době není úplně bezproblémové. Ale je to systém hodně perspektivní.

Pokud někoho obsah přednášky zaujal, pak se vše nahrávalo, takže se můžete kouknout na video (odkaz na něj jistě bude na stránkách CZJUGu).

16. srpna 2009

iBATIS 3 - nový bojovník přichází

Jsem dlouholetý uživatel Hibernate, které používáme na velkém projektu a musím se přiznat, že dnes nám spíš přináší komplikace (především u 50GB databáze už hodně záleží jaká SQL se volají a to nám Hibernate neumožňuje ovlivnit). iBATIS to umí i ve verzi 2. Co se mi líbilo na návrhu připravované verze 3 jsem již psal. Ale my tu již máme první betu trojky. (Než se pustím do svých postřehů, rád bych zmínil seriál otce Fura, který je vyčerpávajícím popisem novinek v porovnání s dřívější verzí iBATISu.)

Krom věcí zmíněných již v předcházejícím článku existují další, které mě hodně potěšili:

  • vytváření TypeHandlerů je o poznání jednodušší než podobných UserTypů v Hibernate
  • objekty natažené z DB nejsou vytvářené pomocí bezparametrického konstruktoru, ale pomocí ObjectFactory, takže máte způsob vytvoření objektu plně v ruce (objekty půjde vytvářet pomocí DI frameworku - Spring, Guice) - to je obrovská bolest Hibernate, která se řešila pomocí AOP
  • vytváření dynamických SQL

Jsem moc rád že komunita za iBATISem stojící sebrala odvahu a vrhla se do designu nového frameworku, i když je to za cenu menší zpětné kompatibility. Moc se jim nový iBATIS povedl a řada věcí řeší bolesti, na které musí každý kdo používá Hibernate narazit.

Spolu s iBATISem byl uvolněn i další produkt a to iBATIS Schema Migration System, který umožnuje automatizovat migraci DB schéma z jedné verze tam i zpět (pokud si SQL skript na zpětnou migraci napíšete). Inspirace jistě pochází z Ruby on Rails Migrations. Bohužel Migrations jak je představil team iBATISu mají jeden zásadní problém: migrační scripty se píší přímo v SQL. Takže jen velmi těžko budeme vytvářet skript, který bude fungovat s různými databázemi nebo s různými konfiguracemi téže databáze.

11. srpna 2009

Gradle - první krůčky

O novém build nástroji gradle jsem již psal. Je to už rok a co se za tu dobu stalo? Gradle nám vyrostl z verze 0.2 na verzi 0.7, která je už velmi rozumně použitelná. A proto jsme se rozhodli gradle použít pro náš první projekt.

Jedná se o velmi jednoduchý projekt (matcher pro easymock). Tento projekt obsahuje 3 třídy, které je potřeba zkompilovat. Neobsahuje testy. Výsledný jar je nutno deploynout do firemní artifactory.

A jak takovéto jednuché věci dosáhnout:

usePlugin "java"
usePlugin "maven"

sourceCompatibility = 1.6
group = "cz.svt"
version = "${version}"

manifest.mainAttributes "Implementation-Title": name, "Implementation-Version": version, "Implementation-Vendor": "ČSAD SVT Praha s.r.o."

configurations {
deployerJars
}

dependencies {
compile "commons-beanutils:commons-beanutils:1.7.0"
compile "org.easymock:easymock:2.2"
compile "org.easymock:easymockclassextension:2.2.1"

deployerJars "org.apache.maven.wagon:wagon-http:1.0-beta-2"
}

repositories {
mavenRepo urls: "${artifactoryURL}/repo1"
}

uploadArchives {
repositories.mavenDeployer {
name = 'httpDeployer'
configuration = configurations.deployerJars
repository(url: "${artifactoryURL}/libs-releases-local") {
authentication userName: "${artifactoryUid}", password: "${artifactoryPwd}"
}
snapshotRepository(url: "${artifactoryURL}/libs-snapshots-local") {
authentication userName: "${artifactoryUid}", password: "${artifactoryPwd}"
}
}
}


Maven uživatel může jenom závidět, jak je tento script kompaktní a krátký. Nyní si postupně projdeme co musíme ve scriptu udělat, aby vše fungovalo.

Nejprve definujeme, které pluginy budeme používat. java plugin potřebujeme pro překlad Javy a maven pro deployment do artifactory (jedná se o maven repository).

Další 3 řádky definují, že používáme Javu 1.6, skupina deployovaného artefaktu je "cz.svt" a na verzi se odvoláváme jako na property (o těch si povíme dále). Následuje řádek s definicí manifestu (hojně používáme property dříve nastavené).

Další 3 řádky definují novou konfiguraci deployerJars, kterou použijeme v tasku na deployment jaru do naší sdílené firemní artifactory.

A začínáme tím zajímavým, závislostmi. Stačí nám na ně 7 řádků. Závislosti se definují podobně jako v mavenu, ale skupiona, název, verze se oddělují dvojtečkou. Jinak gradle umí díky ivy pracovat nejen s ivy, ale i maven repository. Poslední závislost říká, že deployování bude potřebovat wagon-http.

Následně defunujeme repository pro resolvování závislostí (opět pomocí property). Poslední je task uploadArchives, který je nejsložitější z celého scriptu. Složitost ovšem spočívá v definování rozdílné repository pro snapshoty a pro finální verze (v podstatě dvakrát to samé). Protože deployment do artifactory nemůže provést každý, je nutné specifikovat už. jméno a heslo (není přímo ve scriptu, ale je to properta).

Jak gradle resolvuje property? Gradle hledá v aktuálním adresáři soubor gradle.properties a dále se kouká do adresáře $HOME/.gradle/ po stejnojmenném souboru. V těchto souborech můžeme definovat property, jenž můžeme využít v buildu. Takže v projektu uvádíme verzi a v domovském adresáři definujeme adresu repository a už. jméno a heslo.

A jaké budou další krůčky? Nejprve přidáme testy, pak generování standardních reportů (javadocs, findbugs, cobertura) a nakonec groovy, aspectj atd...

16. července 2009

Super myšlenka učení programovat na škole - programovat open source projekt

Když jsme byli na jOpenSpace 2009 tak jsme v hloučku zainteresovaných vedli diskusi na téma: "Co by měla vysoká škola dělat, aby naučila své studenty programovat?". V této diskusi byl hodně aktivní Petr Adámek, který jako učitel na Masarykově univerzitě, měl k tématu hodně co říci.

Já jsem šel do diskuse s nosnou myšlenkou, že škola musí více do svého programu zatáhnout komerční sféru (u nás ve firmě pracuje řada studujících), aby se studenti dostali do kontaktu s realitou. Pracovali na skutečných projektech, se skutečným zadáním, spolupracovali s již protřelými programátory atd.

Petr se mnou de facto souhlasil, ale tvrdil, že to je nemožné zrealizovat, že zájem firem není.

Dneska jsem poslouchal Java Posse #263 - Interview with Cay Horstmann (profesor na universitě), kde zazněla ona geniální myšlenka. Jak pracovat na reálném projektu, když firmy nemají zájem. No přeci pracovat na open source projektu. Cay Horstmann řekl, že mají na universitě předmět, jehož náplní je pracovat na již existujícím open source projektu (existujícím - dle výběru studenta). Student se naučí psát kvalitní kód, spolupracovat s kolegy, číst cizí kód, inteligentně se ptát atd.

Řekl bych, že tato myšlenka rozsekla onen pomyslný gordický uzel, který vznikl v diskusi mezi mnou a Petrem.

25. června 2009

Byl jsem na Scrum Master Training

A stal jsem se certifikovaným Scrum Masterem.

O tom, ale nechci psát, chci psát o tom jak moc bylo toto školení prospěšné a co vše mi přineslo. Školení vedl Boris Gloger, který se ukázal jako perfektní přednášející. Přednášenou oblast dokonale zná, nejenom z již uskutečněných školení, ale i z praxe. Navíc jeho skušenosti se zaváděním Scrumu byly hodně veliké, takže na jakoukoliv otázku byl velmi promptně připraven odpovědět a vždy si věděl rady. Opravdu perfektní školitel.

Školení bylo dvoudenní a konalo se na jihu Čech. Provedlo nás postupně všemi částmi Scrumu, které se Scrum Mastera týkají, o všem jsme si příjemně popovídali a řadu věcí jsme si díky šikovně vybraným cvičením i vyzkoušeli.

Co si ze školení odnáším? Především jde o to, že Scrum Master ze mě zatím nebude, protože vývoj softwaru mám moc rád a hodně těžko bych se bez něj obcházel. A Boris striktně nedoporučuje, aby Scrum Master byl zároveň vývojářem, protože Scrum Master má za úkol podporovat a chránit team před okolními vlivy a to by zároveň jako vývojář nemohl dělat na 100%.

Dále jsem si odnesl velmi důležitou část Scrumu a to: čím více jsou lidé z teamu zatáhnuti do možnosti určovat si práci, rozhodovat o ní jak ji udělají a kolik ji udělají (kolik stihnou udělat za následující sprint), pak se o to více snaží, aby to co slíbili také zvládli. Pokud to rozhodne někdo za ně, pak nebojují za svou čest, ale za čest někoho jiného a to se bojuje podstatně laxněji.

To jsou hlavní poznání, které mi školení přineslo. Pak jsem si ještě odnesl pár drobností: neohodnocovat požadavky (Story) pomocí člověkodnů, ale pomocí bodů (díky tomu, že se určí kolik bodů je schopen team zvládnout za jeden sprint, ví se kolik udělá požadavků), skvělá je metodika ohodnocování složitosti požadavků (opět to dělá team pomocí Planning Pokeru), team v jeden okamžik vždy pracuje na jednom požadavku (aby byl software co nejméně rozvrtaný a aby si vzájemně pomohl, protože chce mít požadavek co nejdříve hotový) atd.

Vřele všem podobné školení doporučuji, i když nehodláte být Scrum Master, ani nehodláte vyvíjet software podle Scrumu. Proč? Protože chytrých nápadů je ve Scrumu spousta a spousta se jich dá použít.

7. května 2009

Tapestry 5. 1 je mezi námi

A je to ... verze 5.1.0.5 prošla hlasováním a stala se finální verzí Tapestry 5.1. Co je nového? Především na straně výkonu aplikace bylo podniknuto hodně kroků: zrychleni vykreslení složitých stránek, sloučení více statických JavaScriptových knihoven do jedné, gzipová komprese statického i dynamického obsahu atd. (blíže viz. release notes).

8. dubna 2009

Google App Engine & Java + Groovy

Skvělé partnerství!

Od mé účasti na Google Developer Day 2008 jsem si říkal, že se musím na Python podívat, abych mohl Google App Engine vyzkoušet a ejhle, už nemusím. Google App Engine podporuje Javu a Groovy!

3. března 2009

Jak jednoduše v groovy implementovat rozhraní s jednou metodou?

Vše je až trapně jednoduché (např. FilenameFilter):


{ dir, name -> name.endsWith(".txt") } as FilenameFilter

Miluju jednoduché věci.

23. února 2009

Tapestry 5.1 - co se na nás chystá?

Pro ty co nečtou Howardův blog, přináším odkaz na zajímavý příspěvek týkající se novinek, které nás čekají v oblasti výkonu v chystané verzi 5.1.

Howard chystá zrychlení vykreslování stránek, především díky optimalizaci volání metod jednotlivých stavů komponent. Zrychlení se bude týkat i vytváření instancí komponent. V profileru Howard zjistil, že volání jistých metod se neustále při vytváření komponent opakuje, takže jejich vytváření bude rozděleno na dvě fáze: fáze analýzy a fáze vytváření. To pomůže stránkám s velkým množstvím komponent.

A co zrychlení na klientu. Context assets (soubory uložené v kontextu aplikace) budou moci být poskytovány pomocí URL, které bude obsahovat verzi a tudíž budou moci být agresivně cachovány (dlouhá doba expirace). Dále Tapestry bude podporovat kompresi posílanáho obsahu (snížení objemu přenášených dat), navíc se chystá cachování zakompreseného obsahu na servru, čímž se ulehčí procesoru od opakované komprese.

A to vše téměř bez porušení zpětné kompatibility. SUPER!

iPod Touch - nechám si ho, je super!

Tak už i já, a to jsem tak dlouho "odolával". Ale vezmu celý příběh hezky popořadě.

Na konci ledna mě zase po letech přepadla myšlenka, že nahradím svům aktuální mobil nějakým tím chytrým modelem. Samozřejmě, že první volba padla na telefon s Androidem. Ale pak jsem zjistil, že jediným dostupným je model G1 od T-Mobilu. Už jsem byl skoro rozhodnut, když mi kolega povídá, že by to nedělal, že to není moc hardwareově povedený mobil a že by volil raději iPhone.

Tak jsem začal své myšlenku ubírat tímto směrem. Ovšem bál jsem se koupit si tak velký telefon, nechtěl bych takovou "plachtu" nosit všude s sebou. Takže jsem zvolil variantu zkouška. Zašel jsem na aukro a vydražil jsem nový iPod Touch.

Je to 14 dní co jsem majitelem a jaký je závěr. Začnu negativní stránkou věci. iTunes fungují pouze pod Mac Os X a Windows. A výhody? Skoro všechny audio podcasty co poslouchám, jsou na iTunes a stahují se mi přímo do iPodu automaticky (pánové z CZpodcastu, je ještě co zlepšovat). Ale co spatřuji jako opravdu skvělou věc je sledování videa. Funguje skvěle, displej je prostě super. Dále jsem se pomocí pluginu do Firefoxu naučil stahovat Flashová videa ze stránek (jako YouTube a InfoQ). Ty jsem pomocí softwaru na konvertování videa pro iPod zkonvertovat do MP4, které je přehratelné na iPodu. A už mám konečně čas se na tato videa podívat (dřív jsem neustále řešil, kdy se na ně kouknu - v práci i doma to byla škoda času), protože cestou v autobuse je to docela příjemná zábava. Druhou skvělou funkčností je prohlížení webu, které přes wifi funguje opravdu super. Především čtení novinek v Google Readeru je promakané na jedničku s hvězdičkou.

Takže můj závěr. Jednoznačně mohu doporučit, jenom velká škoda, že iTunes nejsou i pro Linux. Ale jak dopadne rozřešení mého problému s telefonem? Asi počkám na nějaký Androidí telefon, protože iPhone bych musel jailbrakenout, abych do něj dostal všechen software co bych chtěl, což u Androidu není potřeba. Dále si spíš napíšu aplikaci pro Android než pro iPhone (jako javista).

4. února 2009

XSS, XSRF - o bezpečnosti je potřeba neustále mluvit a o webu to platí dvojnásob

A já jsem si toto téma oživil pomocí prezentace Jeffa Williamse publikovanou na InfoQ: Stopping Attacks in a Web 2.0 World. Velmi povedená prezentace zaměřená na XSS a XSRF (nenechme se zmát názvem prezentace, příklady v ní uvedené se nijak nevážou pouze na Web 2.0 aplikace). Velmi podstatnou součástí prezentace jsou příklady použití těchto útoků. Čeho se dá dosáhnout pomocí XSS je až neuvěřitelné (přiznám se, že mi skoro běhal mráz po zádech).

XSRF je novější než XSS, obrana proti němu je relativně náročná (jedinou možností je posílání serverem generovaného tokenu s každým requestem na aplikaci). Zneužití je komplikovanější, protože se útočník nemůže dostat k response, kterou server poslal.

Co se XSS týče, jedná se o velmi starý druh útoku, který se neustále vyvíjí (přicházejí nové a nové nápady, jak útok zamaskovat, aby nebyl odhalen). Podíváme-li se na ha.ckers.org na příklady XSS útoků, pak u řady z nich nechápu, jak se dají použít. Např. útoky pomocí zakódování textu do Base64 vypadají opravdu nevinně. Nebezpečnost XSS je opravdu velmi vysoká. Obrana je opět relativně prostá, validace jakéhokoliv textu, který do aplikace přichází pomocí whitelistu (tj. pomocí povolených znaků). Nejedná se jenom o data z formulářů, ale i o hidden fieldy, cookies i hlavičky HTTP protokolu.

Kdo se chce dozvědět více, především ohledně popisu útoků, pak nechť použije již zmíněný ha.ckers.org, který je velmi vyčerpávající (z hlediska příkladů utoků). Druhým výborným zdrojem je owasp.org, který je (dle mého soudu) lépe přístupný z hledika podání informací jednoduchou a čitelnou formou. Popisy zde poskytují i návrhy ochrany, případně postupy pro review aplikace z pohledu odolnosti oproti útokům.

29. ledna 2009

Porovnání jQuery a Prototype (odkaz)

Právě jsem si přečetl velmi zajímavé porovnání dvou populárních JavaScriptových knihoven Prototype a jQuery od Glenna Vanderburga pod názvem Why I still prefer Prototype to jQuery.

Text je psaný velmi seriózně, žádné pocity, subjektivní dojmy, ale holá fakta, proto je, dle mého soudu, hodně cenný a zajímavý.

Co se mě a JavaScriptu týče používám jQuery, ale je pravda, že převážně na manipulaci s DOMem, což je jeho silná stránka oproti Prototype. A navíc jsem měl kliku, že jsem nepotřeboval zatím nic, co se v jQuery jeví jako problematické. Každopádně se budu muset na Prototype podívat blížeji.

Hezké počteníčko

21. ledna 2009

Moduly pro Tapestry 5

Poslouchat Java Posse se vyplácí. Dozvěděl jsem se o projektu Chenille Kit, který nabízí poměrně velké množství komponent a služeb, které jsou založené na Tapestry IoC, tj. služby jsou použitelné i mimo Tapestry.

K dispozici jsou následující moduly:


  • Access - pro řízení přístupu ke stránkám aplikace

  • Google - přístup ke službácm Google (např. geocoding)

  • Hibernate - zjednodušení práce s Hibernate

  • Hivemind - zpřístupnění komponent z Hivemindu

  • Image - jednoduché změny velikosti obrázku

  • LDAP - služba na provádění dotazů do LDAPu

  • Lucene - služba na indexování a vyhledávání pomocí Lucene

  • Mail - služba na posílání mailů založená na Commons Mail

  • Quartz - služba na plánování založená na Quartzu

  • Reports - služba pro generování reportů pomocí JasperReports

  • Scripting - služba na plánování založená na BSF

  • Tapestry - zajímavé komponenty pro Tapestry

  • Templating - templatovací služba založena na Velocity a FreeMakeru

Hodně zajímavá snůška. Ukazuje se, že Tapestry má velmi dobře udělaný design a zakomponovat do něj další služby je triviální. A množství služeb se bude jenom rozšiřovat, takže se můžeme těšit na zelené zítřky s Tapestry.

Jak selektivně podle třídy mazat objekty z Hibernate session

Moje honba za LazyInitializationException byla úspěšná (musím se přiznat, že velkým pomocníkem mi byl Eclipse Memory Analyzer, který mi pomohl zjistit, kdo se na objekt odkazuje). Problém byl v tom, že jsem chtěl vymazat objekty ze Sessiony, ale protože jsem neznal jednotlivé instance, zavolal jsem metodu clear a znova zaasocioval to co jsem si myslel, že je důležité (a na něco jsem samozřejmě zapomněl).

V první fázi opravy chyby jsem tedy zaasocioval i další objekty a vše začalo fungovat. Pak mě ovšem postihla myšlenka, proč se mám já a můj kód, který by ideálně měl být na použitém ORM nezávislý, přizpůsobovat Hibernate. Tak jsem začal pátrat v javadoc, zda neexistuje nějaká možnost, jak zjistit jaké objekty jsou v Sessioně uložené. Pak by je stačilo selektivně odstranit pomocí metody evict.

Odpověď zní: existuje. Session má metodu getStatistics, která vrací SessionStatistics. Ten nám umožní zjistit jaké kolekce a jaké entity (včetně jejich identifikátoru) máme v sessioně uloženy. Takže stačí projít tyto údaje a v klidu vymazat co potřebujeme. Napsal jsem na to takovou drobnou třídu:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.engine.CollectionKey;
import org.hibernate.engine.EntityKey;
import org.hibernate.stat.SessionStatistics;

import cz.svt.util.Tuple;
import cz.svt.util.Tuple2;

/**
 * This class is capable to print the summary of Hibernate cache content and also is able to evict objects from cache.
 */
public class HibernateCachceEvicter {
  
/** The Hibernate session. */
  
protected Session session;

  
protected static final Log log = LogFactory.getLog(HibernateCachceEvicter.class);

  
/**
    * Creates the empty evicter.
    */
  
public HibernateCachceEvicter() {
     
super();
  
}

  
/**
    * Creates the evicter and sets the transaction.
    *
@param session The session
    */
  
public HibernateCachceEvicter(Session session) {
     
super();
      setSession
(session);
  
}

  
/**
    * Sets the Hibernate session.
    *
@param session The session
    */
  
public HibernateCachceEvicter setSession(Session session) {
     
this.session = session;
      return this;
  
}

  
/**
    * Prints the content of the Hibernate cache grouped by the collections role and entity name.
    *
@return The cache content, first is entities and second collections
    */
  
public Tuple2<TreeMap<String, Counter>, TreeMap<String, Counter>> logCacheContent() {
     
if ((session != null) && (log.isInfoEnabled())) {
        
try {
           
SessionStatistics statistics = session.getStatistics();
            TreeMap<String, Counter> entities =
new TreeMap<String, Counter>();
           
for (Object key : statistics.getEntityKeys())
              
getCounter(entities, ((EntityKey) key).getEntityName()).inc();
            TreeMap<String, Counter> collections =
new TreeMap<String, Counter>();
           
for (Object key : statistics.getCollectionKeys())
              
getCounter(collections, ((CollectionKey) key).getRole()).inc();
            log.info
("The hibernate cache contains " + statistics.getEntityCount() + " entities.");
           
for (String key : entities.keySet())
              
log.info("  " + key + "=" + entities.get(key).getValue());
            log.info
("The hibernate cache contains " + statistics.getCollectionCount() + " collections.");
           
for (String key : collections.keySet())
              
log.info("  " + key + "=" + collections.get(key).getValue());
           
return Tuple.newTuple(entities, collections);
        
} catch (HibernateException e) {
           
log.warn("Exception during walking through Hibernate cache to log its content", e);
        
}
      }
     
return Tuple.newTuple(null, null);
  
}

  
/**
    * Returns the counter from the map. If there is no such counter yet, creates new one and put it into map.
    *
@param map The map
    *
@param key The key
    *
@return The counter
    */
  
protected Counter getCounter(TreeMap<String, Counter> map, String key) {
     
Counter counter = map.get(key);
     
if (counter == null) map.put(key, counter = new Counter());
     
return counter;
  
}

  
/**
    * Evicts the object of the specified classes from Hibernate cache.
    *
@param classNames The array of the classNames to evict from cache
    */
  
public void evictFromCache(String... classNames) {
     
evictFromCache(new TreeSet<String>(Arrays.asList(classNames)));
  
}

  
/**
    * Evicts the object of the specified classes from Hibernate cache.
    *
@param classNames The sorted set of the classNames to evict from cache
    */
  
public void evictFromCache(SortedSet<String> classNames) {
     
if (session == null) return;
      log.info
("Cache before evicting:");
      TreeMap<String, Counter> entities = logCacheContent
().getV1();
     
try {
        
int entitiesToEvictCount = 0;
        
for (String key : entities.keySet())
           
if (classNames.contains(key)) entitiesToEvictCount += entities.get(key).getValue();
         List<Object> entitiesToEvict =
new ArrayList<Object>(entitiesToEvictCount);
        
for (Object key : session.getStatistics().getEntityKeys()) {
           
EntityKey eKey = (EntityKey) key;
           
if (classNames.contains(eKey.getEntityName()))
             
entitiesToEvict.add(session.load(eKey.getEntityName(), eKey.getIdentifier()));
        
}
        
for (Object entityToEvict : entitiesToEvict)
           
session.evict(entityToEvict);
         log.warn
("Evicted " + entitiesToEvictCount + "entities.");
     
} catch (HibernateException e) {
        
log.warn("Exception during evicting objects from cache", e);
     
}
     
log.info("Cache after evicting:");
      logCacheContent
();
  
}

  
/**
    * This class is counter.
    */
  
protected static class Counter {
     
/** The value of the counter. */
     
int value;

     
/**
       * Creates the counter a resets the value.
       */
     
public Counter() {
        
super();
        
value = 0;
     
}

     
/**
       * Increments the value of the counter.
       */
     
public void inc() {
        
value++;
     
}

     
/**
       * Returns the value.
       *
@return The actual counter value
       */
     
public int getValue() {
        
return value;
     
}

   }

}


Zajímavé je metoda evictFromCache, která provádí mazání objektů ze sessiony. Nejprve si pomocí logCacheContent vypíšu obsah cache (seskupený podle tříd) před mazáním. Pak si zjistím kolik instancí budu mazat (podle výstupu z vypsání obsahu sessiony) a vytvořím patřičně veliké pole. Následuje procházení sessiony a pokud narazím na třídu, kterou chci vymazat, podle identifikátoru instance ji natáhnu metodou load a vložím do pole. Teprve poté je mažu (byl jsem línej, zkoušet, zda by se to nějak nepralo, kdybych zároveň procházel obsah sessiony a zároveň z ní mazal). Na závěr si jenom pro porovnání udělám výpis obsahu po mazání.

Protože nechci mít v kódu odkaz na takovou ošklivou Hibernatí záležitost, udělal jsem si jednoduchý aspect, který mi na požadovaná místa dotlačí vymazání instancí patřičných tříd.

Předpokládám, že to již někdo řešil, ale pokud to někomu pomůže, pak jen berte inspiraci.

PS. Třídu Counter jsem v příkladu nechal jenom pro úplnost.

17. ledna 2009

Parametrizované testy v TestNG

Jak už jsem psal dříve, jako testovací framework používáme TestNG. A protože jsem se konečně dostal ke knize Next Generation Java Testing: TestNG and Advanced Concepts od autora TestNG, Cédrica Beusta, mohu se s vámi podělit o novinky, které jsem načerpal (každopádně ta kniha stojí za přečtení, zabývá se totiž nejen unitovým testováním).

Jak jsem již naznačil v minulém textu, hlavní výhodou, kterou vidím, jsou data providery. Proč se tedy tento článek nejmenuje data providery v TestNG? Protože jsem se dozvěděl i o jiné možnosti parametrizace testů v TestNG. Jedná se o factories (český název továrny mi nepřijde tak jasný), které jsou podobné parametrům v jUnitu 4. Takže jak to funguje (příklad je lepší než 1000 slov, opráším příklad z minulého článku):


public class SquareTest {

private Calculator calculator = new Calculator();
private int param;
private int result;

@Factory
public static Object[] create() {
return new Object[] { new SquareTest(0, 0),
new SquareTest(1, 1), new SquareTest(2, 4), new SquareTest(4, 16) };
}

public SquareTest(int param, int result) {
this.param = param;
this.result = result;
}

@Test
public void square() {
calculator.square(param);
assertEquals(result, calculator.getResult());
}

}
Na první pohled je rozdíl oproti jUnit velmi malý (pomineme-li míň anotací a jejich jiná jména). Nevracíme paramtery, ale již vytvořené instance tříd. Jaká je v tom výhoda? Factory metoda může vracet instance jakýchkoliv tříd, nejenom té ve které je definovaná. Tyto třídy mohou obsahovat nejenom testovací metody (označené anotací @Test) a i factory metody. Je to o hodně flexibilnější.

A proč TestNG podporuje jak factory metody tak data providery, když de facto realizují totéž? Protože pokud parametry využijeme u většiny testovacích metod v třídě, je pohodlnější je předat pomocí konstruktoru z factory metody. Data providery se hodí, pokud potřebujeme více skupin dat pro různé testovací metody. A samozřejmě nic nám nebrání obě věci kombinovat.

Na závěr jsem si nechal dotažení konceptu data provideru k dokonalosti. Co když chceme zavolat testovací metodu hodně krát, opravdu hodně hodně moc krát. Pak asi není ideální vytvořit všechny parametry dopředu, vložit je do dvourozměrného pole a vrátit z data provideru, to zblajzne moc paměti. Jak tedy na to? Data provider bude místo Object[][] vracet Iterator, jehož metoda next() vrátí jednorozměrné pole s hodnotami parametrů.

Pokud jsem vás stále nepřesvědčil, že TestNG stojí za zkoušku, pak to jistě zkusím zase příště.

14. ledna 2009

ORM mých snů - iBatis 3

Všechny velké zajímavé projekty aplikací, na kterých jsem se podílel jako ORM používali Hibernate. Přiznám se, že jsem byl tímto frameworkem zpočátku nadšen. Pak moje nadšení trochu ochablo, ale verze 3 zase přinesla vylepšení, která jsem kvitoval (především z hlediska mapování).

Postupem času, ale čím dál tím víc cítím, že Hibernate (a v podstatě jakýkoliv JPA framework) není to pravé ořechové. Proč?

Nelíbí se mi, že je velmi těžké se z DataTypu dostat na connection, což je potřeba např. pro uložení XML do DB přes datový typ SQLXML, který je nutno vytvářet pomocí metody createSQLXML objectu Connection. O tom, že tuto instanci musím po provedení dotazu zase uvolnit pomocí její metody free ani nemluvě.

Dále se mi nelíbí, že nemám plně pod kontrolou SQL dotazy (jsou generovány z HQL), tj. případná optimalizace dotazů není úplně triviální. Samozřejmě je možné si vyžádat od Hibernate spojení a provést dotaz ručně, ale metoda connection objektu Session je deprecated, tak nevím jak to bude v Hibernate 4.

Šíleně mě začíná obtěžovat cache natažených objektů, Session. Pokud provádím nějaké batchové operace, které manipulují s velkým množstvím objektů (desetisíce), pak je problém jak to celé výkonově optimalizovat. Je nutné průběžně dělat commit, session vyclearovat a znova zaasociovat všechny objekty, které budu potřebovat. Nejen je to pracné, ale velmi často dohledávám původce nějaké LazyInitializationException.

Ale co jiného, co lepšího. Vždy jsem pokukoval po iBatisu. Zkoušel jsem jej, ale něco mi vždy řeklo, že to nějak s Hibernate zmáknu (přeci jenom ten framework znám a málo co mě při práci s ním překvapí). Ale návrh na verzi 3, který se mi dostal pod ruku mě nadchl. Proč?

Šíleně se mi zalíbila myšlenka definice rozhraní, které se zove Mapper, ale de facto je to DAO. Implementaci vytvoří iBatis sám. Např:

public interface EmployeeMapper {
Employee getEmployee (int employeeId);
List listAllEmployees();
}
Navíc se případné anotace, jak mapovat budou umísťovat do tohoto rozhraní a ne do modelu (jsem absolutním nepřítelem anotování modelu anotacemi pro DAO vrstvu).

Dále se mi zalíbila představa generování dynamických SQL dotazů pomocí DSL v Javě. Obávám se, že to není správný přístup. SQL by mělo být mimo aplikaci, takže v XML. Ale myšlenka je to jistě zajímavá.

Teď už nezbývá nic jiného než se těšit jak to celé dopadne.

13. ledna 2009

Optimalizace výkonu databáze DB2 - db2top utility

Jak optimalizovat výkon databáze? Těžko. Jak optimalizovat výkon databáze DB2? Také těžko. Nebo ne?

Tak těď už to neplatí, alespoň si to myslím. Ještě jsem nově objevenou utilitku db2top nezkoušel v případě nouze, ale podle popisu a prvního vyzkoušení je hodně mocná.

Dříve jsem k monitoringu databáze využíval pár utilit operačního systému (v Linuxu např. ps, iostat) a pak řadu nástrojů databáze (např. monitory, snapshoty). db2top integruje všechny tyto schopnosti do jednoho programu.

Utilitky umí pracovat nejenom v interaktivním režimu, ale i v režimu záznamu a následného vyhodnocení. Dokonce je možné hodnoty exportovat do csv a následně importovat např. do Excelu (já používám OpenOffice) a vytvářet grafy.

Koho jsem nalákal rozhodně doporučuji přečíst si DB2 problem determination using db2top utility.

12. ledna 2009

Java 7 - máme se na co těšit

Máme zde nový rok 2009 a první téma v novém roce nemůže patřit ničemu jinému než nově přicházející Javě - tj. Javě 7. Dostalo se ke mně pár zajímavých informací, které oblažily mé srdíčko.

Za prvé: Java Modularity mimo jiné nahradí classpath (také proto, že nepodporuje více verzí jedné knihovny). Konečně se zbavíme classpath: skutečnosti, které přinášela jenom starosti a žádné slasti. Každopádně, kdo kdy řešil nějaký problém spojený classpath, především na serveru je to lahůdka, pak jistě zaplesá.

Za druhé: closures se odkládají. Další moudré rozhodnutí (představení closures vyvolalo velké množství debat). Vzniklo více návrhů a není jasné, který je nejlepší. Proto oddálení uvedení této featurky je rozumné, uvidíme zda je budeme v budoucnu potřebovat (v Javě 7 budou změny podporující dynamické jazyky, jejichž existence možná tuto potřebu zašlape do země).

Věřím, že vás tyto informace potěší alespoň tak, jak potěšili mě. A když to tak náhodnou zase nedopadne, pak se nebojme, chleba kvůli tomu dražší nebude. Rozhodně vám přeji vše nej... do nového roku.