|
Introduzione
Il nostro viaggio all'interno della JSP Standard Tag
Library [1], iniziato con una presentazione delle sue
funzionalità e del nuovo ruolo del framework
JSP [2] nel quadro di applicazioni Java Enterprise e
proseguito con lo sviluppo e l'evoluzione di una semplice
Web Application - denominata NetView - giunge con l'articolo
di questo mese alla sua conclusione.
In particolare, sperimentate nel numero precedente le
funzionalità offerte dai componenti XML di JSP
Standard Tag Library, termineremo la nostra panoramica
dei servizi offerti da JSTL analizzandone i componenti
di internazionalizzazione e di integrazione con database
relazionali, di cui ci avvarremo per implementare le
funzionalità aggiuntive richieste a NetView dal
nostro ipotetico committente.
Per
la massima comprensione dei contenuti che andremo a
trattare si consiglia di fare frequente riferimento
sia agli articoli precedenti [3], [4] e [5] sia all'applicazione
NetView, i cui sorgenti commentati sono liberamente
disponibili e scaricabili da [5].
Funzionalità
SQL di JSTL
I componenti SQL di JSTL - come è lecito attendersi
dalle nostre precedenti esperienze con questa libreria
- sono pochi e chiari, e la loro comprensione risulterà
immediata a chiunque abbia utilizzato, almeno una volta
nella vita, le JDBC API.
Il
componente SQL va innanzitutto specificato nel deployment
descriptor come segue:
<taglib>
<taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
<taglib-location>/WEB-INF/sql.tld</taglib-location>
</taglib>
Le
pagine che utilizzano i tag SQL dichiarano tale Tag
Library con la direttiva taglib:
<%@
taglib prefix="sql" uri="http://java.sun.com/jstl/sql"
%>
La
connessione al database SQL di NetView viene specificata
nel deployment descriptor come default per l'intera
applicazione tramite le seguenti righe:
<context-param>
<param-name>
javax.servlet.jsp.jstl.sql.dataSource
</param-name>
<param-value>
jdbc:<dataSource>,<driverClass>,<userName>,<password>
</param-value>
</context-param>
I
componenti SQL permettono di procedere alla configurazione
delle connessioni tramite una pluralità di metodi,
e precisamente:
-
Ogni tag SQL può, nell'attributo dataSource,
specificare una connessione attraverso il suo percorso
JNDI o tramite la sintassi, da noi utilizzata, avente
la forma "jdbc:<dataSource>,<driverClass>,<userName>,<password>";
qualora il tag non possieda attributo dataSource,
verrà utilizzata la connessione di default;
in alternativa ogni tag SQL può, sempre nell'attributo
dataSource, fare esplicito riferimento ad un oggetto
DataSource impostato da uno strato di codice Java
superiore o tramite un tag <sql:setDataSource>;
- Il
tag <sql:setDataSource> rende disponibile un
nuovo oggetto DataSource alla pagina corrente, eventualmente
impostandolo come nuova connessione di default; tale
oggetto può venire ottenuto da uno strato esterno
di codice, tramite un riferimento JNDI, tramite la
sintassi da noi utilizzata o grazie all'utilizzo diretto
degli attributi driver, url, user e password del tag;
- una
connessione di default può venire impostata
a livello di applicazione - come nel nostro caso -
direttamente nel deployment descriptor, tramite il
suo percorso JNDI o la sintassi "jdbc:<dataSource>,<driverClass>,<userName>,<password>"
da noi utilizzata.
È
rimarchevole come la gestione delle connessioni SQL
di JSTL risulti perfettamente integrata nel contesto
J2EE e permetta agli sviluppatori JSP di sfruttare -
ponendosi in quest'ambito esattamente allo stesso livello
di astrazione degli strati EJB e JDO - le connessioni
preimpostate dall'Application Deployer in sede JNDI,
condividendo in maniera ottimizzata con gli altri componenti
della piattaforma Java Enterprise connessioni JDBC di
qualsiasi natura e traendo vantaggio dalle funzionalità
automatiche di connection pooling e distributed transactions
della piattaforma J2EE.
Le
funzionalità SQL vengono implementate tramite
i seguenti tag:
-
Il tag <sql:query> permette di eseguire una
query SQL (tipicamente tramite lo statement select)
eventualmente parametrica ritornandone il risultato
in una variabile JSTL di tipo javax.servlet.jsp.jstl.sql.Result;
il testo della query può venire fornito come
attributo o come corpo del tag;
- il
tag <sql:update> permette di inviare al server
SQL un comando di aggiornamento (quali insert, update
e delete); esattamente come per il tag precedente
l'aggiornamento può avvenire in modalità
parametrica, ed il testo del comando può venire
specificato come attributo ovvero come corpo del tag;
il risultato - in stile JDBC, il numero di righe inserite,
modificate o cancellate a seguito del comando - viene
archiviato in una variabile JSTL;
- una
serie di tag <sql:query> e <sql:update>
possono venire innestati all'interno di un tag <sql:transaction>
al fine di raggrupparli in una transazione; in base
all'esito della transazione il tag <sql:transaction>
provvederà automaticamente al commit o al rollback;
eventuali parametri a query ed aggiornamenti vengono
forniti tramite tag <sql:param> innestati all'interno
del relativo tag <sql:query> o <sql:update>.
Il
risultato di una query viene fornito sotto forma di
interfaccia javax.servlet.jsp.jstl.sql.Result, le cui
principali proprietà sono:
-
rows: rappresenta le righe della tabella risultato
della query, sotto forma di SortedMap in cui le chiavi
sono costituite dai nomi delle colonne, ed i valori
dai valori delle rispettive colonne;
- rowCount:
il numero di righe della tabella risultato;
- columnNames:
i nomi delle colonne della tabella risultato, sotto
forma di vettore di stringhe disposte secondo l'ordine
con cui le colonne sono state ritornate dal server
SQL.
Implementazione
delle funzionalità di visualizzazione del traffico
Anche in questo caso le modifiche al codice Java sono
pressoché inesistenti, e si limitano alla definizione
di un nuovo Dispatcher.
La
prima sezione della View querytraffic.jsp, analogamente
alla View querylogs.jsp, fornisce all'utente una ricapitolazione
dei criteri di ricerca ed ordinamento attuali, eventualmente
impostando dei valori di default:
<c:if
test="${empty order}">
<c:set var="order" value="${'trd_date
desc'}" />
</c:if>
.
.
<c:choose>
<c:when test="${order=='trd_date desc'}">
Data
</c:when>
<c:when test="${order=='node_id,trd_date desc'}">
Nodo e data
</c:when>
<c:when test="${order=='trd_mbytes desc'}">
Traffico
</c:when>
</c:choose>
.
.
Nell'esempio
la variabile order contiene il criterio di ordinamento
attuale in formato SQL; il primo spezzone di codice,
se tale variabile è vuota - vale a dire nulla
o di lunghezza zero - provvede a valorizzarla per default
con l'ordinamento per data decrescente.
Il contenuto di tale variabile viene quindi presentato
all'utente in forma leggibile.
Successivamente
viene eseguita la query, tenendo conto dei parametri
di ricerca e di ordinamento impostati dall'utente:
<c:choose>
<c:when test="${!empty node}">
<sql:query var="trafficData">
select node_id,trd_date,trd_mbytes
from traffic_data
where node_id=?
order by <c:out value="${order}" />
<sql:param value="${node.id}" />
</sql:query>
</c:when>
<c:otherwise>
<sql:query var="trafficData">
select node_id,trd_date,trd_mbytes
from traffic_data
order by <c:out value="${order}" />
</sql:query>
</c:otherwise>
</c:choose>
Se
l'utente non ha richiesto di restringere la ricerca
ad un nodo particolare, vengono lette tutte le righe
dalla tabella di traffico; altrimenti la selezione viene
limitata alle righe facenti capo al nodo specificato
attraverso una query parametrica, la cui sintassi è
assai simile a quella consueta in ambito JDBC.
In entrambi i casi la selezione tiene conto dell'ordinamento
impostato; si noti che, esattamente come in JDBC, l'ordinamento
non può venire impostato come parametro, ma deve
tradursi nella produzione di una query sql personalizzata.
In contesto JSTL ciò si traduce nella generazione
di un tag <sql:query> a contenuto variabile tramite
il tag <c:out>.
Il risultato della query è ora disponibile nella
variabile trafficData, sulla cui proprietà rows
è possibile iterare, ottenendo una ad una le
righe della nostra tabella risultato:
<c:set
var="totalTraffic" value="${0}"
/>
.
.
<c:forEach items="${trafficData.rows}"
var="cursor">
<tr>
<td>
<a href=
"/netview/main?action=nodeform&subaction=edit&id=<c:out
value='${cursor.node_id}' />">
<c:out value="${cursor.node_id}" />
</a>
</td>
<td><c:out value="${cursor.trd_date}"
/></td>
<td><c:out value="${cursor.trd_mbytes}"
/></td>
</tr>
<c:set var="totalTraffic"
value="${totalTraffic+cursor.trd_mbytes}"
/>
</c:forEach>
.
.
<c:out value="${totalTraffic}" /> MB
Ancora
una volta il codice JSTL si dimostra di assai facile
lettura. Si noti come, contestualmente alla lettura
delle righe, i totali giornalieri di traffico vengano
sommati nella variabile totalTraffic, inizializzata
a 0 prima del tag forEach e visualizzata nell'ultima
riga della tabella.
A
pie' di pagina viene quindi prodotto il form che permette
all'utente di avviare una nuova ricerca; il codice relativo
è simile a quello della View querytraffic.jsp,
con la sola differenza che questa volta la list box
dei nodi è limitata ai soli router, l'unica categoria
di dispositivi in grado di generare dati di traffico.
Internazionalizzazione
e localizzazione
Per internazionalizzazione si intende l'astrazione di
un'applicazione dalle caratteristiche culturali e geografiche
delle piattaforme client.
Al processo di internazionalizzazione si accoppia invariabilmente
la localizzazione, vale a dire l'adattamento - tipicamente
a run-time - di un'applicazione alla caratteristiche
di una specifica regione geografica.
Ai
fini dell'internazionalizzazione si possono identificare
due macrocategorie di informazioni:
-
Informazioni dinamiche, per le quali l'internazionalizzazione
consiste nello specificare la forma in cui esse vengono
presentate all'utente; esempi tipici sono costituiti
da numeri e date, la cui formattazione varia di paese
in paese, mentre l'informazione in quanto tale rimane
immutata;
- informazioni
statiche: in questo caso è l'informazione in
sé ad essere funzione del processo di internazionalizzazione;
ricadono in questa categoria tutti i messaggi di un'applicazione,
che devono variare in ogni regione geografica in base
alla lingua dell'utente.
L'insieme
delle informazioni statiche e delle forme delle informazioni
dinamiche che caratterizzano ogni regione geografica
vengono denominate 'locale' (da intendersi come termine
inglese). Si noti che per un unico paese, ovvero per
un'unica lingua, possono essere definiti più
'locale'. Questo è senz'altro il caso della lingua
inglese, per la quale oltre all'atteso 'locale' en (lingua
inglese / regione geografica non specificata) esiste
tutt'una serie di 'locale' specifici, quali en_US (inglese
/ USA) ed en_AU (inglese / Australia), ma anche della
nostra, più modesta, lingua italiana, per la
quale oltre al 'locale' it (lingua italiana / Italia)
è definito anche il 'locale' it_CH (lingua italiana
/ Svizzera).
L'internazionalizzazione
di un'applicazione consiste nell'identificare ed isolare
tutte le sezioni di un'applicazione dipendenti dalla
regione geografica del client, sostituendo ad informazioni
statiche e forme di presentazione dinamiche riferimenti
a dei gruppi di risorse esterni, definiti 'bundle'.
Nel
mondo Java un 'bundle' è costituito da un file
di testo in formato ASCII, il cui nome è costituito
da un prefisso, usualmente legato all'applicazione e
denominato basename, e dal nome del 'locale' cui tale
file si riferisce. Esso contiene un elenco di informazioni
statiche, vale a dire di messaggi, secondo la seguente
sintassi:
<chiave>=<valore>
<chiave>
è il nome simbolico del messaggio, a cui l'applicazione
si può riferire indipendentemente dalla piattaforma
geografica di esecuzione, mentre <valore> è
il contenuto del messaggio nel 'locale' cui il file
fa riferimento.
In ambito Java usualmente le forme di presentazione
dinamiche sono preconfigurate e vengono attivate automaticamente
dal run-time in base alle caratteristiche del client.
In
contesti Web la piattaforma geografica del client -
vale a dire il 'locale' - viene comunicata all'Application
Server, e quindi all'applicazione, direttamente dal
browser dell'utente (Edit->Preferences->Navigator->Languages
in Mozilla e Strumenti->Opzioni Internet->Lingue
sotto Internet Explorer).
Funzionalità
di internazionalizzazione di JSTL
L'ultimo componente di JSTL che ci rimane da analizzare
è la libreria di internazionalizzazione, detta
anche FMT o I18N.
Come
per gli altri componenti, anche essa va specificata
nel deployment descriptor:
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
Per
avvalersi dei servizi di internazionalizzazione una
pagina deve contenere l'apposita dichiarazione:
<%@
taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"
%>
Il
deployment descriptor accetta tre parametri di configurazione,
da definirsi come context-param, per la libreria FMT:
javax.servlet.jsp.jstl.fmt.localizationContext: il basename
del 'bundle' utilizzato per default dall'applicazione;
javax.servlet.jsp.jstl.fmt.fallbackLocale: il 'locale'
di default da utilizzare se un client non fornisce nessuna
informazione a riguardo o se il 'locale' richiesto dal
client non è disponibile;
javax.servlet.jsp.jstl.fmt.locale: questo parametro,
di utilizzo raro, permette di forzare un 'locale' ignorando
effettivamente le informazioni di localizzazione fornite
dal client.
I
principali tag votati alle funzionalità di internazionalizzazione
sono i seguenti:
-
Il tag <fmt:message> carica dal 'bundle' specificato
nell'attributo omonimo il messaggio la cui chiave
è contenuta nell'attributo key, visualizzandolo
o archiviandolo in una variabile JSTL; qualora l'attributo
bundle non venga fornito viene utilizzato il 'bundle'
corrente; la libreria FMT gestisce anche messaggi
parametrici, vale a dire messaggi in cui una parte
del testo può variare a run-time;
- <fmt:formatNumber>:
formatta un numero in base alle caratteristiche del
'locale' corrente ed alla natura delle informazioni
rappresentate dal numero stesso (numero, valuta o
percentuale); tale tag dispone inoltre di diversi
attributi, che permettono di specificare il formato
e la maschera di output delle informazioni;
- <fmt:parseNumber>:
svolge l'azione opposta rispetto al tag precedente,
effettuando il parsing di una stringa contenente un
numero in formato localizzato e visualizzando o memorizzando
il valore corrispondente in una variabile JSTL;
- <fmt:formatDate>
e <fmt:parseDate>: in maniera analoga ai due
tag precedenti, consentono la formattazione ed il
parsing localizzato di date;
- <fmt:setTimeZone>:
archivia in una variabile o imposta come default una
time zone;
<fmt:timeZone>: processa il contenuto del corpo
del tag in base ad una precisa impostazione di time
zone;
- <fmt:param>:
questo tag, innestato in un tag <fmt:message>,
specifica il valore di un parametro di messaggio;
- il
tag <fmt:bundle> imposta un 'bundle' - specificato
nell'attributo basename - come default all'interno
del corpo del tag, il cui contenuto verrà localizzato
in base al basename fornito ed al 'locale' corrente;
l'impostazione del parametro javax.servlet.jsp.jstl.fmt.localizationContext
- da noi utilizzato - ottiene un effetto analogo,
ma valido in maniera globale per tutte le pagine JSP
dell'applicazione;
- <fmt:setBundle>:
questo tag imposta il parametro javax.servlet.jsp.jstl.fmt.localizationContext;
- <fmt:setLocale>:
questo tag, di utilizzo sporadico, imposta il parametro
javax.servlet.jsp.jstl.fmt.locale.
Implementazione
delle funzionalità di internazionalizzazione
Per gli scopi di questo articolo limiteremo l'internazionalizzazione
alla sola funzionalità di visualizzazione dei
dati del traffico; la procedura per le altre parti dell'applicazione
sarebbe in concreto esattamente identica.
Per maggior chiarezza introdurremo una nuova azione,
querytrafficintl, che produce gli stessi report di traffico
dell'azione querytraffic, ma in versione localizzata.
Come nei casi precedenti le uniche modifiche al codice
Java sono costituite dalla definizione di un nuovo Dispatcher
e dalla sua dichiarazione nella classe FrontControllerServlet.
Procediamo
quindi all'identificazione delle risorse da internazionalizzare
nella View querytrafficintl.jsp:
-
Le risorse dinamiche, per cui l'internazionalizzazione
si applica alle forme di presentazione, sono costituite
dai dati variabili, vale a dire nel nostro caso dalle
date e dai totali di traffico giornalieri recuperati
dal database SQL e dal totale di traffico visualizzato
a pie' di report; la visualizzazione di tali dati
viene quindi racchiusa in tag <fmt:formatDate>
e <fmt:formatNumber>;
- Le
risorse statiche, di cui dobbiamo internazionalizzare
i contenuti, sono costituite da tutti i messaggi prodotti
dalla nostra applicazione, che vanno estrapolati dalla
View, tradotti in tutti i 'locale' che si desidera
supportare ed archiviati in 'bundle' separati; al
posto dei messaggi statici si inseriscono nella View
dei tag <fmt:message>.
A
livello di deployment descriptor configuriamo come 'locale'
di fallback en_US e come basename di default per i 'bundle'
di NetView la stringa 'messages'.
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.fallbackLocale
</param-name>
<param-value>
en_US
</param-value>
</context-param>
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext
</param-name>
<param-value>
messages
</param-value>
</context-param>
Decidiamo
di supportare i 'locale' it ed en_US, creando i due
'bundle' messages_it.properties e messages_en_US.properties,
che archiviamo in WEB-INF/classes. A titolo di esempio
riproduciamo il contenuto del 'bundle' messages_it.properties:
titlequerytrafficintl=Visualizzazione
traffico I18N
allnodes=(Tutti i nodi)
node=Nodo
ordering=Ordinamento
searchcriteria=Ricerca
date=Data
nodedate=Nodo e data
traffic=Traffico
mbytes=Megabyte trasferiti
newsearch=Nuova ricerca
nocriterium=(Nessun criterio)
search=Cerca
Si
noti che, avendo definito un 'locale' di fallback, abbiamo
la certezza che ogni client della nostra applicazione
sarà identificato da un 'locale'; in caso contrario
dovremmo definire un 'bundle' per il 'locale' nullo,
vale a dire un file message.properties con contenuti
analoghi a quelli mostrati.
Le
novità della View querytrafficintl.jsp rispetto
alla View querytraffic.jsp consistono semplicemente
nell'astrazione delle informazioni tramite i tag FMT
di JSTL; osserviamo ad esempio l'aspetto della tabella
contenente i risultati della query prima in querytraffic.jsp:
<tr>
<th>Nodo</th>
<th>Data</th>
<th>Megabyte trasferiti</th>
</tr>
<c:forEach items="${trafficData.rows}"
var="cursor">
<tr>
<td>
<a href=
"/netview/main?action=nodeform&subaction=edit&id=<c:out
value='${cursor.node_id}' />">
<c:out value="${cursor.node_id}" />
</a>
</td>
<td><c:out value="${cursor.trd_date}"
/></td>
<td><c:out value="${cursor.trd_mbytes}"
/></td>
</tr>
<c:set var="totalTraffic"
value="${totalTraffic+cursor.trd_mbytes}"
/>
</c:forEach>
e
quindi in querytrafficintl.jsp:
<tr>
<th><fmt:message key="node" /></th>
<th><fmt:message key="date" /></th>
<th><fmt:message key="mbytes" /></th>
</tr>
<c:forEach items="${trafficData.rows}"
var="cursor">
<tr>
<td>
<a href=
"/netview/main?action=nodeform&subaction=edit&id=<c:out
value='${cursor.node_id}' />">
<c:out value="${cursor.node_id}" />
</a>
</td>
<td>
<fmt:formatDate value="${cursor.trd_date}"
type="date" dateStyle="full"/>
</td>
<td>
<fmt:formatNumber value="${cursor.trd_mbytes}"
type="number" pattern="#,##0"/>
</td>
</tr>
<c:set var="totalTraffic"
value="${totalTraffic+cursor.trd_mbytes}"
/>
</c:forEach>
Come
si può notare i messaggi statici sono stati sostituiti
da tag <fmt:message> e la visualizzazione di date
e numeri avviene per mezzo di tag <fmt:formatDate>
e <fmt:formatNumber>.
Nel primo caso gli attributi type e dateStyle, impostati
rispettivamente a "date" e "full",
richiedono la visualizzazione dei soli dati di giorno,
mese ed anno in forma 'lunga', vale a dire con nomi
completi di giorno e mese.
I dati del traffico vengono invece presentati, secondo
quanto specificato dall'attributo pattern, tramite un
numero di cifre intere da 1 a 4 con eventuale separatore
di migliaia. Tale informazione viene caratterizzata
come semplicemente numerica ponendo l'attributo type
al valore 'number'; le altre possibilità sarebbero
state 'currency' e 'percentage', per indicare che il
valore è da intendersi rispettivamente come valuta
o percentuale.
Si
noti infine che il processo di internazionalizzazione
adottato, consistente nell'adozione dei 'bundle' e nella
completa astrazione di tutte le informazioni statiche,
non è l'unico possibile.
Questo processo ha lo svantaggio di nascondere totalmente
i contenuti reali delle pagine Web ai grafici, complicando
notevolmente le fasi di progettazione, impaginazione
e manutenzione del layout HTML dell'applicazione.
Un'alternativa è data dalla realizzazione di
versioni differenti delle pagine JSP in funzione dei
diversi 'locale' supportati, utilizzando un FrontControllerServlet
esteso per ritornare al client una View specifica in
base alle proprie caratteristiche geografiche.
Quest'impostazione, che privilegia il lavoro dei grafici
e semplifica il lavoro di traduzione per applicazioni
Web caratterizzate da moli di testo significative, ha
tuttavia l'effetto collaterale di produrre un elevato
numero di View duplicate, complicando le fasi di manutenzione
ed aggiornamento del codice.
Nella
pratica è consigliabile un approccio 'ragionato'
che si collochi a metà strada tra questi due
estremi e che faccia uso delle funzionalità di
inclusione parametriche di JSP per mantenere un insieme
unico di View ed isolare la gran parte delle informazioni
testuali in file HTML esterni, direttamente gestibili
dai grafici Web, prevalentemente statici e duplicati
per i diversi 'locale' supportati.
Conclusioni
Con l'analisi e la sperimentazione sul campo dei suoi
componenti di internazionalizzazione e di integrazione
con database relazionali si conclude la nostra trattazione
di JSP Standard Tag Library.
L'introduzione di questa libreria ha rappresentato per
il framework Java Server Pages una vera e propria rivoluzione,
al punto che non è errato asserire che esistano
due JSP: un JSP ante-JSTL ed un JSP post-JSTL.
Il successo di JSP Standard Tag Library e del Java Server
Pages 'moderno' - della cui versione 2.0 è già
disponibile il Proposed Final Draft [6] - viene significativamente
confermato dalle statistiche WebCraft per l'anno 2002
[7], che indicano in JSP il linguaggio di scripting
Web in più rapida ascesa.
Nel
prossimo articolo inizieremo l'analisi di una nuova
Tag Library, con tutta probabilità destinata
a rappresentare uno standard in tema di Java Web User
Interface: stiamo parlando di Java Server Faces, il
framework Java per la realizzazione di interfacce HTML
componentizzate, state-aware, riusabili ed event-driven.
Bibliografia
[1] JSTL - JSP Standard Tag Library: http://java.sun.com/products/jsp/jstl
[2] JSP - JavaServer Pages: http://java.sun.com/products/jsp
[3] Lavinio Cerquetti: "JSP 1.2: La JSP Standard
Tag Library - I parte", Mokabyte N. 67 - Ottobre
2002
[4] Lavinio Cerquetti: "JSP 1.2: La JSP Standard
Tag Library - II parte", Mokabyte N. 68 - Novembre
2002
[5] Lavinio Cerquetti: "JSP 1.2: La JSP Standard
Tag Library - III parte", Mokabyte N. 69 - Dicembre
2002
[6] Java Server Pages 2.0 Specification - Proposed Final
Draft: http://jcp.org/aboutJava/communityprocess/first/jsr152/
[7] Statistiche NetCraft anno 2002: http://www.netcraft.com/survey
Risorse
Il codice dell'applicazione presentata nell'articolo
(NetView versione 1.1) è interamente scaricabile
da [5].
Lavinio
Cerquetti si occupa di design e sviluppo del software
in ambienti distribuiti ed in architetture J2EE multi-tier.
Può essere contattato all'indirizzo di e-mail
lcerquetti@mokabyte.it
|