JSP Oldalak Készítése
Segédlet a PGY2/PGY3 hallgatók részére
Ez a dokumentum képelen felvenni a versenyt az Interneten található számtalan kíváló tutoriallal, amelyek közül az alábbiakat ajánlom:
A jelen dokumentum célja leginkább az, hogy a PGYx tárgy hallgatóit célirányosan bemutassa a JSP technológiákkal kapcsolatos követelményeket - és eközben segítsen megismerni a JSP-t.
1. Szervletek és JSP-k
A dinamikus webszolgáltatások elsődleges technológiája a J2EE szerint a szervletek. Ezek olyan osztályok, amelyeket egy java alapú Web szerverbe lehet regisztrálni, és amelyek bizonyos metódusait a szerver a beérkez HTTP kérések alapján meghívja, és az általuk generált válasz lesz a browsernek HTTP válaszként elküldve.
A szervleteknek két típusa ismert, a HTTPServlet , és ennek egy generikus ősosztálya, a Servlet . Az előbbi dedikáltan a Web-es alkalmazásokra készült, míg a másik egy olyan általánosítás, aminek jelenleg a HTTPServlet azegyetlen igazán használatos leszármazottja (no comment...).
a HTTPServlet-nek két fontosabb metódusa a doPost() és a doGet(), amelyek ugyanazt a két argumentumot: egy HttpServletRequest és egy HtpServletResponse objektumot fogadnak. Az előbbi, konstans osztály tartalmazza a kérés összes adatát, amelyek közül a parameter property (azaz a getParameter() accessor) a legfontosabb, hisz ez tartalmazza a HTTP kérésben átadott paramétereket Ezek azok az adatok, amelyek jellemzően a browserben megjelenő form-ok kitöltésékor keletkeznek. A response argumentum objektum a válasz generálását teszi lehetővé, mindenekelőtt azzal, hogy a getWriter() metóduson keresztül elérhetővé tesz egy printWriter objektumot (A két metódust a generikus Servlet esetén a service interész helyettesíti.)
Ennyi információ elegendő lenne, hogy megrírjuk első szerevletünket, de a JDeveloper elébe megy szándékunknak: ha a File/New/Servlets/HTTP Servlet.. funkcióval létrehozunk egy szervletet, akkor annak lényegi vázát (a paraméterek kiolvasásával együtt) legenerálja, sőt még gondoskodik az új szervletnek a web.xml deployment descriptorban történő regisztrálásáról is.
Ime a szerver kód:
package web; import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.*;
import javax.servlet.http.*;
public class MiniServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=windows-1250";
public void init(ServletConfig config) throws ServletException { super.init(config); }public void doGet(HttpServletRequest req, HttpServletResponse resp)
out.println("<html>");
throws ServletException, IOException {
String uname = req.getParameter("Username");
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
out.println("<head><title>MiniServlet</title></head>");
out.println("<body>");
out.println("The servlet has received a GET Uname param is: " + uname);
out.println("</body>""</html>"); out.close();
} }
A wizardban a generálásnál csak a HTTP GET metódus implementálását választottuk. Érdemes megjegyezni a 2. fázisban megadott 'URL Pattern' stringet, mert ezen keresztül lehet majd a Szervletet elérni:
http://server:port/miniservlet/
Adjunk meg még az URL-ben kérdőjel után egy paramétert is, pl: ?Username=Flórián , és akkor megfigyelhetjük a paraméterek használatát is.
A Szervlet gyors, és a Java kódnak köszönhetően sokoldalú, hiszen használhat különféle osztálykönyvtárakat, így pl. az EJB réteget, vagy adatbázisokat is.
A szervlet API nagyon egyszerű, a WEB-réteg bonyolultságát a ráépülő tecnológiák alkotják. Nem túlzás azt állítani, hogy a fentinél lényegesen bonyolultabb szervletet nem is igazán lehet írni...
A szervletnek nem kell magának egyedül feldolgoznia az egész kérést, ill. legenerálnia a teljes választ, ugyanis a requestDispatcher mechanizmus alkalmazásával meghívhat más szervleteket, JSP-ket (include(), amely rendszerint valamilyen HTML fragmenset állít elő), vagy a válasz-lap teljes generálását átadhatja egy másik forrásnak (forward()):
getServletContext().getRequestDispatcher("/headerPart.jsp").include(request,
response);
getServletContext().getRequestDispatcher("/errorPage.jsp").forward(request,
response);
A JSP technológia
A Szervleteknek előnyei mellett hátrányuk, hogy a html file-t println() hívásokkal, darabokban készül el, a kód tervezői. olvasói, karbantartói számára eléggé nehezen követhető módon.
Mivel az esetek jó részében a web alkalmazások oldalai gondosan, esztétikusan megtervezett, statikus struktúrájú lapot tartalmaznak, ahol a dinamikusan változó tartalom viszonylag egyszerű, felmerül a kérdés, nem lehetne-e az egészet "kifordítani", azaz a HTML szövegben megjelölni azokat a pontokat, ahová a dinamikusan generált tartalom kerül.
Ez a technológia a Java Server Pages, azaz a JSP. A JSP oldalak alapvetően HTML-ek, amelybe szintaktikailag a HTML nyelv kiterjesztésének is tekinthető JSP utasításokat írunk. A futtatókörnyezet a JSP oldalra vonatkozó HTTP kérés kiszolgálása közben végigolvassa a JSP szöveget. Míg a HTML szöveget változtatás nélkül elküldi a válasz-stream-be, az említett kierjesztési pontoknál viszont belegenerálja a hivatkozott tartalmat.
<%@ page contentType="text/html;charset=windows-1250" errorPage="/error.jsp" session="true"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>example</title>
</head>
<body>
<jsp:usebean id="person" class="mypkg.personBean" scope="session"/>
<c:if test=" ${header.clientaddr != null} " >
Üdvözlünk, kedves ${param.username} , a ${header.clientaddr} gépről
</c:if>
<%
personBean pp = getAttribute("person");
pp.setRqscount(pp.getRqscount +1);
%>
<table>
<tr><td>Neve:</td><td> <jsp:getProperty property="name" name="person"/> </td></tr>
<tr><td>Foglalkozása:</td><td> <jsp:getProperty property="job" name="person"/> </td></tr>
<tr><td>Hanyadszor kérdezem:</td><td> <jsp:getProperty property="rqscount" name="person"/> </td></tr>
</table>
</body></html>
A fenti példa lényegében az összes JSP kiterjesztés-típust bemutatja. (A feladat itt egy személy adatainak a megjelenítése táblázatos formában. Az egyik adat egy számláló, amely a lap minden újratöltésénél növekszik.)
Fekete szín jelzi a HTML kódot, ami lényegében egy táblázat. A JSP kiterjesztéseket színkóddal jelöltük:
A kék színű részek a direktívák, amelyek a lap fordításának általános paramétereit tartalmazzák.
Jó tudni, hogy a JSP a gyakorlatban a szervlet technológiára támaszkodik. A válaszgenerálás igazából úgy történik, hogy a JSP oldalakból egy beépített JSP compiler szervlet java kódot állít elő, és azt a kérés kiszolgálásakor már szervletként futtatja le. Mindez automatikusan, futási időben történik, azaz a J2EE containerbe (applikációs szerverbe) JSP oldalakat telepítünk, amelyek az első felhasználáskor lesznek lefordítva.
2. JSP direktívák
A kiterjesztés első lépcsője a direktívák, amelyekkel az oldalra vonatkozó általános vezérlési információ adható meg. Ezeket a <%@ ... %> zárójelek jelzik.
A <%@ page ... %> direktívával az oldal típusa (HTTP content-type-ja), karakterkészlete (encoding) és a JSP szemantika egyes paraméterei (pl. a JSP verziószáma) állíthatók be.
A <%@ taglib ... %> direktíva egy ú.n. címkekönyvtárat (taglib) deklarál egy XML namespace deklarációhoz hasonló formában. Az így deklarált könyvtárak cimkéit az oldalon fel lehet használni. Szemantikailag leginkább egy Java import direktívához hasonlítható.
További direktíva az <%@ include ... %>, amellyel az aktuális oldalba más JSP szővegeket lehet logikailag beilleszteni. Ezek általában nem komplett JSP oldalak, hanem ú.n. fragmensek.
3. JSP cimkék és cimkekönyvtárak (tag-ek és taglib-ek)
A cimkék a JSP technológia alapvető eszközei. Formailag a cimkék a standard HTML cimkékhez hasonlók, azzal a különbséggel, hogy a cimkenév mindig "kettőspontozott", azaz namespace:name formátumú. A namespace-t az előbb megismert <%@ taglib ... %> direktívák prefix attribútuma deklarálja, tehát a fenti példa JSP oldalon a c: már egy érvényes cimke-namespace. A jsp: jelű kitüntetett namespace deklaráció nélkül is használható.
A cimkék - megint csak a HTML-hez igazodva- használhatnak atribútumokat, ill. opcionálisan egy 'body' (törzs) részt is a cimke nyitó és záró jelei között. A body-t -az attribútumokhoz hasonlóan- a cimke dolgozza fel (nem is feltétlenül HTML/JSP nyelven kell hogy megadva legyen), és lehet, hogy az outputba nem is kerül bele (ld c:if), vagy többszot belekerül (c:forEach), valamilyen módosításokkal.
A jsp: cimkék
A leggyakrabban használt alap cimke készletet a jsp: namespace jelzi. Ennek fontosabb elemei:
<jsp:useBean id="..." class="..." scope="..."/>
<jsp:setProperty name="..." property="..." value="..."/>
<jsp:getProperty name="..." property="..."/>
E három összefüggő cimke a JavaBeans objektumok kezeléséhez kapcsolódik, amit a JSP nyelvben igen elterjedten használnak az alkalmazás változóiként, ill. külső kommunikáció biztosítására. A JavaBeans konvenció szerint a bean-ek olyan osztályok, amelyek getProperty()/setProperty() hívásokkal engednek hozzáférést belső állapotukhoz. A fenti JSP címkék éppen ezen hívásokhoz engednek hozzáférést. Semmi nem tiltja, hogy a bean-nek még ezen kívül hagyományos methódusai is legyenek, de ezek egyszerű elérésére a standard jsp: cimkék használatával sokáig nem volt mód (egészen a JSP v2.1-ig, ld. az EL kifejezéseket).
A jsp:useBean cimével létrehozhatunk egy új üres ilyen bean változót, ill. - ami különösen érdekes- utasíthatjuk a JSP containert, hogy ha már korábban használtunk ilyen objektumot, akkor inkább azt szeretnénk használni. A 'korábban'-t itt a scope attribútum határozza meg:
Különös figyelmet érdemel a scope="session" opció, ui. ez egy kíváló (és csaknem az egyetlen) lehetőség egy Web-es alkalmazás oldalai között az alkalmazás állapotának tárolására. Klasszikus esete egy Webshop bevásárlókocsija, amely nyilván tárolja az oldalak váltogatása közben kiválasztott termékeket, de tartalmát a fizetés előtt nem célszerű adatbázisban tárolni. A kliens azonosítását leggyakrabban az ismert browser cookie-k segítségével végzik el.
A "nem sokkal korábban" a konténerben állítható, és általában pár perc. Ez az az türelmi idő, amikor - ha közben újabb kérés nem érkezik-, a szerver már nem vár több kommunikációt az adott kliens felől, így felszabadítja a session scope-ú objektumokat. A felhasználó, ha később mégis visszatér, egy új, üres bevásárlókocsit kap.
Míg a session scope izolálja az egyes felhasználóktól (browser session-ökből) származó kéréseket, az scope=application-nal a különböző felhasználók session-jai között áthallás valósítható meg, Pl. igy vslósulhat meg az "Ön most az x. bejelentkezett felhasználó" funkció.
Igen gyakori eset, hogy a JavaBean egyik property-jét nem egy adott értékre, hanem közvetleül a HTTP kérés egyik paraméterének megfelelően akarjuk beállítani. Ennek megfelelően megengedett, hogy jsp:setProperty cimkék value="..." attribútuma helyett param="xyz" attribútumot adjunk meg, amikor xyz egy kérési paraméter neve:
<jsp:setProperty name="memberPerson" property="userid" param="username"/>
Az alap cimkekészlet másik fontos eleme a
<jsp:forward page="..."/>
<jsp:include page="..."/>
A forward-dal egy másik URL-re (JSP-re, szervletre, vagy akármi egyébre) lehet a kérést átirányítani az aktuális lap feldolgozásának megszakításával. Ez egy fontos lehetőség az alkalmazáson belüli lapok közötti navigációra. Az include-dal egy másik, url-lel azonosított (de még az alkalmazáson belüli) lap szövege beemelhető az aktuálisba. Ez az include -szemben a hasonló direktívával- dinamikusan, futási időben értékelődik ki.
További jsp: cimke a jsp:plugin, emellyel Java és activeX appletek (tehát kliens oldali programok) helyezhetők el a HTML-ben
A JSTL cimkekönyvtárak
A jsp: cimkék igazán csak a legalapvetőbb dinamikus JSP funkciókat támogatják. A JSP technológiához ezért definiáltak egy öt cimkekönyvtárból álló 'standard kiegészítést', amely több gyakran hasznos funkcióval bővíti JSP-t. Ez a Java Standard Tag Library (aktuálisan az 1.1 verzió), amelynek elemei:
A JSTL könyvtárak közül itt csak a core-t ismertetem részletesebben; a továbbiakra vonatkozó részletes dokumentáció a http://java.sun.com/products/jsp/jstl/1.1/docs/tlddocs/index.html alatt található.
A core (szokásos jelölése c:) tag library segítségével a JSP egy többé kevésbé univerzális nyelvvé fejleszthető, Mindenekelőtt itt definiálták a feltételes és ciklusszervező utasításoknak megfelelő tag-eket, ill. az általános (nem JavaBean) skalárváltozók kezelésének lehetőségét.
Példa a c:if-re. A test attribútum rendszerint egy EL kifejezést tartalmaz (ld. alább). Itt a 'save' request paraméter esetén a 'buffer' beanben elmentjük a 'textbox1' request paraméter értékét):
<c:if test="${param.save != null}">
Saving data...
<jsp:setProperty property="data" name="buffer" param="textbox1"/>
</c:if>
A Java case() utasításnak megfelelő JSTL tagek a c:choose, a c:when és a c:otherwise. Íme egy sokat mondó példa:
<c:choose>
<c:when test="${param.username == null}">
Ki vagy?
</c:when>
<c:when test="${param.username == 'Pál'}">
Szia, Palikám!
</c:when>
<c:when test="${param.username == 'József'}">
Szia, Józsikám!
</c:when>
<c:otherwise>
Bocsánat, kedves ${param.username}, Önt nem ismerem!
</c:otherwise>
</c:choose>
A c:forEach egy collection elemein, az fmt:forTokens a megadott tokeneken végez iterációt:
<c:forTokens items=" Matild Mici Cili Franciska" delims=" " var="csaj">
Hahó, ${ csaj } <br/>
</c:forTokens>
A JSP fejlődése megmutatta, hogy nemcsak JavaBean változókra, de néha közönséges skalárokra is szükség van. Ilyeneket a c:set mővelettel lehet definiálni (és tipikusan az EL kifejezésekből használhatók). A JSP változóknak nincs típusuk, így a típus megadása itt sem szükséges. Szükség esetén a c:remove használható változók (akár a jsp:useBean által definiát JavaBean változók) törlésére.
<c: set var="age" value ="22" scope="session" />
<c: remove var="age"/ >
A core könyvtár ugyancsak tartalmaz cimkéket (c:import, c:url, c:redirect), amelyekkel külső szervereken levő URL-ek olvashatók be, vagy hivatkozhatók változóként vagy client redirect (egy, a kliensnek szóló speciális HTTP válasz) formájában.
<c: forward url ="http://www.cnn.com" />
További két cimke a c:catch, amellyel kivételkezelés valósítható meg, ill. a c.out, amivel kifejezéseket lehet a standard outputra küldeni (ezt ma már az EL-ek megnövekedett funkcionalitása miatt ritkán használják).
4. Expression Language (EL) kifejezések:
Az EL kifejezések széles körű használata csak a legújabb 2.0 JSP megjelenése óta támogatott. Mostanra EL kifejezéseket egyaránt használhatunk a 'statikus' HTML szövegben, ill. a tag attribútumokban.
AZ EL kifejezések -ahogy a nevük is mutatja- kifejezések, azaz rendszerint nincs mellékhatásuk, mert a változókat csak olvassák, és a részkifejezéseket tiszta operátorokkal kombinálják:
A kifejezés részei:
A hivatkozott objektumok általában a jsp:useBean-nel, c:set-tel, vagy más módokon létrehozott változók, de az alábbi kitüntetett nevek speciális, ú.n. implicit objektumokra vonatkoznak:
Ill. a különféle változó skópokban tárolt objektumokhoz specifikusan is hozzá lehet férni az alábbi implicit objektumokon keresztül:
*
pageScope: Maps page-scoped variable names to their values
*
requestScope:
Maps request-scoped variable names to their values
*
sessionSope: Maps session-scoped variable names to their
values
*
applicationScope: Maps application-scoped variable names
to their values
pl:
sessionScope.user.firstName, sessionScope.shoppingCart.totalVaue, applicationScope.orderSequenceNum.nextValue.
5. Scriptletek
A cimkekönyvtárak korábban elterjedt, ma már kevésbé népszerű alternatávája az a lehetőség, hogy a JSP oldalunkba közvetlen Java kódot írunk. Ez a kód közvetlenül átkerül majd a JSP-ből generlt szerver kódba. A Scriptleteknek három formája van:
Mára, a 2.1 JSP idejére a tag library-k és az EL fejlődése fokozatosan egyre kevésbé szalonképessé teszi a scriptletek széles körű használatát, hiszene ez szaggatottá, nehezen olvashatóvá teszi a JSP-t. Ugyanakkor nyilván ez a legnagyobb képességő eszköz, hiszen pl. a változóként tárolt JavaBean-eken metódusokat hívni csak ezzel lehet. Ime egy 'elrettentő' példa:
Fejlesztés alatt...
Nem ritka eset, hogy a fejlesztők saját cimkéket definiálnak, pl. az alábbi okokból:
Valami saját GUI tervezési mintát igyekszenek újrafelhasználhatóvá tenni (így fejlődtek ki lényegében a népszerű Web-es alkalmazás-frameworkok is).
Valami speciális API-hoz kell illeszkedni, amihez nincs jó támogatás a meglévő tag-ekkel: pl. képek, grafikák röptében történő generálása.
Végül a saját cimkék alkalmasak a scriptletek kulturált elrejtéséhez. Ha már mindenképpen scriptletet használunk, akkor ezt ne a fő JSP oldalokon történjen.
Az 1.x JSP-ben a cimkék definiálása Java kódolást igényelt, sőt egy XML descriptort is kellett a cimkekönyvtárhoz írni. Az új JSP már támogatja a JSP nyelven megírt saját cimkéket is, ezt mutatjuk most be:
Minden egyes tag-et egy külön JSP file definiál, és a név első része lesz automatikusan a cimke. A cimkedefiníció egy majdnem szokásos JSP file, az alábbi különbségekkel:
A <%@attribute name=" " required="true" %> direktívával a saját JSP tag attribútuimait lehet definiálni
A <jsp:doBody... /> tag pedig, a saját tag alkalmazásakor majd a cimke belsejébe ( <mylib:mytag ... > </mylib:mytag ... > közé) került HTML/JSP (v. egyéb) tartalom kiértékelésére és az outpura generálására szolgál.
Egy cimkekönyvtár egyszerűen egy adott WEB-INF/tags alatti könyvtárban megtalálható cimke-JSP fileok összesságe, azaz nincs szükség XML descriptorra. Egy cimkekönyvtárat deklarálni a szokásos <%@ taglib %> direktíva egy változatával, a következőképpen lehet
<%@ taglib tagdir="/WEB-INF/tags/mytagsdir" prefix=" myNewTags "%>
Ahol a tagdir attribútum kötelezően a WEB-INF/tags könyvtár egyik (akár közvetett) alkönyvtára.
7. JSP oldalak szerkesztése, futtatása és debuggolása a JDeveloperben
A szerkesztést a JDeveloperben számos eszköz segíti:
Az elkészített JSP alkalmazás futtatása is nagyon egyszerűen történik: bármlyik JSP context menüjében a Run opcióval. Ennek hatására lefordulnak a szükséges Java fileok, és becsomagolja és telepíti az egész war file-t, ill. elindítjja a beépített OC4J szervert, sőt még egy kliens browsert is.
A debuggolást segíti, hogy a JDeveloperben nem nehéz megtalálni a JSP-re generált szervlet forrását: a projekt home könyvtárban a /classes/.jsps/ alatt. Ennek oka az, hogy a JDeveloper a hagyományos megoldástól eltérően a beépített OC4J szervere számára már fordítási dőben lefordítja a JSP-ket, és e fordítás eredménye tekinthető meg a .jsps könyvtárban.