MokaByte
Numero 20 - Giugno 1998
|
|||
|
|
||
Giovanni Puliti |
|
||
Riprendiamo il discorso iniziato in passato sui servlet per vedere come questi ci possano venire incontro nella soluzione di un problema piuttosto frequente: la fornitura di un servizio (una pagina htm) ad utenti identificati.
Nel corso dei mesi, in questo spazio dedicato alla tecnologia Java, sono passati in rassegna un po' tutti gli argomenti principali della tecnologia Java fino ad oggi prodotta, partendo dalla creazione e gestione delle interfacce grafiche cone le AWT API ([1]), alla computazione distribuita di RMI ([2]), alla gestione di database con JDBC ([3],[4],[5]), passando per i JavaBeans ([6]) e la gestione degli eventi per delega col Delegation Model ([7]), fino ad arrivare a estensioni dell'ultima generazione come i servlet ([9],[10]).
Questo mese vediamo di analizzare una coppia particolarmente vincente, che da poco ha è stata introdotta sulla scena, ma che è già fra le più utilizzate: JDBC e servlet ([13]).
Lo scopo di questo
articolo non è tanto quello di proporre un esempio complesso e completo
in tutte le sue parti, ma piuttosto di suggerire come la tecnologia Java,
tanto decantata, possa essere utilizzata per risolvere un problema di tutti
i giorni.
Il motivo per
cui ho preferito dare una impostazione generica all'articolo piuttosto
che affrontare un argomento nuovo in profondità, è stato
quello di porre un punto fermo su tutta la teoria vista fin' ora, e vedere,
seppur in maniera stringata, come la teoria possa essere tradotta in pratica.
Il caso
Fatta questa
doverosa premessa, vediamo di esporre il "caso" che affronteremo: si supponga
di avere una serie di documenti web (pagine html) residenti su un qualche
host, che possano essere visualizzati da un browser solo se l'utente che
ne ha fatto richiesta, risulta essere autorizzato (identificato per mezzo
di UID e pass).
Generalmente
questo semplice problema viene risolto utilizzando i tipici meccanismi
di protezione messi a disposizione dal web server che fornisce il servizio
(la fornitura di pagine html).
In [12] è
spiegato come funzionano i meccanismi di autentificazione dei server web
più famosi, come Apache e IIS di MS.
Visto che il
supporto offerto dal web server, è sicuramente più che sufficiente
(ed essendo già pronto anche più sicuro e facile da utilizzare),
verrebbe da chiedersi come mai sia necessario ricorrere ad una soluzione
Java, per garantire la copertura di questo tipo di problemi.
La risposta
è che per mezzo di Java, e delle tecniche legate, si può
dar vita ad una gestione più flessibile, potente, e possibile di
miglioramenti.
Nel mio caso
sono ricorso a questa soluzione, poiché necessitavo di un meccanismo
di controllo che fosse flessibile, dinamico e facilmente manipolabile via
web (quindi senza la necessità di un diretto intervento sul server).
Un vincolo per me fondamentale era soprattutto poter modificare in maniera
banale i permessi ed il periodo di validità, potendo definire intervalli
di tempo a dimensione variabile da un anno a poche ore.
Tutte queste
necessità mi hanno fatto scegliere per una soluzione full Java a
discapito di una tipica server-managing. Essendo inoltre non indispensabile,
ma molto utile poter controllare il flusso delle richieste con statistiche
particolareggiate e personalizzabili, si rendeva necessaria la realizzazione
di un set di script realizzati ad hoc, cosa che può essere facilmente
fatta in Java.
Si tenga presenta
inoltre che il tipo di applicazione che andremo a vedere è in massima
parte analoga ad un classico sistema cgi, per cui il client viene coinvolto
solo in minima parte, solo per l'invio di dati in formato testo e la successiva
visualizzazione dei risultati. In questo caso questa caratteristica permette
di utilizzare una tecnologia moderna e potente come Java, ma non necessita
di nessuna particolare estensione sul lato client (il browser potrebbe
anche non essere Java enabled).
Java come
soluzione globale
Il cuore di
tutta la applicazione è costituito da un servlet, oggetto di cui
è stato abbondantemente parlato in articoli apparsi sulle nostre
riviste [9] [11], alla cui lettura rimando per maggiori approfondimenti.
Senza entrare
troppo nei dettagli, ricordo, molto genericamente, che un servlet, non
è altro che una applicazione Java a tutti gli effetti, che risiede
accanto al web server, e che ne estende le funzionalità di base.
Un servlet è
quindi qualcosa di molto simile ad uno script cgi, con la differenza che,
essendo scritto in Java, ne eredita tutte le caratteristiche (sicurezza,
portabilità, e tutto il resto), e viene gestito da un apposito class
loader.
Quest'ultimo
particolare permette una discreta ottimizzazione sia della gestione delle
risorse (contrariamente agli script cgi, l'esecuzione di uno o più
servlet non richiede la creazione di processi appositi, ma solo il caricamento
in una JVM in esecuzione), sia dei tempi di attivazione (il meccanismo
tipico di Java di class loading, permette di caricare la classe solo nel
momento dell'effettivo bisogno, e di lasciarle in memoria per le richieste
successive).
Riconsiderando
il caso in esame, la nostra applicazione-servlet deve permettere al server
web di fornire una pagina html solo a chi è in possesso di una autorizzazione:
la procedura operativa dovrà essere quindi la seguente:
Figura1 -
Procedura di esecuzione dell’applicazione descritta in questo articolo:
1.il client http (browser) effettua una richiesta di una pagina protetta dal web server che si serve delle estensioni servlet per gestire tali richieste (2). Il servlet controlla se l’utente ha i necessari diritti di accesso per mezzo di una interrogazione ad un database effettuate da un server JDBC, il quale a sua volta si collega con un ODBC Manager (3). In caso di risposta affermativa, il servlet attiva un gestore di servizi (3), il quale prelevando eventualemente i dati da un altro database, li invia ad un impaginatore html (4) per la creazione della pagina html. Contemporaneamente il servlet installa un cookie nel client, per le successive interrogazioni. (3). Infine le informazioni ricavate vengono formattate in modo che la pagina html possa essere inviata al client (5). |
Software utilizzato,
problemi e trucchi
Prima di passare
alla parte pratica, facciamo una piccola parentesi relativamente al software
utilizzato per queste prove. Al momento della stesura di quest'articolo,
era disponibile la versione del JDK 1.2 beta 2, che presenta ancora qualche
problema di giovinezza.
Per non creare
troppi sconvolgimenti nel registry e nelle variabili d'ambiente della mia
macchina, ho deciso di installare solo le librerie necessarie per le prove
(quelle contenute in javax.servlet.*), e di utilizzare per intero (tool
ed api) il JDK 1.1: questo oltre a garantire una maggiore stabilità
dei vari componenti, mi ha permesso di evitare l'insorgere di conflitti
fra i vari tool di sviluppo installati (JBuilder, Visual Cafe, Visual Age
) ed il jdk stesso.
Consiglio questa
soluzione a chi si trovi nella mia stessa situazione, a chi non abbia voglia
di perdere troppo tempo alla ricerca di eventuali problemi nel nuovo (e
sconosciuto) compilatore java, e sopratutto a chi si trovi alle prime armi
ed abbia voglia di fare un po' di prove.
Per poter utilizzare
le librerie relative ai servlet contenute nel JDK 1.2, e solo quelle, è
sufficiente estrarre dal file jdk12beta_home_dir/lib/classes.zip
i file che corrispondono a tali api, e metterli in una dir dove risiede
il .java che si desidera compilare. A meno di strane incompatibilità
il tutto funziona.
Per quanto riguarda
il wb server, le prove sono state effettuate utilizzando il Java Web Server
di Sun, che nella versione 1.0 è gratuitamente scaricabile dalla
rete: anche se tale applicativo non è molto stabile, permette di
fare tutte le prove iniziali per prendere mano con i servlet. Essendo disponibile
sia la versione per Solaris che per Windows, credo che questa sia la piattaforma
ideale per partire.
Meccanismo
di attivazione del servlet
Come è
stato detto in precedenza, un servlet non è altro che un modo per
estendere le funzionalità del web server: per il tipo di progetto
che stiamo realizzando, deve essere possibile per il client poter accedere,
o comunque invocare, il servlet, per poter ottenere il servizio. In particolare,
a noi sarà particolarmente utile poter invocare il servlet da una
pagina html. Per ottenere questo scopo, nel caso si utilizzi il web server
di Sun (anche se la cosa è piuttosto standard), è possibile
seguire due strade: la prima consiste nell'effettuare un mapping fra il
file .class corrispondente al servlet, ed un nome logico, o in alternativa
è possibile associare un file html fittizio (tale file non esiste,
ma viene creato dinamicamente dal servlet con delle println). Nel riquadro
dedicato al Java Web Server, è spiegato come procedere per ottenere
tali remapping.
Per la cronaca,
con la versione 1.0.1 del JavaWebServer, la prima opzione non funziona,
e quindi è necessario per forza far riferimento alla pagina web
fittizia.
Ovviamente tale
soluzione non è indicata se si deve solamente mandare in esecuzione
un processo, dato che dal punto di vista del browser tale pagina è
un documento html a tutti gli effetti, e quindi in sua assenza (senza cioè
averlo prodotto dall'interno del servlet), si genera un errore ("document
contain no data").
Anche se nelle
versioni successive dovrebbe essere eliminato, è questo un inconveniente
non da poco: credo però che la maggior parte delle interazioni utente-servlet
avvengano secondo la modalità tipica cgi, quindi con produzione
di pagina di risposta. Sempre nel riquadro sul server è mostrato
come invocare il servlet nei due casi.
Java Server:
se proprio devo.....
Dal titolo di questo spazio dedicato si capisce che in effetti non è piacevole lavorare con questo oggetto: devo dire che ho dovuto installarlo e deinstallarlo varie volte, prima che decidesse di funzionare (misteri dell’informatica). Anche se presenta alcuni bug fastidiosi, rappresenta un ottimo strumento (per semplicità d’installazione e di gestione, nonché per la disponiiblità per la piattaforma Windows) per iniziare a fare delle prove. Al termine di questa serie di installazioni e disinstallazioni, ho provveduto ad installare il mio servlet di prova. La procedura è molto semplice: dopo essersi loggati (scusate per la brutta parola), utilizzando user e password admin, si può infatti lanciare la finestra dalla quale si possono gestire i servlet. Alla voce add, si può aggiungere ad esempio un MyServlet.class ed associarvi un nome logico con il quale poter identificare ed invocare tale servlet: se ad esempio tale nome logico è myservlet, allora una chiamata all’indirizzo web http://nome_host/servlet/myservlet manda in esecuzione il servlet. Anche se tale metodo è quello più generale, con la versione 1.0.1 del web server, non funziona, ed allora si può ricorrere a creare un alias per il file .class che viene associato con un .htm fittizio (cioé creato direttamente dal servlet). Se questa è una soluzione altrettanto valida, genera un errore in esecuzione nel browser (“document contain no data”), se il vostro è un servlet di servizio che non genera nessun output ipertestuale (la pagina htm di cui sopra). Nella figura 2 si può vedere come installare un servlet, mentre nella successiva come creare un alias.. Una volta installato il servlet per poterlo invocare basterà semplicemente far riferimento a tale indirizzo web http://nome_host/servlet/nome_logico_servlet? param_1 ,…, param_nnel primo caso (in corsivo i valori variabili), mentre se si è creato un alias file http://nome_host /nome_file_servlet.htm? param_1 ,…, param_nIn entrambi I casi I file .class corrispondenti al servlet devono risiedere nella dir JavaServer_dir/servlet. |
Primo collegamento:
login
La procedura
di identificazione dell'utente avviene, la prima volta, utilizzando un
semplice form html col quale si raccolgono i dati da inviare al servlet
(in questo caso uid e pass).
Ad esempio supponendo di aver inserito un form in una pagina html come segue
<FORM action="http://nome_host:8080/myservlet.htm" method="GET">Dopo aver riempito i campi con uid e password, alla pressione del pulsante da parte dell'utente, viene invocato il servlet passandogli i valori inseriti nei due campi testuali.
<TABLE BORDER=0 >
<TR>
<TD>Nome Utente</TD>
<TD><INPUT TYPE="text" NAME="uid" VALUE="" SIZE=25></TD>
</TR>
<TR>
<TD>Codice Utente</TD>
<TD><INPUT TYPE="text" NAME="pass" VALUE="" SIZE=25></TD>
</TR>
<TR>
<TD><INPUT TYPE="SUBMIT" value="OK" BORDER="0"></TD>
</TR>
</TABLE>
</FORM>
req.getParameter("uid");dove uid indica il nome dato nel form html al campo testo, req è un oggetto HttpServletRequest viene passato come parametro esterno dal server web al metodo doGet() secondo la sintassi
doGet (HttpServletRequest req, HttpServletResponse res)Ricordo che tale metodo fa parte della classe HttpServlet e viene invocato dal server quando viene richiesto dal client http un comando GET.
Controllo
login nel database
Una volta che
il servlet possiede le informazioni relative all'utente dovrà controllare
in un database se esso è autorizzato ad usufruire del servizio.
Questa operazione
è molto semplice, e si riconduce ad una normale interrogazione di
un database.
Il codice per
eseguire questo controllo è di tipo standard, visto molte altre
volte
try{Essendo la gestione dei servlet ottimizzata per lo sfruttamento delle risorse, si può pensare di aprire la connessione una volta per tutte al primo caricamento del servlet in memoria, inserendo il codice relativo nel metodo init; questo produce dei buoni miglioramenti, anche se solo un attento studio della reale situazione in cui si opera, può far capire quale sia la soluzione migliore.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException cnfe){
out.println("<CENTER> <H2> <B>");
out.println("Attenzione Driver jdbcodbc non trovato");
out.println("</B></H2></CENTER>");
}
try{
con=DriverManager.getConnection("jdbc:odbc:abbonamenti");
st= con.createStatement();
sql="Select * From Abbonati Where UID='"+UserId+"'";
st.execute(sql);
rs= st.getResultSet();
while (rs.next()){
String UserName =rs.getString(2);
// per le invocazioni successive, crea un Cookie con 2 ore di vita
Cookie c = new Cookie (UserId, PassWord);
c.setComment ("Supports Cookie for HtmlProvider");
c.setMaxAge (2*60*60); // il cookie avrà 2 ore di vita
c.setPath ("/");
c.saveCookie (res);
Permission=true;
}
}
Connessione
alla sorgente di dati
Se si deve realizzare una applicazione Java che si interfacci con un database per mezzo di un bridge JDBC-ODBC, si deve operare su due fronti: da un lato si deve scrivere il codice che utilizzando il driver indicato, permetta di accedere ai dati contenuti nell’archivio, e dall’altro si deve predisporre la struttura esterna all’applicazione che supporti tale interfacciamento. Per quanto riguarda invece il secondo aspetto, esso, pur essendo di semplice realizzazione, rapresenta uno degli ostacoli più frequenti fra i programmatori alle prime armi con tali sistemi. Essendo il Manager ODBC a gestire tutte le operazioni sul database, si deve poter permettere a tale componente di capire quale sia il database interessato in modo che esso possa esportare una interfaccia standard. In ambiente Windows tale operazione può essere eseguita lanciando l’ODBC Manager dal pannello di controllo, ed associando un nome logico al file relativo al database (dalla finestra del System DSN - vedi Fig 4). Tale nome logico dovrà essere poi utilizzato nella stringa di connessione nella istruzione Java connection(“jdbc:odbc:nomeconnessione”);Ricordo che la prima parte, “jdbc:odbc” indica il nome del driver utilizzato per la connessione: in questo caso si fa riferimento al cosiddetto bridge JDBC-ODBC. Sempre su piattaforma Windows tale driver è contenuto nel file jdbcodbc.dll che deve risiedere nella dir java_home/bin Infine la parte finale della stringa di connessione specifica il nome della connessione che il manager ODBC mette a disposizione, e non come a volte mi è stato chiesto il file fisico del database. |
Nel caso in cui
l'utente abbia almeno una entry nell'archivio, per evitare inutili operazioni
di login viene istanziato nel browser del cliente un cookie, con una vita
di due ore.
Eventualmente
si può modificare leggermente il codice in modo da riproporre la
pagina di login in caso di errore ("user non found") o di avvertire in
caso di abbonamento al servizio scaduto.
Nel breve pezzo
di codice precedente sono presenti delle chiamata a println(): esse hanno
lo scopo di inviare codice html al browser per la creazione di messaggi
per l'utente, e sono descritte qui di seguito.
Invio della
pagina
Questa parte
dell'applicazione è sicuramente una delle più semplici: si
tratta in definitiva di aprire uno input-stream (il file html da inviare),
e di riversarne il contenuto, nell'output-stream. In questo caso lo stream
di uscita corrisponde alla finestra del browser dell'utente che ha invocato
il servlet:
questo stream
può essere ricavato per mezzo di poche semplici istruzioni
ServletOutputStream out;Prima di inviare un solo carattere si deve impostare il tipo MIME riconosciuto dal browser (in questo caso "text/html").
res.setContentType("text/html");
out = res.getOutputStream();
Infine la parte
di lettura del file html viene effettuata per mezzo di un oggetto BufferedReader
col quale effettuiamo la lettura bufferizzata una riga alla volta
try{Gestione dei cookies
//String Name=req.getParameter("file_name");
HtmlFile =new File("http://p200:8080/abbonamenti.htm");
InStream =new FileInputStream(HtmlFile);
BufferedReader LineReader = new BufferedReader(new InputStreamReader(InStream));
String HtmlLine = new String();
while ((HtmlLine=LineReader.readLine()) != null)
out.println(HtmlLine.toString());
}
catch(FileNotFoundException fne){
out.println("Attenzione File Not Found "+HtmlFile.toString());
}
catch(IOException ioe){
out.println("Attenzione IO Error");
}
cookies = Cookie.getCookies (req))dove cookies è un array di cookie e req l'HttpServletRequest di cui sopra.
Registrazione
dell'utente
Per completare
il tutto, si potrebbe pensare ad implementare un altro servlet (o a estendere
il primo), in modo da permettere la registrazione di un nuovo utente.
L'implementazione
di tale servizio è molto semplice, e si riconduce alla creazione
di un form html, al relativo link con il servlet installato sul web server.
Tale servlet
eseguirà una serie di operazioni identiche a quelle viste nel paragrafo
relativo al login: in questo caso cambia solo la struttura del form html
e l'istruzione SQL che non è più una select, ma una insert.
Proprio per
la semplicità del caso, non ci soffermeremo troppo su questo punto.
Ricordo solo
che, come è accaduto in precedenza, nel momento in cui si deve produrre
un output nel browser del client (cose del tipo "Benvenuto Sig. Rossi,
la sua registrazione è stata accettata..."), non si dovrà
semplicemente eseguire una println del testo, ma del codice html necessario
per la descrizione della pagina che verrà visualizzata nel browser.
Conclusione
Abbiamo quindi
visto in questo articolo come Java ci permette di risolvere un problema
pratico, la fornitura sotto controllo di pagine html. Nella mia casella
di posta trovo spesso messaggi con i quali i lettori mi chiedono come poter
risolvere i problemi più frequenti o al contrario come in pratica
possano essere utilizzare le varie tecnologie che Java mette a disposizione.
In questo caso ho voluto dare un'idea di come poter risolvere un problema,
banale, ma la cui soluzione permette di potenziare non poco il tipo di
servizio offerto. Da questo punto di vista, ho voluto solo dare un suggerimento,
e non sono espressamente sceso in profondità, lasciando questo compito
ai lettori interessati.
In conclusione
abbiamo visto come i servlet ci permettano di realizzare una applicazione
full-java, ma che sia totalmente decentrata sul lato server.
Anche se tali
oggetti cambiano leggermente la modalità con cui una applicazione
distribuita debba essere pensata, essi non sono in antitesi con quanto
visto fino ad oggi in materia (es. RMI), ma anzi ne aumentano le possibilità.
Con molta probabilità
i servlet saranno fra i protagonisti del settore Java, e contribuiranno
ad aumentarne il successo: da questo punto di vista essi hanno colmato
una lacuna, (quella della programmazione server-side alla maniera dei cgi),
che anche se un po' legata al passato, è ancora molto usata, ed
adesso probabilmente subirà un nuovo impulso ed una nuova vita,
grazie a questa rivisitazione in stile Java.
Bibliografia
[1] "La libreria
AWT" - di Michele Sciabarrà - Computer Programming n° 56, Mar
97
[2] "Remote
Method Invocation" di Giovanni Puliti - Computer Programming n° 65,
Gen 98
[3] "JDBC" di
Michele Sciabarrà - Computer Programming n° 61, Sett. `97
[4] "JDBC API"
di Giovanni Puliti - Mokabyte 9,10,11, Giu-Sett 97
[5] "JDBC Database
Access with Java" di G. Hilton R. Cattel, M. Fisher - Ed. Addison Wesley
[6] "Java Beans:
come si costruisce un bean, e lo si testa col pacchetto di Sun BeanBox"
di Daniela Ruggeri - MB Mag 97
[7] "Il Delegation
Model" di Massimo Carli - Mokabyte 5, Feb. 97.
[8] "Sviluppare
JavaBeans" di Michele Sciabarrà - Computer Programming n° 63,
Nov 97
[9] "I servlet"
di Michele Sciabarrà - Computer Programming n° 63, Nov 97
[10] "Java 1.1"
di Philip Heller e Simon Roberts, Ed. Sybex
[11] "Le Servlet
Api" di Fabrizio Giudici Mokabyte 11, Sett 97
[12] "Http Authentication"
di John Udell, in Byte Gen 98
[13] "Servlet
& Database" di Domenico Di Girolamo _ Mokabyte 16, Febb97.
MokaByte Web 1998 - www.mokabyte.it MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it |