2010. február 6., szombat

@Aszinkron EJB >=3.1 hívás

Az EJB 3.1 megjelenése számos újítást és egyszerűsítést hozott a rendszerbe, a fejlesztők, mint ahogy a 2.x-ről 3.0-ra váltáskor is, igyekeztek még kényelmesebbé tenni az enterprise alkalmazások készítését. Mivel mi Glassfish alkalmazás-szervert használunk, és a kiadást követő napokban kezdtünk bele egy új projektbe, úgy határoztunk, hogy a friss és ropogós EJB-t használjuk (eddig nem bántuk meg) az implementáláshoz. Az új EJB-t tanulmányozva szembeötlött egy lehetőség, ami mellett nem tudtam szó nélkül elmenni. Úgy néz ki, hogy nem csak nekem jutott eszembe, hogy az alkalmazást, amit fejlesztek párhuzamosítsam, hanem az EJB fejlesztőinek is, ezért megalkották az aszinkron EJB hívás fogalmát. A dolog igen egyszerű, a hagyományokhoz híven @Annotációval kell megjelölni azokat a SessionBean metódusokat, amiket nem a hagyományos módon szeretnénk kiajánlani.
package sample;

import javax.ejb.Asynchronous;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class TestBean {

    @Asynchronous
    public void asyncMethod() {
     //some functionality
    }
}
A metódus meghívása a megszokott módon történik, viszont az abban elhelyezett utasítások egy új szálon fognak futni. Ez eddig egyszerű, de mi történik, ha szeretnénk, hogy a hívás valamilyen visszatérési értéket is prezentáljon? Ezt úgy tudjuk megtenni, hogy visszatérési értékként a java.util.concurrent.Future<V> interfészt jelöljük meg, és annak valamely implementációját adjuk vissza.
package sample;

import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

@LocalBean
@Stateless
public class TestBean {

    @Asynchronous
    public Future<String> asyncMethod() {
        try { Thread.sleep(1000);
        } catch (Exception ex) {}
        return new AsyncResult<String>("finish");
    }
}
A dolog érdekessége a hívó oldalon történik, hiszen valahogy kezelni kell tudni, hogy az aszinkron hívás éppen milyen állapotban van.
Future<String> asyncResponse = testBean.asyncMethod();
//some functionality
while (!asyncResponse.isDone()) {
    try { Thread.sleep(100);
    } catch (Exception ex) {}
}

if (!asyncResponse.isCancelled()) {
    try {
        String response = asyncResponse.get();
    } catch (InterruptedException e) {
    } catch (ExecutionException e) {}
}
Láthatjuk mi módon tudjuk "összeszinkronizálni" a hívásokat a szülő szálban. Természetesen meg is lehet szakítani a kérést, a Future cancel metódusával, illetve a rendelkezésünkre állnak a hagyományos Thread notify, notifyAll, és wait metódusai.

2010. február 5., péntek

Btrace bemelegítés

Egy Sun-os konferencián hallottam először a Dtrace nevű eszközről, mely egy dinamikus nyom-követő. Sajnos használható formában csak Solaris vagy OpenSolaris platformokra érhető el, és mivel eléggé kernel szintű integrációja van a rendszerrel nehézkes a portolása. Sokáig vártam egy Linux-os verzióra, de sajnos még a mai napig sincs használható állapotban (vagy csak én nem boldogultam vele).
A Dtrace működése röviden annyi, hogy van egy saját programozási nyelve a D, és ezen a nyelven kell megírnunk a kernelnek, hogy a vizsgált alkalmazás mely állapot-változásait figyelje. A dolog szépsége, hogy az alkalmzásunkba nem kell "hook" pontokat elhelyezni, tehát a működés megfigyelése teljes mértékben kívülről történik, menet közben megváltoztatható, és nem utolsó sorban, akár éles üzemben is bevethető. A programozó döntése, hogy mit figyel, a megfigyelt eseményekről mekkora mennyiségű információt irat ki a képernyőre, vagy akár egy log fájlba.
Bár a Dtrace platform-függő, létezik egy kifejezetten Java alkalmazásokra szánt Java-s átírat, amit Btrace-nek hívnak (a honlap nem tudom meddig lesz elérhető, mert a project a Kenai forrás-megosztó oldalon van elhelyezve, amit jól megvett az Oracle és jól be is szippantja "belső" használatra). A Btrace egyébként nyílt forrású project, és jelenleg az 1.1.1-es verziónál tart.
Használat:
  • Telepítése igen egyszerű, jóformán nincs, a bináris-t ki kell csomagolni a kívánt helyre.
  • Ezután első lépésként meg kell írnunk első Btrace scriptünket, mely annyit csinál, hogy az application.Main osztálynak figyeli a someMethod metódus hívását.
    package btrace;
    import static com.sun.btrace.BTraceUtils.*;
    import com.sun.btrace.annotations.*;
    @BTrace public class ClassTracer {
       @OnMethod(clazz = "application.Main", method = "someMethod")
       public static void methodcalled() {
           println("Method called");
       }
    }
    
  • Következő lépésként a vizsgálandó alkalmazást kell elindítani, egy apró külöbséggel, hogy a JVM-et meg kell kérni, hogy HotSpot módban induljon, ennek módja az alábbi: `java -client -jar sampleapp.jar`. A jar-ba fordított programocska:
    package application;
    public class Main {
       public static void main(String[] args) {
           for (int i = 0; i < 1000; i++) {
               someMethod();
               try { Thread.sleep(100);
               } catch (Exception ex) {}
           }
       }
       public static void someMethod() {}
    }
    
  • Miután fut az alkalmzásunk egy arra alkalmas eszköz segítségével meg kell tudnunk a futó programunk azonosítóját, PID-jét, Linuxon a top parancs segítségével jutunk sikerre, bár nem ez az egyetlen mód.
  • Majd a Btracet kell ráállítani a JVM-re, hogy dolgozza fel a kapott információkat a `btrace PID ClassTracer.java` paranccsal (ha elsőre Connection refused hiba-üzenettel leáll még 1x érdemes megpróbálni futtatni, van, hogy elsőre nem indul el).
  • Nincs más dolgunk, mint élvezni a konzolt elárasztó Method called kiírásokat.
A módszer legnagyobb előnye, hogy bytecode szinten tudjuk nyomon-követni alkalmazásunkat, azt látjuk, ami a virtuális gépben ténylegesen végrehajtódik.
Még néhány lehetőség (a teljesség igénye nélkül):
  • Van mód arra, hogy a clazz vagy method @Annotáció érték helyére reguláris kifejezést illesszünk, így nem csak egy osztályt vagy metódust, tudunk lekezeleni, hanem a mintára illeszkedő összeset.
  • Mivel a callback metódusokat mi hozzuk létre, paraméterként számtalan bemeneti változó közül választhatunk, melyeket @Annotációkkal jelölhetünk. Pár példa: @ProbeClassName String probeClass paraméterrel az osztályt, @ProbeMethodName String probeMethod paraméterrel a metódust kapjuk meg. @Return Class returnClass-al megkapjuk a metódus visszatérési értékét (ehhez az @OnMethodban fel kell venni egy location=@Location(Kind.RETURN) értéket), a AnyType[] args-el az átadott argumentumokat.
  • Egy Btrace scripten belül párhuzamosan több metódussal is követhetjük az állapotot, ennek köszönhetően a programozó fantáziájára van bízva, hogy pontosan mit is szeretne megfigyelni.
  • Lehetőség van időzített ismétlődő műveletek futtatására, így képet kaphatunk a processzor terheltségéről, vagy akár a memória használatról is, ekkor az @OnMethod helyett @OnTimer(long ms) @Annotációt kell használnunk.
  • Meglévő Dtrace scriptjeinkkel is összeköthetjük Btrace scriptjeinet, bár ennek nem sok értelmét látom, hisz ahol van Dtrace ott minek használnák a Btracet.
  • Létezik egy VisualVM plugin is a Btracehez, így a scripteket a VisualVm-en belül is szerkeszthetjük akár futás-időben is. Mindennapi használatra ezt javaslom egyébként, számtalan ponton könnyíti a munkát.
A felhasználói kézikönyv szerintem magáért beszél, továbbá a példa kódok bőbeszédűek, azokból is elsajátítható az eszköz használata.
Még egy fontos jó-tulajdonsága van a Btracenek, mégpedig az, hogy mivel POJO osztályokat kell létrehoznunk, és @Annotációkat használ a sajátságos működéshez, a jar-okat importálva bármely Java képes fejlesztő-eszközhöz van támogatás, ami valljuk be elég nagy könnyebbség a fejlesztőnek.
Zár-szóként remélem kedvet kapott a kedves Olvasó, hogy kipróbálja ezt a remek eszközt, és bár az írás eléggé bevezető jellegű mégis hasznos volt.