- temavalaszt.sql a TemaValaszt adatbázis létrehozásához szüséges dump-file - a projekt NEM tartalmazza a Struts2 használatához szükséges .jar fileokat (töröltem őket, mivel így nem tudtam volna feltölteni otthonról a projektet, a feltölthető állományok méretének korlátozása miatt...) - a szükséges csomagot le lehet tölteni az Apache hivatalos oldaláról: http://struts.apache.org/2.x/index.htm ****** URL: /temaValaszt/login.action ****** Megjegyzések: - a struts.xml konfigurációs állományban megtekinthető, hogy két action van megadva: - login: ennek kétféle eredménye lehet - success (sikeres bejelentkezés esetén a choose action kapja meg a vezérlést) - input (visszairányítás az input.jsp-re, pl. hibás bejelentkezési adatok megadásakor) - choose: ennek is kétféle eredménye lehet - input (ami a temaLista.jsp-re irányít) - error (hiba esetén visszairányítunk a login action-höz) - a login.jsp oldalon az s:form elem submit gombjának attribútum-értékeként megadott "login!verify" szintaxis jelentése: a login nevű action verify metódusa (NEM az alapértelmezett execute()) fogja kezelni az illető kérést - a jsp oldalakon szereplő szövegek erőforrás-állományból (TemValaszt.properties) vannak kinyerve. Ennek az erőforrás-állománynak az elérési útja a struts.properties állományban van beállítva ****** A projektben az osztályok elnevezésére használt névkonvenció értelemszerűen: ...BL - üzleti logikát implementáló osztály ...DAO... - adatbázis-hozzáférési réteg tartozéka ...JDBC... - a DAO interfészek konkrét JDBC implementációja ...Action - Action osztály ...Bean - JavaBean - egy-egy action-höz tartozó elemek egy csomagban vannak (lehet természetesen más logikát is alkalmazni, de jó, ha van valamilyen rendszeresség) ****** Adatbázis-hozzáférés: - az alkalmazás connection pooling-ban résztvevő kapcsolatobjektumot használ, lásd a context.xml konfigurációs állományban megadottakat, melynek alapján a Tomcat létrehozza a megfelelő DataSource osztályt. (nem, mintha ez a konkrét alkalmazás különösebben ígényelné az ilyen fajta kapcsolatobjektum-készlet használatát, viszont kurzuson megbeszéltük, hogy egy komolyabb web alkalmazás esetén miért fontos.) - a kapcsolat zárása finally blokkban történik, amikor épp nincs szükség már rá - DAO: a ... .dao csomagban szerepelnek az egyes adatbázistábláknak megfelelő DAO interfészek, melyek az üzleti logika számára szükséges műveleteket biztosítják, (illetve a DAOFactory absztrakt osztály). A ... .jdbc csomag pedig az interfészek konkrét JDBC implementációját tartalmazza. ****** A temaLista.jsp oldalon meg lehet figyelni az OGNL-kifejezések használatát: - az OGNL kontextusában (Struts2-ben ez az ActionContext objektumnak felel meg) azokat az objektumokat, amelyek nem az ún. érték-veremben (value-stack) vannak (mint pl. az aktuális Action osztály) a "#" szimbólum segítségével érhetjük el: pl. #session.loggedStudent.userName (a szesszióba loggedStudent néven lementett, bejelentkezett felhasználó felhasználóneve), bár ezt az adatot történetesen a Action osztály session nevű mezőjén keresztül vagy a beállított personBean-ből is elérhettük volna... (lásd lennebb, az interceptorokról szóló leírást) - %{...} közé írjuk az OGNL kifejezést, amennyiben ki szeretnénk azt értékeltetni lásd pl.: Mivel a Struts2 elemkönyvtár set eleme kifejezést vár a value attribútum értékeként, ezért ha egyszerűen annyit írnánk, hogy value="black", akkor a kifejezés kiértékelő elkezdene keresgélni egy black nevű változót az érték-veremben (getBlack()...), így viszont egyértelmű, hogy a 'black' stringről van szó. - Az érték-verem tetején eleinte mindig az Action osztály van. Így pl. a Struts2 elemkönyvtár alábbi elemében a "list" kifejezés segítségével az Action osztály list mezőjének értékét kapjuk meg (meghívódik az Action osztály getList() metódusa). (megj.: A ChooseAction execute metódusában hívódik meg az üzleti logikát implementáló ChooseBL osztály getList() metódusa, ami feltölti ezt a listát). Továbbá az iterator elem ki fogja értékelni a törzsét annyiszor, ahány elem a value értékeként megadott listában van, és minden egyes iteráció alkalmával az érték-verembe helyezi (az Action osztály fölé) a lista aktuális elemét (az aktuális témának megfelelő ProjectBean-t). Ezért pl. a elemben az objectID-ra való hivatkozás esetén az OGNL kifejezéskiértékelő először az érték-verem tetején levő aktuális lista-elem (ami ProjectBean típusú) getObjectID metódusát próbálja meghívni (sikeresen), ez pedig visszatéríti a megfelelő téma-azonosítót, mely a studentsForProjectMap indexe. A maphez való hozzáféréskor hasonló módon, előbb az értékverem tetején levő objektum getStudentsForProjectMap() metódusát próbálja meghívni az OGNL kifejezéskiértékelő (ezúttal sikertelenül), majd az értékverem következő elemére próbálja ugyanezt: ez már az Action osztály, melynek végül meghívódik a getStudentsForProjectMap() metódusa, így most már megvannak a témát kiválasztó diák adatai (ez lesz personBean néven az OGNL kontextusába lementve), amennyiben szerepel az adott téma-azonosító a map-ben, különben azt jelenti, hogy nem választotta még ki senki az illető témát (a personBean értéke ekkor null lesz). - esetében pl. az Action osztály (ChooseAction) getIsChosen metódusa hívódik meg (megj.: a ChooseAction osztály chosen mezője az execute metóduson belül van beállítva) ****** pl. előre definiált interceptorok használatára: - a ChooseAction, azon túl, hogy az ActionSupport osztályt terjeszti ki (amely már eleve implementál bizonyos interfészeket, még két interfészt is implementál) - SessionAware interfész: a servletConfig interceptorral működik együtt (mely benne van a choose action-höz rendelt alapértelmezett interceptor-veremben - a struts-default.xml-ben ki lehet keresni a "defaultStack" nevű interceptor-vermet). Hatására meg fog hívódni a setSession metódus, ami session néven elérhetővé teszi a szesszió hatókörbe lementett adatokat (lásd az Action osztály Map típusú "session" mezőjét) - Preparable interfész: ez a prepare interceptorral működik együtt (ez is benne van az alapértelmezett interceptorveremben). Hatására meghívódik az action osztály prepare metódusa. Ez a mi konkrét példánk esetében a szesszióból kinyeri a bejelentkezett felhasználó adatait és azt elmenti ... Ezért, amikor meghívódik az Action osztály execute (vagy más, kérést kezelő) metódusa, akkor ez az információ már hozzáférhető. Ahhoz, hogy ez a mechanizmus működjön, az is fontos, hogy az interceptorveremben a servletConfig interceptor korábban szerepeljen, mint a prepare interceptor (különben a prepare metodus nem férhetne hozzá a szesszióba lementett adatokhoz). Megfigyelhető, hogy a "defaultStack" interceptorveremben ez így is van.