Per programmazione web oriented si intende quel modello di programmazione in cui il client é costituito da un semplice browser che interagisce con la parte server per mezzo del protocollo HTTP. Questa tipologia di programmi, il cui modello di funzionamento viene genericamente denominato Common Gateway Interface (CGI), ha l’obiettivo di permettere l’interfacciamento da parte di un client web con una serie di risorse e di servizi residenti sul server.
Poco prima dell’uscita del JDK 1.2, per la realizzazione di applicazioni CGI in Java, Sun ha introdotto le Servlet API, che sono diventate in poco tempo una delle più importanti specifiche di tutta la piattaforma Java2, e che sono alla base di molti framework moderni orientati al web e non solo. Successivamente con la versione 2.2, le Servlet API sono entrate a far parte formalmente della cosiddetta Java 2 Enterprise Edition, al fianco di altre importanti tecnologie come JDBC, JNDI, JSP, EJB e RMI.
Formalmente una servlet è un oggetto java che viene caricato ed eseguito sul server all’interno di un componente detto Servlet Container, che ne gestisce il ciclo di vita e che gli fornisce un contesto di esecuzione. Una unica istanza della servlet è generata ed inizializzata indipendentemente dal numero di client connessi, il container infatti provvede ad istanziare la servlet non appena il primo client invia la prima richiesta. Le richieste successive saranno gestite dalla stessa servlet. Tutto questo comporta notevoli vantaggi dal punto di vista della efficienza e delle potenzialità operative.
L’Architettura
I packages che contengono tutte le classi necessarie per la programmazione di una servlet sono il javax.servlet
ed il javax.servlet.http.
Si tratta di Standard Extension API, ovvero non fanno parte del core JDK, per questo motivo è importante specificare quale versione della API si utilizza, che deve essere conforme a quella del servlet engine.
L’interfaccia Servlet
del package javax.servlet
contiene i prototipi di tutti i metodi necessari alla gestione del ciclo di vita di una servlet ed alla esecuzione elle operazioni implementate dalla servlet. In particolare sono definiti i metodi init()
e service()
, rispettivamente utilizzati in fase di inizializzazione della servlet e in risposta alle richieste dei client.
La separazione fra i due metodi consente una ottimizzazione delle risorse. Il primo infatti serve per inizializzare la servlet, ed è il posto dove generalmente si eseguono le operazioni più onerose e che devono essere eseguite una sola volta, come ad esempio l’apertura della connessione verso una base dati. Il metodo service invece è quello che viene eseguito al momento di una chiamata POST o GET. Per ogni client che esegue una richiesta il container avvierà un thread che eseguirà il metodo service()
dell’istanza della servlet caricata in memoria.
La classe astratta GenericServlet
implementa le interfacce Servlet
e ServletConfig
e definisce astratto il metodo service()
. Estendendo tale classe è possibile implementare una qualsiasi servlet:
1 2 3 4 5 |
public class BaseServlet extends GenericServlet { public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } } |
Quando la servlet accetta una richiesta da un client riceve due oggetti:
ServletRequest
: utilizzato per la comunicazione dal client verso il server;ServletResponse
: utilizzato per la comunicazione dal server verso il client.
Nel package javax.servlet.http
fornisce le classi che estendono le funzionalità base di una servlet per supportare le caratteristiche del protocollo HTTP come i metodi GET, POST, etc. e la gestione degli header HTTP. In particolare la classe astratta HttpServlet
è quella che va estesa per implementare una servlet che utilizzi il protocollo http.
1 2 3 4 5 6 7 8 9 10 11 |
public class BaseHttpServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub } // Altri metodi http } |
In questo caso la servlet riceve in input due classi estensione HttpServletRequest
e HttpServletResponse
che sono estensioni delle classi ServletRequest
e ServletResponse.
Ovviamente la classe HttpServlet
dispone ancora del metodo service()
il quale si limita, però, ad ispezionare il metodo HTTP richiesto e dichiarato nell’oggetto HttpServletRequest,
e conseguentemente invocare i metodi doGet(), doPost(), etc.
La Servlet Hello World
Utilizzando le nozioni viste fino ad ora implementiamo una servlet HTTP che quando invocata stampa il classico messaggio “Hello World”.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class HelloWorldServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head><title>Hello World</title></head>"); out.println("<body>Hello World</body>"); out.println("</html>"); out.close(); } } |
La prima riga imposta il MIME type della risposta, mentre la riga successiva ottiene dalla response l’oggetto PrintWriter
per creare il testo HTML della pagina da inviare al client.
Per poter utilizzare la servlet deve essere inserita in un progetto Dynamic Web Project che sarà deploiato in un servlet container come Tomcat o WildFly. Fino alla versione 2.5 della specifica la servlet doveva essere registrata attraverso il descrittore del progetto web.xml
nel modo seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>servlet</display-name> <servlet> <description>My First Base Http Servlet</description> <display-name>HelloWorldServlet</display-name> <servlet-name>HelloWorldServlet</servlet-name> <servlet-class>it.javaboss.HelloWorldServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloWorldServlet</servlet-name> <url-pattern>/HelloWorldServlet</url-pattern> </servlet-mapping> </web-app> |
Si noti l’attributo version
del tag <web-app>
valorizzato con 2.5
. Nella dichiarazione della servlet riconosciamo inoltre il tag <url-pattern> che indica l’url di invocazione della servlet che, una volta avviato il container, sarà del tipo: http://localhost:8080/servlet/HelloWordServlet dove la porzione di url /servlet/
indica il “nome” con cui è dichiarata la web application (servlet.war).
Nell’esempio presentato il client non ha alcuna possibilità di interagire con la servlet al di la della sua invocazione. Vediamo ora come sia possibile inviare al container dei parametri che la servlet può utilizzare per le sue elaborazioni. In generale i metodi con cui un client può realizzare tale invio sono due: GET e POST. La differenza fondamentale tra questi due tipi di invio è che con una chiamata di tipo GET i parametri sono passati nella URL della richiesta, delimitati da “?” e “&”, ad esempio:
http://localhost:8080/servlet/HelloWordServlet?name=Java&surname=Boss
Si consideri però che una URL è composta al massimo da 255 byte, quindi c’è un limite al numero di parametri e alla loro lunghezza quando si usa una GET.
Con una chiamata di tipo POST invece i parametri vengono specificati nell’header del pacchetto HTTP.
1 2 3 |
POST /test/demo_form.asp HTTP/1.1 Host: www.javaboss.it name=Java&surname=Boss |
name
e surname
dall’url nel modo seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter("name"); String surname = request.getParameter("surname"); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head><title>Hello World</title></head>"); out.println("<body>Hello World " + name + " " + surname + "</body>"); out.println("</html>"); out.close(); } |
Per completezza riportiamo metodi della classe HttpServletRequest
che possono essere utilizzati per recuperare dei parametri dalla query string (GET) o dall’header (POST):
String getQueryString() |
Ritorna la query string completa (ovviamente solo per GET). |
Enumeration getParameterNames() |
Ritorna una enumerazione dei nomi dei parametri. |
String getParameter(String) |
Ritorna il valore del parametro a partire dal suo nome. Se il parametro prevede più valori deve essere utilizzato il metodo getParameterValues() . |
String) |
Ritorna una array di valori del parametro. |
Codice Sorgente
Il codice sorgente dell’esempio è scaricabile qui: demo.