interceptorok, egy lehetseges implementacio antares felett

aki mar EE -zett, az mar biztos hallott roluk, aki meg nem, az most megtudhatja milyen jo dolgok ezek :-)

az AOP -bol lopva az otletet a lenyeg, hogy egy letezo objektumot tudjunk burkolni ugy, hogy a hasznalaton ez ne latszodjon.
normal Java SE kornyezetben hasonlo funkcionalitas erheto el a dinamikus proxyk segitsegevel, EE -s vilagban pedig az EJB
specifikacio nyujt erre lehetoseget.

de elosszor is: mire jo ez az egesz?

tegyuk fel, hogy le akarjuk merni, hogy mennyi ideig fut egy adott metodus. hogy csinaljuk ezt? peldaul teletomhetjuk loggolo koddal, ami megnezi azi idot amikor meghivodott a metodus, es megnezi ami elott visszater, majd leloggolja a kulonbseget, azonban ehez egyreszt rendelkeznunk kell az adott osztaly forraskodjaval (ez sok helyen ugyis adott), masreszt ha csak debug idejere szeretnenk ezt a funkcionalitast, akkor miutan elesbe megy a projekt, irhatjuk at a kodot. szoval bonyis.

a masik megoldas, hogy beburkoljuk egy dinamikus proxyval, ami mindaddig jo, amig nem valami menedzselt kornyezetben (pl alkalmazasszerver futunk). itt mar eleg veszelyes dolog egy ilyen, ki tudja hol borulunk meg (Java SE kornyzetben siman jo megoldas amugy, lehet szep, dinamikus Decorator Patternt implementalni vele).

Java EE -s kornyezetben definialhatunk interceptor osztalyt a beanunkre, es akkor szepen meglehet mondani melyik metodus hivodjon meg az eredeti meghivasa helyett (szo szerint elkapjuk a hivast: ha akarjuk, megoldhato az is, hogy az eredeti objektum metodusa le se fusson).

Antaresben is szerettem volna ilyen featuret implementalni. A kovetkezo peldan megertheto, hogy mi is ez, es hogy mukodik.

Tegyuk fel, hogy van egy nagyon egyszeru interfeszunk:


public interface Hello {
    public void sayHello();
}

es az ot implementalo osztaly:


public class HelloImpl implements Hello {
    public void sayHello() {
        System.out.println("HAI!");
    }
}

es persze van egy harmadik osztaly, ami szeretne ezt hasznalni. itt kezdodnek a trukkok :-)


public class HelloUser {
    @Inject(strategy=InjectionStrategy.FromManagedPool,
            interceptor=TestInterceptor.class,
            interceptorInjectionStrategy=InterceptorInjectionStrategy.Keep)
    private Hello hello;

    public void use() {
        hello.sayHello();
    }
}

sok ronda dolog van itt, elosszor is kezdjuk az Inject annotacioval. azt mondja, hogy szeretne egy Hello interfeszt implementalo peldanyt injektalni a helloba. nyilvan az injektalast majd egy kontener vegzi, errol egyelore tobbet nem beszelnek, most nem azon van a hangsuly, hanem ket annotacios parameteren: az interceptoron es az interceptorInjectoinStrategyn.

az elso azt mondja meg, hogy szeretnenk ugy injektlani az adott interfeszimplementaciot, hogy az interceptorunkkal el tudjuk kapni, a strategia pedig az, hogy az injekciot vegzo proxy (mert ugye minden kontenerben proxyval csinaljak ezeket :)) hozzon letre egy peldanyt, es ne minden metodushivasnal csinaljon egyet.

nezzuk meg magat az interceptort:


public class TestInterceptor {
    private long start;

    public TestInterceptor() {
        System.out.println("interceptort letrehoztak!!");
    }

    @BeforeInvoke
    public void x(InvocationContext ctx) {
        start = System.currentTimeMillis();
    }

    @AfterInvoke
    public void y(InvocationContext ctx) {
        System.out.println("invocation of " + ctx.getTargetMethod().getName() +
                " on an instance of " + ctx.getTargetInstance().getClass().getCanonicalName() +
                " took " + (System.currentTimeMillis() - start) + " milliseconds");
    }
}

ez a kod azthiszem egyertelmu; az eredeti peldany metodushivasa elott meghivodik az interceptorpeldany megfelelo metodusa, es utana is egy masik metodusa. (persze lehetne ugyanaz is, ha oda raknank az annotaciokat..)
ez az interceptor egyszeruen annyit csinal, amit fent is vazoltam: lemeri mennyi ido volt meghivni a metodust.

az ezeket hasznalo foprogram:


public class Main {
    public static void main(String[] args) {
        Container container = new Container();
        container.addManagedInstance(new HelloImpl());

        try {
            HelloUser user = container.getInstance(HelloUser.class);
            if(user == null) {
                System.out.println("null volt!");
            } else {
                user.use();
            }
        } catch(InjectionException e) {
            e.printStackTrace();
        }
    }
}

es amit kiir futas utan:


interceptort letrehoztak!!
HAI!
invocation of sayHello on an instance of antaresuse.HelloImpl took 4 milliseconds

melyik a konnyebb, kivenni azt a ket annotacioparametert, vagy atirni egy osztaly mondjuk 50 metodusat? :-)

Hozzászólások

[ OFF ]
Jut eszembe, mintha egyszer lett volna egy felmeresed arrol, hogy igenyelnenk-e valami j2EE oktatoanyagot/eloadassorozatot vagy mit toled. Ofkoz tudom hogy vizsgaidoszak van ([ kocsog ]hehe, nekem mar nincs :p[ /kocsog ]) de en szeretnem megerositeni, hogy meg mindig tartom ra az igenyt :)
[ /OFF ]

hat, az van, hogy aki SAI egyetemen van (BME, ELTE, EKF), az kaphat Sunos e-learning hozzaferest, meg kedvezmenyes vizsgat, aki nem, az meg nem. :)

kesz anyagom az elso eloadason kivul nincs konkretan EE temaban, de ha elkezded es elakadsz, szivesen segitek :)

(mondanam, hogy majd csinalok tobb eloadasanyagot, de nem tudom mikor lesz ra idom; a kerdezz-felelek meg mindig gyorsabb)

Az ilyen kis szosszenetek is jok, ha osszegyujtod oket (peldaul a HUP blog kereteben); de szerintem egy alap bevezeto nem lenne rossz, elmagyarazni peldaul hogy mik azok a kontenerek (akkor most hivjam fel a sittszallitokat, es rendeljek kontenert?), egyaltalan erre az egesz hulyeskedesre miert van szukseg. Az EJB mukodesenek alapjairol sem lenne rossz dolgokat hallani. Tudom, hogy le van ez dokumentalva szepen a Sun oldalain, de egy magyarazo szoveg mindig sokkal erthetobb mint a szaraz dokumentumhalom.

Az a grizzly-s cucc is egesz jo volt, peldaul annak a telepiteserol/beallitasarol is lehetne egy cikket irni; meg hogy miert/mire is jo az egyaltalan, es egyebkent is.

Nem akarok most itt hosszan otletelni, nyilvan ilyen alapszintu otleteid neked is vannak. Sokan szivesen latnanak ilyesmit is.
--


()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

Telleg a kliens oldalon kell megmondani, hogy milyen interceptor stack-el akarom meghivni az objektumot? Ugye azert ez meghatarozhato szerver oldalon is.

Gyere ircre, de azert itt elmagyarazom, hogy a jovo nemzedeke okulhasson belole, ha kell.

Szoval, adott egy kontener, amibe be van hajitva egy objektum. Ez az objektum adminisztrativ feladatokat vegez el, ezert nem szeretnem, ha barki hivogatja, korlatozni szeretnem olyan felhasznalokra, akik adminisztrator szerepkorbe tartozik. Tovabba figyelemmel szeretnem kovetni az adminisztratorokat, hogy nehogy visszaeljen a jogaikkal. Adott a dolog: ket interceptor, az egyik ami a jogosultsag ellenorzest vegzi, a masik ami az adminisztratorok tevekenyseget naplozza.

Erezheto, hogy az interceptor stack konfiguracioja nem lehet kliens oldalon mert:
- ha sok a kliens, akkor mindenhol ismetlodne, ami felesleges redundancia
- biztonsagi szempontbol nem elfogadhato (kihagyja az egyik kliens a security interceptort, es maris ugrott a biztonsag)
- ki tudja, h kesobb milyen interceptorokat adunk meg hozza?

ps: A kliens alatt ertem azt a kodreszletet, ami az objektumot hasznalja,