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