MokaByte 73- Aprile 2003 
La servlet 2.4 API
di
Giovanni Puliti
Il 7 marzo scorso è stato presentato il proposed finale draft v.2 della nuova specifica della Servlet API. Anche se non si tratta ancora di una release definitiva, può essere utile iniziare ad analizzare le novità introdotte.

Introduzione
La nuova specifica che andiamo a presentare questo mese è stata da poco presentata dal gruppo di lavoro in versione 2. Non è detto che questa release sia definitiva: prima della vera final, potrebbe arrivare una versione 3 con alcune modifiche ed aggiunte tecniche di minor peso, anche se quello che è stato rilasciato al momento è già sufficientemente definito e chiaro per poter iniziare a discutere della nuova API.
Differentemente dalle release precedenti la 2.2 e la 2.3 in cui furono presentate importanti modifiche ed aggiunte al framework (le self-contained Web applications nella 2.2, i filtri e listeners nella 2.3), in questo caso non state introdotti cambiamenti sostanziali. Per coloro che spesso si trovano ad inseguire gli aggiornamenti tecnologici della piattaforma J2EE, questa notizia potrà apparire sicuramente positiva.
Certamente, a parte alcuni aspetti legati ai nuovi requisiti di sistema, questa nuova release dovrebbe rendere più semplice per i programmatori e per i produttori di servlet container, il passaggio alla nuova versione della API.
In sintesi ecco l'elenco delle novità introdotte con la 2.4

  • Per poter utilizzare i servlet 2.4 adesso è necessario utilizzare l'HTTP/1.1 e la Java 2 Platform, Standard Edition versione 1.3, anche se si può utilizzare la versione 1.4 della J2EE.
  • L'interfaccia ServletRequest fornisce adesso nuovi metodi che permettono di ricavare informazioni circa la connessione verso il client.
  • Un nuovo supporto per l'internazionalizzazione e per la scelta del set di caratteri.
  • Nuove funzionalità aggiunte alla RequestDispatcher.
  • Nuovi listener e nuovi metodi aggiunti alla ServletRequest.
  • E' stato deprecato l'utilizzo del modello sincronizzato basato sull'implementazione della interfaccia SingleThreadModel.
  • E' stata maggiormente chiarita e definita la modalità di interazione con la HttpSession per quanto concerne la procedura di login.
  • Chiarimento del comportamento dei classloaders e dei welcome-file.
  • Il file di descrizione del deploy, web.xml, adesso utilizza un XML Schema per la definizione della struttura e comprende nuovi elementi (tag) per la definizione di nuove funzionalità e comportamenti della applicazione.

Tutte queste modifiche, che potrebbero apparire molte e pesanti, in realtà assumono un peso diverso a seconda dei casi. Quello che vedremo di seguito è una analisi dettagliata di ogni singolo aspetto cercando di comprendere anche quanto e come tali novità impattino realmente nella progettazione ed implementazione di applicazioni web basate su servlet.
I nuovi requirements
Sicuramente la novità più importante da un punto di vista architetturale è dato dal nuovo set di requirements necessari per poter utilizzare la nuova servlet API.
Una prima scelta piuttosto forte è stata quella relativa al protocollo HTTP: l'abbandono definitivo della versione 1.0, permette di sfruttare alcune importanti novità introdotte nella versione 1.1.
La 1.0 rappresenta la versione originale del protocollo e fu standardizzata nel 1996. La 1.1 invece rappresenta la nuova versione del protocollo, la quale offre alcune importanti miglioramenti nelle performance, fra cui un più efficiente utilizzo delle connessioni HTTP, un miglior supporto per il client-side caching, la possibilità di utilizzare richieste HTTP multiple (pipelining), un più raffinato controllo sulla invalidazione della cache e modifiche alle regole di policy.
Questo passaggio rappresenta per certi versi una scelta piuttosto radicale anche perché attualmente non tutti i server (servlet container in particolare) supportano tale protocollo e sarà necessario un po' di tempo per aggiornare lo strato di comunicazione.
E' stata aggiunto il nuovo codici di errore (status code 302) tramite la variabile statica

HttpServletResponse.SC_FOUND

dove Found è il nome utilizzato in HTTP/1.1 corrispondente a quello che in HTTP/1.0 era Moved temporarily. Il codice

HttpServletResponse.SC_MOVED_TEMPORARILY

Continua ad esistere e rappresenta il codice 302, ma è preferibile utilizzare il SC_FOUND: SC_MOVED_TEMPORARILY può essere considerato deprecated, anche se tecnicamente deprecare una variabile è impossibile.
Per quanto riguarda la JVM, ci sono novità anche in tal senso: con la 2.4 API il minimo necessario è rappresentato dalla versione 1.3 della J2SE . Formalmente poi è da notare che la nuova specifica verrà rilasciata nell'ambito della prossima J2EE 1.4: questo non significa che il container utilizzato debba offrire supporto completo per tutta la piattaforma Java 2 Enterprise Edition (ad esempio Tomcat non fornisce supporto per EJB o per JMS), ma nel caso ciò sia possibile, i servlet potranno trarre vantaggio dalla nuova piattaforma in modo più omogeneo e completo.

 

Nuovi metodi di richiesta
Sono stati aggiunti nuovi metodi alla classe ServletRequest per permettere al programmatore di ricavare maggiori informazioni circa il client chiamante. Questo metodi sono:

  • getRemotePort(): restituisce il numero della porta IP del client o dell'ultimo proxy che ha inoltrato la richiesta;
  • getLocalName(): restituisce l' hostname associato alla interfaccia di rete sulla quale la richiesta è stata ricevuta;
  • getLocalAddr(): restituisce l'indirizzo IP della interfaccia di rete sulla quale la richiesta è stata ricevuta;
  • getLocalPort(): restituisce l'IP port number della interfaccia di rete sulla quale la richiesta è stata ricevuta;


Questi metodi, il cui significato e funzionamento dovrebbe apparire piuttosto intuitivo, offrono uno strumento per agire a basso livello sullo strato di comunicazione con il client.
Da notare che alcuni di questi metodi sono stati aggiunti per offrire un maggior livello di informazioni: ad esempio i getServerName() e getServerPort() sono stati ridefiniti per esporre dettagli a livello HTTP, semplicemente ritornando la stringa "host:port" ricavata dall'host header HTTP.
Tutte queste informazioni possono essere particolarmente utili ed importanti nel caso di sistemi bilanciati e di virtual hosting dato che permettono di ricavare un maggior numero di dettagli sul dove e sul come il servlet sia eseguito.

 

Internazionalizzazione
Il supporto per la internazionalizzazione è da sempre uno degli aspetti più importanti di Java: oltre alle normali operazioni di localizzazione delle risorse, una applicazione web deve anche tener conto del set di caratteri da utilizzare per comporre la risposta da inviare al browser. Per questo da sempre ogni programmatore di servlet si è dovuto preoccupare di questi aspetti utilizzando i metodi appositi per impostare nell'header della risposta tali informazioni.
Con la nuova API alcuni nuovi metodi sono stati forniti al fine di facilitare questo compito; in particolare all'interfaccia ServletResponse sono stati aggiunti due nuovi metodi; il primo è il metodo setCharacterEncoding(String encoding), il quale imposta la codifica dei caratteri da utilizzare per comporre la risposta. Questo metodo è utilizzabile in alternativa al classico setContentType(String) oppure alla impostazione del locale tramite il metodo setLocale(Locale).
Come per tutti quei metodi ed operazioni che hanno effetto sull'header della risposta HTTP, questo metodo non ha effetto se invocato dopo l'esecuzione della getWriter() o se una risposta è già stata inviata al client.
Nella bibliografia (vedi [CHARSET]) è riportato un link ad una raccolta dei set di caratteri validi utilizzabili nell'ambito di HTTP.
Il metodo getContentType() invece restituisce il content type della risposta, ovvero il charset impostato tramite i metodi setContentType(), setLocale() o setCharacterEncoding(), visti poco sopra. Se nessun content type è stato impostato, esso restituisce null.
Il metodo setCharacterEncoding() che si associa con il metodo già esistente getCharacterEncoding() fornisce un modo più semplice per manipolare il charset della risposta, consentendo di evitare l'utilizzo ad esempio di una chiamata del tipo setContentType("text/html; charset=UTF-8").

Tutti questi metodi potrebbero apparire non troppo interessanti ed utili, ma la loro presenza adesso costituisce un meccanismo uniforme per impostare il content type utilizzando congiuntamente i metodi setContentType(), setLocale() e setCharacterEncoding().
La scelta dell'utilizzo fra il metodo setLocale() o l'equivalente setCharacterEncoding() dipende dal contesto specifico. Brevemente si potrebbe dire che l'utilizzo di un oggetto Locale può essere più che sufficiente per la maggior parte dei casi, dato che implicitamente esso imposta anche il set di caratteri da utilizzare. Vi possono però essere alcuni casi in cui ad uno stesso locale possono corrispondere (o si potrebbe voler specificare meglio) diversi tipi di set di caratteri.
Ad esempio il locale giapponese potrebbe implicare l'utilizzo di due charset differenti Shift_JIS e EUC-JP. In questi casi il metodo setCharacterEncoding() offre una granularità maggiore per specificare queste impostazioni.
In realtà la Servlet API 2.4 offre uno strumento ancora più flessibile per specificare il locale ed il set di caratteri: tramite alcuni nuovi tag XML è adesso possibile mappare un determinato charset o locale, semplicemente editando il file di deploy web.xml:
ad esempio per specificare il charset Shift_JS da associare al locale "ja" si potrebbe scrivere

<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
<locale-encoding-mapping>
<locale>zh_TW</locale>
<encoding>Big5</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list>

dove la coppia <locale-encoding-mapping> </locale-encoding-mapping> può essere ripetuta un numero arbitrario di volte tante sono le associazioni che si desidera specificare.
Nell'esempio visto si associa al locale ja il charset Shift_JIS ed al zh_TW (Chinese/Taiwan) il charset Big5.
Nel momento in cui il diffondersi di client in grado di leggere e parlare con il charset UTF-8 dovesse crescere, si potrà modificare il tipo di risposta prodotta dalla applicazione senza dover modificare una sola riga di codice Java.

 

RequestDispatcher
Nel caso in cui si effettui una forward server-side, il servlet passa il controllo ad un altro URI, senza la necessità di inoltrare il controllo al browser.
Il metodo che normalmente si utilizza per questa operazione è il forward() della interfaccia RequestDispatcher.
I metodi getRequestURI(), getContextPath(), getServletPath(), getPathInfo() e getQueryString() ritornano le informazioni relative all'URI invocato. A volte può essere necessario ricavare l'URI originale della richiesta, quello invocato prima della invocazione di forward(). Per questo sono stati aggiunti i seguenti attributi:

javax.servlet.forward.request_uri
javax.servlet.forward.context_path
javax.servlet.forward.servlet_path
javax.servlet.forward.path_info
javax.servlet.forward.query_string

Ad esempio tramite l'istruzione

request.getAttribute("javax.servlet.forward.request_uri");

si può ricavare il nome dell'URI invocato originariamente prima della forward.
Il set di attributi aggiunti ricordano molto da vicino quelli introdotti con la versione 2.2 della API:

javax.servlet.include.request_uri
javax.servlet.include.context_path
javax.servlet.include.servlet_path
javax.servlet.include.path_info
javax.servlet.include.query_string

i quali però offrono informazioni relativamente ad operazioni di include e non di forward.
L'utilizzo di una forward, nel caso in cui fossero stati definiti dei filtri di richiesta
Un'altra importante innovazione è quella che vede la possibilità di specificare il comportamento dei listener in concomitanza delle varie tipologie di operazioni eseguite: questo era un aspetto lasciato in sospeso in precedenza, tanto che il comportamento finale della applicazione era non sempre lo stesso al variare del container.
E' stato introdotto un nuovo tag <dispatcher> nel deployment descriptor con I seguenti valori: REQUEST, FORWARD, INCLUDE ed ERROR.
In questo modo un filtro può essere attivato o disattivo a piacimento semplicemente editando il file web.xml; ad esempio

<filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/find/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

in questo caso si vuole specificare che il filtro sarà eseguito sia in occasione di request del client, che di operazioni di forward. I valori include ed error permettono rispettivamente di attivare il filtro sulle operazioni di include che per le richieste verso la pagina di errore (tag <error-page>).
Se non viene specificato nessun elemento di tipo <dispatcher> verrà utilizzato il valore di default corrispondente a REQUEST.
Ascoltatori: novità fra i ServletListeners
Con la 2.4 è stato esteso il funzionamento dei listener, introdotti con la 2.3 i quali potevano ascoltare solamente eventi legati al contesto (creazione o rimozione oppure aggiunta o rimozione di attributi al contesto stesso).
Adesso è possibile creare ascoltatori che ricevano notifica anche quando viene creata o distrutta una request, oppure quando sono aggiunti o rimossi attributi alla request.
La API 2.4 aggiunge le seguenti classi

ServletRequestListener
ServletRequestEvent
ServletRequestAttributeListener
ServletRequestAttributeEvent

Che vanno ad aggiungersi alle abituali ServletContextListener, ServletContextEvent, ServletContextAttributeListener e ServletContextAttributeEvent: la configurazione di questi oggetti è realizzata al solito tramite opportuni tag XML da inserire nel file web.xml.
La nuova specifica chiarisce finalmente un aspetto lasciato in sospeso fino ad oggi, ovvero cosa debba succedere quando un listener lancia una eccezione. Dato che i listener di fatto vivono al di fuori della service call stack, l'eccezione non può essere propagata al servlet (e quindi semmai notificare il client).
Nella Proposed Final Draft 2 non è ancora stato deciso niente di definitivo, ma per il momento è stato deciso che l'eventuale errore verrà gestito tramite la direttiva <error-page> se definita, o in alternativa da un semplice messaggio verso il client corrispondente al codice di errore HTTP 500.

 

Sessione
Una delle innovazioni più importanti nella nuova API risiede nel metodo HttpSession.logout(), che di fatto serve per invalidare una sessione e quindi di implementare in una applicazione web il meccanismo di logout nel caso in cui l'utente si sia collegato tramite uno dei meccanismi standard di autenticazione (BASIC, DIGEST, FORM, CLIENT-CERT).
Purtroppo il metodo, per quanto utile, non funziona in tutti quei casi in cui le credenziali di autorizzazione sono mantenute dal client (di fatto nei metodi BASIC, DIGEST, e CLIENT-CERT).
Anche se si invalida la sessione, niente impedisce al browser di inviare nuovamente i dati di login, e di fatto questo implica il re-login automatico.
Questa è anche una spiegazione indiretta del perché la maggior parte delle applicazioni web implementano meccanismi di login tramite FORM, i quali possono essere facilmente invalidati annullando la sessione di lavoro, o annullando i cookie lato client.
Altra modifica relativa alla gestione delle sessioni è quella che prevede la possibilità di definire una valore nullo o negativo come tempo di scadenza della sessione stessa (tag <session-timeout>). In questo caso la sessione non dovrebbe scadere mai, cosa di per se piuttosto rischiosa, ma utile in certi casi particolari.
Infine nel caso in cui si utilizzino sessioni distribuite, piazzando in sessione un oggetto che non sia serializzato, verrà generata una IllegalArgumentException, evento sicuramente utile al posto di un silente errore applicativo non meglio specificato.

 

Deprecato l'STM
In Servlet 2.4 è stato reso deprecated il modello SingleThreadModel (STM) il cui significato era quello di rendere tread-safe e quindi sincrona, l'invocazione dei servlet da parte dei molti client HTTP.
Il motivo di questa scelta risiede nella impossibilità di garantire in modo certo e sicuro la concorrenza delle chiamate, ma anzi fornisce al programmatore un falso senso di sicurezza. L'argomento sarebbe in realtà piuttosto vasto: ne è data una spiegazione abbastanza chiara e completa in [STM].

 

Schema
Infine l'ultima modifica che è stata introdotta è relativa alla validazione del file XML di deploy. Adesso tale file è definito tramite un XML-schema anche se per compatibilità con le versioni precedenti sono ancora accettate file di deploy basati su DTD.
Anche se il concetto di schema esula dagli scopi di questo articolo, si ricorderà brevemente che uno schema è un metodo alternativo, basato a sua volta su XML, per definire la struttura di un file XML.
Con la nuova definizione sono state aggiunte nuove imposizioni (l'elemento <role-name> deve essere unico), mentre l'ordine degli elementi all'interno di <web-app> può essere variabile. Il tag <distributable> può apparire un numero arbitrario di volte, mentre <description> adesso supporta un attributo xml:lang ad indicare il linguaggio utilizzato.
Da notare che un servlet container non necessariamente deve validare il file di deploy tramite lo schema, mentre il container J2EE si.
Una aggiunta ulteriore che è stata fatta al file di deploy è quella relativa al welcome-file: adesso infatti è possibile specificare un servlet url-pattern come file di welcome all'interno di una applicazione.

 

Conclusione
Al momento non è ancora possibile utilizzare in produzione la nuova specifica 2.4, dato che la maggiore parte dei container non supportano le nuove specifiche, che peraltro non sono ancora disponibili in fase definitiva. Per chi fosse interessato a fare qualche prova, può utilizzare la versione 5.0 (in alfa version) di Tomcat, che supporta già queste nuove funzionalità appena viste. Ovviamente si tratta di un ambiente di esecuzione non definitivo e quindi da non utilizzare in ambito di produzione.
Non vi sono ancora notizie circa il rilascio della versione definitiva della API 2.4, anche se è stato preannunciata la presentazione ufficiale in occasione del prossimo JavaOne di giugno prossimo a S.Francisco. In tale occasione dovrebbe anche essere annunciato il nuovo J2EE 1.4 di cui la servlet API dovrebbe far parte in maniera integrata.
Su MokaByte seguiremo l'evolversi della situazione da vicino, proponendo nuovi aggiornamenti non appena questi si presenteranno.


Bibliografia
[JSR 154] - Sito web ufficiale del Servlet API 2.4 working group, JSR 154
http://www.jcp.org/en/jsr/detail?id=154

[API] - Sito web ufficiale della servlet API http://java.sun.com/products/servlet

[DOWN] - Download page for the J2EE 1.4 specification
http://java.sun.com/j2ee/download.html#platformspec

[STM] - "Servlet Best Practices, Part 1" e seguenti, di aa.vv.
http://www.onjava.com/pub/a/onjava/excerpt/jebp_3/index1.html

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it