Kockadobás kliens-szerver alkalmazás

Fejlesszünk elosztott, hálózatos, datagram alapú üzenetküldéssel megvalósított Java alkalmazást!

A kockadobás kliens egyszerre két szabályos dobókockával dob, amit ezerszer megismétel és a dobott számok összegét datagram típusú üzenetküldéssel folyamatosan elküldi a szervernek. A szerver localhost-on fut és egy megadott porton keresztül várja a klienstől. A szerver és a kliens egyaránt szálkezelést alkalmazó konzolos alkalmazás.

A projektben van egy swing GUI-s alkalmazás, amely JFreeChart oszlopdiagramon – folyamatosan frissítve – megjeleníti az összesített adatokat, mindez a szerver üzenetküldésével irányítva (amint beérkezik egy dobott (2-12-ig) összeg).

A kommunikációnak – a lehetőségekhez képes – biztonságosnak és – a hálózati adatforgalmat tekintve – takarékosnak kell lennie! Ennek részeként szükséges egy azonosító és egy egyszerű szabály (protokoll).

Tekintsük át mondatszerűen a szálkezelést használó kliens és szerver kommunikációhoz kötődő feladatait:

Ezek működését összefogja egy központi vezérlőosztály és ez a fejlesztőeszköz projektablakában így jelenik meg (egyetlen MVC Java projektként):

A program két felületen kommunikál. A háttérben konzolosan logol a kliens, és a háttérben futó szerver időnként frissítteti a grafikus felhasználói felületen (GUI, ablak) megjelenő grafikont:

Kockadobás - Java kliens-szerver alkalmazás működésa

Aki kedvet kapott: bátran készítse el a fenti terv/koncepció/specifikáció alapján az MVC Java projektet. Érdemes alaposan tesztelni: külön a szervert, külön a klienst, először indítva az egyiket, majd a másikat, leállítani az egyiket majd fordítva. Átgondoltan indokolni is hasznos, vajon mi, hogyan és miért történik.

A bejegyzéshez tartozó forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java EE szoftverfejlesztő tanfolyam 1-8. óra: Elosztott alkalmazások, webszolgáltatások, szálkezelés, párhuzamosság alkalmához kapcsolódik. Amikor itt járunk a tananyagban, akkor a GUI felület és a grafikon tervezése, megvalósítása már magabiztosan megy, így elegendő a hálózati kommunikációra helyezni a fókuszt.

Naprendszer szimuláció – elméleti háttér

Naprendszer szimulációt terveztünk és valósítottunk meg Java nyelven, amit három részből álló blog bejegyzés sorozatban mutatunk be (ez az 1. rész):

  • Naprendszer szimuláció 1. rész – elméleti háttér
  • Naprendszer szimuláció 2. rész – objektumorientált tervezés (hamarosan, februárban)
  • Naprendszer szimuláció 3. rész – megvalósítás Java nyelven (hamarosan, márciusban)

A Naprendszer szimuláció elméleti háttere

A Naprendszer szimulációhoz elengedhetetlen, hogy ismerjük a homogén koordinátákat, az elemi műveletek egységes megvalósításához szükséges transzformációs mátrixokat, a tömegvonzás elvét és az implementációhoz szükséges MVC modellt.

Homogén koordináták

Számítógépes algoritmusokkal egyszerű a térbeli transzformáció megvalósítása, ha homogén koordinátákat használunk. Segítségükkel az affin transzformációk egységesen kezelhetők. A cél egy egységes matematikai formalizmus alkalmazása. A pontok az égitestek középpontjait fogják jelölni. Legyen a P pont 3D-beli koordinátái: P=(x, y, z). Szükséges egy konstans érték. Ha w≠0, akkor a P pont koordinátái: P=(w·x, w·y, w·z, w). Ha w=1, akkor a P pont normalizált homogén koordinátái: P=(x, y, z, 1). A pontnégyes kijelölése kölcsönösen egyértelmű.

Transzformációk

Koordináta transzformáció során az ábrázolandó grafikus objektum pontjaihoz (tárgypontokhoz) új koordináta-rendszert rendelünk hozzá. Az objektum nem változik (nem torzul, nem változtatja meg az alakját), csupán a nézőpont változik meg. Például: a koordináta-rendszer eltolása, elforgatása, a koordinátatengelyek felcserélése, tükrözése, és a léptékváltás (nagyítás, kicsinyítés, összenyomás, széthúzás), elforgatjuk az ekliptika síkját a szimulált Naprendszerben.

Pont transzformáció esetén a tárgypontokhoz hozzárendeljük azok egy adott szempont szerinti hasonmását. Például: 3D-s tárgyak leképezése 2D-s képre, objektumok eltolása, forgatása, mozgatása, égitestek mozgatása tömegvonzás alapján. Affin transzformációk (egybevágósági és hasonlósági transzformációk) alkalmazása esetén pont képe pont, szakasz képe szakasz, felület képe felület, valamint metsző térelemek eredeti metszésvonala megegyezik azok leképezett metszésvonalával.

A számítógépes grafika területén az affin transzformációk általános alakja (mátrixosan):

A pont a B=(bx, by, bz) vektorral eltolható. A pont – a T=(t11, t12, …, t33) mátrixot használva – adott szöggel elforgatható, skálázható, tükrözhető. A számítógépes grafikában ezt a transzformációs mátrixot a homogén koordinátákkal alkalmazva, az összes geometriai transzformáció hatékonyan megvalósítható, visszavezethető mátrixok szorzására. Mindezt saját magunk is implementálhatjuk, de része a DirectX és OpenGL rendering pipeline-jának is.

Más módon is lehetne: egyenes és ehhez tartozó szög párossal is dolgozhatnánk.

A tömegvonzás elve

A tömegvonzás bármely két égitest között meghatározott, függ a gravitációs állandótól és az égitestek tömegétől egyenes arányban, az égitestek (tömeg)középpontjainak távolságától fordított arányban. Ez a Newton szerinti értelmezés, amelynek képlete:

A hatás-ellenhatás törvénye miatt a vonzás – egymás felé való gyorsulás – kölcsönös, a gyorsulás az égitestek tömegével fordítottan arányos, sosem nulla. A Naprendszerben a bolygók a Nap körül keringenek, és a bolygóknak lehetnek holdjaik. Egységesen kezelve: égitestek.

A tömegvonzásnak más elméleti megközelítései is vannak: Einstein gödör-modellje, Kepler törvényei, illetve differenciál-egyenletrendszer, integrálszámítás is használható a közelítő képlet helyett (csak ideális modell esetén pontszerű az égitest és gömbszimmetrikus azok tömegeloszlása), illetve ismeretes többféle értelmezés a rendszer/modell stabilitására: Lagrange pontok, Lyapunov stabilitás.

Az MVC modell

A klasszikus megközelítés szerint a szoftveres alkalmazások három, egymástól jól elkülöníthető szereppel rendelkező egységből állnak: modell (model), nézet (view), vezérlő (controller). A Java nyelv Swing komponensei az MVC architektúra szerint működnek.

A vezérlő reagál az érkező eseményre, hozzáfér a modell adatszerkezeteihez, azaz igénybe veszi a modell szolgáltatásait, valamint frissítheti a nézetet. A nézet a vezérlő frissítési kérésére a közvetlenül megkapott adatok alapján, vagy a modelltől elkért adatok alapján frissíti saját magát. A vezérlő határozza meg az alkalmazás, komponens, program működését. Egy modellt több nézet is használhat. A modell közvetlenül is üzenheti a nézetnek, hogy megváltozott. A nézet adja a látványt, amelyet angolul skin vagy „look and feel”-nek neveznek.

Táblázatos komponens testreszabása

táblázat logo

táblázat logoA Java programozási nyelv egyik ismert GUI csomagja a swing. Ennek népszerű grafikus komponense az adatok táblázatos megjelenítését biztosító JTable komponens. A táblázatos megjelenítéshez több beállítás is szükséges. A JTable egy MVC komponens, így külön kezelendők a modell, nézet és a vezérlő funkcióihoz kötődő beállítások. A modell tárolja az adatokat például DefaultTableModel típusú objektumban, amiben szétválaszthatók a fejlécben és a többi cellákban található adatok. A nézethez tartozik a betűméret, a cellák színezése, az adatok igazítása, megjelenítése, a gördítősáv. A viselkedést, a felhasználói reakciót a vezérlő határozza meg, például rendezés, görgetés, fókusz, kijelölés, oszlopok sorrendjének cseréje.

Feladat

Készítsünk olyan Java swing-es kliensprogramot, amely tetszőleges adatforrásból (XML vagy JSON a hálózatról, JDBC adatbázis kapcsolatból, ORM leképzésből származó objektumokból) képes az átvett adatok grafikus felületen való táblázatos megjelenítésére JTable komponenssel! Építsünk arra, hogy az adatokon kívül metaadatok is rendelkezésünkre állnak! A megoldás legyen univerzális!

Képernyőképek

OracleHR képernyőkép

Modell

A táblázatos GUI komponenst kezdetben inicializálni kell, illetve a benne tárolt adatok is törölhetők, ha újrahasznosításra kerül a sor:

Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (1. lépés). A metaadatokból a for() ciklus előállítja az oszlopTomb-öt, és az oszlopTipusTomb-be kerülnek az Oracle adattípusból Java objektumtípusként megfeleltetett adatok. Előbbi a fejléc feliratainak szövegeit tartalmazza, és az utóbbi befolyásolja az egyes cellákban az igazítást, illetve hatással van adott oszlop rendezésére is:

Ki kell nyerni a tároláshoz és a megjelenítéshez kötődő adatokat (2. lépés). A while() ciklus végigjárja az eredménytábla sorait és Object típusú tömböt állít elő az összetartozó rekord mezőiből. Ezek először generikus listába kerülnek, majd onnan kétdimenziós Object típusú tömbbe:

Mi indokolja a tömbökből álló generikus lista ( adatLista) alkalmazását?

A while() ciklus végrehajtása előtt nem tudjuk lekérdezni, hogy mennyi rekordot kaptunk vissza, így nem tudjuk rögtön az adatTomb-be tenni az adatokat. A Java nyelvben a tömbök mérete fix, és a deklaráció során meg kell adni. Az eredménytábla metaadatai között megtalálható a mezők száma, ami felhasználható a kétdimenziós tömb oszlopszámaként. A generikus lista dinamikus, annyi elemből fog állni, ahány lépésben végrehajtódik a while() ciklus. Ezután a listától lekérdezhető az elemszáma ( adatLista.size()), és ezzel megvan a kétdimenziós tömb sorainak száma, ami eddig hiányzott. Persze használhatnánk Vector-t is a tömbökből álló generikus lista helyett (mert a DefaultTableModel-nek van olyan túlterhelt konstruktora, ami átvenné paraméterként), de ezt inkább nem tesszük, hiszen a Vector már régóta obsolete kollekció.

Előállítjuk a vizuális komponens mögötti adatmodellt. Öröklődéssel kiegészítjük két hasznos függvénnyel, így cellák rajzolása/renderelése és rendezése megkaphatja a szükséges adattípust ( getColumnClass()), valamint letiltható a cellák szerkeszthetősége ( isCellEditable()). Utóbbiak inkább a vezérléshez kötődnek, de modellen keresztül itt és így kell beállítani:

Végül a vizuális komponens mögötti adatmodellt kell átadni:

Nézet

Adott betűtípus, betűstílus és betűméret használható a táblázat fejlécében, celláiban, illetve a betűmérettől függhet a sorok magassága:

Hasznos ha JScrollPane típusú gördítősáv tartozik a táblázathoz, így dinamikusan megjeleníthető/elrejthető a függőleges/vízszintes gördítősáv:

Vezérlés

Az adatokhoz valahogyan hozzá kell jutni. Most JDBC kapcsolatot használunk és az Oracle HR sémából kérdezünk le adatokat, de a forráskód-részlet univerzális. A folyamat a következő:

  • Betöltjük a driver osztályt.
  • Autentikációval c kapcsolatot nyitunk az adatbázis-szerver felé.
  • Végrehajtjuk a lekérdező SQL parancsot.
  • Feldolgozzuk az eredményül kapott ResultSet típusú rs objektumot.
  • Végül lezárjuk a c hálózati kapcsolatot.

Ha engedélyezzük, akkor a megjelenő táblázat fejlécében az egyes oszlopok felirataira kattintva elérhetjük, hogy az adott oszlop típusának megfelelően növekvő vagy csökkenő sorrendbe átrendeződjenek az adatok:

A kivételkezelést nem részleteztük a fenti forráskódoknál, de természetesen kötelezően adott.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon, illetve Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalmaihoz kapcsolódik.

Letöltés szimuláció

letöltés logó

letöltés logóLetöltési folyamatot szimulálunk. A paraméterek rugalmasan beállíthatóak. Előre beállított mennyiségű adatot, párhuzamos szálakon/folyamatokon keresztül töltünk le, miközben mérjük az eltelt időt. A folyamatok állapota lehet inaktív, aktív és befejezett. Az aktív folyamatok esetében megjelenő százalék fejezi ki, hogy a folyamat hol tart a rá jutó részfeladat végrehajtásával. Összesített formában követhetjük a hiányzó és a letöltött adat mennyiségét MB-onként és százalékosan is. A folyamat szimulációjához grafikus felületű Java kliensprogram készült, egyszerű GUI komponensekkel (nyomógomb, címke, folyamatindikátor, másképpen JButton, JLabel, JProgressBar swing komponensek).

Az alábbi animáció bemutatja a letöltés szimulációját:

letölés szimuláció

A konkrét paraméterek: 128 MB-nyi adatot töltünk le 256 párhuzamos szálon/folyamaton keresztül, így egy-egy részfeladat 0,5 MB-nyi adat letöltését jelenti. Minden értéket/mérőszámot egész számként ábrázolunk, akár százalékhoz tartozik, akár mértékegységként MB vagy s. A változások – és egyben a frissítés is – 5 ezredmásodpercként történnek a GUI-n.

A Java SE szoftverfejlesztő tanfolyamunkon, a szakmai modul Objektumorientált programozás témakörét követő 29-36. óra Grafikus felhasználói felület alkalmain már tudunk egyszerűbb szimulációs programot tervezni, kódolni, tesztelni. A Java EE szoftverfejlesztő tanfolyamunkon, a szakmai modul 5-8. óra Szálkezelés, párhuzamosság alkalommal többféle elosztott stratégiát ismertetünk, és a 17-24. óra Socket és RMI alapú kommunikáció alkalommal pedig megvalósíthatjuk többféle protokoll szerint a hálózati kapcsolatot, letöltést/feltöltést.

Elosztott alkalmazások esetén többféleképpen is modellezhető és kialakítható a rendszer architektúrája. Elosztott lehet maga a hálózat, a számítási folyamat, az algoritmus. Elosztott objektumok kommunikálhatnak egyenrangúnak tekinthető P2P szerepkörben vagy szerver/kliens oldalon, és több dolog/elem/hardver/szoftver/komponens együttműködéseként is megvalósulhat elosztott alkalmazás. A hálózati kommunikáció folyamatát valamilyen protokoll határozza meg, amit minden komponens ismer és így meghatározott szabályrendszer szerint működik.

Hardver szinten elosztottak a többprocesszoros rendszerek. Szoftveresen elosztott például egy moduláris vállalatirányítási rendszer, illetve a mobilalkalmazások többsége. Tipikus háromrétegű webalkalmazás esetén külön szerver nyújtja az adatbázishoz kapcsolódó szolgáltatásokat, a felhasználó számítógépén található a böngészőben futó/megjelenő kliensprogram/weboldal és a kettő között a felhő rétegben lehet a funkcionálisan elosztott alkalmazáslogika (például validálás, titkosítás, tömörítés, autentikáció, autorizáció).

A vezérlést megvalósító részlet a Java forráskódból:

 A szimuláció elvi szinten:

  • a folyamatok generikus listában vannak,
  • időzítő által meghatározottan, gyorsan és ismétlődve történnek az időzített lépések,
  • ha egy folyamat befejeződik, akkor kikerül a generikus listából,
  • ha a folyamatok generikus listája kiürült, akkor vége a szimulációnak,
  • ki kell választani véletlenszerűen egy folyamatot, léptetni kell véletlenszerűen, amíg be nem fejeződik,
  • folyamatosan nyilván kell tartani a szükséges adatokat a háttérben,
  • folyamatosan frissíteni kell a felhasználói felületet.

Haladóbb megközelítésben másképp is lehetne: a számítási műveletek redukálhatóak lennének, ha lenne egy – minden olyan adat karbantartásáért felelős – modellobjektum, amelynek adatai hozzá lennének rendelve a GUI komponensekhez. Aki már sejti, annak megerősítem, hogy igen, ez observer (megfigyelő) tervezési minta.

A feladat könnyen általánosítható, például:

  • Egy keresési feladatot oldjunk meg az állományrendszerben! Kereshetünk egy konkrét nevű fájlt, adott kiterjesztésű fájlt, joker karakterekkel paraméterezett nevű fájlt/mappát, adott méretű állományt, adott dátum előtt létrehozott fájlt… Az állományrendszer bejárása rekurzív módon történik. A gyökérben lévő mappánként külön, esetleg második szinten lévő mappánként külön indíthatók szálak, párhuzamos folyamatok. Ha egyetlen találat elegendő, akkor bármelyik szál pozitív visszajelzésére minden szál leállítható. A feladatnál nagy eséllyel nagyon különböző méretű mappákon és eltérő mélységű mappaszerkezeteken kell végighaladni, így erre érdemes lehet optimalizálni, de ez már nagyon más szintje ennek a problémának.
  • Active Directory szerkezetben keressünk elérhető nyomtatókat a hálózaton!
  • Elosztott számítási hálózatként működik/működött a SETI@home. Koncepciójának lényege, hogy egy hatalmas feladatot nem nagyon drága szuperszámítógépeken, hanem olcsó gépek ezrein, százezrein, vagy akár millióin végeztetjük el, amelyek jelentős szabad kapacitással (pl. processzoridővel, átmeneti tárhellyel) rendelkeznek és egyébként is csatlakoznak a világhálóra.
  • Hasonlóan elosztott működésű a torrent protokoll. A kliensek/szálak az állományokat több kisebb darabban/szeletben töltik le, természetesen párhuzamosítva. Minden csomópont megkeresi a hiányzó részhez a lehető leggyorsabb kapcsolatot, miközben saját maga is letöltésre kínálja fel a már letöltött fájldarabokat. A módszer nagyon jól beválik nagyméretű fájloknál, például videók esetében. Minél népszerűbb/keresettebb egy fájl, annál többen vesznek részt az elosztásában, ezáltal a letöltési folyamat gyorsabb, mintha mindenki egy központi szerverről töltené le ugyanazt (hiszen az informatikában minden korlátos, a sávszélesség is).
  • A képtömörítést végző algoritmusok is lehetnek elosztottak, ezáltal párhuzamosíthatóak. Például ha felosztjuk a képet 16*16-os méretű egymást nem átfedő részekre, akkor ezek egymástól függetlenül tömöríthetők.
  • A merevlemezek esetén korábban használatos defragmentáló szoftverek felhasználói felülete emlékeztet a mintafeladat ablakára.

Fontos szem előtt tartani, hogy a grafikus megjelenítés csupán a szimulációhoz tartozó – annak megértéséhez szükséges – reprezentáció, így teljesen független lehet a folyamatok valós működésétől.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Skandináv lottó demóprogram

Skandináv lottó (heteslottó) demóprogramot tervezünk és írunk meg Java nyelven. Lépésenként mutatja meg, hogy mi történik a háttérben: hogyan állítja elő véletlenszerűen a lottószelvényt.

Az emlékezet egy logikai tömb. Ebben 36 elem van. A nulladik elem nem számít, és legyen a többi elem (1-35-ig indexelve) kezdetben mind hamis. A cél: legyen a tömbben pontosan 7 db igaz érték. Másképpen: a logikai tömb a lottószelvényen megjátszható számok kiválasztottságát jelöli, igen vagy nem. A heteslottó-szelvény 7 db 1 és 35 közötti különböző egész számból áll.

Mindig 1 és 35 közötti egész véletlenszámot tippelünk. Kezdetben jóSzámDb=0. Az első tipp biztosan jó és jóSzámDb=1. A többi tipp esetén vizsgálni kell, hogy már kiválasztott-e. Ha igen, akkor nincs teendőnk. Ha nem, akkor meg kell jegyezni (kiválasztottá kell tenni, azaz igazzá kell állítani a logikai tömbben) és a jóSzámDb++ (növelhető). Mindezt ciklusban ismételjük, amíg a jóSzámDb<7 feltétel teljesül (másképpen: amíg nincs elegendő kiválasztott szám a szelvényen). Mindez biztosítja az egyediséget, különbözőséget. Ha jóSzámDb==7, akkor kiírjuk a lottószelvényre kerülő számokat az alapján, hol (melyik indexen) van a logikai tömbben igaz érték.

Tekintsük át az alkalmazott módszer hátrányait és előnyeit. Hátrány, hogy 36 logikai érték szükséges ahhoz, hogy 7 különböző számot előállítsunk. Előny, hogy egyszerű az algoritmus (nem kell keresés és megszámolás programozási tétel) és nincs szükség rendezésre sem, mert a szakterületre jellemző „emelkedő számsorrend” a logikai tömb bejárásával önkéntelenül is adódik. Hangsúlyozzuk, hogy ez csupán egyetlen módszer a nagyon sok izgalmas közül, amikkel generálható egy véletlenszerű lottószelvény.

A megvalósítás, Java forráskód egyszerű. Íme egy függvény, amely visszaadja azt kiválasztottságot jelölő logikai tömböt, amiből megfelelően indexelve kiíratható a véletlenszerűen generált lottószelvény:

Egy demóprogram, szimulációs program, oktatóprogram esetén nem is a konkrét feladat megoldása a cél/probléma. Sokkal inkább a lépésenkénti bemutatás, sok-sok konzolos kiírással vagy grafikus szemléltetéssel. Sokszor időzítővel késleltetjük, lassítjuk, gyorsítjuk a folyamatot, de előfordul az is, hogy rengetegszer megismételjük a tevékenységet és a kapott adatokat elemezzük, következtetünk belőlük. Most például a ciklust ki kell cserélni olyan léptetésre, ami a felhasználó kattintásához kötődik. Ha kéri a következő tippet a lottószelvényre, akkor megkapja. Ha nem kattint, akkor nem kapja meg. Az is egy csalás/lehetőség lenne, hogy a háttérben nem is logikai tömb adatszerkezet van, csupán a vizualizáció miatt tűnik annak.

Az elkészült demóprogram megvalósítja a fenti algoritmust. Az alábbi képernyőképeken végiglapozható a demóprogram működése. Nem is az algoritmus megvalósítása a kihívás és a cél, hanem a folyamat lépésenkénti megjelenítése. Java swing grafikus felület készült el.

 

A demóprogram Start állapottal indul. Olyan a lépésenként tesztesetek sorozata, hogy a lottószelvény nem sikerül rögtön elsőre. Az egyik szám már előfordult korábban. A demóprogram Stop állapottal ér véget. A demóprogram pénztárszalagszerűen időnként jelzi, hol tart éppen. A demóprogram képes egymás után több lottószelvényt is előállítani és az emlékezete egyetlen szelvényre korlátozódik.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A Java SE szoftverfejlesztő tanfolyamunkon, a szakmai modul Objektumorientált programozás témakörét követő 29-36. óra Grafikus felhasználói felület alkalmain már tudunk egyszerűbb szimulációs programot tervezni, kódolni, tesztelni. A Java EE szoftverfejlesztő tanfolyamunkon, a szakmai modul 33-40. óra Java Server Pages alkalmain már a program böngészőben futó változatát is el tudjuk készíteni.