2009. június 21., vasárnap

Rémálom a JPA utcában

Ezt a problémát próbáltam felvázolni Tvik-nek a eclipse democamp elött, de azt hiszem túl alacsonyan volt akkor még az agytevékenységem hogy sikerrel járjak. Meg talán viccnek túl hosszú ez az egész.

Szóval egyik nap áthívott az egyik srác egy másik projecttől, hogy segítsek elindulni a JPA persistence tesztek fejlesztésével a projecthez. Eléggé félfüllel követtem a project életét, szóval igazából csak nagyon kevés ötletem van arról hogy mi miért jutott oda ahol van az architektúra fejlődése során, de ehhez a feladathoz ezen a ponton rendelkezésünkre állt:
  • Netbeans-ben kigenerált JPA controller kód (ahogy én értettem ez ugyanaz mint a DAO pattern)
  • persistence.xml kitöltve a production környezethez (JNDI datasource inside)
  • Azt hiszem szintén generált perzisztens osztályok
  • Maven-es build
Feladat: A kódot teszteljük úgy, hogy használjuk fel hozzá kigenerált kódot is. Szóval sima DAO tesztelés.
A generált kód nemnyúkapiszka tárgya.

Első nekifutásra egyszerűen azt javasoltam a kollégámnak, hogy egyszerűen dobjon be egy új persistence.xml-t a teszteknek, és használja azt a tesztekből. Biztosan lehet valahogy, mert a spring és a unitils is meg tudja mondani a persistence layernek, hogy melyik persistence.xml-t használjuk fel. Mondani könnyű persze.
Párhuzamosan én is elkezdtem keresni, hogy vajon a JPA-nak hogy is kell ezt megmondani, de látványosan sehol semmit nem találtam, csak akkor nem foglalkoztam vele tovább mert persze én másik projecten vagyok és azt kell hegeszteni.

Második nekifutás: Be kellett látnunk hogy kicsit alaposabban kell foglalkozzunk a JPA kérdéssel ahhoz hogy a teszteket élesbe állítsuk. Összeültünk egy eredetileg rövidre tervezett pair programming sessionre és nekiláttunk átnézni a teszt menetet. Hamarosan kiderült hogy a spring valami nagyon hosszú tréfát használ arra, hogy a persistence.xml-t más néven keresse meg és ezt nem akartuk átmásolni tőlük, inkáb az volt az alapötlet, hogy a spring-gel inicializáltatunk egy JPA EntityManager-t és azt odaadjuk az érintett DAO objektumoknak. A JPA init ment is mint a karikacsapás, de amikor odaértünk, hogy beletömjük a DAO objektumokba az eredményt, akkor elmúlt a jókedv: a generált osztályok konstruktora inicializálta és private mezőként tartotta az EntityManagert. Azaz szépen udvariasan nem tudunk hozzányúlni. Ráadásul a kódba bele van generálva a persistence unit neve is. Ezt az egészet úgy látszik úgy találták ki, hogy a végleges környezeten kívül máshol ne lehessen futtatni. Azaz nem tesztelő-barát.

Harmadik nekifutás: Override-oljuk a konstruktort. Ja, de az ősosztály default konstruktora akkor is meghívódik. Rövid roham, fejvesztett menekülés.

Negyedik nekifutás: Jó, akkor használjuk a production-ra szánt persistence.xml-t és mielött mindezt felstartoljuk, dobjunk össze egy JNDI contextet a teszt DataSource objektummal. Ez tűnt vagy fél órán keresztül a nagy és tökéletes ördögi tervnek, én teljesen hittem a sikerben amíg a tesztbe drótoztam a spring-test csomag JNDI mock csomagját és startnál elmondtam a varázsigét is, hogy "Fuss QA!!!".... és nem működött. Azért nem, mert a spring JNDI mock csomaga pár metódust nem implementált a JNDI standardból, amit a JPA implementáció akkor is meg akart hívni. Na igen, régen használtam a spring JNDI implementációját és fogalmam nem volt a korlátairól.

Ötödik nekifutás: diplomáciai tárgyalások az ellenséggel. Ekkor kitaláltuk, hogy akkor a build eszközzel etetjük meg azt, amit a szőrös apikkal nem tudtunk. Nos a maven esetében ha két ugyanolyan nevű resource van a src/test/resources és src/main/resources alatt, akkor úgy tűnik kb véletlenszerűen fogja egyiket vagy másikat megtalálni. Nincs más választás, külön profile-ba kell tenni az éleset és a tesztet.
Ant-tal pofátlanul egyszerű lenne a junitnak odalökni egy másik classloadert. Rettenetes mellékhatások: nem futhat egy buildben a deploy és a teszt. A CI konfiguráció se lesz persze egyszerűbb.

Tanulságok:
  1. Rájöttem hogy eddig soha nem próbáltam spring nélkül JPA apit használni
  2. És valószinűleg ez az oka annak, hogy nem utáltam meg már az elején a JPA-t. A JEE architektúra csak kicsit lett kevésbé toldott-foldott mint a jó öreg EJB 2.1 időkben.
  3. A generált kódoktól nagyon udvariatlan dolog, hogy ők akarják inicializálni a persistencemanagert és erről nem lehet lebeszélni őket.
  4. A JPA-ban már az elején nem tetszett, hogy a META-INF/persistence.xml-hez ennyire ragaszkodik, de nem gondoltam, hogy nincs is szabványos megoldás ennek a felülbírálására.
  5. A netbeans generált kódjaitól ments meg uram mintket!
  6. Amikor ilyen kavar a kód, a maven szigorú struktúrája is akadállyá válik.