Komponensalapú
webalkalmazás-fejlesztő rendszer
Jónás Richárd
Debreceni Egyetem
Matematikai és Informatikai
Intézet
Információ Techológia
Tanszék
jonasr@math.klte.hu
Kivonat
Az élet egyre több területén
használják az emberek az Internet által nyújtott lehetőségeket, és az
állandó fejlődésnek köszönhetően mindig újabb igények merülnek fel új
alkalmazások internet-készre fejlesztésére. Hogyan tudunk gyorsan,
megbízhatóan, újrafelhasználhatóan alkalmazásokat építeni? Cikkemben egy
komponensalapú fejlesztőeszköz kerül bemutatásra, amely webalkalmazások
fejlesztését teszi lehetővé. Választ adunk arra a kérdésre, hogy mi a
komponens, majd megvizsgáljuk, hogy hogyan tudjuk a komponenseket kompozícióra
bírni. Végezetül egy-két technikai részlet bemutatása után szemügyre vesszük
egy ezzel a fejlesztőeszközzel készült alkalmazás hatékonyságát.
1. Bevezetés
A komponens – mint szoftverkomponens – fogalma már 30 éve is jelen volt
az információtechnológia területén, azóta mind a definíciója, mind a
felhasználása rendkívül sokrétű lett. A komponens egy olyan kódrészlet,
amely valamilyen szolgáltatást nyújt, azaz jól definiált funkcionalitással
rendelkezik, továbbá kompozíció útján valamilyen cél érdekében rendszert alkot
más komponensekkel. Nem fogjuk megkötni, hogy a komponens milyen nyelven
íródjon, ebből következik, hogy a komponens akár bináris formában
lévő futtatható kódrészlet is lehet. Komponensek esetén az
újrafelhasználhatóság opcionális tulajdonság, ehelyett a komponens
cserélhetőségét szokták kihangsúlyozni. A komponens kompozícióra
született, tehát fontos, hogy nem az (újra)felhasználhatóság van az
alkalmazhatóságért, hanem fordítva. Előfordulhat, hogy egy forráskódú
komponens jobban támogatja az újrafelhasználhatóságot, mint egy futtatható
komponens, de ettől mind a kettő
komponensnek tekinthető.
Manapság több komponensarchitektúra is létezik, melyek mind-mind
különböző cél érdekében jöttek létre. A CORBA [1] az elosztott rendszerek,
a COM és DCOM [2] az asztali alkalmazások, míg az EJB [3] és a JavaBeans [4] a
webalkalmazások területére specializálódott. A CORBA a legjobb példa arra, hogy
komponenseket nem csak objektumorientált nyelveken lehet készíteni, de
általánosságban elmondható, hogy az objektumorientáltság az a filozófia, amely
legjobban illeszkedik a komponensfilozófiához.
Cikkemben egy olyan fejlesztő- és futtatórendszer kerül
bemutatásra, amely komponensek definiálását, fordítását, futtatását és
integrációját teszi lehetővé. A rendszer webalkalmazások komponensalapú
felépítésére specializálódott, amelynek megfelelően egy sor olyan
lehetőséggel élhettem, amellyel általános esetben nem. A
fejlesztőrendszer az [5]-ben ismertetett technológia egy kiforrodtabb
változata.
2. Komponensek
A komponens tehát valamilyen paraméterek alapján egy jól meghatározott
tevékenységet végez el. A komponensnek, ahhoz hogy a szolgáltatásokat
elvégezze, szüksége van bizonyos bemeneti értékekre, hiszen az elvégzendő
tevékenység általában valamilyen értékektől függ. Ha a komponenst egy
olyan környezetben helyezzük el, amely biztosítani tudja számára ezeket az
értékeket, akkor a komponens gond nélkül működhet az adott környezetben.
Ahhoz, hogy az elhelyezést el tudjuk végezni, szükség van a komponensek által
igényelt és a környezet által nyújtott értékek összerendelésére. Tehát mind a
komponensnek, mind annak felhasználójának (másik komponens) szüksége van
valamilyen önelemző funkcióra, amely el tudja végezni, hogy a komponens
beleillik-e a megadott környezetbe.
2.1. Model-View-Controller
Rendszerünkben a komponenseket a jól ismert modell-nézet-vezérlő
elv szerint készíthetjük el. A modell a komponens által kezelendő adatokat
definiálja, a nézet rögzíti, hogy hogyan jelenítse meg a komponens önmagát, a
vezérlő pedig fogadja az eseményeket és feldolgozza azokat. Példaként
tekintsünk egy MVC elven felépülő számlát. A számla modell részét a
szállító és a vevő adatai, a számlán lévő cikkek kódjai, mennyiségei,
nettó árai, stb. alkotják. A számla tartalmaz származtatott adatokat is, az
egyes cikkek bruttó árait, a számla végösszegét. A számla nézet része esetünkben
meghatározza, hogy a számla hogyan nézzen ki egy HTML oldalon, tehát a
számlához tartozó HTML dokumentumrészlet feleltethető meg a nézetnek. A
vezérlő ebben az esetben fogadja a kliens üzeneteit, azaz hogy melyik
számlát kéri el a kliens, továbbá meghatározza mi történjen, ha a számlán egy
bizonyos helyre kattint a felhasználó.
2.2. Reprezentáció
A komponensek reprezentációját egy XML [6] dokumentum segítségével
végezhetjük el. Ehhez elkészült egy nyelv, amellyel a komponensek definíciói
leírhatóak. A komponens névvel rendelkezik, amely azonosítóul szolgál a
rendszeren belül. A komponens adatrésze lehet például egy adatbázisbeli
lekérdezés, amely eredményhalmaza adja a modell részt.
<component
id="SzámlaTételek">
<model>
<query>
SELECT TETEL_AZONOSITO, CIKKKOD,
MENNYISEG, NETTOAR
FROM SZAMLATETEL
WHERE SZAMLA_AZONOSITO = '<get-variable
name="szamla_azon"/>'
</query>
</model>
A komponens definíciója tartalmazhatja a nézet definícióját, amely az
előbb említett adatok megjelenítési formáját rögzíti. A nyelv tartalmaz
egy alapértelmezett nézetmegadási módot, amely következő elven
alapul:
<view>
<begin><![CDATA[<table>]]></begin>
<foreach>
<![CDATA[
<TR><TD>%1<TD>%2<TD
align="right">%3<TD align="right">%4
]]>
</foreach>
<end><![CDATA[</table>]]></end>
</view>
Az előző kódrészlet a SzámlaTételek nevű komponens nézet
részét definiálja. Az alapértelmezett viselkedés alapján először a
nézet begin része
értékelődik ki, tehát a komponens megnyit egy HTML táblázatot. Majd a foreach rész fog
kiértékelődni, minden sorra vonatkozóan. Ez annyiból áll, hogy a foreach tartalmában szereplő
mintába a futtató rendszer behelyettesíti a megfelelő adatokat. A %n helyére az adott sor n-edik
oszlopának eleme fog behelyettesítődni. A modellben található összes sor
kiértékelődése után végezetül az end tartalma lezárja a megnyitott táblázatot.
<contoller>
<view-begin/>
<view-loop>
<view-foreach/>
<view-skip/>
</view-loop>
<view-end/>
</controller>
</component>
A vezérlő a HTML oldal lekérésekor a webszervernek átadott kérési
paramétereket feldolgozza és változóként elérhetővé teszi értéküket a
modellnek és a nézetnek. A modell részben a szamla_azon változó értéke az
ugyanilyen nevű kérési változó értéke lesz. A vezérlő vezényli a
nézet rész kiértékelődését is. A fenti példában is látszik, hogy a
vezérlő annyiban tér el az alapértelmezett viselkedéstől, hogy
csak a páratlan sorszámú sorokat értékeli ki. A view-begin elem a nézet begin részét, a view-end a nézet end részét értékeli ki. A view-loop egy iterációt biztosít a
modell összes adatára, amelyben a view-foreach végrehajtja a nézet foreach elemét az aktuális sorra,
majd veszi az iteráció következő elemét. A view-skip egyszerűen veszi az
iteráció következő elemét.
3. Konténerek
Ahogy azt az előbb is láttuk, a SzámlaTételek komponens csak a számla egy
részét valósítja meg, a szállító-vevő párost, a fizetési határidőt
például nem tartalmazza. A számla komponens teljes megvalósításához több
komponens együttműködésére van szükség. A komponenseket konténerekbe
szervezhetjük, amely így egy összetett feladat megoldására szolgál.
Természetesen a konténer is egy komponens, ezért más konténerekbe
betehető, így a komponensek és konténerek egy hierarchikus
kapcsolatrendszeréhez jutunk, amelyben levélelemként komponensek szerepelnek.
A konténer is leírható az MVC elv segítségével. A konténer modell része
a tartalmazott komponensek sorozata, a nézet része meghatározza a komponensek
elhelyezését, a vezérlő pedig fogadja az eseményeket és meghatározza a
komponensek kiértékelődésének folyamatát.
<container
id="Számla">
<model>
<references>
<use-component
ref="SzámlaFej"/>
<use-component
ref="SzámlaTételek"/>
</references>
</model>
<view>
<layout>
<![CDATA[
<TABLE>
<TR><TD>%1<HR>
<TR><TD>%2
</TABLE>
]]>
</layout>
</view>
</container>
Tehát a konténer modell része a már meglévő komponensekre
hivatkozik. A modell részben lévő komponenseknek a konténer a végrehajtás
során paramétereket fog átadni. Ahhoz, hogy egy komponens helyesen
működjön, minden szükséges paramétert meg kell kapnia az őt tároló konténertől.
Tehát egy komponens akkor tehető be egy konténerbe, ha a konténer a
komponens által igényelt adatokat biztosítani tudja.
4. Fejlesztőrendszer
Elkészült egy fejlesztőrendszer, amellyel a fenti elven
komponensek és konténerek definiálhatók, majd fordíthatók és futtathatók. A
rendszer többrétegű architektúrával rendelkezik.
4.1. Fordítás
A fordítás során a komponensekből a rendszer több Java nyelvű
osztályt generál a következő szabály szerint. Ha a komponens neve például Számla, akkor:
Az utolsó három osztály rendre implementálja a Model, View, Controller nevű interfészeket. A
komponens XML definíciójából a rendszer XSL [7] nyelven megírt dokumentumok
segítésével elkészíti a fent leírt négy Java nyelvű osztály forráskódját,
majd lefordítja azokat. A fordítás után a szükséges komponensek Java osztályai
betöltődnek, majd a rendszer kérésre futtatja őket. A
komponensszerver mindig ellenőrzi a generált Java osztályok forrásainak a
dátumát, és szükség esetén frissíti a kódokat az XML definíciók segítségével.
4.2. Az absztrakció eszközei
A fejlesztés során többféle absztrakciós eszközt is használhatunk annak
érdekében, hogy az újrafelhasználhatóságot fokozhassuk.
Kompozíció. Mivel a komponens részekből tevődik össze, ezért a rendszer
lehetővé teszi a komponens kompozícióját különböző modell, nézet és
vezérlő részekből. Ezt követve például készíthetünk egy TáblaView nevű osztályt, amely
implementálja a View interfészt, és a paraméterben
megkapott modell összes adatát táblázatos formában megjeleníti, szükség szerint
egy lapozás funkcióval együtt. Az itt említett kompozíciós eszköz előnye,
hogy lehetővé teszi a komponensek finomabb tervezését.
Öröklődés. Komponensek készíthetők meglévő komponens
leszármaztatásából is. Ez akkor hasznos, ha a modellen, nézeten vagy a
vezérlőn csak kis változtatásokat kell eszközölnünk, a komponens többi
részét pedig meg szeretnénk tartani, módosítások nélkül.
Absztrakt komponens. Az absztrakt komponens olyan komponens, amelynek
valamely része (esetleg az összes) hiányzik. Ez akkor előnyös, amikor az
implementáció kezdeti szakaszaiban általános megoldásokat szeretnénk adni a
mostani és a jövőben potenciálisan felmerülő követelményekre.
Java programkód. A komponens bármely részébe elhelyezhetjük a code elemet, amely
tetszőleges, de az adott környezethez igazodó Java nyelvű
kódrészletet tartalmaz, amely segítségével azok a problémák is megoldhatók,
amelyekre a fenti eszközök nem adnak támogatást.
<controller>
<view-begin/>
<code>
while ( model.hasMoreRows() ){
Row r = model.nextRow();
if ( r.getColumnAsInteger( 3 ) > 10
)
view.foreach( r );
}
</code>
<view-end/>
</controller>
A fenti kódrészlet csak azokat a sorokat fogja a nézet részben
megjeleníteni, amelyekre igaz, hogy a számlatétel mennyisége nagyobb, mint 10.
5. Konklúzió
Elkészült egy fejlesztőrendszer, amely megvalósítja a fent leírt
elveket, azaz lehetővé teszi komponensek XML nyelvű definícióját. A
rendszer segíti az XML definíciók elkészítését, lépésről lépésre
definiálhatjuk a komponens részeit, tehát az XML nyelv ismerete nélkül is
előállíthatunk működő komponenseket. A komponensek végrehajtási
sebességét egy példaalkalmazás implementációjával teszteltem. A tesztprogram
egy vámnyilvántartó alkalmazás volt, amely csak egy bonyolult számítást
tartalmazott. A komponensek sebességét tulajdonképpen az SQL kérdések
bonyolultsága és a feldolgozás milyensége befolyásolja, de méréseim alapján a
komponensek egy 1200MHz-es Athlon processzorral, 384 MB memóriával
rendelkező számítógépen gyorsan, 0.05-0.1 másodperc alatt végrehajtódnak.
Irodalomjegyzék
[1] Object Management Group, Common Object Request Broker
Architecture,
http://www.omg.org/technology/documents/corba_spec_catalog.htm
[2] Microsoft, The Component Object Model Specification,
http://www.microsoft.com/com/resources/comdocs.asp
[3] Sun Microsystems, Enterprise JavaBeans Specification,
http://java.sun.com/products/ejb/docs.html
[4] Sun Microsystems, JSP homepage, http://java.sun.com/products/jsp/
[5] Jónás Richárd, Web fejlesztés dinamikus Weben keresztül,
NetworkShop, 2002, Eger.
[6] World Wide Web Consortium, Extensible Markup Language,
http://www.w3.org/TR/REC-xml
[7] World Wide Web Consortium, XSL Transformations, http://www.w3.org/TR/xslt