Tags:
create new tag
, view all tags
-- 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!

Topic revision: r6 - 2011-12-20 - arpad
 
This site is powered by the TWiki collaboration platformCopyright © 2008-2012 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback