-- Main.arpad - 2011-12-13
GWT alaklamzás fejlesztése ECLIPSE IDE-vel.
A feladat:
Határidő: december 22
Fontos: Amennyiben a házi feladatát GWT technológiával írja meg, akkor ezt a feladatot nem ezzel technológiával, hanem a ZK-val kell megoldania (erről volt szó a dec 6-i órán)
Aki biztos a dolgában (van legalább három jól sikerült kis ZH-ja), nem köteles beadni e feladatokat, de ha szépen megoldja, nemcsak a kisZH-átlagát javíthatja,
hanem okosodik is.
december 22.-ig adjon be egy olyan GWT alkalmazást, amely az alábbi mintaprogram alapján:
- tartalmaz egy entitást, és annak kezeléséhez szükséges JPA perzisztencia-réteget
- lehetővé teszi az entitások kilistázását egy listában vagy grid-ben. A lista legalább egy oszlopban tartalmazzon valami nem szöveges mezőt (pl. képet, gombot, szöveges input mezőt).
- a listából valamely elem kiválasztható, és a megjelenő popup-ban az entitás részletek megjelennek.
Ez edig
egy KisZH-t ér.
A feladatot kiegészítheti további extrákkal, amitől az egy második
kisZH-ként is beszámítható. Az alábbi extrák közül legalább kettőt kell megoldania:
- a táblázatos megjelenítő új entitások létrehozását és törlését is támogatja
- a táblázatos megjelenítő lapozható (ha sok elem van benne)
- a táblázatos megjelenítő szűrhetó
- a táblázatos megjelenítő megjeleníítési sorrendje változtatható úgy hogy kiválaszthatjuk a sorbarendezési oszlopot
- az inputokra constraint-eket definiál
A feladatot a subversion-ba csekkolva, egy
andras.horvarth@netvisor.hu (Cc. Bakay Árpád) címre írt levéllel kell beadni. A levél tartalmazza az indításhoz esetlegesen szükséges passwordoket.
Tutorial:
Ezen az órán drasztikusan változtatunk az alkalmazottt technológiákon....
1. Telepítse az Eclipse Java EE fejlesztői kiadását (az órai website-ról is letöltheti a két legfrissebb változatot. Az indigóval ki let próbálva az alábbi folyamat, a juno-ról nincs információ).
Fontos tanácsok a JDeveloperhez szokott Ecllipse újoncoknak
(hozzászólásokat, kiegészítéseket köszönettel veszek)
- Az Eclipse nem menti el magától a fileokat, így futtatás előtt nekünk kell erre gondolni.
- Az Eclipse a fordítási hibákat nem mindig mutatja meg a futtatáskor. Ehhez célszerű megnyitni a 'Problems' státusz-ablakot, amit ravaszul eldugtak a Window > Show View > Other > General menüpontok alá.
- Nem sikerült kiderítenem, hogy mitől gondolja az Eclipse hogy egy ismeretlen osztályhoz elkészíti az importokat, és néha miért nem. Ekkor ide-oda kattintgatni kell, és előbb utóbb megjelenik ez a lehetőség.
2. Telepítse bele a GWT-t, az alábbi link alapján
• Az Preferences/Install&Update/Available software sites alát fel kell venni a
http://dl.google.com/eclipse/plugin/3.7 siteot, pl. GWT névvel.
• Utána a Help/Install new software alatt a GWT-t 'Site-ot' kiválasztva, telepítsük a GWT három komponensét (az Androidot kivéve).
• Ez eltart egy ideig...
• Majd újra kell indítani az Eclipse-et
3. Most létrehozhat egy GWT projektet. Ehhez a toolbar-ban levő 'g' ikon kényelmes lehetőséget kívál. Itt kell egy új Web App. Projektet létrehozni. Ez kényelmesen elkészít minden nélkülözhetetlen komponenst az alkalmazásba (úgy, mint a webAppCreator tool, a GWT command-line használatánál.)
- Figyeljünk arrra, hogy egyelőre NE kérjük a Google App Engine használatát, mert az az adatbáziskapcsolatunk kiépítésében bezavar majd.
- Viszont az a jó, ha legeneráltatjuk a Project Sample code-ot.
4. A programot ki is próbálhatjuk:az új projektet futtassuk mint Web Application-t, majd a browserünket irányítsuk a megjelenő linkre.
5. Most egy időre elfelejtjük a GWT-t és elkészítjük a JPA alapú adatbázis/ORM réteget. Ehhez a projekt context menüjéből a configure/convert to JPA projekt... -et kell futtatni, amely 'JPA-sítja' a GWT projektet.
- Első lépésként rákérdez, hogy mely 'facet'-eket akarunk élesíteni, itt már rögtön ki van választva a JPA. Tovább, tovább....
- A harmadik panelen meg kell adni egy JPA library technológiát, ez lehet a mostanában standardnak számító EclipseLink, amit szerencsére tartalmaz is az Eclipse EE verziójaa.
- Valamit meg kell adni egy adatbázis-kapcsolatot, amit most elkészíthetünk. Feltételezve hogy oracle-t választunk, meg kell adni a kapcsolódás paramétereit, és emellett az Oracle JDBC drivert is, ami az órai websoiteról letölthető.
6. A JPA-sított projektünkkel első lépésként csináljunk entitásokat az adatbázis egy-két táblájából project context menü JPA tools /Generate Entities from tables funkciójával. A generált osztály célszerűen a 'shared' package-be kerüljön (gondolva arra, hogy egyszer majd a kliens oldalra is átkerüölnek ezek az entitások). JDeveloperen edzettek számára nem okoz nagy meglepetést, legfeljebb kis csalódást, ui. a generált entitás kevésbé komplett, mint a JDeveloperrel készült.
- Egyrész tnekem kézzel kellett megannotálnom (@Id annotációval) a primary key-t. Ezt nyugodtan kitalálhatta vona a táblából.
- Másrészt pedig semmiféle NamedQuery-t nem generált hozzá, még a szokásos findAll()-t sem
7. Most jön a perzisztencia - unit elkészítése. Ez elég elegánsan támogatott, ui. az 5. pont szerinti JPA-sítással készült egy persistence.xml file, amihez elég kényelmes konfiguráló felület is jár (a source view mellett). Ha az Eclipselink mellett maradtunk, akkor csak a következő dolgokat kell kitölteni:
- A persistence unit neve (ez default a projekt neve)
- A managed osztály(ok), azaz az előbb készített entitás osztályok.
- A 'connection' tabon először is a p.unit típusát itt is át kell állítani "RESOURCE_LOCAL"-ra. Mivel az alkalamzásszerverünkben nincsenek datasource-ok (<jta-data-source> <non-jta-data-sourca>), ezért <parameter>-ek formájában kell beállítani az adatbázis kapcsolatot. Szerencsére van egy 'populat ffrom Connection' funkció, amely magátol beállítja a legtöbb adatott.
- Ha már dolgozunk a persistence.xml-en, tanácsos beállítani a logging level-t (javax.persistence.logging.level property) is valami érzékeny fokozatra (FINE v. FINEST).
- A source nézetben ellenőrizhetjük,. jól dolgozott-e az Eclipse.
8. A következő lépés a DAO elkészítése. Itt visszautalok az előző órára ui. a DAO lényegében ugyanaz mint bármely más WAR fileban. Aki azt megcsinálta, akár át is másolhatja azt a file-t, a többiek elolvashatják ott, hogy mi az, és hogyana készül. Fontos, hogy a DAO-t a 'server' package-be definiéljuk.
9. A következő lépés, hogy kipróbáljuk a perzisztencia réteget. Legyünk óvatosak, és először mint egy standard Java SE alkalmazást futtassuk le. Készítsünk egy public static main() függvényt valahová (célszeráen a DAO-ba), ami valami triviális adatműveletet (pl. e find-ot végez) az adat-rétegen (azaz készit egy
EntityManagerFactory-t, abból egy
EntityManagert, és azt használja). Ne feledje, hogy megint resource_local működünk, azaz az em tranzakció-kezelését manuálisan kell elvégezni (em.getTransaction.begin()/commit()).
10. Most ugyanezt próbáljuk el Web-alkalmazásként is.
Először az automatikusan generált GWT szerver oldalát a 'GreetingServiceImpl' módosítsa úgy, hogy amikor a remote hívást kiszolgálja, akkor használja a DAO valamely szolgáltatását (pl. myDao.getInstance().getAllXXX().size() kiszámításával). Ha ez fut, a perzisztencia réteg működik.
Nem fog! Ugyanis az Eclipse olyan bugyuta (vagy szándékosan ilyen?), hogy az utólagosan hozzáadott library-kat (azaz a JPA providert és JDBC drivert) nem telepíti fel a WAR struktúra WEB-iNF/lib-jébe. Ezeket kézzel kell be másolni oda, (konkrétan: a ojdbcXX.jart, az eclipselink.jar-t ill, a javax.persistence_2.xxxxxx.jar-t).
Ezután már futni kell az alkalmazásnak. (A 4. pontban leírtak szerint indítva.)
11. Eddig tehát oda jutottunk, hogy működik a GWT példaalkalmazás, és hozzá kapcsolva az adatelérő backend (a DAO és az entitás). Következő lépésként alakítsuk át a RPC interfészeket (normál és aszinkron), ill. az implementációt, céljainknak megfelelően. A példaprogramban egyeteln metódus, a getGyerekek() került bele. FIgyeljük meg, hogy az entitásunkat, azaz shared.Gyerek osztály a GWT kliens kódban is gond nélkül használhatjuk, azaz szerencsére nem kell egy külön "Data Transfer Object"-et definiálnunk. (Ezt annak köszönhetjuk, hogy az ENtitások POJO-k).
12. Most jön a legnehezebb rész, a GUI teljes átalakítása.
13. ELőször kezdjük a war könyvtárban levő HTML-el. Itt két lehetőségünk van: vagy tervezünk valami kis keret-html-t, és abban csak bizonyos elemek (jlelemzően <div>-ek) lesznek GWT-sek, vagy lényegében üresen hagyjuk a <body>-t (az automatikus <iframe>-et ill. <noscript> tag-eket leszámítva), és akkor az egész kliens ablakor a GWT GUI tölti majd ki. A példaprogram az előbbi megoldást választotta. A GWT-re kijelölt div neve maindiv, így ehhez fogunk elemeket adni a GWT inícializálásakor.
14. Forduljunk tehát a GWT
EntryPoint-ot implementáló osztályhoz a client package-ban (pl. a plédaprogramban Rg3.java). Az ilyen
EntryPoint implementációkban az onModuleLoad() metódusban szoktak történni a legizgalmasabb dolgok, akár az egész alkalmazáslogika is összeállítható itt.
Mindenekelőtt itt készül el az alkalmazás GUI struktúráját meghatározó, egymásba ágyazott panelek halmaza. Esetünkben ez egy
- VerticalPanel, amiben
- Egy scrollPanel benne
- Valamint előkészítünk egy FlexTable-t is de ez még nem kerül a főpanelhez hozzáadásra (csak ha már lesz mit megjeleníteni).
- A Flextable-en belül Labelelók és TextBox-ok ismétlődnek
- Valamint egy gomb, amivel el lehet tüntetni a FlexTable-t
- Ugyancsak definiálunk egy DialogBox-ot, amelyet majd később kiegészítünk és megjelenítünk, de persze kezdetben ez is láthatatlan.
A fenti widgetek egymásba rendezése és felkonfigurálása elég egyértelmű, mondjuk a
CellTable-t kivéve. Említésre méltó még a
FlexTable Button-ja, ahol egyrészt elég körülménhyesen lehetett beállítani a COLSPAN és ALIGN_CENTER megjelenést, másrészt pedig egy egyszerű eseményvezérlő is kerül rá.
15. A
CellTable azonban így is elg bonyolulttá teszi az alkalamazást.
- Viszonylag egyszerű dolgunk lenne, ha csak szövegeket akarnánk megjeleníteni minden cellában. Ekkor a legyegyszerűbb oszlop-típust, a TextColumn-ot használnánk (pontosabban használjuk is az első oszlopban). Itt a TextColumn egyetlen metódusát kell átírni, azt, ami táblázat adat-entitásaiból kigenerálja a cellák szövegét.
- A második oszlop két szempontból izgalmasabb. Egyrészt itt egy imageResource-ot szeretnénk megjeleníteni (azaz a gyerek fotóját). Ehhez kell definálni egy cella-kirajzőló objektumot (icell), majd annak felhasználásával egy generikus Column-ot adhatunk a táblázathoz. Az icell tehát egy objektum, ami az egyes cellák megjelenítését vezérli.
- Szemben a szöveges oszloppal, a Column getValue-ja ez esetben ImageResource-okat ad vissza, amiről alább lesz szó.
- A cella kirajzoló objektumra eredetileg lett volna egy egészen készenfekvő beépített osztály, az ImageResourceCell, azonban ez -a GWT egy sajátos furcsasága miatt- nem lett volna képes a képeket a cella kívánt méretéhez skálázva megjeleníteni. Ezért a generikus AbstractCell-t használtuk, amelynek render metódusában mi magunk készítjük el a cellába kerülő HTML kódot. Szerintem ez elég kiábrándító a GWT egyszerűnek hirdetett programozási modelljéhez képest.
- Az AbstractCell használatát az is idokolja, hogy kezelni szeretnénk itt a "click" eseményt (érdekes módon az ImageResourceCell-el ezt se lehetett volna). Ezt egyrészt a konstruktorban kellett megjelölni, másrészt pedig az onBrowserEvent metódus felüldefiniálásával. E metódus kitölti és megjeleníti a dialógus-ablakot.
- A harmadik oszlop estén egy 'hivatalos" GWT megoldással a CompositeCell-el próbálkoztam, ez egy olyan cella, amely további cellákat -pontosabban a cellákhoz képzett virtuális Column-okat- tartalmaz. Össze kelett állítani három szabályos oszlopot (két TextColumn-ot, és egy ActionCell-t tartalmazó genreikus oszlopot), és ezeket -ahelyett, hogy a CellTable.addColumn-ot() használnánk- a compositeColumn-nak adjuk oda egy lista formájában. Nem tökéletes, ui. pl. a szövegeket összeolvasztva jeleníti meg, de nem akartam tovább bonyolítani a példát.
- Ebben az oszlopban figyelmet érdemel még az ActionCell, aminek egy ActionCell.Delegate osztályban adtuk oda a gombnyomás eseménykezelését. Ebben az eseménykezelőben a fent említett Flex táblázatot hozzáadjuk a GWT GUI főpaneljéhez.
16.Nézzük meg egy pillanatra az
ImageResource-okat is (resources/GyerekKepek.java). A terv az volt, hogy a képeket a klines GUI tartalmazza egy bundle formájában, hogy a megjelenítés gyors legyen. Sajnos az GWT
ImagBundle nem támogatja képek v. más erőforrások tömeges hozzáadását, hanem mindegyikhez generálni kell egy-egy metódust a forrásba. Itt egy shell scripthez folyamodtam, ami a könyvtár összes jpg filejára generál egy metódus-definíciót (a script megtalálható a resource java fileban). Ráadásul a leggyakrabban használt
ClientBundle típus még azt sem támogatja, hogy név alapján keressük meg a resource-okat (azaz fordításkor ismerni kell a resource metódus-nevét), de szerencsére van
ClientBundleWithLookup, ahol már van getResource(String) metódus.
- Még egy megjegyzés: mivel a GyerekKépek.java egy új package-ba került, ezért azt a package-et hozzá kell adni a GWT mechanizmus vezérlő XML filejához: <source path='resources'/>
17. A fentiek szerint összeállított program végül nem kevés szenvedés után elindult.
Apró dolgok, amire figyeljünk
- Az alkalmazást a 4. pontban leírt módon indíthatjuk el. Ha már fut, akkor viszont ne így, hanem a "Development Mode" (GWT-specifikus) státusz-ablak jobb felső sarkán található "Reload WebServer" gombbal érdemes újraindítani.
- Még így is előfordul néha, hogy a console ablakban bind exception jelenik meg, ami arra utal, hogy az előző példány nem állt le rendesen. Ilyenkor az oprendszerből kell kilőni valahogy a "gwt-user.jar"-t tart is tartalmazó java parancsssorokat (Linuxosok előnyben!).
- Ne feledjük el, hogy az Eclipse alapállapotában futtatás előtt mindent nekünk kell elmenteni (Shift-Ctrl-S = "Save All"). Ezt át lehet állítani a Window -> Preferences -> General -> Workspace panelen.
Tanulság a fentiekből
Úgy gondolom, hogy a GWT egy "ütős" technológia, de talán éppen ezért eléggé bonyolult.
- A sok generikus osztály, override-olandó metódusokkal, delegate-ekkel stb. elég sok töprengést jelent. Vegyük észre, hogy a fenti példa még mindig csak egy egyszerű táblázat, amely mellőzi az olyan szokásos extrákat, mint pl. helybéli editálás, oszlopok szerint választható rendezés, vagy lapozás...
- Ehhez társul az, hogy még a GWT dokumnetációja ill. a (sokat magasztalt) Eclipse integrációja sem egészen kiforrott és áttekinthető.
- Nem emlékeztem meg pl. a GWT Designerről, ami ugyen segített megtenni a GUI tervezés első lépéseit, de igen hamar abba kellett hagynom a használatát (mert pl. az ImagCell-t nem ismerte).
Összességében tehát ez nem játék, hanem egy professzionális technológia, kellő türelem és odafigyelés kell a használat elsajátításához.
Magam részéről elhiszem, hogy a Google is ezt használja a GUI-jaihoz, de úgy hogy, ők is nyakló nélkül élnek az override-okkal és egyéb specializációkkal.
Sok sikert Önöknek is!