Sinistra <- Bison: The GNU Parser Generator - Indice Generale - Copertina - Tao e Open Source -> Destra

Sistemi Liberi


Server Side Java - parte 2

Server Side Java - parte 2

di Stefano Sasso

L'articolo...

In questo articolo continueremo l'apprendimento di JSP e Servlet, vedendo come possono comunicare tra loro. Verrà anche data qualche infarinatura sull'uso dei custom tag. Come già detto, questa serie serve per dare uno spunto al lettore, che poi potrà realizzare applicazioni web in Java utilizzando le nozioni apprese.



Dalle servlet alle jsp

Abbiamo visto come nel paradigma MVC le richieste arrivino tutte al (o ai, se sono più di uno) controller, il quale elabora i dati e passa il controllo alla view appropriata (jsp), o, eventualmente, ad un'altra servlet. Ma come fa una servlet a passare il controllo ad una jsp o ad una servlet?

Ci viene in aiuto la classe javax.servlet.RequestDispatcher. Per passare il controllo ad una jsp creeremo una nuova istanza di RequestDispatcher utilizzando getServletContext().getRequestDispatcher(String jspPath); mentre per richiamare un'altra servlet useremo getServletContext().getNamedDispatcher(String servletName).

Ma il lavoro non è ancora finito: dobbiamo dire infatti a RequestDispatcher di passare effettivamente il controllo alla risorsa specificata. Per questo abbiamo a disposizione più metodi: forward(request, response) o include(request, response). Il primo delega completamente l'invio della risposta alla risorsa, mentre il secondo, dopo aver inviato al client l'output della risorsa, fa proseguire il codice della servlet. In pratica, se ad esempio vogliamo passare il controllo ad un'unica jsp possiamo usare

getServletContext().getRequestDispatcher("/WEB-INF/jsp/unica.jsp").forward(request, response);

mentre se vogliamo mandare al client l'output di più pagine usiamo

getServletContext().getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response);
getServletContext().getRequestDispatcher("/WEB-INF/jsp/body.jsp").include(request, response);
getServletContext().getRequestDispatcher("/WEB-INF/jsp/footer.jsp").include(request, response);

per fare un esempio creiamo la struttura di una nuova applicazione web, che chiameremo Applicazione2. All'interno della directory WEB-INF creiamo anche la cartella "jsp", che conterrà le "viste" (views) della nostra applicazione.

Iniziamo creando il file web.xml, che conterrà anche il mapping di index.html verso la servlet che andremo a creare successivamente.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        <!-- nome e breve descrizione della nostra applicazione -->
        <display-name>applicazione 2</display-name>
        <description>la mia prima applicazione MVC</description>

        <!-- i file indice, simile al DirectoryIndex di apache -->
        <welcome-file-list>
                <welcome-file>index.html</welcome-file>
        </welcome-file-list>

        <!-- dichiariamo ora la presenza della nostra servlet, e gli diamo il nome "prima" -->
        <servlet>
                <servlet-name>indice</servlet-name>
                <servlet-class>IndexServlet</servlet-class>
        </servlet>

        <!-- ora diciamo a tomcat di ridirigere tutte le richieste per index.html verso la nostra servlet -->
        <servlet-mapping>
                <servlet-name>indice</servlet-name>
                <url-pattern>/index.html</url-pattern>
        </servlet-mapping>

</web-app>

All'interno di WEB-INF/classes creiamo ora la nostra servlet IndexServlet.java:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class IndexServlet extends HttpServlet
{
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                // richiamiamo doRequest
                doRequest(request, response);
        }
        
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                // richiamiamo doRequest
                doRequest(request, response);   
        }
        
        public void doRequest(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                // passiamo il controllo alla jsp indice.jsp, in /WEB-INF/jsp/indice.jsp
                getServletContext().getRequestDispatcher("/WEB-INF/jsp/indice.jsp").forward(request, response);
        }
}

e per ultima cosa, all'interno di WEB-INF/jsp creiamo indice.jsp:

<html>
        <head><title>indice</title></head>
        <body>
        <h3>sono indice.jsp, richiamata dalla servlet.</h3>
                <p>come url continuerai a vedere quello con cui hai invocato la servlet, in quanto RequestDispatcher
                non effettua una redirezione HTTP come sendRedirect, ma bensì una redirezione interna.</p>
        </body>
</html>

Abbiamo messo le viste all'interno di una sottodirectory di WEB-INF per fare in modo che non vengano chiamate direttamente dal client.

Compiliamo ora la servlet, creiamo il war, effettuiamo il deploy su tomcat e, andando a http://localhost:8080/Applicazione2 vedremo il risultato del nostro lavoro. Complimenti, abbiamo appena realizzato un'applicazione che rispetta il pattern MVC!

Contesti di vita per il passaggio di variabili tra servlet e JSP (o tra servlet e servlet)

Abbiamo appena visto come da una servlet sia possibile passare il controllo a una jsp o ad un'altra servlet. Ma tenendo in mente il paradigma MVC ci deve sorgere una domanda: come fa il controller a passare i dati elaborati ad una view? Esistono diversi modi per il passaggio di variabili, ciascuno di questi determinato da un contesto di vita:

per salvare ed accedere ai dati salvati nei vari contesti si usano i metodi getAttribute(String name) e setAttribute(String name, Object value) (che abbiamo già visto quando abbiamo trattato il contesto di sessione)

Vediamo un banalissimo esempio, che usa il contesto request per passare dei valori: andiamo a modificare la servlet nell'Applicazione2, facendola diventare:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class IndexServlet extends HttpServlet
{
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                // richiamiamo doRequest
                doRequest(request, response);
        }
        
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                // richiamiamo doRequest
                doRequest(request, response);   
        }
        
        public void doRequest(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                HttpSession session = request.getSession();
                // recuperiamo il nome 
                String nome = "sconosciuto";
                String daSessione  = (String)session.getAttribute("nome");
                String daRichiesta = request.getParameter("nome");
                boolean daSessioneValido = !(daSessione == null || daSessione.equals(""));
                boolean daRichiestaValido = !(daRichiesta == null || daRichiesta.equals(""));
                
                if (daRichiestaValido)
                {
                        session.setAttribute("nome", daRichiesta);
                        nome = daRichiesta;
                }
                else if (daSessioneValido)
                        nome = daSessione;

                String yourIp = request.getRemoteAddr();
                
                // salviamo nella richiesta le variabili che richiameremo dalla jsp
                request.setAttribute("nome", nome);
                request.setAttribute("ip", yourIp);             
                
                // passiamo il controllo alla jsp indice.jsp, in /WEB-INF/jsp/indice.jsp
                getServletContext().getRequestDispatcher("/WEB-INF/jsp/indice.jsp").forward(request, response);
        }
}

il suo funzionamento è chiaro e semplice e, alla luce di quanto abbiamo affrontato fino ad ora, dovreste essere in grado di comprenderlo da soli.

Modifichiamo ora il file WEB-INF/jsp/indice.jsp

<html>
        <head><title>indice</title></head>
        <body>
                <p>Ciao <%=(String)request.getAttribute("nome")%></p>
                <p>Il tuo ip è <%=(String)request.getAttribute("ip")%></p>
        </body>
</html>

Apriamo ora il browser e testiamo il tutto.

I custom tag

Se dovessimo chiedere al nostro grafico di realizzare una jsp simile a quella appena vista, sicuramente andrebbe in confusione a doverci scrivere (o copiare) dentro una moltitudine di codice java: si pensi ad esempio ad una serie di cicli for o while per visualizzare una serie di dati...

Proprio in questo ci vengono in aiuto i custom tag: essi sono dei veri e propri tag xml, che sicuramente semplificheranno la vita al grafico di turno, in quanto, invece di scrivere

<ul>
<% while(nomi.hasNext()) { %>
        <li><%=nomi.next()%></li>
<% } %>
</ul>

potrà semplicemente scrivere qualcosa del tipo

<ul>
<cicli:nomi>
        <li>${nome}</li>
</cicli:nomi>
</ul>

e la parte di implementazione del tag resta solamente al programmatore.

L'utilizzo di tag personalizzati nelle pagine JSP permette di rispettare la naturale propensione dei linguaggi xml ad utilizzare soli elementi di markup ed evitare l'inserimento di codice nelle pagine.

L'utilizzo dei custom tag deve sempre venir dichiarato all'inizio di ogni pagina jsp, utilizzando

<%@ taglib prefix="<namespace>" uri="<URI>" %>

oppure

<%@ taglib prefix="<namespace>" tagdir="<DIR>" %>

sintassi che approfondiremo meglio in seguito.

JSTL, chi è costui?

L'acronimo JSTL sta per "JSP Standard Tag Library", una libreria di tag jsp già pronta all'uso. Sono dei semplici tag che possono essere utili in alcune funzioni più semplici. Per realizzare funzioni più complicate, in seguito, realizzeremo dei nostri custom tag.

JSTL si compone di 4 diverse librerie di tag, ognuna delle quali viene mappata in namespace diversi (detti "prefix" in gergo jsp, ovvero la parte nel nome tag che precede i due punti):

Le librerie più usate sono la "Core" e la "I18N".

Proviamo ora ad utilizzare la libreria "Core" per sostituire il codice java presente nella JSP di "Applicazione2"

WEB-INF/jsp/indice.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
        <head><title>indice</title></head>
        <body>
                <p>Ciao <c:out value='${nome}'/></p>
                <p>Il tuo ip è <c:out value='${ip}'/></p>
        </body>
</html>

Per poter utilizzare le JSTL è necessario creare la cartella lib all'interno di WEB-INF, e copiare al suo interno gli archivi jar che contengono le librerie. (jstl.jar e standard.jar)

Come possiamo poi vedere con il browser il risultato è identico.

Vediamo ora un esempio un po' più complesso:

Creiamo una servlet, chiamata ListaServlet, che verrà invocata con l'url /lista.html

in web.xml inseriamo quindi

        <servlet>
                <servlet-name>lista</servlet-name>
                <servlet-class>ListaServlet</servlet-class>
        </servlet>
        
        <servlet-mapping>
                <servlet-name>lista</servlet-name>
                <url-pattern>/lista.html</url-pattern>
        </servlet-mapping>

Creiamo ora un oggetto, che poi inseriremo in una lista da mostrare al client (creiamolo nel package business): Persona.java in WEB-INF/classes/business

package business;
public class Persona
{
        private String cognome;
        private String nome;
        
        public Persona(String cognome, String nome)
        {
                this.cognome = cognome;
                this.nome = nome;
        }
        
        public String getNome()
        {
                return nome;
        }
        
        public String getCognome()
        {
                return cognome;
        }
}

creiamo ora la servlet: WEB-INF/classes/ListaServlet.java

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import business.Persona;

public class ListaServlet extends HttpServlet
{
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                doRequest(request, response);
        }
        
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                doRequest(request, response);   
        }
        
        public void doRequest(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException
        {
                List persone = new ArrayList();
                Persona p1 = new Persona("Sasso", "Stefano");
                Persona p2 = new Persona("Rossi", "Giorgio");
                Persona p3 = new Persona("Verdi", "Mario");
                persone.add(p1);
                persone.add(p2);
                persone.add(p3);
                
                request.setAttribute("persone", persone);
                
                getServletContext().getRequestDispatcher("/WEB-INF/jsp/lista.jsp").forward(request, response);
        }
}

NOTA: compilando la servlet, dobbiamo dire a javac di aggiungere al classpath anche la directory corrente. Quindi:

javac -classpath <TOMCAT-ROOT>/common/lib/servlet-api.jar:. ListaServlet.java

la nostra jsp, che sarà WEB-INF/jsp/lista.jsp conterrà:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<html>
        <head><title>persone</title></head>
        <body>
                <h3>Elenco persone</h3>

                <ul>
                <c:forEach items="${persone}" var="persona">
                        <li><c:out value='${persona.cognome}'/>, <c:out value='${persona.nome}'/></li>
                </c:forEach>
                </ul>

        </body>
</html>

Proviamo ora a vedere il risultato puntando il browser a http://localhost:8080/Applicazione2/lista.html

Quest'articolo termina qui; nel prossimo articolo vedremo come sviluppare dei nostri custom tag e apprenderemo come usare i "listener" e i filtri.



L'autore

Stefano Sasso si diverte a configurare server e sistemi di rete e si diletta nella programmazione Java, PHP, Perl e Python. Frequenta il corso di laurea in Ingegneria Informatica presso l'Università di Padova e a tempo perso è consulente informatico su piattaforme Open Source.




Sinistra <- Bison: The GNU Parser Generator - Indice Generale - Copertina - Tao e Open Source -> Destra