SERVLET & JSP DISPENSE
PROGRAMMAZIONE LATO SERVER Un server deve rispondere alle richieste del client e permettere di visualizzare le pagine Web. Questo compito è svolto da un software ben definito, il Web server. Quando si invia ad un Web server la richiesta di una pagina statica (ad esempio index.html) questo: Riconosce la richiesta. Cerca e, se presente, trova la pagina nel computer server. Invia la pagina al browser client che la visualizzerà. Il Web server, quindi, non ha una logica di controllo; non è in grado di garantire alcuna interattività con il visitatore tranne quella di fornire pagine web da visualizzare. Ma se volessimo interagire con l utente, rispondere dinamicamente, memorizzare dati in database, eseguire quindi genericamente algoritmi un Web server propriamente detto non è più sufficiente. Avremo bisogno di inserire della logica dietro alle semplici pagine HTML, degli algoritmi che ci permettano, ad esempio, di effettuare dei calcoli o tenere a memoria dei valori. Questo è permesso dagli Application server su cui possiamo creare delle Web Application Visualizzando semplicemente la pagina di un sito non è semplice distinguere apriori delle pagine generate da un server statico da quelle generate da una Web application. Questo perchè in entrambi casi ci troviamo difronte pagine html. La differenza sta nell origine di quelle pagine. Nel primo caso le pagine sono file statici (file.html) che vengono semplicemente prelevati dal server e visualizzati nel browser. Nel caso di Web application, invece, le pagine vengono costruite dinamicamente al tempo della chiamata da un programma che gira sul server e che può essere scritto in qualsiasi linguaggio. Nel caso quindi di Web Application, quando si richiedere una pagina (è riduttivo ora parlare di pagine), viene eseguito prima l'algoritmo che c'è dietro, poi visualizzato il risultato. In genere un'applicazione web si sviluppa su tre livelli: 1. Livello di presentazione: rappresenta l interfaccia utente dell applicazione e si occupa di acquisire dati e visualizzare risultati. 2. Livello intermedio: si occupa delle elaborazioni dei dati; di eseguire la parte algoritmica e generare i risultati poi visualizzati dal precedente livello. 3. Livello dati: memorizzazione e gestione dei dati, come per esempio un database. Per l'esecuzione di questi programmi quindi non serve un semplice Web Server ma è necessario il supporto di un Application Server. Questo, a differenza del Web server, non si limita a rispondere alla richiesta dell utente con la pagina HTML ma è capace di eseguire degli algoritmi per fare calcoli, ricerche, memorizzare, ecc. Grazie ad esso possiamo: affiancare alle normali pagine HTML statiche delle pagine dinamiche che cambieranno il loro aspetto ed il loro contenuto in base a dei parametri che saremo noi a decidere. inserire lato server dei veri e propri algoritmi che ricevano dalle pagine Web delle informazioni, effettuino le opportune elaborazioni e restituiscano i risultati ad altre pagine Web per la visualizzazione.
In base al linguaggio con cui si vuole programmare e creare i programmi lato server, bisogna scegliere un opportuno Application Server che lo supporti. Nel caso di Java l'application Server più diffuso è Tomcat, che è open source ed è usato per quasi il 70% dei server della rete mondiale. Ci sono poi altri Application Server rivolti ad una nicchia di clienti di fascia alta, grosse aziende o grandi organizzazioni: JBoss, un altro AS open source WebSphere, l AS di casa IBM che vanta innumerevoli estensioni. WebLogic, la proposta di BEA, una software house americana specializzata in soluzioni enterprise. Oracle, la piattaforma per la gestione di enormi quantitativi di dati che include anche un ottimo Application server. Ritornando a Tomcat, volendo esser pignoli, è un Servlet Container ed un JSP Engine. Un motore quindi in grado di eseguire lato server applicazioni Web basate sulla tecnologia J2EE e costituite da componenti Servlet e da pagine JSP. In genere Tomcat è utilizzato insieme al Web server Apache: la sezione statica è gestire con Apache, quella dinamica con Tomcat. Nello sviluppo di applicazioni Web molto semplici è inutile utilizzare entrambi gli strumenti, visto che Tomcat si comporta benissimo anche da Web server.
LE SERVLET Una Servlet è emplicemente, un programma scritto in Java e residente su un server, in grado di gestire le richieste generate da uno o più client, attraverso uno scambio di messaggi tra il server ed i client stessi che hanno effettuato la richiesta. Girano all'interno di un Application Server e possono essere considerate delle classi (quindi dei programmi) che estendono le funzionalità del server. Quindi le Servlet sono classi Java e possono avvalersi interamente di tutte le librerie Java viste; bisogna però sottolineare il fatto che le Servlet, a differenza degli Applet, non hanno delle GUI (interfacce grafiche) associate direttamente ad esse. Pertanto, le librerie AWT e Swing non verranno mai utilizzate direttamente quando si desidera implementare una Servlet. Il flusso di una Servlet Vediamo come lavora una Servlet: 1. Un client invia una richiesta (request) per una servlet ad un web application server. 2. Qualora si tratti della prima richiesta, il server istanzia e carica la servlet in questione avviando un thread che gestisca la comunicazione con la servlet stessa. Nel caso, invece, in cui la Servlet sia già stata caricata in precedenza (il che, normalmente, presuppone che un altro client abbia effettuato una richiesta antecedente quella attuale) allora verrà, più semplicemente, creato un ulteriore thread che sarà associato al nuovo client, senza la necessità di ricaricare ancora la Servlet. 3. Il server invia alla servlet la richiesta pervenutagli dal client 4. La servlet costruisce ed imposta la risposta (response) e la inoltra al server 5. Il server invia la risposta al client. Il contenuto della risposta non è necessariamente calcolato da un unico algoritmo contenuto tutto all interno della Servlet invocata (questo può essere, semmai, il caso di applicazioni molto elementari) ma, anzi, è spesso legato ad interazioni della Servlet stessa con altre sorgenti di dati (data source) rappresentate, ad esempio, da Data Base, file di risorsa e, non di rado, altre Servlet.
La Classe HttpServlet Tutte le servlet devono implementare l'interfaccia Servlet, i cui metodi sono invocati automaticamente dall'application Server. public interface Servlet { public void init(servletconfig config) throws ServletException; public ServletConfig getservletconfig(); public void service(servletrequest req, ServletResponse res) throws ServletException, IOException; public String getservletinfo(); public void destroy(); In particolare le Servlet che tratteremo dovranno estendere la classe astratta HttpServlet (del package javax.servlet.http) che implementa appunto questa interfaccia. Vediamo i metodi dell'interfaccia a cosa si riferiscono: 1. void init( ServletConfig config ): inizializza la servlet una volta sola durante il ciclo di esecuzione della servlet; 2. ServletConfig getservletconfig( ): ritorna un oggetto che implementa l'interfaccia ServletConfig e fornisce l'accesso alle informazioni sulla configurazione della servlet, tra cui i parametri di inizializzazione e il ServletContext, ovvero il suo ambiente (il servlet engine in cui vengono eseguite); 3. String getservletinfo( ): viene definito in modo da restituire una stringa con informazioni quali autore e versione; 4. void service(servletrequest request, ServletResponse response): viene mandato in esecuzione in risposta alla richiesta mandata da un client alla servlet; 5. void destroy( ): invocato quando la servlet viene terminata; usato per rilasciare le risorse (connessioni a basi di dati, file aperti, etc). Non ha nulla a che fare con il browser. I metodi su cui porremo attenzione sono init, service, destroy. Il metodo init viene invocato solo quando la servlet viene caricata in memoria, di solito a seguito della prima richiesta per quella servlet. Solo dopo che il metodo è stato eseguito, il server può soddisfare la richiesta del client invocando il metodo service, che riceve la richiesta, la elabora e prepara la risposta per il client. Quindi il metodo service viene richiamato ogni volta che si riceve una richiesta. Tipicamente, per ogni nuova richiesta il servlet engine crea una nuova thread di esecuzione in cui viene mandato in esecuzione service. Solo quando il servlet engine termina la servlet, viene invocato il metodo destroy per rilasciare le risorse della servlet. Quindi di fatto, il codice del programma (o ovviamente il codice da cui partire che poi chiama altre classi), va inserito in service in quanto è il metodo che viene chiamato ogni volta che un client contatta il server (il metodo init viene chiamato solo quando la servlet viene avviata la prima volta, ovvero quando è contattata dal primo client).
HttpServletRequest e HttpServletResponse Il metodo più significativo in una servlet è quindi il metodo service, che prende in ingresso: un oggetto ServletRequest, corrispondente ad uno stream di ingresso, da cui leggere la richiesta del client; un oggetto ServletResponse, corrispondente ad uno stream di uscita, su cui scrivere la risposta. Nella classe HttpServlet, il metodo service() viene sovrascritto ed i parametri che utilizza sono del tipo specifico: HttpServletRequest request: che rende accessibili i dati relativi alla richiesta inoltrata dal client; HttpServletResponse response: in cui inserire la risposta da passare al client. Quindi sono i parametri tramite i quali posso gestire il flusso client-server di cui parlavamo in precedenza: ho una richiesta (request, inviata dal client verso il server) ed una coseguente risposta (response, inviata dal server verso il client). In particolare sugli oggetti di questo tipo abbiamo a disposizione diversi metodi per appunto leggere informazioni sulla richiesta del client e per scrivere informazioni da ritornare come risposta. Metodi dell'interfaccia HttpServletRequest: String getparameter(string name): torna il valore del parametro name passato nella get/post; Enumeration getparameternames( ): parametri nella post; String[] getparametervalues(string name): se name ha valori multipli Cookie[] getcookies( ): memorizzati dal server nel client; HttpSession getsession(boolean create): se l'oggetto non esiste e create = true, allora lo crea. Metodi dell'interfaccia HttpServletResponse: void addcookie( Cookie cookie ): nell'header della risposta passata al client; essa viene memorizzata a seconda dell'età massima e dell'abilitazione o meno ai cookie disposta nel client; ServletOutputStream getoutputstream( ): stream basato su byte per dati binari; PrintWriter getwriter( ): stream basato su caratteri per dati testuali; void setcontenttype(string type): specifica il formato MIME. In particolare ci interesseranno i metodi getparameter(string name) per ricavare gli eventuali parametri passati dal client nella sua richiesta e getwriter() per ottenere lo stream su cui scrivere la risposta.
Servlet Context Un altra importante interfaccia messa a disposizione dal Servlet engine è la javax.servlet.servletcontext. Attraverso di essa è possibile trovare un riferimento al contesto (context) di un applicazione, ovvero ad una serie di informazioni a livello globale condivise tra i vari componenti che costituiscono l applicazione stessa. L'HttpServlet possiede il metodo getservletcontext che ritorna appunto il riferimento al contesto della Servlet. Tra i vari metodi a disposizione sull'oggetto context ottenuto abbiamo per esempio getrealpath( String virtualpath) a cui posso passare come parametro un path virtuale e che ritorna la stringa contenente il Path reale corrispondente (utilizzabile per esempio per creare file nel contesto dell'applicazione web).
Scrivere una Servlet Creare una Servlet vuol dire, in termini pratici, definire una classe che estende la classe HttpServlet e fare l'override dei metodi che ci interessano. In precedenza abbiamo detto che principalmente ci interesserà principalmente il metodo service ( ); in realtà Java può differenziare le richieste di tipo GET e quelle di tipo POST ed eseguire di conseguenza il metodo appropiato tra i seguenti due (metodi propri della classe HttpServlet): void doget(httpservletrequest req, HttpServletResponse resp) Gestisce le richieste HTTP di tipo GET. Viene invocato da service() void dopost(httpservletrequest req, HttpServletResponse resp) Gestisce le richieste HTTP di tipo POST. Viene invocato da service() In particolare, se non sovrascriviamo il metodo service( ) questo, in base al tipo di richiesta che ha avuto dal client, chiamerà automaticamente doget o dopost passandogli la request e la response. Quindi sono questi i metodi che veramente dobbiamo andare a sovrascrivere ed in cui dobbiamo definire il codice dell'algoritmo (nulla ci vieta di fare l'override di service e scrivere lì in codice se non ci interessa la differenzazione tra Get e Post). All'interno del mio codice potrò prendere eventuali parametri inclusi nella richiesta tramite il metodo getparameter sull'oggetto HttpServletRequest:... //prendo il valore del parametro nome String name = request.getparameter("nome");... Oppure scrivere una risposta inserendola nell'oggetto HttpServletResponse:... //indico che il contenuto della mia risposta sarà del codice HTML response.setcontenttype("text/html;charset=utf 8"); //scrivo il codice PrintWriter out = response.getwriter(); out.println("<html>"); out.println("<head>"); out.println("<title>la Mia Prima Servlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>la mia servlet</h1>"); out.println("</body>"); out.println("</html>");... Tale classe va compilata ed il file class posizionato all'interno della cartella della Web Application. Se per esempio il nome della classe è MyServlet (e quindi avrò il file MyServlet.class) nella Web Application, posso accedere alla servlet indicando nel browser l'indirizzo del suo path: miosito.it/myapplication/myservlet. Il browser ci mostrerà il contenuto dell'httpservletresponse che abbiamo tra l'altro indicato essere codice HTML.
Primi Esempi Semplice Stampa: import java.io.ioexception; import java.io.printwriter; import javax.servlet.http.*; public class NewServlet1 extends HttpServlet { protected void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); protected void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); protected void processrequest(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { response.setcontenttype("text/html;charset=utf 8"); PrintWriter out = response.getwriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>la Mia Prima Servlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>questa è un Servlet</h1>"); out.println("</body>"); out.println("</html>"); finally { out.close();
Prendere Parametri col Get / Post import java.io.ioexception; import java.io.printwriter; import javax.servlet.http.*; public class NewServlet1 extends HttpServlet { protected void doget(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); protected void dopost(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); protected void processrequest(httpservletrequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getparameter("nome"); response.setcontenttype("text/html;charset=utf 8"); PrintWriter out = response.getwriter(); try { out.println("<html>"); out.println("<head>"); out.println("<title>la Mia Prima Servlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>nome: " + name + "</h1>"); out.println("</body>"); out.println("</html>"); finally { out.close(); Pagina HTML che richiama la Servlet (con il get): <html> <head> <meta http equiv="content Type" content="text/html; charset=utf 8"> <title>la mia pagina</title> </head> <body> <h1>prova 1</h1> <form action="newservlet1" metod="get"> Inserisci il nome: <input type="text" name="nome"><br> <input type="submit" value="invia"> </form> </body> </html>
Analizziamo il codice: Creiamo una classe "NewServlet1" che estende la classe HttpServlet che implementa a sua volta l'interfaccia Servlet Non sovrascriviamo il metodo service ( ) ma i due metodi doget e dopost In particolare, non esigendo un comportamento diverso per ognuno di essi, mettiamo le operazioni in un metodo a parte processrequest che definiamo noi e che facciamo chiamare sia a doget che a dopost. Nel nostro codice con il metodo setcontenttype stabiliamo il tipo MIME della pagina di risposta. In questo caso e' HTML Il metodo getwriter è un modo per restituire dati all'utente, in particolare getwriter ritorna un writer, cioè un outputstream con il quale sarà possibile ritornare la pagina di risposta al client. Chiamiamo questo metodo sul parametro HttpServletResponse che rappresenta i dati che verranno restiuiti al client. Nel caso in cui prevediamo la lettura di parametri in input, utilizzaiamo HttpServletRequest. In particolare utilizziamo il metodo getparameter(.. ) in cui indichiamo il nome del parametro che ci interessa e di restuisce il valore che gli è stato associato dal client.
I vantaggi delle Servlet Efficienza. Le servlet vengono istanziate e caricate una volta soltanto, alla prima invocazione. Tutte le successive chiamate da parte di nuovi client vengono gestite creando dei nuovi thread che si prendono carico del processo di comunicazione (un thread per ogni client) fino al termine delle rispettive sessioni. Portabilità: Grazie alla tecnologia Java, le servlet possono essere facilmente programmate e portate da una piattaforma ad un altra senza particolari problemi. Persistenza: Dopo il caricamento, una servlet rimane in memoria mantenendo intatte determinate informazioni (come la connessione ad un data base) anche alle successive richieste. Gestione delle sessioni: Il protocollo HTTP è un protocollo stateless (senza stati) e, pertanto non in grado di ricordare i dettagli delle precedenti richieste provenienti da uno stesso client. Le servlet sono in grado di superare questa limitazione.