J2EE – a nagy aknamező

Hát, megvan egy ideje, hogy utoljára írtam. Pedig magamban már többször megfogadtam, hogy rendszeresebben írok. Úgy volna értelme csinálni az egészet, hogy hetente legalább egy bejegyzés születik. Persze ez így hiú ábránd ennyi házi feladat mellett.

Ez az UML házi kész volt. 80 órát egy hét alatt beletolni abba, hogyan kell leimplementálni egy hello world-nél egy szinttel bonyolultabb feladatot J2EE alapon, na ez teljesítmény. És örülök, hogy ennyi idő alatt összeállt…

Hát, megvan egy ideje, hogy utoljára írtam. Pedig magamban már többször megfogadtam, hogy rendszeresebben írok. Úgy volna értelme csinálni az egészet, hogy hetente legalább egy bejegyzés születik. Persze ez így hiú ábránd ennyi házi feladat mellett.

Ez az UML házi kész volt. 80 órát egy hét alatt beletolni abba, hogyan kell leimplementálni egy hello world-nél egy szinttel bonyolultabb feladatot J2EE alapon, na ez teljesítmény. És örülök, hogy ennyi idő alatt összeállt…

Nem véletlen tartott eddig. Van benne egy gyönyörű implementált objektumrelációs leképezés, néhány tag elhelyezése, egy xml-fájl és még néhány sor kód megírása után az objektumokat szépen kimenti az adatbázisba, illetve visszaolvassa onnan igény szerint. Tényleg szép. Csak az ember el ne szúrja a tagelést, mert olyan exception-trace-t kap futáskor, hogy öröm nézni. A legnagyobb, amit sikerült kapni, 40 kB méretű volt. Iszonyatosan redundáns, gyakorlatilag az egész háromszor szerepel, az értelmes tartalom másfél-két sor. Szűrd ki…

A fejlesztés során mi RAP-t használtunk GUI-készítéshez. Nem gyenge technológia: Eclipse plugineket AJAX-szal támogatott weblapokra fordít. De azért ezt több projektből összefejleszteni szép teljesítmény. Például nem mindegy, hogy hol kapcsoljuk össze a projekteket. Ha az Eclipse projekt tulajdonságainál fogjuk, és beikszeljük a Referenced projects résznél (hogy szerepeljen a build path-ban), az nem jó. A plugin.xml fájlban kell a depencencies blokkban bejelölni.

Remek, most már lefordul, elindul, és gyönyörű exception trace megint. Most az a baja, hogy nem töltötte be a futás közben a hivatkozott projekteket. Némi szórakozás után kiderült, hogy a megoldás az, hogy még a futtatási konfigurációnál is fel kell venni a hivatkozott projekteket, mint bundle-t. Eredetileg ezt nyersen az xml-be hakkoltam bele, később hívta fel valaki a figyelmemet, hogy a futtatási konfigurációnál is fel lehet venni.

Hasonló szépségek vannak az APIban is. A RAP1.1M1 és az 1.1M3 között megváltozott az alkalmazás belépési pontjának szintaktikája: az M1-ben Display típusú, míg az M3-ban int típusú visszatérési értéket vár. Érdemes megnézni a sorrendet, Display-ből int.

Beletelt némi időbe, amíg rájöttem, hogyan kell megváltoztatni a mintakódot, hogy lefusson. Gyakorlatilag a Display egy azonosítóját kell visszaadni a metódus végén. Szóval minden érdekes.

Hasonlóan jópofa volt az, hogy amikor beraktuk a szükséges webservice meghívásához szükséges fájlokat, akkor kb 25-30 MB méretben kellett jar-fájlokat a lib könyvtárba tenni. Ami problémássá teszi a dolgot, hogy ez csoportmunkában készült házi feladat, azaz fel kellett tölteni egy svn szerverre, és a többieknek le kellett szedni. Hab a tortán, hogy az svn csak fájlok között frissíti a status bart, így a 10 megás jarnál úgy néz ki a dolog, mintha meghalt volna menet közben. A csúcs az volt, amikor ezt upload közben sikerült valakinek fagyásnak értelmeznie, és kilőtte az Eclipse-et. Ezután egy órába került, amíg sikerült rendbe tennie a rendszert…

Érdekes ez az Enterprise Java technológia. Ekkora aknamezőt még nem láttam… Elég valami apró hiba, és hihetetlen mennyiségű hiba bukkan fel, és a hibaüzenetekből is alig lehet kikövetkeztetni, hogy mi lehetett az ok. Elég bonyolult architektúra, rengeteg 3rd party lib, és nagyon korlátozott tapasztalat: ez elég ahhoz, hogy a nem túl bonyolult feladat megoldását nagyon megnehezítse.

Distributed Connector

A Linux Programozása nevű tárgyra adtam be egy helyi hálózatra tervezett elosztott kommunikációt megvalósító rendszert, és példaalklamazásként elosztott quicksort algoritmust. A rendszer tökéletesen elosztott, ami annyit tesz, hogy a helyi hálózatra csatolt gépek mindegyikén fut egy szolgáltatás, ami UDP üzenetek segítségével tartja a kapcsolatot a többi számítógéppel, és felületet nyújt a kommunikációhoz a felhasználói programoknak.

Letöltés

A Linux Programozása nevű tárgyra adtam be egy helyi hálózatra tervezett elosztott kommunikációt megvalósító rendszert, és példaalklamazásként elosztott quicksort algoritmust. A rendszer tökéletesen elosztott, ami annyit tesz, hogy a helyi hálózatra csatolt gépek mindegyikén fut egy szolgáltatás, ami UDP üzenetek segítségével tartja a kapcsolatot a többi számítógéppel, és felületet nyújt a kommunikációhoz a felhasználói programoknak.

Letöltés

Linux architektúrán legegyszerűbben a következő parancsok végrehajtásával tölthetjük le a forráskódot, és fordíthatjuk le a rendszert:


wget http://users.hszk.bme.hu/~gb606/disco/Makefile
make all

Dokumentáció

Disco Architektúra

A létrehozott rendszer feladata egy helyi hálózatra csatlakoztatott számítógépek kommunikációjának támogatása, anélkül, hogy ehhez szükség lenne központi kiszolgáló segítségére. A központi kiszolgáló feladatait (felhasználók követése, üzenetek továbbítása) szét kell osztani a résztvevő számítógépek között. A legegyszerűbb megoldás az, ha az összes számítógép ismeri a résztvevőket, és kommunikációra UDP üzeneteket, szükség esetén UDP üzenetszórást alkalmazunk.

DisCo architektúra

Minden résztvevő számítógépen fut egy DisCo kiszolgáló, amely a többi számítógéppel tartja a kapcsolatot, és felületet biztosít a felhasználói programok (továbbiakban szolgáltatások, Service-ek) számára Unix Domain Socket-en keresztül.

Lényeges kikötés, hogy egy gépen egyféle szolgáltatásból csak egy futhat egy időben és az egyes szolgáltatások a többi résztvevő gépen csak azonos szolgáltatással kommunikálhat (ez nem jelent korlátozást, hiszen az egyes szolgáltatások maguk is biztosíthatnak felületet a helyi gépen más szolgáltatások számára).

Disco protokoll

Közös csatorna

A résztvevő számítógépek között meg kell oldani azt, hogy az elosztott résztvevők mindegyike egységes képet kapjon a hálózatban szereplő gépekről, és a rajtuk futó szolgáltatásokról. Ezen felül továbbítani kell a szolgáltatások irányított vagy szórt (single-/broadcast) üzeneteit.

A közös csatornán használt üzenetek a következők. A kommunikáció UDP/IP felett történik, minden üzenetet sorvégjel (‘\n’) zár.

Üzenetformátum Paraméterek Jelentés
(%s Számítógép szöveges azonosítója A küldő fél bejelentkezik a hálózatba
=%d %s A rendelkezésre álló szolgáltatások kódolva; szöveges azonosító Bejelentkezésre válasz: a küldő gép szolgáltatásai, és azonosítója
) Kijelentkezés
+%d Szolgáltatás azonosító A küldő számítógépen új szolgáltatás indult
-%d Szolgáltatás azonosító A küldő számítógépen egy szolgáltatás leállt.
!%d %s Szolgáltatás azonosító, üzenet Irányított üzenet szolgáltatások között
.%d %s Szolgáltatás azonosító, üzenet Szórt üzenet szolgáltatások között

Helyi kommunikáció

Kiszolgáló -> Szolgáltatás

Üzenetformátum Paraméterek Jelentés
(%s Helyi gép szöveges azonosítója A kiszolgáló elküldi a helyi gép azonosítóját
+%d %s Távoli gép szám azonosítója, távoli gép szöveges azonosítója Egy távoli gépen elindult az adottal kompatibilis szolgáltatás
-%d Távoli gép azonosítója A távoli gépen leállították az adott szolgáltatást
!%d %s Távoli gép azonosítója, üzenet Irányított üzenet érkezett
.%d %s Távoli gép azonosítója, üzenet Szórt üzenet érkezett

Szolgáltatás -> Kiszolgáló

Üzenetformátum Paraméterek Jelentés
%d Szolgáltatástípus azonosítója Szolgáltatás azonosítása a helyi kiszolgáló számára
!%d %s Címzett azonosítója, üzenet Üzenet küldése a megjelölt számítógépen lévő azonos szolgáltatás számára
. %s Üzenet Üzenet küldése az összes azonos szolgáltatásnak a hálózatban

Protokoll folyamatok

Bejelentkezés

Amikor egy új számítógépen elindul a DisCo kiszolgáló, egy bejelentkezés (‘(%s’) üzenetet küld minden számítógépnek a hálózatban. Nyilvánvaló, hogy kezdetben semmilyen szolgáltatás nem fut az újonnan indult kiszolgáló mellett. A bejelentkezés hatására minden számítógép visszaküldi a saját azonosítóját és a rajta futó szolgáltatások azonosítóit kódolva.

Szolgáltatás indítása, leállítása

Minden távoli szolgáltatás indulásakor vagy leállításakor az eseményt jelezni kell az esetleges azonos típusú helyi szolgáltatásnak. Illetve a helyi szolgáltatásokban történt változást szórt üzenetként továbbítani kell a többi számítógépnek.

Egy példa alkalmazás: quicksort

A bemutatott példában a 2-es azonosítóhoz rendeljük a sort szolgáltatást, amely gyorsrendezést valósít meg az elosztott rendszerben. A hálózat bármelyik gépén beadott rendezési feladatot a rendszer autonóm módon szétosztja a feladatot, majd a végeredmény ott lép ki, ahol a feladatot beadtuk.

Pontosítva az algoritmust, (a továbbiakban node-nak hívjuk a sort szolgáltatással rendelkező számítógépeket) ha egy node kap egy rendezési feladatot (egy másik node-tól, vagy a rendszeren kívüli felhasználótól) azt két részre osztja, majd kiválaszt véletlenszerűen két node-ot, akiknek elküldi a két részfeladatot. Minden node oda küldi vissza az eredményt, ahonnan kapta. Ha a végeredményről van szó, akkor azt a részeredmények összerakása után a helyi fájlrendszerben output.txt fájlba rakja.

Protokoll

A quicksort szolgáltatás a DisCo kiszolgáló felületét használva, a fent leírt protokollra épít, az üzeneteit tehát DisCo protokoll szerint felcímezve küldi. Alant csak a DisCo protokoll üzenetrészében szereplő tartalmat fejtem ki, bár az is fontos információt tartalmaz, hogy ki az üzenet címzettje.

Üzenetformátum Paraméterek Jelentés
?%d%c%d,%d,… Feladatazonosító (aminek szerepelnie kell a válaszban); részjelölő karakter: ‘< ' vagy '>‘ attól függően, hogy a feladat a teljes feladat alsó, vagy felső részfeladata; majd a rendezési feladatot leíró vesszővel elválasztott számsorozat (első eleme az adatok száma, majd jönnek a számok) A küldő node feladatot ad a címzettnek.
!%d%c%d,%d,… A feladatkiírásban szereplő azonosító; a részjelölő karakter; és a rendezett számsorozat hasonlóan a feladatkiíráshoz A küldő node befejezte a feladatot, és küldi az eredményt.

Rendezés menete

A felhasználó bármelyik a rendszerbe csatlakozott számítógépen létrehoz egy feladatfájlt, benne a rendezésre váró számokkal, sorvégjellel elválasztva. E fájl nevét beírva a sort programba bekerül a feladat a rendszerbe. Ha minden jól megy, pár pillanat múlva a megoldás bekerül az output.txt -be.

Telepítés és futtatás

A DisCo kiszolgálót és a quicksort szolgáltatást a következő parancsok beírásával lehet a leggyorsabban telepíteni (minden előzetes letöltés nélkül):


wget http://users.hszk.bme.hu/~gb606/disco/Makefile
make all

A fent letöltött Makefile letölti az összes szükséges forrásfájlt, és előállítja a szükséges bináris állományokat, és generál egy azonnal használható input.txt -t. A kiszolgálót futtatni a


./disco _név_

-el lehet, ahol a _név_ egy szabadon választott szöveges azonosítója a számítógépnek. A rendezési feladat generálására alkalmas eszköz az


./inputgen _kimenetszám_

, ami stdout-ra a megadott számú véletlenszámot írja ki. Végül a sort szolgáltatást a ./sort paranccsal indíthatjuk el.

Java kimenet UTF-8 kódolásban

Csalódtam a rendszeremben. Pár nappal ezelöttig abban a szent meggyőződésben voltam, hogy a Kubuntu tetőtől talpig UTF-8 kódolással dolgozik. Nos ez többnyire igaz. A java VM esetén az stdout alapértelmezett kódolása latin-1, ami normális esetben nem tűnik fel. Önálló labor feladatom során merült fel, hogy egy java program kimenetét kellett böngészőben megjeleníteni. A program bemenetként egy UTF-8 kódolású XML fájlt kapott, fel sem merült bennem, hogy gond lehet a kódolással.

Csalódtam a rendszeremben. Pár nappal ezelöttig abban a szent meggyőződésben voltam, hogy a Kubuntu tetőtől talpig UTF-8 kódolással dolgozik. Nos ez többnyire igaz. A java VM esetén az stdout alapértelmezett kódolása latin-1, ami normális esetben nem tűnik fel. Önálló labor feladatom során merült fel, hogy egy java program kimenetét kellett böngészőben megjeleníteni. A program bemenetként egy UTF-8 kódolású XML fájlt kapott, fel sem merült bennem, hogy gond lehet a kódolással.

Meglepetésemre a program kimenetén minden ékezetes karaktert szorgalmasan kicserélt egy-egy kérdőjelre. Néhány óra bogarászás és kutatás után kiderült, hogy a Java belső kódolásként [[http://hu.wikipedia.org/wiki/UCS|UCS]]-t használ, minden bemenetet erre konvertál, és ebből alakítja át a kimenetet a megfelelő kódolásra.

További kutatással sikerült egy egyszerű módot találnom, amivel beállíthatom a sztandard kimenet kódolását. UTF-8 beállításához a következő néhány sorral kell kezdeni a main() függvényt:


try{
PrintStream out = new PrintStream(System.out,true,"UTF-8");
System.setOut(out);
}catch(Exception e){}

Rövid magyarázat: a PrintStream osztály egy egyszerű szűrőként dolgozik, ami a bemenetét UTF-8-ra átkódolva adja tovább a megadott Stream-re (jelen esetben a System.out). Ezután beállítjuk a létrehozott Stream-et alapértelmezett kimenetként. Voilá. Minden további kimenet UTF-8 kódolású lesz.

BMÓ project

2006-ot írunk. A Cubus Sapiens faj nemcsak elkezdte világhódító terveinek kiteljesítését, de már a gyerekeket is megfelelő egyedi intézményekbe járatja. Ilyen intézmény a BMÓ (Budapesti Műszaki és Gazdaságtudományi Óvoda). Az egyik nap, amikor a gyerekek kint játszottak az udvaron, az őket felügyelő két jómunkásember (óvó néni) rájött, hogy egyre kevesebb gyereket felügyelnek – kiderült, hogy elfelejtették feltelepíteni a legújabb biztonsági frissítést, és valahogy port nyílt a tűzfalon. A felügyelők azonnali igazgatósági értekezlet összehívása mellett döntöttek, ahol rövid tanácskozás után arra a döntésre jutottak, hogy az óvó nénik feladata lesz, hogy visszahozzák az elcsatangolt kölyköket. Egyszerre legfeljebb két óvó néni tud kinn a terepen lépkedni, a többiek a benn levőkre vigyáznak.

2006-ot írunk. A Cubus Sapiens faj nemcsak elkezdte világhódító terveinek kiteljesítését, de már a gyerekeket is megfelelő egyedi intézményekbe járatja. Ilyen intézmény a BMÓ (Budapesti Műszaki és Gazdaságtudományi Óvoda). Az egyik nap, amikor a gyerekek kint játszottak az udvaron, az őket felügyelő két jómunkásember (óvó néni) rájött, hogy egyre kevesebb gyereket felügyelnek – kiderült, hogy elfelejtették feltelepíteni a legújabb biztonsági frissítést, és valahogy port nyílt a tűzfalon. A felügyelők azonnali igazgatósági értekezlet összehívása mellett döntöttek, ahol rövid tanácskozás után arra a döntésre jutottak, hogy az óvó nénik feladata lesz, hogy visszahozzák az elcsatangolt kölyköket. Egyszerre legfeljebb két óvó néni tud kinn a terepen lépkedni, a többiek a benn levőkre vigyáznak.

A gyerekek mind szeretik a csokoládét, ezért egy 3-jegyű bináris szelettel (később 3Bit vagy csoki) könnyen rávehetők a visszatérésre. Az óvó nénik 5 szelet csokit tudnak egyszerre magukkal vinni, a készlet a pályán található csokiautomatákból pótolható. Ilyen automaták szerte a környéken vannak, valamint az óvodában is található egy. Ha egy óvó néni egy gyereknek csokit ad, az sorba áll az óvó néni mögött, így egy óvó néni egyszerre akárhány gyereket is vezethet.

A környéken szokott kóborolni minden nap Watch, a dog. Watch időnként minden látható ok nélkül elkezd ugatni, amit a BMÓs gyerekek nem szeretnek, és elszaladnak előle. A BMÓ környékén csokiautomaták és üzletek is fellelhetők – köztük a gyerekek kedvence, a PlayStation Cubus játékbolt, aminek előszeretettel nézegetik a kirakatát. Ha elmennek egy ilyen üzlet mellett, bizonyos gyerekek nekiállhatnak bámulni a kirakatot, aminek hatására elengedik a szomszédaik kezét, így megtörve a sort. Előfordulhat az is, hogy egy gyerek egy csokiautomatát kezd el bámulni. Ekkor is elengedi a szomszédai kezét.Egyéb épületek is lehetnek a pályán – a játékban annyi a szerepük, hogy sem az óvó nénik, sem a lurkók, sem Watch nem mehet keresztül rajtuk. Ezen felül lehetnek még utak és ligetek – ezek adják meg a lehetséges haladási irányokat.

A játékosok óvó néniket irányítanak, és céljuk az, hogy minél több elkóborolt lurkót kísérjenek vissza a BMÓra.

A csapat: Cubus Sapiens

A csapatban mindvégig maximálisan megvalósult a munkamegosztás a kódolás és dokumentáció terén a modern eszközök (cvs, wiki) révén. Mindazonáltal mindenki hozzájárult még ezen felül azzal az egyedi tálentumával, amire ő utánozhatatlanul képes. Íme a tagok (szigorúan alfabetikus rendezés szerint):

Grill Balázs Levente ‘Balage’

Az alázat és alkalmazkodóképesség megtestesülése. Photoshop-kezelő képességeit a GTA első részeit is lefőző játékgrafika megalkotására fordította.

Harmath Dénes Izidor ‘thSoft’

Gugli bácsi segítségével ő próbált minél kevésbé ízléstelen ikonokat keresni és találni a kezelőfelület csinosítására. A team dicsőségét megörökítendő, annak monogramját beleszerkesztette a 3. pályába. (Reméli, hogy az utókor ezt nem valamiféle kódszegmensre vagy sztrájkellenes játékprogramra való utalásként értelmezi.)

Kishonti István ‘Overander’

Az objektum-orientált szépség következetes képviselője. Mennyiségi rekordot állított fel az egy use-case diagramon elhelyezett use-case-ek, valamint az egy pályán szabadon eresztett lurkók számában.

Ujhelyi Zoltán ‘Stampie’

Őreá hárult a csapat rugdosásának hálás feladata és az egyéb adminisztrációs teendők. A team webmestereként ő lőtte be fáradságos munkával a fent említett online toolokat a hatékony csapatmunka érdekében. Az OpenOffice.org és a MagicDraw varázslatos világában elmerülve világcsúcsot állított fel a “Legtöbb Wiki Oldalból Generált PDF”, illetve a “Legtapasztaltabb UML-Diagram Készítő” kategóriákban.

Letöltés

Az utókor okulására a program fejlesztésének különböző fázisait is közzétesszük. Felhívjuk azonban a figyelmet arra, hogy bár az itt szereplő anyagok javarészt teljesítették a követelményeket, ezek fenntartások nélküli lemásolása, bizonyos (utólag már általunk is kevésbé bölcsnek ítélt) tervezői döntések átvétele nem javasolt, figyelembe véve a félévek egyedi feladatspecifikációt és projekt követelményeit.