JSP = Java Server Pages
by Andrea Mantoni
Ultima modifica: %%mtime(%d %B %Y)
= Introduzione =
Le *JSP = Java Server Pages*
sono una estensione dei servlet
utilizzate per pagine web con ridotto contenuto dinamico.
JSP può essere utilizzato come un linguaggio di scripting
"(X)HTML-embedded" (~JavaScript).
funzionamento:
al suo primo accesso, una pagina JSP viene convertita in un servlet dal *JSP container*,
quindi compilata e mandata in esecuzione come un servlet.
tipi di elementi di una pagina JSP:
- *template text* = codice HTML statico -> viene stampato automaticamente nel metodo "service" con "out.println"
- *commenti*:
<%-- JSP comment --%>
OSS.: i commenti JSP non sono visibili al client,
i commenti HTML sono passati al client.
- *direttive* -> sono interpretate direttamente dal JPS container senza compilazione (valutate una sola volta a compile-time)
<%@ direttiva attr="valore" ... %>
- *azioni/actions* (valutate ogni volta a request-time)
- *scriplet* = codice Java incluso _NEL_ metodo "service"
<% Java Statement; ... %>
- *dichiarazioni* -> codice Java incluso _FUORI_ dal metodo "service", all'interno della classe che estende Servlet (dichiarazioni di metodi/funzioni, campi privati, etc.)
<%! declaration %>
- *espressioni* -> Java value convertito automaticamente in 1 oggetto String e stampata come codice HTML statico nel metodo "service"
<%= Java Value %>
- *azioni standard* -> tags XML estesi automaticamente in codice Java
- *custom tags / tag personalizzati*
...
OSS.: per le dichiarazioni la posizione nella pagina NON è rilevante,
mentre per gli scriplet e le espressioni lo è.
Ad es. una espressione può accedere ad un campo privato anche prima di averlo dichiarato
ma non può accedere ad una variabile locale di "service" prima della sua dichiarazione.
Per migliorare la separazione tra *content vs. presentation*,
è consigliabile evitare di mischiare codice Java e codice (X)HTML.
Sono possibili varie soluzioni, una l'evoluzione dell'altra:
- develop separate utility classes and invoke them in JSP pages -> mediante la direttiva "page import"
- JavaBeans, manipolati mediante le azioni standard
- custom tags
- *MVC = Model View Controller* architecture:
un servlet analizza la request e compila un bean (il "model") contenente tutti i dati della risposta che salva.
Quindi forwarda la request ad una pagina JSP, che si limita a visualizzare i dati della response (il "view").
REGOLE CHIAVE: Il servlet NON effettua alcun output sulla response. Le pagine JSP NON modificano i beans.
-> può essere implementato mediante un framework (ad es. Apache Struts)
== XML syntax ==
E' un'alternativa alla sintassi "classica"
usata per generare documenti totalmente XML-compilant:
template text
...
...
...
...
Per evidenziare le pagine scritte con questa sintassi si usa l'estensione dei file ".jspx".
== JSP 2.0 EL=Expression Language ==
E' una sintassi semplificata per le azioni JSP introdotta in JSP 2.0:
{ expression }
"expression" può essere:
- una variabile di pagina, della request, della sessione, o del ServletContext (ricercata in that order)
- un campo privato di un bean (senza bisogno di importarlo prima!)
- uno dei seguenti implicit objects: -> accessibili anche come collections
pageContext
pageContext.request
pageContext.request.method
pageContext.request.contextPath
pageContext.request.pathInfo
pageContext.request.pathTranslated
pageContext.request.queryString
pageContext.request.requestURI
pageContext.request.contentLength
pageContext.request.contentType
pageContext.response
pageContext.session
pageContext.session.maxInactiveInterval
pageContext.session.id
pageContext.session.new
pageContext.session.creationTime
pageContext.session.lastAccessedTime
pageContext.out
pageContext.exception
pageContext.exception.message
pageContext.exception.stackTrace
... -> cercare elenco completo
pageScope
requestScope
sessionScope
applicationScope
param -> parametri della request
paramValues
header
headerValues
cookie
initParam
"expression" può contenere diversi operatori:
... (gli stessi del C)
empty -> test !=null & !=""
== -> anche per il confronto tra stringhe
not
and
or
eq
ne
lt
gt
le
ge
div
mod
instanceof
e costanti:
true|false
'stringa'
...
Consente l'uso di variabili Java all'interno dei tag HTML (standard e non),
altrimenti impossibile.
esempi:
${customer.firstName}
${customerNames[0]}
${stateCapitals["maryland"]}
${pageContext.session.id}
${param.custID}
${header.Accept}
${header["Accept"]} <%-- equivalente al precedente --%>
${cookie.userCookie.value}
${cookie["userCookie"].value}
${initParam.defaultColor}
${sessionScope.username}
Nelle pagine JSP in cui si usa è bene specificare la direttiva:
<%@ page isELIgnored="false" %>
= Direttive =
sintassi generale:
<%@ directive attribute="value" ... %>
<%@ directive attribute1="value1" attribute2="value2"
attribute3="value3"
...
attributeN="valueN" %>
sono di 3 tipi principali:
- *page*, con vari attributi possibili:
<%@ page import="package.class1,package.class2,..." %> (default: java.lang.*, javax.servlet.*, javax.servlet.jsp.*, javax.servlet.http.*) -> ~= Java import statement
OSS.: _NON è possibile accedere a classi nel package di default._
-> per poter essere accessibili, TUTTE le classi esterne devono essere incluse in un package, e quindi importate.
<%@ page contentType="MIME-Type" %> (default: "text/html")
<%@ page contentType="MIME-Type; charset=CharsetName" %> (default: "ISO-8859-1")
<%@ page pageEncoding="CharacterSet" %> (default: "ISO-8859-1")
<%@ page session="true|false" %> (default: "true")
OSS.: in ogni pagina JSP, se non specificato diversamente, viene invocato automaticamente "request.getSession( true );"
<%@ page isELIgnored="true|false" %> (default: "false") -> se "true" disattiva il parsing dello JSP Expression Language (EL)
<%@ page buffer="sizekb|none" %> (default: >=8kb) -> La response diventa committed solo quando si sono scritti "sizekb" in essa (~"response.setBufferSize(int size);")
<%@ page autoFlush="true|false" %> (default: "true")
<%@ page info="Some Message" %>
<%@ page errorPage="Relative URL" %> -> designa un'altra pagina (HTML o JSP) per la gestione delle eccezioni non catturate
<%@ page isErrorPage="true|false" %> (default: "false") -> dichiara che la pagina corrente può gestire le eccezioni sollevate da un altra pagina per mezzo della variabile "exception"
OSS.: nel caso si sollevi un eccezione non catturata nella pagina JSP,
la request corrente viene forwardata _lato-server_ alla pagina designata.
- *include* = importa il codice JSP di altre pagine _a compile-time_.
<%@ include file="Relative URL" %>
DIFFERENZA CHIAVE: la direttiva "include" importa il codice JSP, l'azione standard "include" importa solo l'output della pagina inclusa.
la direttiva "include" agisce a compile-time, l'azione standard "include" agisce a request-time.
- *taglib* = importa una libreria di custom tags per l'utilizzo nella pagina, e vi associa un prefisso.
<%@ taglib uri="Relative URL" prefix="tagprefix" %>
esempi:
<%@ page pageEncoding="UTF-8"%>
<%@ page session="false" %>
<%@ page errorPage="error.jsp" %>
<%@ include file="header.jsp" %>
<%@ taglib uri="mail.tld" prefix="mail" %>
= Scriplets ed espressioni =
Come in Javascript,
vi sono degli oggetti istanziati implicitamente (*implicit objects*):
- ServletRequest request
- ServletResponse response
- javax.servlet.jsp.JspWriter out (il body della response)
- HttpSession session (solo se la direttiva session==true)
- ServletContext application
- ServletConfig config
- javax.servlet.jsp.PageContext pageContext (utile come aggregatore di references agli altri implicit objects)
- page == this (non molto utile)
- JspException exception (solo per le pagine di errore designate)
Questi sono istanziati come _variabili locali_ del metodo "service",
quindi sono accessibili negli scriplets e nelle espressioni, ma NON nelle dichiarazioni.
Per lo stesso motivo, non sono accessibili da utility classes esterne.
_Tuttavia è possibile passare normalmente questi oggetti ad altri metodi/classi._
TRICK:
per differenziare il comportamento tra GET e POST, è possibile usare:
if( request.getMethod().equals("GET") ) {
// doGet
} else if( request.getMethod().equals("POST") ) {
// doPost
} else ...
= Dichiarazioni =
Le dichiarazioni dei campi con inizializzazione sono eseguite una sola volta (come nei servlet).
Ad es.:
<%! int counter=0; %> // contatore (globale) inizializzato una sola volta
<% counter++; %> // incrementa il contatore ogni volta che viene visitata la pagina
Per le inizializzazioni più complesse,
è possibile overloadare i metodi:
jspInit()
jspDestroy()
OSS.: "_jspService" non può essere overloadato, ne "doPost" e "doGet".
Per evitare che più thread accedano contemporaneamente ad un campo (race conditions),
si devono usare i blocchi "synchronized":
<%
synchronized( this ) {
counter++;
}
%>
MEMO: il lock non può essere preso sui tipi primitivi!
Piuttosto che dichiarare molti metodi in una pagina JSP
è preferibile inserire i metodi in utility classes esterne e dichiararli statici.
vantaggi:
- facilita la riusabilità del codice
- migliore separazione content vs. presentation
...
= Azioni standard =
Sono tradotte in codice Java a compile-time.
...
-> ~= RequestDispatcher.forward
NOTA: con forward si delega TOTALMENTE la scrittura della pagina di output
l'output corrente viene resettato,
e l'elaborazione della pagina corrente NON prosegue!
-> ~= RequestDispatcher.include
(richiesto in JSP<1.1)
NOTA: la request corrente viene reindirizzata,
l'output della pagina accodato,
e l'elaborazione della pagina corrente prosegue
aggiunta di parametri alla richiesta corrente:
...
OSS.: le limitazioni per queste redirezioni sono analoghe a quelle del RequestDispatcher dei servlet:
l'URL può fare riferimento solo a risorse dinamiche (servlet o pagine jsp) nella stessa webapp.
== JavaBeans ==
Un *JavaBean* è una classe Java che segue delle convenzioni particolari:
- must have a zero-argument (default) constructor;
OSS.: SE non viene definito alcun costruttore, la JVM ne aggiunge automaticamente uno senza argomenti
- no public fields, all fields must be private (and named as "fieldName");
- private fields can be accessed through public *getter and setter methods* only:
type getXx() // getter
boolean isXx() // getter for booleans
void setXx(type) // setter
OSS.: i campi sono anche detti *properties*.
esempio:
package vehicles; // SEMPRE NECESSARIO PER POTER ACCEDERE ALLA CLASSE!
class Car
{
Car()
{
}
// ... (altri costruttori)
private int numPassengers;
public int getNumPassengers() { // getter method for "numPassengers"
return numPassengers;
}
// if no setter is defined then the field is read-only
private boolean leased;
public boolean isLeased() {
return leased;
}
public void setLeased(boolean leased) {
this.leased = leased;
}
}
E' possibile manipolare i JavaBeans nelle pagine JSP con delle azioni standard specifiche:
- creazione/importazione di una istanza:
OSS.: Lo *scope*=visibilità dei beans può essere di 4 tipi:
- *page* (default) = visibile solo nella stessa pagina JSP,
non condiviso;
- *request* = distrutto alla chiusura della richiesta corrente,
può essere condiviso se la richiesta è forwardata;
- *session* = distrutto alla chiusura della sessione corrente,
_condiviso tra tutte le pagine richieste dallo stesso utente_ nella stessa sessione;
- *application* = distrutto alla chiusura del server,
condiviso tra tutte le pagine JSP nello stesso web application context, _anche da utenti diversi_.
OSS.: Un JavaBean viene istanziato ex-novo sse non ne è presente un altro con lo stesso "id" e "scope",
_altrimenti viene ritornata una reference ad un oggetto preesistente_.
se l'oggetto viene creato ex-novo, equivale a:
<% package.class nomeIstanza = new package.class(); %>
- creazione ed inizializzazione:
statements di inizializzazione eseguiti solo se il beam è creato ex-novo
- creazione/importazione di una istanza serializzata:
- lettura/output del valore di un campo nella pagina:
equivale a:
<%= nomeIstanza.getCampo() %>
- modifica:
-> legge il nuovo valore da un parametro della richiesta corrente
-> come il precedente + assume param="campo"
-> prova a settare _tutte_ le properties dai parametri omonimi della request
OSS.: se un parametro non è presente nella request o se il casting fallisce,
il campo è lasciato inalterato.
-> So, you usually design your beans to have identifiable default values that let you determine if a property has been modified.
-> some developers eschew the automatic conversion, define all of their settable bean properties to be of type String, and use explicit try/catch blocks to handle malformed data.
OSS.: un JavaBean condiviso potrebbe essere settato congiuntamente da più pagine.
equivale a:
<% nomeIstanza.setCampo(...); %>
== JSTL = JSP Standard Tag Library ==
Sono ulteriori azioni standard che sostituiscono alcuni costrutti tipici del linguaggio Java
e forniscono molti tags di uso comune:
- assegnazioni di variabili nei vari scopes
- strutture di controllo, looping, etc. ("if...then", etc.)
- connessione a database e invio di statements in SQL
- manipolazione di stringhe, formattazione
... -> http://www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm
Sono spesso usate in combinazione con lo JSP 2.0 EL.
Vengono importati nella pagina che le usa con la direttiva "taglib":
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
sintassi generale:
core tags:
formatting tags:
SQL tags:
XML tags:
standard functions (most string-manilupating):
=== Core Tags ===
There are following Core JSTL Tags:
Like <%= ... >, but for expressions. Inserisce automaticamente gli escapes XML.
Sets the result of an expression evaluation in a 'scope'
Removes a scoped variable (from a particular scope, if specified).
Catches any Throwable that occurs in its body and optionally exposes it.
Simple conditional tag which evalutes its body if the supplied condition is true.
Simple conditional tag that establishes a context for mutually exclusive conditional operations, marked by and
Subtag of that includes its body if its condition evalutes to 'true'.
Subtag of that follows tags and runs only if all of the prior conditions evaluated to 'false'.
Retrieves an absolute or relative URL and exposes its contents to either the page, a String in 'var', or a Reader in 'varReader'.
The basic iteration tag, accepting many different collection types and supporting subsetting and other functionality .
Iterates over tokens, separated by the supplied delimeters.
Adds a parameter to a containing 'import' tag's URL.
Redirects to a new URL. (redirect client-side)
Creates a URL with optional query parameters
esempi:
...
...
...
...
Item
<%-- default scope: page --%>
OSS.: l'attributo "var" NON può contenere un espressione!
<%-- variabile booleana --%>
....
...
=== Formatting tags ===
Following is the list of Formatting JSTL Tags:
To render numerical value with specific precision or format.
Parses the string representation of a number, currency, or percentage.
-> PRODUCE 1 STRINGA FORMATTATA (DA 1 OGGETTO DATE)
-> PRODUCE 1 OGGETTO DATE (DA 1 STRINGA)
Loads a resource bundle to be used by its tag body.
Stores the given locale in the locale configuration variable.
Loads a resource bundle and stores it in the named scoped variable or the bundle configuration variable.
Specifies the time zone for any time formatting or parsing actions nested in its body.
Stores the given time zone in the time zone configuration variable
To display an internationalized message.
Sets the request character encoding
=== SQL tags ===
Following is the list of SQL JSTL Tags:
Apre la connessione con un DB
Executes 1 (AND ONLY 1) SQL query defined in its body or through the sql attribute.
Executes 1 (AND ONLY 1) SQL update defined in its body or through the sql attribute.
OPTIONAL: Sets a parameter in an SQL statement to the specified value.
OPTIONAL: Sets a parameter in an SQL statement to the specified java.util.Date value.
Provides nested database action elements with a shared Connection, set up to execute all statements as one transaction.
esempi:
SELECT * FROM Table WHERE NOME='${nome}'
<%-- testa che il risultato non sia vuoto --%>
...
<%-- se il risultato contiene una singola tupla --%>
<%-- se il risultato contiene più tuple --%>
=== XML tags ===
=== Standard functions ===
Following is the list of JSTL Functions:
fn:contains() Tests if an input string contains the specified substring.
fn:containsIgnoreCase() Tests if an input string contains the specified substring in a case insensitive way.
fn:endsWith() Tests if an input string ends with the specified suffix.
fn:escapeXml() Escapes characters that could be interpreted as XML markup.
fn:indexOf() Returns the index withing a string of the first occurrence of a specified substring.
fn:join() Joins all elements of an array into a string.
fn:length() Returns the number of items in a collection, or the number of characters in a string.
fn:replace() Returns a string resulting from replacing in an input string all occurrences with a given string.
fn:split() Splits a string into an array of substrings.
fn:startsWith() Tests if an input string starts with the specified prefix.
fn:substring() Returns a subset of a string.
fn:substringAfter() Returns a subset of a string following a specific substring.
fn:substringBefore() Returns a subset of a string before a specific substring.
fn:toLowerCase() Converts all of the characters of a string to lower case.
fn:toUpperCase() Converts all of the characters of a string to upper case.
fn:trim() Removes white spaces from both ends of a string.
= Oracle ADF = Application Development Framework =
Framework proprietario di Oracle contente diverse librerie di tag personalizzati:
- JSF = JavaServer Faces
- Apache Struts
= Tag personalizzati =
Possono essere di 3 tipi:
- senza attributi e senza body
- con attributi e senza body:
- con attributi e con body:
body
OSS.: il "body" può contenere template text e/o altro codice JSP.
== Classic tag API (JSP <=2.3) ==
Vengono definiti mediante classi Java che implementano l'interfaccia
javax.servlet.jsp.tagext.Tag -> senza body o con body inalterterato
javax.servlet.jsp.tagext.BodyTag -> con modifica del body
o estendono gli adapters:
TagSupport
BodyTagSupport
dette *tag handlers*.
esempio Tag:
package ...; // RICHIESTO!
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
class SampleTag extends TagSupport {
public int doStartTag() // RICHIESTO: elabora il primo "blocco" del tag
throws JspException // OPZIONALE: l'unica eccezione che può lanciare è questa
{
// ottiene dal campo ereditato "pageContext" altri oggetti su cui effettuare l'I/O
JspWriter out = pageContext.getOut();
ServletRequest request = pageContext.getRequest();
ServletResponse response = pageContext.getResponse();
ServletContext context = pageContext.getServletContext();
HttpSession session = pageContext.getSession();
// effettua l'I/O
// REQUIRED: TUTTE LE ECCEZIONI DEVONO ESSERE CATTURATE INTERNAMENTE!
try {
out.println(...);
// ...
} catch(Exception ex) {
throw new JspException( ex.getMessage() ); // OPZIONALE: reindirizza l'eccezione all'esterno
}
return SKIP_BODY|EVAL_BODY_INCLUDE; // dichiara se si deve eseguire+includere il body del tag o no
} // END of doStartTag
public int doEndTag() // OPZIONALE: viene _sempre_ eseguito dopo doStartTag e dopo il body (se presente).
{
// ...
return EVAL_PAGE|SKIP_PAGE; // dichiara se si deve continuare l'elaborazione della pagina JSP dopo il tag corrente o no
}
// OPZIONALE: getters and setters per la lettura degli attributi
private int attributeName = 0;
public void setAttributeName(String attributeName) { // invocato automaticamente dal JSP engine per il passaggio del valore del parametro
this.attributeName = Integer.parseInt(attributeName);
}
// OSS.: il getter method è opzionale.
//... (altri attributi)
// OPZIONALE: altri campi privati -> N.B.: _vengono condivisi tra tutti i tags dello stesso tipo, anche in pagine diverse!_ -> problemi race conditions, usare sempre blocchi "synchronized" per l'accesso
} // END of class
esempio BodyTag:
package ...; // RICHIESTO!
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
class SampleTag extends BodyTagSupport {
?? // ... (stesse regole di prima + doStartTag DEVE ritornare EVAL_BODY_BUFFERED)
?? public int doAfterBody() { // modifica il contenuto del body PRIMA dell'elaborazione del JSP container
BodyContent body = this.getBodyContent();
String bodyString = body.getString(); // ritorna il body come una stringa STATICA
Reader bodyReader = body.getReader();
JspWriter out = body.getEnclosingWriter(); // ritorna il body come uno stream MODIFICABILE
// ... (effettua I/O nel body)
return SKIP_BODY; // = la modifica è finita
return EVAL_BODY_INCLUDE; // = richiama ricorsivamente se stessa
}
}
E' inoltre richiesto un
*file .TLD = Tag Library Descriptor* (in XML),
posto in una subdirectory di "WEB-INF",
che descriva al JSP engine le caratteristiche dei tags,
e che deve essere importato dalle pagine JSP mediante la direttiva "taglib".
esempio:
1.0
1.1
Nome libreria
Descrizione libreria
tagName
package.className
descrizione tag
empty|JSP|tagdependent
attributeName
true // OSS.: se un attributo non viene specificato, il suo valore viene lasciato inalterato
true // se "true" acconsente che il valore dell'attributo venga specificato con un'espressione JSP
// ... (altri attributi)
//... (altri tags)
Il body può essere di 3 tipi:
- empty = no body allowed
- JSP = body with JSP code allowed, viene eseguito automaticamente dal JSP engine
- tagdependent = RAW body allowed, _non_ viene eseguito automaticamente dal JSP engine
== SimpleTag API (only for JSP >=2.4) ==
A differenza della classic tag API,
viene creata una nuova istanza della classe per ogni occorrenza del tag nella pagina -> i campi privati non sono condivisi
Un'altra differenza riguarda il "bodycontent" nel TLD:
?? - JSP è accettato, ma il codice JSP non viene effettivamente eseguito;
?? - è supportato anche "scriptless" = body senza codice JSP.
N.B.: when using the SimpleTag API, it is illegal to include scripting elements in the body of the tag.
esempio:
public class ExampleTag extends SimpleTagSupport {
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
out.print("Hello World!");
getJspBody().invoke(null); // OPZIONALE: esegue+include il body (se ammesso e presente)
}
}
== JSP-based .tag files (only for JSP >=2.x ) ==
Consentono la definizione di custom tags usando codice JSP invece di codice Java.
VANTAGGIO: _NON_ richiedono un TLD. -> non è necessario definire anticipatamente le caratteristiche del tag (attributi, body, etc.)
Per essere usate è sufficiente salvarle in una subdir di "WEB-INF/tags",
quindi includerle con:
<%@ taglib tagdir="/WEB-INF/tags" prefix="csajsp" %>
In base al loro nome, verranno caricate automaticamente a run-time dalla dir specificata.
Le regole sintattiche sono le stesse del codice JSP standard, in più sono fornite:
- la direttiva "attribute", per la definizione degli attributi -> _salvati automaticamente come variabili locali di service_;
- l'azione standard "" -> esegue+espande il contenuto del body (se presente)
...
esempio:
<%@ attribute name="length" required="false" %>
<%
int len = 50;
try {
len = Integer.parseInt(length);
} catch(NumberFormatException nfe) {}
%>
Body:
<%= coreservlets.Primes.nextPrime
(coreservlets.Primes.random(len)) %>
= Fonti =
http://java.sun.com/products/jsp/tags/11/tags11.html
http://java.html.it/guide/leggi/23/guida-jsp/
http://www.jsptut.com