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
|