2014. április 23., szerda

groovy.lang.Tuple kicsit közelebbről

Belefutottam egy aranyos Groovy osztályba, a Tuple-be. Ha jobban megvizsgáljuk az öröklési láncot láthatjuk, hogy a Tuple lényegében egy AbstractList implementáció, és arra való, hogy összetartozó értékeket tároljunk benne. Példányosításkor meghatározzuk a tartalmát, amit a későbbiekben nem lehet módosítani. Kisértetiesen hasonlít a Collections.unmodifiableList()-hez, de a következő példa jól szemlélteti a Tuple eleganciáját:
Collections.UnmodifiableRandomAccessList foo() { Collections.unmodifiableList([]) }
Ezt ne próbáljátok meg lefordítani, nem fog sikerülni. A probléma az, hogy a UnmodifiableRandomAccessList a Collections belső osztája, ha pedig List-re cseréljük a visszatérési érték típusát, akkor a hívó fél részére nem lesz egyértelmű, hogy a lista nem módosítható, vagy ne adj isten egy verzióváltás során lesz UnmodifiableRandomAccessList, és a működő kód megy tönkre (sose láttunk még csak hasonlót sem). Itt jön az elegancia:
Tuple foo() { new Tuple() }
Ez az egyszerű példa megmutatja mi mindenre alkalmas többek között a Tuple, ha a munkatársainkat szeretnénk kicsit megviccelni (dynamic type rulz)
def x = (a,b,c) = { x -> new Tuple(x*10,"${x*20}") } << 5
println "$a,$b,$c,$x"

2014. január 18., szombat

Ping-conf Day 2

A második nap a Ping-conferencián hozta az első nap színvonalát. Yevgeniy Brikman izgalmas demóval egybekötött előadása mindenkit lázba hozott korán reggel. Prezentációját azzal kezdte, hogy elmesélte, hogy másfél éve használják a Play Frameworköt LinkedInnél több mint 60 projekthez nagy sikerrel. Megnevezett két problémás területet tapasztalataik alapján:
  • Nehezen kezelhető komplexitás (szerintem ez nem feltétlen csak Playben jött volna elő az ő esetükben)
  • Borzalmas teljesítmény
Az első ponttal kapcsolatban megmutatta, hogy a LinkedIn oldalán rengeteg apró kis tartalmi rész van, amik ráadásul eltérnek a különböző felhasználó típusonként (újonc, prémium, etc), ezért nem lehetséges egyetlen kontrollerbe tenni a logikát (hecc kedvéért kipróbálták, és a fordító elszállt a scala fájl méretére hivatkozva). A megoldás, amit használnak, hogy minden egyes kis doboz önállóan is él, tehát van saját kontrollere, nézete, meg minden ami kell. Nagy előnye a módszernek, hogy nagyon könnyen tesztelhető, továbbá nagyon könnyen skálázható vízszintesen, ugyanis az egyes modulok szétoszthatóak több szerver között. A publikus oldalak kontrollereiben pedig egyszerűen legenerálják az egyes részeket, és összefűzik a kiszolgálandó HTMLbe. A technika hátulütője, hogy a statikus erőforrásokat a kontrollerben kell kezelni (szerk,. megj. illetve nem lehet ezen erőforrások tömörítését, egybefűzését valamilyen pluginnel leegyszerűsíteni).
Ezt a fragmentációs technikát választva az alábbiak igényelnek külön odafigyelést:
  • Cookiek esetén szükségük van egy metódusra, ami a darabkák headerjeiből összefűzte a Cookie bejegyzéseket
  • Statikus erőforrások beillesztésére szintén a headerbe kell tenni  X.CSS ill. X-JS bejegyzéseket, amit aztán összefűznek, deduplikálják, és a HTML headbe tesznek (erről később, hogy miért pont oda)
  • Hibakezeléshez a legfelső réteg kiolvassa a darabkák státsz-kódját
Miután végigmentünk a komplexitás témakörén, áttértünk a teljesítmény problémákra. Első lépésben átalakította az alkalmazást, hogy a hagyományos String alapú HTML generálás helyett streamelje a kimenetet. A Scala nyelvben vannak natív eszközök, mindössze egy custom render templatet kellett bekötni a Playbe (az alapértelmezett StringBuildert használ), és az Enumerator, Future párossal megoldotta, hogy a szerver az előállított HTMLt azonnal küldte is a kliensnek. Akit érdekel részletesebben a dolog itt tud utánaolvasni. Ez azért növeli a teljesítményt, mert a HTML headben elhelyezett erőforrások letöltésével nem kell a kliensnek megvárnia, amíg minden kis darabka összeáll. Következő trükk, amivel tovább fokozta a teljesítmény és a felhasználói élményt eléggé leleményes. A darabkák kimenetét a HTML bodyn kívülre, egy script type="text/html-stream" elembe tette, majd JavaScripttel a helyükre illesztette, amikor végzett velük a szerver. A módszer előnye, hogy nincs szükség darabonként egy AJAX kérésre, így tehermentesíthető a szerver, mégis a felhasználó azonnal kap választ a kérésére. Amire érdemes odafigyelni, hogy a hirtelen a helyükre kerülő elemek miatt ugrálhat az oldal, megzavarva ezzel az éppen kattintani vágyó felhasználót, valamint a headerek már a kérés elején kiküldésre kerülnek, ezért azokat a kérés további részében nem lehet változtatni. Nehezebb az oldalt tesztelni, és SEO problémák is felmerülhetnek. A demóalkalmazás egyébként elérhető githubon.

A következő prezentációt James Proper tartotta, aki szintén a teljesítmény-optimalizációt választotta témájául. Az első dolog, amit szép teljesítmény-tesztekkel bemutatott, hogy az aszinkron nem egyenlő a gyorsasággal, sőt! Egy egyszerű példán keresztül bebizonyította, hogy az aszinkron kérés mennyivel tud lassabb lenni a sok context switch miatt, amik elveszik a drága processzoridőt. Folytatásként több módszert is bemutatott, amivel növelhető a teljesítmény, és elkerülhető a rengeteg context switch.
  • Az első javaslata az volt, hogy használjuk a Scala beépített ExecutionContextjét, ami a Java Fork/Join osztályaira épül. Bővebben
  • Második lehetőségként azt ajánlotta, hogy ne használjuk az első pontban említett contextet, hanem váltsunk ImmediateExecutionContextre
Általános jó tanácsként említette, hogy sose használjunk blokkolt erőforrásokat ExecutionContextek használatakor, ha mégis szükségünk van rá, akkor inkább konfiguráljunk menetirányítókat típusonként (na jó ezt senki sem érteni :) "Configure dispatcher per type"). Mivel az Akka tud ilyet, és a Playben van beépített Akka, adja magát, hogy azt érdemes használni.
Kiemelte azt is, hogy nagyon nagyon nagyon nagy weboldalaknál lehet teljesítmény-növekedést elérni, ha a routingot több különálló fájlba tesszük, és egy custom router megírásával csak a megfelelő fájlt dolgozzuk fel, amikor a kérés beérkezik. A gyorsulás oka, hogy a  Play regexpeket illeszt az URI-re, és ha több száz illesztést kell csinálnia, mire a route fájl végén megtalálja a keresett bejegyzést, az felesleges processzor-terhelést jelent. A futtatott benchmarkoknál pár százalékos teljesítmény-növekedés volt mérhető.


A teljesítmény optimalizációs témaköröket elhagyva a következő előadást ismét  Julien Tournay és Pascal Voitot tartotta, és témája a Scalaz-Stream volt. A prezentáció lényege pár mondatban összegfoglalva annyi, hogy miként lehet streamek fogadására lecserélni a jelenleg is működő Iteratee megoldást Playben erre, az egyébként még fejlesztés alatt álló, megoldásra. A prezentációban egyetemi előadásokra emlékeztető részletességgel  mesélték el többek között, hogy miért is jó ez az újfajta megközelítés, hogy milyen problémákba ütközhetünk a HTTP, és a WebSocket eltérősége miatt, stb.


Ebédszünet előtt még Adam Evans és Asher Glynn tartott egy (számomra nem annyira érdekes) előadást arról, hogy milyen tapasztalataik voltak, amikor a BBC gyerekeknek szóló PHP-s megoldását lecserélték Play frameworkre. Architekturálisan volt egy PHP frontendjük, ami a BBC Java-s backendjéhez fordult adatokért. Ami miatt a Playre esett a választásuk:
  • Mert Scala, ezt nem is fejtették ki bővebben
  • Full stack web-framework
  • Reaktív, könnyű benne non blocking szolgáltatásokat készíteni és hívni
  • Typed safe template rendszer eléggé awesome
  • Sok vállalat használja, és zizeg az egész
Miután kiválasztották a keretrendszert csináltak egy pilot projektet, ami nagyon jól sikerült, a PHP fejlesztőknek könnyű volt átszokni Scalára, és mindenki boldog.


A sponsor pitch után következő előadás igazán érdekes volt. Grant Klopper a The Guardian hírportál frontendjét kiszolgáló alkalmazásról beszélt, milyen problémákkal kell szembenézniük, milyen megoldásaik vannak, stb. Elmesélte, hogy naponta három Budapestnyi ember látogatja meg a weboldalukat, átlagosan mindegyik megnéz három oldalt. 900 kérést kell kiszolgálniuk minden másodpercben. A rendszer teljesen nyílt forrású, és elérhető a githubon, sőt nem csak elérhető, hanem konkrétan onnan buildelik a live rendszert. A teljes frontend letölthető egyetlen futtatható jar fájlként, és elindítható a java -jar frontend-artifact.jar paranccsal. A rengeteg kérés miatt mindent cachelnek, és a rendszer garantálja, hogy nem töltődik be sohasem ugyanaz a tartalom kétszer, a második kérés mindenféleképpen cache hit lesz. Beszélt továbbá még a deployment folyamatukról is, ami abból áll, hogy elindítanak három új verziót, regisztrálják őket a load balancerbe, majd lekapcsolják a régi hármat. Előadás után külön odamentem hozzá, és rákérdeztem miként kezelik azt a szituációt, amikor az összes node fut, de valami kódváltozás miatt a cachelt elem is megváltozik, és a régi verzióknak még a régit kell kiszolgálniuk, az újaknak pedig már az újakat. Dolgozom elosztott szinkron cachel, és ez nálunk bizony okoz problémát és fejfájást, éppen ezért mi kikapcsoljuk a cachet egészen addig, amíg minden nodeon az új verziójú szoftver nem fut. A válasz egész egyszerűen annyi volt, hogy sehogy. Azt mondta, hogy olyan rövid ideig fordulhat elő ez az eset, és a magas kérésszám miatt a statisztikában meg sem jeleni az a pár hibás kérés.


Utolsó előtti előadást Tobias Neef tartotta a kontrollerek absztraktálásának lehetőségeiről a Playben, hogy a lehető legjobban elkerülhessük a kódismétlést. Pár pontban összeszedve a lényeg:

A konferencia Johan Andrén előadásával zárult, aki a különböző aszinkron lehetőségekről beszélt Scala és Java platformokon. Először a problémára világított rá Johan, miszerint az általunk írt kód a kiszolgálási idő nagyon kicsi részében van végrehajtási fázisban, és az idő nagy részét különböző erőforrásokra való várakozással tölti. Tegyük fel, hogy ha van 20 adatbázis, és 200 kiszolgáló szálunk, akkor a 200 + 1-edik adatbázist nem használó szál nem lesz kiszolgálva. A problémára háromféle megoldást mutatott be.
Az első, használjuk bátran a Future és Promise osztályokat (részletes információt itt találsz). Hátrányának azt nevezte meg, hogy amikor több rétegen keresztül dobáljuk a Future osztályokat, a rétegek között állandó jelleggel konvertálni kell azokat. Például egyik szerviz visszatér a userek listájával, de a hívónak JSON listát kell visszaadni, amit az őt hívónak HTML kóddá kell alakítani, stb. stb. Javaslata szerint az alábbi esetekben érdemes ezt a megoldás választanunk:
  • Amikor más szolgáltatásokkal kommunikálunk
  • Párhuzamos végrehajtásra, amikor a szálak teljesen elkülöníthetők egymástól
  • Egyszerű háttérszolgáltatások implementálásakor
Utóbbi esetre nem tenném a nyakam, Tomcat esetén mivel a kiszolgáló szál referenciát őriz a Future objektumra szépen bevárja annak végeredményét, szóval én nem javaslom ezt a kombinációt.
Következő lehetőségként az Akka Actorsokat vetette fel, segítségével esemény vezérelt programozást tudunk megvalósítani. Az eljárás lényege, hogy van egy bemenő sor, amibe be tudják a kiszolgáló-szálak küldeni a kéréseket, és a rendszer egy szálon szépen sorban végrehajtja azokat. A technika nem sebességéről híres, blokkolja a többi futó szálat, de cserébe jól skálázható. Mikor használjuk:
  • Amikor állapotokra van szükségünk
  • Adat streamelés
  • Esemény vezérelt programozásra
  • Háttérfolyamatok végzésére
Utolsó lehetősségként a Iteratee-k használatát mutatta be. Sajnos ennek nincs Java-s megfelelője, a Chunks API tud valami hasonlót, de képességei messze elmaradnak a Scala natív megoldásától. A dolog lényege, hogy pici szeletekben adható át a feldolgozásnak az adat (Enumerators), és a rendszer lényegében reagál az adatra, és várja a következő darabkát.
  • Enumerator[A] -> Iteratee[B, R]
  • Enumerator[A] -> Enumeratee[A, B] -> Iteratee[C, R]
Lényeges külömbség a hagyományos feldolgozással szemben, hogy az első hibás darabka esetén már lehet kezelni a hibát, nem kell a teljes adatfolyamot megvárni.
Mikor használjuk:
  • Streamel adatok esetén

A konferencia végére igencsak megfogyatkozott a létszám, láthatóan mindenki kellőképpen elfáradt. Meg kell hagyni, hogy elég tartalmasak voltak az előadások, szóval le a kalappal a szervezők előtt, nem bízták a véletlenre a mentális zombiságunkat. Gratula ezúton is.

2014. január 16., csütörtök

Ping-conf Day 1

Volt szerencsém a napomat a Ping-conferencián tölteni. A 2 napos rendezvény fő témája a Play Framework, mely a legelterjedtebb, hanem az egyetlen Scala nyelvre épülő webes keretrendszer. A rendezvény bár nem volt olcsó, mégis elég nagy tömeget mozgatott meg, hogy őszinte legyek sokkal kevesebb magyar résztvevőre számítottam. A konferencia, mely minden igényt kielégítő módon volt megszervezve, népszerűsége láttán elgondolkoztam, hogy a JUMon is több Scalás előadással készülhetnénk, mert az itt látottak alapján komoly érdeklődés van a Scála iránt Magyarországon.


Az első előadást Sadek Drobi tartotta, és a funkcionális programozás alapjairól beszélt, betekintést nyújtva példákon keresztül a composability szépségeibe. Röviden: minden funkciónak kötelezően van visszatérési értéke, és ezekkel a funkciókkal könnyedén lehet hívási láncokat alkotni. Az előadás végén elhangzott egy kérdés, miszerint mennyivel jobb megoldás ez, mint a Java 8-cal érkező Lambda kifejezések? A válasz tömör volt és egyszerű, mivel a Java hemzseg a void metódusoktól, igazán sosem lesz benne az az erő, amit a Scala nyújtani képes.


A második prezentációt Matt Hamer tartotta, és témának a többszörös JVM architektúrákat választotta. Számomra ez volt az egyik legérdekesebb előadás. Matt elmesélte, hogy az alkalmazás, amit fejlesztenek több részre van felosztva, egy prezentációs rétegre, ahol Play biztosítja a kiszolgálást, és n+1 business rétegre -szintén Play-, ami értelem szerűen backendként funkcionál a prezentációs rétegnek, és egyéb klienseknek. A szeparáció elsődleges okainak a következőket nevezte meg:
  • Könnyebb Load balancing
  • Izoláció a rétegek között
  • Különböző gyakorisággal történik a deployment
Miután ismertette az architektúrát, Matt részletesen kifejtette, a két réteg közötti kommunikációs lehetőségeket. Először számba vette a JSON küldözgetést, de ez két JVM között nem a legideálisabb választás, ezért áttért Java közelibb megoldásokra.
  • java.io.Serialize, mint az köztudott érzékeny a verziózásra, egyáltalán nem rugalmas
  • Protocol Buffer, sokkal jobb választás, de a konfigurációs állományokat állandóan karba kell tartani
  • Akka custom serialization, ez a megoldás bizonyult a legjobbnak, orvosolja a kompatibilitási problémákat, nem kell konfigurációs állományokat napra készen tartani, és nem utolsó sorban a Playben van beépített Akka

Egy rövid szünetet követően a harmadik téma következett "Build reactive apps on the JVM" címmel. Christopher Hunt saját fejlesztésű keretrendszerüket mutatta be. Megmondom őszintén nem tudtam teljesen elképzelni a felépítését, de nagy vonalakban egy Play frameworkre épülő, Rhino enginet használó JavaScript, igen jól olvastad JavaScript webframeworkről volt szó. A rendszerhez már most is elérhető néhány plugin (CoffeeScript, JS Hint, Less, Require.js, Jasmine). Ez jutott eszembe.

A következő, szám szerint a negyedik prezentáció Yann Simon nevéhez fűződik, témája pedig a Cake Patter In Scala gyakorlati bemutatása volt. A példa alkalmazás 3 részre volt tagolva, 1 frontend, és 2 backend modulból állt. Az első verzióban a frontend modulba be voltak égetve a backend modul szervizei, és a hívások. A megoldással a legnagyobb probléma, hogy a teszt-piramis (unit, component, integration) valójában nem is piramis alakú, hiszen kis számú unit teszten kívül lényegében mindent az integration teszt szintjén kell vizsgálni. A probléma felismerését követően elkezdte átalakítani az egyes rétegeket, amíg el nem jutott arra a szintre, hogy mindent a Scala Component based Dependency Injectionnel oldott meg. Én valahol a folyamat közepén vesztettem el a fonalat, mikor elszaporodtak a trait x extends y with z, meg class a extends b self: c kifejezések. Mindentől függetlenül 2 fontos dolgot leszűrtem:
  • Ez a megoldás annyival jobb más DI megoldásoknál (Spring, Guice), hogy már fordítási időben kiderül ha valami nem stimmel, és nem csak futás időben jönnek elő a problémák
  • Nem érdemes kimaxolni ezt a megoldást, mert az egyszerű példa esetén is 20 trait "osztályra" volt szükség, inkább az elégséges szinten érdemes megállni

Az ebéd szünetet követően Julien Tournay és Pascal Voitot tartott egy érdekes és egyben vicces szösszenetet "Typesafing your blobs" címmel. Hogy pontosan elénk tárhassák a problémát egészen az ősrobbanásig mentek vissza, majd az utazás a dinoszauruszok korán át egészen napjainkig tartott. Történelmi kalandozásuk során megálltak egy pillanatra a modern korba, amikor az emberek kísérletet tettek egy egységes protokoll kidolgozására, hogy a különböző informatikai rendszerek tudjanak egymással kommunikálni, és megszületett a SOAP. Folytatásként arról beszéltek, hogy a technológia fejlődésével egyre inkább ki vannak téve a webes szolgáltatások egymásnak és pontosan ezért egyre fontosabb, hogy a külső forrásokból érkező adatokat rugalmasan tudjuk kezelni és ellenőrizni. Példának hozták fel, hogy 2 éve a WebSoket még csak Sky-fi volt, ma pedig már szolgáltatások épülnek rá, utalva ezzel, hogy milyen rohamosan fejlődik a világ. A fent említett dolgok vezették rá a srácokat, hogy egy teljesen új, a régivel kompatibilis, de a mai kor elvárásainak megfelelő (vagy inkább jövőbe mutató) validációs API-t fejlesztettek a Playhez. Természetesen teljesen funkcionális szemléletben készült a kód, kihasználja a Scala macrokban rejlő lehetőségeket, és a case classoknak valamint az implicit deklarációnak köszönhetően marshallerek, és konfigurációk nélkül képes egy JSON stinget Scala osztállyá alakítani. Az API másik erőssége, hogy POST-olt adatok ellenőrzésére is alkalmas kvázi módosítás nélkül. A fejlesztés még nagyban folyik, nem production ready a cucc, de akit érdekel itt megtalálja a projektet.

Az utolsó előtti előadást Matthias Nehlsen tartotta, és reaktív alkalmazás-fejlesztésről beszélt Play, Scala.js, és ReactJs segítségével. Engem főleg a ReactJs fogott meg, régóta keresek valami hasonló megoldást, de az AngularJs, Ember.js, KnockOut.js eddig nem nagyon jött be. A ReactJs-nek nagyon rövid a tanulási görbéje, és a teljesítménye is kiemelkedő, köszönhető annak, hogy a valós DOM fa mellett van belül egy virtuális DOM, és a DOM manipuláció után csak a diff-et szinkronizálja a valós fával. Érdekes volt látni, hogy milyen egyszerűen kötötte össze a nézeteket renderelő ReactJst a Scalaból generált JavaScript kódot, és a Scala.js-t a Play-el Mathias, lényegében minden Scalában volt megírva, volt pár action binding JavaScriptben, és némi HTML-nek látszó ReactJs template.

Utoljára maradt Nicolas Martignole esettanulmánya, melyben két általa fejlesztett projekt sikertörténetét tárta a nagyérdemű elé. Webes rétegnek Play2-t választotta, adattárolásra és cachelésre pedig Redist. Láthatóan ő boldog volt a megoldással :).

Ennyi történt az első napon, igyekszem holnap is megjelenni/jegyzetelni. Folytatás...

2012. december 30., vasárnap

Reflection - a bajkeverő csodagyerek

Albert Hoffmann szavai csengnek a fülemben, amikor a Reflection-re gondolok: "bajkeverő csodagyerek". Egyfelől milyen hasznos, hogy futásidőben példányosítani lehet osztályokat, hogy feltérképezhetjük őket, hogy módisíthatjuk őket, stb. Aki dolgozott már modern webes keretrendszerrel az tisztában van a Reflection minden előnyével, hisz ez a legegyszerűbb módja feltölteni egy objektumot a kéréssel érkező paramétereknek, vagy egy proxy segítségével megváltoztatni egy adattag viselkedését. Ilyen értelemben elképzelhetetlen lenne a (mai értelemben vett) Java EE terjedése, mert Reflection nélkül még mindig a kőkörban élnénk.
RequestParams rp = new RequestParams(); 
final Field[] fields = rp.getClass().getDeclaredFields();
for (final Field field : fields) {
 firstChar = String.valueOf(field.getName().charAt(0));
 methodName = "set" + field.getName().replaceFirst(firstChar, firstChar.toUpperCase());
 try {
  method = rp.getClass().getMethod(methodName, String.class);
  method.invoke(rp, request.getParameter(field.getName()));
 } catch (final SecurityException e) {
 } catch (final NoSuchMethodException e) {
 } catch (final IllegalArgumentException e) {
 } catch (final IllegalAccessException e) {
 } catch (final InvocationTargetException e) {
 }
}
A példa jól szemlélteti, hogy pofon egyszerű feltölteni a kapott paraméterekkel egy objektumot, újabb paraméterek létrehozásakor nem kell setterekkel bajlódni, ráadásul a kód teljesen hordozható, így bármely osztályra dinamikusan alkalmazható (a példa csak String-eket kezel az egyszerűség kedvéért).
Itt merül fel a kérdés, hogy a Reflection nem egy kétélű fegyver? Mivel mondhatni teljes hozzáférést biztosít objektumjaink belső működéséhez és felépítéséhez, nem lehet ezt rosszra is használni? A válasz egyértelmű IGEN! Daily WTF-en találtam ezt a kedves kis szösszenetet:
class ValueMunger extends Thread {
    public void run() {
        while(true) {
            munge();
            try { sleep(1000); } catch (Throwable t) { }
        }
    }
    
    public void munge() {
        try {
            Field field = Integer.class.getDeclaredField( "value" );
            field.setAccessible( true );
            for(int i = -127; i <= 128; i++)
            field.setInt( 
                Integer.valueOf(i),
                // either the same (90%), +1 (10%), or 42 (1%)
                Math.random() < 0.9 ? i : Math.random() < 0.1 ? 42 : i+1  );
        } catch (Throwable t) { ; }
    }

}
A fenti kódrészlet az Integer wrapper cachet-t zagyválja egy kicsit össze, időnként nem a valós értéket kapjuk vissza, hanem az élet értelmét, ami üzleti kritikus alkalmazásokban igen nagy probléma, nem szeretnénk egy pészmékerben ilyesmi kóddal találkozni, de azt sem szeretnénk, ha a banki alkalmazás viccelné meg a bankszámlánkat időnként. A final mezőink, a private tagjaink sincsenek biztonságban, ahogy az Enum-jaink sem, és ezen a ponton a végtelenségi lehetne sorolni az ártalmas és vicces példákat, ugyanis kijelenthető, hogy (Sun/Oracle) Java verziótól függően szinte mindenhez hozzá lehet férni Reflection API segítségével, nincs menekvés.
Ártalmas kódot hagyhat hátra kilépő munkatárs, tölthetünk le ártatlanul egy külső osztálykönyvtárral, szóval több forrásból is beszerezhetőek, de vajon mit lehet tenni ellenük? Az egyetlen gyógyszer a tudatos és körültekintő felhasználás.

  • Használjunk statikus kódelemzőt, és szigorúan tartsuk számon azokat a részeket, ahol az alkalmazásunk Reflection API-t használ.
  • Használjunk nyílt forrású könyvtárakat, és fordítsuk magunk (kódelemzés után).
  • A letöltött osztálykönyvtárak hitelességét minden lehetséges módon ellenőrizzük, ugyanis koránt sem biztos, hogy senki nem törte fel kedvenc keretrendszerünk weboldalát, és cserélte ott le a letölthető jar-okat saját módosított verziójára (jó lenne a Linux repository-khoz hasonló központosított megoldás).
  • Csak a készítő által aláírt osztályokat, és jar-okat használjunk, ha külső forrásra kell támaszkodnunk.
  • Minimalizáljuk a külső forrásokat, ne használjunk két külön osztálykönyvtárat közel ugyanarra a feladatra.
Ha van még ötletetek, milyen módszerekkel védhetjük még a hátsónkat, kérlek ne tartsátok vissza magatokat, és írjátok meg, nekem hirtelen ennyi jutott eszembe. A bejegyzésben taglalt téma nem újkeletű, de azt hiszem sosem lehet róla eleget beszélni.

2012. november 4., vasárnap

Eclipselink beizzítása Jboss 7.1 környezetben

Valaha fejlesztettünk egy portált Liferay platformon. Mivel a fejlesztés elején a gyári Service Buildert elvetettük, teljesen kézenfekvő megoldás volt EJB/JPA párossal implementálni az üzleti és a perzisztens réteget. Első körben Hibernatere esett a választás, mert a Jboss alkalmazásszerver "natívan" támogatta, nem volt más dolgunk, mint deployolni az alkalmazást, és működött. Fejlesztés során nem is akadt semmi problémánk, ám az élesítés előtti utolsó hajrában szembesültünk azzal a ténnyel, hogy a perzisztens réteg teljesítménye a béka feneke alatt van, majd egy kis kutakodás után kikristályosodott, hogy a Hibernate nem bánik túl kedvezően az erőforrásokkal. Nincs mese, másodlagos cachet kell beüzemelni (Ehcachere esett a választás), de sajnos még ezzel sem hozta azt az alkalmazás, amit elvárnánk/megszoktunk. Ekkor jött az ötlet, hogy dobjuk ki a Hibernatet. Eclipselinkkel már volt tapasztalatunk, ráadásul nem is rossz, így megejtettük a váltást. Egyáltalán nem bántuk meg, a teljesítmény az egekbe szökött (pontos számadataink nincsenek, egyszerűen nem bírtuk elérni, hogy az adatbázisszerver CPU 10% fölé menjen, az addigi konstans 80% helyett). Eclipselinkre való átállás nem volt zökkenőmentes, nem pont ugyanúgy értelmezi a JPQL-t mint a Hibernate (egész pontosan az Eclipselink JPQL-t valósít meg, míg a Hibernate a saját HQL-jét használja), ezen felül a Jbossból is ki kellett gyomlálni pár Hibernate jart, úgy, hogy közben a Liferay, ami egyébként Hibernatre épül, még működőképes maradjon.

Az élesítés óta megjelent a Liferayből 2 újabb verzió, és szerettem volna frissen tartani a rendszert, de természetesen az új Liferayhez új Jboss is dukál. A frissítendő architektúra hibridsége miatt nem volt könnyű szülés a művelet, ennek lépéseit szeretném megosztani.

Az új Jboss architektúra teljesen modularizált, ezért egy 3rd party lib használata nem annyi, hogy bemásoljuk a megfelelő jar-t a classpathra, hanem modulként definiálnunk kell a rendszerben. Nincs ez másként az Eclipselinkkel sem. Modul létrehozásához annyit kell tennünk, hogy létrehozzuk a modules/org/eclipse/persistence/main könyvtárakat az alkalmazásszerverünkben, és a main könyvtárba bemásoljuk az eclipselink.jart, a konfigurációhoz pedig létre kell hoznunk ugyanitt egy module.xml-t, amiben a modult magát definiáljuk.


 
 
 
  
  
  
  
  
  
  
  
  
  
  
  
 

Az előző lépéshez hasonlóan egy Ant, és egy adatbázis modult is létre kell hozni (estemben egy PostgreSQLt).

    
        
    


    
        
    
    
        
        
        
    

Ezután nincs más dolgunk az alkalmazásszerveren, mint a standalone/configuration/standalone.xml-t szerkeszteni. Először is definiáljuk a szükséges DataSource-ot.
        
                
                    jdbc:postgresql://[hostname]:[port]/[database]
                    postgres
                    
                        14
                        20
                    
                    
                        postgres
                    
                
                
                    
                        org.postgresql.xa.PGXADataSource
                    
                
            
Lehet, hogy ez Liferay specifikus, de nálam volt Hibernate konfiguráció is ebben az xmlben, ami miatt timeoutolt a perzisztens réteg inicializálása, így azt egy kecses mozdulattal kitöröltem (egyelőre nem jelentkezett a hiányából fakadó probléma).

Miután az alkalmzásszerver konfigurációjával végeztünk, már csak az alkalmazásunkat kell egy kicsit reszelgetni. Ahol JNDI név szerint hivatkoztunk EJB Session Beanekre, ott a hivatkozott nevet célszerű átírni. A Jboss az EJB deployment során kiírja, hogy milyen neveken regisztrálta a beaneket, nekem nem mindegyik működött ??, de ez a forma bevált: global/[appname]/[serviceimplclassname].
A persistence.xml-ben is be kell állítanunk pár beállítást.
org.eclipse.persistence.jpa.PersistenceProvider
java:/jboss/datasources/LiferayPool

 
 


Utolsó lépésként az alkalmazásunkban be kell állítani, hogy milyen Jboss modulok a függőségei, ezt két féle módon is megtehetjük, vagy a META-INF/MANIFEST.MF állományban vesszük fel az alábbi sort:
Dependencies: org.eclipse.persistence
vagy létrehozunk ugyanitt egy jboss-deployment-structure.xml nevű konfigurációs állományt, az alábbi tartalommal:

  
    
      
    
  

Mindenkinek javaslom, hogy tegyen egy próbát az Eclipselinkkel, számos területen jobb, mint a Hibernate, nem csak teljesítményben és szabványkövetésben. Kedvenc tulajdonságom pl, hogy Hibernattel ellentétben a Session bezárása után is eléri még a DataSourcot (limitáltan), így a lustán inicializált relációkat nem kell kézzel betöltögetni még az üzleti rétegben, hanem a helyükre tett proxyn keresztül később is eléri azokat.