Kihagyás

9. gyakorlat

Java Server Pages (JSP)

A JSP technológia csakúgy, mint a Servletek azért hivatottak, hogy a webes alkalmazásokat készíthessünk. Gondoljunk rájuk úgy, mint a szervleteket kiegészítő technológiára. A JSP ténylegesen maga is egy Servlet, a Servletből származik. Minden funkciót, amit a servletek kapcsán tanultunk itt is használni tudunk. A JSP oldalakra tekintsünk úgy, mint egyszerű HTML oldalakra, azonban azok dinamikus voltát az biztosítja, hogy beágyazhatunk beléjük Java kódot, mellyel a tartalmat generálhatjuk (dinamikussá tehetjük). Az előző órán látottak alapján mindenki azt mondhatja, hogy a komplex HTML válasz előállítása a servleten belül nem csak hogy macerás, de magas hibahajlammal bír.

A JSP oldalak megértését nagyban segíti az alábbi ábra áttanulmányozása:

jspflow

Az egyik kulcslépés a JSP-k servletekké alakítása. Ezt az átalakítást az alkalmazás szerver végzi el. Ezután a megszokott folyamat zajlik.

A JSP oldalak, HTML-szerűen tagekkel írhatóak le, de benne Java kódot is írhatunk. Hozzunk létre egy új Maven webapp-os projektet, ahogy azt a szervleteknél is tettük és adjuk, hozzá a dependency-khez a servlet-api-t. Ezután a generált projekt index.jsp oldalán a következő kódot írjuk be, majd indítsunk el a Tomcat Servert (beállítás ugyanaz, mint a servleteknél):

1
2
3
4
5
<html>  
<body>  
  <% out.print(2*5); %>  
</body>  
</html>  

Ez a 10-es eredményt írja ki. Később megnézzük, hogy pontosan mi történik.

A JSP oldalakat a projektünkön belül a webapp mappába kell helyezni, de fontos, hogy ne a WEB-INF mappába rakjuk, mert az zárt az alkalmazásszerveren belül, azokat a fájlokat a kliens nem éri el direkt módon (web.xml-ben kell ilyen esetben megadni). Jobb klikk webapp -> New -> JSP/JSPX Page -> JSP Page

Tipp

A JSP oldalakat használjuk megjelenítésre, a controller logikát szervezzük Servletekbe!

Amint azt láttuk a servleteket az alkalmazásszerverünk Servlet konténere menedzseli. A JSP oldalakat a JSP konténer kezeli. JSP fejlesztés során ajánlott a maven dependency-k közé felvenni a következőt:

1
2
3
4
5
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.2.1-b03</version>
</dependency>

Feladat

Adjunk hozzá egy új JSP oldalt a projektünkhöz és az első oldalra helyezzünk el egy linket, ami átvisz a másik oldalra!

Megoldás

index.jsp:

1
2
3
4
5
6
<html>
  <body>
  <h2>Hello World!</h2>
  <a href="second.jsp">Go To Second Page</a>
  </body>
</html>

second.jsp:

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Second Page</title>
</head>
<body>
    <h1>Second Page</h1>
</body>
</html>

A second.jsp-ben található első sort az IntelliJ generálja. Később megismerkedünk ezekkel az elemekkel is.

JSP életciklusok

A JSP oldalak az alábbi életciklussal rendelkeznek. Egy részüket már láthattuk korábban:

  • Transzformálás: A JSP oldalak szervletté alakítása. Pl.: index.jsp -> index_jsp.java
  • Fordítás: A legenerált szervleteket lefordítjuk, amennyiben az átalakítás sikeres volt
  • Osztálybetöltés: Memóriába töltés
  • Példányosítás: A JSP konténer létrehoz egy példányt a JSP oldalból (már szervletként gondolhatunk rá)
  • Inicializálás: ServletConfig és ServletContext elérhetővé válik az oldal számára
  • Kérés feldolgozás: Minden kérést külön szálon szolgálnak ki a JSP oldalak is, mint ahogyan a szervleteknél is
  • Megsemmisítés: Hasonlóan, mint a szervleteknél a JSP oldalak is megsemmisülnek életük végén (pl.: szerver leállítás)

Életciklus metódusok

Ahogy a szervleteknél is jelen voltak az életciklusokat támogató metódusok (init, service, destroy), itt is megtalálhatóak:

  • jspInit(): a JspPage interface-ben van megadva
  • _jspService(HttpServletRequest request, HttpServletResponse response): HttpJspPage interface tartalmazza
  • jspDestroy(): a JspPage interface-ben van megadva

A JspPage és a HttpJspPage interfészeket nem kell megadnunk direktben, ezeket az interface-eket automatikusan implementálja minden JSP oldal. Ezeket a metódusokat a JSP oldalon belül tudjuk felüldefiniálni, amennyiben az szükséges (általában nem az) a declaration tag-ek használatával (lásd lentebb).

JSP scripting

A JSP oldalakon belül használhatunk úgynevezett scripting tageket (<%...%>). Ezeken belül java kódokat és kifejezéseket adhatunk meg. 3 típusát különböztetjük meg:

  • scriptlet tag: <% java source code %>
  • expression tag: <%= expresison %>
  • declaration tag: <%! declaration %>

Példa egy paraméter kiírására:

index.html

1
2
3
4
5
6
7
8
<html>  
  <body>  
    <form action="welcome.jsp">  
      <input type="text" name="username">  
      <input type="submit" value="go"><br/>  
    </form>  
  </body>  
</html>  

welcome.jsp

1
2
3
4
5
6
7
8
<html>  
  <body>  
  <%  
    String name = request.getParameter("username");
    out.print("welcome "+name);  
  %>  
  </body>  
</html> 

Ez kiírja az index.html form-jában megadott felhasználói nevet a JSP oldalon. Vegyük észre az out.print sort, melyben kérdezhetnénk, hogy miért nincs a System az out eléírva. A JSP oldalakon használható néhány előre definiált, úgynevezett implicit objektum, mint például az out, melynek típusa JspWriter, működését tekintve pedig a servletekben használt PrintWriter-hez hasonlít. Egy másik implicit objektum is szerepel, mégpedig a request. Ez a HttpServletRequest objektumunkat jelöli és automatikusan megkapja a JSP oldal. A scriptletek mindig java utasításokat tartalmaznak, így az utasítások végére pontosvesszőt kell írnunk!

A servletekben HTML kódot írtunk Java kódba, most pedig HTML-be írunk Java-t. Nem úgy tűnik, hogy sokkal jobb volna a JSP... Valóban a scriptletek eléggé el tudják csúfítani a JSP oldalak forráskódját, ezért is találták ki az Expression Language technológiát, melyet a későbbiekben tárgyalunk.

A JSP tartalmára tekinthetünk, úgy mintha a service metódusban lennénk és ott írnánk ki a HTML-es tartalmat (végső soron tényleg ez történik). A scriptlet-ként írt kód, így ebbe a metódusba kerül, melynek következményeként a scriptletben nem definiálhatunk metódusokat vagy adattagokat. Erre ad megoldást a declaration tag (ez <%! ... %>), mivel az ezek közé a tag-ek közé írt kódot a service() metóduson kívülre generálja az alkalmazás szerver JSP translator motorja.

Példa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>  
  <body>  
    <%!   
      int cube(int n){  
        return n*n*n*;  
      }  
    %> 

    Cube of 3 is: <% out.print(cube(3)); %>
  </body>  
</html>  

A 3. tag az expression tag, melybe írt kód a response output stream-jére íródik. Nem kell out.print-et írni! Főként változók vagy metódushívások értékének kiírására használható.

Példa:

1
2
3
4
5
  <html>  
    <body>  
      Current Time: <%= java.util.Calendar.getInstance().getTime() %>  
    </body>  
  </html>  

Implicit-objektumok

A JSP oldalakon implicit objektumokat is kapunk, melyet a web konténer inicializál. Ezek közül már láttuk is az out és a request implicit objektumokat, melyek rendre a servlet-nél látott PrintWriter-hez hasonló (JspWriter típusú) output stream írója, illetve a HttpServletRequest objektumunk.

A következő implicit objektumok közül válogathatunk (zárójelben a típusukat láthatjuk):

  • application (ServletContext)
  • config (ServletConfig)
  • jspContext (JspContext)
  • out (JspWriter)
  • page (Object)
  • pageContext (PageContext)
  • request (HttpServletRequest)
  • response (HttpServletResponse)
  • session (HttpSession)
  • exception (Throwable)

A fenti implicit objektumokat a scriptletekben bárhol használhatjuk, mivel az olyan, mintha a _jspService() metódusban írnánk kódot. Ezt továbbgondolva, adódik, hogy ezeket az implicit objektumokat nem használhatjuk a declaration tag-ek között, mivel az magához a servlet osztályhoz fog tartozni (adattagokat és metódusokat definiálhatok).

Most nézzük meg a fontosabb implicit objektumokat részletesebben!

Request implicit objektum

A request implicit objektumot a szervlet service(HttpServletRequest req, ...) metódusában paraméterként megkapott request objektummal azonosítható (ténylegesen ugyanaz az objektum), típusa HttpServletRequest. Funkcionalitását tekintve is megegyezik a servletekben megismert request objektummal, attribútumokat állíthatunk be, kérhetünk le, ugyanígy a paramétereket is elérhetjük.

Response implicit objektum

A response implicit objektumot a szervlet service(..., HttpServletResponse resp) metódusában paraméterként megkapott response objektummal azonosítható, típusa HttpServletResponse. Hasonlóan használható mint a szervleteknél.

Példa jsp oldalon belüli redirect-re:

1
2
3
<%   
  response.sendRedirect("http://www.google.com");  
%>  

Config implicit objektum

Amennyiben a jsp oldalunknak szeretnénk init paramétereket beállítani, akkor azt nem tudjuk megtenni olyan annotációs formában, csak a web.xml-ben. Nézzünk egy példát, hogy hogyan lehet JSP oldalakat megadni a web.xml-ben!

index.jsp

1
2
3
4
5
<html>
<body>
<h2>Hello <%= config.getInitParameter("name") %> </h2>
</body>
</html>

web.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>index</servlet-name>
    <jsp-file>/index.jsp</jsp-file>
    <init-param>
      <param-name>name</param-name>
      <param-value>Kandisz Nóra</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>index</servlet-name>
    <url-pattern>/index</url-pattern>
  </servlet-mapping>
</web-app>

A JSP oldalon kiírjuk a web.xml-ben megadott name init paraméter értékét. A web.xml-es megadás arra is megfelelő, hogy elrejtsük az url-ből, hogy jsp oldalakkal játszunk a háttérben. Mivel a JSP oldalak servletekké alakulnak a háttérben, ezért a web.xml-ben is ugyanúgy adjuk meg őket, mint ahogyan az a hagyományos servleteknél történt, viszont vegyük észre, hogy itt megadjuk a jsp-file elemet is, melyben megadjuk azt, hogy itt egy jsp fájlról van szó.

Application implicit objektum

Hasonlóan a ServletConfig objektumhoz, a ServletContext-hez is hozzáférhetünk implicit objektum formájában. A ServletContext objektumnak megfelelő implicit objektum az application.

Példa context param kiolvasására:

web.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<web-app>  

<servlet>  
  <servlet-name>welcome</servlet-name>  
  <jsp-file>/welcome.jsp</jsp-file>  
</servlet>  

<servlet-mapping>  
  <servlet-name>welcome</servlet-name>  
  <url-pattern>/welcome</url-pattern>  
</servlet-mapping>  

<context-param>  
  <param-name>driver</param-name>  
  <param-value>org.sqlite.JDBC</param-value>  
</context-param>  

</web-app>  

welcome.jsp:

1
2
3
4
<%   
  String driver=application.getInitParameter("driver");  
  out.print("driver name is="+driver);  
%>  

Session implicit objektum

A request-től elkérhető HttpSession típusú objektum megfelelője.

Példa session használatára.

index.html

1
2
3
4
5
6
7
8
<html>  
  <body>  
    <form action="welcome.jsp">  
      <input type="text" name="uname">  
      <input type="submit" value="go"><br/>  
    </form>  
  </body>  
</html>  

welcome.jsp:

1
2
3
4
5
6
7
8
9
<html>  
  <body>  
  <%   
    String name = request.getParameter("uname");  
    out.print("Welcome " + name);  
    session.setAttribute("user",name);
  %>
  </body>  
</html>  
A beállított attribútumot más oldalakon a session.getAttribute("user") hívással kérhetjük le. Mivel a session-kezelés eléggé költséges, így ha nincs rá szükség, akkor megmondhatjuk a JSP oldalnak, hogy ne készítsen session objektumot az adott oldalhoz. A session például felesleges lehet a login oldalon. Ehhez a <%@ page session="false" %> kódrészletet illesszük az oldal tetejére. A pontos viselkedést később tisztázzuk a direktíváknál.

pageContext implicit objektum

A pageContext objektum a javax.servlet.jsp.PageContext osztály egy példánya, melyen keresztül elérhetőek a különböző scope-okban lévő attribútumok (session scope, request scope, application scope). A pageContext-en keresztül a többi implicit objektumot is el tudjuk érni.

Példa:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<html>  
<body>  
<%   
String name=request.getParameter("uname");  
out.print("Welcome "+name);  

pageContext.setAttribute("user",name,PageContext.SESSION_SCOPE);    
%>  
</body>  
</html>  

A pageContext implicit objektumot ritkábban használjuk, mivel helyette használhatjuk az implicit objektumokat közvetlenül.

Feladat

Készítsünk JSP oldalt, melyen egy gomb megnyomásakor egy számláló értékét növeljük eggyel! A számlálót nem kell kell külön szervletben növelni. Csináljunk mindent egyetlen JSP oldalon!

Megoldás

index.jsp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<html>
  <body>
  <%!
      private static int counter = 0;
  %>

  <%
      if(request.getParameter("submit") != null){
          counter++;
      }
  %>

  <h1>Counter value: <%= counter %></h1>

  <form method="post" action="index.jsp">
      <button type="submit" name="submit">Increment</button>
  </form>

  </body>
</html>

A form-on belül a gombnak a name attribútumát fontos submit-re állítani, mert a request paraméterei között ilyen néven fogjuk elérni azt. Az eredmény az, hogy csak akkor növeljük a counter értékét, ha a postban megtalálható volt a submit paraméter (bármi is legyen az értéke).

Feladat

Készítsünk egy form-ot, melyen bekérjük a felhasználó adatait: név, email, születési év! A form megnyomásakor kerüljünk át egy másik jsp oldalra, ahol kiírjuk ezeket a paramétereket, de előtte a kapott értékeket (ha vannak) beletesszük a session-be. A kiírás is a session-ből történjen!

Megoldás

index.jsp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  <html>
  <body>

  <h1>Update profile</h1>

  <form method="post" action="details.jsp">
      <div>
          <label for="name">Name</label>
          <input type="text" id="name" name="name">
      </div>
      <div>
          <label for="email">Email</label>
          <input type="email" id="email" name="email">
      </div>
      <div>
          <label for="age">Age</label>
          <input type="number" id="age" name="age">
      </div>
      <button type="submit" name="submit">Update profile</button>
  </form>

  </body>
  </html>

details.jsp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>User details</title>
</head>
<body>

<%
    String name = request.getParameter("name");
    String email = request.getParameter("email");
    String age = request.getParameter("age");

    if(name != null && !name.isEmpty()){
        session.setAttribute("name", name);
    }
    if(email != null && !email.isEmpty()){
        session.setAttribute("email", email);
    }
    if(age != null && !age.isEmpty()){
        session.setAttribute("age", age);
    }
%>

<h1>User details</h1>

<table>
    <tr>
        <td>Name:</td>
        <td><%= session.getAttribute("name") == null ? "Not specified" : session.getAttribute("name") %></td>
    </tr>
    <tr>
        <td>Email:</td>
        <td><%= session.getAttribute("email") == null ? "Not specified" : session.getAttribute("email")  %></td>
    </tr>
    <tr>
        <td>Age:</td>
        <td><%= session.getAttribute("age") == null ? "Not specified" : session.getAttribute("age") %></td>
    </tr>
</table>

</body>
</html>

JSP direktívák

A direktívák az alkalmazásszervernek szóló üzenetek, melyekkel megmondhatjuk, hogy a JSP oldalt milyen módon alakítsa szervletté a konténer, illetve módosítani tudjuk, hogy milyen képességű szervletünk keletkezik a transzformálás után. 3 típusa van:

  • page direktíva
  • include direktíva
  • taglib direktíva

Ezek szintaxisa

<%@ directive attribute="value" %>

, ahol a directive maga a page, include, vagy taglib.

page direktíva

Az egész oldalra vonatkozó attribútumok megadására szolgál. Egy page direktívához megadhatóak attribútumok, melyeket egy vagy akár több page direktívában helyezhetünk el a hatás ugyanaz. Használata:

<%@ page attribute="value" %>

Nézzük milyen attribútumokkal találkozhatunk a leggyakrabban.

import: Hasonlóan a java-s import-hoz itt is pontosan egy java osztályt húzhatunk be vele.

Példa:

1
2
3
4
5
6
<%@ page import="java.util.Date" %> 
<html>  
<body>  
Today is: <%= new Date()%>
</body>  
</html>  
Addig nem használhatjuk a Date osztályt, ameddig nem húztuk be.

contentType: Ahogyan a servleteknél a response.setContentType("text/html") beállította a response mime típusát, itt ezt a következőképpen tehetjük meg.

1
2
3
4
5
6
7
8
<html>  
<body>  

  <%@ page contentType="text/html" %>  

  Szevasz Jozsi!
</body>  
</html>  

errorPage: Ebben az attribútumban megadhatjuk, hogy amennyiben az oldalunk egy hibát dob, akkor azt melyik hibaoldal kezelje le. Itt bármilyen URI-t megadhatunk.

isErrorPage: Ezt akkor adjuk meg, amikor az adott jsp oldal egy hibakezelő oldal. Az implicit objektumok között szerepelt az exception, amiről nem beszéltünk még. Ez az implicit objektum csak az olyan oldalakon érhető el, melyeknél az isErrorPage attribútumot igazra állítottuk.

session: Ahogy azt korábban már láttuk a session objektum létrehozását tilthatjuk le. Alapértelmezetten engedélyezve van.

include direktíva

Az include direktíva segítségével behúzhatjuk más erőforrások (jsp, hmtl, text fájlok) tartalmát. Igen hasznos lehet akkor, amikor az oldalainkat logikailag fel akarjuk osztani pl: header, main, footer részekre. Az include-ot a következő formában használjuk:

<%@ include file="test.jsp" %>

ahol a test.jsp egy relatív útvonalmegadás az aktuális jsp oldalhoz képest.

Például egy header behúzása:

1
2
3
4
5
6
7
<html>  
<body>  
  <%@ include file="header.html" %>

  Today is: <%= java.util.Calendar.getInstance().getTime() %>  
</body>  
</html>  

taglib direktíva

A taglib direktíva tag library-k behúzására alkalmazható, melyek custom tag-eket tartalmaznak. Később majd látjuk, hogy hogyan csinálhatunk saját tageket. Pl.: a <%= java.util.Calendar.getInstance().getTime() %> scriptlet helyett, csinálhatunk egy saját <currentTime> tag-et, mely kiírja az aktuális időt és ilyen módon nem "koszolja" össze a forráskódot.

Expression Language

Az Expression Language vagy röviden EL, egy olyan bővítés, mely sokkal egyszerűbbé teszi az objektumokkal való munkát, mint például a request, application, response, stb. Mint korábban láttuk, nem igazán jó, hogy a JSP-ben alapvetően HTML kód van, mely teli van tűzdelve Java kódokkal. Az Expression Language ebben is segít.

Szintaxisa:

${ expression }

Az ilyen kifejezéseket a JSP oldalon belül akárhol használhatjuk.

Ahogy a scriptletekben is rendelkezünk implicit objektumokkal, így az Expression Language-ben is elérjük ezeket.

Implicit objektumok listája:

  • pageScope (csak az ezen oldalhoz tárolt attribútumok map-je)
  • requestScope (a request-ben tárolt attribútumok map-je)
  • sessionScope (a session-ben tárolt attribútumok map-je)
  • applicationScope (a ServletContext-ben tárolt attribútumok map-je)
  • param (request paraméterek lekérdezéséhez)
  • paramValues (a request paraméterek listája)
  • header (a kérés header-je)
  • headerValues (az összes request header lekérdezése)
  • cookie (cookie-k lekérdezéséhez)
  • initParam (a context init paraméterek lekérdezéséhez, amit a web.xml-ben adtunk meg, servlet init paramhoz nem használható)

Figyelem

A fenti implicit objektumokat ne keverjük össze a JSP implicit objektumokkal!

Az Expression Language igazi ereje abban van, hogy nem kell folyamatosan váltanunk HTML és java code között.

Vegyük a következő példát:

index.html

1
2
3
4
5
6
7
8
<html>  
  <body>  
    <form action="welcome.jsp">  
      <input type="text" name="uname">  
      <input type="submit" value="go"><br/>  
    </form>  
  </body>  
</html>  

welcome.jsp:

1
2
3
4
5
6
7
8
<html>  
  <body>  
  <%   
    String name = request.getParameter("uname");  
    out.print("Welcome " + name);  
  %>
  </body>  
</html>  

Expression Language használatával a welcome.jsp a következőképpen is írható:

1
2
3
4
5
<html>  
  <body> 
    Welcome ${param.uname}
  </body>  
</html>  

Az EL-el írt kód sokkal rövidebb, sokkal tisztább, könnyebben érthető, így mindenképpen ajánlatos a használata.

Segítség!!! Nem működik az EL!

Ennek több oka lehet. A legvalószínűbb az, hogy a web.xml rosszul van megírva. Mivel a 2.4-es servlet specifikációtól kezdődően támogatott az EL, így a Maven által generált webapp nem igazán jó, mivel abban 2.3-as DTD specifikáció van megadva, ami igencsak helytelen.

1
2
3
4
5
6
7
8
  <!DOCTYPE web-app PUBLIC
  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd" >

  <web-app>
    <display-name>Archetype Created Web Application</display-name>

  </web-app>

A legegyszerűbb megoldás, hogy a <!DOCTYPE ... megadást kitöröljük.

EL operátorok

A legtöbb EL implicit objektum egy map amiből értékeket kérdezhetünk le. Erre használható a property access operátor (.), amivel a mapből lekérhetjük a megfelelő értéket. Például:

${requestScope.person.name}

Ez lekéri a requestScope-ban definiált person attribútumot, melynek aztán lekéri a name attribútumát. A person itt már nem egy map, hanem egy Bean objektum. Az ilyen Bean objektumokon is használhatjuk a property access operátort. A Bean alatt az általunk már megismert Bean-eket értjük (publikus konstruktor, getter/setter, szerializálható). Fontos, hogy Bean-en és java.util.Map objektumokon használhatjuk ezt az operátort.

A következő fontos operátor a collection access operátor. Tömbök vagy listák elemeihez kapunk hozzáférést a használatukkal. Példák:

${myList[1]} // a myList lista 1 elemét adja vissza

${myList[“1”]} // nem csak számmal, hanem string-el is megadhatjuk az indexet

${myMap[expr]} // ha az index nem string akkor a paraméter EL kifejezésként kiértékelődik

${myMap[myList[1]]} // akár többszörösen is egymásba lehet ágyazni

Az EL kifejezéseken belül használhatjuk a további megszokott egyszerű operátorokat is:

  • aritmetikai operátorok (+, -, *, /, %)
  • logikai operátorok (&&, ||, !)
  • relációs operátorok (<, >, ==, !=, <=, >=)

Fontos

Fontos észben tartani a következőket, amikor EL-t használunk:

  • Az EL kifejezések mindig a következő formában szerepelnek: ${...}
  • Egy oldalon a EL feldolgozást letilthatjuk a <%@ page isELIgnored="true" %> page direktíva használatával
  • Az EL segítségével lekérdezhetünk attribútumokat, header-öket, cookie-kat, viszont nem állíthatunk be értéket
  • Az EL implicit objektumai különböznek a JSP implicit objektumaitól, kivéve a pageContext
  • Az EL NULL-barát: nem dobál exception, ha nincs meg valamilyen attribútum

JSP action tag-ek

A jsp action tag-ek halmaza sokat segíthet, hogy ne kelljen átváltani java kódra, helyette jsp-t írhatunk továbbra is. Itt csak a legfontosabbakat action tag-eket mutatjuk be.

jsp:useBean

Akkor használjuk, ha egy Java Bean-t le szeretnénk kérni egy adott scope-ból, vagy egy új objektumot létrehozni egy adott scope-ban. Példa:

1
<jsp:useBean id="person" class="hu.alkfejl.Person" scope="session" />

Ebben az esetben a JSP konténer megkeresi a person nevű bejegyzést a session-ben. Amennyiben ilyet nem talál, akkor létrehoz egy új Person objektumot és person néven be is állítja azt a session-ben. Miután definiáltuk a Bean-t a JSP oldalon, onnantól kezdve lekérhetjük a property-jeit egy másik jsp action tag-gel, melyet getProperty-nek hívnak:

1
<jsp:getProperty name="person" property="name" />

Ezt viszont nem igazán szoktuk használni, mivel már ismerjük az EL képességeit, tehát a fenti megfelelője:

1
${person.name}

A get mellett van egy setProperty jsp action tag, amit viszont már sokkal inkább használunk, mivel az EL segítségével nem tudunk értékeket beállítani.

1
<jsp:setProperty name="person" property="name" value="Gipsz Jakab" />

Ha a name property-t csak akkor akarjuk beállítani, ha új objektumot kellett létrehozni (azaz nem volt még benne a sessionben), akkor a setProperty-t a useBean tag gyerekeként adjuk meg!

1
2
3
<jsp:useBean id="person" class="hu.alkfejl.Person" scope="session">
  <jsp:setProperty name="person" property="name" value="Gipsz Jakab" />
</jsp:useBean>

Néhány tudnivaló:

  • Amennyiben a useBean-ek nem adunk meg scope-ot akkor a page az alapértelmezett.
  • Ha a request paraméterek alapján akarom beállítani a property-t, akkor a setProperty param attribútumában adhatom meg, hogy milyen nevű request paraméter értékét akarom a bean-nek odaadni. Ha a property neve megegyezik a request paraméter nevével, akkor elhagyhatom a param attribútumot.
  • Amennyiben a bean összes property-jét be akarom állítani és azok neve rendre megegyezik a request paraméterek neveivel, akkor írhatom a következőt: <jsp:setProperty name="person" property="*" />

jsp:forward

A szervleteknél megismert RequestDispatcher forward metódusa helyett használható.

Az alábbi kódrészletek ekvivalensek:

1
<jsp:forward page="welcome.jsp" />

Ugyanez a servlet doGet, doPost, stb metódusokban:

1
2
RequestDispatcher rd = request.getRequestDispatcher("welcome.jsp");
rd.forward(request, response);

jsp:include

A szervleteknél megismert RequestDispatcher include metódusával megegyező.

Az alábbi kódrészletek ekvivalensek:

1
<jsp:include page="welcome.jsp" />

Ugyanez a servlet doGet, doPost, stb metódusokban:

1
2
RequestDispatcher rd = request.getRequestDispatcher("welcome.jsp");
rd.include(request, response);

Egy fontos különbség azonban van: include direktíva használatakor az include a transzformáláskor történik (azaz része lesz a lefordított servlet-nek), míg jelen esetben futási időben. Ez azt jelenti, hogy abban az esetben ha erősen dinamikus JSP oldalunk van, akkor az include direktíva csődöt mondhat, így ilyenkor érdemes lehet a jsp:include-ot használni.

JSTL

A JSTL (JSP Standard Tag Library) egy adag egyedi tag-et ad a kezünkbe, melyekkel sokkal könnyebb a fejlesztés, illetve az EL mellett, a JSTL használatával is sokkal szebb kódot írhatunk a JSP oldalban, anélkül, hogy váltanunk kéne scriptlet-re. A JSTL szolgáltat néhány bejárással és elágazással kapcsolatos tag-et. A JSTL-ben több csoport található meg, de mi csak a core library-t fogjuk használni. Ennek használatához először a pom.xml-be fel kell vennünk a következő dependency-t:

1
2
3
4
5
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

Majd azon az oldalon, ahol használni szeretnénk a jstl-t ott a következőt kell, beillesztenünk.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

c:out

A c:out hasonlít a <%= ... %> expression scriptlet-hez, viszont itt EL-el használhatjuk.

Példa:

1
2
3
4
5
6
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  
<html>  
  <body>  
  <c:out value="${sessionScope.user.name}"/>  
  </body>  
</html> 

A fenti kód a session-ből kiszedné a beállított user bean objektumot, melynek a kiírja a name fieldjét.

c:import

Hasonló a <jsp:include> action tag-hez, viszont képes külső url-ekről is behúzni a tartalmat.

c:set

A <c:set> változók beállítására használható, melynek megadhatjuk a scope-ot is (requestScope, sessionScope, applicationScope).

Példa:

1
<c:set var="income" scope="session" value="${99 * 20 * 100}"/> 

Fontos, hogy a scope megadásánál nem kell megadni a teljes implicit objektumot, azaz nem sessionScope-ot írunk, hanem szimplán session-t.

c:remove

A c:set ellenkezője, eltávolíthatunk a megadott scope-ból változókat. Fő a takarítás magunk után, ha már nincs szükségünk valamire.

c:if

A válaszba feltételesen helyezhetünk el tartalmat.

Példa:

1
2
3
<c:if test="${income > 8000}">  
   <p>My income is: <c:out value="${income}"/><p>  
</c:if>  

A fenti példában csak akkor írjuk ki a jövedelmet, ha az nagyobb mint 8000.

c:choose

A Java-s switch megfelelője.

Példa:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<c:choose>  
    <c:when test="${income <= 1000}">  
       Income is not good.  
    </c:when>  
    <c:when test="${income > 10000}">  
        Income is very good.  
    </c:when>  
    <c:otherwise>  
       Income is undetermined...  
    </c:otherwise>  
</c:choose>  

A case ágaknak megfeleltethető a when, illetve a default ágnak az otherwise.

c:forEach

Iterációhoz használható a c:forEach tag, melyben index alapú, illetve a tényleges foreach iteráció is megadható.

Példa:

1
2
3
<c:forEach var="j" begin="1" end="3">  
   Item <c:out value="${j}"/>
</c:forEach> 

Egy másik példa során, ha adott mondjuk a sessionben egy List típusú lista akkor annak elemeit a következőképpen írhatjuk ki.

1
2
3
<c:forEach items="${sessionScope.texts}" var="text">  
   <c:out value="${text}"/>
</c:forEach> 

c:redirect

A szervleteknél látott response.sendRedirect("welcome.jsp") mintájára használható, de megint csak nem kell átváltanunk scriptlet-be. Az url-ben megadható külső erőforrás is.

Feladatok

Feladat

Készítsünk egy képek feltöltésére alkalmas JSP alkalmazást, mely Base64-be alakítja át a képet!

Megoldás

index.jsp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>
<body>

<form action="image-upload" enctype = "multipart/form-data" method="post">
    <label for="img">Select image:</label>
    <input type="file" id="img" name="img" accept="image/*">
    <input type="submit">
</form>

</body>
</html>

ImageUpload.java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Base64;
import java.util.Vector;

@WebServlet("/image-upload")
@MultipartConfig(
        fileSizeThreshold = 1024 * 1024, // 1 MB
        maxFileSize = 1024 * 1024 * 5, // 5 MB
        maxRequestSize = 1024 * 1024 * 5 * 5 // 25 MB
)
public class ImageUpload extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Vector<InputStream> allParts = new Vector<>();

        for (Part part : req.getParts()) {
            allParts.add(part.getInputStream());
        }

        SequenceInputStream appended = new SequenceInputStream(allParts.elements());
        byte[] img = appended.readAllBytes();
        String result = Base64.getEncoder().encodeToString(img);

        System.out.println(result);
    }
}

Fontos, hogy a form-ban megadjuk, hogy multipart data-t küldünk majd. A szervletben a MultipartConfig annotációban jelezhetjük, hogy mekkora mennyiségű adatot hajlandó fogadni a szervletünk. Ezután az adatrészeket a request-től a getParts()-al kérhetjük le, és minden part-tól elkérhetünk egy InputStream-et. Mivel nekünk a kép egyben kell, ezért ezeket a Streameket össze kell fűznünk. Erre alkalmas a SequenceInputStream, melynek van egy konstruktora, mely Enumeration-et vár. Emiatt egy Vector-ba összegyűjtjük az InputStreameket, melyre az elements() hívás pontosan egy ilyen enumeration-t ad vissza. Ebből a SequenceInputStream-ből már kinyerhetjük a byte tömböt, melyet aztán a Base64 encoderrel tudunk kódolni String-gé.

A kinyert String-et lementhetjük akár adatbázisba is. Most csak kiírjuk a konzolra. Teszteléskor használhatunk egy online decodert, hogy visszanyerjük az eredeti képet. Pl.: https://codebeautify.org/base64-to-image-converter

Feladat

Készítsük el a contacts projektünket JSP oldalak és servletek használatával! Első körben elegendő a listázást és a hozzáadást implementálni.

Megoldás

A megoldást lásd a pub-on!


Utolsó frissítés: 2024-03-19 07:29:57